summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore13
-rw-r--r--CMakeLists.txt157
-rw-r--r--README.md99
-rw-r--r--angdos.cfg634
-rw-r--r--doc/DEBUG.txt8
-rw-r--r--doc/RELEASE.txt8
-rw-r--r--doc/changes.txt208
-rw-r--r--doc/credits.txt67
-rw-r--r--doc/images/screenshot.pngbin0 -> 20223 bytes
-rw-r--r--lib/.gitignore2
-rw-r--r--lib/CMakeLists.txt19
-rw-r--r--lib/apex/delete.me1
-rw-r--r--lib/cmov/delete.me1
-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.txt2885
-rw-r--r--lib/edit/ab_info.txt103
-rw-r--r--lib/edit/ba_info.txt233
-rw-r--r--lib/edit/between.map71
-rw-r--r--lib/edit/d_info.txt508
-rw-r--r--lib/edit/dragons.map43
-rw-r--r--lib/edit/e_info.txt2206
-rw-r--r--lib/edit/evil.map52
-rw-r--r--lib/edit/f_info.txt936
-rw-r--r--lib/edit/fireprof.map60
-rw-r--r--lib/edit/haunted.map49
-rw-r--r--lib/edit/k_info.txt6271
-rw-r--r--lib/edit/library.map62
-rw-r--r--lib/edit/maeglin.map85
-rw-r--r--lib/edit/misc.txt88
-rw-r--r--lib/edit/nirnaeth.map64
-rw-r--r--lib/edit/numenor.txt80
-rw-r--r--lib/edit/ow_info.txt441
-rw-r--r--lib/edit/p_info.txt1924
-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.txt18974
-rw-r--r--lib/edit/ra_info.txt1923
-rw-r--r--lib/edit/re_info.txt179
-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.txt536
-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.txt73
-rw-r--r--lib/edit/special.txt67
-rw-r--r--lib/edit/spiders.map66
-rw-r--r--lib/edit/st_info.txt723
-rw-r--r--lib/edit/t_bree.txt128
-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.txt216
-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.txt141
-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.txt815
-rw-r--r--lib/edit/trolls.map58
-rw-r--r--lib/edit/v_info.txt2282
-rw-r--r--lib/edit/volcano.txt83
-rw-r--r--lib/edit/w_info.txt120
-rw-r--r--lib/edit/wf_info.txt166
-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/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.txt113
-rw-r--r--lib/help/advanced.hlp14
-rw-r--r--lib/help/attack.txt148
-rw-r--r--lib/help/automat.txt503
-rw-r--r--lib/help/birth.txt592
-rw-r--r--lib/help/bldg.txt59
-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_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.txt1231
-rw-r--r--lib/help/corspoil.txt136
-rw-r--r--lib/help/debug.txt274
-rw-r--r--lib/help/defines.txt616
-rw-r--r--lib/help/dungeon.txt706
-rw-r--r--lib/help/dunspoil.txt173
-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.txt583
-rw-r--r--lib/help/inscrip.txt65
-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.txt2342
-rw-r--r--lib/help/magic.hlp41
-rw-r--r--lib/help/magic.txt142
-rw-r--r--lib/help/newbie.hlp16
-rw-r--r--lib/help/option.txt577
-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.txt532
-rw-r--r--lib/help/spoil_faq.txt73
-rw-r--r--lib/help/spoiler.hlp17
-rw-r--r--lib/help/tome_faq.txt369
-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/CMakeLists.txt1
-rw-r--r--lib/mods/theme/CMakeLists.txt14
-rw-r--r--lib/mods/theme/apex/delete.me1
-rw-r--r--lib/mods/theme/changelog.txt73
-rw-r--r--lib/mods/theme/data/delete.me0
-rw-r--r--lib/mods/theme/dngn/dun1.142
-rw-r--r--lib/mods/theme/dngn/dun1.222
-rw-r--r--lib/mods/theme/dngn/dun10.03
-rw-r--r--lib/mods/theme/dngn/dun11.202
-rw-r--r--lib/mods/theme/dngn/dun11.222
-rw-r--r--lib/mods/theme/dngn/dun17.155
-rw-r--r--lib/mods/theme/dngn/dun18.02
-rw-r--r--lib/mods/theme/dngn/dun18.12
-rw-r--r--lib/mods/theme/dngn/dun19.115
-rw-r--r--lib/mods/theme/dngn/dun2.312
-rw-r--r--lib/mods/theme/dngn/dun20.15
-rw-r--r--lib/mods/theme/dngn/dun22.102
-rw-r--r--lib/mods/theme/dngn/dun22.205
-rw-r--r--lib/mods/theme/dngn/dun22.55
-rw-r--r--lib/mods/theme/dngn/dun24.03
-rw-r--r--lib/mods/theme/dngn/dun29.156
-rw-r--r--lib/mods/theme/dngn/dun3.185
-rw-r--r--lib/mods/theme/dngn/dun3.285
-rw-r--r--lib/mods/theme/dngn/dun3.35
-rw-r--r--lib/mods/theme/dngn/dun36.55
-rw-r--r--lib/mods/theme/dngn/dun39.09
-rw-r--r--lib/mods/theme/dngn/dun4.92
-rw-r--r--lib/mods/theme/dngn/dun40.09
-rw-r--r--lib/mods/theme/dngn/dun5.03
-rw-r--r--lib/mods/theme/dngn/dun5.1414
-rw-r--r--lib/mods/theme/dngn/dun6.03
-rw-r--r--lib/mods/theme/edit/a_info.txt3354
-rw-r--r--lib/mods/theme/edit/ab_info.txt103
-rw-r--r--lib/mods/theme/edit/ba_info.txt260
-rw-r--r--lib/mods/theme/edit/between.map71
-rw-r--r--lib/mods/theme/edit/d_info.txt698
-rw-r--r--lib/mods/theme/edit/dragons.map43
-rw-r--r--lib/mods/theme/edit/e_info.txt3003
-rw-r--r--lib/mods/theme/edit/evil.map52
-rw-r--r--lib/mods/theme/edit/f_info.txt1240
-rw-r--r--lib/mods/theme/edit/fireprof.map60
-rw-r--r--lib/mods/theme/edit/haunted.map49
-rw-r--r--lib/mods/theme/edit/k_info.txt6861
-rw-r--r--lib/mods/theme/edit/library.map62
-rw-r--r--lib/mods/theme/edit/maeglin.map85
-rw-r--r--lib/mods/theme/edit/misc.txt88
-rw-r--r--lib/mods/theme/edit/nirnaeth.map64
-rw-r--r--lib/mods/theme/edit/numenor.txt80
-rw-r--r--lib/mods/theme/edit/ow_info.txt1410
-rw-r--r--lib/mods/theme/edit/p_info.txt2834
-rw-r--r--lib/mods/theme/edit/qrand1.map32
-rw-r--r--lib/mods/theme/edit/qrand10.map36
-rw-r--r--lib/mods/theme/edit/qrand11.map36
-rw-r--r--lib/mods/theme/edit/qrand12.map36
-rw-r--r--lib/mods/theme/edit/qrand14.map37
-rw-r--r--lib/mods/theme/edit/qrand5.map27
-rw-r--r--lib/mods/theme/edit/qrand6.map37
-rw-r--r--lib/mods/theme/edit/qrand7.map35
-rw-r--r--lib/mods/theme/edit/r_info.txt19009
-rw-r--r--lib/mods/theme/edit/ra_info.txt1981
-rw-r--r--lib/mods/theme/edit/re_info.txt179
-rw-r--r--lib/mods/theme/edit/readme.txt96
-rw-r--r--lib/mods/theme/edit/s_bilbo.map58
-rw-r--r--lib/mods/theme/edit/s_bridge.map104
-rw-r--r--lib/mods/theme/edit/s_crypt.map109
-rw-r--r--lib/mods/theme/edit/s_death.map104
-rw-r--r--lib/mods/theme/edit/s_doom.map226
-rw-r--r--lib/mods/theme/edit/s_factory.map238
-rw-r--r--lib/mods/theme/edit/s_gates.map117
-rw-r--r--lib/mods/theme/edit/s_info.txt538
-rw-r--r--lib/mods/theme/edit/s_name.map110
-rw-r--r--lib/mods/theme/edit/s_orc.map109
-rw-r--r--lib/mods/theme/edit/s_orthanc.map99
-rw-r--r--lib/mods/theme/edit/s_ship.map239
-rw-r--r--lib/mods/theme/edit/s_smaug.map78
-rw-r--r--lib/mods/theme/edit/s_thorin.map47
-rw-r--r--lib/mods/theme/edit/set_info.txt250
-rw-r--r--lib/mods/theme/edit/special.txt67
-rw-r--r--lib/mods/theme/edit/spiders.map66
-rw-r--r--lib/mods/theme/edit/st_info.txt1139
-rw-r--r--lib/mods/theme/edit/t_beorn.txt108
-rw-r--r--lib/mods/theme/edit/t_bree.txt137
-rw-r--r--lib/mods/theme/edit/t_cerin.txt98
-rw-r--r--lib/mods/theme/edit/t_d_beorn.txt75
-rw-r--r--lib/mods/theme/edit/t_d_bree.txt91
-rw-r--r--lib/mods/theme/edit/t_d_cerin.txt75
-rw-r--r--lib/mods/theme/edit/t_d_dale.txt75
-rw-r--r--lib/mods/theme/edit/t_d_edoras.txt75
-rw-r--r--lib/mods/theme/edit/t_d_esga.txt75
-rw-r--r--lib/mods/theme/edit/t_d_gond.txt100
-rw-r--r--lib/mods/theme/edit/t_d_helm.txt75
-rw-r--r--lib/mods/theme/edit/t_d_henn.txt75
-rw-r--r--lib/mods/theme/edit/t_d_hobb.txt75
-rw-r--r--lib/mods/theme/edit/t_d_imlad.txt75
-rw-r--r--lib/mods/theme/edit/t_d_khaz.txt79
-rw-r--r--lib/mods/theme/edit/t_d_lori.txt75
-rw-r--r--lib/mods/theme/edit/t_d_mina.txt79
-rw-r--r--lib/mods/theme/edit/t_d_osgili.txt78
-rw-r--r--lib/mods/theme/edit/t_d_pelar.txt78
-rw-r--r--lib/mods/theme/edit/t_d_thrand.txt75
-rw-r--r--lib/mods/theme/edit/t_dale.txt96
-rw-r--r--lib/mods/theme/edit/t_edoras.txt117
-rw-r--r--lib/mods/theme/edit/t_esga.txt105
-rw-r--r--lib/mods/theme/edit/t_gondol.txt216
-rw-r--r--lib/mods/theme/edit/t_helm.txt73
-rw-r--r--lib/mods/theme/edit/t_henn.txt96
-rw-r--r--lib/mods/theme/edit/t_hobb.txt108
-rw-r--r--lib/mods/theme/edit/t_imlad.txt90
-rw-r--r--lib/mods/theme/edit/t_info.txt125
-rw-r--r--lib/mods/theme/edit/t_khazad.txt114
-rw-r--r--lib/mods/theme/edit/t_lorien.txt185
-rw-r--r--lib/mods/theme/edit/t_minas.txt141
-rw-r--r--lib/mods/theme/edit/t_osgili.txt136
-rw-r--r--lib/mods/theme/edit/t_pelar.txt105
-rw-r--r--lib/mods/theme/edit/t_pref.txt145
-rw-r--r--lib/mods/theme/edit/t_thrand.txt103
-rw-r--r--lib/mods/theme/edit/thieves.map64
-rw-r--r--lib/mods/theme/edit/thrain.map35
-rw-r--r--lib/mods/theme/edit/tr_info.txt815
-rw-r--r--lib/mods/theme/edit/trolls.map58
-rw-r--r--lib/mods/theme/edit/v_info.txt2282
-rw-r--r--lib/mods/theme/edit/volcano.txt83
-rw-r--r--lib/mods/theme/edit/w_info.txt141
-rw-r--r--lib/mods/theme/edit/wf_info.txt384
-rw-r--r--lib/mods/theme/edit/wights.map82
-rw-r--r--lib/mods/theme/edit/wolves.map55
-rw-r--r--lib/mods/theme/file/book-0.txt86
-rw-r--r--lib/mods/theme/file/book-1.txt83
-rw-r--r--lib/mods/theme/file/book-10.txt75
-rw-r--r--lib/mods/theme/file/book-101.txt4
-rw-r--r--lib/mods/theme/file/book-102.txt4
-rw-r--r--lib/mods/theme/file/book-103.txt6
-rw-r--r--lib/mods/theme/file/book-104.txt6
-rw-r--r--lib/mods/theme/file/book-105.txt7
-rw-r--r--lib/mods/theme/file/book-106.txt5
-rw-r--r--lib/mods/theme/file/book-107.txt6
-rw-r--r--lib/mods/theme/file/book-11.txt51
-rw-r--r--lib/mods/theme/file/book-12.txt68
-rw-r--r--lib/mods/theme/file/book-13.txt54
-rw-r--r--lib/mods/theme/file/book-14.txt55
-rw-r--r--lib/mods/theme/file/book-15.txt68
-rw-r--r--lib/mods/theme/file/book-16.txt43
-rw-r--r--lib/mods/theme/file/book-17.txt47
-rw-r--r--lib/mods/theme/file/book-18.txt55
-rw-r--r--lib/mods/theme/file/book-19.txt47
-rw-r--r--lib/mods/theme/file/book-2.txt90
-rw-r--r--lib/mods/theme/file/book-20.txt192
-rw-r--r--lib/mods/theme/file/book-200.txt5
-rw-r--r--lib/mods/theme/file/book-201.txt5
-rw-r--r--lib/mods/theme/file/book-202.txt5
-rw-r--r--lib/mods/theme/file/book-203.txt5
-rw-r--r--lib/mods/theme/file/book-204.txt5
-rw-r--r--lib/mods/theme/file/book-205.txt5
-rw-r--r--lib/mods/theme/file/book-206.txt5
-rw-r--r--lib/mods/theme/file/book-207.txt5
-rw-r--r--lib/mods/theme/file/book-208.txt5
-rw-r--r--lib/mods/theme/file/book-209.txt5
-rw-r--r--lib/mods/theme/file/book-210.txt5
-rw-r--r--lib/mods/theme/file/book-211.txt5
-rw-r--r--lib/mods/theme/file/book-212.txt5
-rw-r--r--lib/mods/theme/file/book-213.txt5
-rw-r--r--lib/mods/theme/file/book-214.txt5
-rw-r--r--lib/mods/theme/file/book-215.txt5
-rw-r--r--lib/mods/theme/file/book-216.txt5
-rw-r--r--lib/mods/theme/file/book-22.txt56
-rw-r--r--lib/mods/theme/file/book-23.txt81
-rw-r--r--lib/mods/theme/file/book-24.txt59
-rw-r--r--lib/mods/theme/file/book-25.txt98
-rw-r--r--lib/mods/theme/file/book-26.txt56
-rw-r--r--lib/mods/theme/file/book-27.txt75
-rw-r--r--lib/mods/theme/file/book-28.txt102
-rw-r--r--lib/mods/theme/file/book-29.txt75
-rw-r--r--lib/mods/theme/file/book-30.txt58
-rw-r--r--lib/mods/theme/file/book-31.txt63
-rw-r--r--lib/mods/theme/file/book-32.txt37
-rw-r--r--lib/mods/theme/file/book-33.txt21
-rw-r--r--lib/mods/theme/file/book-4.txt11
-rw-r--r--lib/mods/theme/file/book-6.txt171
-rw-r--r--lib/mods/theme/file/book-7.txt83
-rw-r--r--lib/mods/theme/file/book-8.txt101
-rw-r--r--lib/mods/theme/file/book-9.txt99
-rw-r--r--lib/mods/theme/file/bravado.txt105
-rw-r--r--lib/mods/theme/file/chainswd.txt8
-rw-r--r--lib/mods/theme/file/dam_huge.txt9
-rw-r--r--lib/mods/theme/file/dam_lots.txt21
-rw-r--r--lib/mods/theme/file/dam_med.txt25
-rw-r--r--lib/mods/theme/file/dam_none.txt24
-rw-r--r--lib/mods/theme/file/dam_xxx.txt11
-rw-r--r--lib/mods/theme/file/dead.txt24
-rw-r--r--lib/mods/theme/file/death.txt351
-rw-r--r--lib/mods/theme/file/error.txt67
-rw-r--r--lib/mods/theme/file/mondeath.txt334
-rw-r--r--lib/mods/theme/file/monfear.txt63
-rw-r--r--lib/mods/theme/file/monspeak.txt517
-rw-r--r--lib/mods/theme/file/news.txt23
-rw-r--r--lib/mods/theme/file/news2.txt23
-rw-r--r--lib/mods/theme/file/rart_f.txt86
-rw-r--r--lib/mods/theme/file/rart_s.txt87
-rw-r--r--lib/mods/theme/file/readme!36
-rw-r--r--lib/mods/theme/file/rumors.txt201
-rw-r--r--lib/mods/theme/file/sample.txt5
-rw-r--r--lib/mods/theme/file/sfail.txt34
-rw-r--r--lib/mods/theme/file/silly.txt301
-rw-r--r--lib/mods/theme/file/smeagol.txt29
-rw-r--r--lib/mods/theme/file/smeagolr.txt5
-rw-r--r--lib/mods/theme/file/speakpet.txt53
-rw-r--r--lib/mods/theme/file/timefun.txt92
-rw-r--r--lib/mods/theme/file/timenorm.txt83
-rw-r--r--lib/mods/theme/help/ability.txt113
-rw-r--r--lib/mods/theme/help/advanced.hlp14
-rw-r--r--lib/mods/theme/help/attack.txt148
-rw-r--r--lib/mods/theme/help/automat.txt503
-rw-r--r--lib/mods/theme/help/birth.txt659
-rw-r--r--lib/mods/theme/help/c_archer.txt68
-rw-r--r--lib/mods/theme/help/c_ascet.txt46
-rw-r--r--lib/mods/theme/help/c_assass.txt58
-rw-r--r--lib/mods/theme/help/c_axemas.txt51
-rw-r--r--lib/mods/theme/help/c_bard.txt69
-rw-r--r--lib/mods/theme/help/c_clairv.txt47
-rw-r--r--lib/mods/theme/help/c_demono.txt54
-rw-r--r--lib/mods/theme/help/c_druid.txt55
-rw-r--r--lib/mods/theme/help/c_geoman.txt59
-rw-r--r--lib/mods/theme/help/c_hafted.txt54
-rw-r--r--lib/mods/theme/help/c_lorema.txt54
-rw-r--r--lib/mods/theme/help/c_mage.txt67
-rw-r--r--lib/mods/theme/help/c_mercen.txt49
-rw-r--r--lib/mods/theme/help/c_mimic.txt53
-rw-r--r--lib/mods/theme/help/c_mindcr.txt57
-rw-r--r--lib/mods/theme/help/c_monk.txt87
-rw-r--r--lib/mods/theme/help/c_necro.txt80
-rw-r--r--lib/mods/theme/help/c_pacif.txt10
-rw-r--r--lib/mods/theme/help/c_palad.txt49
-rw-r--r--lib/mods/theme/help/c_peacemag.txt46
-rw-r--r--lib/mods/theme/help/c_polear.txt52
-rw-r--r--lib/mods/theme/help/c_posses.txt70
-rw-r--r--lib/mods/theme/help/c_pr_drk.txt57
-rw-r--r--lib/mods/theme/help/c_pr_eru.txt55
-rw-r--r--lib/mods/theme/help/c_pr_man.txt54
-rw-r--r--lib/mods/theme/help/c_pr_mand.txt49
-rw-r--r--lib/mods/theme/help/c_pr_ulmo.txt50
-rw-r--r--lib/mods/theme/help/c_pr_varda.txt50
-rw-r--r--lib/mods/theme/help/c_priest.txt17
-rw-r--r--lib/mods/theme/help/c_ranger.txt56
-rw-r--r--lib/mods/theme/help/c_rogue.txt62
-rw-r--r--lib/mods/theme/help/c_runecr.txt110
-rw-r--r--lib/mods/theme/help/c_sniper.txt50
-rw-r--r--lib/mods/theme/help/c_sorcer.txt68
-rw-r--r--lib/mods/theme/help/c_stonewr.txt50
-rw-r--r--lib/mods/theme/help/c_summon.txt80
-rw-r--r--lib/mods/theme/help/c_swordm.txt52
-rw-r--r--lib/mods/theme/help/c_symbia.txt68
-rw-r--r--lib/mods/theme/help/c_thaum.txt84
-rw-r--r--lib/mods/theme/help/c_trapper.txt46
-rw-r--r--lib/mods/theme/help/c_unbel.txt65
-rw-r--r--lib/mods/theme/help/c_wainrid.txt49
-rw-r--r--lib/mods/theme/help/c_warper.txt60
-rw-r--r--lib/mods/theme/help/c_warrio.txt54
-rw-r--r--lib/mods/theme/help/command.txt1231
-rw-r--r--lib/mods/theme/help/corspoil.txt136
-rw-r--r--lib/mods/theme/help/debug.txt277
-rw-r--r--lib/mods/theme/help/defines.txt616
-rw-r--r--lib/mods/theme/help/dungeon.txt703
-rw-r--r--lib/mods/theme/help/dunspoil.txt173
-rw-r--r--lib/mods/theme/help/experien.hlp28
-rw-r--r--lib/mods/theme/help/explore.hlp16
-rw-r--r--lib/mods/theme/help/fatespoi.txt28
-rw-r--r--lib/mods/theme/help/foot.aux4
-rw-r--r--lib/mods/theme/help/g_aule.txt61
-rw-r--r--lib/mods/theme/help/g_eru.txt65
-rw-r--r--lib/mods/theme/help/g_mandos.txt56
-rw-r--r--lib/mods/theme/help/g_manwe.txt62
-rw-r--r--lib/mods/theme/help/g_melkor.txt65
-rw-r--r--lib/mods/theme/help/g_tulkas.txt45
-rw-r--r--lib/mods/theme/help/g_ulmo.txt58
-rw-r--r--lib/mods/theme/help/g_varda.txt54
-rw-r--r--lib/mods/theme/help/g_yavann.txt62
-rw-r--r--lib/mods/theme/help/gambling.txt29
-rw-r--r--lib/mods/theme/help/general.txt39
-rw-r--r--lib/mods/theme/help/gods.txt42
-rw-r--r--lib/mods/theme/help/head.aux10
-rw-r--r--lib/mods/theme/help/help.hlp33
-rw-r--r--lib/mods/theme/help/index.txt636
-rw-r--r--lib/mods/theme/help/inscrip.txt65
-rw-r--r--lib/mods/theme/help/luckspoi.txt129
-rw-r--r--lib/mods/theme/help/m_air.txt42
-rw-r--r--lib/mods/theme/help/m_convey.txt71
-rw-r--r--lib/mods/theme/help/m_demono.txt44
-rw-r--r--lib/mods/theme/help/m_divin.txt38
-rw-r--r--lib/mods/theme/help/m_earth.txt35
-rw-r--r--lib/mods/theme/help/m_fire.txt53
-rw-r--r--lib/mods/theme/help/m_geoman.txt75
-rw-r--r--lib/mods/theme/help/m_mana.txt40
-rw-r--r--lib/mods/theme/help/m_meta.txt75
-rw-r--r--lib/mods/theme/help/m_mimic.txt33
-rw-r--r--lib/mods/theme/help/m_mind.txt49
-rw-r--r--lib/mods/theme/help/m_mindcr.txt54
-rw-r--r--lib/mods/theme/help/m_music.txt77
-rw-r--r--lib/mods/theme/help/m_nature.txt54
-rw-r--r--lib/mods/theme/help/m_necrom.txt35
-rw-r--r--lib/mods/theme/help/m_symbio.txt50
-rw-r--r--lib/mods/theme/help/m_tempo.txt42
-rw-r--r--lib/mods/theme/help/m_thaum.txt31
-rw-r--r--lib/mods/theme/help/m_udun.txt35
-rw-r--r--lib/mods/theme/help/m_water.txt34
-rw-r--r--lib/mods/theme/help/macrofaq.txt2346
-rw-r--r--lib/mods/theme/help/magic.hlp41
-rw-r--r--lib/mods/theme/help/magic.txt142
-rw-r--r--lib/mods/theme/help/newbie.hlp16
-rw-r--r--lib/mods/theme/help/option.txt576
-rw-r--r--lib/mods/theme/help/r_beorn.txt33
-rw-r--r--lib/mods/theme/help/r_demon.txt31
-rw-r--r--lib/mods/theme/help/r_dragon.txt33
-rw-r--r--lib/mods/theme/help/r_drkelf.txt33
-rw-r--r--lib/mods/theme/help/r_druadan.txt32
-rw-r--r--lib/mods/theme/help/r_dunad.txt32
-rw-r--r--lib/mods/theme/help/r_dwarf.txt39
-rw-r--r--lib/mods/theme/help/r_eagle.txt32
-rw-r--r--lib/mods/theme/help/r_easterl.txt34
-rw-r--r--lib/mods/theme/help/r_elf.txt32
-rw-r--r--lib/mods/theme/help/r_ent.txt39
-rw-r--r--lib/mods/theme/help/r_gnome.txt36
-rw-r--r--lib/mods/theme/help/r_hafelf.txt31
-rw-r--r--lib/mods/theme/help/r_hafogr.txt31
-rw-r--r--lib/mods/theme/help/r_hielf.txt34
-rw-r--r--lib/mods/theme/help/r_hobbit.txt39
-rw-r--r--lib/mods/theme/help/r_human.txt23
-rw-r--r--lib/mods/theme/help/r_maia.txt20
-rw-r--r--lib/mods/theme/help/r_orc.txt35
-rw-r--r--lib/mods/theme/help/r_pettyd.txt30
-rw-r--r--lib/mods/theme/help/r_rohank.txt33
-rw-r--r--lib/mods/theme/help/r_troll.txt34
-rw-r--r--lib/mods/theme/help/r_wodelf.txt37
-rw-r--r--lib/mods/theme/help/r_yeek.txt30
-rw-r--r--lib/mods/theme/help/rm_adanrog.txt31
-rw-r--r--lib/mods/theme/help/rm_aewrog.txt35
-rw-r--r--lib/mods/theme/help/rm_barb.txt38
-rw-r--r--lib/mods/theme/help/rm_black.txt31
-rw-r--r--lib/mods/theme/help/rm_blue.txt31
-rw-r--r--lib/mods/theme/help/rm_cabrog.txt35
-rw-r--r--lib/mods/theme/help/rm_class.txt14
-rw-r--r--lib/mods/theme/help/rm_drarog.txt34
-rw-r--r--lib/mods/theme/help/rm_ether.txt32
-rw-r--r--lib/mods/theme/help/rm_green.txt30
-rw-r--r--lib/mods/theme/help/rm_herm.txt36
-rw-r--r--lib/mods/theme/help/rm_hurog.txt27
-rw-r--r--lib/mods/theme/help/rm_limrog.txt33
-rw-r--r--lib/mods/theme/help/rm_lsoul.txt26
-rw-r--r--lib/mods/theme/help/rm_lygrog.txt27
-rw-r--r--lib/mods/theme/help/rm_narrog.txt34
-rw-r--r--lib/mods/theme/help/rm_rawrog.txt30
-rw-r--r--lib/mods/theme/help/rm_red.txt30
-rw-r--r--lib/mods/theme/help/rm_sarnrog.txt30
-rw-r--r--lib/mods/theme/help/rm_skel.txt40
-rw-r--r--lib/mods/theme/help/rm_spec.txt44
-rw-r--r--lib/mods/theme/help/rm_vamp.txt32
-rw-r--r--lib/mods/theme/help/rm_white.txt32
-rw-r--r--lib/mods/theme/help/rm_zomb.txt39
-rw-r--r--lib/mods/theme/help/skills.txt532
-rw-r--r--lib/mods/theme/help/spoil_faq.txt63
-rw-r--r--lib/mods/theme/help/spoiler.hlp17
-rw-r--r--lib/mods/theme/help/tome_faq.txt377
-rw-r--r--lib/mods/theme/help/version.txt354
-rw-r--r--lib/mods/theme/help/whattome.txt30
-rw-r--r--lib/mods/theme/help/wishing.txt24
-rw-r--r--lib/mods/theme/note/delete.me1
-rw-r--r--lib/mods/theme/permission.txt3
-rw-r--r--lib/mods/theme/pref/422color.prf909
-rw-r--r--lib/mods/theme/pref/colors.prf53
-rw-r--r--lib/mods/theme/pref/font-ibm.prf365
-rw-r--r--lib/mods/theme/pref/font-mac.prf18
-rw-r--r--lib/mods/theme/pref/font-win.prf304
-rw-r--r--lib/mods/theme/pref/font-x11.prf22
-rw-r--r--lib/mods/theme/pref/font-xxx.prf469
-rw-r--r--lib/mods/theme/pref/font.prf40
-rw-r--r--lib/mods/theme/pref/pref-gcu.prf70
-rw-r--r--lib/mods/theme/pref/pref-mac.prf243
-rw-r--r--lib/mods/theme/pref/pref-sdl.prf144
-rw-r--r--lib/mods/theme/pref/pref-win.prf534
-rw-r--r--lib/mods/theme/pref/pref-x11.prf413
-rw-r--r--lib/mods/theme/pref/pref.prf297
-rw-r--r--lib/mods/theme/pref/trap-xxx.prf428
-rw-r--r--lib/mods/theme/pref/user.prf33
-rw-r--r--lib/mods/theme/pref/xtra-gcu.prf34
-rw-r--r--lib/mods/theme/pref/xtra-xxx.prf137
-rw-r--r--lib/mods/theme/save/delete.me1
-rw-r--r--lib/mods/theme/theme.txt313
-rw-r--r--lib/mods/theme/user/all.prf457
-rw-r--r--lib/mods/theme/user/automat.atm667
-rw-r--r--lib/mods/theme/user/fierce.atm761
-rw-r--r--lib/note/delete.me1
-rw-r--r--lib/patch/delete.me1
-rw-r--r--lib/pref/colors.prf53
-rw-r--r--lib/pref/font-mac.prf18
-rw-r--r--lib/pref/font-win.prf301
-rw-r--r--lib/pref/font-x11.prf22
-rw-r--r--lib/pref/font-xxx.prf469
-rw-r--r--lib/pref/font.prf40
-rw-r--r--lib/pref/pref-gcu.prf70
-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.prf297
-rw-r--r--lib/pref/trap-xxx.prf428
-rw-r--r--lib/pref/user.prf33
-rw-r--r--lib/pref/xtra-gcu.prf34
-rw-r--r--lib/pref/xtra-xxx.prf137
-rw-r--r--lib/save/delete.me1
-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/music/delete.me1
-rw-r--r--src/.gitignore3
-rw-r--r--src/CMakeLists.txt160
-rw-r--r--src/ENGLISH.txt35
-rw-r--r--src/ability_type.hpp27
-rw-r--r--src/ability_type_fwd.hpp3
-rw-r--r--src/activation.hpp13
-rw-r--r--src/alloc_entry.hpp20
-rw-r--r--src/alloc_entry_fwd.hpp3
-rw-r--r--src/angband.h94
-rw-r--r--src/angband.icobin0 -> 766 bytes
-rw-r--r--src/angband.rc111
-rw-r--r--src/artifact_type.hpp60
-rw-r--r--src/artifact_type_fwd.hpp3
-rw-r--r--src/between_exit.hpp18
-rw-r--r--src/birth.cc3724
-rw-r--r--src/birth.h14
-rw-r--r--src/birth.hpp9
-rw-r--r--src/birther.hpp35
-rw-r--r--src/bldg.cc1469
-rw-r--r--src/bldg.hpp9
-rw-r--r--src/body.hpp12
-rw-r--r--src/cave.cc4693
-rw-r--r--src/cave.hpp55
-rw-r--r--src/cave_type.hpp65
-rw-r--r--src/cave_type_fwd.hpp3
-rw-r--r--src/cli_comm.hpp13
-rw-r--r--src/cli_comm_fwd.hpp3
-rw-r--r--src/cmd1.cc4999
-rw-r--r--src/cmd1.hpp25
-rw-r--r--src/cmd2.cc5014
-rw-r--r--src/cmd2.hpp33
-rw-r--r--src/cmd3.cc2110
-rw-r--r--src/cmd3.hpp24
-rw-r--r--src/cmd4.cc4415
-rw-r--r--src/cmd4.hpp28
-rw-r--r--src/cmd5.cc2214
-rw-r--r--src/cmd5.hpp16
-rw-r--r--src/cmd6.cc7928
-rw-r--r--src/cmd6.hpp18
-rw-r--r--src/cmd7.cc4466
-rw-r--r--src/cmd7.hpp28
-rw-r--r--src/config.h131
-rw-r--r--src/corrupt.cc1003
-rw-r--r--src/corrupt.hpp47
-rw-r--r--src/defines.h3879
-rw-r--r--src/deity_type.hpp11
-rw-r--r--src/deity_type_fwd.hpp3
-rw-r--r--src/device_allocation.cc20
-rw-r--r--src/device_allocation.hpp17
-rw-r--r--src/device_allocation_fwd.hpp8
-rw-r--r--src/dice.cc98
-rw-r--r--src/dice.hpp13
-rw-r--r--src/dice_fwd.hpp12
-rw-r--r--src/dungeon.cc5565
-rw-r--r--src/dungeon.h14
-rw-r--r--src/dungeon.hpp6
-rw-r--r--src/dungeon_info_type.hpp72
-rw-r--r--src/dungeon_info_type_fwd.hpp3
-rw-r--r--src/effect_type.hpp17
-rw-r--r--src/ego_item_type.hpp68
-rw-r--r--src/ego_item_type_fwd.hpp3
-rw-r--r--src/fate.hpp22
-rw-r--r--src/feature_type.hpp37
-rw-r--r--src/feature_type_fwd.hpp3
-rw-r--r--src/files.cc5788
-rw-r--r--src/files.h17
-rw-r--r--src/files.hpp27
-rw-r--r--src/flags_group.hpp20
-rw-r--r--src/gen_evol.cc160
-rw-r--r--src/gen_evol.hpp6
-rw-r--r--src/gen_maze.cc294
-rw-r--r--src/gen_maze.hpp5
-rw-r--r--src/generate.cc8698
-rw-r--r--src/generate.hpp12
-rw-r--r--src/gf_name_type.hpp10
-rw-r--r--src/gods.cc212
-rw-r--r--src/gods.hpp13
-rw-r--r--src/h-basic.h27
-rw-r--r--src/h-config.h84
-rw-r--r--src/h-define.h92
-rw-r--r--src/h-system.h90
-rw-r--r--src/h-type.h187
-rw-r--r--src/help.cc742
-rw-r--r--src/help.hpp11
-rw-r--r--src/help_info.hpp17
-rw-r--r--src/hiscore.cc85
-rw-r--r--src/hiscore.hpp83
-rw-r--r--src/hist_type.hpp16
-rw-r--r--src/hist_type_fwd.hpp3
-rw-r--r--src/hook_build_room1_in.hpp8
-rw-r--r--src/hook_calculate_hp_in.hpp7
-rw-r--r--src/hook_calculate_hp_out.hpp7
-rw-r--r--src/hook_chardump_in.hpp7
-rw-r--r--src/hook_chat_in.hpp7
-rw-r--r--src/hook_drop_in.hpp5
-rw-r--r--src/hook_eat_in.hpp7
-rw-r--r--src/hook_eat_out.hpp7
-rw-r--r--src/hook_enter_dungeon_in.hpp7
-rw-r--r--src/hook_get_in.hpp8
-rw-r--r--src/hook_give_in.hpp6
-rw-r--r--src/hook_identify_in.hpp9
-rw-r--r--src/hook_init_quest_in.hpp5
-rw-r--r--src/hook_mon_speak_in.hpp8
-rw-r--r--src/hook_monster_ai_in.hpp9
-rw-r--r--src/hook_monster_ai_out.hpp8
-rw-r--r--src/hook_monster_death_in.hpp7
-rw-r--r--src/hook_move_in.hpp6
-rw-r--r--src/hook_new_monster_end_in.hpp7
-rw-r--r--src/hook_new_monster_in.hpp7
-rw-r--r--src/hook_player_level_in.hpp5
-rw-r--r--src/hook_quest_fail_in.hpp7
-rw-r--r--src/hook_quest_finish_in.hpp7
-rw-r--r--src/hook_stair_in.hpp7
-rw-r--r--src/hook_stair_out.hpp7
-rw-r--r--src/hook_wield_in.hpp7
-rw-r--r--src/hook_wild_gen_in.hpp7
-rw-r--r--src/hooks.cc113
-rw-r--r--src/hooks.hpp10
-rw-r--r--src/identify_mode.hpp3
-rw-r--r--src/include/tome/enum_string_map.hpp55
-rw-r--r--src/include/tome/make_array.hpp13
-rw-r--r--src/include/tome/squelch/automatizer.hpp156
-rw-r--r--src/include/tome/squelch/automatizer_fwd.hpp10
-rw-r--r--src/include/tome/squelch/condition.hpp632
-rw-r--r--src/include/tome/squelch/condition_fwd.hpp15
-rw-r--r--src/include/tome/squelch/condition_metadata.hpp12
-rw-r--r--src/include/tome/squelch/condition_metadata_fwd.hpp14
-rw-r--r--src/include/tome/squelch/cursor.hpp50
-rw-r--r--src/include/tome/squelch/cursor_fwd.hpp10
-rw-r--r--src/include/tome/squelch/object_status.hpp28
-rw-r--r--src/include/tome/squelch/object_status_fwd.hpp12
-rw-r--r--src/include/tome/squelch/rule.hpp162
-rw-r--r--src/include/tome/squelch/rule_fwd.hpp16
-rw-r--r--src/include/tome/squelch/tree_printer.hpp49
-rw-r--r--src/include/tome/squelch/tree_printer_fwd.hpp10
-rw-r--r--src/init1.cc10225
-rw-r--r--src/init1.hpp26
-rw-r--r--src/init2.cc1494
-rw-r--r--src/init2.h14
-rw-r--r--src/init2.hpp9
-rw-r--r--src/inscription_info_type.hpp14
-rw-r--r--src/inventory.hpp35
-rw-r--r--src/joke.cc40
-rw-r--r--src/joke.hpp5
-rw-r--r--src/levels.cc237
-rw-r--r--src/levels.hpp13
-rw-r--r--src/loadsave.cc2975
-rw-r--r--src/loadsave.h16
-rw-r--r--src/loadsave.hpp7
-rw-r--r--src/lua_bind.cc277
-rw-r--r--src/lua_bind.hpp34
-rw-r--r--src/magic_power.hpp15
-rw-r--r--src/main-gcu.c1021
-rw-r--r--src/main-gtk2.c1977
-rw-r--r--src/main-sdl.c2100
-rw-r--r--src/main-win.c3356
-rw-r--r--src/main-x11.c2588
-rw-r--r--src/main.c374
-rw-r--r--src/martial_arts.hpp18
-rw-r--r--src/melee1.cc3048
-rw-r--r--src/melee1.hpp7
-rw-r--r--src/melee2.cc7475
-rw-r--r--src/melee2.hpp12
-rw-r--r--src/messages.cc368
-rw-r--r--src/messages.hpp9
-rw-r--r--src/meta_class_type.hpp10
-rw-r--r--src/meta_class_type_fwd.hpp3
-rw-r--r--src/mimic.cc728
-rw-r--r--src/mimic.hpp10
-rw-r--r--src/module_type.hpp64
-rw-r--r--src/modules.cc1279
-rw-r--r--src/modules.h15
-rw-r--r--src/modules.hpp11
-rw-r--r--src/monster1.cc1887
-rw-r--r--src/monster1.hpp5
-rw-r--r--src/monster2.cc3985
-rw-r--r--src/monster2.hpp52
-rw-r--r--src/monster3.cc722
-rw-r--r--src/monster3.hpp20
-rw-r--r--src/monster_blow.hpp19
-rw-r--r--src/monster_ego.hpp81
-rw-r--r--src/monster_ego_fwd.hpp3
-rw-r--r--src/monster_power.hpp14
-rw-r--r--src/monster_race.hpp116
-rw-r--r--src/monster_race_fwd.hpp3
-rw-r--r--src/monster_type.cc8
-rw-r--r--src/monster_type.hpp98
-rw-r--r--src/monster_type_fwd.hpp3
-rw-r--r--src/move_info_type.hpp15
-rw-r--r--src/music.hpp17
-rw-r--r--src/notes.cc185
-rw-r--r--src/notes.hpp6
-rw-r--r--src/obj_theme.hpp15
-rw-r--r--src/obj_theme_fwd.hpp3
-rw-r--r--src/object1.cc6698
-rw-r--r--src/object1.hpp46
-rw-r--r--src/object2.cc6465
-rw-r--r--src/object2.hpp69
-rw-r--r--src/object_filter.cc98
-rw-r--r--src/object_filter.hpp99
-rw-r--r--src/object_kind.hpp83
-rw-r--r--src/object_kind_fwd.hpp3
-rw-r--r--src/object_type.hpp104
-rw-r--r--src/object_type_fwd.hpp3
-rw-r--r--src/option_type.hpp40
-rw-r--r--src/options.cc89
-rw-r--r--src/options.hpp89
-rw-r--r--src/owner_type.hpp39
-rw-r--r--src/owner_type_fwd.hpp3
-rw-r--r--src/player_class.hpp105
-rw-r--r--src/player_class_fwd.hpp3
-rw-r--r--src/player_defs.hpp6
-rw-r--r--src/player_race.hpp83
-rw-r--r--src/player_race_fwd.hpp3
-rw-r--r--src/player_race_mod.hpp87
-rw-r--r--src/player_race_mod_fwd.hpp3
-rw-r--r--src/player_sex.hpp17
-rw-r--r--src/player_sex_fwd.hpp3
-rw-r--r--src/player_spec.hpp38
-rw-r--r--src/player_spec_fwd.hpp3
-rw-r--r--src/player_type.hpp423
-rw-r--r--src/player_type_fwd.hpp3
-rw-r--r--src/power_type.hpp19
-rw-r--r--src/powers.cc1221
-rw-r--r--src/powers.hpp74
-rw-r--r--src/q_betwen.cc212
-rw-r--r--src/q_betwen.hpp5
-rw-r--r--src/q_bounty.cc170
-rw-r--r--src/q_bounty.hpp8
-rw-r--r--src/q_dragons.cc164
-rw-r--r--src/q_dragons.hpp5
-rw-r--r--src/q_eol.cc226
-rw-r--r--src/q_eol.hpp5
-rw-r--r--src/q_evil.cc132
-rw-r--r--src/q_evil.hpp5
-rw-r--r--src/q_fireprof.cc577
-rw-r--r--src/q_fireprof.hpp7
-rw-r--r--src/q_god.cc1212
-rw-r--r--src/q_god.hpp6
-rw-r--r--src/q_haunted.cc163
-rw-r--r--src/q_haunted.hpp5
-rw-r--r--src/q_hobbit.cc230
-rw-r--r--src/q_hobbit.hpp5
-rw-r--r--src/q_invas.cc222
-rw-r--r--src/q_invas.hpp5
-rw-r--r--src/q_library.cc526
-rw-r--r--src/q_library.hpp8
-rw-r--r--src/q_main.cc210
-rw-r--r--src/q_main.hpp7
-rw-r--r--src/q_narsil.cc122
-rw-r--r--src/q_narsil.hpp5
-rw-r--r--src/q_nazgul.cc145
-rw-r--r--src/q_nazgul.hpp5
-rw-r--r--src/q_nirna.cc125
-rw-r--r--src/q_nirna.hpp5
-rw-r--r--src/q_one.cc378
-rw-r--r--src/q_one.hpp5
-rw-r--r--src/q_poison.cc263
-rw-r--r--src/q_poison.hpp5
-rw-r--r--src/q_rand.cc655
-rw-r--r--src/q_rand.hpp8
-rw-r--r--src/q_shroom.cc311
-rw-r--r--src/q_shroom.hpp5
-rw-r--r--src/q_spider.cc127
-rw-r--r--src/q_spider.hpp5
-rw-r--r--src/q_thief.cc193
-rw-r--r--src/q_thief.hpp5
-rw-r--r--src/q_thrain.cc261
-rw-r--r--src/q_thrain.hpp5
-rw-r--r--src/q_troll.cc194
-rw-r--r--src/q_troll.hpp5
-rw-r--r--src/q_ultrae.cc8
-rw-r--r--src/q_ultrae.hpp5
-rw-r--r--src/q_ultrag.cc292
-rw-r--r--src/q_ultrag.hpp5
-rw-r--r--src/q_wight.cc179
-rw-r--r--src/q_wight.hpp5
-rw-r--r--src/q_wolves.cc146
-rw-r--r--src/q_wolves.hpp5
-rw-r--r--src/quark.cc96
-rw-r--r--src/quark.hpp12
-rw-r--r--src/quest.cc17
-rw-r--r--src/quest.hpp3
-rw-r--r--src/quest_type.hpp27
-rw-r--r--src/randart.cc481
-rw-r--r--src/randart.hpp9
-rw-r--r--src/randart_gen_type.hpp9
-rw-r--r--src/randart_gen_type_fwd.hpp3
-rw-r--r--src/randart_part_type.hpp43
-rw-r--r--src/randart_part_type_fwd.hpp3
-rw-r--r--src/random_artifact.hpp18
-rw-r--r--src/random_quest.hpp10
-rw-r--r--src/random_spell.hpp21
-rw-r--r--src/range.cc11
-rw-r--r--src/range.hpp15
-rw-r--r--src/range_fwd.hpp4
-rw-r--r--src/rule_type.hpp22
-rw-r--r--src/rune_spell.hpp15
-rw-r--r--src/rune_spell_fwd.hpp3
-rw-r--r--src/school_book.hpp15
-rw-r--r--src/school_book_fwd.hpp3
-rw-r--r--src/school_type.hpp21
-rw-r--r--src/school_type_fwd.hpp3
-rw-r--r--src/script.cc30
-rw-r--r--src/script.h12
-rw-r--r--src/set_type.hpp28
-rw-r--r--src/set_type_fwd.hpp3
-rw-r--r--src/skill_type.hpp37
-rw-r--r--src/skill_type_fwd.hpp3
-rw-r--r--src/skills.cc1829
-rw-r--r--src/skills.hpp27
-rw-r--r--src/skills_defs.hpp63
-rw-r--r--src/spell_type.cc433
-rw-r--r--src/spell_type.hpp86
-rw-r--r--src/spell_type_fwd.hpp16
-rw-r--r--src/spells1.cc9275
-rw-r--r--src/spells1.hpp32
-rw-r--r--src/spells2.cc6837
-rw-r--r--src/spells2.hpp115
-rw-r--r--src/spells3.cc4606
-rw-r--r--src/spells3.hpp445
-rw-r--r--src/spells4.cc541
-rw-r--r--src/spells4.hpp42
-rw-r--r--src/spells5.cc2395
-rw-r--r--src/spells5.hpp9
-rw-r--r--src/spells6.cc402
-rw-r--r--src/spells6.hpp7
-rw-r--r--src/squelch/CMakeLists.txt9
-rw-r--r--src/squelch/automatizer.cc278
-rw-r--r--src/squelch/condition.cc1078
-rw-r--r--src/squelch/condition_metadata.cc496
-rw-r--r--src/squelch/cursor.cc96
-rw-r--r--src/squelch/object_status.cc153
-rw-r--r--src/squelch/rule.cc332
-rw-r--r--src/squelch/tree_printer.cc89
-rw-r--r--src/squeltch.cc593
-rw-r--r--src/squeltch.hpp13
-rw-r--r--src/stairs_direction.hpp3
-rw-r--r--src/stats.hpp11
-rw-r--r--src/status.cc783
-rw-r--r--src/status.hpp3
-rw-r--r--src/store.cc3878
-rw-r--r--src/store.hpp12
-rw-r--r--src/store_action_type.hpp17
-rw-r--r--src/store_action_type_fwd.hpp3
-rw-r--r--src/store_info_type.hpp32
-rw-r--r--src/store_info_type_fwd.hpp3
-rw-r--r--src/store_type.hpp43
-rw-r--r--src/store_type_fwd.hpp3
-rw-r--r--src/tables.cc4506
-rw-r--r--src/tables.h12
-rw-r--r--src/tables.hpp82
-rw-r--r--src/tactic_info_type.hpp17
-rw-r--r--src/terrain.hpp19
-rw-r--r--src/timer_type.hpp18
-rw-r--r--src/timer_type_fwd.hpp3
-rw-r--r--src/town_type.hpp21
-rw-r--r--src/town_type_fwd.hpp3
-rw-r--r--src/trap_type.hpp24
-rw-r--r--src/trap_type_fwd.hpp3
-rw-r--r--src/traps.cc3174
-rw-r--r--src/traps.hpp13
-rw-r--r--src/tval_desc.hpp17
-rw-r--r--src/util.cc3685
-rw-r--r--src/util.h22
-rw-r--r--src/util.hpp74
-rw-r--r--src/variable.cc1065
-rw-r--r--src/variable.h39
-rw-r--r--src/variable.hpp308
-rw-r--r--src/vault_type.hpp24
-rw-r--r--src/vault_type_fwd.hpp3
-rw-r--r--src/wild.cc1301
-rw-r--r--src/wild.hpp6
-rw-r--r--src/wilderness_map.hpp15
-rw-r--r--src/wilderness_map_fwd.hpp3
-rw-r--r--src/wilderness_type_info.hpp25
-rw-r--r--src/wilderness_type_info_fwd.hpp3
-rw-r--r--src/wizard1.cc2499
-rw-r--r--src/wizard1.hpp3
-rw-r--r--src/wizard2.cc1868
-rw-r--r--src/wizard2.hpp8
-rw-r--r--src/xtra1.cc4684
-rw-r--r--src/xtra1.hpp24
-rw-r--r--src/xtra2.cc5626
-rw-r--r--src/xtra2.hpp96
-rw-r--r--src/z-form.c678
-rw-r--r--src/z-form.h37
-rw-r--r--src/z-rand.cc376
-rw-r--r--src/z-rand.hpp67
-rw-r--r--src/z-term.c1898
-rw-r--r--src/z-term.h269
-rw-r--r--src/z-util.c108
-rw-r--r--src/z-util.h41
-rw-r--r--tests/get_level_device.cc184
-rw-r--r--tests/harness.cc7
-rw-r--r--tests/lua_get_level.cc135
-rw-r--r--tome.ini94
-rw-r--r--vendor/bandit/.travis.yml17
-rw-r--r--vendor/bandit/.vimrc0
-rw-r--r--vendor/bandit/CMakeLists.txt60
-rw-r--r--vendor/bandit/LICENSE.md21
-rw-r--r--vendor/bandit/README.md63
-rw-r--r--vendor/bandit/bandit/adapters/adapter.h12
-rw-r--r--vendor/bandit/bandit/adapters/adapters.h16
-rw-r--r--vendor/bandit/bandit/adapters/snowhouse.h22
-rw-r--r--vendor/bandit/bandit/assertion_exception.h41
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/BeCloseTo.h55
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/BeEmpty.h32
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/BeFalsy.h39
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/BeGTE.h45
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/BeGreaterThan.h39
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/BeLTE.h45
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/BeLessThan.h39
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/BeNull.h29
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/BeTruthy.h35
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/Contain.h58
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/Equal.h90
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/MatchProxy.h43
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/Matcher.h74
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/MatcherException.h16
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/ThrowException.h60
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/ValueProxy.h26
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/matchers.h19
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/matchers/must.h36
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/CMakeLists.txt49
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/LICENSE_1_0.txt23
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/README.md419
-rwxr-xr-xvendor/bandit/bandit/assertion_frameworks/snowhouse/cross_compile.sh50
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/basic_assertions.cpp228
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/boolean_operators.cpp48
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/container_spec.cpp85
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/custom_matchers_test.cpp69
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/exceptions_tests.cpp97
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/expression_error_handling.cpp28
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/main.cpp43
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/map_tests.cpp38
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/operator_tests.cpp137
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/sequence_container_tests.cpp192
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/string_line_tests.cpp179
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/string_tests.cpp65
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/stringize_tests.cpp111
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/example/tests.h16
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assert.h126
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertionexception.h58
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertmacro.h22
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/constraints.h23
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/containsconstraint.h80
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/endswithconstraint.h53
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalsconstraint.h83
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalscontainerconstraint.h80
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalswithdeltaconstraint.h51
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/andexpression.h46
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression.h38
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression_fwd.h15
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/notexpression.h44
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/orexpression.h46
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/fulfillsconstraint.h51
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/haslengthconstraint.h60
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanconstraint.h55
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanorequaltoconstraint.h55
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanconstraint.h54
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanorequaltoconstraint.h55
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/startswithconstraint.h52
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/exceptions.h120
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintadapter.h39
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintlist.h91
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/expressionbuilder.h357
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/fluent.h38
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/andoperator.h54
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/alloperator.h35
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atleastoperator.h41
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atmostoperator.h39
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionconstraintevaluator.h113
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionoperator.h24
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/exactlyoperator.h39
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/noneoperator.h33
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/constraintoperator.h70
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/invalidexpressionexception.h28
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/notoperator.h53
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/oroperator.h55
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/snowhouse.h33
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringize.h104
-rw-r--r--vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringizers.h60
-rw-r--r--vendor/bandit/bandit/bandit.h42
-rw-r--r--vendor/bandit/bandit/context.h97
-rw-r--r--vendor/bandit/bandit/external/optionparser.h2825
-rw-r--r--vendor/bandit/bandit/failure_formatters/default_failure_formatter.h30
-rw-r--r--vendor/bandit/bandit/failure_formatters/failure_formatter.h13
-rw-r--r--vendor/bandit/bandit/failure_formatters/failure_formatters.h16
-rw-r--r--vendor/bandit/bandit/failure_formatters/visual_studio_failure_formatter.h36
-rw-r--r--vendor/bandit/bandit/grammar.h185
-rw-r--r--vendor/bandit/bandit/listener.h27
-rw-r--r--vendor/bandit/bandit/options.h111
-rw-r--r--vendor/bandit/bandit/registration/registrar.h25
-rw-r--r--vendor/bandit/bandit/registration/registration.h7
-rw-r--r--vendor/bandit/bandit/registration/spec_registry.h17
-rw-r--r--vendor/bandit/bandit/reporters/colorizer.h141
-rw-r--r--vendor/bandit/bandit/reporters/dots_reporter.h69
-rw-r--r--vendor/bandit/bandit/reporters/info_reporter.h194
-rw-r--r--vendor/bandit/bandit/reporters/progress_reporter.h116
-rw-r--r--vendor/bandit/bandit/reporters/reporters.h29
-rw-r--r--vendor/bandit/bandit/reporters/single_line_reporter.h86
-rw-r--r--vendor/bandit/bandit/reporters/spec_reporter.h126
-rw-r--r--vendor/bandit/bandit/reporters/test_run_summary.h90
-rw-r--r--vendor/bandit/bandit/reporters/xunit_reporter.h109
-rw-r--r--vendor/bandit/bandit/run_policies/always_run_policy.h16
-rw-r--r--vendor/bandit/bandit/run_policies/bandit_run_policy.h161
-rw-r--r--vendor/bandit/bandit/run_policies/never_run_policy.h14
-rw-r--r--vendor/bandit/bandit/run_policies/run_policies.h9
-rw-r--r--vendor/bandit/bandit/run_policies/run_policy.h44
-rw-r--r--vendor/bandit/bandit/runner.h103
-rw-r--r--vendor/bandit/bandit/skip_policies/always_include_policy.h16
-rw-r--r--vendor/bandit/bandit/skip_policies/always_skip_policy.h15
-rw-r--r--vendor/bandit/bandit/skip_policies/name_contains_skip_policy.h28
-rw-r--r--vendor/bandit/bandit/skip_policies/skip_policies.h9
-rw-r--r--vendor/bandit/bandit/skip_policies/skip_policy.h29
-rw-r--r--vendor/bandit/bandit/test_run_error.h12
-rw-r--r--vendor/bandit/cmake/cotire.cmake3185
-rwxr-xr-xvendor/bandit/cross_compile.sh43
-rw-r--r--vendor/bandit/specs/before_each_after_each.spec.cpp78
-rw-r--r--vendor/bandit/specs/context.spec.cpp44
-rw-r--r--vendor/bandit/specs/describe.spec.cpp117
-rw-r--r--vendor/bandit/specs/failure_formatters/default_formatter.spec.cpp21
-rw-r--r--vendor/bandit/specs/failure_formatters/visual_studio_failure_formatter.spec.cpp22
-rw-r--r--vendor/bandit/specs/fakes/fake_context.h69
-rw-r--r--vendor/bandit/specs/fakes/fake_reporter.h78
-rw-r--r--vendor/bandit/specs/fakes/fakes.h8
-rw-r--r--vendor/bandit/specs/fakes/logging_fake.h32
-rw-r--r--vendor/bandit/specs/fuzzbox.spec.cpp77
-rw-r--r--vendor/bandit/specs/it.spec.cpp355
-rw-r--r--vendor/bandit/specs/main.cpp6
-rw-r--r--vendor/bandit/specs/matchers/be_close_to.cpp112
-rw-r--r--vendor/bandit/specs/matchers/be_empty.cpp89
-rw-r--r--vendor/bandit/specs/matchers/be_falsy.cpp85
-rw-r--r--vendor/bandit/specs/matchers/be_greater_than.cpp105
-rw-r--r--vendor/bandit/specs/matchers/be_gte.cpp120
-rw-r--r--vendor/bandit/specs/matchers/be_less_than.cpp105
-rw-r--r--vendor/bandit/specs/matchers/be_lte.cpp119
-rw-r--r--vendor/bandit/specs/matchers/be_null.cpp43
-rw-r--r--vendor/bandit/specs/matchers/be_truthy.cpp85
-rw-r--r--vendor/bandit/specs/matchers/contain.cpp156
-rw-r--r--vendor/bandit/specs/matchers/equal.cpp214
-rw-r--r--vendor/bandit/specs/matchers/throw_exception.cpp104
-rw-r--r--vendor/bandit/specs/options.spec.cpp121
-rw-r--r--vendor/bandit/specs/reporters/colorizer.spec.cpp45
-rw-r--r--vendor/bandit/specs/reporters/dots_reporter.spec.cpp202
-rw-r--r--vendor/bandit/specs/reporters/single_line_reporter.spec.cpp201
-rw-r--r--vendor/bandit/specs/reporters/xunit_reporter.spec.cpp161
-rw-r--r--vendor/bandit/specs/run.spec.cpp77
-rw-r--r--vendor/bandit/specs/run_policies/bandit_run_policy.spec.cpp250
-rw-r--r--vendor/bandit/specs/specs.h10
-rw-r--r--vendor/bandit/specs/synopsis.spec.cpp54
-rw-r--r--vendor/bandit/specs/util/argv_helper.h62
-rw-r--r--vendor/bandit/specs/util/util.h6
1277 files changed, 384807 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a052f12e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+.idea
+*.o
+lib*.a
+*.~*
+*.#*
+CMakeFiles
+CMakeCache.txt
+cmake_install.cmake
+install_manifest.txt
+Makefile
+compile_commands.json
+/nbproject
+tome2.cbp
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 00000000..9519785a
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,157 @@
+# Project definition.
+PROJECT (tome2)
+CMAKE_MINIMUM_REQUIRED (VERSION 2.8)
+
+# We want a readable feature summary.
+INCLUDE(FeatureSummary)
+
+# pkg-config support
+INCLUDE(FindPkgConfig)
+
+#
+# Basic common compiler flags.
+#
+SET(COMMON_COMPILER_FLAGS "-pipe -Wall -Wno-unused-value -fsanitize=undefined -fsanitize=address")
+
+#
+# GCC/G++ flags
+#
+IF(CMAKE_COMPILER_IS_GNUCC)
+ # Let's set sensible options.
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_COMPILER_FLAGS}")
+ SET(CMAKE_C_FLAGS_RELEASE "-O2")
+ SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_COMPILER_FLAGS} --std=c++11 -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC")
+ SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
+ SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
+ENDIF()
+
+#
+# Clang flags
+#
+IF("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_COMPILER_FLAGS}")
+ SET(CMAKE_C_FLAGS_RELEASE "-O2")
+ SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
+ENDIF()
+IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_COMPILER_FLAGS} --std=c++11 -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC")
+ SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
+ SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
+ENDIF()
+
+# Add standard math library
+SET(LIBS ${LIBS} m)
+
+#
+# JSON support
+#
+PKG_CHECK_MODULES(JANSSON REQUIRED jansson)
+IF(JANSSON_FOUND)
+ ADD_DEFINITIONS(${JANSSON_CFLAGS})
+ INCLUDE_DIRECTORIES(${JANSSON_INCLUDE_DIRS})
+ LINK_DIRECTORIES(${JANSSON_LIBRARY_DIRS})
+ SET(LIBS ${LIBS} ${JANSSON_LIBRARIES})
+ENDIF()
+
+#
+# BOOST
+#
+FIND_PACKAGE(Boost 1.54.0 REQUIRED COMPONENTS system filesystem)
+
+IF(Boost_FOUND)
+ ADD_DEFINITIONS(-DBOOST_FILESYSTEM_NO_DEPRECATED)
+ INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
+ SET(LIBS ${LIBS} ${Boost_LIBRARIES} ${Boost_SYSTEM_LIBRARY} ${Boost_FILESYSTEM_LIBRARY})
+ENDIF()
+
+#
+# X11 support (OPTIONAL)
+#
+FIND_PACKAGE(X11)
+IF(X11_FOUND)
+ # Add X11 flags/options
+ ADD_DEFINITIONS(-DUSE_X11)
+ INCLUDE_DIRECTORIES(${X11_INCLUDE_DIR})
+ SET(LIBS ${LIBS} ${X11_LIBRARIES})
+ENDIF()
+
+#
+# GTK2 support (OPTIONAL)
+#
+FIND_PACKAGE(GTK2)
+IF(GTK2_FOUND)
+ # Add GTK flags/options
+ ADD_DEFINITIONS(-DUSE_GTK2)
+ INCLUDE_DIRECTORIES(${GTK2_INCLUDE_DIRS})
+ SET(LIBS ${LIBS} ${GTK2_LIBRARIES})
+ENDIF()
+
+#
+# SDL support (OPTIONAL)
+#
+FIND_PACKAGE(SDL)
+IF(SDL_FOUND)
+ # This is a bit roundabout, but we're working around
+ # the FindSDL_* scripts not respecting the REQUIRED
+ # flag.
+ #
+ # the SDL port also requires SDL_image and SDL_ttf
+ FIND_PACKAGE(SDL_image)
+ FIND_PACKAGE(SDL_ttf)
+ IF(SDLIMAGE_FOUND AND SDLTTF_FOUND)
+ # Add SDL flags/options
+ ADD_DEFINITIONS(-DUSE_SDL)
+ INCLUDE_DIRECTORIES(${SDL_INCLUDE_DIR} ${SDLIMAGE_INCLUDE_DIR} ${SDLTTF_INCLUDE_DIR})
+ SET(LIBS ${LIBS} ${SDLIMAGE_LIBRARY} ${SDLTTF_LIBRARY} ${SDL_LIBRARY} m)
+ ELSE()
+ # Let user know that (and why) we haven't enabled SDL.
+ IF(SDLIMAGE_FOUND)
+ MESSAGE(STATUS "Found SDL and SDL_image, but not SDL_ttf!")
+ ELSEIF(SDLTTF_FOUND)
+ MESSAGE(STATUS "Found SDL and SDL_ttf, but not SDL_image!")
+ ELSE()
+ MESSAGE(STATUS "Found SDL, but not SDL_image nor SDL_ttf!")
+ ENDIF()
+ # add info about finding but not enabling SDL
+ SET_FEATURE_INFO(SDL "not enabled")
+ ENDIF()
+ENDIF()
+
+#
+# Curses support (OPTIONAL)
+#
+FIND_PACKAGE(Curses)
+IF(CURSES_FOUND)
+ # Add Curses flags/options
+ ADD_DEFINITIONS(-DUSE_GCU)
+ INCLUDE_DIRECTORIES(${CURSES_INCLUDE_DIR})
+ SET(LIBS ${LIBS} ${CURSES_LIBRARIES})
+ENDIF()
+
+#
+# Windows support
+#
+if(WIN32)
+ # Add Windows flags/options
+ ADD_DEFINITIONS(-DWINDOWS)
+ SET(EXECUTABLE_OPTIONS WIN32)
+ SET(LIBS ${LIBS} winmm wsock32)
+endif(WIN32)
+
+#
+# Set the path for loading the library bits.
+#
+IF(SYSTEM_INSTALL)
+ SET(DEFAULT_PATH "${CMAKE_INSTALL_PREFIX}/lib/tome")
+ELSE()
+ SET(DEFAULT_PATH "./lib")
+ENDIF()
+ADD_DEFINITIONS(-DDEFAULT_PATH="${DEFAULT_PATH}")
+
+# Print out a summary of features.
+PRINT_ENABLED_FEATURES()
+
+# Add the source subdirectory.
+ADD_SUBDIRECTORY (src)
+ADD_SUBDIRECTORY (lib)
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..cdf11c24
--- /dev/null
+++ b/README.md
@@ -0,0 +1,99 @@
+ToME is a [rogue-like](https://en.wikipedia.org/wiki/Roguelike) game.
+
+![Screenshot](/doc/images/screenshot.png)
+
+## Getting Started
+
+### Prerequisites
+
+See below for specific distribution-specific hints, if needed.
+
+You will need to have the following libraries installed on your system
+somewhere where CMake can find them:
+
+- [jansson](http://www.digip.org/jansson/)
+- [Boost](https://www.boost.org/)
+
+Version requirements may vary somewhat, but usually you should be
+aiming for having at least a **recent** version of the above libraries.
+
+### Option 1: Running In-Place
+
+**This is currently the recommended option**, but it means that you
+don't 'install' ToME as such, you just run it from the build
+directory.
+
+To configure for your system, run
+
+ $ cmake .
+ $ make
+
+You should now be able to run
+
+ $ ./src/tome
+
+to start ToME.
+
+**Important:** The current working directory must be at the root of
+the source tree for the above command to run -- if it isn't, then
+you'll get mysterious errors about ToME not being able to find files
+(at best).
+
+
+### Option 2: Installing System-Wide
+
+To configure for your system, run
+
+ $ cmake -DSYSTEM_INSTALL:BOOL=true .
+ $ make
+ $ sudo make install
+
+You can now run ToME from anywhere and it will always use the files
+installed in the system-specific location.
+
+
+## Compiling on Ubuntu
+
+To compile on an Ubuntu install, you'll need at least the
+
+- `cmake`
+- `build-essential`
+- `libjansson-dev`
+- `libboost-all-dev`
+
+packages.
+
+Each frontend requires the additional packages listed below:
+
+- X11: `libx11-dev`
+- SDL: `libsdl-image1.2-dev` `libsdl-ttf2.0-dev`
+- ncurses: `libncurses5-dev`
+
+
+## Compiling on OpenBSD
+
+As of February 2010, the OpenBSD package cmake-2.4.8p2 is too old for
+building ToME. You may need to compile a newer version of CMake.
+
+If you have X11, then a bug in CMake may cause a linker error when
+linking the executable. As a workaround, set the environment variable
+`LDFLAGS` when running CMake. Example:
+
+ $ env LDFLAGS=-L/usr/X11R6/lib cmake .
+ $ make
+
+The SDL frontend also requires these packages:
+
+- `sdl-image`
+- `sdl-ttf`
+
+
+## Compiling on Windows using MinGW
+
+The source **MUST** be unpacked in a directory without spaces in the
+name.
+
+To configure and compile on Windows using MinGW, use the commands
+
+ $ cmake -G "MinGW Makefiles"
+ $ mingw32-make
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/doc/DEBUG.txt b/doc/DEBUG.txt
new file mode 100644
index 00000000..f7146c28
--- /dev/null
+++ b/doc/DEBUG.txt
@@ -0,0 +1,8 @@
+Debugging
+=========
+
+To turn on Debug mode when compiling run cmake with the
+
+ -DCMAKE_BUILD_TYPE=Debug
+
+parameter.
diff --git a/doc/RELEASE.txt b/doc/RELEASE.txt
new file mode 100644
index 00000000..ab082315
--- /dev/null
+++ b/doc/RELEASE.txt
@@ -0,0 +1,8 @@
+Release Checklist:
+==================
+
+* Bump version number.
+* Update changes.txt
+* Update IS_CVS in defines.h before tagging; undo post-tagging.
+* Check that system-wide installation succeeds.
+* Disable C/C++ compiler sanitization options
diff --git a/doc/changes.txt b/doc/changes.txt
new file mode 100644
index 00000000..7835ab9a
--- /dev/null
+++ b/doc/changes.txt
@@ -0,0 +1,208 @@
+T.O.M.E 2.4.x (ah)
+
+Game:
+
+- Removed Alchemist class from ToME module. They were
+ horribly broken and encouraged only scummy play. They
+ were also indirectly responsible for a lot of items
+ that were junk to every other character class.
+- Increased size of the home drastically.
+
+T.o.M.E 2.3.10 (ah)
+
+User Interface:
+
+- Always display list of selectable objects, i.e. remove the option of
+ pressing '*' to hide list.
+- GTK2: Allow running with Shift + arrow keys. (Thanks to Lord
+ Estraven.)
+- SDL fixes. (Thanks to Lord Estraven.)
+- System-wide character scores were removed. Use the ladder at
+ http://angband.oook.cz instead.
+- Panic saves are no longer created. Saving state when memory is
+ likely corrupted seems like a bad idea.
+- Remove long-obsolete front-ends.
+- Unix: Removed pointless and error-prone signal handling aimed at
+ preventing cheating.
+- Removed gamma correction.
+
+Game:
+
+- Killerbunnies: Character dump now lists companions.
+- Killerbunnies: Fix dodge messages.
+- Imported Theme 1.2.0 since this excellent module by Furiosity seems
+ to not be downloadable any more.
+- Further Thaumaturgy tweaks by Lord Estraven
+
+Build:
+
+- Setuid support REMOVED; do NOT install ToME as setuid!
+- Fixes for system installation. Thanks to 'darwin' for reporting.
+- Fix linking problem with the 'curses' front-end.
+- Add DEBUG.txt file for information on enabling debugging in builds.
+
+
+
+T.o.M.E 2.3.9 (ah)
+
+User Interface:
+
+- Always display list of selectable objects immediately
+ instead of requiring user to press '*'.
+- Fix display issues with extremely wide terminals.
+- Automatizer: Fix memory corruption issues.
+- Remove obsolete and pointless options.
+
+Game:
+
+- Items are now immediately pseudo-identified upon pickup.
+- Psycometry now always Identifies regardless of level.
+- Remove the need to instantly leave for a certain quest. Lots
+ of players would get caught out by this.
+- A few Mindcraft powers now scale with skill level. (Credit
+ for these goes to Lord Estraven.)
+- "Far reaching attack" now works for *all* polearms.
+- Fixes and tweaks for Thaumaturgy to make view/area spells less
+ overpowered and to make bolt/ball spells more useful. (Credit
+ goes to Lord Estraven.)
+
+T.o.M.E 2.3.8 (ah)
+
+Game:
+
+- Fix duration display for the Shapeshift Mimicry power. Thanks
+ to morchant for the fix.
+- Fix for creating "inventory" and "equipment" rules from the
+ Automatizer UI. Thanks to morchant for the fix.
+- Fix for Lua code which should hopefully get things working better
+ for OpenBSD users. Thanks to Kernigh for the patch.
+- Change "molten glass wall" to use a different internal code to
+ hopefully avoid clashes with modules such as Theme.
+- Removed the check on low fuel on your light source when traveling.
+ It doesn't make sense since you can already travel without any light
+ equipped.
+
+Build System:
+
+- Added support for building the GTK2 interface; only lightly tested.
+- Added support for building on Windows with MinGW. Thanks to wino45
+ for help with this.
+- Miscellaneous fixes to the CMake files. Thanks to Kernigh for
+ contributing these.
+
+T.o.M.E 2.3.7 (ah)
+
+- Remove item pval from antimagic field strength calculation since it
+ may be both non-zero and invisible (to the player).
+- Miscellaneous 64 bit fixes.
+- Fix Lua errors when hitting <ESC> while choosing spell.
+- Killerbunnies: Automatizer: Add patch which adds new <inventory/>
+ and <equipment/> rules.
+- Killerbunnies: Add "you do not know all your fate" to Fate menu
+ if you haven't been discovered all your fates.
+- Killerbunnies: Display a message if trying to activate Piercing Shots
+ without the necessary skill levels.
+
+T.o.M.E 2.3.6 (ah)
+
+- Don't generate impassable glass walls.
+- Mark *all* quest monsters properly.
+- Avoid generating up staircases in selected dungeons.
+- Mimicry cloaks of Abomination now aggravate properly.
+- Properly handle item set effects with certain traps.
+- Fix crash bug during character dumps.
+- Misc. Mimicry fixes.
+- Prevent immunities from Balrog Form persisting too long.
+- Fix for loading/saving on Linux distribution using Fortify.
+- Fix for module directory paths.
+- Fix miscellaneous problems on 64-bit platforms.
+- Princess room should now always be generated.
+- Extra Blows applies to barehand combat too.
+
+
+
+
+
+
+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/doc/credits.txt b/doc/credits.txt
new file mode 100644
index 00000000..96e8ef60
--- /dev/null
+++ b/doc/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/doc/images/screenshot.png b/doc/images/screenshot.png
new file mode 100644
index 00000000..457c68f5
--- /dev/null
+++ b/doc/images/screenshot.png
Binary files differ
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/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 00000000..15591830
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,19 @@
+INSTALL(DIRECTORY
+ apex
+ cmov
+ data
+ dngn
+ edit
+ file
+ help
+ info
+ note
+ pref
+ save
+ user
+ xtra
+ DESTINATION ${DEFAULT_PATH}
+ PATTERN "delete.me" EXCLUDE
+ )
+
+ADD_SUBDIRECTORY (mods)
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/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/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..2e9c38b2
--- /dev/null
+++ b/lib/edit/a_info.txt
@@ -0,0 +1,2885 @@
+# 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
+
+
+
+# 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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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: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 | EASY_USE
+a:LEBOHAUM
+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: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: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: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: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: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: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: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: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: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: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 | EASY_USE | SHOW_MODS
+a: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: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: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: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: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: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..976c6d03
--- /dev/null
+++ b/lib/edit/ab_info.txt
@@ -0,0 +1,103 @@
+# 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
+
+# Do not forget to update misc.txt with an entry like the following :
+# Maximum number of traits in ab_info.txt
+# M:b:50
+
+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: Combat@30, Dex@17
+I:5
+k:30:Combat
+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: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/ba_info.txt b/lib/edit/ba_info.txt
new file mode 100644
index 00000000..e5c49759
--- /dev/null
+++ b/lib/edit/ba_info.txt
@@ -0,0 +1,233 @@
+# 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
+
+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: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: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
+
+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
+
+#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 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: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..1458cdb7
--- /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:0:0:0:0:0:0:2
+
+# Floor with grass with a bronze thunderlord
+F:z:89:5:958:0:0:0:0:0:0:2
+
+# Floor with dirt with a bronze thunderlord
+F:Z:88:5:958:0:0:0:0:0:0:2
+
+# Floor with dirt with a gold thunderlord
+F:D:88:5:959:0:0:0:0:0:0:2
+
+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..24526ea2
--- /dev/null
+++ b/lib/edit/d_info.txt
@@ -0,0 +1,508 @@
+# 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
+
+### 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 | NO_UP
+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 | NO_UP
+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 | NO_UP
+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:30: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 | NO_UP
+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..f3bc1d31
--- /dev/null
+++ b/lib/edit/e_info.txt
@@ -0,0 +1,2206 @@
+# 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.
+
+
+
+### 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: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: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: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: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: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: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: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: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: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..a2f00914
--- /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:0:0:0:0:0:0:2
+
+# Greater Balrog
+F:B:88:0:807:0:0:0:0:0:0:2
+
+# Pit Fiend
+F:P:88:0:812:0:0:0:0:0:0:2
+
+# 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..5ab0cbdc
--- /dev/null
+++ b/lib/edit/f_info.txt
@@ -0,0 +1,936 @@
+# 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.
+
+
+# 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
+
+# A diggable glass wall.
+N:103:molten glass wall
+G:.:B
+F:NO_WALK | WALL | CAN_PASS | TUNNELABLE | NOTICE
+F:DONT_NOTICE_RUNNING
+D:1:You tunnel into the molten glass wall...
+D:2:a molten glass wall blocking your way
+
+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/fireprof.map b/lib/edit/fireprof.map
new file mode 100644
index 00000000..35f6bec4
--- /dev/null
+++ b/lib/edit/fireprof.map
@@ -0,0 +1,60 @@
+# 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
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..612bdc5c
--- /dev/null
+++ b/lib/edit/k_info.txt
@@ -0,0 +1,6271 @@
+# 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).
+
+
+##### 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: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: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: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: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 | EASY_USE
+F:ACTIVATE | ACTIVATE_NO_WIELD
+a: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: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: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: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: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: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: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: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: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: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: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: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: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: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 potion 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
+
+# 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
+
+
+# Amulet of Nothing
+
+# FIXME: Could remove "of Nothing" (amulets, rings, rods)
+
+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.
+
+# 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: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.
+
+# 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.
+
+# 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.
+
+######### 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
+
+# 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
+
+# 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: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.
+
+# 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/library.map b/lib/edit/library.map
new file mode 100644
index 00000000..2369fbd6
--- /dev/null
+++ b/lib/edit/library.map
@@ -0,0 +1,62 @@
+# 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
diff --git a/lib/edit/maeglin.map b/lib/edit/maeglin.map
new file mode 100644
index 00000000..e3be9972
--- /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:0:0:0:0:0:0:2
+
+# 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:0:0:0:0:0:0:2
+
+# 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..b2471a06
--- /dev/null
+++ b/lib/edit/misc.txt
@@ -0,0 +1,88 @@
+# 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 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..a8c06999
--- /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:0:0:0:0:0:0:2
+
+# Dirt with Cave Troll
+F:b:88:5:496:0:0:0:0:0:0:2
+
+# Dirt with with Eldrak
+F:c:88:1:620:0:0:0:0:0:0:2
+
+# Dirt with with Ettin
+F:e:88:1:621:0:0:0:0:0:0:2
+
+# Dirt with with War troll
+F:f:88:1:631:0:0:0:0:0:0:2
+
+# Dirt with with Hru
+F:g:88:1:709:0:0:0:0:0:0:2
+
+# Dirt with Ulik the Troll
+F:h:88:5:729:0:0:0:0:0:0:2
+
+# Dirt with Ancient green dragon
+F:i:88:5:618:0:0:0:0:0:0:2
+
+# 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..d71dfa26
--- /dev/null
+++ b/lib/edit/ow_info.txt
@@ -0,0 +1,441 @@
+# 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
+
+N:0:Bilbo the Friendly(Hobbit)
+I:20000:120
+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:120
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:2:Otick(Human)
+I:100:120
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:3:Merana(Human)
+I:0:120
+C:200:100:95
+L:Human
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:4:Mirimbar(High-Elf)
+I:0:120
+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:130
+C:120:100:80
+L:Human
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:6:Sultan the Midget(Gnome)
+I:30000:120
+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:120
+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:140
+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:130
+C:120:100:80
+L:Human
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:10:Decado the Handsome(Dunadan)
+I:25000:140
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:11:Wieland the Smith(Dwarf)
+I:30000:140
+C:120:100:80
+L:Gnome | Dwarf | Petty-Dwarf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:12:Arnold the Beastly(Barbarian)
+I:5000:140
+C:120:100:80
+
+N:13:Arndal Beast-Slayer(Half-Elf)
+I:10000:130
+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:140
+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:130
+C:120:100:80
+L:Gnome | Dwarf | Petty-Dwarf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:16:Aragorn(Dunadan)
+I:20000:140
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:17:Sondar(Human)
+I:0:140
+C:120:100:80
+
+N:18:Celebor(Half-Elf)
+I:100:120
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:19:Sharra(Human)
+I:25000:140
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:20:Hjolgar(Barbarian)
+I:5000:140
+C:120:100:80
+#L:Warrior |
+
+N:21:Tanistil(Elf)
+I:5000:140
+C:120:100:80
+#L:Mage | Sorceror | Thaumaturgist
+#H:Warrior |
+
+N:22:Eldore(Human)
+I:5000:140
+C:120:100:80
+#L:Priest
+#H:Necromancer
+
+N:23:Vilios(Human)
+I:5000:140
+C:120:100:80
+#L:Paladin
+#H:Necromancer
+
+N:24:Angros(Elf)
+I:5000:140
+C:120:100:80
+#L:Ranger
+
+N:25:Palano(Thunderlord)
+I:0:140
+C:120:100:80
+L:Thunderlord
+
+N:26:Ludwig the Humble(Dwarf)
+I:5000:130
+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:130
+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:130
+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:130
+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:130
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:31:Wizzle the Chaotic(Hobbit)
+I:10000:130
+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:140
+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:140
+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:140
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:35:Buggerby the Great(Gnome)
+I:20000:140
+C:120:100:80
+L:Gnome | Dwarf | Petty-Dwarf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:36:Inglorian the Mage(Human)
+I:30000:140
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:37:Luthien Starshine(High-Elf)
+I:30000:130
+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:180
+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:180
+C:120:100:80
+#L:Rogue
+H:Gnome | Dwarf | Human | RohanKnight | Elf | Half-Elf | High-Elf
+
+N:40:Zorak the Smart(Dwarf)
+I:30000:180
+C:120:100:80
+#L:Rogue
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:41:Tipo the Fair(Human)
+I:30000:180
+C:120:100:80
+#L:Rogue
+H:Orc | Troll | DeathMold | Half-Ogre | Beorning | Kobold |
+
+
+N:42:Dolaf the Greedy(Human)
+I:10000:130
+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:110
+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:110
+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:110
+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:130
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:47:Esperion the Beastlover(High-Elf)
+I:15000:110
+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:110
+C:120:100:80
+L:Human | Dunadan | RohanKnight
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:49:Tril-akheb the Supreme(Elf)
+I:30000:110
+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:110
+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:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:52:Celeborn(High-Elf)
+I:15000:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:53:Aulendil(Elf)
+I:30000:110
+C:120:100:80
+#L:Warrior |
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:54:Valceronwe(Elf)
+I:30000:110
+C:120:100:80
+#L:Mage | Thaumaturgist | Sorceror
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:55:Voronwe(Elf)
+I:30000:110
+C:120:100:80
+#L:Priest | Paladin
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:56:Celegail(Elf)
+I:30000:110
+C:120:100:80
+#L:Ranger
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:57:Turgon(High-Elf)
+I:30000:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:58:Pengolodh(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:59:Aerandir(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:60:Celebrimbor(High-Elf)
+I:0:110
+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:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf |
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:62:Arlindel(High-Elf)
+I:0:110
+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:110
+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:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf |
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:65:Earendur(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:66:Glorfindel(High-Elf)
+I:0:110
+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:110
+C:120:100:80
+L:High-Elf | Half-Elf
+#L:Paladin
+H:Orc | Troll | Half-Ogre | Beorning | Kobold |
+
+N:68:Kanris(Human)
+I:5000:140
+C:120:100:80
+
+N:69:Barliman Butterbur(Human)
+I:100:120
+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..c02e765a
--- /dev/null
+++ b/lib/edit/p_info.txt
@@ -0,0 +1,1924 @@
+# 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.
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# 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: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
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..5c93f1f6
--- /dev/null
+++ b/lib/edit/r_info.txt
@@ -0,0 +1,18974 @@
+# 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.
+
+##### 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..30ff3f38
--- /dev/null
+++ b/lib/edit/ra_info.txt
@@ -0,0 +1,1923 @@
+# 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
+
+# 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..c1fb01c8
--- /dev/null
+++ b/lib/edit/re_info.txt
@@ -0,0 +1,179 @@
+# 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.
+
+# 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
+
+# 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..d53fb7e0
--- /dev/null
+++ b/lib/edit/s_info.txt
@@ -0,0 +1,536 @@
+# 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
+
+################################## 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
+
+# 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: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..f114b7ef
--- /dev/null
+++ b/lib/edit/set_info.txt
@@ -0,0 +1,73 @@
+# 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.
+
+# N:idx:name
+# D:description
+# P:artifact index:number of item needed:pval
+# F:flags
+
+# 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..146c152c
--- /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:0:0:0:0:0:0:2
+
+# Grass with Elder aranea
+F:e:89:3:964:0:0:0:0:0:0:2
+
+# 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..d0defd31
--- /dev/null
+++ b/lib/edit/st_info.txt
@@ -0,0 +1,723 @@
+# File: st_info.txt
+
+# 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
+
+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:240
+
+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
+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: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_bree.txt b/lib/edit/t_bree.txt
new file mode 100644
index 00000000..3ea29922
--- /dev/null
+++ b/lib/edit/t_bree.txt
@@ -0,0 +1,128 @@
+# 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
+
+# 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#####--------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..51cf4b39
--- /dev/null
+++ b/lib/edit/t_gondol.txt
@@ -0,0 +1,216 @@
+# 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
+
+# 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:####$$$$#############$$$$# #########.........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..9f6ae669
--- /dev/null
+++ b/lib/edit/t_minas.txt
@@ -0,0 +1,141 @@
+# 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
+
+# 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--###-###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..a2cdb442
--- /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 locked door
+F:M:38:22
+
+# Locked 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:0:0:0:0:0:0:2
+
+# Floor with Bandit
+F:b:1:6:150:43:*:0:0:0:0:2
+
+# Floor with novice warrior
+F:c:1:6:43:0:0:0:0:0:0:2
+
+# Floor with novice mage
+F:e:1:6:46:0:0:0:0:0:0:2
+
+# Dark floor with novice warrior
+F:f:1:4:43:0:0:0:0:0:0:2
+
+# 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..8adc41be
--- /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:2
+F:2:1:0:952:0:0:0:0:0:61:2
+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..0643f1ab
--- /dev/null
+++ b/lib/edit/tr_info.txt
@@ -0,0 +1,815 @@
+# 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
+
+#
+# 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:Whoa!
+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..e5d104fd
--- /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:0:0:0:0:0:0:2
+
+# Floor with stone troll
+F:s:89:3:401:0:0:0:0:0:0:2
+
+# Floor with algroth
+F:a:89:3:424:0:0:0:0:0:0:2
+
+# Floor with Bert
+F:b:89:3:493:0:0:0:0:0:0:2
+
+# Floor with Bill
+F:i:89:3:494:0:0:0:0:0:0:2
+
+# 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..964f2b54
--- /dev/null
+++ b/lib/edit/v_info.txt
@@ -0,0 +1,2282 @@
+# 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
+
+### 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 (checkerboard)
+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..1d4b25f9
--- /dev/null
+++ b/lib/edit/wf_info.txt
@@ -0,0 +1,166 @@
+# 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
+
+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..d35c20ee
--- /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:0:0:0:0:0:0:2
+
+# Floor with barrow wight
+F:b:88:3:499:0:0:0:0:0:0:2
+
+# Floor with Emperor Wight
+F:e:88:3:604:0:0:0:0:0:0:2
+
+# 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^^^^^^^^^gb8^XX
+D:X^^^^^^.f^^^^^^^^^^^^.fgb^^XX
+D:X^^^^.ff2^^^^^^.^^^^^^.g^^^XX
+D:X^^^^^3.^^^^^^^f^^^^^^^^^^^XX
+D:X^^^^^^^^^^^^bg.^^^^^^^^^^^XX
+D:X^^^^^^^^^^^7bgf^^^^^^^^f^^XX
+D:X^^^^^^^^^^^^^^^^^^^..^..^^XX
+D:X^^^^^^^^^^^^.^^^^^^...ffg^XX
+D:X^^^^...^^^^^ff^^^^^^.fgb^^XX
+D:X^^^.fff^^^^bgb^^^^..fgbee^^X
+D:X^^^^g5^^^^.g8^^^^^f^fgbe,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..014932f5
--- /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 a 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/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..a6b5cc76
--- /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
+#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..ea656899
--- /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
+#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..17d89dbc
--- /dev/null
+++ b/lib/help/ability.txt
@@ -0,0 +1,113 @@
+|||||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*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
+~~~~~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..8595712b
--- /dev/null
+++ b/lib/help/advanced.hlp
@@ -0,0 +1,14 @@
+|||||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]
+ *****/ddebug.txt*0[(d) Debug commands]
+ *****/eversion.txt*0[(e) 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..0ba56d19
--- /dev/null
+++ b/lib/help/automat.txt
@@ -0,0 +1,503 @@
+|||||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, 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..990b5eba
--- /dev/null
+++ b/lib/help/birth.txt
@@ -0,0 +1,592 @@
+|||||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_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
+
+ 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_archer.txt b/lib/help/c_archer.txt
new file mode 100644
index 00000000..a115e6f2
--- /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_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..ad3b4843
--- /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..04a63fd8
--- /dev/null
+++ b/lib/help/command.txt
@@ -0,0 +1,1231 @@
+|||||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*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*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".
+ 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.
+~~~~~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 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. 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. 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
+ 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).
+~~~~~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..65e7ff91
--- /dev/null
+++ b/lib/help/debug.txt
@@ -0,0 +1,274 @@
+|||||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] P (unused)
+ *****debug.txt*33[q Get a quest] Q (unused)
+ r (unused) *****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.
+~~~~~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.
+~~~~~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/defines.txt b/lib/help/defines.txt
new file mode 100644
index 00000000..147e61a1
--- /dev/null
+++ b/lib/help/defines.txt
@@ -0,0 +1,616 @@
+|||||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 ('!') */
+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
+~~~~~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
+~~~~~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..f719fabc
--- /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
+ & (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 Guldur 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/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..87293d27
--- /dev/null
+++ b/lib/help/index.txt
@@ -0,0 +1,583 @@
+|||||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*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]
+ *****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_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]
+ *****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*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]
+ *****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/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..8194d7fe
--- /dev/null
+++ b/lib/help/macrofaq.txt
@@ -0,0 +1,2342 @@
+|||||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.
+
+#####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----------------------------------------------------------------------
+
+(Content removed because it was obsolete. Inscribe-on-pickup is now the default.)
+
+#####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 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.6 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.7 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
+alert_hitpoint
+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..14fa6570
--- /dev/null
+++ b/lib/help/magic.txt
@@ -0,0 +1,142 @@
+|||||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] *****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].
+
+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..7563f83b
--- /dev/null
+++ b/lib/help/option.txt
@@ -0,0 +1,577 @@
+|||||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.
+
+#####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 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.
+
+#####GItems always sell for 0 gold [no_selling]
+ Disables selling items back to shops for money. The value of gold found in the
+ dungeon is increased to compensate.
+
+~~~~~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 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"]).
+
+#####GAudible bell (on errors, etc) [ring_bell]
+ Attempt to make a "bell" noise when various errors occur.
+
+~~~~~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.
+
+#####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.
+
+#####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.
+
+#####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.
+~~~~~3
+#####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.
+
+#####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.
+
+#####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.
+
+~~~~~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.
+
+#####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..fe68da6e
--- /dev/null
+++ b/lib/help/skills.txt
@@ -0,0 +1,532 @@
+|||||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*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, and Thaumaturgy.
+~~~~~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.
+~~~~~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..996c0d32
--- /dev/null
+++ b/lib/help/spoiler.hlp
@@ -0,0 +1,17 @@
+|||||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]
+ *****/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..171b74d4
--- /dev/null
+++ b/lib/help/tome_faq.txt
@@ -0,0 +1,369 @@
+|||||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.
+
+#####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.
+~~~~~11|Runes
+#####G------------------------------------------------------------------------------
+#####GQ: I keep coming across "runes". What are they?
+
+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.
+
+~~~~~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/CMakeLists.txt b/lib/mods/CMakeLists.txt
new file mode 100644
index 00000000..5cce23ea
--- /dev/null
+++ b/lib/mods/CMakeLists.txt
@@ -0,0 +1 @@
+ADD_SUBDIRECTORY(theme)
diff --git a/lib/mods/theme/CMakeLists.txt b/lib/mods/theme/CMakeLists.txt
new file mode 100644
index 00000000..f1160786
--- /dev/null
+++ b/lib/mods/theme/CMakeLists.txt
@@ -0,0 +1,14 @@
+INSTALL(DIRECTORY
+ apex
+ data
+ dngn
+ edit
+ file
+ help
+ note
+ pref
+ save
+ user
+ DESTINATION ${DEFAULT_PATH}/mods/theme
+ PATTERN "delete.me" EXCLUDE
+ )
diff --git a/lib/mods/theme/apex/delete.me b/lib/mods/theme/apex/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/mods/theme/apex/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/mods/theme/changelog.txt b/lib/mods/theme/changelog.txt
new file mode 100644
index 00000000..3cb3cb45
--- /dev/null
+++ b/lib/mods/theme/changelog.txt
@@ -0,0 +1,73 @@
+=== RECENT VERSION HISTORY (1.1.5 to 1.2.0) ===
+
+1.1.5 AKA "Angles and Corners"
+- Dropped the NOTICE flag from regular sandwalls.
+- Flight is now possible over wooden wilderness structures.
+- The Anduin River lost the CAN_RUN and DONT_NOTICE_RUNNING flags.
+- The Orc Cave now leads you out to the other side of the Misty Mountains.
+- Put an Eagles' nest into Khazad-Dum.
+
+- The Map of Thror no longer lives in the Bree town quest. It is now on a special (branch) level of the Barrow-Downs.
+- The Key of Thorin no longer lives in the Minas Anor town quest. It is now on a special (branch) level of Mirkwood.
+- Reworked the Bridge of Khazad-Dum level to be more nasty and random, played with the layout a bit.
+- Added new level "Orthanc" in the Isengard dungeon, based heavily on the one created by Burb Lulls. There is, however, a canonical twist (or two). >:)
+
+1.1.6 AKA "Curses to This Mirage"
+- Druids (r_info) lost the EVIL flag. These are supposed to be Yavanna's people and they're certainly not evil. The dark elven druids are stil evil.
+- All neutral monsters defined in r_info have been forced into the wilderness.
+- The coaligned spirits now have the GOOD flag. This is mostly to clean up the Mandos piety code (later).
+
+- Fixed the automatic stat gain script to forget stat gain on death, so the savefile can be reused and monster memory retained.
+- Drastically reduced piety gain for quaffing pots of corruption. It's meant to be a fun thematic addition, not a scumming tactic.
+- Aewroeg, Narroeg and Wainriders now start with their stated spellbooks
+
+1.1.7 AKA "Poor Madeleine"
+- Rings and amulets no longer ignore the elements. The rationale for this was that Middle-earth magical items are *really* magical and therefore powerful, but it was unbalancing, not to mention made it a pain destroying objects in the late game.
+- Rings of Lordly Protection have been renamed to 'of Arnor'
+- Pushed Potions of Corruption deeper into the dungeon.
+- Changed names on Aule's and Ulmo's spellbooks to be more impressive-sounding
+- Removed the miscellaneous food items, except for Longbottom leaf, milk and honey.
+- New item types: Ranger's Arrow, Throwing Axe, Buckler, Small Mithril Boomerang, Mithril Boomerang (the latter three adapted from FuryMod)
+
+1.1.8 AKA "Little White Candle"
+- Weapons of Unmagic now have negative to-hit and to-dam modifiers; demonology equipment can no longer get this ego type.
+- Reworked the T-Plus "Rogue" ego type for boots to balance it: reduced the pval, added to_h and to_d penalties and made the stealth and luck bonuses rarer.
+- Boots of Elvenkind no longer guarantee a speed bonus; chance is 50%.
+- Pushed 'of the Maiar' items deeper into the dungeon.
+- Filthy Rags of Leprousness are no more; they're replaced by Armour of Dunharrow (same effects, but can show up on all soft & hard armour plus cloaks)
+- A number of the "bad" ego types got the aggravation flag to offset the benefits of flip-scumming.
+- Drastically reduced magic breath chances on Ethereal robes and cloaks.
+- The 'of Lordliness' ego type is now 'of Arnor'; 'of Might' is now 'of Gondor'; 'of Nothingness' is now 'of Angmar'; boots of speed are now 'of Rohan' and all 'of Stealth' is now 'of Eriador'. Because of the hobbit connection, the 'of Eriador' items now have a 10% chance of a luck bonus equal to the stealth bonus (which remains at 100%).
+- The 'of Arnor' and 'of Gondor' ego types can now show up on all headgear, not just crowns.
+- The 'Sharp' ego has been extended to arrows and crossbow bolts (I added a new index and called them 'Sharpened' to make sure the additional cost is not exorbitant).
+- The 'of Sensitivity' ego type is no longer possible on body armour; there were too many T-lines in e_info.txt.
+- The 'Elemental' ego type on rings is only possible on high-level rings and amulets now; same reason as above.
+- The 'Blazing' ego type is now only possible on crowns, high-level rings and amulets; same reason as above.
+- The 'Radiant' ego type is now only possible on crowns, high-level rings and all amulets (as before); same reason as above.
+- The 'Glowing' and 'Dazzling' ego types are now only possible on crowns and low-level rings and amulets; same reason as above.
+- Rings of the Elements have been removed; one Elemental ego for jewelry is enough.
+- New ego types: Magical for jewelry, cloaks, soft armour and gloves -- makes the item ignore the elements and gives a 50% chance of the ability to store a spell. Magical for missile launchers -- adds a tiny boost to mana; designed with magic-using archer and warrior-types in mind.
+
+1.1.9 AKA "Kit Bag Full of Marbles"
+- Randart magestaves can now gain % to life
+- Randart armour, lights, jewelry and magestaves can now get the ability to store a spell
+- Randart high-level soft armour, hard armour and DSM can gain nether immunity
+- Randart light weapons (no broken ones) can gain sentience
+
+- Humans may no longer be Lost Souls for thematic reasons; the Doom of Men is that their fate after death is unknown to anyone except Eru. This applies to: Edain, Druedain, Rohirrim, Dunedain, Easterlings and Beornings. Also no longer allowed as LostSouls are yeeks, orcs, trolls and half-ogres.
+- Flying creatures (Eagles, Dragons, Aewroeg) now learn to fly high enough to pass mountains at clvl50.
+
+- FF now may also offer the following skills: Nature, Runecraft, Boulder-throwing.
+
+1.2.0 AKA "Twining Light"
+- Ultimate amulet no longer weighs 60lbs. >.>
+- Reworked the Paur* artifact gauntlets to be more thematic, added stat bonuses and sustains. I never liked the fact that it was a no-brainer which of them to keep if you had all four.
+- Fixed some rarity bugs with a number of Theme artifacts.
+- Removed the lesser (Dwarven) rings of power (except the Ring of Durin). They were taking up artifact space and didn't do much. They'll be back, tweaked, in a 3.0.0-compatible Theme.
+- Fixed some lurking bugs in in set_info
+- Reduced rarity on the Cloak of Valinor. It was a little incongruous that it cost far less than Ancanaur and was far more rare than is decent.
+- Gandalf's robe now gives fire immunity
+- Ancanaur is now a shadow blade, not a bluesteel blade.
+- The Golden Horn of the Thunderlords now ignores elements.
+- New artifacts: Erkenbrand's shield, Gimli's axe and shield.
+- New item set: Gimli's Gear (boots, axe, shield) \ No newline at end of file
diff --git a/lib/mods/theme/data/delete.me b/lib/mods/theme/data/delete.me
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/lib/mods/theme/data/delete.me
diff --git a/lib/mods/theme/dngn/dun1.14 b/lib/mods/theme/dngn/dun1.14
new file mode 100644
index 00000000..6421e80b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun1.22 b/lib/mods/theme/dngn/dun1.22
new file mode 100644
index 00000000..cbf78046
--- /dev/null
+++ b/lib/mods/theme/dngn/dun1.22
@@ -0,0 +1,2 @@
+# On this level there is a stairway leading to a trail left by a purposeful dwarf
+B:40 \ No newline at end of file
diff --git a/lib/mods/theme/dngn/dun10.0 b/lib/mods/theme/dngn/dun10.0
new file mode 100644
index 00000000..b89ce05e
--- /dev/null
+++ b/lib/mods/theme/dngn/dun10.0
@@ -0,0 +1,3 @@
+# Father branch is Mirkwood(1), on level 14
+A:1
+L:14
diff --git a/lib/mods/theme/dngn/dun11.20 b/lib/mods/theme/dngn/dun11.20
new file mode 100644
index 00000000..9cc611d3
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun11.22 b/lib/mods/theme/dngn/dun11.22
new file mode 100644
index 00000000..149e2c33
--- /dev/null
+++ b/lib/mods/theme/dngn/dun11.22
@@ -0,0 +1,2 @@
+#I'm downright evil
+F:NO_GENO |
diff --git a/lib/mods/theme/dngn/dun17.15 b/lib/mods/theme/dngn/dun17.15
new file mode 100644
index 00000000..d08bf5e7
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun18.0 b/lib/mods/theme/dngn/dun18.0
new file mode 100644
index 00000000..da1b6c27
--- /dev/null
+++ b/lib/mods/theme/dngn/dun18.0
@@ -0,0 +1,2 @@
+# The level is SAVED in the playername.mz? file
+#S:mz0
diff --git a/lib/mods/theme/dngn/dun18.1 b/lib/mods/theme/dngn/dun18.1
new file mode 100644
index 00000000..70f27718
--- /dev/null
+++ b/lib/mods/theme/dngn/dun18.1
@@ -0,0 +1,2 @@
+# The level is SAVED in the playername.mz? file
+#S:mz1
diff --git a/lib/mods/theme/dngn/dun19.11 b/lib/mods/theme/dngn/dun19.11
new file mode 100644
index 00000000..7fba690d
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun2.31 b/lib/mods/theme/dngn/dun2.31
new file mode 100644
index 00000000..dd8669a5
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun20.1 b/lib/mods/theme/dngn/dun20.1
new file mode 100644
index 00000000..61bc6a65
--- /dev/null
+++ b/lib/mods/theme/dngn/dun20.1
@@ -0,0 +1,5 @@
+U:s_smaug.map
+N:Lower Halls
+D:You can just make out a malevolent red glow at the end of the corridor.
+F:DESC
+F:NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR | ASK_LEAVE \ No newline at end of file
diff --git a/lib/mods/theme/dngn/dun22.10 b/lib/mods/theme/dngn/dun22.10
new file mode 100644
index 00000000..e7eb116e
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun22.20 b/lib/mods/theme/dngn/dun22.20
new file mode 100644
index 00000000..a04a2788
--- /dev/null
+++ b/lib/mods/theme/dngn/dun22.20
@@ -0,0 +1,5 @@
+N:The Bridge
+U:s_bridge.map
+D:You hear the beating of drums in the Deep.
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT \ No newline at end of file
diff --git a/lib/mods/theme/dngn/dun22.5 b/lib/mods/theme/dngn/dun22.5
new file mode 100644
index 00000000..11d8e51f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun24.0 b/lib/mods/theme/dngn/dun24.0
new file mode 100644
index 00000000..bbb93f85
--- /dev/null
+++ b/lib/mods/theme/dngn/dun24.0
@@ -0,0 +1,3 @@
+# Father branch is the Moria(22), on level 10
+A:22
+L:10
diff --git a/lib/mods/theme/dngn/dun29.15 b/lib/mods/theme/dngn/dun29.15
new file mode 100644
index 00000000..4df873b5
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun3.18 b/lib/mods/theme/dngn/dun3.18
new file mode 100644
index 00000000..84c0a74a
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun3.28 b/lib/mods/theme/dngn/dun3.28
new file mode 100644
index 00000000..0acd4193
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun3.3 b/lib/mods/theme/dngn/dun3.3
new file mode 100644
index 00000000..710ef5f8
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun36.5 b/lib/mods/theme/dngn/dun36.5
new file mode 100644
index 00000000..9fddd4dd
--- /dev/null
+++ b/lib/mods/theme/dngn/dun36.5
@@ -0,0 +1,5 @@
+N:Orthanc
+U:s_orthanc.map
+D:#BAnd here you shall stay, foolish adventurer, and die quickly. For I am Saruman the Wise, Saruman the Ring-maker, Saruman of Many Colours!#w
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT \ No newline at end of file
diff --git a/lib/mods/theme/dngn/dun39.0 b/lib/mods/theme/dngn/dun39.0
new file mode 100644
index 00000000..57fa485e
--- /dev/null
+++ b/lib/mods/theme/dngn/dun39.0
@@ -0,0 +1,9 @@
+# Father branch is Barrow-Downs(4), on level 10
+A:4
+L:9
+
+N:Bilbo's trail
+U:s_bilbo.map
+D:#yYou hear someone shout, "I am in a frightful hurry, but Gandalf told me to leave this map for you! Find it, then seek Thorin in Mirkwood!"#w
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT \ No newline at end of file
diff --git a/lib/mods/theme/dngn/dun4.9 b/lib/mods/theme/dngn/dun4.9
new file mode 100644
index 00000000..e09273d6
--- /dev/null
+++ b/lib/mods/theme/dngn/dun4.9
@@ -0,0 +1,2 @@
+# On this level there is a stairway leading to a trail left by a fleeing hobbit
+B:39 \ No newline at end of file
diff --git a/lib/mods/theme/dngn/dun40.0 b/lib/mods/theme/dngn/dun40.0
new file mode 100644
index 00000000..0cd769c4
--- /dev/null
+++ b/lib/mods/theme/dngn/dun40.0
@@ -0,0 +1,9 @@
+# Father branch is Mirkwood(1), on level 33
+A:1
+L:22
+
+N:Thorin's trail
+U:s_thorin.map
+D:#yYou hear someone shout, "My mad kinsmen have taken the key to the Lonely Mountain! Get the key, then find that fool hobbit and my map! If you haven't already."#w
+F:DESC | NO_GENO | NO_NEW_MONSTER | SPECIAL | NO_STAIR
+F:ASK_LEAVE | NO_TELEPORT \ No newline at end of file
diff --git a/lib/mods/theme/dngn/dun5.0 b/lib/mods/theme/dngn/dun5.0
new file mode 100644
index 00000000..48a7bea6
--- /dev/null
+++ b/lib/mods/theme/dngn/dun5.0
@@ -0,0 +1,3 @@
+# Father branch is the Mordor, on level 32
+A:2
+L:31
diff --git a/lib/mods/theme/dngn/dun5.14 b/lib/mods/theme/dngn/dun5.14
new file mode 100644
index 00000000..3d7a3080
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/dngn/dun6.0 b/lib/mods/theme/dngn/dun6.0
new file mode 100644
index 00000000..c750ea67
--- /dev/null
+++ b/lib/mods/theme/dngn/dun6.0
@@ -0,0 +1,3 @@
+# Father branch is Void(11), on level 20
+A:11
+L:20
diff --git a/lib/mods/theme/edit/a_info.txt b/lib/mods/theme/edit/a_info.txt
new file mode 100644
index 00000000..c9f888cc
--- /dev/null
+++ b/lib/mods/theme/edit/a_info.txt
@@ -0,0 +1,3354 @@
+# 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
+# The 'Theme' module for ToME introduces many changes into this file; please
+# refer to changes.txt in the module root folder for details.
+
+
+# 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: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:MAP_LIGHT
+Z:detect curses
+D:The shining Star of the West, a famed heirloom of Elendil's house.
+D:A white diamond set as a star in a silver fillet to be bound at the
+D:forehead.
+
+# 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 | SPECIAL_GENE
+a: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 Annatar (replaces Carlammas in Theme)
+
+N:4:of Annatar
+I:40:10:10
+W:70:30:3:90000
+F:HIDE_TYPE | MANA | LIFE | SPELL | DRAIN_EXP | AGGRAVATE
+F:INSTA_ART
+D:This fiery golden circle once belonged to one who was known to the elves
+D:of Eregion as 'The Lord of Gifts'. Treacherous were his gifts, as was his
+D:teaching. Sauron the Sorcerer lurked behind the guise and his gifts, like
+D:this necklace, were not without cost.
+
+# 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:DISP_EVIL
+D:The ancient heirloom of Ingwe, high lord of the Vanyar, against whom nothing
+D:of evil could stand.
+
+
+# The Necklace 'Nauglamir'
+# Inherits Flare's old activation, since Flare is gone in Theme.
+
+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 | SPECIAL_GENE
+a:DIM_DOOR
+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 Shadow Blade 'Ancanaur' - adapted from Annals of Ea
+
+
+N:7:'Ancanaur'
+I:23:32:3
+W:80:120:45:5000000
+P:0:12d12:10:10:0
+F:CHR | CON | DEX | DRAIN_EXP | FREE_ACT | HEAVY_CURSE | HIDE_TYPE | HOLD_LIFE | IM_FIRE | INT | KILL_DEMON | LEVELS | LITE1 | REGEN | RES_DARK | RES_FEAR | RES_NETHER | RES_NEXUS | SEE_INVIS | SHOW_MODS | SLAY_EVIL | SLAY_UNDEAD | SPEED | STR | WIS
+Z:mind blast
+D:"The Jaws of Fire", this is the sword that Feanor forged in secret
+D:to do battle against his enemies. The sword which threatened Fingolfin,
+D:the sword which in the end did not prevent its owner's untimely death.
+
+# 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: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 Slaughter Axe 'Dramborleg' - Tuor's axe
+# Replaces the Ring of Tulkas in Theme
+# Inherits the activation.
+
+N:9:'Dramborleg'
+I:24:30:5
+W:90:60:300:500000
+P:0:5d7:18:25:0
+F:CRIT | HIDE_TYPE | KILL_DEMON | SHOW_MODS | VORPAL | WOUNDING | ACTIVATE
+a:TULKAS
+D:The great axe of Tuor, Thudder-Sharp is its name. The axe that smote
+D:both a heavy dint as of a club and cleft as a sword. When it was swung
+D:by the hands of Tuor, it sang like the rush of eagle's wings in the
+D:air and took death as it fell. Its name alone instills fear in Balrogs
+D:and other corruptions of Morgoth.
+
+# 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: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: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: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: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.
+
+
+# The Key of Orthanc - replaces Stone of Lore
+# This artifact has a low artifact level to prevent it being worthless for
+# chars with poor magic skills.
+
+N:15:of Orthanc
+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:STONE_LORE
+D:The key to the tower of Saruman, which fills your mind
+D:with images of knowledge and dreadful understanding. It
+D:is not a regular key - it is a perfectly round stone
+D:that is meant to fit into a hole. It can be used to
+D:light your way in the dungeon as well.
+
+# The Multi-Hued Dragon Scale Mail 'Lothronfaun'
+
+N:16:'Lothronfaun'
+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:RAZORBACK
+D:A massive suit of heavy dragon scales deeply saturated with many colours.
+D:It throbs with angry energies. May-cloud it is called, after the
+D:element lightning which courses through it with unusual vigour.
+
+# The Power Dragon Scale Mail of the Sun
+
+N:17:of the Sun
+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:BLADETURNER
+D:A suit of tilkal, 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 Galvorn Plate Mail of Eol
+
+N:19:of Eol
+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:CURE_1000
+D:A suit of imperishable galvorn, with unconquerable strength to endure evil
+D:and disruptive magics. It protects the life force of its wearer like
+D:nothing else can. Eol the Dark Elf made it in secret for his son, but
+D:Maeglin left Nan Elmoth with his mother Aredhel and never got to wear it.
+
+# 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: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: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: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 Mithril Helm of Gil-galad
+# Designed with no-infravision warrior-types in mind.
+# Replaces Marda's coat in Theme
+
+N:26:of Gil-galad
+I:32:8:10
+W:50:80:50:20000
+P:6:1d3:0:0:10
+F:ACTIVATE | HIDE_TYPE | INFRA | LITE2 | LITE3 | SEARCH
+a:SUNLIGHT
+D:The shining helm that Gil-galad, legendary Elven-king, wore in battle.
+
+# The Leather Jerkin of Tom Bombadil
+# Based on a suggestion by Maverick in the forums:
+# Replaces Trone's coat in Theme.
+
+
+N:27:of Tom Bombadil
+I:36:12:1
+W:10:10:70:30000
+P:40:2d4:0:0:0
+F:CHR | CON | DEX | HIDE_TYPE | INT | STR | WIS
+Z:teleport
+D:This garment was once the property of Tom Bombadil -
+D:a strange being rumoured to be older than Arda itself. It
+D:may be the explanation for how Tom could always turn up
+D:when he was most needed.
+
+# 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 Mithril Shield of Thorin
+
+N:30:of Thorin
+I:34:7:4
+W:30:6:65:60000
+P:5: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 shield forged of true-silver is proof against the Element
+D: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: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: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: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: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: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: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 of Valinor
+# Replaces Colluin in Theme
+# *THE* cloak for Barehanders/Mages
+
+N:44:of Valinor
+I:35:1:0
+W:60:90:10:40000
+P:1:0d0:0:0:20
+F:ACTIVATE | IM_ACID | IM_COLD | IM_ELEC | IM_FIRE | RES_POIS
+a:COLLUIN
+D:A cape worn by a hero from Valinor, a land utterly beyond the strife
+D:of the 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: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: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: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: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: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 of Eregion (formerly 'Paurhach')
+
+N:54:of Eregion
+I:31:2:4
+W:10:5:25:33000
+P:2:1d1:0:0:15
+F:RES_FIRE | ACTIVATE |
+F:SUST_STR | STR | REGEN | SPELL_CONTAIN | WIELD_CAST
+a:BO_FIRE_1
+D:A fiery set of gauntlets that can even shoot fire from the user's
+D:hands. Wrought by Curufin's people, they guard the wearer's
+D:strength and enhance it.
+
+# The Set of Gauntlets of Nargothrond (formerly 'Paurnimmen')
+
+N:55:of Nargothrond
+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:BO_COLD_1
+D:A set of handgear so icy as to be able to fire frost bolts. Wrought by
+D:Finrod Felagund himself, it is inscribed with runes that guard and
+D:boost health.
+
+# The Set of Gauntlets of Lorien (formerly 'Pauraegen')
+
+N:56:of Lorien
+I:31:2:4
+W:10:5:25:33000
+P:2:1d1:0:0:15
+F:RES_ELEC | ACTIVATE | FREE_ACT |
+F:SUST_DEX | DEX | SPELL_CONTAIN | WIELD_CAST
+a:BO_ELEC_1
+D:A set of handgear wrought by the Galadhrim. Sparks surround it, enabling
+D:the wearer to fire bolts of electricity. Ever has lightning improved and
+D:protected agility, and these gauntlets are no exception.
+
+# The Set of Gauntlets of Ossiriand (formerly 'Paurnen')
+
+N:57:of Ossiriand
+I:31:2:4
+W:10:5:25:33000
+P:2:1d1:0:0:15
+F:RES_ACID | ACTIVATE |
+F:SUST_CHR | CHR | SPELL_CONTAIN | WIELD_CAST
+a:BO_ACID_1
+D:Mighty was the woodcraft of the Laiquendi, for they learned to control the
+D:element of acid to a level theretofore unknown. This set of handgear is so
+D: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: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: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: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 Broken Dagger 'Angrist'
+
+N:65:'Angrist'
+I:23:1:4
+W:25:80:12:150000
+P:0:2d4:10:15:5
+F:BRAND_ACID | BRAND_POIS | DEX | FREE_ACT | HIDE_TYPE | LUCK | RES_DARK | SEARCH | SEE_INVIS | SHOW_MODS | SLAY_EVIL | SLAY_ORC | SLAY_TROLL | STEALTH | SUST_DEX
+D:The knife Beren Barahir`s son took from Curufin and with which he cut
+D:out a Silmaril from the crown of Morgoth, The blade snapped and broke
+D:when he tried to gain one more and the splint hit Morgoth at his chin.
+D:It was made of Telchar the dwarf from Nogrod and it could cut through
+D:stone. Even broken, it retains power.
+
+# The Dagger of Samwise
+
+N:66:of Samwise
+I:23:4:0
+W:4:100:12:12000
+P:0:1d4:4:6:0
+F:ACTIVATE | BRAND_FIRE | LEVELS | LITE1 | RES_FIRE | SHOW_MODS
+a:BO_FIRE_1
+D:The blade of Samwise Gamgee, chosen by him from the hoard of the
+D:Barrow-wights. A fiery dagger finely balanced for deadly throws.
+
+# The Dagger of Peregrin
+
+N:67:of Peregrin
+I:23:4:0
+W:3:100:12:11000
+P:0:1d4:4:6:0
+F:ACTIVATE | BRAND_COLD | LEVELS | RES_COLD | SHOW_MODS
+a:BO_COLD_1
+D:The blade of Peregrin Took, chosen by him from the hoard of the
+D:Barrow-wights. A frosty dagger finely balanced for deadly throws.
+
+# The Dagger of Meriadoc
+
+N:68:of Meriadoc
+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:BO_ELEC_1
+D:The blade of Meriadoc Brandybuck, chosen by him from the hoard of
+D:the Barrow-wights. A dagger covered in sparks and finely balanced
+D: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: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: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. Inscribed upon the guard in Cirth
+D:is the following --
+D:gûd daedheloth, dam an Glamhoth." - "Turgon King of Gondolin wields, has and
+D:holds the sword Glamdring, foe of Morgoth's realm, hammer to the Din-horde."
+
+# The Quarterstaff of Thranduil [Replaces the 'Aeglin' sword in Theme]
+
+N:74:of Thranduil
+I:21:3:0
+W:20:18:150:50000
+P:0:1d9:10:20:0
+F:BRAND_POIS | COULD2H | RES_POIS | SLAY_ANIMAL | HIDE_TYPE | SHOW_MODS | ESP_ANIMAL
+f:COULD2H
+D:The carven oak staff of the King of the Woodland Realm,
+D:this weapon is a fighter's best friend in a forest.
+
+# 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 'Anglachel'
+
+N:76:'Anglachel'
+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. Its blade once shone brightly, it can cleave
+D:iron as though it is old wood. Beleg Cuthalion chose this sword from the
+D:armoury of Thingol in Doriath. Turin son of Hurin slew his friend Beleg
+D:with it by accident, and since then the blade is black and dull.
+
+# 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 Long Sword 'Herugrim' - sword of Theoden
+# Replaces 'Aglarang' in Theme
+
+N:81:'Herugrim'
+I:23:17:2
+W:60:40:130:300000
+P:0:3d5:8:9:0
+F:ACTIVATE | ESP_EVIL | FREE_ACT | HIDE_TYPE | INT | RES_CONF | RES_DARK | SHOW_MODS | VORPAL
+a:CURE_INSANITY
+D:The ancient blade of Theoden, King of the Mark. It was taken from
+D:the king by Grima the Wormtongue, and hidden from him in a dusty
+D:chest. It was once instrumental in restoring the King's wits from
+D:evil bewitchment.
+
+# 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: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: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. Its pommel glows anew with the Quenya words --
+D:"Anar. Nanye Anduril i ne Narsil i macil Elendilo. Lercuvanten i moli
+D:Mordoreo. Isil." - "Sun. I am Anduril who once was Narsil, sword of Elendil.
+D:The slaves of Mordor shall flee from me. Moon."
+
+# 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 Short Sword 'Sting'
+
+N:88:'Sting'
+I:23:10:2
+W:20:205:75:100000
+P:0:1d7:7:8:0
+F:BLOWS | CON | DEX | ESP_ORC | ESP_SPIDER | ESP_UNDEAD | FREE_ACT |
+F:LEVELS | LITE1 | RES_LITE | SEE_INVIS | SHOW_MODS | SLAY_ANIMAL |
+F:SLAY_EVIL | SLAY_ORC | SLAY_UNDEAD | SPEED | STR
+D:The perfect size for Bilbo, and stamped forever by the courage he found
+D:in Mirkwood, this sturdy little blade grants the wearer combat prowess
+D:and survival abilities they did not know they had. The blade is inscribed
+D:with Tengwar writing -- "Sting is my name, I am the spiders' bane."
+
+# 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 'Daedheloth'
+
+N:91:'Daedheloth'
+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 'Sereghathol'
+
+N:92:'Sereghathol'
+I:23:17:2
+W:50:30:150:250000
+P:0:5d5:32:32:0
+F:DEX | FREE_ACT | LEVELS | REGEN | SEE_INVIS | SLAY_EVIL | SLOW_DIGEST | SPEED | STR | VORPAL | WOUNDING
+D:Blood-Sword it is called, after its thirst for the blood of foes.
+
+# The Beaked Axe of Dain Ironfoot
+
+N:93:of Dain Ironfoot
+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: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 'Aiglos'
+
+N:97:'Aiglos'
+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: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.
+D:The spear is inscribed with Tengwar runes, reading "Gil-galad ech vae
+D:vaegannen matha, / Aith heleg nin i orch gostatha; / Nin cíniel na
+D:nguruthos / Hon ess nín istatha --
+D:well-made spear, / The Orc will fear my point of ice; / When he sees me,
+D:in fear of death / he will know my name
+
+# The Spear of Caradhras
+
+N:98:of Caradhras
+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 | FEATHER | ESP_GIANT |
+F:SEE_INVIS | ACTIVATE | BLESSED | SHOW_MODS
+a:STONE_MUD
+D:A magical spear, rumoured to have been forged by Orome himself
+D:in the coldest reach of the cruel Redhorn.
+
+# 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: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: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 Broad Sword 'Guthwine' - sword of Eomer
+# Replaces the Trident of Wrath in Theme
+
+N:107:'Guthwine'
+I:23:16:1
+W:30:10:150:45000
+P:0:2d6:6:7:0
+F:CRIT | ESP_ORC | HIDE_TYPE | LITE1 | RES_BLIND | RES_FEAR |
+F:SHOW_MODS | SLAY_ORC | WOUNDING
+D:The sword of Eomer, son of Eomund, leader of the Riders of the Mark.
+D:As one flashed this sword with Anduril during a battle long ago.
+D:Legendary are its powers in the rallying of desperate troops.
+
+# The Trident of Osse
+
+N:108:of Osse
+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:TELE_AWAY
+D:The awesome weapon imbued with some of the power of the Vala Ulmo,
+D:Lord of Waters. It allows the wearer to laugh in scorn at the dread
+D:powers of the undead, and be 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: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 Dernhelm - from T-Plus by Ingeborg S. Norden
+# Replaces the Long Sword of the Dawn in Theme
+
+N:110:of Dernhelm
+I:23:17:5
+W:70:50:150:250000
+P:0:3d5:20:25:0
+F:BLESSED | ESP_UNIQUE | FREE_ACT | HIDE_TYPE | HOLD_LIFE | KILL_UNDEAD |
+F:LITE1 | LUCK | RES_DISEN | RES_FEAR | RES_MORGUL | SEE_INVIS |
+F:SHOW_MODS | SLAY_EVIL | SPEED | STEALTH | VORPAL | WOUNDING | SPECIAL_GENE
+D:Eomer's sister Eowyn once wielded this shining sword when she
+D:battled the Witch-King of Angmar, hiding her true identity to
+D:join the battle. Others are less likely to notice the wielder
+D:immediately. Eowyn's sword also allowed her to move swiftly,
+D:sense powerful enemies, inflict terrible wounds, and partly
+D:withstand the worst attacks of the Nazgul.
+
+# 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: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: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 'Maegnas-in-sereg'
+
+N:114:'Maegnas-in-sereg'
+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 'Naurgil'
+
+N:115:'Naurgil'
+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: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: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 Gamil Zirak
+
+N:117:of Gamil Zirak
+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:Gamil Zirak was a great craftsman, the master of Telchar of Nogrod.
+D:This weapon was rescued from Thingol's treasury, and it is rumoured
+D:that its wielder need not fear dragons or other fell beings of the
+D:Dark.
+
+# 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: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: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 'Nguruthos'
+
+N:121:'Nguruthos'
+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. It is named 'Fear of Death' - you wield the
+D:Fear of Dragons and the Despair 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: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 | HEAVY_CURSE |
+F:CURSED | AGGRAVATE | BRAND_FIRE | SLAY_ANIMAL | SLAY_DEMON |
+F:RES_FIRE | ESP_SPIDER | VORPAL | RES_LITE | LITE1 | REGEN |
+F:ESP_DEMON | WOUNDING | 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: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 Manwe
+# The ULTIMATE "weapon" for a mage
+
+N:127:of Manwe
+I:6:1:12
+W:127:220:20:9000000
+P:0:1d4:-19:-19:0
+F:INT | CHR | WIS | MANA | SPELL | ACTIVATE | LUCK |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD | 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: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 | IM_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 Small Metal Shield of Gimli
+
+N:132:of Gimli
+I:34:3:3
+W:40:3:65:80000
+P:3:0d0:9:10:40
+F:INT | CON | RES_FEAR | HIDE_TYPE | IM_FIRE | RES_SHARDS
+Z:find secret passages
+D:A gift from the King of Rohan to Gimli the Dwarf, this shield
+D:combines the cunning and stamina of Gimli Elf-friend, Gimli the
+D:Lock-bearer.
+
+# The Bearded Axe of Gimli
+
+N:133:of Gimli
+I:24:3:2
+W:35:60:35:65000
+P:0:1d6:5:15:0
+F:STR | BLOWS | LEVELS | SLAY_ORC | FREE_ACT | VORPAL |
+F:ESP_UNDEAD | ESP_NONLIVING | COULD2H
+f:COULD2H
+D:"Gimli sensed the Dead behind following, but he continued on
+D:after Aragorn." The trusty axe of Gimli son of Gloin, one of
+D:the Nine Walkers of old.
+
+# 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 Harp of Thorin
+
+N:135:of Thorin
+I:14:59:2
+W:50:10:30:40000
+P:0:1d1:0:0:0
+F:ACTIVATE | CHR | INFRA | INSTA_ART | LUCK | STEALTH | SUST_CHR | TUNNEL
+a:CHARM_OTHERS
+Z:remove fear
+D:This magical instrument once belonged to Thorin Oakenshield,
+D:a mighty dwarf warrior of old. The sounds emanating from it
+D:once gave a frightened hobbit a glimpse of beauty and helped
+D:allay his fears, and to this day the harp preserves its magical
+D:powers to encourage when all seems horribly wrong.
+
+# The Mithril Helm of Thorin
+
+N:136:of Thorin
+I:32:8:3
+W:40:5:20:55000
+P:10:2d4:0:0:0
+F:ACTIVATE | DEX | HIDE_TYPE | HOLD_LIFE | REGEN | RES_FEAR | SUST_CON | SUST_DEX | SUST_STR
+a:TERROR
+D:Mighty was Thorin Oakenshield as he emerged from the Gate of the
+D:Lonely Mountain on the day of the Battle of the Five Armies, clad
+D:in shining armour, part of which was this helm. He gleamed like
+D:gold in the dying fire of the day, red light leapt from his
+D:eyes, and his foes were terrified at the mere sight of him.
+
+# 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
+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
+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
+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:5:0:0
+F:STR | TUNNEL | SUST_STR | HIDE_TYPE | LITE1 | ACTIVATE | CLIMB
+F:RES_CHAOS | RES_LITE | RES_DARK
+a: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
+a: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
+a: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
+a: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
+a:BOROMIR
+D:The great horn made of the horns of kine of Araw. It is inlaid with silver
+D:and gold signs; when blown, it can be heard for miles over. The horn gives
+D:courage and endurance to its wearer, provided that secrecy is not desired.
+D:"Loud and clear it sounds in the valleys of the hills... and then let all
+D: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: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 Tulkas
+# The ULTIMATE weapon for a Swordmaster class
+
+N:147:of Tulkas
+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: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:2:0d0:0:0:0
+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: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 Orome
+# The ULTIMATE crossbow for a crossbow-master
+
+N:152:of Orome
+I:19:24:5
+W:127:220:130:8000000
+P:0:0d0:36:28:0
+F:CON | DEX | ESP_EVIL | ESP_ORC | ESP_TROLL | FLY | FREE_ACT | IM_ELEC | INFRA | INVIS | LUCK | NO_MAGIC | PRECOGNITION | REFLECT | RES_BLIND | RES_CHAOS | RES_CONF | RES_DISEN | SEE_INVIS | SLOW_DIGEST | SPECIAL_GENE | SPEED | STEALTH | SUST_CHR | SUST_CON | SUST_DEX | SUST_INT | SUST_STR | SUST_WIS | ULTIMATE | XTRA_MIGHT | XTRA_SHOTS
+D:A crossbow handcrafted by Aule for the Huntsman of the Valar during
+D:the first pursuit of Melkor Bauglir.
+
+# 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 'Lhugdagnir'
+
+N:154:'Lhugdagnir'
+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 'Cam-tal-crist'
+
+N:155:'Cam-tal-crist'
+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: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: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 'Lavandagnir'
+
+N:158:'Lavandagnir'
+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: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 Helcar
+
+N:159:of Helcar
+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: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 ###
+
+# The Catapult Trap Set of the Edain
+
+N:161:of the Edain
+I:46:3: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 of the Noegyth Nibin
+
+N:162:of the Noegyth Nibin
+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 of the Naugrim
+
+N:163:of the Naugrim
+I:46:2: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:15000
+P:0:3d2:6:10:0
+F:BLESSED | DEX | HIDE_TYPE | RES_FIRE | SLAY_ORC | SLAY_TROLL | STR
+D:These are the shards of the mighty blade of Isildur, which deprived
+D:the dark lord Sauron of The One Ring of Power. Legend has it that
+D:the sword that was broken shall be reforged. You can barely make out
+D:a Tengwar inscription on the pommel, reading "Narsil essenya, macil
+D:meletya; Telchar carneron Navarotesse." - "Narsil is my name, a mighty
+D:sword; Telchar made me in Nogrod."
+
+# The Chain Mail of Peregrin Took
+# From T-Plus by Ingeborg S. Norden
+
+N:165:of Peregrin Took
+I:37:4:2
+W:20:3:220:32000
+P:14:1d4:3:5:11
+F:ACTIVATE | ESP_TROLL | HIDE_TYPE | LITE1 | REGEN | RES_FEAR | SHOW_MODS | STR | SUST_STR
+a:GORLIM
+D:This sturdy mail shirt was a gift from the nobility of Gondor to the halfling
+D:Peregrin Took. It enables a warrior to fight more capably and cling to life when
+D:others would be killed. It also reveals enemies (especially trolls) hiding in the
+D:dark, and terrifies anyone who threatens the wearer.
+
+# The Balance Dragon Scale Mail 'Loknare'
+
+N:166:'Loknare'
+I:38:20: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: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. Loknare means Dragonblaze.
+
+# 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: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: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: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: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: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 | ACTIVATE |
+F:RES_FIRE | RES_ELEC | RES_NETHER | RES_DISEN | HOLD_LIFE |
+F:COULD2H
+f:COULD2H
+a: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: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 'Dolcrist'
+
+N:177:'Dolcrist'
+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 | ACTIVATE |
+F:RES_NEXUS | RES_BLIND | RES_SOUND |
+F:KILL_DRAGON | SLAY_ANIMAL | BRAND_POIS | BRAND_ELEC |
+F:COULD2H
+f:COULD2H
+a:SKULLCLEAVER
+D:This mighty bludgeon brings destruction to all around it, and is the
+D:bane of dragons and magic. Skull-cleaver (or head-cleaver) it is called.
+
+# 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 |
+F:ACTIVATE | LUCK | SPELL_CONTAIN | WIELD_CAST
+a: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 Elven cloak of Peregrin Took
+# From T-Plus by Ingeborg S. Norden
+
+N:184:of Peregrin Took
+I:35:2:3
+W:10:10:10:12500
+P:4:0d0:0:0:16
+F:ACTIVATE | CHR | DEX | HIDE_TYPE | INVIS | LUCK | SEARCH | STEALTH
+a:CURE_HUNGER
+D:This simple-looking cloak, dyed in hues that blend into the woodlands, was a gift
+D:from the elves of Lothlorien to the halfling Peregrin Took. Its wearer has an
+D:uncanny knack for making friends, escaping bonds, moving among enemies completely
+D:unseen--and finding food.
+
+# The Harp of Tom Bombadil
+# From T-Plus by Ingeborg S. Norden
+
+N:185:of Tom Bombadil
+I:14:59:4
+W:80:60:20:150000
+P:0:3d4:0:0:0
+F:ACTIVATE | BLESSED | CHR | ESP_UNDEAD | FREE_ACT |
+F:HIDE_TYPE | HOLD_LIFE | LIFE | LUCK | RES_FEAR |
+F:RES_SOUND | SEARCH | SEE_INVIS | STEALTH | SUST_CHR
+a:PROT_EVIL
+D:This small, serviceable wooden harp once belonged to Tom Bombadil--
+D:a mysterious figure whose song prevented the Wight-King from
+D:attacking Frodo and his companions. Its music still inspires
+D:boldness and strengthens the life force of all who play it.
+
+# The Quarterstaff of Radagast
+# From T-Plus by Ingeborg S. Norden
+
+N:186:of Radagast
+I:21:3:2
+W:100:120:150:180000
+P:0:2d9:10:13:0
+F:ACTIVATE | BLESSED | CHR | ESP_ANIMAL | ESP_EVIL |
+F:HIDE_TYPE | INFRA | INT | MANA | RES_COLD | RES_ELEC |
+F:RES_FIRE | RES_MORGUL | SEARCH | SEE_INVIS | SHOW_MODS |
+F:SLAY_ANIMAL | SLAY_EVIL | SPELL | SPELL_CONTAIN |
+F:STEALTH | WATER_BREATH | WIELD_CAST | WIS
+a:RADAGAST
+Z:grow trees
+D:Rumoured to be a gift from Yavanna to Radagast the Brown, this
+D:plain-seeming oak staff shields its bearer against the elements
+D:and helps him fight off malicious beasts (such as the goblins'
+D:Warg-steeds). It also grants exceptional insight into Nature
+D:magic, and can even cause trees or healing herbs to spring forth
+D:at the bearer's command.
+
+# The Horn 'Valaroma'
+# From T-Plus by Ingeborg S. Norden
+
+N:187:'Valaroma'
+I:14:60:4
+W:80:60:15:90000
+P:0:3d4:0:0:0
+F:ACTIVATE | BLESSED | CON | ESP_ANIMAL | ESP_EVIL |
+F:FREE_ACT | HIDE_TYPE | LUCK | RES_CONF | RES_FEAR |
+F:RES_SOUND | SEARCH | SEE_INVIS | STEALTH
+a:VALAROMA
+D:This heavenly instrument, wrought from gleaming silver, most
+D:often appears in the hands of the Valarin huntsman Orome; yet
+D:he may lend mortal champions the horn from time to time. Its
+D:music inspires courage and clarity of mind in pure-hearted
+D:beings, but drives evil ones far away.
+
+# The Cloak 'Menelcol'
+# From T-Plus by Ingeborg S. Norden
+
+N:188:'Menelcol'
+I:35:1:5
+W:70:70:10:27500
+P:1:0d0:0:0:25
+F:ACTIVATE | CHR | FLY | HIDE_TYPE | IM_ELEC | INFRA |
+F:LITE1 | MAGIC_BREATH | RES_COLD | RES_DARK | RES_LITE |
+F:SEARCH | SEE_INVIS | SUST_CHR
+a:GILGALAD
+D:This deep-blue velvet cloak, embroidered with silvery stars, sheds a
+D:celestial light that reveals hidden things and bestows unearthly
+D:beauty. It also wards off damage from elements of the skies, and
+D:allows the wearer to fly unscathed even through airless space.
+
+# The Filthy Rag of Ghan-buri-Ghan
+# Suggested by ShinesmanOffWhite in the forums
+
+N:189:of Ghan-buri-Ghan
+I:36:1:2
+W:25:10:20:30000
+P:2:0d0:0:0:32
+F:CON | HIDE_TYPE | LIFE | RES_COLD | RES_FIRE |
+F:RES_POIS | RES_SOUND | STR | SUST_CON |
+F:SUST_STR | SUST_WIS | WIS
+Z:poison dart
+D:The wrappings of a leader among the wild Men of Druadan
+D:forest. It contains a multitude of tiny pockets filled
+D:with small darts dripping with venom.
+
+# The Cloak of Ghan-buri-Ghan
+# Suggested by ShinesmanOffWhite in the forums
+
+N:190:of Ghan-buri-Ghan
+I:35:1:5
+W:30:15:10:25000
+P:1:0d0:5:5:13
+F:DEX | HIDE_TYPE | INVIS | SPEED | STEALTH | SUST_DEX
+Z:berserk
+D:This dark-coloured cloak once belonged to the leader of the
+D:Druedain. As you put it on, you feel safer from attempts to
+D:waylay you on your travels.
+
+# The Black Banner of Gondor
+# Suggested by ShinesmanOffWhite in the forums
+
+N:191:of Gondor
+I:39:108:-3
+W:60:30:50:50000
+P:0:0d0:0:0:0
+F:ACTIVATE | ESP_UNDEAD | HIDE_TYPE | INSTA_ART | INVIS |
+F:LITE1 | RES_CHAOS | RES_DARK | RES_NETHER | STEALTH
+a:SUMMON_UNDEAD
+D:A large banner of pure black, strangely gleaming with a dark light
+D:that is faint and at the same time so bright it attracts attention.
+
+# The Mage Staff of Saruman
+# Suggested by ShinesmanOffWhite in the forums
+
+N:192:of Saruman
+I:6:1:4
+W:60:80:12:60000
+P:0:1d4:5:5:0
+F:ACTIVATE | HIDE_TYPE | INT | MANA | SHOW_MODS | SPEED |
+F:SPELL | SPELL_CONTAIN | WIELD_CAST
+a:BA_COLD_1
+Z:weigh magic
+D:A white quarterstaff that faintly gleams a pale white, it once belonged
+D:to one of the most powerful beings on Middle-Earth, Saruman the White.
+D:Saruman fell into shadow, and the staff left him during his travels. As
+D:you wield it, you become much more attuned to magic.
+
+# The Robe of Curunir
+# Suggested by ShinesmanOffWhite in the forums
+
+N:193:of Curunir
+I:36:2:10
+W:60:80:20:2000000
+P:2:0d0:-40:-40:36
+F:ACTIVATE | HIDE_TYPE | IM_COLD | IM_ELEC | SPEED | SPELL_CONTAIN |
+F:SUST_CHR | SUST_CON | SUST_DEX | SUST_INT | SUST_STR | WIELD_CAST
+a:BA_ELEC_2
+D:The white robe of the Istari wizard Curunir, known on Middle-earth as
+D:Saruman the White and Saruman of Many Colours. Imbued with cold and
+D:lightning used in the creation of the Fire of Orthanc, it grants the
+D:wearer some mastery over these elements.
+
+# The Light Crossbow of Brand
+# Adapted from Hengband
+
+N:194:of Brand
+I:19:23:10
+W:30:10:110:40000
+P:0:0d0:5:7:0
+F:ACTIVATE | HIDE_TYPE | RES_COLD | SHOW_MODS | STEALTH
+a:RUNE_EXPLO
+D:The bow of Brand, last King of Dale. It was given to him
+D:as a gift by the King under the Mountain of Erebor, and
+D:has access to an especially secret realm of Dwarven lore.
+
+# The Pearl 'Nimphelos'
+# Adapted from Annals of Ea by Feanor
+
+N:195:'Nimphelos'
+I:39:109:1
+W:20:10:10:40000
+P:0:1d1:0:0:0
+F:HIDE_TYPE | INFRA | INSTA_ART | LITE3 | SEARCH
+Z:dazzle
+D:It was a gift from the Falas-Elves to the Naugrim who built Menegroth,
+D:the abode of Thingol and Melian. It shines like starlight on the waves
+D:of the sea.
+
+# The Silmaril of Flames
+# Adapted from Annals of Ea by Feanor
+
+N:196:of Flames
+I:39:110:2
+W:75:90:200:100000
+P:0:10d10:0:0:0
+F:ACTIVATE | AUTO_CURSE | CURSED | DRAIN_EXP | DRAIN_MANA |
+F:ESP_EVIL | HEAVY_CURSE | HIDE_TYPE | INSTA_ART | LITE2 |
+F:LITE3 | RES_CHAOS | RES_DARK | RES_FIRE | RES_LITE |
+F:RES_NETHER | RES_NEXUS | RES_SHARDS | SEE_INVIS | STR
+a:INVULN
+Z:banish evil
+D:A Silmaril of Feanor. It shines with the unquenchable light of the
+D:White Trees of Valinor. It was stolen from the camp of Eonwe by
+D:Maedhros the Tall, whose evil deeds made him unable to hold the
+D:shining jewel. Desperate and anguished, he cast himself into a
+D:fiery chasm, along with the jewel. Yet the jewel seems to have
+D:survived. Forever did the Oath of Feanor bind this jewel to the fate
+D:of Middle-earth.
+
+# The Silmaril of the Seas
+# Adapted from Annals of Ea by Feanor
+
+N:197:of the Seas
+I:39:111:2
+W:75:90:200:100000
+P:0:10d10:0:0:0
+F:ACTIVATE | AUTO_CURSE | CURSED | DRAIN_EXP | DRAIN_HP | ESP_EVIL |
+F:HEAVY_CURSE | HIDE_TYPE | INSTA_ART | INT | LITE2 | LITE3 |
+F:RES_CHAOS | RES_DARK | RES_LITE | RES_NETHER | RES_NEXUS | SEE_INVIS
+a:RECALL
+Z:banish evil
+D:A Silmaril of Feanor. It shines with the unquenchable light of the
+D:White Trees of Valinor. It was stolen from the camp of Eonwe by
+D:Maglor, yet he soon discovered that the light of the Valar cannot
+D:abide in the hands of evildoers. Thus he could not bear to hold it,
+D:and he cast it into the sea. Thereafter he wandered ever upon the
+D:shores singing in pain, regret and sorrow. Forever did the Oath of
+D:Feanor bind this jewel to the fate of Middle-earth.
+
+# The Sceptre of Numenor
+
+N:198:of Numenor
+I:12:2:-3
+W:50:40:24:80000
+P:0:0d0:0:0:0
+F:ACTIVATE | AUTO_CURSE | CHR | CON | CURSED | HEAVY_CURSE |
+F:INSTA_ART | LUCK | SPEED | SPELL_CONTAIN | STR | WIELD_CAST | WRAITH
+a:DISP_GOOD
+D:The chief mark of royalty in Westernesse, it is said to have perished
+D:in the fall of Numenor. It once belonged to Ar-Pharazon the Golden and
+D:the evilness of that fallen King still courses through it.
+
+# The Rod of Annuminas
+
+N:199:of Annuminas
+I:12:3:4
+W:60:50:24:80000
+P:0:0d0:0:0:0
+F:ACTIVATE | BLESSED | CHR | FREE_ACT | INSTA_ART | LUCK |
+F:RES_CONF | SPELL_CONTAIN | WIELD_CAST
+a:RUNE_PROT
+D:The chief mark of royalty in Arnor, possibly the oldest remaining work
+D:of Men. It came from Numenor, but it belonged to the Lords of Andunie
+D:of whom the first was Valandil son of Silmarien. It was passed down to
+D:Elendil and thus survived the downfall of Numenor.
+# The Feanorian Lamp of Taniquetil
+# From T-Plus by Ingeborg S. Norden
+
+N:200:of Taniquetil
+I:39:4:2
+W:75:60:200:100000
+P:0:1d1:0:0:0
+F:ACTIVATE | BLESSED | ESP_ALL | HIDE_TYPE | INFRA | INT |
+F:LITE1 | LITE2 | LITE3 | RES_BLIND | RES_LITE | SEARCH |
+F:SEE_INVIS | WATER_BREATH | WIS
+a:PALANTIR
+Z:detect curses
+D:This holy lamp once belonged to a seeress who warned Ar-Pharazon of
+D:the dire fate Numenor would suffer for daring to break the ban against
+D:mortals' entering the Blessed Realms alive. The king had the seeress
+D:executed and discarded her possessions--but rumors persist that a source
+D:of pure vision, untainted by Sauron's darkness, lies hidden away
+D:somewhere on Arda.
+
+# 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: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 'Fuin'
+
+N:203:'Fuin'
+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 'Coimir' (formerly 'Toris Mejistos')
+# From T-Plus by Ingeborg S. Norden
+
+N:204:'Coimir'
+I:40:18:2
+W:80:120:3:120000
+P:0:0d0:0:0:0
+F:FREE_ACT | HIDE_TYPE | HOLD_LIFE | INSTA_ART | LIFE | LITE1 | LUCK | REGEN | RES_NETHER | SLOW_DIGEST | SPECIAL_GENE | SPELL_CONTAIN | SUST_CON | SUST_DEX | SUST_STR | WATER_BREATH | WIELD_CAST
+Z:restore life
+D:Called 'Life-jewel' by the Vanyar of old, this flawless sapphire
+D:pendant bears potent runes that preserve body and soul.
+
+# 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: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: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
+
+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: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.
+
+### More new artifacts in Theme ###
+
+# The Map of Thror
+
+N:209:of Thror
+I:8:33:0
+W:70:100:30:1
+P:0:0d0:0:0:0
+F:INSTA_ART | SPECIAL_GENE
+D:A map made by Thror, the King under the Mountain. It shows the Lonely
+D:Mountain of Erebor, the Running River, and the lands about devastated by
+D:Smaug. It also contains mention of a secret entrance to the Mountain.
+D:This map and a key are all you need to enter a special cavern in Erebor.
+
+# The Key of Thorin
+
+N:210:of Thorin
+I:11:13:0
+W:70:100:5:1
+P:0:0d0:0:0:0
+F:INSTA_ART | SPECIAL_GENE
+D:A small silver key, given to Thorin Oakenshield by Gandalf the Grey,
+D:who got it from Thrain, Thorin's father, in Dol Guldur. It opens a
+D:magical gate to a special cavern in the Lonely Mountain - you will
+D:also need a map to show you the gate's precise location.
+
+# The Cup of Thror
+
+N:211:of Thror
+I:11:14:0
+W:70:100:50:100000
+P:0:0d0:0:0:0
+F:ACTIVATE | ACTIVATE_NO_WIELD | HIDE_TYPE | INSTA_ART | SPECIAL_GENE
+a:ALCHEMY
+D:It was made for Thror, King under the Mountain. It is a huge golden bowl
+D:with two handles, hammered and carved, with birds and flowers whose eyes
+D:and leaves are made in jewels.
+
+# The Red Arrow of Gondor
+
+N:212:of Gondor
+I:11:15:0
+W:40:70:2:70000
+P:0:0d0:0:0:0
+F:ACTIVATE | ACTIVATE_NO_WIELD | HIDE_TYPE | INSTA_ART
+a:BOROMIR
+D:The summons of Gondor, an arrow sent as a symbol of desperate need from
+D:Gondor to its northern allies, the Rohirrim. The tradition dates back to
+D:the time of Borondir, who rode north from Gondor to summon aid from the
+D:ancestors of the Rohirrim, the Eotheod. The Red Arrow was only sent north
+D:in the most perilous of circumstances.
+
+# ToME artifacts (new since 2.3.x) #
+
+# 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
+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 |
+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:50:400:0
+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
+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
+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.
+
+# Theme artifacts continued #
+
+# The Robe of Belegaer
+# Based on Robe of the Seven Seas from T-Plus by Ingeborg S. Norden
+
+N:219:of Belegaer
+I:36:2:5
+W:100:200:20:1500000
+P:2:0d0:0:0:20
+F:FREE_ACT | HIDE_TYPE | IM_ACID | IM_COLD | IM_ELEC | LITE1 | LUCK | RES_BLIND | RES_DARK | SEARCH | STEALTH | WATER_BREATH
+D:This pearl-trimmed blue robe was created by a Maia loremaster in
+D:Ulmo's service. No ocean storm can harm its wearer or anything
+D:he carries; the pressure and darkness of deep water cannot hinder
+D:him, either.
+
+# The Necklace of Girion
+
+N:220:of Girion
+I:40:4:5
+W:20:5:2:15000
+P:0:1d1:0:0:10
+F:ACTIVATE | CHR | INSTA_ART | RES_NEXUS | RES_POIS | SEE_INVIS | SUST_CHR
+a:GROW_MOLD
+D:A necklace of emeralds, green as the grass. It once belonged to Girion,
+D:King of Dale, and was given to the Dwarves of the Lonely Mountain as
+D:payment for a mithril mail shirt for Girion's son. It seems to have
+D:overgrown with a strange moss over the years.
+
+# The Silver Bolt 'Dailir' - the 'dart' of Beleg Cuthalion, based on Heart's Blood
+
+N:221:'Dailir'
+I:18:3:5
+W:85:40:3:35000
+P:0:8d5:15:20:0
+F:CRIT | HIDE_TYPE | SHOW_MODS | VORPAL | WOUNDING
+D:The beloved dart of Beleg Cuthalion. It never failed to be found
+D:unharmed, until it broke when Beleg fell upon it while he was
+D:carrying Turin Turambar away from an Orc-camp, the night Beleg
+D:met his end. Turin remade the bolt and kept it to his dying day in
+D:memory of his friend.
+
+# The Amulet of Faramir [adapted from Oangband]
+# The equivalent of the Phial (not too rare) for a stealth bonus and a source of RES_CONF early
+
+N:222:of Faramir
+I:40:29:5
+W:10:2:2:10000
+P:0:0d0:18:0:8
+F:HIDE_TYPE | INSTA_ART | RES_CONF | SHOW_MODS | STEALTH | SUST_DEX
+D:A slim neckpiece of True-silver, with quiet spells of Ithilien to aid and
+D:protect the wearer.
+
+# The Large Mithril Shield of Earendil
+# Adapted from Oangband; its chief benefits are the +to_ac and the activation
+
+N:223:of Earendil
+I:34:8:0
+W:95:80:100:90000
+P:20:1d1:0:0:80
+F:ACTIVATE | RES_BLIND | RES_DARK | RES_ELEC | RES_FIRE | RES_NETHER
+a:BLADETURNER
+D:A shining shield, once borne by the great mariner Earendil, "scored with
+D:runes to keep all wounds and harm from him".
+
+# The Long Bow of Legolas
+
+N:224:of Legolas
+I:19:13:3
+W:40:20:40:30000
+P:0:0d0:17:19:0
+F:XTRA_MIGHT | XTRA_SHOTS | FREE_ACT | HIDE_TYPE | ESP_ORC |
+F:STEALTH | INFRA | DEX
+D:The great bow of Legolas, one of the Nine Walkers of old.
+D:Handcrafted specially for Thranduil's son in Lothlorien,
+D:this bow gives clarity of sight and agility to the wielder.
+
+# The Large Metal Shield of Erkenbrand
+
+N:225:of Erkenbrand
+I:34:5:3
+W:40:9:120:200000
+P:0:0d0:0:0:30
+F:REFLECT | STR | CHR | SUST_DEX | HIDE_TYPE | ESP_ORC |
+F:FREE_ACT | REGEN
+D:Tall and strong stood Erkenbrand, Lord of the Westfold, as he rode
+D:to combat the forces of Isengard. The valour of Helm Hammerhand lived
+D:again in him. This shield is painted red according to Rohan custom
+D:and it grants magical protection against enemy projectiles, as well
+D:as lets the wearer sense approaching enemy hordes.
+
+# XXX 226 Room for another artifact here XXX #
+# XXX 227 Room for another artifact here XXX #
+# XXX 228 Room for another artifact here XXX #
+# XXX 229 Room for another artifact here XXX #
+# XXX 230 Room for another artifact here XXX #
+# XXX 231 Room for another artifact here XXX #
+# XXX 232 Room for another artifact here XXX #
+# XXX 233 Room for another artifact here XXX #
+# XXX 234 Room for another artifact here XXX #
+# XXX 235 Room for another artifact here XXX #
+# XXX 236 Room for another artifact here XXX #
+# XXX 237 Room for another artifact here XXX #
+# XXX 238 Room for another artifact here XXX #
+# XXX 239 Room for another artifact here XXX #
+# XXX 240 Room for another artifact here XXX #
+
+### New Ultimate items added in Theme ###
+
+# The Lucerne Hammer of the Eruchin (Haftedmasters/Eru warriors)
+
+N:241:of the Eruchin
+I:21:10:10
+W:127:220:130:9000000
+P:0:5d6:21:26:50
+F:ACTIVATE | BLESSED | BRAND_COLD | BRAND_ELEC | BRAND_FIRE |
+F:CHR | CON | FREE_ACT | IM_COLD | LIFE | LITE1 | LUCK |
+F:NO_MAGIC | PRECOGNITION | REGEN | RES_DARK | RES_FIRE |
+F:SEE_INVIS | SHOW_MODS | SLAY_DEMON | SLAY_EVIL | SLAY_TROLL |
+F:SLAY_UNDEAD | SLOW_DIGEST | SPECIAL_GENE | SUST_CHR | SUST_CON |
+F:SUST_DEX | SUST_INT | SUST_STR | SUST_WIS | ULTIMATE | VORPAL
+a:ERU
+D:A weapon forged by Aule himself and blessed by Eru Iluvatar,
+D:calling upon the strength of all his children (hence the name,
+D:which means Children of Eru). It is Eru Iluvatar's gift to you,
+D:made for a single purpose
+
+# The Trident of Ulmo (Polearmmasters)
+
+N:242:of Ulmo
+I:22:5:10
+W:127:220:130:9000000
+P:0:5d6:21:26:50
+F:ACTIVATE | BLESSED | BRAND_COLD | BRAND_ELEC | BRAND_FIRE |
+F:CHR | CON | FREE_ACT | IM_COLD | LIFE | LITE1 | LUCK |
+F:NO_MAGIC | PRECOGNITION | REGEN | RES_DARK | RES_FIRE |
+F:SEE_INVIS | SHOW_MODS | SLAY_DEMON | SLAY_EVIL | SLAY_TROLL |
+F:SLAY_UNDEAD | SLOW_DIGEST | SPECIAL_GENE | SUST_CHR | SUST_CON |
+F:SUST_DEX | SUST_INT | SUST_STR | SUST_WIS | ULTIMATE | VORPAL
+a:ERU
+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 Broad Axe of Aule (Axemasters)
+
+N:243:of Aule
+I:24:11:10
+W:127:220:130:9000000
+P:0:5d6:21:26:50
+F:ACTIVATE | BLESSED | BRAND_COLD | BRAND_ELEC | BRAND_FIRE |
+F:CHR | CON | FREE_ACT | IM_COLD | LIFE | LITE1 | LUCK |
+F:NO_MAGIC | PRECOGNITION | REGEN | RES_DARK | RES_FIRE |
+F:SEE_INVIS | SHOW_MODS | SLAY_DEMON | SLAY_EVIL | SLAY_TROLL |
+F:SLAY_UNDEAD | SLOW_DIGEST | SPECIAL_GENE | SUST_CHR | SUST_CON |
+F:SUST_DEX | SUST_INT | SUST_STR | SUST_WIS | ULTIMATE | VORPAL
+a:ERU
+D:Aule's mighty weapon, granted to you to aid the defeat of Melkor.
+
+# The Rapier of Vaire (Rogues, whose Critical-hits skill is only useful with lighter swords)
+
+N:244:of Vaire
+I:23:7:10
+W:127:220:130:9000000
+P:0:5d6:21:26:50
+F:ACTIVATE | BLESSED | BRAND_COLD | BRAND_ELEC | BRAND_FIRE | CHR |
+F:CON | FREE_ACT | IM_COLD | LIFE | LITE1 | LUCK | NO_MAGIC |
+F:PRECOGNITION | REGEN | RES_DARK | RES_FIRE | SEE_INVIS | SHOW_MODS |
+F:SLAY_DEMON | SLAY_EVIL | SLAY_TROLL | SLAY_UNDEAD | SLOW_DIGEST |
+F:SPECIAL_GENE | SUST_CHR | SUST_CON | SUST_DEX | SUST_INT | SUST_STR |
+F:SUST_WIS | ULTIMATE | VORPAL
+a:ERU
+D:A shining rapier used by Vaire to cut the strings of time when
+D:dire need arises. Its power with that of the Flame Imperishable
+D:will let you destroy Melkor forever.
+
+# The Long Bow of Irmo (Bow-masters)
+
+N:245:of Irmo
+I:19:13:5
+W:127:220:130:8000000
+P:0:0d0:36:28:0
+F:CON | DEX | ESP_EVIL | ESP_ORC | ESP_TROLL | FLY | FREE_ACT |
+F:IM_ELEC | INFRA | INVIS | LUCK | NO_MAGIC | PRECOGNITION |
+F:REFLECT | RES_BLIND | RES_CHAOS | RES_CONF | RES_DISEN |
+F:SEE_INVIS | SLOW_DIGEST | SPECIAL_GENE | SPEED | STEALTH |
+F:SUST_CHR | SUST_CON | SUST_DEX | SUST_INT | SUST_STR |
+F:SUST_WIS | ULTIMATE | XTRA_MIGHT | XTRA_SHOTS
+D:A gift from Orome to Irmo, a mighty bow that sings the songs of the
+D:garden of Lorien. It will guide your hand and steady your grip in
+D:your encounter with Melkor.
+
+# The Sling of Nessa (Sling-masters)
+
+N:246:of Nessa
+I:19:2:5
+W:127:220:130:8000000
+P:0:0d0:36:28:0
+F:CON | DEX | ESP_EVIL | ESP_ORC | ESP_TROLL | FLY | FREE_ACT |
+F:IM_ELEC | INFRA | INVIS | LUCK | NO_MAGIC | PRECOGNITION |
+F:REFLECT | RES_BLIND | RES_CHAOS | RES_CONF | RES_DISEN |
+F:SEE_INVIS | SLOW_DIGEST | SPECIAL_GENE | SPEED | STEALTH |
+F:SUST_CHR | SUST_CON | SUST_DEX | SUST_INT | SUST_STR | SUST_WIS |
+F:ULTIMATE | XTRA_MIGHT | XTRA_SHOTS
+D:This sling was made for the most lithe of the Valier to provide
+D:her with amusement as she used it to shoot magical pebbles of
+D:cheer and charm while dancing in the lush green fields of Valinor.
+D:She grants it to you now in your time of trial.
+
+# The Metal Boomerang of Varda (Boomerang-masters)
+
+N:247:of Varda
+I:15:4:5
+W:127:220:130:8000000
+P:0:0d0:36:28:0
+F:CON | DEX | ESP_EVIL | ESP_ORC | ESP_TROLL | FLY | FREE_ACT |
+F:IM_ELEC | INFRA | INVIS | LUCK | NO_MAGIC | PRECOGNITION |
+F:REFLECT | RES_BLIND | RES_CHAOS | RES_CONF | RES_DISEN |
+F:SEE_INVIS | SLOW_DIGEST | SPECIAL_GENE | SPEED | STEALTH |
+F:SUST_CHR | SUST_CON | SUST_DEX | SUST_INT | SUST_STR | SUST_WIS |
+F:ULTIMATE | XTRA_MIGHT | XTRA_SHOTS
+D:Made from living starlight, this boomerang will dispel the darkest
+D:of darkness. It is a gift from Varda Elentari, to aid you in your
+D:task.
+
+# The Amulet of Mandos (Weaponless fighters)
+
+N:248:of Mandos
+I:40:27:0
+W:100:16:60:500000
+P:0:2d4:0:0:85
+F:ACTIVATE | ESP_DRAGON | FEATHER | FLY | HOLD_LIFE | IGNORE_ACID |
+F:IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | REGEN | RES_ACID |
+F:RES_BLIND | RES_CHAOS | RES_COLD | RES_CONF | RES_DARK | RES_DISEN |
+F:RES_ELEC | RES_FIRE | RES_LITE | RES_NETHER | RES_NEXUS | RES_POIS |
+F:RES_SHARDS | RES_SOUND | SPECIAL_GENE | ULTIMATE
+a:BLADETURNER
+D:This amulet contains the power of Mandos, the Doomsman of the
+D:Valar. It will grant you protection against the foul dark magic
+D:of Melkor, though you will still need your wits and strength
+D:about you as you face your ultimate foe.
+
+# XXX 249 Room for another artifact here XXX ###
+# XXX 250 Room for another artifact here XXX ###
+# XXX 251 Room for another artifact here XXX ###
+# XXX 252 Room for another artifact here XXX ###
+# XXX 253 Room for another artifact here XXX ###
+# XXX 254 Room for another artifact here XXX ###
+# XXX 255 Room for another artifact here XXX ###
+# XXX 256 Room for another artifact here XXX ###
+
+### 256 is the maximum number of artifacts (257 in misc.txt).
+
+# N: serial number : & object name~
+# G: symbol : color
+# I: tval : sval : pval
+# W: depth : rarity : weight : cost
+# P: base armor class : base damage : plus to-hit : plus to-dam : plus to-ac
+# F: flag | flag | etc
+# D: description
diff --git a/lib/mods/theme/edit/ab_info.txt b/lib/mods/theme/edit/ab_info.txt
new file mode 100644
index 00000000..976c6d03
--- /dev/null
+++ b/lib/mods/theme/edit/ab_info.txt
@@ -0,0 +1,103 @@
+# 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
+
+# Do not forget to update misc.txt with an entry like the following :
+# Maximum number of traits in ab_info.txt
+# M:b:50
+
+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: Combat@30, Dex@17
+I:5
+k:30:Combat
+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: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/mods/theme/edit/ba_info.txt b/lib/mods/theme/edit/ba_info.txt
new file mode 100644
index 00000000..b0270989
--- /dev/null
+++ b/lib/mods/theme/edit/ba_info.txt
@@ -0,0 +1,260 @@
+# 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
+
+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: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: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
+
+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
+
+#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 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: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
+
+# Free rest for the night option for homes
+N:62:Rest for the night
+C:0:0:0
+I:17:0:r
+
+# Free rest for the night option at some 'town' locations
+# Restricted to 'liked' races for thematic reasons
+N:63:Rest for the night
+C:0:0:0
+I:17:2:r
+
+# Enchant arrows option with a different letter for Imladris forge
+N:64:Enchant arrows
+C:550:500:100
+I:30:0:r
+
+# Getting free dinner at some 'town' locations
+# Restricted to 'liked' races for thematic reasons
+N:65:Get food and drink
+C:0:0:0
+I:18:2:f
+
+# Ask Bard for directions to Erebor
+N:66:Ask about Erebor
+C:0:0:0
+I:66:0:a \ No newline at end of file
diff --git a/lib/mods/theme/edit/between.map b/lib/mods/theme/edit/between.map
new file mode 100644
index 00000000..4fed5c95
--- /dev/null
+++ b/lib/mods/theme/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:0:0:0:0:0:0:2
+
+# Floor with grass with a bronze thunderlord
+F:z:89:5:958:0:0:0:0:0:0:2
+
+# Floor with dirt with a bronze thunderlord
+F:Z:88:5:958:0:0:0:0:0:0:2
+
+# Floor with grass with a gold thunderlord
+F:D:88:5:959:0:0:0:0:0:0:2
+
+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/mods/theme/edit/d_info.txt b/lib/mods/theme/edit/d_info.txt
new file mode 100644
index 00000000..f5d836f6
--- /dev/null
+++ b/lib/mods/theme/edit/d_info.txt
@@ -0,0 +1,698 @@
+# 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
+
+### 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:Barad-Dur
+D:BDr:a door to the tower of Barad-Dur.
+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 | NO_RECALL
+R:100:0
+
+N:3:Angband
+D:Ang:an entrance to the Pits of Angband.
+W:67:100: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
+# Theme adds *fog* (dense mist) on the Barrow-Downs :)
+#L:88:94:210:2:199:4
+L:88:78:89:18:199:4
+A:96:80:97:19:57:1: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:Orodruin
+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 'Fuin'
+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: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 "Coimir".
+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 Waiting
+D:HWa:*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.
+# Updated for Theme to lead out into Gorgoroth a la Moria
+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:RANDOM_TOWNS | CIRCULAR_ROOMS
+F:FILL_METHOD_2
+F:FORCE_DOWN
+F:WILD_65_56__67_53
+R:2:0
+R:49:3
+M:SPIDER | 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
+F:FORCE_DOWN
+F:WILD_49_21__51_19
+R:30:3
+M:TROLL
+R:20:0
+R:50:3
+M:ORC | 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:30: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 | R_CHAR_l
+
+# 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 | 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
+# the Robe of Curunir (Theme update as Trone's coat is gone)
+# 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_228
+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_CHAR_l
+R:30:3
+M:ANIMAL
+R:40:0
+
+# The Withered Heath
+# level 22-30
+# guarded by the Sandworm Queen (and her children), who will drop her armour
+N:27:The Withered Heath
+D:SwL:the Withered Heath, from whence came the Great Worms.
+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:100:1
+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 Elenwe the Lost
+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
+
+### New dungeons added for Theme module ###
+
+
+# Forodwaith
+# levels 75-80
+# Guarded by The Hunter
+N:31:Forodwaith
+D:NWa:a path leading through the wastelands of the North
+W:75:80:40:0:14:160
+# ice, ash, and dirt
+L:90:20:93:40:88:40
+#Ugly - using floor tiles for walls, only rooms have real walls
+A:90:20:93:40:88:40:88:211
+O:20:20:20:20
+# it is always dark here in the northern wastelands
+E:2d4:1:DARK
+F:NO_DOORS | CAVERN | COLD | NO_DESTROY | EMPTY | FLAT |
+F:NO_RECALL | LIFE_LEVEL | NO_STREAMERS | NO_SHAFT |
+F:FINAL_GUARDIAN_389 |
+F:FILL_METHOD_4
+R:100:3
+M:COLD_BLOOD | HURT_LITE | IM_COLD
+
+# Emyn Luin
+# levels 60-70
+# Guarded by Naugladur, who has Nauglamir
+N:32:Emyn Luin
+D:ELu:a path into the depths of the Blue Mountains
+W:60:70:30:0:14:160
+# grass, flowers, and dirt
+L:89:45:81:5:88:50
+# blue mountains, granite, hailstones
+A:215:100:215:0:215:0:56:211
+# lots of treasure, not much magic
+O:50:20:10:30
+F:CAVE | CAVERN | CIRCULAR_ROOMS | RANDOM_TOWNS |
+F:NO_STREAMERS | NO_RECALL | NO_DESTROY
+# no_recall because it should not be so easy to get Nauglamir. :P
+F:FINAL_GUARDIAN_457 | FINAL_ARTIFACT_6
+F:FILL_METHOD_3
+R:100:0
+M:R_CHAR_k | R_CHAR_o
+
+#Dol Amroth - Castle of Prince Imrahil
+#levels 25-35
+#Guarded by Prince Imrahil (yes, he's evil in this game)
+N:33:Dol Amroth
+D:DAm:a way to the top of the castle of Dol Amroth
+W:25:35:15:0:14:160
+# Vanilla-style
+L:1:100:1:0:1:0
+A:56:100:56:0:56:0:57:58
+O:20:20:20:20
+F:SMALLEST | NO_DESTROY | TOWER | RANDOM_TOWNS |
+F:ADJUST_LEVEL_1 | NO_STREAMERS | NO_SHAFT | NO_STAIR |
+F:NO_EASY_MOVE | FILL_METHOD_2
+F:FINAL_GUARDIAN_402 |
+R:80:3
+M:R_CHAR_p | R_CHAR_P
+R:20:3
+M:SMART | TAKE_ITEM
+
+#Angmar
+#levels 80-90
+#Guarded by Fuinur, who has Eowyn's sword
+N:34:Angmar
+D:WRA:a dark path through the Witch Realm of Angmar
+W:80:90:49:0:14:160
+# Tainted, dark, evil
+L:93:70:174:20:226:10
+# Dark mountain chains only
+A:214:100:214:0:214:0:214:214
+O:20:20:20:20
+# In addition to swamp water poison, we have disenchantment
+E:1d1:1:DISENCHANT
+F:ADJUST_LEVEL_1_2 | NO_DOORS | NO_STREAMERS |
+F:HOT | FLAT | NO_SHAFT | NO_NEW_MONSTER | CIRCULAR_ROOMS |
+F:FINAL_GUARDIAN_242 | FINAL_ARTIFACT_110
+F:FILL_METHOD_2
+R:50:0
+R:50:3
+M:RES_DISE | UNDEAD | DEMON | NONLIVING
+
+#Near Harad
+#levels 20-25
+#Guarded by Herumor, who has the heavy crossbow of Umbar
+N:35:Near Harad
+D:NHa:a desert path into Near Harad
+W:20:25:15:0:14:160
+#It's a desert, so sand and only sand
+L:91:100:91:0:91:0
+#Ugly - using floor tiles for walls, only rooms have real walls
+A:91:100:91:0:91:0:98:91
+O:20:20:20:20
+#Living is slow in the desert, heh :)
+E:1d1:1:INERTIA
+F:NO_DOORS | CAVE | CAVERN | HOT | NO_DESTROY | EMPTY | FLAT
+F:RANDOM_TOWNS | NO_STREAMERS | NO_SHAFT |
+F:FINAL_GUARDIAN_395 | FINAL_ARTIFACT_171
+F:FILL_METHOD_4
+#It's a desert (sort of wilderness) so WILD_TOO monsters, plus the 'p's for the Haradrim
+R:30:0
+R:70:3
+M:WILD_TOO | R_CHAR_p
+
+#Isengard - Orc Cave on steroids.
+#levels 35-40
+#It ends in a special level with the Palantir of Orthanc and Sharkey
+N:36:Isengard
+D:Isg:a passage to the caves beneath Isengard
+W:35:40:20:0:14:160
+# Like the Orc caves
+L:88:100:1:0:1:0
+A:97:100:56:0:56:0:57:97
+O:20:20:20:20
+F:CAVE | ADJUST_LEVEL_2 | NO_STREAMERS |
+F:FILL_METHOD_0
+R:20:0
+R:30:3
+M:TROLL | R_CHAR_T |
+R:50:3
+M:ORC | R_CHAR_o | R_CHAR_O
+
+# Tol Eressea - of course you never actually set foot on Tol Eressea ;)
+# levels 40-45
+# Guarded by Marda and the Robe of Belegaer
+N:37:Tol Eressea
+D:TEr:a way to the Lonely Isle
+W:40:45:40:0:14:160
+# shallow water, lilies
+L:84:60:222:40:222:0
+# Going to have to add walls here to avoid being overly nasty
+A:211:100:211:0:211:0:211:211
+# As little loot as possible, this is open water, after all
+O:1:1:1:1
+F:SMALLEST | NO_DOORS | NO_DESTROY | EMPTY | FLAT |
+F:ADJUST_LEVEL_1 | NO_STREAMERS | NO_SHAFT | NO_NEW_MONSTER |
+F:FINAL_GUARDIAN_791 | FINAL_ARTIFACT_219 |
+F:FILL_METHOD_0
+R:1:0
+R:99:1
+M:R_CHAR_B
+
+#Utumno
+#levels 101-127
+#Guarded by no one (yet!)
+N:38:Utumno
+D:Utu:an entrance to the depths of Utumno
+W:101: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:CAVERN | NO_EASY_MOVE | NO_RECALL
+F:ADJUST_LEVEL_1_2 | ADJUST_LEVEL_1
+F:FILL_METHOD_0
+R:100:0
+
+# Bilbo's trail in the Barrow-downs
+# just one special level that later becomes a different one, sans princess.
+N:39:Bilbo's trail
+D:Btr:a trail left by a fleeing hobbit
+W:10:10:1:0:14:160
+L:88:94:210:2:199:4
+A:96:80:97:19:57:1:57:97
+A:100:0:0
+O:20:20:20:20
+F:FLAT
+F:FILL_METHOD_3
+R:25:1
+M:UNDEAD
+R:75:0
+
+# Thorin's trail in Mirkwood
+# just one special level that later becomes a different one, sans princess.
+N:40:Thorin's trail
+D:Ttr:a trail left by a purposeful dwarf
+W:33:33:15: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:NO_DOORS | NO_DESTROY | FLAT
+F:FILL_METHOD_0
+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/mods/theme/edit/dragons.map b/lib/mods/theme/edit/dragons.map
new file mode 100644
index 00000000..164c97c8
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/e_info.txt b/lib/mods/theme/edit/e_info.txt
new file mode 100644
index 00000000..70d0e917
--- /dev/null
+++ b/lib/mods/theme/edit/e_info.txt
@@ -0,0 +1,3003 @@
+# 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.
+
+
+
+### Mage Staff ###
+
+N:1:of Mana
+X:A:24:20
+T:6:0:99
+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:99
+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:99
+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:99
+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:99
+T:37:0:99
+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:99
+T:37:0:99
+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:99
+T:37:0:99
+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:99
+T:37:0:99
+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:99
+T:37:0:99
+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:99
+T:37:0:99
+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
+
+# Armour of Dunharrow - built on Filthy rags of leprousness
+N:12:of Dunharrow
+T:35:0:99
+T:36:0:99
+T:37:0:99
+X:A:30:0
+W:0:1:10:0
+C:0:0:0:-6
+R:100
+F:CON | STR | R_STAT | CURSED | HEAVY_CURSE | AGGRAVATE
+# No CURSE_NO_DROP here, players seems to unlike surprises
+
+# Mithirl & Galvorn 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:99
+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:99
+X:A:35:16
+W:0:3:27:500
+C:0:0:0:3
+Z:blink
+R:100
+F:ACTIVATE
+a:JUMP
+
+### Shields ###
+
+N:16:of Resist Acid
+T:115:56:56
+T:34:0:5
+T:34:7:99
+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:99
+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:99
+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:99
+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:99
+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:99
+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: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 Gondor
+X:A:33:19
+W:0:1:8:2000
+C:0:0:0:3
+T:32:0:99
+T:33:0:99
+R:100
+F:STR | DEX | CON | SUST_STR | SUST_DEX | SUST_CON | FREE_ACT
+F:R_HIGH
+
+N:29:of Arnor
+X:A:33:17
+W:0:1:8:2000
+C:0:0:0:3
+T:32:0:99
+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:99
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD | RES_SHARDS
+
+N:42:of Eriador
+X:A:31:10
+W:0:8:18:500
+C:0:0:0:3
+T:35:0:99
+R:100
+F:STEALTH
+f:STEALTH
+R:10
+F:LUCK
+f:LUCK
+
+N:43:of Aman
+X:A:31:20
+W:0:1:28:4000
+C:0:0:20:3
+T:35:0:99
+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:99
+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:99
+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:99
+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:99
+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:99
+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 Eriador
+X:A:35:16
+W:0:8:27:500
+C:0:0:0:3
+T:30:0:99
+R:100
+F:STEALTH
+f:STEALTH
+R:10
+F:LUCK
+f:LUCK
+
+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 Rohan
+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:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:SPIN
+
+# The "Elemental" brands (4) (6)
+
+N:73:Acidic
+T:125:0:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:29
+T:23:31:99
+T:24:0:99
+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:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:125:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:19
+T:21:21:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:125:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:23:0:99
+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:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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
+
+# 'of the Thunderlords' renamed to 'of Tulkas' and picked up some new flags:) --furiosity
+N:99:of Tulkas
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+X:A:24:22
+W:10:6:90:45000
+C:4:4:0:2
+a:TELEPORT
+R:100
+F:SLAY_EVIL | KILL_DRAGON | TELEPORT | FREE_ACT | SEARCH | BRAND_ELEC
+F:REGEN | SLOW_DIGEST | RES_MORGUL | ACTIVATE | ESP_DRAGON
+R:50
+F:RES_NEXUS | HOLD_LIFE
+R:30
+F:R_HIGH | KILL_UNDEAD
+R:12
+F:ABILITY | KILL_DEMON
+R:2
+F:R_P_ABILITY | PVAL_M3 | LIMIT_BLOWS
+
+N:100:of Gondolin
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+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:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:SPECTRAL
+
+N:103:of Morgul
+T:125:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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 Angmar
+T:125:0:99
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+T:15:0:99
+X:A:25:10
+W:0:8:21:1000
+C:15:5:0:0
+
+N:106:of Power
+T:19:0:99
+T:15:0:99
+X:A:25:10
+W:0:8:21:1000
+C:5:15:0:0
+
+N:107:of Extra Might
+T:19:0:99
+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:99
+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:1:1
+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:99
+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:99
+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:BA_ACID_H
+
+# Rods ego
+N:131:Capacity of
+T:67:0:99
+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:99
+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:99
+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:99
+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:99
+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:99
+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
+
+# Magical: affects soft armour, gloves, cloaks
+
+N:147:Magical
+X:B:0:2
+T:31:0:99
+T:35:0:99
+T:36:0:99
+T:40:0:99
+T:45:0:99
+W:5:1:10:2000
+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
+R:50
+F:SPELL_CONTAIN | WIELD_CAST
+f:SPELL_CONTAIN | WIELD_CAST
+
+# Ring and Amulet egos
+
+N:148:Cursed
+X:B:0:0
+T:40:0:99
+T:45:0:99
+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:99
+T:111:0:99
+T:55:0:99
+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:99
+T:65:0:29
+T:65:31:99
+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) [not in Theme, it isn't!]
+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) [not in Theme, it isn't!]
+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:4
+T:30:2:3
+R:100
+F:STEALTH | HIDE_TYPE | FEATHER | IGNORE_ACID | IGNORE_FIRE | ABILITY
+R:50
+F:SPEED
+
+# 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:99
+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:99
+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: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: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: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:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+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:99
+R:100
+F:MAGIC_BREATH
+
+# Ego DSM
+N:187:Polished
+T:38:0:99
+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
+
+### New ego-items from T-Plus by Ingeborg S. Norden ###
+
+# Rogue's Boots, no Metal-Shod or cursed ones allowed // Reworked to balance in Theme 1.1.5
+
+N:189:Rogue's
+X:B:35:15
+W:15:10:25:2500
+C:-5:-5:0:2
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE |
+T:30:2:3
+Z:panic hit
+R:100
+F:SEARCH
+f:SEARCH
+R:50
+F:STEALTH
+f:STEALTH
+R:25
+F:LUCK
+f:LUCK
+R:10
+F:R_HIGH |
+
+### Egos for rings, amulets and crowns (partly inspired by Multiband)
+
+# Glowing (no "Nothings", Amulets of Brilliance, or Rings of
+# Light and Darkness Resistance)
+
+N:190:Glowing
+X:B:0:5
+T:33:0:99
+T:40:0:5
+T:40:7:15
+T:45:0:38
+W:5:1:10:1250
+C:0:0:0:0
+R:100
+F:LITE1 |
+f:LITE1 |
+
+# Dazzling (as above, with more light plus resistance/granted ability)
+
+N:191:Dazzling
+X:B:0:7
+T:33:0:99
+T:40:0:5
+T:40:7:15
+T:45:0:38
+W:10:3:12:2500
+C:0:0:0:0
+Z:illuminate
+R:100
+F:LITE2 | RES_LITE |
+f:LITE2 |
+R:50
+F:LITE1 |
+f:LITE1 |
+R:25
+F:LITE3 |
+f:LITE3 |
+
+# Radiant (Dazzling with some extras;
+# no cursed jewelry)
+
+N:192:Radiant
+X:B:0:12
+T:33:0:99
+T:40:17:99
+T:45:0:38
+T:45:40:49
+T:45:51:99
+W:15:5:15:3500
+C:0:0:0:0
+Z:illuminate
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE |
+R:100
+F:LITE3 | RES_LITE |
+f:LITE3 |
+R:50
+F:LITE2 |
+f:LITE2 |
+R:25
+F:LITE1 |
+f:LITE1 |
+R:20
+F:RES_BLIND |
+R:2
+F:REFLECT |
+
+# Blazing (Glowing plus fiery sheath, resistance, undamaged by
+# fire; restrictions as per Glowing, but no Cold Resistance/Ice
+# rings either)
+
+N:193:Blazing
+X:B:0:15
+T:33:0:99
+T:40:17:99
+T:45:20:38
+T:45:40:49
+T:45:51:99
+W:15:5:15:3000
+C:0:0:0:0
+R:100
+F:LITE1 | RES_FIRE | SH_FIRE |
+f:LITE1 | SH_FIRE | IGNORE_FIRE
+R:25
+F:LITE2 |
+f:LITE2 |
+R:10
+F:LITE3 |
+f:LITE3 |
+R:2
+F:IM_FIRE |
+
+# Lucky (amulets only--resists cursing, undamaged by elements, luck
+# bonus added; no inherently cursed types or Nothing amulets)
+
+N:194:Lucky
+X:B:0:12
+T:40:2:15
+T:40:17:99
+W:15:5:15:2750
+C:0:0:0:5
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE |
+R:100
+F:BLESSED | LUCK |
+
+# Unlucky (amulets only; no Prot/Evil, Doom, or Nothing amulets)
+N:195:Unlucky
+X:B:0:0
+T:40:3:15
+T:40:17:99
+W:0:3:10:0
+C:-5:-5:-5:-5
+R:100
+F:CURSED | AUTO_CURSE | LUCK | AGGRAVATE
+R:20
+F:HEAVY_CURSE |
+
+# Armour of the Maiar (of the Chosen in T-Plus)
+
+N:196:of the Maiar
+T:36:2:99
+T:37:2:99
+X:A:30:30
+W:10:3:45:15000
+C:0:0:15:0
+R:100
+F:BLESSED | ESP_EVIL | ESP_GIANT | ESP_TROLL |
+F:FREE_ACT | RES_FEAR |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+F:OLD_RESIST |
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+f:FREE_ACT | IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+Z:berserk
+R:25
+F:R_HIGH | SUSTAIN |
+
+### Weapons of the Maiar (of the Chosen in T-Plus)
+N:197:of the Maiar
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+X:A:24:35
+W:10:5:45:30000
+C:6:6:4:3
+R:100
+F:SLAY_GIANT | SLAY_TROLL |
+F:SEE_INVIS | FREE_ACT | BLESSED | RES_FEAR |
+F:ESP_GIANT | ESP_TROLL |
+F:LIMIT_BLOWS |
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+f:FREE_ACT | IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+R:33
+F:ESP_EVIL | SLAY_EVIL | R_ANY |
+R:10
+F:BLOWS
+R:1
+F:PVAL_M1
+r:F:CHAOTIC | DRAIN_HP | VAMPIRIC | CURSED | HEAVY_CURSE |
+
+# Robe of Ithryn (of the Archmagi in T-Plus)
+N:198:of the Ithryn
+T:36:2:2
+X:A:30:30
+W:15:3:45:15000
+C:-5:-5:15:2
+R:100
+F:FREE_ACT | RES_BLIND | RES_CONF | R_HIGH | SUST_INT |
+F:SPELL_CONTAIN | WIELD_CAST |
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+f:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | SUST_INT |
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE |
+R:33
+F:RES_DISEN | SPELL |
+R:10
+F:MANA |
+
+# Robe of Sanctity
+
+N:199:of Sanctity
+T:36:2:2
+X:A:30:30
+W:15:3:45:15000
+C:0:0:15:2
+R:100
+F:FREE_ACT | RES_BLIND | RES_CONF | R_HIGH | SUST_WIS |
+F:BLESSED | ACTIVATE |
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+f:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | SUST_WIS |
+a:PROT_EVIL
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE |
+R:33
+F:ESP_EVIL |
+R:10
+F:SPELL |
+
+N:200:Ethereal
+T:35:0:99
+T:36:2:2
+X:B:25:25
+W:15:2:20:27500
+C:0:0:20:0
+R:100
+F:FREE_ACT | SEE_INVIS | HOLD_LIFE |
+F:ACTIVATE |
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+f:FREE_ACT | IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE |
+a:SPECTRAL
+R:33
+F:RES_NETHER |
+R:5
+F:MAGIC_BREATH |
+f:MAGIC_BREATH |
+
+### More new ego-items from Annals of Ea by Feanor:
+
+# Mage staff of the Sindar
+N:201:of the Sindar
+T:6:0:99
+X:A:24:80
+W:40:25:100:25000
+C:0:0:0:5
+R:100
+F:ESP_EVIL | SPELL | MANA
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+
+# Swords of the Noldor
+N:202:of the Noldor
+T:23:0:99
+X:A:24:35
+W:40:25: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
+F:SLAY_EVIL | BLESSED | WOUNDING
+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
+
+# Spear of Vanyar
+N:203:of the Vanyar
+T:22:0:99
+R:100
+X:A:24:35
+W:40:25:100:50000
+C:15:15:20:4
+F:STEALTH | HOLD_LIFE | SEE_INVIS | WIS | INT |
+F:REGEN | RES_FEAR | RES_DARK | RES_LITE | RES_BLIND |
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_ELEC | IGNORE_COLD |
+F:SLAY_DEMON | SLAY_UNDEAD | BLESSED |
+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
+
+# Axe of the Nandor
+N:204:of the Nandor
+T:24:0:99
+R:100
+X:A:24:35
+W:40:25:100:50000
+C:15:15:20:5
+F:STEALTH | SPEED | SEE_INVIS | RES_COLD | RES_ELEC |
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_COLD | IGNORE_ELEC |
+F:SLAY_ORC | SLAY_ANIMAL | RES_POIS |
+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
+
+# Arrow of Teleri
+N:205:of the Teleri
+T:17:0:99
+X:A:23:10
+W:40:25:100:5000
+R:100
+F:R_ANY | WOUNDING | BLESSED
+
+# Hafted of Avari
+N:206:of the Avari
+T:21:0:99
+R:100
+X:A:24:35
+W:40:25:100:50000
+C:15:15:20:5
+F:STEALTH | INVIS | SEE_INVIS | INFRA | RES_DARK |
+F:RES_BLIND | SLAY_UNDEAD |
+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
+
+### Ravenred's Weapons of Unmagic
+
+N:207:of Unmagic
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+X:A:24:24
+W:0:2:44:8000
+C:-10:-10:0:0
+R:100
+F:ANTIMAGIC_50
+f:ANTIMAGIC_50
+
+### Amulet and ring egos suggested by power
+
+#Blessed - anything but Doom
+N:208:Blessed
+T:40:0:99
+T:45:1:99
+X:B:0:10
+W:0:5:100:2000
+C:0:0:10:2
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE |
+R:100
+F:WIS | SEE_INVIS | HOLD_LIFE | BLESSED
+f:WIS | SEE_INVIS | HOLD_LIFE | BLESSED
+
+#Demonic - anything but Devotion and Protection from Evil
+N:209:Demonic
+T:40:0:1
+T:40:3:24
+T:40:26:99
+T:45:0:99
+X:B:0:10
+W:0:5:100:2000
+C:0:0:0:2
+r:F:BLESSED
+R:100
+F:ESP_DEMON | RES_FIRE
+f:ESP_DEMON | RES_FIRE
+
+# Jewellery of the Rohirrim - no speed rings
+N:210:Rohirric
+T:40:0:99
+T:45:0:30
+T:45:32:99
+X:B:0:10
+W:0:5:100:20000
+C:0:0:0:3
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE |
+R:100
+F:RES_FEAR | SPEED
+f:RES_FEAR | SPEED
+
+# Draconic - anything but Devotion and Protection from Evil
+N:211:Draconic
+T:40:0:1
+T:40:3:24
+T:40:26:99
+T:45:0:99
+X:B:0:10
+W:0:5:100:2000
+C:5:5:10:1
+r:F:BLESSED
+R:100
+F:ESP_DRAGON
+f:ESP_DRAGON
+
+#Elemental - the ones that already give resists are obviously not eligible
+N:212:Elemental
+T:40:16:28
+T:40:30:99
+T:45:10:16
+T:45:20:99
+X:B:0:30
+W:25:5:50:1000
+C:0:0:0:0
+R:100
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_COLD | IGNORE_ELEC |
+F:RES_ACID | RES_FIRE | RES_COLD | RES_ELEC | R_ANY
+R:20
+F:IM_FIRE | R_HIGH
+R:15
+F:IM_COLD | R_HIGH
+R:10
+F:IM_ELEC | R_HIGH
+R:5
+F:IM_ACID | R_HIGH
+R:1
+F:IM_NETHER
+
+### New ego-items unique to Theme module ###
+
+#Musical instruments of Melkor
+
+N:213:of Melkor
+T:14:0:59
+T:14:61:99
+X:A:0:30
+W:0:5:30:0
+C:0:0:0:-10
+R:100
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_COLD | IGNORE_ELEC |
+F:CURSED | HEAVY_CURSE | AUTO_CURSE | AGGRAVATE
+R:50
+F:R_STAT | CURSED | HEAVY_CURSE | AUTO_CURSE
+
+#Horns of Ulmo (Ulumuri)
+
+N:214:of Ulmo
+T:14:60:60
+X:A:0:80
+W:0:5:80:5000
+C:0:0:0:0
+R:100
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_COLD | IGNORE_ELEC |
+F:PVAL_M5 | R_IMMUNITY
+
+# Reverse Armour of the Maiar (of the Chosen in T-Plus)
+N:215:of the Fallen
+T:36:2:99
+T:37:2:99
+X:A:30:30
+W:0:3:45:0
+C:0:0:-15:0
+R:25
+F:CURSED | AUTO_CURSE | ESP_GOOD |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+R:50
+F:CURSED | AUTO_CURSE | R_LOW | SUSTAIN | AGGRAVATE
+R:75
+F:CURSED | AUTO_CURSE | WRAITH | DRAIN_EXP |
+R:100
+F:CURSED | AUTO_CURSE | DRAIN_MANA | DRAIN_HP | BLACK_BREATH
+
+###Reverse Weapons of the Maiar (of the Chosen in T-Plus)
+N:216:of the Fallen
+T:15:0:99
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+X:A:24:35
+W:0:5:45:0
+C:+6:+6:-5:-3
+R:100
+F:NEVER_BLOW | IM_NETHER | PERMA_CURSE
+R:50
+F:CURSED | HEAVY_CURSE | AUTO_CURSE | NO_MAGIC | CLONE
+R:33
+F:CURSED | HEAVY_CURSE | AUTO_CURSE | DRAIN_EXP | DRAIN_MANA
+R:10
+F:CURSED | HEAVY_CURSE | AUTO_CURSE | CHAOTIC | DRAIN_HP
+R:1
+F:R_STAT | CURSED | AUTO_CURSE | ESP_GOOD |
+F:RES_ACID | RES_ELEC | RES_FIRE | RES_COLD |
+
+# Robe of Fools (reverse of Ithryn/Archmagi in T-Plus)
+N:217:of the Fools
+T:36:2:2
+X:A:30:30
+W:0:3:45:0
+C:-5:-5:-15:-2
+R:100
+F:CURSED | HEAVY_CURSE | AUTO_CURSE | LIFE
+R:33
+F:CURSED | HEAVY_CURSE | AUTO_CURSE | RES_DISEN | SPELL | NO_MAGIC |
+R:10
+F:CURSED | HEAVY_CURSE | AUTO_CURSE | MANA |
+
+# Robe of Vice (reverse of Sanctity in T-Plus)
+N:218:of Vice
+T:36:2:2
+X:A:30:30
+W:0:3:45:0
+C:0:0:-15:-2
+R:100
+F:CURSED | HEAVY_CURSE | AUTO_CURSE | LIFE | AGGRAVATE
+R:33
+F:ESP_GOOD | ANTIMAGIC_50
+
+# Soft and Hard Armour of Sensitivity, no paper armour
+N:219:of Sensitivity
+T:30:0:99
+T:31:0:99
+T:32:0:99
+T:33:0:99
+T:34:0:99
+X:A:30:30
+W:0:3:45:0
+C:0:0:-5:-2
+R:100
+F:CURSED | SENS_FIRE | R_STAT |
+
+# Elemental ammo
+N:220:Elemental
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:B:30:30
+W:0:1:50:1000
+C:5:5:0:0
+R:100
+F:BRAND_ACID | IGNORE_ACID
+F:BRAND_ELEC | IGNORE_ELEC
+F:BRAND_FIRE | IGNORE_FIRE
+F:BRAND_COLD | IGNORE_COLD
+F:BRAND_POIS |
+f:BRAND_ACID |
+f:BRAND_ELEC |
+f:BRAND_FIRE |
+f:BRAND_COLD |
+f:BRAND_POIS |
+
+# Cursed ammo
+
+N:221:of Angmar
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:A:30:30
+W:0:1:50:0
+C:-10:-10:0:0
+R:100
+F:CURSED | IMMOVABLE
+
+# Bows of Numenor
+
+N:222:of Numenor
+T:19:12:34
+X:A:50:50
+W:0:1:50:0
+C:10:10:0:3
+R:100
+F:DEX | XTRA_MIGHT | XTRA_SHOTS |
+F:RES_FEAR | RES_POIS | RES_DARK | FREE_ACT
+
+# Lights of Valinor (no refillable lights)
+
+N:223:of Valinor
+T:39:2:4
+X:A:60:60
+W:0:2:60:45000
+C:0:0:0:2
+R:100
+F:STR | INT | WIS | DEX | CON | CHR
+F:ESP_ALL
+f:STR | INT | WIS| DEX | CON | CHR | ESP_ALL
+
+# Lucky weapons - named after smith of legend
+N:224:of Telchar
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+X:A:70:70
+W:10:2:70:3000
+C:10:10:0:2
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE
+R:100
+F:LUCK
+f:LUCK
+
+# Unlucky weapons - of the Din-horde (Orcs)
+N:225:of the Glamhoth
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+X:A:50:50
+W:0:2:50:0
+C:5:5:0:-2
+R:100
+F:LUCK | CURSED | HEAVY_CURSE | AUTO_CURSE | AGGRAVATE
+f:LUCK | CURSED | HEAVY_CURSE | AUTO_CURSE | AGGRAVATE
+
+# Holy ammo - based on 'Of Holy Might' from Hengband
+N:226:Blessed
+T:16:0:99
+T:17:0:99
+T:18:0:99
+X:B:30:30
+W:0:5:60:600
+C:5:10:0:0
+R:100
+F:SLAY_EVIL | SLAY_DEMON | SLAY_UNDEAD | BRAND_FIRE | BLESSED
+F:IGNORE_FIRE | IGNORE_ACID
+
+# Holy Avenger weapons brought back, modified
+N:227:(Holy Avenger)
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+X:A:50:50
+W:0:2:60:20000
+C:5:5:0:3
+R:100
+F:WIS | SUSTAIN |
+F:SLAY_EVIL | SLAY_UNDEAD | SLAY_DEMON |
+F:SEE_INVIS | BLESSED
+
+# A Whip of the Balroeg, adapted from Oangband
+N:228:of the Balroeg
+T:21:2:2
+X:A:50:50
+W:0:3:50:12000
+C:-10:10:0:-3
+R:100
+F:STEALTH | BRAND_FIRE | RES_FIRE | LITE2 |
+F:IGNORE_FIRE | ESP_GOOD |
+R:5
+F:IM_FIRE
+
+# Bows of the Nazgul, adapted from Angband
+N:229:of the Nazgul
+T:19:0:99
+T:15:0:99
+R:80
+X:A:25:10
+W:0:3:50:0
+C:0:0:0:-1
+r:F:BLESSED
+F:DRAIN_EXP | SEE_INVIS | CHR
+
+# Magical missile launchers
+# Tiny mana boost for magic-using archers or warrior-types
+N:230:Magical
+T:19:0:99
+T:15:0:99
+X:B:25:10
+W:25:9:30:8000
+C:0:0:0:1
+R:100
+F:MANA
+f:MANA
+
+# Magical jewelry (no rings/amulets of spell, anti-magic, cursed items)
+N:231:Magical
+X:B:0:2
+T:40:0:12
+T:40:14:26
+T:40:28:99
+T:45:0:56
+T:45:59:99
+W:5:1:10:2000
+C:0:0:0:0
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE
+R:100
+F:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+f:IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+R:50
+F:SPELL_CONTAIN | WIELD_CAST
+f:SPELL_CONTAIN | WIELD_CAST
+
+# Magestaves of Sorcery, added in Theme 1.0.1
+N:232:of Sorcery
+X:A:24:60
+T:6:0:99
+W:30:1:8:100000
+C:-80:-80:0:3
+r:F:CURSED | HEAVY_CURSE | PERMA_CURSE
+R:100
+F:MANA | SPELL | LIFE
+R:50
+F:PVAL_M2
+
+# Sentient weapons, added in Theme 1.0.2
+N:233:of Doriath
+T:21:0:99
+T:22:0:99
+T:23:0:99
+T:24:0:99
+X:A:70:70
+W:0:2:70:50000
+C:1:1:0:0
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE
+R:100
+F:LEVELS
+f:LEVELS
+
+# Bows of Mirkwood, added in Theme 1.0.2
+N:234:of Mirkwood
+T:19:12:13
+X:A:25:20
+W:50:2:21:50000
+C:10:10:0:2
+R:100
+F:CON | XTRA_MIGHT | XTRA_SHOTS | RES_POIS | STEALTH
+F:IGNORE_ACID | IGNORE_FIRE | HIDE_TYPE | SEE_INVIS
+
+# Spear of Mirkwood, added in Theme 1.0.2
+N:235:of Mirkwood
+T:22:2:2
+X:A:24:35
+W:40:25:100:50000
+C:15:15:20:3
+r:F:CURSED | HEAVY_CURSE | AUTO_CURSE
+R:100
+F:STEALTH | HOLD_LIFE | SEE_INVIS | CON |
+F:RES_FEAR | RES_DARK | RES_LITE | RES_BLIND |
+F:IGNORE_ACID | IGNORE_FIRE |
+F:R_ANY | R_LOW | SUSTAIN
+R:30
+F:R_ANY | R_LOW | SUSTAIN
+R:20
+F:R_ANY | R_LOW | SUSTAIN | R_HIGH
+
+# Dwarven jewelry - no speed rings; added in Theme 1.1.4
+N:236:Dwarven
+T:40:0:99
+T:45:0:30
+T:45:32:99
+X:B:0:5
+W:5:1:10:1250
+C:0:0:10:1
+R:100
+F:INFRA
+Z:stone to mud
+R:50
+F:SUSTAIN
+R:25
+F:R_ANY | SUSTAIN
+R:5
+F:CON
+
+# Sharpened arrows and bolts; added in Theme 1.1.5
+# Separating from 'Sharp' weapons to avoid cost of 5000 AU per missile
+N:237:Sharpened
+T:17:0:99
+T:18:0:99
+X:B:23:10
+W:5:2:30:400
+R:100
+F:VORPAL
+f:VORPAL
+
+# 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/mods/theme/edit/evil.map b/lib/mods/theme/edit/evil.map
new file mode 100644
index 00000000..a2f00914
--- /dev/null
+++ b/lib/mods/theme/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:0:0:0:0:0:0:2
+
+# Greater Balrog
+F:B:88:0:807:0:0:0:0:0:0:2
+
+# Pit Fiend
+F:P:88:0:812:0:0:0:0:0:0:2
+
+# 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/mods/theme/edit/f_info.txt b/lib/mods/theme/edit/f_info.txt
new file mode 100644
index 00000000..2b42aa47
--- /dev/null
+++ b/lib/mods/theme/edit/f_info.txt
@@ -0,0 +1,1240 @@
+# 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.
+
+
+# 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
+
+#Elanor
+N:79:grass with Elanor flowers
+G:&:y
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+#Fumellar
+N:80:grass with Fumella flowers
+G:;:r
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+#Anemones
+N:81:grass with anemones
+G:;:v
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+#Niphredil
+N:82:grass with Niphredil flowers
+G:;:w
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+#Iris
+N:83:grass with irises
+G:;:b
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+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:puddle of 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
+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
+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
+
+# A diggable glass wall.
+N:103:molten glass wall
+G:.:B
+F:NO_WALK | WALL | CAN_PASS | TUNNELABLE | NOTICE
+F:DONT_NOTICE_RUNNING
+D:1:You tunnel into the molten glass wall...
+D:2:a molten glass wall blocking your way
+
+N:159:Void Jumpgate
+G:+:v
+F:FLOOR | REMEMBER | NOTICE | PERMANENT | CAN_RUN
+D:0:A dark rift opens to the void here.
+
+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.
+
+N:166:Altar of Stone
+G:0:s
+F:FLOOR | REMEMBER | NOTICE | CAN_RUN
+D:0:You grow a desire to forge items.
+
+N:167:Altar of Light
+G:0:y
+F:FLOOR | REMEMBER | NOTICE | CAN_RUN
+D:0:You grow a desire to light up dark places.
+
+N:168:Altar of Waters
+G:0:b
+F:FLOOR | REMEMBER | NOTICE | CAN_RUN
+D:0:You grow a desire to bathe in the ocean.
+
+N:169:Altar of Doom
+G:0:o
+F:FLOOR | REMEMBER | NOTICE | CAN_RUN
+D:0:You grow a desire to do justice.
+
+# 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 Kingsfoil flowers
+N:199:grass with Kingsfoil 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
+
+# Low hill
+N:213:low hill
+G:^:g
+F:FLOOR | DONT_NOTICE_RUNNING| CAN_RUN | SUPPORT_LIGHT | NO_VISION
+D:0:You go over the hill.
+D:1:You cannot tunnel through that.
+
+#Angband and Mordor (Ered Lithui, Ephel Duath) mountains
+N:214:dark mountain chain
+G:^:D
+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
+
+#Ered Luin - Blue Mountains
+N:215:blue mountain chain
+G:^:B
+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
+
+#Ered Mithrin - Grey Mountains
+N:216:grey mountain chain
+G:^:s
+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
+
+#Orodruin - Mount Doom
+N:217:part of Mount Doom
+G:^:R
+F:WALL | NO_WALK | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+F:PERMANENT
+D:1:You cannot tunnel through that.
+D:2:a stream of searing lava barring your way
+
+#Snow-capped peak
+N:218:snow-capped peak
+G:^:w
+F:WALL | NO_WALK | NO_VISION | NOTICE | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING | PERMANENT
+D:1:The ice is too cold to tunnel into it.
+D:2:an unusually thick wall of ice barring your way
+
+#Fir tree
+#BUG - these do not burn.
+N:219:fir 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 fir tree.
+D:2:a fir tree blocking your way
+
+#section of a flet (wooden platforms built high up in the trees of Lothlórien)
+N:220:section of a flet
+G:_:u
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+
+#light post
+N:221:light post
+G:|:w
+F:WALL | NO_WALK | CAN_FLY | CAN_PASS
+F:SUPPORT_LIGHT | REMEMBER
+D:1:You cannot tunnel a light post.
+D:2:A light post blocking your way
+
+#Water lily
+N:222:water lily
+G:;:B
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+#Dead Marshes swamp water
+N:223:part of the Dead Marshes
+G:~:G
+E:10d10:6:CONFUSION
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT
+
+#Morannon
+N:224:Black Gate
+G:+:D
+F:CAN_CLIMB | CAN_PASS | SUPPORT_LIGHT
+F:WALL | NO_WALK | NO_VISION | NOTICE
+F:DONT_NOTICE_RUNNING
+F:PERMANENT
+D:1:You cannot tunnel through that.
+D:2:a closed Black Gate blocking your way
+
+#River (overland view only)
+N:225:river
+G:~:w
+S:w:w:w:B:w:w:B
+F:ATTR_MULTI
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT | CAN_RUN
+F:DONT_NOTICE_RUNNING | SUPPORT_GROWTH
+
+#Swamp
+N:226:swamp pool
+G:~:g
+E:1d1:1:POISON
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT
+
+#Anduin river
+N:227:stream of the Anduin river
+G:~:B
+E:1d1:1:WATER
+S:w:w:w:b:w:w:b
+F:ATTR_MULTI
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT | SUPPORT_GROWTH
+
+#Road sign to Gondolin to avoid Maeglin quest silliness somewhat
+N:228:road sign that says 'Find Gondolin!'
+G:?:v
+S:v:R:B:G:w:v:R
+F:ATTR_MULTI
+F:WALL | PERMANENT | NOTICE | SUPPORT_LIGHT | REMEMBER |
+
+### New terrain features for town use ###
+
+#Beehive
+N:229:beehive
+G:*:o
+E:1d1:1:POISON
+F:WALL | PERMANENT | CAN_FLY | REMEMBER | SUPPORT_LIGHT |
+F:CAN_PASS | NO_WALK | NO_VISION | DONT_NOTICE_RUNNING |
+D:0:Ouch! A bee stung you!
+D:1:You'll just get stung.
+D:2:a beehive blocking your way
+
+#Dirt road - same as dirt but more appropriate for towns
+N:230:dirt road
+G:.:U
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN
+F:SUPPORT_GROWTH
+
+#Wide gate
+N:231:wide gate
+G:-:D
+F:WALL | PERMANENT | CAN_FLY | SUPPORT_LIGHT | NOTICE |
+F:CAN_PASS | NO_WALK | NO_VISION | DONT_NOTICE_RUNNING |
+D:1:You cannot tunnel through that.
+D:2:a closed gate barring your way
+
+#Same gate, but opened
+N:232:open gate
+G:':D
+F:FLOOR | DONT_NOTICE_RUNNING | CAN_RUN | SUPPORT_LIGHT |
+
+### For wood houses, bridges, etc:
+
+#Wooden board - horizontal
+N:233:wooden board
+G:-:u
+F:WALL | PERMANENT | SUPPORT_LIGHT | NO_WALK | NO_VISION | CAN_FLY
+D:1:You cannot tunnel through that.
+D:2:a wooden board blocking your way
+
+#Wooden board - vertical
+N:234:wooden board
+G:|:u
+F:WALL | PERMANENT | SUPPORT_LIGHT | NO_WALK | NO_VISION | CAN_FLY
+D:1:You cannot tunnel through that.
+D:2:a wooden board blocking your way
+
+#Light wooden board - horizontal
+N:235:wooden board
+G:-:U
+F:WALL | PERMANENT | SUPPORT_LIGHT | NO_WALK | NO_VISION | CAN_FLY
+D:1:You cannot tunnel through that.
+D:2:a wooden board blocking your way
+
+#Light wooden board - vertical
+N:236:wooden board
+G:|:U
+F:WALL | PERMANENT | SUPPORT_LIGHT | NO_WALK | NO_VISION | CAN_FLY
+D:1:You cannot tunnel through that.
+D:2:a wooden board blocking your way
+
+#White tree
+#BUG - these do not burn.
+N:237:white tree
+G:#:w
+F:CAN_FLY | CAN_PASS | SUPPORT_LIGHT
+F:WALL | NO_WALK | NO_VISION | PERMANENT
+F:DONT_NOTICE_RUNNING
+D:1:You cannot tunnel through that.
+D:2:a white tree blocking your way
+
+#Swift waterfall
+N:238:swift waterfall
+G:|:B
+E:25d10:1:WATER
+S:w:w:w:b:w:w:b
+F:ATTR_MULTI
+F:FLOOR | CAN_LEVITATE | CAN_FLY | REMEMBER | SUPPORT_LIGHT |
+F:DONT_NOTICE_RUNNING | SUPPORT_GROWTH |
+
+#Slippery rock ledge
+N:239:slippery rock ledge
+G:&:s
+E:5d10:1:COLD
+F:FLOOR | CAN_LEVITATE | CAN_FLY | SUPPORT_LIGHT |
+F:SUPPORT_GROWTH
+
+#Stable
+N:240:stable
+G:#:u
+F:WALL | NO_WALK | NO_VISION | PERMANENT | CAN_FLY
+D:1:You cannot tunnel through that.
+D:2:a stable wall blocking your way
+
+#Wooden plank
+N:241:wooden plank
+G:%:U
+F:FLOOR | CAN_RUN | DONT_NOTICE_RUNNING | SUPPORT_LIGHT |
+F:SUPPORT_GROWTH |
+
+#Fosse (dry moat)
+N:242:fosse pit
+G:&:g
+F:CAN_LEVITATE | CAN_FLY
+F:NO_WALK | SUPPORT_LIGHT
+F:DONT_NOTICE_RUNNING
+D:1:You cannot tunnel through that.
+D:2:a dry moat blocking your way
+
+#Mallorn
+N:243:Mallorn
+G:*:y
+S:y:W:W:y:w:W:y
+F:CAN_FLY | CAN_PASS | SUPPORT_LIGHT
+F:WALL | NO_WALK | NO_VISION
+F:DONT_NOTICE_RUNNING
+D:1:It isn't a good idea to harm a Mallorn.
+D:2:a Mallorn blocking your way
+
+# New features for the Maps of Lord Dimwit
+
+N:244: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:245: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:246: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:247: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
+
+N:248:door of Orthanc
+G:':r
+F:FLOOR | NOTICE | REMEMBER | CAN_RUN
+F:SUPPORT_LIGHT | DONT_NOTICE_RUNNING
+D:0:The Key of Orthanc allows you to pass. \ No newline at end of file
diff --git a/lib/mods/theme/edit/fireprof.map b/lib/mods/theme/edit/fireprof.map
new file mode 100644
index 00000000..35f6bec4
--- /dev/null
+++ b/lib/mods/theme/edit/fireprof.map
@@ -0,0 +1,60 @@
+# 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
diff --git a/lib/mods/theme/edit/haunted.map b/lib/mods/theme/edit/haunted.map
new file mode 100644
index 00000000..49f72d5b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/k_info.txt b/lib/mods/theme/edit/k_info.txt
new file mode 100644
index 00000000..c8a78082
--- /dev/null
+++ b/lib/mods/theme/edit/k_info.txt
@@ -0,0 +1,6861 @@
+# 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).
+
+# N: serial number : & object name~
+# G: symbol : color
+# I: tval : sval : pval
+# W: depth : rarity : weight : cost
+# P: base armor class : base damage : plus to-hit : plus to-dam : plus to-ac
+# A: depth/rarity : depth/rarity : etc
+# F: flag | flag | etc
+
+
+##### 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:Fear
+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 Fear
+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 Cram
+G:,:U
+I:80:35:5000
+W:0:0:10:3
+A:0/1:5/1:10/1
+D:It is biscuitish, keeps good indefinitely, is supposed to be sustaining but
+D:certainly not entertaining. Its origin is Esgaroth, the city of Lake-men.
+D:You can 'E'at it.
+
+N:22:& Round Seed-Cake~
+G:,:U
+I:80:32:500
+W:0:0:2:1
+D:It's a treat, but 'E'ating it will only fill your stomach 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:Small golden-brown wafers wrapped in silver leaves. Its fabrication is a
+D:secret of the Elves, and it sustains life where nothing else can. If you
+D:'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 from Eriador. You can drink it by pressing 'E'.
+
+N:27:& Pint~ of Old Winyards
+G:,:r
+I:80:39:1000
+W:0:0:10:2
+D:A bottle of fine wine from Eriador. 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 'Coimir' -- 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:This weapon is light and easy to fight with. It has nasty barbs and
+D:hooks fixed to the thong to make it useful in combat.
+D:Whips easily give 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-6 ft 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:Another 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:Short metal arrows, to be fired with crossbows. You can use it for 'f'iring a
+D: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. Probably worthless.
+
+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 suit of soft armour, 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:& Galvorn 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 | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+D:A suit of plate armour fashioned from a metal whose secret is only known to Eol,
+D:the dark Elvensmith. It is practically unbreakable.
+
+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 | SUST_STR
+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 | SUST_DEX
+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 | SUST_CON
+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 | SUST_INT
+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: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: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:BA_FIRE_4
+F:RES_FIRE | ACTIVATE
+f:RES_FIRE | IGNORE_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:BA_ACID_4
+F:RES_ACID | ACTIVATE
+f:RES_ACID | IGNORE_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:BA_COLD_4
+P:0:0d0:0:0:15
+F:RES_COLD | ACTIVATE
+f:RES_COLD | IGNORE_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 | EASY_USE | ACTIVATE |
+a:WHIRLWIND
+D:This ring magically improves your control in combat, allowing you to hit more often.
+D:It can also sometimes be used to hit several nearby opponents with deadly accuracy.
+
+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.
+D:If you have any pets, it will also make them turn against you.
+
+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: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.
+
+# Replacing Adornment with Protection from Evil from T-Plus by Ingeborg S. Norden
+
+N:169:Protection from Evil
+G:":d
+I:40:2:0
+W:25:0:3:10000
+A:25/1
+F:EASY_KNOW | ACTIVATE | BLESSED | ESP_EVIL
+a:PROT_EVIL
+D:This blessed amulet fends off evil beings and warns the wearer
+D:of their presence.
+
+##### 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 your 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 doors and stairs.
+
+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:Aggravation
+G:?:d
+I:70:1:0
+W:5:0:5:0
+A:5/1
+D:This nasty scroll will make a loud noise when read, 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 create a nutritious mass in your stomach, satisfying your hunger.
+
+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. It may not work on weapons that are already powerful or unique.
+
+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.
+
+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 surface 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.
+
+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 permanently 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 beneficial 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 permanently 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 naive.
+
+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
+F:FOUNTAIN
+D:This potion infuses you with permanent 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
+F:FOUNTAIN
+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
+F:FOUNTAIN
+D:This potion fundamentally twists your physical features, as if you were another mother's child.
+D:Your physique will be shaped anew. This may make you more or less hardy in battle.
+
+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 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 will make you fall asleep for some turns, allowing monsters
+D:to attack you with impunity.
+
+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.
+
+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. It will impede your ability to see clearly.
+
+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.
+
+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 that will temporarily force you to move and act a lot slower.
+
+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 permanently improves your agility.
+
+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:This wicked potion will make you lose a lot of the experience you've gained.
+
+N:255:Salt Water
+G:!:d
+I:71:5:0
+W:0:0:4:1
+A:0/1
+P:0:1d1:0:0:0
+F:FOUNTAIN
+D:A solution of salt in water, made for curing meat. Drinking it causes 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 potion 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 render you less vulnerable to outward 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 and reduce the damage they cause.
+
+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 negate the effect of all 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. You can use it for 'f'iring a bow.
+
+# 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 | EASY_USE
+F:ACTIVATE | ACTIVATE_NO_WIELD
+a: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:A temporary weapon that only a necromancer can use.
+
+# 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:IGNORE_ACID | IGNORE_FIRE | IGNORE_ELEC | IGNORE_COLD |
+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 slick black 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.
+
+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, locked and possibly trapped
+
+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.
+D:It is locked and possibly trapped.
+
+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 large container made of wood, with a heavy iron lock, and probably a trap.
+
+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. It's likely to be trapped.
+
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+D:A rod of knowledge which will tell you about nearby creatures' health.
+D:If they are sleeping, the intrusion will wake them.
+
+N:354:Recall
+G:-:d
+I:66:3:80
+W:30:0:15:4500
+A:30/4
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:EASY_KNOW
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+D:A small but extremely cold shard of ice will fly from this rod to the enemy you zap it at.
+
+N:359:Fire Bolts
+G:-:d
+I:66:22:40
+W:30:0:15:3000
+A:30/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+D:This rod fires a magical flaming arrow at your foe, burning them.
+
+N:360:Polymorph
+G:-:d
+I:66:19:25
+W:35:0:15:1200
+A:35/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+D:This rod of change will cause its target creature to mutate into someone else.
+D:Beware, it can make a weak enemy into a more powerful one.
+
+N:361:Slow Monster
+G:-:d
+I:66:17:25
+W:30:0:15:1500
+A:30/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+D:This obstructive rod will slow the creature its spell 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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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.
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+
+N:369:Acid Balls
+G:-:d
+I:66:24:60
+W:70:0:15:5500
+A:70/1
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+D:This rod lets you remember and gain back previously reduced abilities.
+
+N:377:Speed
+G:-:d
+I:66:11:100
+W:95:0:15:50000
+A:95/16
+P:0:1d1:0:0:0
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE
+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 onto 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 onto 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:A yellowing, mud-stained, cracked skull.
+
+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.
+
+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:The only thing this dog has is its bones.
+
+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:A dead critter's remains.
+
+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:The remains of a human who met his or her demise here.
+
+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:The remains of an elf who met his or her demise here.
+
+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:The remains of a gnome who met his or her demise here.
+
+##### 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:BR_ACID
+F:RES_ACID | FLY |
+f:RES_ACID |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A pitch-black armour made from a black dragon's hide.
+D:It contains some of the dead 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:BR_ELEC
+F:RES_ELEC | FLY |
+f:RES_ELEC |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A shimmering bright blue armour made from a blue dragon's hide.
+D:It contains some of the dead beast's powers.
+
+N:402:& White Dragon Scale Mail~
+G:[:w
+I:38:3:0
+W:50:0:200:40000
+A:50/8
+a: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:A gleaming white armour made from a white dragon's hide.
+D:It contains some of the dead beast's powers.
+
+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:BR_FIRE
+F:RES_FIRE | FLY |
+f:RES_FIRE |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A glowing red armour made from a red dragon's hide.
+D:It contains some of the dead beast's powers.
+
+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:BR_POIS
+F:RES_POIS | FLY |
+f:RES_POIS |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A dirty green, foul-smelling armour made from a green dragon's hide.
+D:It contains some of the dead beast's powers.
+
+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: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 glowing red, blue, green, black, and white.
+D:made from a multihued dragon's hide, it contains some of the dead beast's powers.
+
+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:BR_LIGHT
+F:RES_LITE | RES_DARK | FLY |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A strangely glowing armour made from a pseudo-dragon's hide.
+D:It contains some of the dead beast's powers.
+
+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:BR_SHARD
+F:RES_SOUND | RES_SHARDS | FLY |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A sharp-scaled armour that seems to roar, made from a law dragon's hide.
+D:It contains some of the dead beast's powers.
+
+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:BR_CONF
+F:RES_CONF | FLY |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A brownish armour glittering in a dazzling light, made from a bronze dragon's hide.
+D:It contains some of the dead beast's powers.
+
+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:BR_SOUND
+F:RES_SOUND | FLY |
+F:ACTIVATE | IGNORE_ACID | IGNORE_ELEC | IGNORE_FIRE | IGNORE_COLD
+D:A suit of armour with rustling scales, made from a gold dragon's hide.
+D:It contains some of the dead beast's powers.
+
+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: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: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: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.
+
+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 potion 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
+F:FOUNTAIN
+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 that lets 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:Arnor
+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 | ACTIVATE | EASY_USE |
+f:BLOWS | ACTIVATE
+a:SPIN
+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.
+
+# 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: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 Annatar -- see artifact list
+
+N:503:& Amulet~
+G:":d
+I:40:10:0
+W:70:0:3:90000
+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
+# Gone in Theme
+#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 variant of banded mail. The metal strips are applied to the backing of chain,
+D:leather, or cloth rather than horizontally.
+
+# 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:slightly greater damage per hit 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
+F:RES_COLD
+D:A coat made from the fur of various wild animals - it is
+D:somewhat bulky, but still spacious enough to wear over armour.
+D:It is ideal for cold weather conditions.
+
+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 larger version of a throwing axe. It has a single
+D:blade with a pick on the reverse, designed for armour piercing.
+
+##### New armour #####
+
+N:535:& Mumak Hide Armour~
+G:(:s
+I:36:8:0
+W:15:0:110:400
+A:15/1
+P:8:1d1:-1:0:0
+D:This suit of armour is fashioned from the hide of a dead Mumak,
+D:or Oliphaunt. It is tough, leathery, and a bit cumbersome.
+
+N:536:& Leather Jerkin~
+G:(:U
+I:36:12:0
+W:20:0:70:550
+A:20/3
+P:7:1d2:0:0:0
+D:A kind of vest with a colarless neck, made from roughout leather. This
+D:is what Middle-earth rangers usually wear on their travels.
+
+##### 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:It looks like it has just come from the forge. Designed specifically for
+D:foot soldiers combatting mounted cavalry, it is too heavy to be effective
+D: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, because 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
+F:SENS_FIRE
+D:Thickly pleated sheets of paper assembled together into
+D:a suit of armour. It is useful against arrows, but it may
+D:catch on fire quite easily.
+
+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:& Golden Ring Mail~
+G:(:y
+I:36:15:0
+W:35:0:150:500
+A:35/7
+P:8:1d4:-1:0:0
+D:A suit of non-overlapping thin golden rings sewn onto a soft leather
+D:backing. It looks beautiful, and is worn on special occasions.
+
+### 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.
+
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+
+N:569:Nothing
+G:-:d
+I:66:0:1
+W:5:0:10:50
+A:5/1
+P:0:1d1:0:0:0
+
+
+#Artifact Potion
+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:This magical potion contains a small part of the power of
+D:Eru Iluvatar on Middle-Earth.
+
+# XXX
+# XXX
+# XXX
+
+# 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:BA_ELEC_4
+F:RES_ELEC | ACTIVATE
+f:RES_ELEC | IGNORE_ELEC
+D:This sparkling circlet grants you protection, makes electricity less
+D:dangerous and even allows you to call forth a ball of lightning.
+
+# XXX
+# XXX
+# XXX
+
+# The Ring of Flare -- see artifact list
+# Gone in Theme.
+#N:582:& Ring~
+#G:=:y
+#I:45:52:5
+#W:50:25:2:75000
+#F:INSTA_ART
+
+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 to this state so
+D:that your eyes can still perceive your hidden form.
+
+# Potion of Corruption
+
+N:585:Corruption
+G:!:d
+I:71:10:0
+W:10:0:4:0
+A:10/1:17/1:27/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.
+
+# XXX
+
+######### 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.
+
+# Artifact Lore - Edged weapons
+N:591:Artifact Lore Vol. I
+G:?:o
+I:8:6:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Hafted weapons
+N:592:Artifact Lore Vol. II
+G:?:o
+I:8:7:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Polearms
+N:593:Artifact Lore Vol. III
+G:?:o
+I:8:8:0
+W:40:0:5:500
+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:100
+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:200
+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:300
+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:400
+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:500
+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:600
+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:700
+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:800
+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:900
+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:1000
+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:1200
+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.
+
+### New Parchments for Theme - monster and artifact spoilers
+
+# XXX
+
+# Artifact Lore - Axes
+N:607:Artifact Lore Vol. IV
+G:?:o
+I:8:22:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Shooters and missiles
+N:608:Artifact Lore Vol. V
+G:?:o
+I:8:23:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Musical instruments
+N:609:Artifact Lore Vol. VI
+G:?:o
+I:8:24:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Body armour
+N:610:Artifact Lore Vol. VII
+G:?:o
+I:8:25:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Cloaks
+N:611:Artifact Lore Vol. VIII
+G:?:o
+I:8:26:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Shields and Boots
+N:612:Artifact Lore Vol. IX
+G:?:o
+I:8:27:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Headgear and Gloves
+N:613:Artifact Lore Vol. X
+G:?:o
+I:8:28:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Gothmog's set and other sets
+N:614:Artifact Lore Vol. XI
+G:?:o
+I:8:29:0
+W:40:0:5:1000
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Light sources
+N:615:Artifact Lore Vol. IX
+G:?:o
+I:8:30:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Amulets and Rings
+N:616:Artifact Lore Vol. X
+G:?:o
+I:8:31:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# Artifact Lore - Tools
+N:617:Artifact Lore Vol. XI
+G:?:o
+I:8:32:0
+W:40:0:5:500
+A:40/3
+D:This parchment contains information about unique
+D:artifacts that are rumoured to exist upon Arda.
+
+# 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
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+
+# 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:A decaying corpse.
+
+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 bony remains of some 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:A severed 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:A white bony skull.
+
+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:A piece of raw meat.
+
+N:646:& Great Eagle Down 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 coat is made from the down of Manwe's Great Eagles,
+D:gathered painstakingly from nests. It is magical, protecting
+D:the wearer from extremes of temperatures.
+
+# The Key to Orthanc -- see artifact list
+N:647:& Key~
+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 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 small curved piece of wood 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 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.
+
+# XXX
+# XXX
+
+# 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
+F:EASY_KNOW
+D:A rune signifying the caster.
+
+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 | EASY_KNOW
+D:A rune signifying a beam or ray.
+
+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 | EASY_KNOW
+D:A rune signifying a ball or sphere.
+
+N:682:Knowledge
+G:?:b
+I:104:91:0
+W:6:5:2:200
+A:6/1
+P:0:1d1:0:0:0
+F:EASY_KNOW
+D:A rune signifying knowledge.
+
+N:683:Life
+G:?:D
+I:104:53:0
+W:3:5:2:200
+A:3/1
+P:0:1d1:0:0:0
+F:EASY_KNOW
+D:A rune signifying life.
+
+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 | EASY_KNOW
+D:A rune signifying flame.
+
+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 | EASY_KNOW
+D:A rune signifying 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 | EASY_KNOW
+D:A rune signifying a lightning beam.
+
+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 | EASY_KNOW
+D:A rune signifying 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 | EASY_KNOW
+D:A rune signifying an element.
+
+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 | EASY_KNOW
+D:A rune signifying raw chaos.
+
+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 | EASY_KNOW
+D:A rune signifying the mind.
+
+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 | EASY_KNOW
+D:A rune signifying the action of holding, or sleep.
+
+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 | EASY_KNOW
+D:A rune signifying an arrow.
+
+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 | EASY_KNOW
+D:A rune signifying a powerful surge.
+
+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 | EASY_KNOW
+D:A rune signifying a powerful blast.
+
+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 | EASY_KNOW
+D:A rune signifying the forces of gravity.
+
+# XXX
+
+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 | EASY_KNOW
+D:A rune signifying undeath.
+
+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 | EASY_KNOW
+D:A rune signifying protection.
+
+# 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:A sprig of a healing plant brought to Middle-Earth by the Numenoreans. Also known
+D:as Kingsfoil, it will cure you from the ravages of the Black Breath.
+
+# 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:100
+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:150
+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:300
+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:400
+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:A piece of clay that looks like it got broken off something.
+
+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:A standard beginner's spellbook with some useful spells.
+D:The cover is blood-stained and weather-worn.
+
+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.
+D:The smell of the cover makes you think of wind.
+
+# 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 - no longer an artifact in Theme
+
+N:743:Learning
+G:!:d
+I:71:12:200
+W:99:2:2:100000
+A:99/10
+P:0:1d1:0:0:0
+F:EASY_KNOW
+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:100
+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:200
+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 bearer.
+
+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, and you hear the screams of tormented souls.
+
+N:765:& Ironclad Tome~ of Aule
+G:?:s
+I:111:63:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:The sleek black cover of this tome is covered with intricate
+D:carved decorations, inset with rubies that sparkle like fire.
+
+N:766:& Shining Tome~ of Varda
+G:?:y
+I:111:64:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:This tome's cover and pages radiate a soft white light.
+
+N:767:& Ocean Tome~ of Ulmo
+G:?:B
+I:111:65:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:This book smells faintly of seaweed and appears to be wet.
+
+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
+D:The cover of this tome reminds you of tree bark.
+D:You feel the smell of grass and flowers as you read it.
+
+# 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:& Holy Tome~ of Mandos
+G:?:w
+I:111:66:0
+W:50:0:30:25000
+A:50/4
+P:0:1d1:0:0:0
+F:FULL_NAME | EASY_KNOW
+D:Just holding this tome makes you fill with quiet strength.
+
+# XXX #
+# XXX #
+# XXX #
+# XXX #
+
+# 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 it 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: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 a snake, able to wriggle out of tight corners, impervious to poisons and poisonous
+D:yourself.
+
+# XXX
+# XXX
+# XXX
+# XXX
+# XXX
+
+# 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:This parchment contains vital information no adventurer can live without.
+
+##### 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, and originates from a demon.
+D:Some demonic energy is still coursing through it.
+
+# 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 depths, where even the Dwarves fear to go.
+
+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 of gold.
+
+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:& Tilkal Rod~ of#
+G:-:v
+I:67:200:0
+W:70:0:15:2000
+A:70/30
+P:0:1d1:0:0:0
+F:ATTR_MULTI
+F:IGNORE_ACID | IGNORE_FIRE | IGNORE_COLD | IGNORE_ELEC
+D:This is the rarest and most powerful kind of rod there is. Its material was once used
+D:to chain Melkor Bauglir in the Halls of Mandos, and only Aule knows the secret of its
+D:making. Treasure this rod greatly.
+
+N:801:& Greater Ration~ of Health
+G:,:g
+I:80:41:0
+W:90:0:2:60000
+A:90/70
+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.
+
+# 808 was 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.
+
+### Dwarven Rings of Power were here
+
+# XXX #
+# XXX #
+# XXX #
+# XXX #
+# XXX #
+# XXX #
+
+### More God relics ###
+
+N:825:& Piece~ of the Relic of Aule
+G:~:v
+I:11:16:0
+W:0:0:0:1000
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE | FULL_NAME
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+D:Although it looks like a piece of junk, it is actually part of an ancient
+D:relic belonging to the Stonewrights. The relic now lies in pieces, hidden
+D:from all but the most faithful followers of Aule.
+
+N:826:& Piece~ of the Relic of Varda
+G:~:v
+I:11:17:0
+W:0:0:0:1000
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE | FULL_NAME
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+D:Although it looks like a piece of junk, it is actually part of an ancient
+D:relic belonging to the Priests of Varda. The relic now lies in pieces,
+D:hidden from all but the most faithful followers of Varda.
+
+N:827:& Piece~ of the Relic of Ulmo
+G:~:v
+I:11:18:0
+W:0:0:0:1000
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE | FULL_NAME
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+D:Although it looks like a piece of junk, it is actually part of an ancient
+D:relic belonging to the Priests of Ulmo. The relic now lies in pieces, hidden
+D:from all but the most faithful followers of Ulmo.
+
+N:828:& Piece~ of the Relic of Mandos
+G:~:v
+I:11:19:0
+W:0:0:0:1000
+A:0/1
+P:0:1d1:0:0:0
+F:SPECIAL_GENE | FULL_NAME
+F:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE |
+D:Although it looks like a piece of junk, it is actually part of an ancient
+D:relic belonging to the Priests of Mandos. The relic now lies in pieces,
+D:hidden from all but the most faithful followers of Mandos.
+
+# XXX Orome's relic goes here #
+# XXX Lorien/Nienna's relic goes here #
+
+### New items for the Theme module ###
+
+### Food ###
+
+N:831:& Pinch~ of Longbottom Leaf
+G:.:g
+I:80:45:10
+#Prized at stores :P
+W:1:1:1:300
+A:1/1
+D:It's chewing tobacco, and not very nutritious. It is said to
+D:contain great stress-relieving properties.
+
+# XXX #
+# XXX #
+# XXX #
+# XXX #
+
+N:836:& Jar~ of Honey
+G:~:y
+I:80:43:1000
+W:1:1:10:50
+A:1/1:5/1:10/1:15/1
+D:A clay jar filled with pure honey. You can 'E'at it.
+
+N:837:& Jug~ of Milk
+G:~:w
+I:80:44:1000
+W:1:1:10:50
+A:1/1
+D:A clay jug filled with fresh milk. You wonder how that is
+D:possible in the dungeon. You can drink it by pressing 'E'.
+
+### Amulets and rings ###
+
+N:838:of War
+G:":d
+I:40:3:3
+W:70:12:3:55000
+A:70/12
+F:BLOWS | STR | DEX
+F:AGGRAVATE | DRAIN_MANA | DRAIN_EXP
+D:This rare amulet will magically improve your fighting prowess
+D:greatly, but this improvement comes at a cost. Nothing can
+D:ignore the challenge this amulet magically issues when worn.
+
+N:839:of Life
+G:":d
+I:40:16:1
+W:70:12:3:100000
+A:70/12
+F:LIFE | SUST_CON | HOLD_LIFE | DRAIN_MANA
+D:This rare amulet will magically increase your life force and
+D:even prevent it from being sapped. However, wearing this
+D:amulet makes you feel less adept at using magic.
+
+# New jewelry from T-Plus by Ingeborg S. Norden
+# They reuse some svals and so colours will match. This will change when ToME3 comes out.
+
+N:840:Wizardry
+G:=:d
+I:45:10:2
+W:100:0:2:125000
+A:100/10
+F:MANA | SPELL | SPELL_CONTAIN | WIELD_CAST | HIDE_TYPE |
+f:IGNORE_ACID | IGNORE_COLD | IGNORE_ELEC | IGNORE_FIRE | SPELL |
+D:This powerful ring not only stores a chosen spell (which must be
+D:engraved by 'copying' it); it also improves the power of cast spells
+D:and augments the wearer's magical reserves.
+
+N:841:Vitality
+G:=:d
+I:45:11:2
+W:100:0:2:125000
+A:100/10
+F:SUST_STR | SUST_DEX | SUST_CON | REGEN |
+F:HOLD_LIFE | LIFE |
+F:HIDE_TYPE | ACTIVATE |
+a:CURE_HUNGER
+f:SUST_STR | SUST_DEX | SUST_CON | REGEN |
+D:This valuable ring enhances and protects the wearer's life force in every way.
+
+N:842:Clear Thought
+G:=:d
+I:45:12:0
+W:100:0:2:125000
+A:100/10
+F:SUST_INT | SUST_WIS | RES_FEAR | RES_CONF |
+F:ACTIVATE | EASY_KNOW |
+a:CURE_INSANITY
+f:SUST_INT | SUST_WIS |
+D:This valuable ring protects the wearer's intellect and intuition, as well as
+D:guarding him from most mental attacks. From time to time, the wearer can
+D:even purge himself of insanity.
+
+### New scroll ###
+N:846:Sterilise
+G:?:d
+I:70:54:0
+W:40:0:5:200
+A:30/1
+D:This scroll humanely neuters all nearby monsters.
+
+### Maps ###
+
+### Massimiliano Marangio's map ###
+N:847:Map of Middle-earth
+G:?:s
+I:8:204:0
+W:70:100:30:100000
+A:70/3
+D:A large map describing all lands of Middle-earth.
+
+# Map of Edoras
+N:848:Map of Edoras
+G:?:s
+I:8:205:0
+W:10:100:5:500
+D:A map of the capital city of Rohan.
+
+# Map of Esgaroth
+N:849:Map of Esgaroth
+G:?:s
+I:8:206:0
+W:15:100:5:1500
+D:A map of the city upon the Long Lake at the foot
+D:of the Lonely Mountain.
+
+# Map of Hobbiton
+N:850:Map of Hobbiton
+G:?:s
+I:8:207:0
+W:5:100:5:150
+D:A map of the famous Halfling village.
+
+# Map of Osgiliath
+N:851:Map of Osgiliath
+G:?:s
+I:8:208:0
+W:25:100:5:10000
+D:A map of the proud stronghold of Gondor.
+
+# Map of Pelargir
+N:852:Map of Pelargir
+G:?:s
+I:8:209:0
+W:25:100:5:10000
+D:A map of the great city of Men at the mouth
+D:of the Anduin river.
+
+# Map of Beorn's domain
+N:853:Map of Beorn's domain
+G:?:s
+I:8:210:0
+W:20:100:5:3000
+D:A map showing the location of the house of Beorn.
+
+# Map of Dale
+N:854:Map of Dale
+G:?:s
+I:8:211:0
+W:10:100:5:200
+D:A map of the area where Dale is being rebuilt.
+
+# Map of Henneth Annun
+N:855:Map of Henneth Annun
+G:?:s
+I:8:212:0
+W:70:100:5:30000
+D:A map showing the secret location of the Ranger
+D:outpost in Ithilien.
+
+# Map of Helm's Deep
+N:856:Map of Helm's Deep
+G:?:s
+I:8:213:0
+W:50:100:5:3000
+D:A map showing the location of the greatest
+D:fortress in Rohan.
+
+# Map of Thranduil's realm
+N:857:Map of Thranduil's realm
+G:?:s
+I:8:214:0
+W:60:100:5:8000
+D:A map showing the location of the secret realm
+D:of the Wood-elves.
+
+# Map of Imladris
+N:858:Map of Imladris
+G:?:s
+I:8:215:0
+W:60:100:5:9000
+
+### Axes from T-Plus ###
+
+N:859:& Bearded Axe~
+G:\:s
+I:24:3:0
+W:8:0:35:100
+P:0:1d6:0:0:0
+A:8/1:15/1
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A throwing-length axe with a "beard", an increased depth at the lower
+D:end of the blade. Surprisingly light and durable, it doubles as a
+D:wood-chopping implement in times of peace.
+
+N:860:& Double Axe~
+G:\:s
+I:24:4:0
+W:25:0:200:450
+P:0:3d6:0:0:0
+A:25/1:40/1
+F:SHOW_MODS | MUST2H
+f:MUST2H
+D:A mighty axe with two moon-shaped blades on each side of the headpiece,
+D:the immense weight leads to slow but powerful swings.
+
+N:861:& Crusader Axe~
+G:\:W
+I:24:6:0
+W:10:0:130:300
+A:10/1:20/1
+P:0:2d5:0:0:0
+F:SHOW_MODS
+D:The predecessor of the Battle Axe. Shorter and lighter, it delivers less
+D:of a punch, but is favored by mounted cavalry.
+
+N:862:& Reaper Axe~
+G:\:W
+I:24:7:0
+W:22:0:150:420
+A:22/1
+P:0:3d4:0:0:0
+F:SHOW_MODS | COULD2H
+f:COULD2H
+D:A large axe with an elongated moon shaped blade. Strikes a good balance
+D:between swinging power and speed.
+
+### New armour adapted from T-Plus (well, I had Mithril Helms before T-Plus
+### did, but they didn't work, so I took them out. :P)
+
+N:863:& Mithril Helm~
+G:]:B
+I:32:8:0
+W:50:0:60:2500
+P:7:1d2:0:0:5
+F:IGNORE_ACID
+D:A cone-shaped helm with cheekguards, made of True-silver.
+
+N:864:& Set~ of Mithril Gauntlets
+G:]:B
+I:31:4:0
+W:50:0:25:3500
+A:50/2
+P:4:1d2:0:0:5
+F:IGNORE_ACID
+D:Flexible and strong set of handguards, with a gripping
+D:surface and surprisingly smooth interior.
+
+N:865:& Small Mithril Shield~
+G:):B
+I:34:7:0
+W:50:0:65:5000
+A:50/2
+P:5:1d2:0:0:5
+F:IGNORE_ACID
+D:A light shield made of a metal alloy, with a mithril
+D:coating.
+
+### Added in Theme
+N:866:& Large Mithril Shield~
+G:):B
+I:34:8:0
+W:50:0:80:7000
+A:50/2
+P:8:1d2:0:0:10
+F:IGNORE_ACID
+D:A sturdy shield made of a metal alloy, with a mithril
+D:coating.
+
+### Artifacts ###
+
+# New artifacts needed for Erebor quest
+
+N:867:Map
+G:?:U
+I:8:33:0
+W:70:100:30:50000
+F:INSTA_ART |
+D:At first glance, this looks like another crumpled piece of paper.
+D:As you smooth out the parchment, you see the faint outline of a
+D:map... You have a feeling you might find use for this map one day.
+D:Better hang on to it, you never know when you might need it.
+
+N:868:& Key~
+G:-:W
+I:11:13:0
+W:70:100:5:50000
+F:INSTA_ART |
+D:As you clear away some mud from this item, you notice that it is
+D:actually a small silver key. You wonder what it opens. Perhaps
+D:you should hang on to this key, you might find it useful later.
+D:Better hang on to it, you never know when you might need it.
+
+# New artifact that acts as a junkart
+N:869:& Cup~
+G:+:y
+I:11:14:0
+W:70:100:50:100000
+F:INSTA_ART |
+D:It looks like a lump of clay, and as you prepare to throw it
+D:away, a piece breaks off, and you see a faint gleam of gold.
+D:Taking off the rest of the caked-on mud, you see that it is
+D:a golden cup with ornate jewels. It may hold hidden powers.
+
+# The Red Arrow of Gondor - see artifact list
+N:870:& Red Arrow~
+G:{:R
+I:11:15:0
+W:40:70:2:70000
+F:INSTA_ART
+D:As you clear away some dirt from this object, you notice that it is an arrow
+D:with steel barbs, black flights, and a red tip. Perhaps it isn't so useless.
+
+# The Sceptre of Numenor - see artifact list
+N:871:& Sceptre~
+G:|:B
+I:12:2:4
+W:50:40:24:80000
+F:INSTA_ART
+D:A beautiful golden sceptre of kings, encrusted with shining gems.
+
+# The Rod of Annuminas - see artifact list
+N:872:& Rod~
+G:|:G
+I:12:3:4
+W:60:50:24:80000
+F:INSTA_ART
+D:A royal sceptre made of gold and silver, inlaid with emeralds.
+
+# The Necklace of Girion - see artifact list
+N:873:& Necklace~
+G:":G
+I:40:4:5
+W:20:5:2:15000
+F:INSTA_ART
+
+# The Amulet of Faramir - see artifact list
+N:874:& Amulet~
+G:":B
+I:40:29:5
+W:10:2:2:10000
+F:INSTA_ART
+
+# The Black Banner of Gondor - see artifact list
+N:875:& Black Banner~
+G:~:D
+I:39:108:0
+W:60:30:50:50000
+F:LITE1 | INSTA_ART
+D:A strange banner of black thread rolled up as though it were a scroll.
+
+# The Pearl 'Nimphelos' - see artifact list
+N:876:& Pearl~
+G:~:w
+I:39:109:1
+W:20:10:10:40000
+P:0:1d1:0:0:0
+F:LITE3 | INSTA_ART | FULL_NAME
+D:A beautiful shining pearl, about the size of a dove's egg.
+
+# The Silmaril of Flames - see artifact list
+N:877:& Silmaril~
+G:*:R
+I:39:110:2
+W:75:90:200:100000
+P:0:10d10:0:0:0
+F:LITE2 | LITE3 | INSTA_ART | FULL_NAME
+D:A dazzling, mesmerizingly beautiful jewel, shining in pure white
+D:light with a slight tinge of red. It hurts your eyes to look at it,
+D:yet you cannot bear to turn away from it, either.
+
+# The Silmaril of the Seas - see artifact list
+N:878:& Silmaril~
+G:*:B
+I:39:111:2
+W:75:90:200:100000
+P:0:10d10:0:0:0
+F:LITE2 | LITE3 | INSTA_ART
+D:A dazzling, mesmerizingly beautiful jewel, shining in pure white
+D:light with a slight tinge of blue. It hurts your eyes to look at it,
+D:yet you cannot bear to turn away from it, either.
+
+### Golden Harp of Thorin - see artifact list.
+N:879:& Golden Harp~
+G:/:y
+I:14:59:2
+W:50:0:30:40000
+P:0:1d1:0:0:0
+F:INSTA_ART
+D:A musical instrument made of pure shining gold.
+
+### Map of the Northern Reaches
+
+N:880:Map of Forodwaith
+G:?:s
+I:8:216:0
+W:40:100:30:40000
+A:40/3
+D:A map showing the northern reaches of Middle-earth.
+
+N:881:& Ranger's Arrow~
+G:{:g
+I:17:3:0
+W:5:7:1:3
+P:0:2d6:0:0:0
+A:5/7:20/7:50/7
+F:SHOW_MODS
+F:SLAY_ANIMAL | ESP_ANIMAL
+D:A sharpened metal head on a piece of wood, fitted with decorative
+D:green feathers. You can use it for 'f'iring a bow.
+
+N:882:& Throwing Axe~
+G:/:s
+I:24:7:0
+A:0/2:5/2:10/2:20/2
+W:0:0:15:30
+P:0:1d3:0:0:0
+F:SHOW_MODS
+D:The smallest and lightest of its kind, this axe has a single blade
+D:with a sharp steel tip, counterbalanced by a pointed fluke.
+
+# Buckler adapted from FuryMod in Theme 1.1.5
+
+N:883:& Buckler~
+G:):U
+I:34:1:1
+W:10:9:50:10
+P:-30:1d1:0:0:0
+A:10/2:15/2:20/2
+F:BLOWS
+f:BLOWS
+D:A small, round fist-shield made of wood, with a central dome
+D:under which there is a handle. Generally used with smaller
+D:arms; the buckler does not afford much protection. Its chief
+D:use is as an aid in fighting.
+
+# Mithril boomerangs adapted from FuryMod in Theme 1.1.5
+
+N:884:& Small Mithril Boomerang~
+G:{:B
+I:15:3:0
+W:25:0:40:300
+A:25/1:35:/2
+P:0:2d5:0:0:0
+F:SHOW_MODS | IGNORE_ACID
+D:A small curved piece of wood with mithril blades on the "forward" edges.
+
+N:885:& Mithril Boomerang~
+G:{:B
+I:15:4:0
+W:35:5:40:1000
+A:35/1:50/2
+P:0:5d6:0:0:0
+F:SHOW_MODS | IGNORE_ACID
+D:A curved leaf-shaped piece of wood, its "forward" edges enhanced with mithril blades.
+
+# N: serial number : & object name~
+# G: symbol : color
+# I: tval : sval : pval
+# W: depth : rarity : weight : cost
+# P: base armor class : base damage : plus to-hit : plus to-dam : plus to-ac
+# A: depth/rarity : depth/rarity : etc
+# F: flag | flag | etc \ No newline at end of file
diff --git a/lib/mods/theme/edit/library.map b/lib/mods/theme/edit/library.map
new file mode 100644
index 00000000..2369fbd6
--- /dev/null
+++ b/lib/mods/theme/edit/library.map
@@ -0,0 +1,62 @@
+# 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
diff --git a/lib/mods/theme/edit/maeglin.map b/lib/mods/theme/edit/maeglin.map
new file mode 100644
index 00000000..e3be9972
--- /dev/null
+++ b/lib/mods/theme/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:0:0:0:0:0:0:2
+
+# 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:0:0:0:0:0:0:2
+
+# 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/mods/theme/edit/misc.txt b/lib/mods/theme/edit/misc.txt
new file mode 100644
index 00000000..98375869
--- /dev/null
+++ b/lib/mods/theme/edit/misc.txt
@@ -0,0 +1,88 @@
+# File: misc.txt
+
+# Maximum number of towns
+M:T:100
+
+# Maximum number of non random towns(must be < 20)
+M:t:17
+
+# 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:519
+
+# Maximum number of monsters in r_info.txt
+# WARNING ! add one more to the real count for the player ghost !!
+M:R:1081
+
+# 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:886
+
+# Maximum number of vaults in v_info.txt
+M:V:108
+
+# Maximum number of terrain features in f_info.txt
+M:F:249
+
+# Maximum number of artifacts in a_info.txt
+M:A:257
+
+# Maximum number of sets types in set_info.txt
+M:s:17
+
+# Maximum number of ego-items in e_info.txt
+M:E:238
+
+# Maximum number of dungeon types in d_info.txt
+M:D:41
+
+# Maximum number of trap types in tr_info.txt
+M:U:176
+
+# Maximum number of terrain types in wf_info.txt
+M:W:64
+
+# Maximum number of owners types in ow_info.txt
+M:N:215
+
+# Maximum number of building actions in ba_info.txt
+M:B:70
+
+# Maximum number of store types in st_info.txt
+M:S:89
+
+# 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:24
+
+# Maximum number of subrace types in p_info.txt
+M:P:S:26
+
+# Maximum number of class types in p_info.txt
+M:P:C:50
+
+# 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:294
+
+# 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/mods/theme/edit/nirnaeth.map b/lib/mods/theme/edit/nirnaeth.map
new file mode 100644
index 00000000..a8c06999
--- /dev/null
+++ b/lib/mods/theme/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:0:0:0:0:0:0:2
+
+# Dirt with Cave Troll
+F:b:88:5:496:0:0:0:0:0:0:2
+
+# Dirt with with Eldrak
+F:c:88:1:620:0:0:0:0:0:0:2
+
+# Dirt with with Ettin
+F:e:88:1:621:0:0:0:0:0:0:2
+
+# Dirt with with War troll
+F:f:88:1:631:0:0:0:0:0:0:2
+
+# Dirt with with Hru
+F:g:88:1:709:0:0:0:0:0:0:2
+
+# Dirt with Ulik the Troll
+F:h:88:5:729:0:0:0:0:0:0:2
+
+# Dirt with Ancient green dragon
+F:i:88:5:618:0:0:0:0:0:0:2
+
+# 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/mods/theme/edit/numenor.txt b/lib/mods/theme/edit/numenor.txt
new file mode 100644
index 00000000..ec8621b1
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/ow_info.txt b/lib/mods/theme/edit/ow_info.txt
new file mode 100644
index 00000000..75683581
--- /dev/null
+++ b/lib/mods/theme/edit/ow_info.txt
@@ -0,0 +1,1410 @@
+# 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
+
+# The zero index owner. If she owns a shop, there is a problem. :P
+N:0:Bell Goodchild(Hobbit)
+I:20000:120
+C:120:100:80
+L:Elf | Half-Elf | High-Elf | Dunadan | Hobbit | Dwarf | RohanKnight
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Druadan | Dark-Elf
+
+### The General Store - 1 ###
+
+N:1:Balin(Dwarf)
+I:25000:130
+C:150:100:50
+L:Dwarf | Petty-Dwarf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Troll
+
+N:2:Berylla Boffin(Hobbit)
+I:20000:130
+C:125:100:70
+L:Human | Hobbit
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+N:3:Adrahil(Half-Elf)
+I:30000:120
+C:110:100:80
+L:Half-Elf | High-Elf | Dunadan | Elf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Dwarf | Troll
+
+N:4:Aegnor(Wood-Elf)
+I:10000:140
+C:110:100:80
+L:High-Elf | Dunadan | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll | Easterling
+
+### The Armoury - 2 ###
+
+N:5:Bifur(Dwarf)
+I:20000:130
+C:125:100:70
+L:Hobbit | Petty-Dwarf | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:6:Lalia Clayhanger(Hobbit)
+I:25000:130
+C:125:100:50
+L:Human | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:7:Alcarin(Human)
+I:10000:140
+C:110:100:80
+L:Human | Half-Elf | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Petty-Dwarf | Troll | Easterling
+
+N:8:Alatariel(High-Elf)
+I:30000:120
+C:110:100:80
+L:High-Elf | Dunadan | Elf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Troll | Easterling
+
+### The Weaponsmith - 3 ###
+
+N:9:Bofur(Dwarf)
+I:30000:120
+C:125:100:70
+L:Human | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:10:Daisy Gamgee(Hobbit)
+I:20000:130
+C:110:100:80
+L:Human | Hobbit
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:11:Beregond(Dunadan)
+I:25000:130
+C:110:100:80
+L:Human | High-Elf | Dunadan | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+N:12:Amarie(Dark-Elf)
+I:10000:140
+C:125:100:50
+L:Dark-Elf | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Petty-Dwarf | Dwarf | Troll | Easterling
+
+### The Temple - 4 ###
+
+N:13:Bombur(Dwarf)
+I:20000:130
+C:125:100:70
+L:Half-Elf | Petty-Dwarf | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:14:Dora Baggins(Hobbit)
+I:30000:120
+C:110:100:80
+L:Human | High-Elf | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:15:Bergil(Half-Elf)
+I:10000:140
+C:125:100:50
+L:Human | Half-Elf | Dunadan | Elf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Troll | Easterling
+
+N:16:Amdir(Wood-Elf)
+I:25000:130
+C:110:100:80
+L:High-Elf | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Dwarf | Troll | Easterling
+
+### The Alchemist - 5 ###
+
+N:17:Borin(Dwarf)
+I:20000:130
+C:110:100:80
+L:Petty-Dwarf | Dwarf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Elf | Troll
+
+N:18:Elfstan Fairbairn(Hobbit)
+I:10000:140
+C:125:100:50
+L:Human | Half-Elf | High-Elf | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Dwarf | Troll
+
+N:19:Cemendur(Human)
+I:25000:130
+C:110:100:80
+L:Human | Half-Elf | RohanKnight | Dunadan | Elf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Troll | Easterling
+
+N:20:Annael(High-Elf)
+I:30000:120
+C:125:100:70
+L:Human | Half-Elf | High-Elf | Dunadan | Elf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Dwarf | Troll
+
+### The Magic Shop - 6 ###
+
+N:21:Dis(Dwarf)
+I:30000:120
+C:125:100:50
+L:Petty-Dwarf | Dwarf | Half-Elf | Dunadan
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Human | Troll | Eagle
+
+N:22:Folco Boffin(Hobbit)
+I:10000:140
+C:110:100:80
+L:Hobbit
+H:Dragon | Demon | Orc | Troll
+
+N:23:Ciryon(Dunadan)
+I:25000:130
+C:110:100:80
+L:Human | Half-Elf | High-Elf | RohanKnight | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+N:24:Arminas(Dark-Elf)
+I:20000:130
+C:125:100:70
+L:Human | Dark-Elf | Elf
+H:Dragon | Demon | Beorning | Orc | High-Elf | Half-Ogre | Petty-Dwarf | Troll | Easterling
+
+### The Black Market - 7 ###
+
+N:25:Dori(Dwarf)
+I:30000:150
+C:110:100:90
+L:Dwarf | Easterling
+H:Dragon | Demon | Orc
+
+N:26:Halfred Greenhand(Hobbit)
+I:30000:150
+C:110:100:90
+L:Hobbit
+H:Dragon | Demon | Troll
+
+N:27:Deorwine(Half-Elf)
+I:30000:150
+C:110:100:90
+L:Half-Elf | Easterling
+H:Dragon | Demon | Dark-Elf
+
+N:28:Artanis(Wood-Elf)
+I:30000:150
+C:110:100:90
+L:High-Elf | Elf | Easterling
+H:Dragon | Demon | Orc | Troll
+
+### The Bookstore - 9 ###
+
+N:29:Dwalin(Dwarf)
+I:20000:130
+C:125:100:70
+L:Human | Petty-Dwarf | Dwarf
+H:Dragon | Demon | Orc | Elf | Half-Ogre | Troll
+
+N:30:Tanta Hornblower(Hobbit)
+I:10000:140
+C:110:100:80
+L:Human | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Troll
+
+N:31:Dorlas(Human)
+I:30000:120
+C:150:100:50
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+N:32:Caranthir(High-Elf)
+I:25000:130
+C:110:100:80
+L:Half-Elf | High-Elf | Elf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Dwarf | Troll | Easterling
+
+### The Pet Shop - 0 ###
+
+N:33:Fili(Dwarf)
+I:20000:130
+C:125:100:70
+L:RohanKnight | Petty-Dwarf | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:34:Lalia Clayhanger(Hobbit)
+I:25000:130
+C:110:100:80
+L:Human | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Troll
+
+N:35:Elfwine(Dunadan)
+I:10000:140
+C:110:100:80
+L:Human | Half-Elf | High-Elf | RohanKnight | Dunadan | Elf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Troll
+
+N:36:Edrahil(Dark-Elf)
+I:30000:120
+C:150:100:50
+L:Dark-Elf | Elf | Easterling
+H:Dragon | Demon | Orc | High-Elf | Half-Ogre | Dwarf | Troll
+
+### The Mayors/Kings/Rulers ###
+
+#Bree
+N:37:Uldrik(Human)
+I:0:0
+C:0:0:0
+L:Human | RohanKnight | Dunadan | Hobbit |
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+#Minas Anor
+N:38:Aragorn (Dunadan)
+I:0:0
+C:0:0:0
+L:Human | High-Elf | RohanKnight | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Petty-Dwarf | Troll
+
+#Khazad-Dum
+N:39:Gimli(Dwarf)
+I:0:0
+C:0:0:0
+L:RohanKnight | Dunadan | Hobbit | Elf | Dwarf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+#Beorn's Halls
+N:40:Deor(Beorning)
+I:100:100
+C:110:100:90
+L:Beorning | Dwarf | Maia | Hobbit | Ent
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+#Dale
+N:41:Bard the Grim(Human)
+I:100:100
+C:110:100:90
+L:Human | Hobbit | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+#Henneth Annun
+N:42:Halbarad(Dunadan)
+I:100:100
+C:110:100:90
+L:Human | High-Elf | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+#Imladris
+N:43:Elrond Half-Elven
+I:100:100
+C:110:100:90
+L:Human | Half-Elf | High-Elf | RohanKnight | Dunadan | Hobbit | Elf | Dwarf | Ent
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+#Helm's Deep
+N:44:Erkenbrand(RohanKnight)
+I:100:100
+C:110:100:90
+L:Human | Maia | RohanKnight | Dunadan | Hobbit | Elf | Dwarf | Ent
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Troll
+
+#Thranduil's Halls
+N:45:Legolas Greenleaf(Wood-Elf)
+I:100:100
+C:110:100:90
+L:High-Elf | Dunadan | Ent | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Petty-Dwarf | Troll
+
+#Edoras
+N:46:Theoden(RohanKnight)
+I:100:100
+C:110:100:90
+L:Human | RohanKnight | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+#Esgaroth
+N:47:The Master(Human)
+I:100:100
+C:110:100:90
+L:Human
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Troll
+
+#Hobbiton
+N:48:Samwise Gamgee(Hobbit)
+I:100:100
+C:110:100:90
+L:Hobbit | Elf | High-Elf | Dark-Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+#Osgiliath
+N:49:Eldacar(Human)
+I:100:100
+C:110:100:90
+L:Human | Half-Elf | High-Elf | RohanKnight | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Dwarf | Troll
+
+#Pelargir
+N:50:Earnil(Dunadan)
+I:100:100
+C:110:100:90
+L:Human | Half-Elf | High-Elf | RohanKnight | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Dwarf | Troll
+
+### Caras Galadhon owners ###
+
+N:51:Galadriel(High-Elf)
+I:15000:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning
+
+N:52:Celeborn(High-Elf)
+I:15000:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning
+
+N:53:Aulendil(Elf)
+I:30000:110
+C:120:100:80
+#L:Warrior |
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning
+
+N:54:Valceronwe(Elf)
+I:30000:110
+C:120:100:80
+#L:Mage | Thaumaturgist | Sorceror
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning
+
+N:55:Voronwe(Elf)
+I:30000:110
+C:120:100:80
+#L:Priest | Paladin
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning
+
+N:56:Celegail(Elf)
+I:30000:110
+C:120:100:80
+#L:Ranger
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning
+
+### Gondolin owners ###
+
+N:57:Turgon(High-Elf)
+I:30000:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:58:Pengolodh(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:59:Aerandir(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:60:Celebrimbor(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+#L:Warrior |
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:61:Lomelosse(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf |
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:62:Arlindel(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+#L:Harper | Ranger
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:63:Sulraen(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+#L:Mage | Sorceror
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:64:Firiel(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf |
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:65:Earendur(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf | Elf
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:66:Glorfindel(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf
+#L:Ranger
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+N:67:Ecthelion(High-Elf)
+I:0:110
+C:120:100:80
+L:High-Elf | Half-Elf
+#L:Paladin
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+### The Inn-Keepers (minus Gondolin) ###
+
+#Bree
+N:68:Barliman Butterbur(Human)
+I:100:120
+C:120:100:80
+L:Dunadan | Hobbit | Human |
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning | Easterling
+
+#Pelargir
+N:69:Ciryatur(Dunadan)
+I:100:120
+C:110:100:80
+L:Dunadan | Human | RohanKnight
+H:Dragon | Demon | Orc | Troll | Half-Ogre | Beorning
+
+#Caras Galadhon
+N:70:Celebor(Elf)
+I:100:120
+C:110:100:80
+L:High-Elf | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+#Minas Anor
+N:71:Bregolas(Human)
+I:100:120
+C:110:100:80
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+#Khazad-Dum
+N:72:Thror(Dwarf)
+I:100:120
+C:110:100:80
+L:Petty-Dwarf | Dwarf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | High-Elf | Troll
+
+#Dale
+N:73:Troin(Dwarf)
+I:100:120
+C:110:100:80
+L:Human | Dwarf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+#Edoras
+N:74:Theodwyn(Shieldmaiden)
+I:100:120
+C:110:100:80
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+#Esgaroth
+N:75:Garm(Human)
+I:100:120
+C:110:100:80
+L:Human | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Petty-Dwarf | Troll
+
+#Hobbiton
+N:76:Rose Cotton(Hobbit)
+I:100:120
+C:110:100:80
+L:Human | High-Elf | Hobbit
+H:Dragon | Demon | Orc | Half-Ogre | Troll | Easterling
+
+#Osgiliath
+N:77:Palantir(Human)
+I:100:120
+C:110:100:80
+L:Human | High-Elf | RohanKnight | Dunadan | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+### The Soothsayers ###
+
+N:78:Ori(Dwarf)
+I:20000:130
+C:125:100:70
+L:Hobbit | Petty-Dwarf | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:79:Tolman Gardner(Hobbit)
+I:25000:130
+C:150:100:50
+L:Human | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:80:Inziladun(Human)
+I:10000:140
+C:110:100:80
+L:Human | Half-Elf | RohanKnight | Dunadan | Easterling
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Petty-Dwarf | Troll
+
+N:81:Gelmir(High-Elf)
+I:30000:120
+C:110:100:80
+L:High-Elf | Dunadan | Elf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Troll
+
+### The Eagles ###
+
+N:82:Palano(Eagle)
+I:30000:130
+C:125:100:50
+L:Eagle
+H:Dragon | Demon | RohanKnight
+
+N:83:Eglad(Eagle)
+I:30000:130
+C:125:100:50
+L:Eagle
+H:Dragon | Demon | Hobbit
+
+N:84:Hiron(Eagle)
+I:30000:130
+C:125:100:50
+L:Eagle
+H:Dragon | Demon | Dunadan
+
+N:85:Grada(Eagle)
+I:30000:130
+C:125:100:50
+L:Eagle
+H:Dragon | Demon | High-Elf
+
+### The Librarians ###
+
+N:86:Frerin(Dwarf)
+I:25000:130
+C:150:100:50
+L:Dwarf | Petty-Dwarf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Troll
+
+N:87:Malva Headstrong(Hobbit)
+I:20000:130
+C:125:100:70
+L:Human | Hobbit
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:88:Erendis(Half-Elf)
+I:30000:120
+C:110:100:80
+L:Half-Elf | High-Elf | Dunadan | Elf | Easterling
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Dwarf | Troll
+
+N:89:Elemmakil(Wood-Elf)
+I:10000:140
+C:110:100:80
+L:High-Elf | Dunadan | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+### The Casino Owners ###
+
+N:90:Fror(Dwarf)
+I:20000:130
+C:125:100:70
+L:Hobbit | Petty-Dwarf | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:91:Marmadas Brandybuck(Hobbit)
+I:25000:130
+C:150:100:50
+L:Human | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:92:Fastred(Human)
+I:10000:140
+C:110:100:80
+L:Human | Half-Elf | RohanKnight | Dunadan | Easterling
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Petty-Dwarf | Troll
+
+N:93:Elured(High-Elf)
+I:30000:120
+C:110:100:80
+L:High-Elf | Dunadan | Elf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Troll
+
+### The Beastmasters ###
+
+N:94:Gloin(Dwarf)
+I:30000:120
+C:125:100:70
+L:Human | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:95:Milo Burrows(Hobbit)
+I:20000:130
+C:110:100:80
+L:Human | Hobbit
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:96:Findegil(Dunadan)
+I:25000:130
+C:110:100:80
+L:Human | High-Elf | Dunadan | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+N:97:Elurin(Dark-Elf)
+I:10000:140
+C:150:100:50
+L:Dark-Elf | Elf | Easterling
+H:Dragon | Demon | Orc | Half-Ogre | Petty-Dwarf | Dwarf | Troll
+
+### Gondor/Rohan owners ###
+
+#Fighters Hall
+
+N:98:Tarcil(Human)
+I:30000:120
+C:150:100:50
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Orc | Half-Ogre | Troll | Easterling
+
+N:99:Ulbar(Easterling)
+I:10000:140
+C:110:100:80
+L:Human | RohanKnight | Dunadan
+L:Half-Elf | High-Elf | Elf | Easterling
+
+N:100:Brego(RohanKnight)
+I:25000:130
+C:110:100:80
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Dark-Elf | Petty-Dwarf | Troll | Easterling
+
+N:101:Ostoher(Dunadan)
+I:20000:130
+C:125:100:70
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Dwarf | Troll | Easterling
+
+#Tower of Magery
+
+N:102:Arveleg(Human)
+I:25000:130
+C:125:100:70
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Orc | Half-Ogre | Troll | Easterling
+
+N:103:Uldar(Easterling)
+I:30000:120
+C:110:100:80
+L:Human | RohanKnight | Dunadan | Easterling
+L:Half-Elf | High-Elf | Elf
+
+N:104:Aldor(RohanKnight)
+I:20000:130
+C:150:100:50
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Dark-Elf | Petty-Dwarf | Troll | Easterling
+
+N:105:Tarannon(Dunadan)
+I:10000:140
+C:110:100:80
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Dwarf | Troll | Easterling
+
+#Inner Temple
+
+N:106:Eradan(Human)
+I:30000:120
+C:125:100:70
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+N:107:Ulwise(Easterling)
+I:25000:130
+C:150:100:50
+L:Human | RohanKnight | Dunadan | Easterling
+L:Half-Elf | High-Elf | Elf
+
+N:108:Gram(RohanKnight)
+I:20000:130
+C:110:100:80
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Dark-Elf | Petty-Dwarf | Troll | Easterling
+
+N:109:Minalcar(Dunadan)
+I:10000:140
+C:110:100:80
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Dwarf | Troll
+
+#Paladins Guild
+
+N:110:Herion(Human)
+I:30000:120
+C:150:100:50
+L:Human | RohanKnight | Dunadan | Easterling
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+N:111:Ulgug(Easterling)
+I:10000:140
+C:110:100:80
+L:Human | RohanKnight | Dunadan | Easterling
+L:Half-Elf | High-Elf | Elf
+
+N:112:Walda(RohanKnight)
+I:25000:130
+C:110:100:80
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Dark-Elf | Petty-Dwarf | Troll | Easterling
+
+N:113:Calimehtar(Dunadan)
+I:20000:130
+C:125:100:70
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Dwarf | Troll | Easterling
+
+#Rangers Guild
+
+N:114:Egalmoth(Human)
+I:25000:130
+C:125:100:70
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Orc | Half-Ogre | Troll | Easterling
+
+N:115:Ulaf(Easterling)
+I:30000:120
+C:110:100:80
+L:Human | RohanKnight | Dunadan | Easterling
+L:Half-Elf | High-Elf | Elf
+
+N:116:Fengel(RohanKnight)
+I:20000:130
+C:150:100:50
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Dark-Elf | Petty-Dwarf | Troll | Easterling
+
+N:117:Telemnar(Dunadan)
+I:10000:140
+C:110:100:80
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Dwarf | Troll
+
+### Dungeon Markets ###
+
+### The Axesmiths ###
+
+N:118:Ris(Dwarf)
+I:30000:150
+C:150:100:50
+L:Dwarf
+H:Dragon | Demon | Orc
+
+N:119:Malach Aradan(Human)
+I:25000:200
+C:125:100:60
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:120:Indis(Half-Elf)
+I:20000:170
+C:115:100:70
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:121:Rogdug(Half-Orc)
+I:10000:130
+C:125:100:80
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Haftedsmiths ###
+
+N:122:Sogur(Dwarf)
+I:20000:170
+C:125:100:80
+L:Dwarf
+H:Dragon | Demon | Orc
+
+N:123:Manwendil(Human)
+I:25000:200
+C:115:100:70
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:124:Lenwe(Half-Elf)
+I:10000:130
+C:150:100:50
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:125:Ghaz(Half-Orc)
+I:30000:150
+C:125:100:60
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Polearmsmiths ###
+
+N:126:Tolin(Dwarf)
+I:10000:130
+C:115:100:70
+L:Dwarf
+H:Dragon | Demon | Orc
+
+N:127:Narmacil(Human)
+I:25000:200
+C:125:100:80
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:128:Lindir(Half-Elf)
+I:20000:170
+C:150:100:50
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:129:Stogash(Half-Orc)
+I:30000:150
+C:125:100:60
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Swordsmiths ###
+
+N:130:Tis(Dwarf)
+I:20000:170
+C:125:100:80
+L:Dwarf
+H:Dragon | Demon | Orc
+
+N:131:Nuneth(Human)
+I:10000:130
+C:125:100:60
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:132:Mahtan(Half-Elf)
+I:30000:150
+C:115:100:70
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:133:Rudak(Half-Orc)
+I:25000:200
+C:150:100:50
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Rare Jewellers ###
+
+N:134:Uin(Dwarf)
+I:25000:200
+C:115:100:70
+L:Dwarf
+H:Dragon | Demon | Orc
+
+N:135:Ornendil(Human)
+I:10000:130
+C:125:100:60
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:136:Malgalad(Half-Elf)
+I:30000:150
+C:125:100:80
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:137:Ghashuf(Half-Orc)
+I:20000:170
+C:150:100:50
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Jewellers ###
+
+N:138:Vali(Dwarf)
+I:25000:200
+C:150:100:50
+L:Dwarf
+H:Dragon | Demon | Orc
+
+N:139:Orodreth(Human)
+I:30000:150
+C:115:100:70
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:140:Theodred(Half-Elf)
+I:10000:130
+C:125:100:80
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:141:Rangush(Half-Orc)
+I:20000:170
+C:125:100:60
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Footwear Shop owners ###
+
+N:142:Nellas(Human)
+I:10000:130
+C:115:100:70
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:143:Tindomiel(Half-Elf)
+I:25000:200
+C:125:100:80
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:144:Ragnor(Half-Elf)
+I:20000:170
+C:150:100:50
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:145:Idrish(Half-Orc)
+I:30000:150
+C:125:100:60
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Rare Footwear Shop owners ###
+
+N:146:Nerwen(Human)
+I:20000:170
+C:125:100:80
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:147:Ulbar (Half-Elf)
+I:10000:130
+C:125:100:60
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:148:Pelendur(Half-Elf)
+I:30000:150
+C:115:100:70
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:149:Budgar(Half-Orc)
+I:25000:200
+C:150:100:50
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Dungeon Librarians ###
+
+N:150:Nom(Human)
+I:25000:200
+C:115:100:70
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:151:Urwen (Half-Elf)
+I:10000:130
+C:125:100:60
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:152:Rian(Half-Elf)
+I:30000:150
+C:125:100:80
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:153:Mosrog(Half-Orc)
+I:20000:170
+C:150:100:50
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Expensive Black Marketeers ###
+
+N:154:Olwe(Human)
+I:30000:200
+C:125:100:90
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:155:Valacar(Half-Elf)
+I:30000:200
+C:125:100:90
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:156:Silmarien(Half-Elf)
+I:30000:200
+C:125:100:90
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:157:Ghaz(Half-Orc)
+I:30000:200
+C:125:100:90
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Common Shop Owners ###
+
+N:158:Ioreth(Human)
+I:25000:200
+C:150:100:50
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:159:Vidugavia(Half-Elf)
+I:30000:150
+C:115:100:70
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:160:Soronto(Half-Elf)
+I:10000:130
+C:125:100:80
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:161:Nazg(Half-Orc)
+I:20000:170
+C:125:100:60
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Dragon Hunters ###
+
+N:162:Oropher(Human)
+I:10000:130
+C:115:100:70
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:163:Walda(Half-Elf)
+I:25000:200
+C:125:100:80
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:164:Mithrellas(Half-Elf)
+I:20000:170
+C:150:100:50
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:165:Urbag(Half-Orc)
+I:30000:150
+C:125:100:60
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Speed Ring Market Owners ###
+
+N:166:Orophin(Human)
+I:20000:170
+C:125:100:80
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:167:Wulf(Half-Orc)
+I:10000:130
+C:125:100:60
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+N:168:Baguk(Half-Troll)
+I:30000:150
+C:115:100:70
+L:Troll | Easterling
+H:Dragon | Demon | Human
+
+N:169:Zikram(Half-Orc)
+I:25000:200
+C:150:100:50
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Scribes ###
+
+N:170:Rumil(Human)
+I:25000:200
+C:115:100:70
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:171:Saeros(Half-Elf)
+I:10000:130
+C:125:100:60
+L:Half-Elf | Easterling
+H:Dragon | Demon | Half-Ogre
+
+N:172:Zartosh(Half-Orc)
+I:30000:150
+C:125:100:80
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+N:173:Shog(Half-Troll)
+I:20000:170
+C:150:100:50
+L:Troll | Easterling
+H:Dragon | Demon | Human
+
+### The Potion Peddlers ###
+
+N:174:Zamin(Human)
+I:25000:200
+C:150:100:50
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:175:Algosh(Half-Orc)
+I:30000:150
+C:115:100:70
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+N:176:Seghash(Half-Troll)
+I:10000:130
+C:125:100:80
+L:Troll | Easterling
+H:Dragon | Demon | Human
+
+N:177:Kabbug(Half-Orc)
+I:20000:170
+C:125:100:60
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+### The Master Archers ###
+
+N:178:Palin(Dwarf)
+I:30000:120
+C:125:100:70
+L:Human | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+N:179:Wilcome Cotton(Hobbit)
+I:20000:130
+C:110:100:80
+L:Human | Hobbit
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+N:180:Inzilbeth(Dunadan)
+I:25000:130
+C:110:100:80
+L:Human | High-Elf | Dunadan | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll
+
+N:181:Gildor Inglorion(Dark-Elf)
+I:10000:140
+C:150:100:50
+L:Dark-Elf | Elf | Easterling
+H:Dragon | Demon | Orc | Half-Ogre | Petty-Dwarf | Dwarf | Troll
+
+### Theme stores ###
+
+### The Miners / Builders ###
+
+N:182:Gror(Dwarf)
+I:20000:130
+C:125:100:70
+L:Half-Elf | Petty-Dwarf | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:183:Saradoc Oldbuck(Hobbit)
+I:30000:120
+C:110:100:80
+L:Human | High-Elf | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:184:Gloredhel(Half-Elf)
+I:10000:140
+C:150:100:50
+L:Human | Half-Elf | Dunadan | Elf | Easterling
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Troll
+
+N:185:Erellont(Wood-Elf)
+I:25000:130
+C:110:100:80
+L:High-Elf | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Dwarf | Troll | Easterling
+
+### The Hunters ###
+
+N:186:Kili(Dwarf)
+I:20000:130
+C:110:100:80
+L:Petty-Dwarf | Dwarf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Elf | Troll
+
+N:187:Ruby Bolger(Hobbit)
+I:10000:140
+C:150:100:50
+L:Human | Half-Elf | High-Elf | Hobbit | Elf | Easterling
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Dwarf | Troll
+
+N:188:Goldwine(Human)
+I:25000:130
+C:110:100:80
+L:Human | Half-Elf | RohanKnight | Dunadan | Elf | Easterling
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Troll
+
+N:189:Erestor(High-Elf)
+I:30000:120
+C:125:100:70
+L:Human | Half-Elf | High-Elf | Dunadan | Elf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Dwarf | Troll | Easterling
+
+### The Runecrafters ###
+
+N:190:Nori(Dwarf)
+I:30000:120
+C:150:100:50
+L:Petty-Dwarf | Dwarf | Half-Elf | Dunadan
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Human | Troll | Eagle
+
+N:191:Camellia Sackville(Hobbit)
+I:10000:140
+C:110:100:80
+L:Hobbit | Easterling
+H:Dragon | Demon | Orc | Troll
+
+N:192:Hador Lorindol(Dunadan)
+I:25000:130
+C:110:100:80
+L:Human | Half-Elf | High-Elf | RohanKnight | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Orc | Half-Ogre | Troll | Easterling
+
+N:193:Galathil(Dark-Elf)
+I:20000:130
+C:125:100:70
+L:Human | Dark-Elf | Elf
+H:Dragon | Demon | Beorning | Orc | High-Elf | Half-Ogre | Petty-Dwarf | Troll
+
+### The Musicians ###
+
+N:194:Oin(Dwarf)
+I:20000:130
+C:125:100:70
+L:Human | Petty-Dwarf | Dwarf
+H:Dragon | Demon | Orc | Elf | Half-Ogre | Troll | Easterling
+
+N:195:Robin Smallburrow(Hobbit)
+I:10000:140
+C:110:100:80
+L:Human | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Troll | Easterling
+
+N:196:Hareth(Half-Elf)
+I:30000:120
+C:150:100:50
+L:Human | RohanKnight | Dunadan
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+N:197:Galdor(Wood-Elf)
+I:25000:130
+C:110:100:80
+L:Half-Elf | High-Elf | Elf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Petty-Dwarf | Dwarf | Troll | Easterling
+
+### The Precious Metalsmiths ###
+
+N:198:Gabil(Dwarf)
+I:10000:130
+C:115:100:70
+L:Dwarf
+H:Dragon | Demon | Orc
+
+N:199:Isil(Human)
+I:25000:200
+C:125:100:80
+L:Human | Easterling
+H:Dragon | Demon | Troll
+
+N:200:Grima(Half-Orc)
+I:20000:170
+C:150:100:50
+L:Orc | Easterling
+H:Dragon | Demon | Elf
+
+N:201:Kosh(Half-Troll)
+I:30000:150
+C:125:100:60
+L:Troll | Easterling
+H:Dragon | Demon | Human
+
+### The Mapmakers ###
+
+N:202:Pas(Dwarf)
+I:20000:130
+C:125:100:70
+L:Half-Elf | Petty-Dwarf | Dwarf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll
+
+N:203:Isumbras Took(Hobbit)
+I:30000:120
+C:110:100:80
+L:Human | High-Elf | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
+
+N:204:Labadal(Half-Elf)
+I:10000:140
+C:150:100:50
+L:Human | Half-Elf | Dunadan | Elf | Easterling
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Troll
+
+N:205:Guilin(Wood-Elf)
+I:25000:130
+C:110:100:80
+L:High-Elf | Dunadan | Hobbit | Elf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Dwarf | Troll
+
+### The Farmers ###
+
+N:206:Rili(Dwarf)
+I:20000:130
+C:110:100:80
+L:Petty-Dwarf | Dwarf
+H:Dragon | Demon | Orc | Dark-Elf | Half-Ogre | Elf | Troll
+
+N:207:Wiseman Gamwich(Hobbit)
+I:10000:140
+C:150:100:50
+L:Human | Half-Elf | High-Elf | Hobbit | Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Dwarf | Troll
+
+N:208:Lalaith(Human)
+I:25000:130
+C:110:100:80
+L:Human | Half-Elf | RohanKnight | Dunadan | Elf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Troll | Easterling
+
+N:209:Gwindor(High-Elf)
+I:30000:120
+C:125:100:70
+L:Human | Half-Elf | High-Elf | Dunadan | Elf
+H:Dragon | Demon | Beorning | Orc | Dark-Elf | Half-Ogre | Dwarf | Troll
+
+### The Old Mage in Minas Anor ###
+
+N:210:Malbeth the Seer
+I:20000:130
+C:110:100:80
+L:Human | RohanKnight | Dunadan | High-Elf
+H:Dragon | Demon | Beorning | Orc | Half-Ogre | Troll | Easterling
diff --git a/lib/mods/theme/edit/p_info.txt b/lib/mods/theme/edit/p_info.txt
new file mode 100644
index 00000000..89d335ab
--- /dev/null
+++ b/lib/mods/theme/edit/p_info.txt
@@ -0,0 +1,2834 @@
+# 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.
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# 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:3
+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:g:Varda Elentari
+C:a:g:Aule the Smith
+C:a:g:Ulmo
+C:a:g:Mandos
+
+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:g:Varda Elentari
+C:a:g:Aule the Smith
+C:a:g:Ulmo
+C:a:g:Mandos
+
+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:b:20:Far reaching attack
+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:a:N:Wainrider
+C:a:D:A proud warrior riding a majestic chariot.
+C:a:D:Wainriders are traditionally aligned with Melkor.
+C:a:O:71:10:1d1
+C:a:O:23:32:1d1
+C:a:g:Melkor Bauglir
+C:a:k:+0:+600: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:+0:+500:Critical-hits
+C:a:k:+0:+500:Magic
+C:a:k:+0:+500:Spirituality
+C:a:k:+1000:+200:Prayer
+C:a:k:=0:=0:Antimagic
+
+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:+2000:+50:Magic
+C:a:k:=0:=0:Spell-power
+C:a:k:=0:=0:Mana
+C:a:k:=0:=0:Fire
+C:a:k:=0:=0:Water
+C:a:k:=0:=0:Air
+C:a:k:=0:=0:Earth
+C:a:k:=0:=0:Temporal
+C:a:k:=0:=0:Divination
+C:a:k:=0:=0:Conveyance
+C:a:k:=0:=0:Nature
+C:a:k:=0:=0:Meta
+C:a:k:=0:=0:Mind
+C:a:k:=0:=0:Necromancy
+C:a:k:=0:=0:Runecraft
+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
+
+### Clairvoyant - new class added in Theme
+
+C:a:N:Clairvoyant
+C:a:D:The master of the mind who sacrifices breadth of
+C:a:D:ability for additional favour with their deity.
+C:a:k:=0:=0:Mana
+C:a:k:=0:=0:Fire
+C:a:k:=0:=0:Water
+C:a:k:=0:=0:Air
+C:a:k:=0:=0:Earth
+C:a:k:=0:=0:Conveyance
+C:a:k:=0:=0:Nature
+C:a:k:=0:=0:Temporal
+C:a:k:=0:=0:Meta
+C:a:k:=0:=0:Necromancy
+C:a:k:=0:=0:Runecraft
+C:a:k:=0:=0:Thaumaturgy
+C:a:k:+1000:+300:Divination
+C:a:k:+1000:+300:Mind
+C:a:k:+0:+500:Spirituality
+C:a:k:+0:+500:Prayer
+C:a:k:+1000:+700:Mindcraft
+C:a:O:111:50:1d1
+C:a:O:21:3:1d1
+
+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:the nature and divination magic schools, and trapping.
+C:a:k:+0:+200:Weaponmastery
+C:a:k:+0:+400:Magic
+C:a:k:+0:+500:Nature
+C:a:k:+0:+500:Divination
+C:a:k:+0:+700:Disarming
+C:a:k:+0:+50:Sneakiness
+C:a:k:+0:+200:Monster-lore
+C:a:k:+0:+300:Summoning
+C:a:O:19:12:1d1
+C:a:O:17:1:10d3
+C:a:O:23:10:1d1
+C:a:O:46:1:1d1
+C:a:g:Nobody
+C:a:g:Manwe Sulimo
+C:a:g:Tulkas
+C:a:g:Yavanna Kementari
+C:a:g:Varda Elentari
+C:a:g:Aule the Smith
+C:a:g:Ulmo
+C:a:g:Mandos
+C:a:b:1:Trapping
+
+C:a:N:Sniper
+C:a:D:Snipers are very stealthy archers without much hand-to-hand combat
+C:a:D:ability, but with a penchant to disappear when it suits them.
+C:a:k:+0:-300:Combat
+C:a:k:+1000:+100:Sneakiness
+C:a:k:+1000:+700:Stealth
+C:a:k:+0:+100:Disarming
+C:a:k:+1000:+700:Backstab
+C:a:k:+0:+300:Magic
+C:a:k:+1000:+500:Conveyance
+C:a:O:19:12:1d1
+C:a:O:17:1:10d3
+C:a:g:All Gods
+
+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:-550:Magic-Device
+
+C:a:N:Mercenary
+C:a:D:Mercenaries are daring swashbucklers, masters of the blade.
+C:a:k:+0:+200:Weaponmastery
+C:a:k:+0:+400:Sword-mastery
+C:a:k:+0:+200:Critical-hits
+C:a:k:+0:+050:Magic-Device
+C:a:k:=0:=0:Conveyance
+C:a:k:=0:=0:Divination
+C:a:k:+0:+500:Temporal
+C:a:b:15:Extra Max Blow(2)
+C:a:O:23:7:1d1
+C:a:O:35:1:1d1
+
+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: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:g:All Gods
+
+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:g:All Gods
+
+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:g:All Gods
+
+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:g:All Gods
+
+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:g:All Gods
+
+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:g:All Gods
+
+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:a:g:All Gods
+
+C:a:N:Ascetic
+C:a:D:The monk who has sworn off all worldly things.
+C:a:D:The ascetics do not believe in magic or gods.
+C:a:k:+0:+200:Combat
+C:a:k:=0:=0:Weaponmastery
+C:a:k:+0:+200:Barehand-combat
+C:a:k:+1000:+1000:Antimagic
+C:a:k:+0:+200:Disarming
+C:a:k:+0:+300:Stealth
+C:a:k:+0:+1000:Dodging
+C:a:k:=0:=0:Magic
+C:a:k:=0:=0:Magic-Device
+C:a:k:=0:=0:Spell-power
+C:a:k:=0:=0:Meta
+C:a:k:=0:=0:Conveyance
+C:a:k:=0:=0:Divination
+C:a:k:=0:=0:Temporal
+C:a:k:=0:=0:Nature
+C:a:k:=0:=0:Prayer
+C:a:k:=0:=0:Music
+C:a:k:=0:=0:Summoning
+C:a:k:=0:=0:Symbiosis
+C:a:k:=0:=0:Possession
+C:a:k:=0:=0:Mimicry
+C:a:b:10:Spread blows
+C:a:b:1:Extra Max Blow(1)
+C:a:b:5:Extra Max Blow(2)
+C:a:O:71:37:1d1
+C:a:g:Nobody
+
+C:N:5:Pacifist
+C:D:0:Pacifists do not believe in violence and do not want to kill
+C:D:0:everything in sight.
+C:D:1:Novice
+C:D:1:Appeaser
+C:D:1:Arbitrator
+C:D:1:Elf-Friend
+C:D:1:Conciliator
+C:D:1:Diplomat
+C:D:1:Mediator
+C:D:1:Negotiator
+C:D:1:Pacifist
+C:D:1:Peacemaker
+C:S:0:2:2:2:0:4:0:0
+C:K:10:10:0:1:10:10:10:10
+C:X:0:0:0:0:0:0:0:0
+C:P:0:0
+C:B:0:0:0
+C:C:H:H:20000:2:40
+C:Z:detect doors and traps
+C:E:-1:0:0:0:0:0
+C:k:=0:=0:Combat
+C:k:=0:=0:Weaponmastery
+C:k:=0:=0:Archery
+C:k:=0:=0:Barehand-combat
+C:k:+0:+600:Sneakiness
+C:k:+0:+600:Stealth
+C:k:+1000:+700:Dodging
+C:k:+1000:+600:Disarming
+C:k:+1000:+600:Magic
+C:k:=0:=0:Magic-Device
+C:k:+0:+500:Spell-power
+C:k:+1000:+1000:Nature
+C:k:+0:+700:Spirituality
+C:k:+1000:+400:Monster-lore
+C:k:=0:=0:Possession
+C:k:=0:=0:Corpse-preservation
+C:k:=0:=0:Mimicry
+C:k:=0:=0:Music
+C:b:15:Perfect casting
+C:g:All Gods
+
+C:a:N:Trapper
+C:a:D:These pacifists use traps to snare monsters, and
+C:a:D:make totems from corpses to summon aid.
+C:O:46:1:1d1
+C:a:k:+1000:+600:Summoning
+C:a:b:1:Trapping
+C:a:b:10:Ammo creation
+
+C:a:N:Peace-mage
+C:a:D:These pacifists use magic to escape danger, and
+C:a:D:rely on symbiotic relationships to defend themselves.
+C:a:O:70:6:1d1
+C:a:k:+0:+700:Meta
+C:a:k:+1000:+700:Conveyance
+C:a:k:+0:+700:Divination
+C:a:k:+0:+700:Temporal
+C:a:k:+1000:+600:Symbiosis
+
+C:N:6: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:g:Varda Elentari
+C:a:g:Aule the Smith
+C:a:g:Ulmo
+C:a:g:Mandos
+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
+
+C:a:N:Stonewright
+C:a:D:A war-priest of Aule who serves to protect and
+C:a:D:further the works of the Valarin Smith.
+C:a:O:24:8:1d1
+C:a:g:Aule the Smith
+C:a:k:+0:+400:Axe-mastery
+C:a:k:+0:+300:Prayer
+C:a:k:+0:-600:Necromancy
+C:a:k:+0:+100:Spell-power
+C:a:b:10:Extra Max Blow(1)
+
+C:a:N:Priest(Varda)
+C:a:D:A priest who serves Varda to bring light
+C:a:D:to all the reaches of the world.
+C:a:O:21:5:1d1
+C:a:g:Varda Elentari
+C:a:k:+0:+300:Prayer
+C:a:k:+0:-600:Necromancy
+C:a:k:+0:+100:Spell-power
+C:a:k:+0:+400:Archery
+
+C:a:N:Priest(Ulmo)
+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:22:5:1d1
+C:a:g:Ulmo
+C:a:k:+0:+400:Polearm-mastery
+C:a:k:+0:+300:Prayer
+C:a:k:+0:-600:Necromancy
+C:a:k:+0:+100:Spell-power
+
+C:a:N:Priest(Mandos)
+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:Mandos
+C:a:k:+0:+400:Hafted-mastery
+C:a:k:+0:+300:Prayer
+C:a:k:+0:-600:Necromancy
+C:a:k:+0:+100:Spell-power
+
+###############################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 | Pacifist
+
+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 | Pacifist
+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 | Pacifist
+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:0
+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 | Pacifist
+R:k:+0:+300:Sling-mastery
+R:k:+1500:+000:Disarming
+R:k:+1800:+000:Magic-Device
+R:k:+9000:+000:Spirituality
+R:k:+6000:+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 | Pacifist
+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 | LITE1
+R:C:Warrior | Priest | Pacifist
+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:+0:+200:Hafted-mastery
+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 | Pacifist
+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 | Pacifist
+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 | Pacifist
+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
+
+### Druedain - replace Kobolds in Theme
+
+R:N:12:Druadan
+R:D:Druedain are an ancient branch of the race of Men.
+R:D:Wiser and quicker than the Edain, but weaker and less intelligent.
+R:D:Not as pretty as their common human cousins, but sturdier.
+R:S:-2:-3:2:3:2:-2:1
+R:K:5:0:0:5:15:15:0:5
+R:P:9:115:0:82
+R:M:14:6:65:6:162:25:58:4:145:20
+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 | Loremaster | Pacifist
+R:k:+0:+250:Boomerang-mastery
+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 | LITE1
+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 | RES_POIS
+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 | Pacifist
+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 | Pacifist
+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
+
+### Eagles - replace Thunderlords
+
+R:N:17:Eagle
+R:D:A Great Eagle of Manwe, his most faithful servant.
+R:D:They have been given many gifts from their master.
+R:S:6:2:1:-2:3:6:4
+R:K:6:0:10:-16:30:10:0:0
+R:P:12:300:5:89
+R:M:14:6:180:6:255:25:150:4:230:20
+R:E:0:1:0:4:1:1
+R:R:1:0
+R:F:FEATHER | FLY
+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:RES_FIRE |
+R:R:35:0
+R:F:RES_POIS |
+R:R:45:0
+R:F:IM_ELEC |
+R:R:50:0
+R:F:CLIMB
+R:k:+1000:+300:Barehand-combat
+R:C:Loremaster | Mage | Priest | Pacifist
+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
+
+### Base race characteristics tweaked from Annals of Ea for Theme.
+
+R:N:18:Dragon
+R:D:One of Morgoth's favourite pets.
+R:D:Very strong and smart, but unstealthy.
+R:D:They cannot play instruments or wield weapons.
+# R:S:str:int:wis:dex:con:chr:luck
+R:S:3:2:2:-2:2:-5:-2
+R:K:5:5:5:-10:5:5:5:-20
+R:P:9:250:5:100
+R:M:50:150:200:-200:80:120:54:-50:100:60
+# R:E:weapons:torso:arms:finger:head:legs
+R:E:0:1:0:6:1:1
+R:R:1:0
+R:k:=0:=0:Weaponmastery
+R:k:+0:+400:Barehand-combat
+R:k:=0:=0:Geomancy
+R:F:FLY
+R:C:Loremaster | Mage | Priest | Pacifist
+#R:G:EVIL
+R:k:+1000:+000:Disarming
+R:k:+500:+000:Magic-Device
+R:k:-4000:+000:Spirituality
+R:k:-10000:+000:Stealth
+R:k:+000:+000:Sneakiness
+
+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 | Pacifist
+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
+
+### Wood-Elves tweaked in Theme.
+
+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:D:They are more dangerous but less wise than High Elves.
+R:S:2:2:-3:5:0:1:0
+R:K:5:6:6:5:8:12:-5: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 | Pacifist
+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:+1000:+000:Weaponmastery
+R:k:+4000:+000:Archery
+
+### Maiar heavily tweaked in Theme
+
+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: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 | Pacifist
+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
+
+### Easterling -- new race added in Theme.
+
+R:N:22:Easterling
+R:D:The humans of the Southeast, servants of the Dark.
+R:D:They are traditional masters of many combat skills.
+R:D:Fanatical warriors, they do not trust magic.
+R:S:2:-2:-2:-2:2:-1:-1
+R:K:0:-5:-1:0:0:10:5:5
+R:P:10:140:0:105
+R:M:14:6:72:6:180:25:66:4:150:20
+R:E:1:1:1:2:1:1
+R:C:Warrior | Archer
+R:G:XTRA_MIGHT_XBOW | NO_STUN
+R:R:5:0
+R:F:FREE_ACT
+R:R:15:0
+R:F:RES_CONF
+R:k:+1000:+100:Combat
+R:k:+500:+100:Weaponmastery
+R:k:+0:+200:Sword-mastery
+R:k:+0:+200:Axe-mastery
+R:k:+0:+200:Hafted-mastery
+R:k:+0:+200:Polearm-mastery
+R:k:+2000:+200:Archery
+R:k:+1000:+300:Sneakiness
+R:k:+1000:+200:Stealth
+R:k:+1000:+200:Disarming
+R:k:=0:=0:Magic
+R:k:=0:=0:Magic-Device
+R:k:=0:=0:Spell-power
+R:k:+2500:+300:Spirituality
+
+### Demon -- new race added in Theme
+
+R:N:23:Demon
+R:D:Demons (Roeg) are minor servants of the Dark,
+R:D:they were once natural creatures and have been
+R:D:corrupted by Melkor to serve his ends.
+# They all get an inherent CHR penalty, other stats depend on subrace.
+R:S:0:0:0:0:0:-1:0
+# Again, here everything depends on subrace
+R:K:0:0:0:0:0:0:0:0
+R:P:10:170:3:109
+R:M:14:6:72:6:180:25:66:4:150:20
+# Everything at zero here, depends on type of demon.
+R:E:0:0:0:0:0:0
+R:C:Warrior | Archer | Mage | Rogue | Priest | Loremaster
+R:G:CORRUPT
+R:F:RES_FEAR | HOLD_LIFE | RES_DARK
+R:Z:spear of darkness
+R:k:+500:+000:Disarming
+R:k:+050:+000:Magic-Device
+R:k:-5000:+000:Spirituality
+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:Druadan | Petty-Dwarf | Dark-Elf | Ent | RohanKnight | Eagle |
+S:A:Yeek | Wood-Elf | Maia | Easterling
+
+# 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 | Druadan | Petty-Dwarf |
+S:A:Dark-Elf | RohanKnight | Yeek | Easterling
+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 | Easterling |
+S:A:Druadan | 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 | Easterling |
+S:A:Druadan | 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 | Easterling |
+S:A:Druadan | 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 | Druadan | Easterling
+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:Eagle | 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:Half-Elf | Elf | High-Elf | Dark-Elf | Wood-Elf | Maia |
+S:A:Hobbit | Gnome | Dwarf | Petty-Dwarf | Ent | Eagle |
+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
+
+### Dragon subraces added for Theme
+
+S:N:10:Red
+S:D:B:These majestic creatures are surrounded by a furious blaze.
+S:D:B:They are especially resistant to fire and with time learn
+S:D:B:to resist its effects entirely. They are stronger than other
+S:D:B:dragons and can prevent attacks from sapping their strength.
+S:S:3:0:0:0:0:0:0:100
+S:K:0:0:0:0:-2:2:5:0
+S:P:0:0:1
+S:M:0:0:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+S:A:Dragon
+S:C:F:Warrior | Archer
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SUST_STR | SH_FIRE | RES_FIRE
+S:R:5:1
+S:F:STR
+S:R:10:1
+S:F:STR
+S:R:15:1
+S:F:STR
+S:R:20:1
+S:F:STR
+S:R:30:1
+S:F:STR
+S:R:40:1
+S:F:STR
+S:R:45:0
+S:F:IM_FIRE
+S:R:50:1
+S:F:STR | CLIMB
+S:Z:fire breath
+S:k:+1000:+600:Fire
+
+S:N:11:Black
+S:D:B:The hide of these dragons glistens with droplets of viscous
+S:D:B:liquid. They are especially resistant to acid and with time
+S:D:B:will come to resist its effects entirely. They look more
+S:D:B:beautiful than other dragons and are expert at protecting that.
+S:S:0:0:0:0:0:3:0:100
+S:K:2:0:1: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:Dragon
+S:C:F:Archer | Warrior
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SUST_CHR | | RES_ACID
+S:R:5:1
+S:F:CHR
+S:R:10:1
+S:F:CHR
+S:R:15:1
+S:F:CHR
+S:R:20:1
+S:F:CHR
+S:R:30:1
+S:F:CHR
+S:R:40:1
+S:F:CHR
+S:R:45:0
+S:F:IM_ACID
+S:R:50:1
+S:F:CHR | CLIMB
+S:Z:spit acid
+S:k:+1000:+600:Water
+
+S:N:12:Green
+S:D:B:The entire forms of these creatures give off sickly greenish.
+S:D:B:vapours. They are especially resistant to poison and resist
+S:D:B:poison attacks exceptionally well. They are healthier and sturdier
+S:D:B:than other dragons and are not as susceptible to diseases.
+S:S:0:0:0:0:3:0:0:100
+S:K:0:0:2: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:Dragon
+S:C:F:Archer | Warrior
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SUST_CON | RES_POIS
+S:R:5:1
+S:F:CON
+S:R:10:1
+S:F:CON
+S:R:15:1
+S:F:CON
+S:R:20:1
+S:F:CON
+S:R:30:1
+S:F:CON
+S:R:40:1
+S:F:CON
+S:R:50:1
+S:F:CON | CLIMB
+S:Z:poison dart
+S:k:+1000:+600:Air
+
+S:N:13:Blue
+S:D:B:These enormous creatures are wreathed in living lightning.
+S:D:B:They are especially nimble and agile, and their dexterity
+S:D:B:is not sapped easily.
+S:S:0:0:0:3:0:0:0:100
+S:K:3:0:0:2:1:0:0:0
+S:P:0:0:1
+S:M:0:0:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+S:A:Dragon
+S:C:F:Archer | Warrior
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SUST_DEX | RES_ELEC | SH_ELEC
+S:R:5:1
+S:F:DEX
+S:R:10:1
+S:F:DEX
+S:R:15:1
+S:F:DEX
+S:R:20:1
+S:F:DEX
+S:R:30:1
+S:F:DEX
+S:R:40:1
+S:F:DEX
+S:R:45:0
+S:F:IM_ELEC
+S:R:50:1
+S:F:DEX | CLIMB
+S:Z:dazzle
+S:k:+1000:+600:Earth
+
+S:N:14:White
+S:D:B:These enormous dragons look like they'd been hewed from ice.
+S:D:B:They are the most cunning and calculating among their kind,
+S:D:B:and attacks against their intelligence are unlikely to succeed.
+S:S:0:3:0:0:0:0:0:100
+S:K:0:2: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:Dragon
+S:C:F:Archer | Warrior
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SUST_INT | RES_COLD | SENS_FIRE
+S:R:5:1
+S:F:INT
+S:R:10:1
+S:F:INT
+S:R:15:1
+S:F:INT
+S:R:20:1
+S:F:INT
+S:R:30:1
+S:F:INT
+S:R:40:1
+S:F:INT
+S:R:45:0
+S:F:IM_COLD
+S:R:50:1
+S:F:INT | CLIMB
+S:Z:cold breath
+S:k:+1000:+600:Divination
+
+S:N:15:Ethereal
+S:D:B:These dragons' body surfaces seem to be in constant flux.
+S:D:B:They are powerful undead beings and are thus wiser than other
+S:D:B:dragons. Wisdom-sapping and life-draining attacks are not
+S:D:B:very effective against them.
+S:S:0:0:3:0:0:0:0:100
+S:K:0:0:1:5:2:2:0:0
+S:P:0:0:3
+S:M:0:0:0:0:0:0:0:0:0:0
+S:E:0:0:0:0:0:0
+S:A:Dragon
+S:C:F:Archer | Warrior
+S:G:NO_SUBRACE_CHANGE | UNDEAD | SEMI_WRAITH
+S:R:1:0
+S:F:SUST_WIS | HOLD_LIFE
+S:R:5:1
+S:F:WIS
+S:R:10:1
+S:F:WIS
+S:R:15:1
+S:F:WIS
+S:R:20:1
+S:F:WIS
+S:R:30:1
+S:F:WIS
+S:R:40:1
+S:F:WIS
+S:R:50:1
+S:F:WIS | CLIMB
+S:Z:chaos breath
+S:k:+1000:+600:Meta
+
+### Demon subraces for Theme module
+
+S:N:16:(Narrog)
+S:D:A:These ratlike demons have fangs that drip with venom,
+S:D:A:quick, scurrying feet, and winglike appendages on their
+S:D:A:slick, black backs. They are somewhat weak and sickly,
+S:D:A:but sly, cunning, and agile.
+S:S:-1:1:1:1:-1:-2:0:100
+S:K:2:0:1:2:2:20:0:0
+S:P:1:20:1
+S:M:14:6:72:6:180:25:66:4:150:20
+S:E:1:1:1:2:1:1
+S:A:Demon
+S:C:F:Priest
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:RES_POIS
+S:R:10:2
+S:F:SPEED
+S:R:30:2
+S:F:SPEED
+S:R:45:0
+S:F:FLY
+S:R:50:2
+S:F:SPEED
+S:Z:poison dart
+R:k:-200:+000:Weaponmastery
+R:k:+1000:+300:Archery
+R:k:+1000:+000:Sneakiness
+R:k:+500:+200:Disarming
+S:k:+1000:+500:Stealth
+R:k:+050:+000:Magic-Device
+S:k:+0:+200:Magic
+S:k:+1000:+300:Conveyance
+R:k:-500:+000:Spirituality
+
+S:N:17:(Aewrog)
+S:D:A:These birdlike demons' wings flap with blinding speed.
+S:D:A:They are somewhat physically weak, but may befuddle enemies
+S:D:A:so that they believe they'd never seen anything so beautiful.
+S:S:-2:0:0:0:0:+3:0:100
+S:K:0:0:1:-2:0:10:0:0
+S:P:1:0:1
+S:M:14:6:72:6:180:25:66:4:150:20
+# R:E:weapons:torso:arms:finger:head:legs
+S:E:0:1:2:4:1:1
+S:A:Demon
+S:C:F:Warrior | Archer | Priest
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:SUST_CHR | FLY
+S:R:5:0
+S:F:FREE_ACT
+S:R:10:0
+S:F:RES_FIRE
+S:R:50:0
+S:F:CLIMB
+R:k:-400:+000:Weaponmastery
+R:k:+000:+300:Archery
+R:k:+1000:+000:Sneakiness
+R:k:+500:+100:Disarming
+S:k:+500:+200:Stealth
+S:k:+0:+200:Magic
+S:k:+1000:+300:Mind
+S:Z:dazzle
+
+S:N:18:(Hurog)
+S:D:A:These doglike demons are one of the lowest forms of Morgoth's
+S:D:A:corrupted races, though their sense of smell is uncanny, and
+S:D:A:increases with experience.
+S:S:-1:0:0:0:-1:0:0:100
+S:K:2:0:2:0:5:30:0:0
+S:P:0:-10:2
+S:M:14:6:72:6:180:25:66:4:150:20
+S:E:1:1:1:2:1:1
+S:A:Demon
+S:C:F:Priest
+S:G:NO_SUBRACE_CHANGE
+S:R:5:0
+S:F:ESP_ANIMAL
+S:R:10:1
+S:F:ESP_ORC | SEARCH
+S:R:15:0
+S:F:ESP_TROLL
+S:R:20:1
+S:F:ESP_DRAGON | SEARCH
+S:R:25:0
+S:F:ESP_UNDEAD
+S:R:30:1
+S:F:ESP_NONLIVING | SEARCH
+S:R:35:0
+S:F:ESP_EVIL
+S:R:40:1
+S:F:ESP_GOOD | SEARCH
+S:R:45:0
+S:F:ESP_DEMON
+S:R:50:1
+S:F:ESP_ALL | SEARCH
+S:Z:smell monsters
+S:Z:smell metal
+R:k:+500:+300:Stealth
+R:k:+500:+200:Sneakiness
+R:k:+000:+000:Weaponmastery
+R:k:+000:+000:Archery
+
+S:N:19:(Sarnrog)
+S:D:A:Medium-sized winged humanoids that look like living stone.
+S:D:A:They are clumsy and dumb, but very strong and stout, and
+S:D:A:resist the elements fairly well.
+S:S:2:-1:-2:-2:2:-1:0:100
+# S:K:dis:dev:sav:stl:srh:fos:thn:thb
+S:K:-1:0:0:-8:-2:-10:5:0
+S:P:2:20:0
+S:M:14:6:72:6:180:25:66:4:150:20
+S:E:1:1:1:2:1:1
+S:A:Demon
+S:C:F:Priest | Mage
+S:G:NO_SUBRACE_CHANGE | NO_STUN
+S:R:1:0
+S:F:RES_POIS
+S:R:10:1
+S:F:RES_FIRE | TUNNEL
+S:R:20:1
+S:F:RES_ACID | TUNNEL
+S:R:30:1
+S:F:RES_ELEC | TUNNEL
+S:R:40:1
+S:F:RES_COLD | TUNNEL
+S:R:45:1
+S:F:RES_SHARDS | TUNNEL
+S:R:50:1
+S:F:RES_SOUND | TUNNEL
+S:k:+1000:+500:Boulder-throwing
+S:Z:eat rock
+R:k:-800:+000:Stealth
+R:k:-500:+000:Sneakiness
+R:k:-500:+000:Archery
+
+S:N:20:(Caborrog)
+S:D:A:Among the foulest of Morgoth's servants, these froglike
+S:D:A:demons are fast and smart, but not very strong or stealthy.
+S:D:A:As a result of their corruption, they develop a strange
+S:D:A:protective layer around them as they mature.
+S:S:-1:2:1:0:0:-3:0:100
+# S:K:dis:dev:sav:stl:srh:fos:thn:thb
+S:K:0:0:1:-10:2:10:0:0
+S:P:2:10:0
+S:M:14:6:72:6:180:25:66:4:150:20
+S:E:1:1:1:2:1:1
+S:A:Demon
+S:C:F:Archer | Priest
+S:G:NO_SUBRACE_CHANGE | NO_STUN | AC_LEVEL
+S:R:1:0
+S:F:RES_POIS
+S:R:1:7
+S:F:SPEED
+S:R:10:2
+S:F:INT
+S:R:30:2
+S:F:INT
+S:R:50:2
+S:F:INT
+S:k:+1000:+400:Dodging
+S:k:+0:+200:Magic
+S:k:+0:+300:Conveyance
+S:k:+0:+300:Temporal
+S:Z:panic hit
+R:k:-1000:+000:Stealth
+R:k:-500:+000:Sneakiness
+
+S:N:21:(Draugrog)
+S:D:A:These doglike demons are of a higher order than the Huroeg,
+S:D:A:they learn more quickly and have more sophisticated magical
+S:D:A:powers. Their arrogant snarling, however, makes them quite
+S:D:A:unstealthy.
+S:S:1:1:1:1:1:-1:0:100
+S:K:2:0:2:-20:5:30:0:0
+S:P:0:-20:2
+S:M:14:6:72:6:180:25:66:4:150:20
+S:E:1:1:1:2:1:1
+S:A:Demon
+S:C:F:Priest
+S:G:NO_SUBRACE_CHANGE
+S:R:4:0
+S:F:ESP_ANIMAL
+S:R:8:1
+S:F:ESP_ORC | SEARCH
+S:R:12:0
+S:F:ESP_TROLL
+S:R:16:1
+S:F:ESP_DRAGON | SEARCH
+S:R:20:0
+S:F:ESP_UNDEAD
+S:R:24:1
+S:F:ESP_NONLIVING | SEARCH
+S:R:28:0
+S:F:ESP_EVIL
+S:R:32:1
+S:F:ESP_GOOD | SEARCH
+S:R:36:0
+S:F:ESP_DEMON
+S:R:40:1
+S:F:ESP_ALL | SEARCH
+S:R:50:0
+S:F:PRECOGNITION
+S:Z:smell monsters
+S:Z:smell metal
+S:k:+0:+200:Magic
+S:k:+0:+200:Spirituality
+S:k:+1000:+500:Mindcraft
+R:k:-10000:+000:Stealth
+R:k:-500:+200:Sneakiness
+R:k:+300:+000:Weaponmastery
+R:k:+200:+000:Archery
+
+### This is Theme's implementation of the Avatar subrace from T-Plus by Ingeborg S. Norden, with obvious limitations.
+
+S:N:22:(Lygrog)
+S:D:A:These slithering snakelike forms lack many physical
+S:D:A:advantages, but are no less deadly for it. Their magical
+S:D:A:abilities, stealth, and cunning are legendary.
+S:S:-3:5:5:5:-1:-6:0:200
+S:K:5:5:5:10:5:50:0:0
+S:P:2:40:3
+S:M:14:6:72:6:180:25:66:4:150:20
+S:E:0:1:0:1:1:0
+S:A:Demon
+S:C:F:Warrior | Archer | Priest | Rogue
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:FEATHER | FREE_ACT | LITE1 | RES_POIS |
+S:R:1:0
+S:F:REGEN | SEE_INVIS | SLOW_DIGEST | SUST_CHR |
+S:R:1:10
+S:F:SPEED |
+S:R:5:0
+S:F:ESP_ANIMAL | RES_FIRE | RES_COLD | SUST_WIS |
+S:R:5:1
+S:F:LUCK | SPEED |
+S:R:10:0
+S:F:ESP_ORC | RES_ELEC | RES_ACID | SUST_INT | WATER_BREATH |
+S:R:10:1
+S:F:LUCK | SPEED |
+S:R:15:0
+S:F:ESP_TROLL | FLY | RES_CONF | RES_LITE | SUST_CON |
+S:R:15:1
+S:F:LUCK | SPEED | SPELL |
+S:R:20:0
+S:F:ESP_DRAGON | ESP_GIANT | LITE2 | RES_BLIND | SUST_DEX |
+S:R:20:1
+S:F:LUCK | SPEED |
+S:R:25:0
+S:F:ESP_UNDEAD | ESP_NONLIVING | RES_DISEN | RES_CHAOS | SUST_STR |
+S:R:25:1
+S:F:LUCK | SPEED |
+S:R:30:0
+S:F:ESP_EVIL | ESP_DEMON | RES_SOUND |
+S:R:30:1
+S:F:LUCK | SPEED | SPELL |
+S:R:35:0
+S:F:CLIMB | ESP_GOOD | ESP_THUNDERLORD | RES_SHARDS |
+S:R:35:1
+S:F:LUCK | SPEED |
+S:R:40:0
+S:F:ESP_UNIQUE | LITE3 | PRECOGNITION | RES_NEXUS | RES_NETHER |
+S:R:40:1
+S:F:LUCK | SPEED |
+S:R:45:0
+S:F:ESP_ALL | IM_ACID | IM_ELEC | IM_FIRE | IM_COLD | MAGIC_BREATH |
+S:R:45:1
+S:F:SPEED | SPELL |
+S:R:50:0
+S:F:IM_NETHER | REFLECT | WRAITH |
+S:R:50:1
+S:F:LUCK | SPEED |
+R:k:+25000:+500:Stealth
+R:k:+2000:+200:Sneakiness
+R:k:+2000:+300:Magic-Device
+
+S:N:23:(Limrog)
+S:D:A:Humanoid creatures with gill slits at the necks,
+S:D:A:they seem to flit in and out of existence.
+S:S:-2:1:1:3:-1:-1:2:150
+# S:K:dis:dev:sav:stl:srh:fos:thn:thb
+S:K:2:2:2:2:2:30:0:0
+S:P:1:50:2
+S:M:14:6:72:6:180:25:66:4:150:20
+S:E:1:1:1:2:1:1
+S:A:Demon
+S:C:F:Priest
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:WATER_BREATH
+S:R:50:0
+S:F:MAGIC_BREATH
+R:k:+3000:+300:Stealth
+R:k:+500:+200:Sneakiness
+R:k:+2000:+200:Disarming
+S:k:+0:+200:Magic
+S:k:+0:+500:Conveyance
+S:k:+0:+500:Temporal
+S:k:+0:+500:Meta
+S:Z:teleport
+S:Z:blink
+S:Z:panic hit
+S:Z:find secret passages
+
+S:N:24:(Rawrog)
+S:D:A:These creatures resemble lions standing on two legs.
+S:D:A:Very strong and smart, they instill fear in all who
+S:D:A:encounter them and are afraid of nothing.
+S:S:2:1:1:-1:2:1:1:100
+S:K:0:0:4:-3:0:10:10:10
+S:P:2:30:0
+S:M:14:6:72:6:180:25:66:4:150:20
+S:E:1:1:1:2:1:1
+S:A:Demon
+S:C:F:Priest | Mage
+S:G:NO_SUBRACE_CHANGE
+S:R:5:0
+S:F:RES_FIRE | RES_COLD
+S:R:10:0
+S:F:RES_ELEC | RES_ACID
+S:R:15:0
+S:F:RES_POIS | RES_CONF
+S:k:+1000:+100:Combat
+S:k:+1000:+100:Weaponmastery
+R:k:+1000:+100:Archery
+R:k:-300:+000:Stealth
+R:k:+500:+200:Sneakiness
+R:k:+500:+100:Disarming
+S:Z:scare monster
+S:Z:berserk
+
+S:N:25:(Adanrog)
+S:D:A:Horrifying humanoids wreathed in flames.
+S:D:A:They can be equally skilled in combat and
+S:D:A:magic; and they can temporarily assume the
+S:D:A:forms of true Balrogs.
+S:S:1:1:1:1:1:1:0:150
+S:K:1:1:1:1:1:20:15:10
+S:P:3:50:2
+S:M:14:6:72:6:180:25:66:4:150:20
+S:E:1:1:1:2:1:1
+S:A:Demon
+S:C:F:Priest | Loremaster | Rogue
+S:G:NO_SUBRACE_CHANGE
+S:R:1:0
+S:F:IM_FIRE | SH_FIRE
+S:k:+0:+300:Combat
+S:k:+0:+300:Magic
+S:Z:turn into a Balrog
+R:k:+1000:+300:Weaponmastery
+R:k:+1000:+300:Archery
+R:k:+1000:+200:Stealth
+R:k:+500:+200:Sneakiness
+R:k:+1000:+200:Disarming
+R:k:+500:+200:Magic
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# 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.
+# Eagle: 89 -> 90.
+# 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.)
+# Druadan: 82 -> 83 -> 71 -> 72 -> 73.
+# Dragon: 100 -> 101 -> 102 -> 103 -> 104.
+# Easterling: 105 -> 106 -> 107 -> 108.
+# Demon: 109 -> 110 -> 111 -> 112.
+
+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,
+
+### Kobold histories replaced by Druadan histories
+
+H:185:100:82:83:50:You are one of several children of
+
+H:186:40:83:71:50:a Druadan Gatherer.
+H:187:75:83:71:55:a Druadan Hunter.
+H:188:95:83:71:65:a Druadan Shaman.
+H:189:100:83:71:100:Ghan-buri-Ghan.
+
+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 Mark.
+
+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 Eagles of Manwe.
+H:205:100:89:90:60:You are the one of the most known Eagles of Manwe.
+
+H:206:90:90:0:100:Your back and breast are dark brown, and you have very large wings.
+H:207:100:90:0:120:Your back and breast are dazzling white, your wings magnificent.
+
+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.
+
+### Death Mold histories have been replaced by Dragon histories.
+
+H:234:10:100:101:30:You were born in the land of Rhun,
+H:235:20:100:101:35:You were born in the Misty Mountains,
+H:236:30:100:101:40:You were born in the Blue Mountains,
+H:237:40:100:101:45:You were born in Dol Guldur,
+H:238:50:100:101:50:You were born in Angmar,
+H:239:60:100:101:50:You were born in Barad-dur,
+H:240:70:100:101:55:You were born at the foot of the Orodruin,
+H:241:80:100:101:60:You were born in Angband,
+H:242:90:100:101:65:You were born in Utumno,
+H:243:100:100:101:70:You were born in the Void,
+
+H:244:10:101:102:30:awakened by accident.
+H:245:20:101:102:35:awakened by hunger.
+H:246:30:101:102:40:awakened by the glint of jewels.
+H:247:40:101:102:45:awakened by an unsuspecting traveler.
+H:248:50:101:102:50:awakened by a corrupt Mage.
+H:249:60:101:102:50:awakened by an evil Sorcerer.
+H:250:70:101:102:55:awakened by a powerful evil Wizard.
+H:251:80:101:102:60:awakened by the Witch-King of Angmar.
+H:252:90:101:102:65:awakened by Gorthaur.
+H:253:100:101:102:70:awakened by Melkor Bauglir.
+
+H:254:100:102:103:50:Since then you have destroyed
+
+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:enemies of the Dark.
+
+### Easterling histories added in the Theme module
+
+H:266:50:105:106:50:You are one of many children of
+H:267:100:105:106:50:You are the only child of
+
+H:268:20:106:107:35:an Easterling slave.
+H:269:30:106:107:40:an Easterling landlord.
+H:270:40:106:107:45:an Easterling squire.
+H:271:50:106:107:50:an Easterling warrior.
+H:272:60:106:107:55:a Wainrider from Near Harad.
+H:273:70:106:107:60:a warlord from Far Harad.
+H:274:80:106:107:65:a Corsair of Umbar.
+H:275:90:106:107:70:Ulfast, son of Ulfang.
+H:276:100:106:107:75:Ulwarth, son of Ulfang.
+
+H:277:25:107:108:10:You have brown eyes,
+H:278:50:107:108:30:You have piercing black eyes,
+H:279:100:107:108:60:You have alert yellow eyes,
+
+H:280:50:108:0:25:chestnut brown hair, and a dark complexion.
+H:281:100:108:0:50:jet-black hair, and a dark complexion.
+
+H:282:40:109:110:60:You have only recently been corrupted,
+H:283:60:109:110:70:You've been corrupted for quite a while,
+H:284:100:109:110:80:You were one of the first beings to be corrupted,
+
+H:285:40:110:111:60:and your entire being detests this fact.
+H:286:60:110:111:70:but you don't mind it so much anymore.
+H:287:100:110:111:80:and you consider that to have been your true birth.
+
+H:288:40:111:112:60:Your watery green eyes
+H:289:60:111:112:70:Your piercing black eyes
+H:290:100:111:112:80:Your glowing red eyes
+
+H:291:40:112:0:60:radiate miserable servility.
+H:292:60:112:0:70:radiate contempt.
+H:293:100:112:0:80:radiate pure evil.
+
+##############################################################################
+##############################################################################
+##############################################################################
+##############################################################################
+# 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:Pacifist
diff --git a/lib/mods/theme/edit/qrand1.map b/lib/mods/theme/edit/qrand1.map
new file mode 100644
index 00000000..f42cbf1c
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/qrand10.map b/lib/mods/theme/edit/qrand10.map
new file mode 100644
index 00000000..ae45b9cb
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/qrand11.map b/lib/mods/theme/edit/qrand11.map
new file mode 100644
index 00000000..4af3c266
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/qrand12.map b/lib/mods/theme/edit/qrand12.map
new file mode 100644
index 00000000..4621ef0b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/qrand14.map b/lib/mods/theme/edit/qrand14.map
new file mode 100644
index 00000000..9f339db0
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/qrand5.map b/lib/mods/theme/edit/qrand5.map
new file mode 100644
index 00000000..cc5d79ee
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/qrand6.map b/lib/mods/theme/edit/qrand6.map
new file mode 100644
index 00000000..3b55e985
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/qrand7.map b/lib/mods/theme/edit/qrand7.map
new file mode 100644
index 00000000..a7c0607f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/r_info.txt b/lib/mods/theme/edit/r_info.txt
new file mode 100644
index 00000000..18bf6643
--- /dev/null
+++ b/lib/mods/theme/edit/r_info.txt
@@ -0,0 +1,19009 @@
+# 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
+# With many new monsters replacing old ones for Theme (ToME module)
+
+# 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.
+
+##### 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:W
+I:110:1d2:30:1:255
+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_GRASS | WILD_TOWN | WILD_ONLY |
+F:ANIMAL | DROP_CORPSE | DROP_SKELETON |
+F:MORTAL | BASEANGBAND | NEUTRAL | NO_TARGET
+D:A skinny little furball with sharp claws.
+
+N:3:Sparrow
+G:B:W
+I:110:1d1:30:1:255
+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 | NEUTRAL | NO_TARGET | AQUATIC
+D:Utterly harmless, except when angry.
+
+N:4:Chaffinch
+G:B:R
+I:110:1d1:30:1:255
+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 | AQUATIC
+F:MORTAL | BASEANGBAND | NEUTRAL | NO_TARGET
+D:Utterly harmless, except when angry.
+
+N:5:Wild rabbit
+G:r:U
+I:110:1d2:30:1:255
+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 | NEUTRAL | NO_TARGET
+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 | NEUTRAL | NO_TARGET |
+D:He has a strong axe with a sharp edge.
+
+N:7:Scruffy little dog
+G:C:s
+I:110:1d3:20:1:255
+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 | NEUTRAL | NO_TARGET
+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
+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_TARGET | NO_DEATH
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A broad and thick-set hobbit with a round red face. "There's earth under
+D:his old feet, and clay on his fingers; wisdom in his bones, and both his
+D:eyes are open." He seems sad about something.
+
+N:9:Blubbering idiot
+G:t:W
+I:110:1d2:6:1:255
+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 | NEUTRAL | NO_TARGET |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He tends to blubber a lot.
+
+N:10:Boil-covered wretch
+G:t:R
+I:110:1d2:6:1:255
+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 | NEUTRAL | NO_TARGET
+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:255
+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 | NEUTRAL | NO_TARGET
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:Drooling and comical, he runs around town with a stupid grin.
+
+N:12:Pitiful-looking beggar
+G:t:U
+I:110:1d4:10:1:255
+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 | NEUTRAL | NO_TARGET
+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:255
+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 | NEUTRAL | NO_TARGET
+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_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 | NEUTRAL | NO_TARGET
+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 | NEUTRAL | NO_TARGET
+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:The Squint-eyed Southerner
+G:t:s
+I:120:8d20:20:15:400
+W:10:4:1700:50
+E:1:1:1:2:1:1
+O:0:50:0:50
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:PUNCH:HURT:3d8
+B:PUNCH:HURT:3d8
+F:UNIQUE | MALE | CAN_SPEAK | BASEANGBAND | HAS_LITE |
+F:FORCE_MAXHP | DROP_CORPSE | WILD_TOWN | WILD_ONLY | DROP_1D2 |
+F:ONLY_ITEM | DROP_GOOD | DROP_GREAT | DROP_RANDART | MORTAL
+D:He doesn't look like he's from around here, and a gut
+D:feeling tells you he's a spy.
+
+##### 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_MOUNTAIN |
+F:ANIMAL | HAS_EGG | MORTAL | BASEANGBAND |
+D:A small, harmless lizard.
+
+# New monster added by furiosity for the Theme module
+N:24:Ox
+G:c:D
+I:50:10d10:0:50:255
+W:0:1:4000:0
+E:0:1:0:2:1:2
+O:0:0:0:0
+B:KICK:HURT:10d10
+B:KICK:HURT:10d10
+B:BUTT:HURT:10d10
+B:BUTT:HURT:10d10
+F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_ONLY | WILD_GRASS
+F:FRIEND | FRIENDS | FORCE_SLEEP | STUPID | WEIRD_MIND |
+F:SUSCEP_POIS | KILL_BODY | NEUTRAL | NO_TARGET | KILL_ITEM |
+D:A strong and powerful animal with sharp-looking horns. Large cattle
+D:used to carry and draw burdens, and for milk and meat. It is domestic and
+D:utterly harmless, except when angry.
+
+# New monster added by furiosity for the Theme module
+N:25:Kine of Araw
+G:c:r
+I:80:20d20:0:50:255
+W:0:1:5000:0
+E:0:1:0:2:1:2
+O:0:0:0:0
+B:KICK:HURT:20d20
+B:KICK:HURT:20d20
+B:KICK:HURT:20d20
+B:KICK:HURT:20d20
+F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_ONLY | WILD_GRASS |
+F:FRIEND | FRIENDS | FORCE_SLEEP | WEIRD_MIND | SUSCEP_POIS | GOOD |
+F:KILL_BODY | NEUTRAL | NO_TARGET | KILL_ITEM |
+D:The oxen that live on the lands around the Sea of Rhun are hardier
+D:and wilder than any others in Middle-earth. Legends claim that they
+D:are descended from the cattle of the Huntsman of the Valar, Orome
+D:himself, and so they are named the Kine of Araw (Araw being the Sindarin
+D:form of Orome's name). It is a huge four-legged beast that won't attack
+D:unless provoked.
+
+# New monster added by furiosity for the Theme module
+N:26:Sheep
+G:c:w
+I:70:5d5:0:30:255
+W:0:1:2000:0
+E:0:1:0:2:1:2
+O:0:0:0:0
+B:KICK:HURT:5d5
+B:KICK:HURT:5d5
+B:KICK:HURT:5d5
+B:KICK:HURT:5d5
+F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_ONLY | WILD_GRASS |
+F:FRIEND | FRIENDS | FORCE_SLEEP | WEIRD_MIND | SUSCEP_POIS | NEUTRAL | NO_TARGET |
+D:Woolly grazing animals, especially common on the Barrow-downs
+D:and in the Vales of Anduin. Utterly harmless unless angered.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:29:Meara
+G:c:W
+I:130:10d12:0:70:255
+W:0:1:3500:0
+E:0:1:0:2:1:2
+O:0:0:0:0
+B:KICK:HURT:10d12
+B:KICK:HURT:10d12
+B:KICK:HURT:10d12
+B:KICK:HURT:10d12
+F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_ONLY |
+F:WILD_GRASS | FORCE_SLEEP | WEIRD_MIND | NEUTRAL | NO_TARGET | GOOD |
+D:The great Mearas are legendary horses bred by the Rohirrim.
+D:Swift and sure they are, but only members of the royal
+D:family of Rohan are allowed to ride them. This animal will
+D:not attack unless angered.
+
+# New monster added by furiosity for the Theme module
+N:30:Horse
+G:c:s
+I:120:8d10:0:50:255
+W:0:1:3000:0
+E:0:1:0:2:1:2
+O:0:0:0:0
+B:KICK:HURT:8d10
+B:KICK:HURT:8d10
+B:KICK:HURT:8d10
+B:KICK:HURT:8d10
+F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_ONLY | WILD_GRASS |
+F:FORCE_SLEEP | WEIRD_MIND | SUSCEP_POIS | WILD_TOWN | NEUTRAL | NO_TARGET |
+D:Beasts of burden and transport used by Elves and Men through the ages.
+D:A noble animal, it can be quite a sight as it prances gracefully
+D:through the fields. It won't attack you unless provoked.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:34:The Boar of Everholt
+G:c:R
+I:110:15d10:0:3:20
+W:5:1:1600:60
+E:0:1:0:2:1:2
+O:0:0:0:0
+B:BUTT:HURT:3d6
+B:BUTT:HURT:3d6
+B:BUTT:HURT:3d6
+F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_TOO |
+F:WILD_GRASS | FORCE_SLEEP | WEIRD_MIND | WILD_WOOD | UNIQUE |
+D:A monstrous beast that inhabited Everholt in the Firien Wood,
+D:around the feet of the Halifirien in the White Mountains. It
+D:is twice the size of a regular boar, and twice as aggressive.
+D:King Folca of Rohan met his end at the tusks of this creature.
+
+N:35:Jackal
+G:C:y
+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 wild 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 | 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 |
+F:MORTAL | SUSCEP_FIRE | BASEANGBAND | NO_CUT
+D:A lone insect may be harmless, but there's a whole swarm of
+D:them here!
+
+# New monster added by furiosity for the Theme module
+N:39:Boar
+G:c:o
+I:110:3d6:0:3:20
+W:3:1:800:5
+E:0:1:0:2:1:2
+O:0:0:0:0
+B:BUTT:HURT:3d3
+B:BUTT:HURT:3d3
+F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_TOO |
+F:WILD_GRASS | FORCE_SLEEP | WEIRD_MIND | WILD_WOOD |
+D:Aggressive tusked beasts common to woodland regions.
+
+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 | 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.
+
+# New monster added by furiosity for the Theme module
+N:41:Cow
+G:c:u
+I:70:7d7:0:50:255
+W:0:1:3500:0
+E:0:1:0:2:1:2
+O:0:0:0:0
+B:KICK:HURT:7d7
+B:BUTT:HURT:7d7
+B:KICK:HURT:7d7
+B:BUTT:HURT:7d7
+F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_ONLY | WILD_GRASS |
+F:FRIEND | FRIENDS | FORCE_SLEEP | WEIRD_MIND | SUSCEP_POIS | NEUTRAL | NO_TARGET |
+D:Domestic horned beasts kept throughout Middle-earth.
+D:Utterly harmless, unless angered.
+
+# New monster added by furiosity for the Theme module
+#N:42:Pony
+#G:c:y
+#I:120:4d5:0:200
+#W:0:1:1000:0
+#E:0:1:0:2:1:2
+#O:0:0:0:0
+#B:KICK:HURT:4d5
+#B:KICK:HURT:4d5
+#B:KICK:HURT:4d5
+#B:KICK:HURT:4d5
+#F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_ONLY | WILD_GRASS |
+#F:FORCE_SLEEP | WEIRD_MIND | SUSCEP_POIS | NEUTRAL | NO_TARGET | WILD_TOWN |
+#D:It's a short, stocky pack animal, related to the horse, but smaller.
+#D:A common beast of burden in Middle-earth, especially used by Hobbits
+#D:and Dwarves. It won't attack you unless provoked.
+
+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 |
+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.
+
+# New monster added by furiosity for the Theme module
+N:52:Deer
+G:c:U
+I:110:8d8:0:30:255
+W:0:1:3000:0
+E:0:1:0:0:1:2
+O:0:0:0:0
+B:KICK:HURT:8d8
+B:BUTT:HURT:8d8
+B:KICK:HURT:8d8
+B:BUTT:HURT:8d8
+F:BASEANGBAND | ANIMAL | MORTAL | DROP_CORPSE | WILD_ONLY | WILD_GRASS |
+F:FORCE_SLEEP | WEIRD_MIND | SUSCEP_POIS | NEUTRAL | NO_TARGET | WILD_WOOD |
+D:Graceful creature found throughout Middle-earth. Deer are
+D:traditionally dedicated to the Vala Nessa, and are said to
+D:follow her as she travels through the wild lands. They will
+D:never attack without provocation.
+
+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 | DROP_CORPSE |
+F:BASH_DOOR |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is as big as a wolf.
+
+# New monster added by furiosity for the Theme module
+N:57:Lion
+G:f:R
+I:120:14d20:50:60:0
+W:24:2:3000:80
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:TERRIFY:2d8
+B:CLAW:HURT:2d16
+B:CLAW:HURT:2d16
+F:BASH_DOOR | WILD_TOO | WILD_GRASS | WILD_WOOD | WILD_MOUNTAIN |
+F:ANIMAL | DROP_SKELETON | DROP_CORPSE | MORTAL | BASEANGBAND
+D:Master of all felines, it's a huge cat with enormous paws
+D:and a thick mane. Its roar is deafeningly terrifying.
+
+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 | AQUATIC
+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:7:2:670:16
+E:1:1:1:2:1:1
+O:50:50:0:0
+B:TOUCH:EAT_GOLD
+B:BITE:HURT:2d4
+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
+S:1_IN_25 |
+S:S_SPIDER
+D:Also known as Gollum. He is of the Periannath, but does not look it.
+D:He is thin and frail, sneaking around in dark corners, hoping to
+D:reclaim his "precious," and he'll use any means necessary to do so.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:67:Squirrel
+G:r:o
+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:WILD_ONLY | WILD_GRASS | WILD_WOOD |
+F:ANIMAL | DROP_SKELETON | DROP_CORPSE | NEUTRAL | NO_TARGET |
+F:MORTAL | BASEANGBAND
+D:A funny chittering creature, looking for nuts.
+
+N:68:Raven
+G:B:W
+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_ONLY | WILD_WOOD | CAN_FLY | DROP_CORPSE
+F:MORTAL | HAS_EGG | NEUTRAL | NO_TARGET | IMPRESED | BASEANGBAND | AQUATIC
+D:Larger than a crow, and pitch black.
+
+N:69:White midge
+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 an evil relative of the moth, native to marshlands.
+
+# New monster added by furiosity for the Theme module
+# Based on the Vorpal bunny from Zangband
+N:70:Squirrel of Mirkwood
+G:r:g
+I:120:10d10:40:40:0
+W:13:1:600:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:6d1
+B:BITE:HURT:7d1
+F:WILD_TOO | WILD_WOOD | DROP_CORPSE | CAN_FLY |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:An ominous-looking squirrel with dark fur, covered in
+D:leaves for better masquerade. It has glowing eyes and
+D:unusually sharp teeth. It is a horrific creation of the
+D:Necromancer in Dol Guldur, a corrupted form of a normal
+D:squirrel.
+
+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 |
+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: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.
+
+# New monster added by furiosity for the Theme module
+N:77:Ape
+G:q:y
+I:115:5d10:8:10:2
+W:8:10:800:10
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:BITE:HURT:2d2
+B:BITE:DISEASE:1d1
+B:BITE:EAT_FOOD:1d1
+F:BASEANGBAND | ANIMAL | MORTAL | AI_ANNOY |
+F:RAND_50 | RAND_25 | SMART | WEIRD_MIND |
+S:1_IN_10
+S:SHRIEK
+D:A large humanoid form, relatively rare on Middle-earth.
+D:It is bounding toward you, noisy and dirty.
+
+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 | WILD_ONLY
+F:MORTAL | NEUTRAL | NO_TARGET | IMPRESED | 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 Orcling, 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.
+
+# New monster added by furiosity for the Theme module
+# Ranger chieftain without the magic other than arrows
+N:91:Corsair of Umbar
+G:p:U
+I:120:50d20:20:60:10
+W:41:2:1800:1500
+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 |
+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:ARROW_2 | ARROW_3 | ARROW_4 | MISSILE |
+D:A person with no love for Gondor. He thinks you are a soldier in
+D:the King's army.
+
+# New monster added by furiosity for the Theme module
+# Master rogue with some tweaks
+N:92:Dunlending
+G:p:g
+I:120:15d9:20:30:40
+W:25:2:1600:130
+E:1:1:1:2:1:1
+O:80:10:10:0
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+B:HIT:POISON:2d8
+F:MALE | DROP_SKELETON | DROP_CORPSE |
+F:DROP_2D2 | SUSCEP_ELEC | FRIENDS |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:ARROW_1 |
+D:A person with no love for Rohan. He thinks you are a Rider of the
+D:Mark in disguise.
+
+N:93:Apprentice 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 skillful in combat.
+D:He seems to consider you an agent of Morgoth.
+
+# New monster added by furiosity for the Theme module
+N:98:Man of Harad
+G:p:u
+I:110:16d10: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 | REGENERATE | MALE |
+F:EVIL | HURT_LITE | BASEANGBAND
+D:A dark-skinned human who worships Sauron. A powerful warrior,
+D:almost as strong as a troll. He has bizarre white eyes.
+
+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.
+
+# New monster added for the Theme module
+# From UnAngband
+N:102:Lurtz, Uruk Captain of the White Hand
+G:o:g
+I:110:72d10:20:95:50
+W:22:3:1800:550
+E:1:1:1:2:1:1
+O:0:50:0:50
+B:HIT:HURT:3d8
+B:HIT:HURT:3d5
+B:HIT:HURT:3d5
+F:BASEANGBAND | UNIQUE | FORCE_MAXHP | CAN_SPEAK | SMART |
+F:EVIL | ORC | OPEN_DOOR | BASH_DOOR | IM_FIRE | IM_COLD |
+F:IM_POIS | ESCORT | ESCORTS | DROP_1D2 | DROP_GOOD | MALE |
+D:A strong and cunning orc warrior, a commander of Saruman's orcs.
+
+# New monster added for the Theme module
+# From T-Plus by Ingeborg S. Norden
+N:103:Munchkin
+G:l:B
+I:120:35d50:255:75:0
+W:60:5:330:20000
+E:1:1:1:2:1:1
+O:0:45:45:10
+B:INSULT:INSANITY:2d10
+B:INSULT:EAT_GOLD:2d10
+B:INSULT:EAT_ITEM:2d10
+F:MALE | SMART | CAN_SPEAK | JOKEANGBAND |
+F:FORCE_MAXHP |
+F:RAND_25 |
+F:DROP_90 | DROP_GOOD | DROP_GREAT |
+F:DROP_4D2 | DROP_2D2 | ONLY_ITEM | AI_ANNOY |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | KILL_BODY | RES_NETH | RES_DISE |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS | NO_CONF | NO_SLEEP |
+F:RES_NEXU | RES_PLAS | HAS_LITE | NO_CUT | NO_STUN |
+F:REFLECTING |
+S:1_IN_5
+S:HEAL | HASTE | TPORT | TELE_AWAY | BLIND | CONF | SCARE |
+S:CAUSE_2 | FORGET | TRAPS |
+S:BO_MANA | ROCKET |
+D:This annoying little gnome won't stop bragging about his great
+D:magical powers and shiny new equipment--unless someone silences
+D:him permanently. Unfortunately, your own weapons and spells
+D:don't seem to work as well as they should on this fellow...
+
+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:Yuck! It doesn't look so tasty.
+
+N:109:Apprentice 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:Apprentice 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.
+
+# New monster added for the Theme module
+# Suggested by Atarlost in the t-o-m-e.net forums
+N:111:Petty-dwarf
+G:k:s
+I:110:6d6:25:80:200
+W:8:4:0:30
+B:HIT:HURT:3d4
+B:TOUCH:EAT_GOLD
+F:MALE
+F:DROP_1D2 | DROP_GOOD
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR
+F:EVIL
+S:1_IN_8
+S:ARROW_1
+D:A filthy little dwarf. He wants your purse, and judging by the
+D:size of his axe, he's quite likely to get it.
+
+# New monster added for the Theme module
+# Suggested by Atarlost in the t-o-m-e.net forums
+N:112:Petty-dwarf mage
+G:k:R
+I:120:7d10:20:16:20
+W:10:1:0:50
+B:HIT:HURT:1d6
+B:HIT:HURT:UN_BONUS:1d6
+F:MALE
+F:FORCE_SLEEP
+F:ONLY_ITEM | DROP_1D2
+F:OPEN_DOOR | BASH_DOOR
+F:EVIL
+S:1_IN_5
+S:BLIND | CONF | MISSILE | DARKNESS | BA_POIS
+D:A small dwarf in mage's robes. He looks comical, but he has slain any
+D:adventurer foolish enough to laugh at him.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:115:Butterfly
+G:I:B
+I:120:1d1:6:5:10
+W:3:1:100:1
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:RAND_50 | RAND_25 | NEUTRAL | NO_TARGET | WILD_ONLY | WILD_WOOD | NEVER_BLOW |
+F:WEIRD_MIND | ANIMAL | MORTAL | BASEANGBAND | WILD_GRASS |
+D:A large insect with beautiful fluttering wings.
+
+N:116:Apprentice 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:o
+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 |
+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.
+
+# New monster added by furiosity for the Theme module
+N:123:Moth
+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:EAT_LITE:1d1
+F:RAND_50 | RAND_25 | HURT_LITE | WILD_TOO | WILD_WOOD |
+F:WEIRD_MIND | ANIMAL | MORTAL | BASEANGBAND | WILD_GRASS |
+S:MULTIPLY
+D:Nocturnal insect of a kind closely related to butterflies.
+
+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:Hurog
+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 |
+S:1_IN_50 |
+S:BR_NETH
+D:A minor demonic servant of evil. Its features remind you of a dog - in fact,
+D:Huroeg are the result of the corruption of dogs by Morgoth.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:132:Nightingale
+G:B:B
+I:110:1d1:30:1:10
+W:0:3:80:0
+E:0:1:1:0:1:0
+O:0:0:0:0
+F:RAND_25 | CAN_FLY | WILD_ONLY | WILD_WOOD | WILD_GRASS |
+F:ANIMAL | DROP_SKELETON | NEVER_BLOW | NEUTRAL | NO_TARGET |
+F:MORTAL | BASEANGBAND | DG_CURSE |
+D:Small brown birds of thrush kind, famous for their clear
+D:singing, and the fact that they will sing during the night
+D:as well as the day (hence their name). Since the days of
+D:Luthien, the nightingales of Middle-earth are surrounded
+D:by a strange blue aura and are rumoured to be under great
+D:protection of the Valar.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:135:Gorcrow
+G:B:G
+I:120:3d5:40:12:0
+W:3:2:300:10
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+B:BITE:HURT:1d4
+B:BITE:DISEASE:1d4
+F:ANIMAL | WILD_TOO | WILD_WOOD | CAN_FLY | DROP_CORPSE | WILD_SHORE |
+F:MORTAL | HAS_EGG | BASEANGBAND | EVIL | WILD_SWAMP | IMPRESED |
+D:It is a hooded crow, camouflaged well for a swampland or forest.
+D:It is a carrion bird that often lives close alongside the mewlips,
+D:the remains of whose prey it devours.
+
+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:Grima the 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 was once the chief counsellor to King Theoden of Rohan.
+D:He betrayed king and country by becoming a spy for the
+D:corrupted Istari Saruman.
+
+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 | JOKEANGBAND | HAS_LITE
+S:1_IN_5
+S:ARROW_2 | HEAL | TRAPS
+D:The legendary archer who steals from the rich (you qualify).
+
+# New monster added by furiosity for the Theme module
+N:139:Gull
+G:B:o
+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_ONLY | WILD_SHORE | CAN_FLY | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | WILD_OCEAN | NEUTRAL | NO_TARGET |
+D:A sea-bird found around the shores of Middle-earth. The sound of a
+D:gull mewing is said to awaken the Sea-longing in the heart of an Elf.
+
+N:140:Lagduf, the Snaga
+G:o:o
+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:An orc soldier who served under Shagrat in the garrison of the Tower of Cirith Ungol.
+
+# New monster added by furiosity for the Theme module
+N:141:Kirinki
+G:B:R
+I:110:1d1:30:1:10
+W:1:9:80:0
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:RAND_25 | CAN_FLY | WILD_TOO | WILD_WOOD | WILD_GRASS |
+F:ANIMAL | DROP_SKELETON | HAS_EGG | IMPRESED |
+F:MORTAL | BASEANGBAND
+S:1_IN_10
+S:SHRIEK
+D:A tiny scarlet bird from Numenor, with a really high voice.
+
+N:142:Apprentice 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.
+
+# New monster added by furiosity for the Theme module
+N:145:Swan
+G:B:w
+I:120:9d10:8:100:255
+W:20:3:300:0
+E:0:1:0:6:1:0
+O:0:0:0:0
+F:CAN_FLY | NEUTRAL | NO_TARGET | GOOD |
+F:MORTAL | NEVER_BLOW | DG_CURSE | WILD_ONLY |
+F:AQUATIC | WILD_SHORE | NO_TARGET | WILD_OCEAN
+D:Beautiful and graceful large white birds inhabiting aquatic
+D:regions. They never do any harm, and it is said that anyone
+D:who kills a swan incurs the wrath of the Valar.
+
+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:Apprentice 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 Morgoth.
+
+N:148:Caborrog
+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 |
+S:1_IN_50 |
+S:BR_NETH
+D:A minor demonic servant of evil. It resembles a frog - in fact,
+D:Caborroeg are the result of Morgoth's corruption of frogs.
+
+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 cash!
+
+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 | IMPRESED | BASEANGBAND | AQUATIC
+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.
+
+# New monster added by furiosity for the Theme module
+N:153:Thrush
+G:B:U
+I:120:3d5:40:12:0
+W:0:2:400:0
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d3
+B:BITE:HURT:1d3
+F:ANIMAL | WILD_ONLY | WILD_WOOD | CAN_FLY | DROP_CORPSE |
+F:MORTAL | HAS_EGG | BASEANGBAND | NEUTRAL | NO_TARGET | WILD_GRASS |
+D:Large speckled brown birds with a special fondness for snails,
+D:whose shells they remove by breaking them against stones.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:155:Fox
+G:C:o
+I:120:7d7:30:30:20
+W:10:1:600:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+F:BASH_DOOR | WILD_TOO | WILD_WOOD | WILD_WASTE | WILD_MOUNTAIN |
+F:ANIMAL | DROP_CORPSE | MORTAL | BASEANGBAND
+S:1_IN_8 |
+S:SHRIEK
+D:Dog-like carnivore of woodland and farmland, distinctive for
+D:its red-orange coats and its eerie plaintive cries.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:158:Fly of Mordor
+G:I:U
+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 | EVIL |
+F:WEIRD_MIND | ANIMAL | MORTAL | BASEANGBAND
+S:SHRIEK
+D:An evil swarm of bloodsucking flies. They are grey, brown and black
+D:insects, together in a homogeneous mass. They are loud, hateful and
+D:hungry, and marked with a red eye-shape upon their backs.
+
+# New monster added by furiosity for the Theme module
+# Based on Serpent men from Zangband
+N:159:Limlug
+G:J:G
+I:120:15d10:20:40:20
+W:22:6: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 | AQUATIC |
+F:DROP_60 | DROP_2D2 | FRIENDS | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | EVIL | MORTAL | BASEANGBAND
+F:WILD_TOO | WILD_OCEAN | WILD_SHORE |
+S:1_IN_8
+S:BA_POIS | SCARE | HOLD
+D:A sea-serpent of Elvish legend.
+
+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:u
+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:Hatchling 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 newly-born dragon is still soft, its eyes unaccustomed to light and
+D:its scales a pale blue.
+
+N:164:Hatchling 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 | 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 newly-born dragon is still soft, its eyes unaccustomed to light and
+D:its scales a pale white.
+
+N:165:Hatchling 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 newly-born dragon is still soft, its eyes unaccustomed to light and
+D:its scales a sickly green.
+
+N:166:Hatchling 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 newly-born dragon is still soft, its eyes unaccustomed to light and
+D:its scales a dull black.
+
+N:167:Hatchling 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 newly-born 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.
+
+# New monster added for the Theme module
+# From UnAngband
+N:170:Radbug, the Goblin
+G:o:y
+I:110:15d10:20:20:60
+W:7:3:1500:100
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+B:HIT:HURT:1d10
+F:BASEANGBAND | UNIQUE | MALE | EVIL | ORC | FORCE_MAXHP |
+F:CAN_SPEAK | DROP_CORPSE | WILD_TOO | OPEN_DOOR | BASH_DOOR |
+F:HURT_LITE | ONLY_ITEM | DROP_1D2 | DROP_GOOD | ESCORT |
+D:Strong and powerful, for a goblin.
+
+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_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:r
+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 | NEUTRAL | NO_TARGET | IMPRESED | HAS_EGG | AQUATIC
+D:A magnificent huge predatory bird.
+
+N:173:War bear
+G:q:r
+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:A bear with tusks, trained to kill.
+
+N:174:Killer bee
+G:I:o
+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:W
+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 large 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.
+
+# New monster added for the Theme module
+# Based on Nightshade from T-Plus by Ingeborg S. Norden
+N:177:The Lucky Hobbit
+G:h:D
+I:110:40d5:40:10:3
+W:10:2:660:0
+E:0:1:1:2:1:1
+O:0:40:10:40
+B:TOUCH:EAT_FOOD
+B:TOUCH:EAT_ITEM
+B:BEG:EAT_GOLD
+B:INSULT:*
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE |
+F:FORCE_MAXHP | FORCE_DEPTH | WILD_TOWN | WILD_ONLY |
+F:OPEN_DOOR | BASH_DOOR | DROP_GOOD |
+F:RAND_25 | DROP_60 | DROP_2D2 | DROP_GREAT | ONLY_ITEM |
+F:MORTAL | JOKEANGBAND | HAS_LITE
+D:An obscenely lucky and very stealthy Halfling. You have an intense
+D:desire to kill this creature.
+
+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.
+
+# New monster added for the Theme module
+# Suggested by Atarlost in the t-o-m-e.net forums
+N:179:Dark dwarven warrior
+G:k:u
+I:110:2d100:25:120:30
+W:20:2:0:160
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+F:MALE
+F:FORCE_SLEEP | FRIENDS
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD
+F:OPEN_DOOR | BASH_DOOR
+D:The dwarves of Nogrod were ever greedy, and through this Morgoth was able to
+D:snare them. Now they act as slavemasters in his mines.
+
+# New monster added for the Theme module
+# Suggested by Atarlost in the t-o-m-e.net forums
+N:180:Dark dwarven smith
+G:k:w
+I:110:2d100:25:120:1
+W:22:1:0:175
+B:HIT:SHATTER:6d6
+F:MALE
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD
+F:OPEN_DOOR | BASH_DOOR
+F:EVIL
+D:The dwarves of Nogrod were ever greedy, and through this Morgoth was able to
+D:snare them. This smith has been forging blades for the Orcs.
+
+# New monster added for the Theme module
+# Suggested by Atarlost in the t-o-m-e.net forums
+N:181:Dark dwarven lord
+G:k:D
+I:110:5d100:25:150:180
+W:25:2:0:300
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+B:HIT:HURT:3d6
+F:MALE | EVIL
+F:FORCE_SLEEP | FORCE_MAXHP
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD
+F:OPEN_DOOR | BASH_DOOR
+F:IM_FIRE | IM_COLD | NO_CONF | NO_SLEEP
+S:1_IN_8
+S:HEAL | BO_FIRE | BO_ACID
+D:The dwarves of Nogrod were ever greedy, and through this Morgoth was able to
+D:snare them. This is a lord among the dark dwarves.
+
+# New monster added by furiosity for the Theme module
+# Based on Atarlost's suggestions for dark dwarves
+# and on dark elven priests.
+N:182:Dark dwarven priest
+G:k:g
+I:120:2d100:20:30:30
+W:27:1:1200:50
+E:1:1:1:2:1:1
+O:0:10:90:0
+B:HIT:HURT:2d8
+B:HIT:HURT:2d8
+F:MALE |
+F:FORCE_SLEEP |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+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:The dwarves of Nogrod were ever greedy, and through this Morgoth was able to
+D:snare them. This priest serves Melkor unquestioningly, and today it is your
+D:turn to die.
+
+N:183: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: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. Short and broad, he has crooked
+D:legs and arms that hang almost to the ground.
+
+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:The beating of its 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 with the claws and the face of an owl.
+
+# New monster added by furiosity for the Theme module
+N:189:Clear mewlip
+G:i:w
+I:110:2d5:12:6:10
+W:1:1:0:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:HURT:1d2
+F:BASEANGBAND | EMPTY_MIND | STUPID | ATTR_CLEAR |
+F:UNDEAD | RAND_50 | CAN_SWIM | INVISIBLE | EVIL |
+D:An evil cannibal spirit from the marshlands.
+
+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:Limrog
+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. It resembles a fish - Limroeg
+D:are actually fish that were corrupted by Morgoth.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:200:Gray mewlip
+G:i:s
+I:110:4d7:12:9:10
+W:3:1:0:5
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:HURT:2d4
+F:BASEANGBAND | EMPTY_MIND | STUPID |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL |
+D:An evil cannibal spirit from the marshlands. It is
+D:especially aggressive.
+
+# New monster added by furiosity for the Theme module
+N:201:Orange mewlip
+G:i:o
+I:110:6d9:12:12:10
+W:5:1:0:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:POISON:2d4
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL |
+D:An evil cannibal spirit from the marshlands. It is
+D:surrounded by a foul stench.
+
+N:202:Undead mass
+G:j:D
+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 other body parts.
+D:It seems to be growing.
+
+# New monster added by furiosity for the Theme module
+N:203:Bloodshot mewlip
+G:i:r
+I:110:6d9:12:12:10
+W:5:1:0:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:LOSE_STR:2d4
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL |
+D:An evil cannibal spirit from the marshlands. Its form seems
+D:infused with blood, surely that of its victims.
+
+N:204:Hatchling 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 newly-born dragon is still soft, its eyes unaccustomed to light and
+D:its scales shimmering with a hint of colour.
+
+# New monster added by furiosity for the Theme module
+N:205:Green mewlip
+G:i:g
+I:110:6d9:12:12:10
+W:5:1:0:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:POISON:2d4
+B:TOUCH:CONFUSE:3d5
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL
+D:An evil cannibal spirit from the marshlands. It is
+D:surrounded by a foul stench and an aura of mystery.
+
+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."
+
+# New monster added by furiosity for the Theme module
+N:207:Blue mewlip
+G:i:b
+I:110:6d9:12:12:10
+W:5:1:0:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:ELEC:3d5
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL | IM_ELEC |
+D:An evil cannibal spirit from the marshlands. It is
+D:surrounded by a barely noticeable aura of sparks.
+
+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.
+
+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 |
+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 is coated with 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...
+
+# New monster added by furiosity for the Theme module
+N:214:Brown mewlip
+G:i:u
+I:110:6d9:12:12:10
+W:5:1:0:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:LOSE_CHR:3d5
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL | NO_CUT |
+D:An evil cannibal spirit from the marshlands. It seems to
+D:be rising straight from the earth to get you.
+
+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 from the Misty Mountains
+D:He's been known to pick on Shire-folk.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:217:Stone mewlip
+G:i:W
+I:110:7d10:12:25:10
+W:10:5:0:2
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:HURT:3d5
+B:TOUCH:SHATTER:1d1
+# Yes, I'm nasty. :P -furiosity
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL | NO_CUT | NO_STUN |
+D:An evil cannibal spirit from the marshlands. It seems to
+D:be coming from the walls.
+
+N:218:Hatchling 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 newly-born dragon is still soft, its eyes unaccustomed to light and
+D:its scales a dull bronze.
+
+N:219:Hatchling 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 newly-born 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.
+
+# New monster added by furiosity for the Theme module
+N:221:Yellow mewlip
+G:i:y
+I:110:7d10:12:25:10
+W:15:5:0:20
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:EAT_LITE:3d5
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL | NO_CUT |
+D:An evil cannibal spirit from the marshlands. It seems to
+D:be surrounded with a strange aura of dark light.
+
+# New monster added by furiosity for the Theme module
+N:222:Pink mewlip
+G:i:R
+I:110:9d12:12:30:10
+W:20:5:0:30
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:LOSE_STR:4d4
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL | NO_CUT |
+D:An evil cannibal spirit from the marshlands. It glows
+D:an eerily unnatural pink colour.
+
+# New monster added by furiosity for the Theme module
+N:223:Tree mewlip
+G:i:G
+I:110:10d12:12:40:10
+W:25:3:0:50
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:POISON:5d5
+B:TOUCH:LOSE_DEX:5d5
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL | CAN_FLY |
+D:An evil cannibal spirit from the marshlands. It prefers
+D:to dwell in the trees and is rather stealthy.
+
+# New monster added by furiosity for the Theme module
+N:224:Air mewlip
+G:i:B
+I:110:11d12:12:45:10
+W:25:3:0:70
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:POISON:6d6
+B:TOUCH:BLIND:6d6
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL | IM_ELEC |
+D:An evil cannibal spirit from the marshlands. It seems to
+D:be materializing out of thin air.
+
+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 Vala. He believes you to be a
+D:servant of the Shadow.
+
+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 the Void.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# Based on Plague worm mass from T-Plus by Ingeborg S. Norden
+N:232:Plague mewlip
+G:i:U
+I:110:100d11:15:70:20
+W:40:3:0:300
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:DISEASE:3d5
+B:TOUCH:PARASITE:6d5
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL |
+D:An evil cannibal spirit from the marshlands. Foul stench
+D:of decay surrounds it, and you are afraid to let it get
+D:near you.
+
+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 pile of flesh, 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:B
+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:They say it is 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.
+
+# New monster added by furiosity for the Theme module
+# Based on Charnel worm mass from T-Plus by Ingeborg S. Norden
+N:237:Death mewlip
+G:i:D
+I:130:44d100:255:75:10
+W:80:5:10:4000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:TOUCH:EXP_20:7d5
+B:TOUCH:DISEASE:7d5
+B:TOUCH:PARASITE:14d10
+B:TOUCH:LOSE_ALL:7d5
+F:BASEANGBAND | EMPTY_MIND | STUPID | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL | NO_FEAR |
+F:FORCE_MAXHP | KILL_ITEM | KILL_BODY | NO_CONF |
+F:NO_SLEEP | NO_CUT | NO_STUN | REGENERATE | IM_COLD |
+F:RES_NETH | RES_PLAS | RES_WATE | RES_DISE | RES_NEXU |
+S:1_IN_5
+S:ANIM_DEAD |
+D:An evil cannibal spirit from the marshlands. It has been given
+D:additional power by necromantic magic, making it nearly invulnerable.
+# Wow, a dangerous icky thing :P
+
+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 spellcaster.
+
+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: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 Yavanna Kementari. He thinks you want to
+D:destroy the forests of Arda.
+
+# New monster added by furiosity for the Theme module
+# A Black Numenorean (hell knight) on steroids
+N:242:Fuinur, Lord of the Haradrim
+G:p:r
+I:140:80d100:50:300:0
+W:90:1:0:150000
+E:1:1:1:2:1:1
+O:0:40:60:0
+B:HIT:HURT:20d5
+B:HIT:EXP_80:20d5
+F:UNIQUE | MALE | MORTAL | BASEANGBAND | HAS_LITE |
+F:FORCE_MAXHP | SMART | IM_FIRE | IM_ELEC | IM_POIS |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GREAT |
+F:RES_NETH | RES_NEXU | RES_PLAS | RES_WATE |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | EVIL | SPECIAL_GENE |
+S:1_IN_5 |
+S:BLIND | SCARE | CAUSE_4 | BA_NETH | BA_FIRE | BO_PLAS
+S:S_HI_DEMON | S_HI_UNDEAD | HASTE |
+D:A Man of Numenor who fell under the influence of Sauron during the time
+D:the Dark Lord dwelt on that island. He sailed east to Middle-earth, with
+D:a companion named Herumor, and settled in the southern region of Harad.
+D:These two came to hold great power among the Haradrim. Fuinur has been
+D:appointed the task of guarding the realm of Angmar from intruders, in
+D:anticipation of Sauron's victory over the free People.
+
+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 pile of flesh. It is
+D:eating away the floor it 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.
+
+# New monster added by furiosity for the Theme module
+N:249:Glorfindel of Rivendell
+G:h:b
+I:120:8d90:20:70:10
+W:25:3:1400:25000
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:10d8
+B:HIT:HURT:10d8
+B:HIT:HURT:10d8
+F:BASEANGBAND | WILD_TOO | WILD_WOOD | WILD_GRASS |
+F:UNIQUE | MALE | FORCE_MAXHP | GOOD | CAN_SPEAK |
+F:SMART | PET | HAS_LITE | OPEN_DOOR | BASH_DOOR |
+F:DROP_CORPSE | DROP_SKELETON | MORTAL | CAN_SWIM |
+F:RES_WATE | RES_NETH | IM_COLD | IM_ACID | IM_POIS |
+S:1_IN_2 |
+S:ARROW_4 | S_KIN |
+D:A fair Elf in a travel cloak, wielding a longbow and short sword.
+D:Stately and graceful, he rides his steed Asfaloth with great speed.
+D:It is rumoured that he is none other but Glorfindel of Gondolin, who
+D:has been sent back from the halls of Mandos to serve the Free People
+D:of Middle-earth in this time of darkness and sorrow.
+
+N:250:Giant white dragonfly
+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 dragonfly that drips frost.
+
+N:251:Snaga sapper
+G:o:o
+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 | BASEANGBAND | HAS_LITE
+D:He is one of the many weaker 'slave' orcs, often mistakenly called a
+D:goblin. He is equipped with an explosive charge.
+
+# New monster added by furiosity for the Theme module
+# Powered-up high elf
+N:252:Finrod Felagund
+G:h:B
+I:120:50d50:20:80:10
+W:40:3:1400:1000
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:MALE | OPEN_DOOR | BASH_DOOR | UNIQUE |
+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_5 |
+S:S_KIN
+D:The eldest son of Finarfin and brother to Galadriel, who founded
+D:Minas Tirith in the Pass of Sirion, and delved his citadel at
+D:Nargothrond on the River Narog. He went with Beren on the Quest
+D:of the Silmaril, and was lost in the pits of Sauron on Tol-in-Gaurhoth.
+D:His spirit tarried at the Halls of Waiting, and he was allowed to return
+D:to Middle-earth in this time of strife.
+
+N:253:Gibbering mouther
+G:j:R
+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.
+
+# New monster added by furiosity for the Theme module
+N:254:Maedhros the Tall
+G:h:u
+I:120:60d25:20:80:10
+W:45:10:2300:2500
+E:1:1:1:1:1:1
+O:20:80:0:0
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:MALE | NO_FEAR | NO_STUN | BASH_DOOR | KILL_BODY | FORCE_MAXHP |
+F:DROP_SKELETON | DROP_CORPSE | UNIQUE | SMART | DROP_4D2 |
+F:MORTAL | BASEANGBAND | HAS_LITE | DROP_GREAT | WILD_TOO | WILD_GRASS |
+S:1_IN_4 | HASTE | SCARE |
+D:The eldest of the Seven Sons of Feanor, and considered to be their
+D:leader. In Valinor he swore the Oath of Feanor, and followed his
+D:father back to Middle-earth. After Feanor's death, Morgoth captured
+D:Maedhros through trickery, and hung him by the wrist from the heights
+D:of Thangorodrim. Fingon succeeded in rescuing him, but he lost his right
+D:hand in their escape. He searches forever for the Silmarils of Feanor.
+
+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. If you see
+D:one, it usually means orcs are nearby.
+
+N:258:Cheerful leprawn
+G:l: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 | BASEANGBAND | PET
+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:Just looking at it makes you itchy all over.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:263:Maglor the Mighty Singer
+G:h:g
+I:120:60d25:20:80:10
+W:45:10:2300:2500
+E:1:1:1:2:1:1
+O:0:20:80:0
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:MALE | NO_FEAR | NO_STUN | BASH_DOOR | KILL_BODY | FORCE_MAXHP |
+F:DROP_SKELETON | DROP_CORPSE | UNIQUE | SMART | DROP_4D2 |
+F:MORTAL | BASEANGBAND | HAS_LITE | DROP_GREAT | WILD_TOO | WILD_GRASS |
+S:1_IN_3 |
+S:HEAL | SCARE | CAUSE_2 | HOLD | CONF | S_ANIMALS |
+D:The second son of Feanor, who inherited more of his mother Nerdanel's
+D:gentle spirit than any of his brothers. Maglor was famed as a poet and
+D:bard, but he took the Oath of Feanor in Tirion and shared in the woes
+D:that came of it. He searches forever for the Silmarils of Feanor.
+
+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 with man and orc, combining man's
+D:strength and cunning with orcish evil. The Dunlendings fighting on
+D:Saruman's side were first noted to mix their blood with the orcs.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:268:Celegorm the Fair
+G:h:o
+I:120:60d25:20:80:10
+W:45:10:2300:2500
+E:1:1:1:2:1:1
+O:0:20:80:0
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:MALE | NO_FEAR | NO_STUN | BASH_DOOR | KILL_BODY | FORCE_MAXHP |
+F:DROP_SKELETON | DROP_CORPSE | UNIQUE | SMART | DROP_4D2 |
+F:MORTAL | BASEANGBAND | HAS_LITE | DROP_GREAT | WILD_TOO | WILD_GRASS |
+S:1_IN_3 |
+S:S_MONSTER | S_ANIMAL | S_KIN | S_MONSTERS | S_ANIMALS | S_KIN |
+D:Celegorm the Fair was the third of the seven sons of Feanor.
+D:The most ambitious of the seven, he followed the oath of his father
+D:with the greatest ardour. He searches forever for the Silmarils.
+
+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.
+D:It 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 skull animated by necromantic spells. You'll seldom catch one alone.
+
+# New monster added by furiosity for the Theme module
+N:274:Caranthir the Dark
+G:h:D
+I:120:60d25:20:80:0
+W:45:10:2300:2500
+E:1:1:1:2:1:1
+O:0:20:80:0
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:TOUCH:EAT_ITEM:10d10
+F:MALE | NO_FEAR | NO_STUN | BASH_DOOR | KILL_BODY | FORCE_MAXHP |
+F:DROP_SKELETON | DROP_CORPSE | UNIQUE | SMART | DROP_4D2 |
+F:MORTAL | BASEANGBAND | HAS_LITE | DROP_GREAT | WILD_TOO | WILD_GRASS |
+S:1_IN_3 |
+S:TRAPS | CONF | SCARE |
+D:The fourth son of Feanor, who turned on his own kind because of the
+D:Oath of his father. He searches forever for the Silmarils of Feanor.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:276:Curufin the Crafty
+G:h:y
+I:120:60d25:20:80:10
+W:45:10:2300:2500
+E:1:1:1:2:1:1
+O:0:80:0:20
+B:HIT:HURT:10d10
+B:HIT:EAT_ITEM:10d10
+B:HIT:EAT_ITEM:10d10
+B:HIT:EAT_ITEM:10d10
+F:MALE | NO_FEAR | NO_STUN | BASH_DOOR | KILL_BODY | FORCE_MAXHP |
+F:DROP_SKELETON | DROP_CORPSE | UNIQUE | SMART | DROP_4D2 |
+F:MORTAL | BASEANGBAND | HAS_LITE | DROP_GREAT | WILD_TOO | WILD_GRASS |
+S:1_IN_3 |
+S:TELE_TO | TELE_AWAY | TPORT | BLINK | TRAPS |
+D:The fifth son of Feanor, closely associated with his elder
+D:brother Celegorm. Bound by the Oath of Feanor, he searches
+D:forever for the Silmarils.
+
+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:Aewrog
+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
+S:1_IN_50 |
+S:BR_NETH
+D:A minor demonic servant of evil. It resembles a bird - in fact,
+D:the Aewroeg are the result of the corruption of smaller birds by Morgoth.
+
+N:281:Gnome mage
+G:l: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 unnatural 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 dragonfly
+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 large, 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 surround his 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 | WILD_TOO |
+F:WEIRD_MIND | ANIMAL | BASEANGBAND
+S:MULTIPLY
+D:A giant buzzing wasp, its stinger drips venom.
+
+N:290:Lizard man
+G:l: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 being 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. He professed to follow
+D:Caranthir, but turned on the Sons of Feanor, and so
+D:brought about their defeat.
+
+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 | AQUATIC
+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:He can drive himself into such a terrible battle-frenzy that he
+D:can survive blows which should kill him, and still apparently feel
+D:no pain. He tramples weaker creatures underfoot in his eagerness
+D:to get to his real enemy, and his battle-cry strikes terror into
+D:his foes.
+
+N:294:Draugrog
+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 |
+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:Draugroeg are doglike demons, dogs corrupted by Morgoth.
+
+N:295:Sphinx
+G:H:G
+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.
+D:Unfortunately, you do not understand the language
+D:it speaks.
+
+N:296:Narrog
+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:A minor demonic servant of evil. It resembles a rat - in fact,
+D:the Narroeg are the result of the corruption of rats by Morgoth.
+
+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:Trolls were made by Melkor Bauglir in mockery of the Ents. This one
+D:is green-skinned and very 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 this 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:An aura of lightning forms a ghostly halo around this hound, and
+D:sparks sting 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. His armour bears the mark of Saruman - a large white hand.
+
+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 has power and great cunning, as leader of the garrison at Cirith Ungol.
+D:He is a large Uruk with an evil face, protruding fangs and long arms.
+
+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: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 | BASEANGBAND |
+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.
+
+# New monster added by furiosity for the Theme module
+N:318:Amrod, Son of Feanor
+G:h:W
+I:120:60d25:20:80:10
+W:45:10:2300:2500
+E:1:1:1:2:1:1
+O:0:80:0:20
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:MALE | NO_FEAR | NO_STUN | BASH_DOOR | KILL_BODY | FORCE_MAXHP |
+F:DROP_SKELETON | DROP_CORPSE | UNIQUE | SMART | DROP_4D2 |
+F:MORTAL | BASEANGBAND | HAS_LITE | DROP_GREAT | WILD_TOO | WILD_GRASS |
+S:1_IN_3 |
+S:ARROW_1 | ARROW_2 | ARROW_3 | ARROW_4 | MISSILE
+D:The youngest son of Feanor along with his twin brother Amras.
+D:He is bound by the Oath of Feanor and searches forever for the
+D:Silmarils.
+
+# New monster added by furiosity for the Theme module
+N:319:Amras, Son of Feanor
+G:h:w
+I:120:60d25:20:80:10
+W:45:10:2300:2500
+E:1:1:1:2:1:1
+O:0:80:0:20
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+B:HIT:HURT:10d10
+F:MALE | NO_FEAR | NO_STUN | BASH_DOOR | KILL_BODY | FORCE_MAXHP |
+F:DROP_SKELETON | DROP_CORPSE | UNIQUE | SMART | DROP_4D2 |
+F:MORTAL | BASEANGBAND | HAS_LITE | DROP_GREAT | WILD_TOO | WILD_GRASS |
+S:1_IN_3 |
+S:ARROW_1 | ARROW_2 | ARROW_3 | ARROW_4 | MISSILE |
+S:TPORT | BLINK | TELE_TO | TELE_AWAY | S_MONSTER
+D:The youngest son of Feanor along with his twin brother Amrod.
+D:He is bound by the Oath of Feanor and searches forever for the
+D:Silmarils.
+
+N:320:Giant bronze dragonfly
+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 gleaming dragonfly's wings beat mesmerizingly 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:He is eighteen feet tall and looking at you.
+
+N:322:Giant black dragonfly
+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 dragonfly 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 of hard stone.
+
+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 growth on the dungeon floor, glowing red.
+
+N:325:Giant gold dragonfly
+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.
+
+# New monster added by furiosity for the Theme module
+# Anti-Naugladur
+N:326:Telchar the Smith
+G:k:w
+I:110:20d100:30:200:200
+W:70:1:0:12000
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+F:UNIQUE | MALE | CAN_SPEAK |
+F:FORCE_MAXHP | SMART | PET |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_GREAT
+F:OPEN_DOOR | BASH_DOOR
+F:GOOD | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS
+D:A Dwarf of Nogrod in the Blue Mountains, and one of the
+D:greatest smiths in the history of Middle-earth. Among his
+D:works were Angrist, Narsil, and the Dragon-helm of Dor-lómin.
+D:He is he last among the Dwarves of Nogrod to be on the
+D:side of the Light.
+
+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:g
+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:R
+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, and he hates all dwarves and their friends.
+
+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:l:r
+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 leader of lizard men, coming from the depths.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:334:Roac, son of Carc
+G:B:W
+I:120:30d10:40:12:0
+W:0:2:500:500
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:2d4
+B:BITE:HURT:2d4
+F:BASEANGBAND | UNIQUE | MALE | CAN_SPEAK | FORCE_MAXHP | PET |
+F:WILD_ONLY | WILD_WOOD | WILD_MOUNTAIN | WILD_GRASS | WILD_WASTE |
+F:ANIMAL | CAN_FLY | DROP_CORPSE | SMART | GOOD | MORTAL |
+S:1_IN_2
+S:S_MONSTER | S_MONSTERS
+D:One of the ravens who lived on Ravenhill, one of the foothills of Erebor,
+D:the Lonely Mountain. Roac is a very ancient bird indeed: his father Carc
+D:had seen Smaug descend on the mountain, and Roac was no less than 153 years
+D:old ('out of the egg', as he put it) when Smaug was destroyed. He is a
+D:leader among the ravens and his cunning is not to be underestimated.
+
+N:335:Great eagle
+G:B:r
+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 | NEUTRAL | NO_TARGET |
+F:WILD_MOUNTAIN | WILD_VOLCANO | WILD_WASTE | WILD_WOOD | WILD_ONLY |
+F:ANIMAL | GOOD | DROP_CORPSE | BASEANGBAND | IMPRESED | HAS_EGG | AQUATIC |
+D:Greater and more intelligent than most of its kind, this eagle is
+D:a messenger between the forces of good. It answers to Manwe Sulimo.
+
+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 its 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 hybrid 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. It looks harmless.
+
+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, related to the yeti.
+
+# New monster added by furiosity for the Theme module
+N:344:Carc of Ravenhill
+G:B:W
+I:120:6d100:40:12:0
+W:0:2:500:500
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:HURT:4d8
+B:BITE:HURT:4d8
+F:BASEANGBAND | UNIQUE | MALE | CAN_SPEAK | FORCE_MAXHP | PET |
+F:WILD_ONLY | WILD_WOOD | WILD_MOUNTAIN | WILD_GRASS | WILD_WASTE |
+F:ANIMAL | CAN_FLY | DROP_CORPSE | SMART | GOOD | MORTAL |
+S:1_IN_2
+S:S_ANIMAL | S_ANIMALS
+D:One of the ravens who lived on Ravenhill, one of the foothills of Erebor,
+D:the Lonely Mountain. Carc seems to have been the chief of those birds,
+D:and dwelt with his wife above the Dwarves' guard-chamber on the hill.
+D:He is a wise old bird who knows what is best for his folk and Arda.
+
+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 devoted to Tulkas Astaldo. He considers you to be an
+D:agent of Morgoth.
+
+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:D
+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, 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 humanoid, wreathed in clouds.
+
+N:350:Ugluk, the Uruk
+G:o:R
+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.
+D:He was raised on man-flesh at Isengard, and bears the mark of the White Hand.
+
+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:R
+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.
+
+# New monster added by furiosity for the Theme module
+N:357:Alatar, the Blue Wizard
+G:p:b
+I:120:49d101:101:100:0
+W:110:7:1600:35000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:UN_BONUS:8d12
+B:HIT:TERRIFY:7d7
+F:BASEANGBAND | FORCE_DEPTH | FORCE_MAXHP | FORCE_SLEEP |
+F:UNIQUE | MALE | CAN_SPEAK | PET | DROP_CORPSE |
+F:REFLECTING | RES_TELE | SMART | GOOD |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GREAT |
+F:OPEN_DOOR | BASH_DOOR | HAS_LITE | TAKE_ITEM |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | IM_POIS |
+F:RES_NETH | NO_CONF | NO_SLEEP | NO_FEAR |
+S:1_IN_2 |
+S:HEAL | HASTE | TPORT | TELE_AWAY | BLIND | CONF | SCARE |
+S:CAUSE_4 | BRAIN_SMASH | FORGET | TRAPS | CAUSE_4 |
+S:BA_FIRE | BA_MANA | BO_FIRE | BO_MANA | BO_PLAS | BR_PLAS |
+S:S_MONSTERS | S_HI_DRAGON | S_KIN | S_ANIMALS |
+D:He is dressed in blue from head to toe. He is one of the five Istari
+D:who came to the northwest of Middle-earth in the Third Age; he
+D:journeyed into the east with Pallando, and never returned to the western
+D:lands. They stumbled upon a magical portal to the remnants of Utumno and
+D:could not find their way back. Alatar is still trying to continue the
+D:task the Valar have appointed him.
+
+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 | HAS_LITE |
+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 orc-like figure covered in wrappings.
+
+N:363:Wolf chieftain
+G:C:g
+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 leader whose pack is in the service of the Dark Lord,
+D:and whose howls strike fear into the boldest hearts.
+
+# New monster added by furiosity for the Theme module
+N:364:Pallando, the Blue Wizard
+G:p:b
+I:120:49d101:101:100:0
+W:120:7:1600:45000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:UN_BONUS:10d14
+B:HIT:TERRIFY:9d9
+F:BASEANGBAND | FORCE_DEPTH | FORCE_MAXHP | FORCE_SLEEP |
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE |
+F:REFLECTING | RES_TELE | SMART | EVIL |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GREAT |
+F:OPEN_DOOR | BASH_DOOR | HAS_LITE | TAKE_ITEM |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | IM_POIS |
+F:RES_NETH | NO_CONF | NO_SLEEP | NO_FEAR |
+S:1_IN_2 |
+S:HEAL | HASTE | TPORT | TELE_AWAY | BLIND | CONF | SCARE |
+S:CAUSE_4 | BRAIN_SMASH | FORGET | TRAPS |
+S:BA_FIRE | BA_MANA | BO_FIRE | BO_MANA | BO_PLAS | BR_PLAS |
+S:S_HI_UNDEAD | S_HI_DRAGON | S_HI_DEMON |
+D:He is dressed in blue from head to toe. He is one of the five Istari
+D:who came to the northwest of Middle-earth in the Third Age; he
+D:journeyed into the east with Alatar, and never returned to the western
+D:lands. They stumbled upon a magical portal to the remnants of Utumno and
+D:could not find their way back. Pallando has fallen into the Shadow
+D:since being in Utumno, and has designs on ruling the free people.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# Powered-up Lagduf
+N:370:Muzgash, the Snaga
+G:o:o
+I:110:33d10:20:32:30
+W:22:3:1700:80
+E:1:1:1:2:1:1
+O:10:80:0:0
+B:HIT:HURT:3d11
+B:HIT:HURT:3d11
+B:HIT:HURT:3d10
+B:HIT:HURT:3d10
+F:UNIQUE | MALE | EVIL | ORC | FORCE_MAXHP | ESCORT |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD | SMART | CAN_SPEAK |
+F:OPEN_DOOR | BASH_DOOR | CAN_SPEAK | DROP_SKELETON | DROP_CORPSE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A friend of Lagduf's, he is no less evil but more cunning.
+
+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:R
+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:A large, strong and agile orc, the slayer of dwarven king Thror.
+
+# New monster added for the Theme module
+# From UnAngband
+N:374:Bill Ferny
+G:p:b
+I:120:6d10:16:8:5
+W:5:3:180:90
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+B:HIT:EAT_GOLD:1d8
+F:BASEANGBAND | UNIQUE | MALE | FORCE_MAXHP | CAN_SPEAK |
+F:OPEN_DOOR | BASH_DOOR | HAS_LITE | SMART | EVIL |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD |
+D:A swarthy man of Bree. He has heavy black brows and dark scornful
+D:eyes; his large mouth is curled in a sneer. He sells anything to
+D:anybody and enjoys making mischief for his personal amusement.
+
+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 frighteningly 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. He is a descendant
+D:of Castamir, the Usurper of Gondor.
+
+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, arisen when a humanoid was killed violently
+D:in a forest. It remembers its past life and wants revenge.
+
+N:382:Khim, Son of Mim
+G:k: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:k: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:v
+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.
+
+# New monster added by furiosity for the Theme module
+N:388:Beruthiel, Queen of Cats
+G:f:B
+I:115:7d100:100:200:10
+W:33:5:0:30000
+E:0:1:0:2:1:0
+O:30:60:0:10
+B:CLAW:HURT:8d11
+B:CLAW:EAT_LITE:8d11
+B:CLAW:LOSE_INT:8d11
+F:BASEANGBAND | WILD_TOO | WILD_WOOD | WILD_GRASS | WILD_MOUNTAIN |
+F:UNIQUE | FEMALE | CAN_SPEAK | SMART | FORCE_MAXHP | EVIL | MORTAL |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | ANIMAL | FORCE_SLEEP |
+F:HURT_LITE | IM_FIRE | IM_POIS | RES_NETH | NO_SLEEP | NO_CONF |
+S:1_IN_5
+S:S_KIN | SHRIEK | S_ANIMALS | BLINK | S_MONSTERS
+D:The dark and mournful Queen to Tarannon Falastur, the twelfth King
+D:of Gondor. She owned ten marvellous and magical cats, that she set
+D:to spy on the doings of the people of Gondor. Her scheming was her
+D:undoing, though, since her husband set her adrift on the Great Sea,
+D:with only her cats for company. She sailed into the far south and
+D:encountered a mysterious wizard dressed in blue, who granted her
+D:one wish - to become a cat herself. She met Tevildo on her travels,
+D:and became his consort and a servant of Morgoth.
+
+# New monster added by furiosity for the Theme module
+N:389:The Hunter
+G:H:v
+I:130:10d500:60:150:0
+W:80:1:0:80000
+E:0:1:0:2:2:2
+O:20:40:20:10
+B:HIT:UN_BONUS:5d10
+B:HIT:EXP_80:5d10
+B:HIT:ABOMINATION:5d10
+B:HIT:DISEASE:5d10
+F:BASEANGBAND | UNIQUE | FORCE_MAXHP |
+F:ELDRITCH_HORROR | REFLECTING |
+F:NO_SLEEP | NO_CUT | NO_STUN | NO_CONF | NO_FEAR |
+F:ONLY_ITEM | DROP_4D2 | DROP_GREAT | DROP_RANDART |
+F:IM_FIRE | IM_COLD | IM_ACID | IM_POIS | SPECIAL_GENE |
+F:RES_NETH | RES_PLAS | EVIL | EMPTY_MIND |
+D:A shadowy creature of Elvish legend, said to have plagued the
+D:newly-awakened Elves at Cuivienen. Those Elves captured by the
+D:Hunter were thought to have been corrupted by Melkor to form
+D:the race of Orcs. You sense corruption all around you as it
+D:approaches. Your heart fills with despair and anguish, and you
+D:can just barely resist fleeing as far away from it as possible.
+
+
+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. He is a descendant
+D:of Castamir, the usurper of Gondor's throne.
+
+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:JOKEANGBAND
+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.
+
+# New monster added by furiosity for the Theme module
+# Basically an insanely OOD Black Numenorean (hell knight)
+N:395:Herumor, Lord of the Haradrim
+G:p:o
+I:120:50d100:40:160:0
+W:25:1:0:29000
+E:1:1:1:2:1:1
+O:0:40:60:0
+B:HIT:HURT:10d5
+B:HIT:EXP_80:10d5
+F:UNIQUE | MALE | MORTAL | BASEANGBAND | HAS_LITE | CAN_SPEAK |
+F:FORCE_MAXHP | SMART | IM_FIRE | IM_COLD | IM_POIS | IM_COLD |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | RES_NETH | RES_NEXU | RES_PLAS |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | EVIL | SPECIAL_GENE |
+S:1_IN_5 |
+S:BLIND | SCARE | CAUSE_3 | BA_NETH | BA_FIRE | BO_PLAS
+S:S_MONSTERS | S_DEMON
+D:One of the many Numenoreans who fell under the shadow of Sauron
+D:after Ar-Pharazon brought the Dark Lord to Numenor. With another
+D:named Fuinur, he settled among the Haradrim on Middle-Earth, and
+D:became a lord in the lands of Harad. From this, and his worship of
+D:Sauron's darkness, he must have taken taken his name: Herumor means
+D:'lord of the dark'.
+
+# New monster added by furiosity for the Theme module
+N:396:Fimbrethil
+G:#:s
+I:120:50d100:30:120:40
+W:42:3:6000:13500
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:CRUSH:HURT:12d12
+B:CRUSH:HURT:12d12
+B:CRUSH:HURT:12d12
+B:CRUSH:HURT:12d12
+F:FORCE_SLEEP | FORCE_MAXHP | UNIQUE | SUSCEP_FIRE |
+F:WILD_ONLY | WILD_WOOD | WILD_GRASS | WILD_MOUNTAIN | WILD_SHORE |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | MOVE_BODY | DROP_CORPSE |
+F:SMART | TAKE_ITEM | BASH_DOOR | KILL_WALL | NO_SLEEP |
+F:GOOD | NEUTRAL | NO_TARGET | BASEANGBAND | NO_CUT | FEMALE | CAN_FLY |
+F:DG_CURSE | WYRM_PROTECT |
+D:She is one of the Ent-wives who disappeared into the East a
+D:long time ago. She was the wife of Fangorn the Treebeard when
+D:the separation occurred, and she seems to have taken it quite
+D:badly. Her leaves are all withered to a dull gray and she is
+D:just standing there and crying. You should leave her alone.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# Basically Lugdush with extra power.
+N:399:Mauhur, the Uruk
+G:o:y
+I:110:66d10:20:90:20
+W:31:4:2500:850
+E:1:1:1:2:1:1
+O:10:90:0:0
+B:HIT:HURT:4d8
+B:HIT:HURT:4d8
+B:HIT:HURT:4d8
+B:HIT:HURT:4d8
+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: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:It is said that Melkor created trolls in mockery of the Ents.
+D:This giant creature has scabby black skin and powerful fists.
+
+# New monster added by furiosity for the Theme module
+N:402:Prince Imrahil the Proud
+G:p:w
+I:110:10d100:10:60:0
+W:35:1:1800:6000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT
+B:HIT:HURT
+B:WAIL:TERRIFY
+B:INSULT:*
+F:BASEANGBAND | SPECIAL_GENE | UNIQUE | MALE | CAN_SPEAK |
+F:OPEN_DOOR | BASH_DOOR | TAKE_ITEM | HAS_LITE | SUSCEP_POIS |
+F:ONLY_ITEM | DROP_3D2 | DROP_GOOD | DROP_RANDART | FORCE_MAXHP |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | RES_NETH | RES_WATE |
+S:1_IN_8 |
+S:S_UNDEAD | S_KIN | S_DEMON | S_DRAGON |
+D:The proud ruler of the gray-eyed Men of Dol Amroth, Imrahil
+D:had been aligned with the forces of good for most of his life.
+D:However, Sauron has managed to sway Imrahil to the Shadow by
+D:making him jealous of King Elessar and the splendour of Gondor.
+D:He now sits sullenly upon his throne in Dol Amroth, unable to
+D:resist Sauron's enchantments, and growing ever more bitter and
+D:vengeful against the Free People.
+
+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:It is said that Melkor created trolls in mockery of the Ents.
+D:This creature has an extremely tough hide, covered with warts.
+
+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 that feeds on raw magical energy.
+
+# New monster added for the Theme module
+# Adapted from UnAngband
+N:407:Umuiyan, Doorkeeper of Tevildo
+G:f:s
+I:120:48d100:100:200:2
+W:66:3:0:30000
+E:0:1:0:2:1:0
+O:30:60:0:10
+B:HIT:CONFUSE:12d12
+B:TOUCH:LOSE_CHR:2d12
+B:HIT:BLIND:10d5
+B:HIT:TERRIFY:15d1
+F:UNIQUE | MALE | CAN_SPEAK | FORCE_MAXHP |
+F:EVIL | OPEN_DOOR | BASH_DOOR | ANIMAL |
+F:INVISIBLE | NO_SLEEP | NO_FEAR | MORTAL |
+F:DROP_4D2 | DROP_GOOD | ONLY_ITEM |
+F:IM_FIRE | IM_COLD | IM_POIS | NO_CONF |
+S:1_IN_3
+S:TELE_AWAY | S_KIN |
+D:A giant cat, servant of Tevildo. His yellow eyes look
+D:upon you with a baleful stare.
+
+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.
+
+# New monster added for the Theme module
+# From UnAngband
+N:409:Oikeroi, Bodyguard of Tevildo
+G:f:r
+I:130:48d100:100:200:2
+W:66:3:0:30000
+E:0:1:0:2:1:0
+O:30:60:0:10
+B:HIT:CONFUSE:12d12
+B:TOUCH:LOSE_DEX:2d12
+B:HIT:BLIND:10d5
+B:HIT:PARALYZE:15d1
+F:UNIQUE | MALE | CAN_SPEAK | FORCE_MAXHP |
+F:EVIL | OPEN_DOOR | BASH_DOOR | ANIMAL |
+F:INVISIBLE | NO_STUN | NO_SLEEP | MORTAL |
+F:DROP_4D2 | DROP_GOOD | ONLY_ITEM |
+F:IM_FIRE | IM_COLD | IM_POIS | NO_CONF |
+S:1_IN_3 |
+S:TELE_TO | S_KIN |
+D:A fierce and warlike cat, serving as a bodyguard of Tevildo.
+D:This giant cat looks down upon you with disdain.
+
+N:410:Gwaihir the Windlord
+G:B:v
+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.
+
+# New monster added by furiosity for the Theme module
+# From UnAngband
+N:412:Lotho Sackville-Baggins, Betrayer of the Shire
+G:h:s
+I:120:6d10:16:8:5
+W:5:3:800:90
+E:1:1:1:2:1:1
+O:40:60:0:0
+B:HIT:HURT:1d8
+B:HIT:HURT:1d8
+F:BASEANGBAND | WILD_TOO | WILD_TOWN | MORTAL |
+F:UNIQUE | MALE | CAN_SPEAK | DROP_CORPSE |
+F:HAS_LITE | OPEN_DOOR | BASH_DOOR | EVIL |
+F:FORCE_MAXHP | FORCE_SLEEP |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+D:The 'Chief Shiriff' of Bag End. Using money obtained from Isengard
+D:and the sale of pipe-weed, he bought up much property in the Shire
+D:and supported the ruffians known as `The Chief's Men'. After
+D:imprisoning Will Whitfoot, the rightful Mayor of the Shire, Lotho
+D:took over and began industrializing and regimenting life in the Shire.
+
+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, faithless and treacherous.
+D:Along with his brother, he brought about the fall of the
+D:Sons of Feanor.
+
+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.
+
+# New monster added for the Theme module
+# Adapted from UnAngband
+N:417:Thranduil, King of the Wood Elves
+G:h:G
+I:120:11d100:20:80:10
+W:32:4:0:1000
+E:1:1:1:2:1:1
+O:0:40:30:30
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+B:HIT:HURT:3d8
+#Force him into Mirkwood:
+F:FORCE_DEPTH | ONLY_DEPTH |
+F:BASEANGBAND | WILD_TOO | WILD_WOOD | WILD_GRASS |
+F:UNIQUE | MALE | CAN_SPEAK | MORTAL | TAKE_ITEM |
+F:CAN_SWIM | OPEN_DOOR | BASH_DOOR | HAS_LITE |
+F:PET | NO_CONF | NO_SLEEP | GOOD |
+F:ONLY_ITEM | DROP_2D2| DROP_GREAT | NO_FEAR |
+F:IM_POIS | IM_COLD | RES_WATE | RES_TELE |
+S:1_IN_5
+S:BO_FIRE | BO_COLD | BLIND | CONF | HASTE | S_KIN |
+S:DARKNESS | S_MONSTER | S_ANIMALS | S_SPIDER |
+D:A strong wood-elf with a penchant for treasure. He is
+D:wise and powerful, and it is said that even the independent
+D:dark elves will rally to his side when he travels.
+
+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:k: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:A Petty-Dwarf, one of the last of his race. He betrayed Turin
+D:Turambar and gave him up to Morgoth's forces.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# From UnAngband
+N:422:Denethor, Steward of Gondor
+G:p:r
+I:120:55d100:40:160:10
+W:61:3:0:25000
+E:1:1:1:2:1:1
+O:20:50:10:5
+B:HIT:FIRE:9d12
+B:HIT:HURT:10d10
+F:UNIQUE | MALE | SMART | CAN_SPEAK | MORTAL |
+F:BASEANGBAND | FORCE_MAXHP | WEIRD_MIND | EVIL |
+F:HAS_LITE | ESCORT | ESCORTS | ONLY_ITEM |
+F:DROP_4D2 | DROP_3D2 | DROP_GOOD | TAKE_ITEM |
+F:OPEN_DOOR | BASH_DOOR | DROP_USEFUL |
+F:IM_ACID | IM_ELEC | IM_FIRE | IM_POIS | RES_PLAS |
+S:1_IN_2 |
+S:BA_FIRE | BRAIN_SMASH | CAUSE_2 | BO_FIRE | BO_MANA |
+S:BO_PLAS | SCARE | CONF | HASTE | HEAL | TPORT |
+S:TELE_TO | TELE_AWAY | TRAPS | FORGET | S_KIN |
+D:The proud former 'king' of Gondor, he has been driven mad
+D:by his contact with Sauron through the Palantir. You see
+D:the glint of a terrible fire in his eyes.
+
+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:Trolls were created by Melkor in mockery of the Ents. This one is
+D:a powerful troll form. Venom drips from its needle-like claws.
+
+# New monster added for the Theme module
+# From UnAngband
+
+N:425:Boromir, Son of Denethor
+G:p:W
+I:110:10d100:20:90:30
+W:34:5:0:1200
+E:1:1:1:2:1:1
+O:10:80:0:10
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:BASEANGBAND | WILD_TOO | WILD_WOOD |
+F:UNIQUE | MALE | CAN_SPEAK | SMART |
+F:FORCE_MAXHP | MORTAL | HAS_LITE | PET |
+F:OPEN_DOOR | BASH_DOOR | TAKE_ITEM |
+F:DROP_2D2 | DROP_GOOD | ONLY_ITEM |
+S:1_IN_10 |
+S:ARROW_2 | S_KIN |
+D:A noble son of the Steward of Gondor.
+
+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:W
+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 | BASEANGBAND
+S:1_IN_6
+S:SCARE
+D:Headless humanoid abomination, ever hungry for blood.
+
+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:R
+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:It has three heads - goat, dragon and gorgon - all attached to a
+D:lion's body.
+
+# New monster added for the Theme module
+# From UnAngband
+N:434:Faramir, Son of Denethor
+G:p:W
+I:110:10d100:20:90:30
+W:34:5:0:1200
+E:1:1:1:2:1:1
+O:20:50:10:5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+B:HIT:HURT:5d5
+F:BASEANGBAND | UNIQUE | MALE | CAN_SPEAK | PET |
+F:WILD_TOO | WILD_WOOD | FORCE_MAXHP | MORTAL |
+F:OPEN_DOOR | BASH_DOOR | TAKE_ITEM | HAS_LITE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD |
+S:1_IN_10 |
+S:ARROW_2 | S_KIN
+D:The noble son of the Steward of Gondor guards the depths
+D:of Ithilien against the Enemy.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# from UnAngband
+N:437:Harry Goatleaf, Gatekeeper of Bree
+G:p:w
+I:100:8d10:40:10:5
+W:5:4:1200:0
+E:1:1:1:2:1:1
+O:20:20:20:20
+B:GAZE:*
+B:WAIL:*
+B:INSULT:*
+B:CHARGE:HURT:1d2
+F:BASEANGBAND | WILD_TOO | WILD_TOWN | WILD_WOOD |
+F:UNIQUE | CAN_SPEAK | MALE | HAS_LITE | MORTAL |
+F:OPEN_DOOR | BASH_DOOR | TAKE_ITEM |
+F:ONLY_ITEM | DROP_1D2 | DROP_GOOD |
+D:The suspicious and gruff keeper of the West-gate of Bree. He betrayed his
+D:people by letting Saruman's thugs into the city, and so has been turned out.
+
+# New monster added for the Theme module
+# Description from UnAngband
+# Basically just an upgrade on the silent watcher.
+N:438:The Watcher of Cirith Ungol
+G:g:s
+I:140:60d200:100:300:0
+W:60:10:4000:80000
+E:3:0:3:3:2:0
+O:0:0:0:0
+B:GAZE:TERRIFY:5d8
+B:GAZE:PARALYZE:5d8
+B:GAZE:LOSE_STR:5d8
+B:GAZE:HALLU:5d8
+F:UNIQUE | FORCE_MAXHP | INVISIBLE | WEIRD_MIND |
+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 |
+F:BASEANGBAND | NO_CUT | REGENERATE |
+S:1_IN_3 |
+S:SHRIEK | S_SPIDER | S_HI_UNDEAD | MIND_BLAST | DRAIN_MANA
+D:It looks like a great figure seated upon a throne, with three joined
+D:bodies and three vulture-like heads. It seems to be carved out of huge
+D:blocks of stone, immovable, and yet strangely aware of your presence.
+D:The black stones of its eyes glitter with its dreadful will. Visible or
+D:invisible, none shall pass by this guardian.
+
+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.
+
+# New monster added for the Theme module
+# Adapted from UnAngband
+N:441:Tom Bombadil
+G:Y:b
+I:140:440d100:255:10000:0
+W:127:5:0:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:SHOW:*
+F:BASEANGBAND | WILD_ONLY | WILD_GRASS | WILD_WOOD | WILD_SHORE |
+F:UNIQUE | MALE | FORCE_MAXHP | GOOD | HAS_LITE | CAN_SPEAK |
+F:NO_THEFT | CAN_FLY | NO_CUT | DEATH_ORB |
+F:WYRM_PROTECT | DG_CURSE | GOOD | OPEN_DOOR | BASH_DOOR | SMART |
+F:RES_NETH | NEVER_BLOW | REFLECTING | WEIRD_MIND |
+F:REGENERATE | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_TELE | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE | PET |
+F:NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP | CAN_SWIM | AQUATIC |
+S:1_IN_5 |
+S:CONF | MIND_BLAST | BRAIN_SMASH |
+S:BA_MANA | CAUSE_4 | MISSILE | SCARE | SLOW | HOLD | BO_MANA |
+S:HASTE | HAND_DOOM | HEAL | BLINK | BR_MANA | S_MONSTERS |
+D:A mysterious figure in a blue coat, his face creased in laughter.
+D:Legend has it that he is older than Arda itself, and will aid
+D:the forces of good in any way he can.
+
+N:442:Wainrider
+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 riding a majestic war chariot; 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 | NEUTRAL | NO_TARGET |
+F:IM_COLD | IM_POIS | IM_FIRE | IM_ELEC | WILD_ONLY | 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 only 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.
+
+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:y
+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.
+
+# New monster added by furiosity for the Theme module
+# Based partly on the Icky queen.
+N:452:The Mewlip Queen
+G:i:v
+I:120:3d100:30:100:15
+W:30:5:10:4000
+E:0:0:0:0:0:0
+O:40:30:10:10
+B:CRAWL:POISON:5d7
+B:CRAWL:EAT_FOOD:5d7
+F:BASEANGBAND | EMPTY_MIND | SMART | IM_POIS |
+F:UNDEAD | RAND_50 | CAN_SWIM | EVIL | NO_FEAR |
+F:FORCE_MAXHP | UNIQUE | FEMALE | REGENERATE |
+F:ONLY_ITEM | DROP_2D2 | DROP_GREAT | ESCORT | ESCORTS |
+S:1_IN_5
+S:ANIM_DEAD | S_KIN
+D:A ruler of mewlips. She might not be the most powerful of
+D:them all, but she is the smartest, and has her minions
+D:at her beck and call.
+
+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:Snow-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:Melkor created trolls in mockery of Yavanna's Ents. This
+D:is a white troll with powerfully clawed hands.
+
+# New monster added for the Theme module
+# From UnAngband
+N:455:Fluithuin the Ogress, Consort of Morgoth
+G:O:b
+I:120:53d100:40:140:30
+W:69:3:3000:29000
+E:1:1:1:2:1:1
+O:5:85:0:5
+B:HIT:HURT:11d10
+B:HIT:HURT:11d10
+B:HIT:SHATTER:11d10
+F:UNIQUE | CAN_SPEAK | FEMALE |
+F:RAND_25 | EVIL | GIANT | FORCE_MAXHP |
+F:DROP_GOOD | ONLY_ITEM | DROP_4D2 |
+F:OPEN_DOOR | BASH_DOOR | MOVE_BODY | KILL_ITEM |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_POIS |
+S:1_IN_5
+S:BA_ACID | BA_WATE | BO_ICEE
+D:A powerful, fierce ogress, consort to Morgoth, rumoured to
+D:be the mother of Gothmog, high captain of Balrogs.
+
+# New monster added by furiosity for the Theme module
+# Based on UnAngband, though there Ulbandi is male and same as ToME's Lokkak
+
+N:456:Ulbandi the Ogress, Consort of Morgoth
+G:O:R
+I:110:15d100:20:100:50
+W:40:5:3000:15000
+E:1:1:1:2:1:1
+O:5:85:0:5
+B:HIT:HURT:8d8
+B:HIT:HURT:8d8
+B:HIT:POISON:8d8
+F:UNIQUE | FEMALE | CAN_SPEAK |
+F:EVIL | GIANT | FORCE_MAXHP |
+F:DROP_GOOD | DROP_2D2 | ONLY_ITEM |
+F:OPEN_DOOR | ESCORTS | IM_POIS |
+S:1_IN_9 |
+S:CONF | SCARE | HEAL | SHRIEK | S_KIN
+D:A powerful ogress, consort to Morgoth, rumoured to be
+D:the mother of Gothmog, high captain of Balrogs, also
+D:rumoured to be related to Lokkak, the Ogre Chieftain.
+
+# New monster added for the Theme module
+# Suggested by Atarlost in the t-o-m-e.net forum
+N:457:Naugladur, Lord of Nogrod
+G:k:G
+I:110:20d100:30:200:200
+W:70:1:0:12000
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+B:HIT:HURT:4d6
+F:UNIQUE | MALE | ESCORT | ESCORTS | CAN_SPEAK |
+F:FORCE_MAXHP | SMART | SPECIAL_GENE
+F:ONLY_ITEM | DROP_2D2 | DROP_GOOD | DROP_GREAT
+F:OPEN_DOOR | BASH_DOOR
+F:EVIL | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS
+D:The slayer of Thingol, and the thief of the Nauglamir. He leads the dark
+D:dwarves who escaped from Nogrod.
+
+N:458: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: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 that legends are made of. 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 | SUSCEP_FIRE |
+F:EVIL | DRAGON | IM_COLD | BASEANGBAND | ATTR_MULTI
+S:1_IN_10 |
+S:SCARE |
+S:BR_COLD
+D:It has a form that legends are made of. 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 that legends are made of. 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 that legends are made of. Its still-tender scales are a
+D:rich bronze hue, and its shape masks its true form.
+
+# New monster added for the Theme module
+# From the Annals of Ea module (by Feanor)
+N:463:Androg the Outlaw
+G:p:b
+I:110:72d10:20:95:20
+W:21:3:2400:600
+E:1:1:1:2:1:1
+O:30:8: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 | IM_FIRE | IM_COLD | IM_POIS |
+F:MORTAL | BASEANGBAND | HAS_LITE |
+D:A member of a band of outlaws, he was spared his life when
+D:Neithan (that is, Turin) killed Forweg, the previous leader.
+
+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 pure 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 magic.
+
+# New monster added for the Theme module
+# From Annals of Ea module (by Feanor)
+
+N:466:Amlach, son of Imlach
+G:p:s
+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 a trustworthy man... or is he? Morgoth seem's sending spies in
+D:disguise, and perhaps this is one of them.
+
+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 | CAN_SPEAK |
+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:v
+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. It appeared when a
+D:humanoid creature died a violent death in a graveyard. It remembers
+D:its past life and seeks revenge.
+
+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 out 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.
+
+# New monster added for the Theme module
+# From T-Plus module by Ingeborg S. Norden
+
+N:476:Radagast the Brown
+G:p:u
+I:120:35d101:101:100:0
+W:55:7:1600:21000
+E:1:1:1:2:1:1
+O:0:0:100:0
+B:HIT:HURT:6d4
+B:HIT:HURT:6d4
+F:UNIQUE | MALE | CAN_SPEAK | PET | DROP_CORPSE |
+F:FORCE_SLEEP | FORCE_MAXHP | REFLECTING | RES_TELE | CAN_FLY |
+F:ONLY_ITEM | DROP_2D2 | DROP_3D2 | DROP_4D2 | DROP_GOOD |
+F:SMART | OPEN_DOOR | BASH_DOOR |
+F:GOOD | IM_COLD | IM_FIRE |
+F:IM_ELEC | IM_POIS | RES_WATE | BASEANGBAND | HAS_LITE
+S:1_IN_6 |
+S:HEAL | HASTE | BLINK | TPORT | BLIND | CONF | SCARE |
+S:CAUSE_2 | FORGET | TRAPS |
+S:BA_ELEC | BA_COLD | BA_WATE | BO_WATE | BO_ICEE | BO_ELEC |
+S:S_ANIMAL |
+D:Sometimes belittled as a bird-loving fool, Radagast is weaker
+D:mentally and magically than his fellows among the Istari.
+D:Nonetheless, his knowledge of woodcraft and command over the
+D:sky-borne elements makes Radagast a force to be reckoned with.
+
+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:An apparition from beyond. It does not seem friendly.
+
+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. She normally guards the pass through Cirith Ungol, but
+D:occasionally goes out foraging for food to 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 reach you from a distance.
+
+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:Easterling
+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 | FRIENDS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:One of the Swarthy Men, allied with the Dark since time
+D:immemorable.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:489:Bjorn the Warper
+G:q:D
+I:115:22d10:6:92:0
+W:31:10:3040:2000
+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:JOKEANGBAND | WILD_TOO | WILD_GRASS | WILD_WOOD | WILD_SHORE |
+F:UNIQUE | MALE | FORCE_MAXHP | HAS_LITE | CAN_SPEAK |
+F:ANIMAL | MOVE_BODY | GOOD | SMART | BASH_DOOR | ONLY_ITEM |
+F:DROP_2D2 | DROP_GOOD | DROP_GREAT | FORCE_SLEEP | NO_CUT |
+F:NO_SLEEP | IM_ACID | IM_ELEC | IM_COLD | SUSCEP_POIS | IM_FIRE |
+S:1_IN_5 |
+S:BLINK | TELE_TO | TELE_AWAY | TPORT
+D:He is a descendant of Beorn to the 3.79th degree (his mother was
+D:Swedish, hence his name). He has blue eyes, bleached blond hair,
+D:and a tanned complexion. He signed up at the Adventurer's Guild
+D:because he thought the advertised position was "Surfer". He thinks
+D:you're the one responsible for tricking him.
+
+# New monster added by furiosity for the Theme module
+N:490:Vaire, the Weaver
+G:A:W
+I:200:440d100:255:1000:0
+W:0:50:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:BASEANGBAND | WILD_ONLY | FORCE_DEPTH | WILD_TOWN |
+F:WILD_GRASS | WILD_MOUNTAIN | WILD_WOOD | WILD_SHORE |
+F:UNIQUE | FEMALE | FORCE_MAXHP | HAS_LITE | POWERFUL |
+F:NO_THEFT | CAN_FLY | NO_CUT | DEATH_ORB | CAN_SPEAK |
+F:WYRM_PROTECT | DG_CURSE | GOOD | SMART | NEUTRAL | NO_TARGET | NEVER_MOVE |
+F:RES_NETH | NEVER_BLOW | REFLECTING | WEIRD_MIND | NO_DEATH |
+F:REGENERATE | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_TELE | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE |
+F:NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP | CAN_SWIM | AQUATIC |
+S:1_IN_1
+S:HAND_DOOM | BR_MANA | HAND_DOOM | BR_MANA | HAND_DOOM
+D:A Queen of the Valar, the spouse of Mandos, who weaves the
+D:tales of the history of Arda.
+
+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:An unwholesome breed between a human and a troll. It is
+D:somewhat smarter than regular trolls.
+
+# New monster added by furiosity for the Theme module
+N:492:Irmo of Lorien
+G:A:g
+I:200:440d100:255:1000:0
+W:0:50:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:BASEANGBAND | WILD_ONLY | FORCE_DEPTH | WILD_TOWN |
+F:WILD_GRASS | WILD_MOUNTAIN | WILD_WOOD | WILD_SHORE |
+F:UNIQUE | MALE | FORCE_MAXHP | HAS_LITE | POWERFUL |
+F:NO_THEFT | CAN_FLY | NO_CUT | DEATH_ORB | CAN_SPEAK |
+F:WYRM_PROTECT | DG_CURSE | GOOD | SMART | NEUTRAL | NO_TARGET | NEVER_MOVE |
+F:RES_NETH | NEVER_BLOW | REFLECTING | WEIRD_MIND | NO_DEATH |
+F:REGENERATE | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_TELE | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE |
+F:NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP | CAN_SWIM | AQUATIC |
+S:1_IN_1
+S:HAND_DOOM | BR_MANA | HAND_DOOM | BR_MANA | HAND_DOOM
+D:A Vala more commonly called Lorien, from the gardens of
+D:Lorien in Valinor.
+
+
+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:Trolls were created by Morgoth in mockery of the Ents.
+D:He is a vicious monster, feared for his ferocity.
+
+# New monster added by furiosity for the Theme module
+N:497:Este, the Gentle
+G:A:B
+I:200:440d100:255:1000:0
+W:0:50:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:BASEANGBAND | WILD_ONLY | FORCE_DEPTH | WILD_TOWN |
+F:WILD_GRASS | WILD_MOUNTAIN | WILD_WOOD | WILD_SHORE |
+F:UNIQUE | FEMALE | FORCE_MAXHP | HAS_LITE | POWERFUL |
+F:NO_THEFT | CAN_FLY | NO_CUT | DEATH_ORB | CAN_SPEAK |
+F:WYRM_PROTECT | DG_CURSE | GOOD | SMART | NEUTRAL | NO_TARGET | NEVER_MOVE |
+F:RES_NETH | NEVER_BLOW | REFLECTING | WEIRD_MIND | NO_DEATH |
+F:REGENERATE | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_TELE | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE |
+F:NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP | CAN_SWIM | AQUATIC |
+S:1_IN_1
+S:HAND_DOOM | BR_MANA | HAND_DOOM | BR_MANA | HAND_DOOM
+D:A lady of the Valar, the spouse of Irmo.
+
+# New monster added by furiosity for the Theme module
+#N:498:Nienna, the Sorrowful
+#G:A:D
+#I:200:440d100:255:1000:0
+#W:0:50:0:0
+#E:0:0:0:0:0:0
+#O:0:0:0:0
+#F:BASEANGBAND | WILD_ONLY | FORCE_DEPTH | WILD_TOWN |
+#F:WILD_GRASS | WILD_MOUNTAIN | WILD_WOOD | WILD_SHORE |
+#F:UNIQUE | FEMALE | FORCE_MAXHP | HAS_LITE | POWERFUL |
+#F:NO_THEFT | CAN_FLY | NO_CUT | DEATH_ORB | CAN_SPEAK |
+#F:WYRM_PROTECT | DG_CURSE | GOOD | SMART | NEUTRAL | NO_TARGET | NEVER_MOVE |
+#F:RES_NETH | NEVER_BLOW | REFLECTING | WEIRD_MIND | NO_DEATH |
+#F:REGENERATE | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+#F:RES_TELE | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE |
+#F:NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP | CAN_SWIM | AQUATIC |
+#S:1_IN_1
+#S:HAND_DOOM | BR_MANA | HAND_DOOM | BR_MANA | HAND_DOOM
+#D:A Queen of the Valar, the sister of Namo and Irmo, who dwells
+#D:alone on the western borders of the World. Nienna ranks as one of
+#D:the eight Aratar, the most powerful of the Valar. Grief and mourning
+#D:are Nienna's province; she weeps for the suffering of Arda. She teaches
+#D:pity and endurance; she often goes to the halls of Mandos to comfort
+#D:and counsel those in the Halls of Waiting. The Maia Olorin was her
+#D:student and learned much from her.
+
+N:499:Barrow wight
+G:W:B
+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:An evil spirit sent to dwell in the Barrow-downs by the Witch-king
+D:of Angmar during his wars with Arnor.
+
+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. You hear strange humming and
+D:rustling sounds as it approaches.
+
+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 balance,
+D:and despises your efforts to upset it.
+
+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 | JOKEANGBAND
+D:He who laughs at Groo's brains will find there is nothing to laugh
+D:about... erm, nobody laughs at Groo and lives.
+
+# New monster added by furiosity for the Theme module
+N:506:Nessa the Lithe
+G:A:b
+I:200:440d100:255:1000:0
+W:0:50:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:BASEANGBAND | WILD_ONLY | FORCE_DEPTH | WILD_TOWN |
+F:WILD_GRASS | WILD_MOUNTAIN | WILD_WOOD | WILD_SHORE |
+F:UNIQUE | FEMALE | FORCE_MAXHP | HAS_LITE | POWERFUL |
+F:NO_THEFT | CAN_FLY | NO_CUT | DEATH_ORB | CAN_SPEAK |
+F:WYRM_PROTECT | DG_CURSE | GOOD | SMART | NEUTRAL | NO_TARGET | NEVER_MOVE |
+F:RES_NETH | NEVER_BLOW | REFLECTING | WEIRD_MIND | NO_DEATH |
+F:REGENERATE | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_TELE | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE |
+F:NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP | CAN_SWIM | AQUATIC |
+S:1_IN_1
+S:HAND_DOOM | BR_MANA | HAND_DOOM | BR_MANA | HAND_DOOM
+D:The spouse of Tulkas and sister to Orome, who delights in dancing
+D:on the green lawns of Valimar.
+
+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:Trolls are said to have been created by Melkor in mockery of
+D:the Ents. He 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.
+
+# New monster added by furiosity for the Theme module
+#N:511:Orome, the Huntsman of the Valar
+#G:A:G
+#I:200:440d100:255:1000:0
+#W:0:50:0:0
+#E:0:0:0:0:0:0
+#O:0:0:0:0
+#F:BASEANGBAND | WILD_ONLY | FORCE_DEPTH | WILD_TOWN |
+#F:WILD_GRASS | WILD_MOUNTAIN | WILD_WOOD | WILD_SHORE |
+#F:UNIQUE | MALE | FORCE_MAXHP | HAS_LITE | POWERFUL |
+#F:NO_THEFT | CAN_FLY | NO_CUT | DEATH_ORB | CAN_SPEAK |
+#F:WYRM_PROTECT | DG_CURSE | GOOD | SMART | NEUTRAL | NO_TARGET | NEVER_MOVE |
+#F:RES_NETH | NEVER_BLOW | REFLECTING | WEIRD_MIND | NO_DEATH |
+#F:REGENERATE | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+#F:RES_TELE | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE |
+#F:NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP | CAN_SWIM | AQUATIC |
+#S:1_IN_1
+#S:HAND_DOOM | BR_MANA | HAND_DOOM | BR_MANA | HAND_DOOM
+#D:The brother of Nessa and one of the eight Aratar. In ancient times,
+#D:he rode often in the forests of Middle-earth, and it was he who first
+#D:discovered the Eldar at Cuivienen.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:515:Vana, the Ever-young
+G:A:y
+I:200:440d100:255:1000:0
+W:0:50:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:BASEANGBAND | WILD_ONLY | FORCE_DEPTH | WILD_TOWN |
+F:WILD_GRASS | WILD_MOUNTAIN | WILD_WOOD | WILD_SHORE |
+F:UNIQUE | FEMALE | FORCE_MAXHP | HAS_LITE | POWERFUL |
+F:NO_THEFT | CAN_FLY | NO_CUT | DEATH_ORB | CAN_SPEAK |
+F:WYRM_PROTECT | DG_CURSE | GOOD | SMART | NEUTRAL | NO_TARGET | NEVER_MOVE |
+F:RES_NETH | NEVER_BLOW | REFLECTING | WEIRD_MIND | NO_DEATH |
+F:REGENERATE | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_TELE | RES_WATE | RES_PLAS | RES_NEXU | RES_DISE |
+F:NO_FEAR | NO_STUN | NO_CONF | NO_SLEEP | CAN_SWIM | AQUATIC |
+S:1_IN_1
+S:HAND_DOOM | BR_MANA | HAND_DOOM | BR_MANA | HAND_DOOM
+D:The sister of Yavanna and spouse of Orome.
+D:Flowers open and birds sing at her passing.
+
+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 vast 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:It does not look dangerous, yet you don't want to get
+D:any closer.
+
+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:This ancient creature 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.
+
+# New monster added for the Theme module
+# From T-Plus module by Ingeborg S. Norden
+
+N:523:Ingeborg, the Runemistress
+G:p:y
+I:155:180d75:111:175:0
+W:127:7:1600:444444
+E:2:1:2:6:1:1
+O:20:20:20:20
+B:GAZE:EAT_GOLD:10d10
+B:CRUSH:HURT:10d10
+B:TOUCH:UN_BONUS
+B:TOUCH:UN_POWER
+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 | INVISIBLE |
+F:SMART | MOVE_BODY | TAKE_ITEM | POWERFUL | RES_TELE |
+F:REGENERATE | CAN_FLY | CAN_SWIM | DG_CURSE | WYRM_PROTECT |
+F:GOOD | IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_PLAS | RES_DISE | RES_NETH | RES_NEXU | RES_WATE |
+F:NO_CONF | NO_FEAR | NO_SLEEP | NO_STUN | RES_TELE
+F:MORTAL | PET | JOKEANGBAND | HAS_LITE
+S:1_IN_2 |
+S:S_BUG | S_HI_DRAGON | S_KIN | S_RNG | S_UNIQUE |
+S:TELE_AWAY | TELE_LEVEL | TELE_TO | TPORT |
+S:HAND_DOOM | HEAL |
+S:BO_ICEE | BO_MANA | BO_PLAS |
+S:BA_COLD | BA_MANA | BA_WATE | ROCKET |
+S:BR_LITE | BR_DISE | BR_INER | BR_MANA | BR_NEXU | BR_SOUN | BR_TIME |
+S:BRAIN_SMASH | FORGET |
+D:A heavy-set, fair-haired figure who cruises through the dungeon in
+D:a strangely made magical cart of some kind. Wherever she travels,
+D:monsters and magical devices appear which no one on Arda has seen
+D:before. Rumor has it that she's planning to foil the Dark Lord
+D:by reshaping the entire world, and traveling through time to ensure
+D:that NO Rings of Power ever get forged.
+
+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:Sarnrog
+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 minor demonic servant of evil, it looks like it is made of stone.
+
+N:529:Malicious leprawn
+G:l: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 gnome has a fiendish gleam in his 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.
+
+# New monster added by furiosity for the Theme module
+# Based on actual character's stats
+
+N:531:Lindal Lossehelin
+G:h:R
+I:123:8d80:4:105:0
+W:49:3:970:2000
+E:1:1:1:2:1:1
+O:25:25:25:25
+B:HIT:HURT:10d8
+B:HIT:HURT:10d8
+# Wearing a Greater Mimic for a symbiote
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+F:JOKEANGBAND | WILD_TOO | WILD_WOOD | WILD_GRASS |
+F:UNIQUE | MALE | GOOD | DROP_CORPSE | SMART | PET |
+F:OPEN_DOOR | BASH_DOOR | MORTAL | HAS_LITE | CAN_SWIM |
+F:CAN_SPEAK | DROP_4D2 | DROP_GREAT | FORCE_MAXHP |
+F:NO_SLEEP | NO_CONF | NO_CUT | RES_NEXU | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_NETH | RES_DISE | AURA_FIRE | CAN_FLY |
+#Spells chez archery
+S:1_IN_3
+S:ARROW_1 | ARROW_2 | ARROW_3 | ARROW_4 |
+#Spells chez a Greater Mimic named Glorp
+S:CAUSE_3 | BO_ACID | BO_ELEC | BO_FIRE | BO_COLD |
+S:SCARE | BLIND | CONF | FORGET | S_MONSTER
+D:An elf cloaked in green wielding a crossbow. He has sky blue
+D:eyes, straight black hair, and a fair face. He is the only
+D:child of a Telerin mage, and serves in King Thranduil's army.
+D:He has been sent on a quest by a strange wizard, and his
+D:aims are not so different from your own.
+
+N:532:Variag
+G:p:r
+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 from Khand, 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:Death incarnate, its hideous black body seems to struggle
+D:against reality as the universe itself struggles to banish it.
+
+# New monster added by furiosity for the Theme module
+# Based on character dump current as of May 7, 2004
+# http://www.killerbunnies.org/angband/ladder/zizzo/zizzo_15.html
+N:535:Zizzo, Last of the Yeeks
+G:y:G
+I:110:5d10:3:35:0
+W:26:3:240:2000
+E:1:1:1:2:1:1
+O:50:0:50:0
+# Poor at combat:
+B:BITE:HURT:1d1
+B:INSULT:*
+B:WAIL:*
+F:JOKEANGBAND | WILD_TOO | WILD_WOOD | WILD_GRASS |
+F:UNIQUE | MALE | GOOD | DROP_CORPSE | SMART | ANIMAL |
+F:OPEN_DOOR | BASH_DOOR | MORTAL | HAS_LITE |
+F:CAN_SPEAK | DROP_4D2 | DROP_GREAT | FORCE_MAXHP |
+F:AI_ANNOY | WEIRD_MIND | TAKE_ITEM | IM_POIS |
+F:IM_ACID | IM_ELEC | IM_FIRE | IM_COLD | POWERFUL |
+S:1_IN_1 |
+# Good at sorcery:
+S:BO_MANA | BO_MANA | BR_ELEC | BR_ACID | BO_MANA |
+S:BR_LITE | BR_WALL | BR_FIRE | BR_COLD | BR_POIS |
+D:He is one of five children of a brown Yeek. He is a credit to the
+D:family. He has dark brown eyes, straight black hair, and a dark
+D:complexion. He has an annoying habit of making strange "zizz" noises,
+D:hence his nickname. He is a sorcerer of some renown, and he thinks
+D:you have just the thing he needs to make it to the next level.
+
+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: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:They say Melkor created trolls in mockery of the Ents. This is a
+D: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 tradition of archery.
+
+N:540:Gravity 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 | 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 round these monsters.
+
+N:541:Acidic cytoplasm
+G:j:B
+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.
+
+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 strikes 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.
+
+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 and 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 that legends are made of. 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:Cold-drake
+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 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:Trolls are abominations created by Melkor in mockery of the Ents.
+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.
+
+# New monster added by furiosity for the Theme module
+# Based on character dump as of May 7, 2004
+# http://angband.oook.cz/ladder-show.php?id=2884
+N:552:Erianyth, the Sorceress
+G:A:r
+I:120:40d100:0:130:0
+W:50:7:1830:2000
+E:1:1:1:2:1:1
+O:25:25:25:25
+B:HIT:HURT:5d12
+B:HIT:HURT:5d12
+B:HIT:SHATTER:5d12
+F:JOKEANGBAND | WILD_TOO | WILD_MOUNTAIN | WILD_WASTE |
+F:UNIQUE | FEMALE | GOOD | DROP_CORPSE | SMART |
+F:OPEN_DOOR | BASH_DOOR | HAS_LITE | CAN_SWIM |
+F:CAN_SPEAK | DROP_4D2 | DROP_GREAT | FORCE_MAXHP |
+F:NO_SLEEP | NO_CONF | NO_CUT | RES_NEXU | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_NETH | RES_DISE | CAN_FLY | NO_FEAR | PASS_WALL |
+F:REFLECTING | REGENERATE | WEIRD_MIND |
+F:KILL_BODY | RES_TELE | INVISIBLE | NO_STUN |
+S:1_IN_4 |
+# Spells chez Thaumaturgy, dead Morgy, and her being Maia:
+S:BR_POIS | ROCKET | SHRIEK | BR_WALL | BR_INER |
+# Spells chez Master Q named 'Fear' (though not all, she *is* Maia):
+S:BLINK | TELE_TO | S_MONSTERS | S_ANIMALS |
+D:A mighty Maia of legend, associated with Vana. In the past, she
+D:dwelt on Middle-earth in the form of a beneficent but unseen force.
+D:She looks like she is a wall. It is rumoured that she has once
+D:destroyed Morgoth Bauglir and is out to banish him from the Void as
+D:well. It is a matter of principle for this sorceress, and you are
+D:blocking her path to justice.
+
+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 that legends are made of. Beautiful scales of shimmering
+D:and magical colours cover it.
+
+# New monster added by furiosity for the Theme module
+# Based on "Day in the life Of..." forum thread:
+# http://www.t-o-m-e.net/forum/viewtopic.php?t=4377
+N:557:Karrazix the Brave
+G:D:u
+I:120:22d100:5:100:0
+W:29:3:20000:2000
+E:0:1:0:6:1:0
+O:50:50:0:0
+B:CLAW:HURT:10d8
+B:CLAW:HURT:10d8
+B:CLAW:HURT:10d8
+F:JOKEANGBAND | WILD_TOO | WILD_WOOD | WILD_MOUNTAIN |
+F:UNIQUE | FEMALE | CAN_SPEAK | GOOD | DROP_CORPSE |
+F:SMART | PET | OPEN_DOOR | BASH_DOOR | HAS_LITE |
+F:CAN_FLY | DROP_4D2 | DROP_GREAT | FORCE_MAXHP | DRAGON |
+F:NO_SLEEP | NO_CONF | NO_FEAR | MOVE_BODY | POWERFUL |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+S:1_IN_4 |
+S:BR_FIRE | BR_COLD | BR_ELEC | BR_ACID | BR_POIS |
+D:She is a small, but fierce dragon - so fierce that in her fury she
+D:appears to be one of the Great Worms. She has turned away from
+D:Morgoth and worships Tulkas Astaldo. She intensely dislikes vampires
+D:in general and Thuringwethil in particular. She has red eyes and
+D:glittering scales.
+
+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 that legends are made of. Its still-tender scales are a
+D:tarnished gold hue, and light is reflected from its form.
+
+N:560:Blue drake
+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:Green drake
+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:Bronze drake
+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 that legends are made of. Its still-tender scales are a
+D:deepest red hue. Heat radiates from its form.
+
+# New monster added by furiosity for the Theme module
+# Based on character dump as of May 10, 2004
+# http://angband.oook.cz/ladder-show.php?id=2933
+N:564:Sir Physt
+G:V:y
+I:140:20d100:0:170:0
+W:45:3:1940:2000
+E:1:1:1:2:1:1
+O:25:25:25:25
+B:CHARGE:HURT:10d8
+# Wearing an Ultimate Mimic for a symbiote
+B:BITE:POISON:4d4
+B:BUTT:CONFUSE:4d4
+B:SPIT:BLIND:4d4
+F:JOKEANGBAND | WILD_TOO | WILD_WOOD | WILD_GRASS |
+F:WILD_WASTE | WILD_MOUNTAIN | WILD_SHORE |
+F:UNIQUE | MALE | GOOD | SMART | UNDEAD | REFLECTING |
+F:OPEN_DOOR | BASH_DOOR | HAS_LITE | CAN_SWIM |
+F:CAN_SPEAK | DROP_4D2 | DROP_GREAT | FORCE_MAXHP |
+F:NO_SLEEP | NO_CONF | NO_CUT | RES_NEXU | MOVE_BODY |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_NETH | RES_DISE | AURA_FIRE | CAN_FLY | REGENERATE |
+F:NO_STUN | KILL_BODY | NO_FEAR | WEIRD_MIND |
+#Spells chez Mindcraft
+S:1_IN_3
+S:BRAIN_SMASH | FORGET | MIND_BLAST |
+#Spells chez an Ultimate Mimic:
+S:CAUSE_3 | BA_POIS | SCARE | BLIND | CONF | S_MONSTER
+D:He is the head of the unholy holy order of Nosferatu. His nephew
+D:Sir Otilc played with swords and died as a result. Sir Physt is
+D:out to rectify the situation with his fists and his mind. Tulkas
+D:Astaldo has given this former Dunadan his blessings to go and
+D:destroy Morgoth Bauglir, Lord of Darkness. He believes it is his
+D:job and no one else's, and he approaches you with a menacing stare.
+
+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:Adanrog
+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. Adanroeg are
+D:humans corrupted by the evil forces of Morgoth into demonic slaves
+D:to evil.
+
+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:Rawrog
+G:u:B
+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:It resembles a lion standing on two feet. In fact, Rawroeg are
+D:lions corrupted by Morgoth into demonic servants of evil.
+
+# New monster added by furiosity for the Theme module
+# Based on character dump as of May 7, 2004
+# http://angband.oook.cz/ladder-show.php?id=2838
+N:569:Nick LeYeek, Second Last of the Yeeks
+G:y:o
+I:130:35d100:127:100:0
+W:50:6:650:2000
+E:1:1:1:2:1:1
+O:25:25:25:25
+# Not good at melee:
+B:HIT:HURT:1d1
+B:INSULT:*
+B:SHOW:*
+F:JOKEANGBAND | WILD_TOO | WILD_WOOD | WILD_GRASS |
+F:UNIQUE | MALE | GOOD | DROP_CORPSE | SMART | ANIMAL |
+F:OPEN_DOOR | BASH_DOOR | MORTAL | HAS_LITE | CAN_SWIM |
+F:CAN_SPEAK | DROP_4D2 | DROP_GREAT | FORCE_MAXHP |
+F:PASS_WALL | NO_SLEEP | REGENERATE | REFLECTING |
+F:AURA_FIRE | IM_ACID | IM_ELEC | IM_FIRE | IM_COLD |
+F:IM_POIS | NO_CONF | RES_PLAS | RES_DISE | RES_NEXU |
+F:RES_NETH | RES_WATE |
+S:1_IN_5
+# Spells chez symbiote skull druj:
+S:BA_WATE | MIND_BLAST | BRAIN_SMASH | CAUSE_2 |
+S:BO_PLAS | SLOW | TRAPS | S_UNDEAD | BO_NETH |
+# Spells chez thaumaturgy and having killed Morgy:
+S:BR_SOUN | BR_MANA | BR_NUKE | BR_DISI | ROCKET |
+D:A yeek bard more famous for his buff physique and marriage to the beautiful
+D:but dumb Jessica Yeekson than his music. The dark forces of Morgoth killed
+D:his friend Justin TimberYeek and kidnapped poor Jessica Yeekson, and Nick
+D:vowed revenge. He is the only yeek that could be called powerful. It is rumoured
+D:that he had been successful in defeating Morgoth in a previous incarnation, and
+D:now he is back to finish the job for good. You are all that he perceives to
+D:stand in his way.
+
+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.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# Based on oook character dump as of May 7, 2004
+# http://angband.oook.cz/ladder-show.php?id=2616
+N:572:Slappy, Abbess of Pain
+G:h:U
+I:120:39d10:4:128:0
+W:33:3:780:2000
+E:1:1:1:2:1:1
+O:34:0:33:33
+B:HIT:HURT:10d8
+B:PUNCH:HURT:10d8
+B:KICK:HURT:10d8
+B:CHARGE:HURT:10d8
+F:JOKEANGBAND | WILD_TOO | WILD_WOOD | WILD_GRASS|
+F:UNIQUE | FEMALE | GOOD | DROP_CORPSE | SMART |
+F:CAN_SPEAK | DROP_4D2 | DROP_GREAT | FORCE_MAXHP |
+F:OPEN_DOOR | BASH_DOOR | MORTAL | HAS_LITE |
+F:NO_SLEEP | NO_CONF | CAN_SWIM | REGENERATE |
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:RES_NEXU | MOVE_BODY | KILL_WALL | CAN_FLY |
+#Well, she has a boomerang:
+S:1_IN_4 |
+S:ARROW_2
+D:A lithe female wood-elf wearing no armour - she is
+D:a monk of Manwe Sulimo, one of the most feared of her order.
+D:She is one of several children of an Avarin Prince. She has
+D:light grey eyes, straight black hair, and a fair face. She
+D:has unfinished business with some Basilisks and then she's
+D:going after Morgoth's crown. You are in her way.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:574:Snow tiger
+G:f:w
+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 | IM_COLD |
+F:MORTAL | BASEANGBAND
+D:A large feline that is well-adapted to extremely cold
+D:environments.
+
+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:v
+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 | JOKEANGBAND
+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:BASEANGBAND | NO_CUT
+S:1_IN_7
+S:SLOW | BR_TIME |
+D:You have not seen it yet.
+
+N:580: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: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 she thinks 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: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: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 slurps 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:Fire-drake
+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:Golden drake
+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 spectra of colour.
+
+N:592:Black drake
+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:Multi-hued drake
+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: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:595:Orfax, son of Boldor
+G:y:R
+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:The son of the yeek King, he has some power, but he's still a yeek.
+
+N:596:Boldor, King of the Yeeks
+G:y:r
+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:597:Black Numenorean
+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. Ever a faithful servant of Sauron, he'll stop at nothing to destroy
+D:you.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:605:Giant tree ant
+G:a:G
+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:POISON:3d12
+B:BITE:POISON:3d12
+F:FORCE_MAXHP | KILL_BODY | FRIENDS | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR |
+F:ANIMAL | IM_POIS |
+F:MORTAL | BASEANGBAND
+D:A giant ant covered in shaggy fur. Its powerful jaws drip venom.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:608:Giant yellow ant
+G:a:y
+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:BLIND:3d12
+B:BITE:BLIND:3d12
+F:FORCE_MAXHP | KILL_BODY | FRIENDS | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR | ANIMAL |
+F:MORTAL | BASEANGBAND
+D:A giant ant covered in shaggy fur. Its powerful jaws click with blinding speed.
+
+# New monster added by furiosity for the Theme module
+N:609:Giant green ant
+G:a:g
+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:CONFUSE:3d12
+B:BITE:CONFUSE:3d12
+F:FORCE_MAXHP | KILL_BODY | FRIENDS | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR | ANIMAL | NO_CONF |
+F:MORTAL | BASEANGBAND
+D:A giant ant covered in shaggy fur. Its powerful jaws make puzzling
+D:noises as they snap shut.
+
+# New monster added by furiosity for the Theme module
+N:610:Aquatic ant
+G:a:B
+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:HURT:3d12
+B:BITE:HURT:3d12
+F:FORCE_MAXHP | KILL_BODY | FRIENDS | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR | CAN_SWIM |
+F:ANIMAL | RES_WATE | AQUATIC |
+F:MORTAL | BASEANGBAND
+S:1_IN_4
+S:BA_WATE
+D:A strange antlike creature, animated by a powerful wizard you
+D:cannot see.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:616:Termite
+G:a:U
+I:110:2d10:4:9:40
+W:20:1:1:100
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d6
+B:BITE:HURT:1d6
+F:FORCE_MAXHP | KILL_BODY | FRIENDS | DROP_SKELETON
+F:WEIRD_MIND | BASH_DOOR | SUSCEP_FIRE |
+F:ANIMAL | AI_ANNOY | KILL_TREES |
+F:MORTAL | BASEANGBAND
+S:MULTIPLY
+D:An extremely annoying antlike creature. Its bulbous eyes seem
+D:almost covetous as they focus on your wooden paraphernalia.
+
+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 |
+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.
+
+# New monster added by furiosity for the Theme module
+N:619:Giant snow bat
+G:b:w
+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:COLD:1d2
+B:CLAW:COLD:1d2
+F:RAND_50 | CAN_FLY | WILD_TOO | WILD_MOUNTAIN | WILD_WOOD |
+F:FORCE_SLEEP | ANIMAL | DROP_CORPSE | AI_ANNOY | IM_COLD |
+F:SUSCEP_FIRE | MORTAL | BASEANGBAND
+D:A giant bat adapted to extremely cold temperatures.
+
+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:Melkor created trolls in mockery of the Ents. A massive troll of
+D:huge strength, extremely stupid and extremely 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:Melkor created trolls in mockery of the Ents. A massive two-headed troll,
+D:larger and stronger than many men together.
+
+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 hatred for 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 | JOKEANGBAND | NO_CUT
+S:1_IN_6 |
+S:BR_CHAO | BR_NEXU | BR_NUKE
+D:A swirling spiral of mist, constantly changing its appearance.
+
+# New monster added by furiosity for the Theme module
+N:626:Giant grey bat
+G:b:s
+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:PARALYZE: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 whose very presence makes you feel weary.
+
+# New monster added by furiosity for the Theme module
+N:627:Giant silver bat
+G:b:W
+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:LOSE_DEX:1d3
+B:CLAW:HALLU: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, its wings shimmering a pale mesmerizing silver.
+
+# New monster added by furiosity for the Theme module
+N:628:Giant yellow bat
+G:b:y
+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:LOSE_CHR:1d3
+B:CLAW:EAT_LITE:1d2
+B:CLAW:EAT_LITE: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 which seems to absorb all light as it passes.
+
+N:629:Shadowfax, steed of Gandalf
+G:c: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 | NEUTRAL | NO_TARGET |
+F:REGENERATE | BASH_DOOR | IM_FIRE | IM_COLD | IM_ELEC |
+F:IM_POIS | NO_FEAR | DROP_CORPSE | WILD_ONLY | WILD_WOOD | WILD_GRASS |
+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 ghostly troll-like being from the ethereal plane.
+
+N:631:War troll
+G:T:R
+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:An corruption of Morgoth, it was made in mockery of the Ents.
+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, whom
+D:she rarely fights without.
+
+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 spellcasting.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:640:Giant green bat
+G:b:G
+I:130:3d8:12:20:50
+W:20:2:600:18
+E:0:1:1:0:1:0
+O:0:0:0:0
+B:BITE:TERRIFY:1d3
+B:CLAW:LOSE_INT:1d2
+B:CLAW:DISEASE: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 which is an expert at disguise in a forest.
+
+# New monster added by furiosity for the Theme module
+N:641:Death vortex
+G:v:D
+I:130:40d20:100:40:0
+W:60:2:0:6000
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:LOSE_ALL:5d5
+B:ENGULF:EXP_80:5d5
+B:ENGULF:LOSE_ALL:5d5
+B:ENGULF:EXP_80:5d5
+F:FORCE_SLEEP | CAN_FLY |
+F:RAND_50 | RAND_25 | RES_DISE | RES_NETH | RES_NEXU |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | BASEANGBAND | NO_CUT
+F:IM_ACID | IM_FIRE | IM_COLD | IM_ELEC | IM_POIS |
+F:NO_CONF | NO_SLEEP | NO_FEAR | NONLIVING | RES_PLAS |
+S:1_IN_6 |
+S:ANIM_DEAD | BO_MANA | DARKNESS | BA_DARK | DRAIN_MANA |
+D:A whirlpool of darkness that smells like death and decay.
+
+# New monster added by furiosity for the Theme module
+N:642:Gas vortex
+G:v:g
+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:POISON:3d3
+F:FORCE_SLEEP | RAND_50 | CAN_FLY | WILD_WASTE |
+F:EMPTY_MIND | BASH_DOOR | POWERFUL | WILD_TOO |
+F:IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP |
+F:NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT |
+S:1_IN_6 |
+S:BR_POIS |
+D:A whirlpool of noxious gases.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:647:Mana vortex
+G:v:y
+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:LOSE_ALL:3d3
+F:FORCE_SLEEP | RAND_50 | CAN_FLY | WILD_WASTE |
+F:EMPTY_MIND | BASH_DOOR | WILD_TOO | POWERFUL |
+F:IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP |
+F:NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT |
+S:1_IN_8 |
+S:BR_MANA |
+D:A whirlpool of pure magical energy.
+
+N:648:Helcungol
+G:U:W
+I:110:40d10:20:50:80
+W:40:2:2700:2000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:STING:POISON:3d4
+B:SPIT:COLD:3d4
+B:HIT:HURT:3d4
+B:CRUSH:HURT:8d12
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:FRIENDS |
+F:ONLY_ITEM | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | IM_FIRE |
+F:EVIL | DEMON | IM_COLD | NO_CONF | NO_SLEEP | BASEANGBAND
+S:1_IN_8 |
+S:BLIND | CONF | SLOW
+D:One of the spider demons, spawn of Ungoliant. It looks like
+D:a giant bloated spider and its claws are icy cold.
+
+N:649:Lygrog
+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:Lygroeg are corrupted demonic snakelike forms, an evil creation
+D:of Morgoth.
+
+# New monster added by furiosity for the Theme module
+N:650:Slow vortex
+G:v:W
+I:80:9d9:100:30:0
+W:21:1:0:100
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:ENGULF:PARALYZE:3d3
+F:FORCE_SLEEP | RAND_50 | CAN_FLY | WILD_WASTE |
+F:EMPTY_MIND | BASH_DOOR | WILD_TOO | POWERFUL |
+F:NO_FEAR | NO_CONF | NO_SLEEP |
+F:NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT |
+S:1_IN_8 |
+S:BR_INER | BR_GRAV |
+D:A whirlpool of inertia and gravity, twisting slowly.
+
+# New monster added by furiosity for the Theme module
+N:651:Nether vortex
+G:v:G
+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:EXP_40:3d3
+F:FORCE_SLEEP | RAND_50 | CAN_FLY | WILD_WASTE |
+F:EMPTY_MIND | BASH_DOOR | WILD_TOO | POWERFUL |
+F:IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP |
+F:NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT |
+S:1_IN_8 |
+S:BR_NETH |
+D:A whirlpool of nether forces.
+
+# New monster added by furiosity for the Theme module
+N:652:Puzzling vortex
+G:v:U
+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:CONFUSE:3d3
+F:FORCE_SLEEP | RAND_50 | CAN_FLY | WILD_WASTE |
+F:EMPTY_MIND | BASH_DOOR | WILD_TOO | POWERFUL |
+F:IM_FIRE | NO_FEAR | NO_CONF | NO_SLEEP |
+F:NONLIVING | BASEANGBAND | HAS_LITE | NO_CUT |
+S:1_IN_8 |
+S:BR_CONF |
+D:A whirlpool of something strange.
+
+# New monster added by furiosity for the Theme module
+N:653:Dark yeek
+G:y:D
+I:110:4d8:18:18:10
+W:8:1:800:15
+E:1:1:1:2:1:1
+O:0:50:0:30
+B:HIT:HURT:3d6
+F:DROP_60 | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR |
+F:ANIMAL | IM_ACID |
+F:MORTAL | BASEANGBAND
+S:1_IN_9
+S:S_KIN | CAUSE_1
+D:It is a strange small humanoid with a malevolent stare.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:655:White yeek
+G:y:w
+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 | IM_COLD |
+F:MORTAL | BASEANGBAND
+S:1_IN_10
+S:BO_COLD | BO_ICEE |
+D:It is a strange small humanoid adapted to cold environments.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:659:Gray yeek
+G:y:s
+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
+S:1_IN_6
+S:BO_ACID |
+D:It is a strange small humanoid cloaked in gray. You notice
+D:some suspicious-looking vials in his pack.
+
+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 to ever forge weapons of meteorite iron. His dark
+D:countenance glares at you in disdain.
+
+# New monster added by furiosity for the Theme module
+N:661:Yellow yeek
+G:y:y
+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 | IM_POIS |
+F:MORTAL | BASEANGBAND
+S:1_IN_7 |
+S:BA_POIS
+D:It is a strange small humanoid. He's strangely stinky.
+
+# New monster added by furiosity for the Theme module
+# A brown yeek with the PET flag.
+N:662:Adventurer 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 | PET |
+F:MORTAL | BASEANGBAND
+D:It is a strange small humanoid, a fellow adventurer.
+
+# New monster added by furiosity for the Theme module
+N:663:Dark mushroom patch
+G:,:D
+I:110:1d2:2:1:0
+W:8:1:10:1
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:SPORE:EXP_10:1d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | DROP_CORPSE |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+S:1_IN_10 |
+S:DARKNESS
+D:Yum! It looks quite tasty.
+
+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:W
+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:Death incarnate, its hideous black body seems to struggle against
+D:reality as the universe 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 worm.
+
+# New monster added by furiosity for the Theme module
+N:669:White mushroom patch
+G:,:w
+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:COLD: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.
+
+# New monster added by furiosity for the Theme module
+N:670:Brown mushroom patch
+G:,:u
+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:DISEASE: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.
+
+# New monster added by furiosity for the Theme module
+N:671:Silver mushroom patch
+G:,:W
+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:EAT_LITE: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.
+
+# New monster added by furiosity for the Theme module
+N:672:Green mushroom patch
+G:,:G
+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:POISON: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: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:y
+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 dragon is
+D:a master of light and dark. Its form disappears from sight as it cloaks
+D:itself in unearthly shadows.
+
+# New monster added by furiosity for the Theme module
+N:677:Dark elemental
+G:E:D
+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:BLIND:4d6
+B:HIT:EAT_LITE:4d6
+F:FORCE_SLEEP | RAND_25 |
+F:EMPTY_MIND | CAN_FLY | WILD_TOO | WILD_VOLCANO |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+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:BR_DARK
+D:It is a twisting pillar of pure darkness.
+
+# New monster added by furiosity for the Theme module
+N:678:Slow elemental
+G:E:s
+I:100:30d8:12:50:50
+W:33:2:0:350
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:HIT:PARALYZE:4d6
+B:HIT:PARALYZE:4d6
+F:FORCE_SLEEP | RAND_25 |
+F:EMPTY_MIND | CAN_FLY | WILD_TOO | WILD_VOLCANO |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+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:BR_INER
+D:It is a slowly twisting pillar of energy.
+
+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 leprawn
+G:l: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 | JOKEANGBAND | NO_CUT
+S:MULTIPLY |
+S:1_IN_6 |
+S:BLINK | TPORT | TELE_TO | CAUSE_3 | ANIM_DEAD
+D:Nasty undead little gnomes.
+
+# New monster added by furiosity for the Theme module
+N:681:Chaos elemental
+G:E:v
+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:EXP_40:4d6
+B:HIT:INSANITY:4d6
+F:FORCE_SLEEP | RAND_25 |
+F:EMPTY_MIND | CAN_FLY | WILD_TOO | WILD_VOLCANO |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+F:IM_FIRE | IM_POIS | IM_ELEC | IM_ACID | ELDRITCH_HORROR |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | HAS_LITE | NO_CUT
+S:1_IN_6 |
+S:BR_CHAO
+D:It is a massive tornado of raw chaos.
+
+# New monster added by furiosity for the Theme module
+N:682:Confusion elemental
+G:E:U
+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:CONFUSE:4d6
+B:HIT:CONFUSE:4d6
+F:FORCE_SLEEP | RAND_25 |
+F:EMPTY_MIND | CAN_FLY | WILD_TOO | WILD_VOLCANO |
+F:KILL_ITEM | KILL_BODY | BASH_DOOR | POWERFUL |
+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:BR_CONF
+D:It is confusing sight to behold.
+
+# New monster added by furiosity for the Theme module
+N:683:Large blue snake
+G:J:b
+I:100:4d8:5:38:75
+W:4:1:1000:9
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+B:CRUSH:ELEC:1d6
+F:RAND_25 | CAN_SWIM | WILD_TOO | DROP_SKELETON | DROP_CORPSE
+F:BASH_DOOR | HAS_EGG | ANIMAL | MORTAL | BASEANGBAND | IM_ELEC |
+D:It is about ten feet long, its lithe form crackling with sparks.
+
+# New monster added by furiosity for the Theme module
+N:684:Large silver snake
+G:J:W
+I:100:4d8:5:38:75
+W:4:1:1000:9
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+B:CRUSH:EAT_LITE: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, and seems to feed on light.
+
+# New monster added by furiosity for the Theme module
+N:685:Large purple snake
+G:J:v
+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:LOSE_CON: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 and looks diseased.
+
+N:686:Judge Death
+G:W:R
+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 | JOKEANGBAND | 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:s
+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: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.
+
+# New monster added by furiosity for the Theme module
+N:695:Large red snake
+G:J:R
+I:100:4d8:5:38:75
+W:4:1:1000:10
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d4
+B:CRUSH:FIRE: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, its form glowing with fire.
+
+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 | SPECIAL_GENE |
+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 great dragons 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 what legends are made of, 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 | JOKEANGBAND | 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 |
+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.
+
+# New monster added by furiosity for the Theme module
+N:700:Large eel
+G:J:U
+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:TERRIFY:1d6
+F:RAND_25 | CAN_SWIM | WILD_ONLY | DROP_SKELETON | DROP_CORPSE | WILD_SHORE
+F:BASH_DOOR | HAS_EGG | ANIMAL | MORTAL | BASEANGBAND | NEUTRAL | NO_TARGET |
+D:It is about ten feet long. Its survival depends on being
+D:confused for a much more dangerous snake.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:705:Killer gray beetle
+G:K:s
+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:ACID:4d5
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | IM_ACID | CAN_FLY |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a giant beetle surrounded by droplets of acid.
+
+# New monster added by furiosity for the Theme module
+N:706:Killer orange beetle
+G:K:o
+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:BLIND:4d5
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | CAN_FLY |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a giant beetle with a corrosive spit.
+
+# New monster added by furiosity for the Theme module
+N:707:Killer blue beetle
+G:K:b
+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:ELEC:4d5
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | IM_ELEC | CAN_FLY |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a giant beetle surrounded by sparks.
+
+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:Ents are some of the oldest creatures that awoke on Arda. It is a
+D: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:Rock giant
+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 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...
+
+# New monster added by furiosity for the Theme module
+N:712:Killer silver beetle
+G:K:W
+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:EAT_LITE:4d5
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | HURT_LITE | CAN_FLY |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a giant beetle surrounded by a strange dark light.
+
+# New monster added by furiosity for the Theme module
+N:713:Killer green beetle
+G:K:G
+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:CONFUSE:4d5
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | NO_CONF | CAN_FLY |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a giant beetle surrounded by a puzzling green aura.
+
+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:Also known as Bregalad. Unusually hasty, for an ent. He hates evil creatures,
+D:orcs in 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 | DROP_RANDART | KILL_TREES |
+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 | BRAIN_SMASH |
+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:HURT:5d8
+B:BITE:HURT: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 | NEUTRAL | NO_TARGET |
+F:WILD_ONLY | WILD_SHORE | WILD_OCEAN | MORTAL | BASEANGBAND
+D:A great water-beast, with an almost unpenetrable skin.
+
+# New monster added by furiosity for the Theme module
+N:717:Killer aquatic beetle
+G:K:B
+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:COLD:4d5
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | RES_WATE | CAN_FLY | SUSCEP_FIRE |
+F:MORTAL | BASEANGBAND | HAS_LITE | AQUATIC |
+D:It is a giant beetle that prefers to live in water.
+
+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:Menelrog
+G:U:U
+I:120:40d120:20:80:80
+W:60:3:0:10000
+E:1:1:1:2:1:1
+O:50:0:50:0
+B:GAZE:TERRIFY
+B:HIT:POISON:6d6
+B:SPIT:FIRE:6d6
+B:SPIT:ACID: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 | SMART |
+F:KILL_WALL | WILD_TOO |
+F:ONLY_ITEM | DROP_GREAT | DROP_GOOD | BASEANGBAND
+S:1_IN_4 |
+S:HOLD | BLINK | CONF | S_DEMON | BRAIN_SMASH | BO_PLAS
+D:Morgoth's horrific corruption of Manwe's Great Eagles, with the feathers
+D:devolved into scales, and the talons so powerful they can tear down
+D:stone walls. This demon is a power to be reckoned with; it is powerful
+D:in both combat and magic, and it's the only demon which may roam the wild.
+
+N:720:Mornungol
+G:U:s
+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:LOSE_CHR:10d2
+B:SPIT:ACID:4d10
+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 | CONF
+D:One of the spider demons, spawn of Ungoliant. It looks like
+D:a giant bloated spider, and its claws are dripping with acid.
+
+# New monster added by furiosity for the Theme module
+N:721:Killer tree beetle
+G:K:U
+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:HALLU:4d5
+F:WEIRD_MIND | BASH_DOOR | WILD_TOO | DROP_CORPSE |
+F:ANIMAL | KILL_TREES | CAN_FLY | SUSCEP_COLD |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:It is a giant beetle, a relative of the termite.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# From EyAngband
+N:723:6-headed hydra
+G:M:w
+I:115:37d10:20:80:20
+W:29:2:6000:800
+E:0:1:0:2:3:0
+O:0:0:0:0
+B:BITE:POISON:4d4
+B:BITE:POISON:4d4
+B:BITE:POISON:4d4
+B:BITE:POISON:4d4
+F:BASEANGBAND | ANIMAL | MORTAL | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:FORCE_SLEEP | CAN_SWIM | FORCE_SLEEP | DROP_CORPSE | MOVE_BODY |
+F:IM_POIS | SUSCEP_FIRE | ONLY_GOLD | DROP_4D2 | DROP_2D2 | IM_COLD |
+S:1_IN_5 |
+S:SCARE | BA_POIS | BR_POIS |
+D:A strange reptilian creature with 6 heads dripping venom. It has
+D:adapted to living in cold environments.
+
+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 dark fire. The world
+D:itself seems to cry out against it.
+
+N:728:Great Storm Worm
+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:Trolls are said to be corruptions of Morgoth, made in mockery of Ents.
+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.
+
+# New monster added by furiosity for the Theme module
+# From UnAngband
+N:730:8-headed hydra
+G:M:s
+I:120:100d11:20:92:20
+W:39:2:6500:1000
+E:0:1:0:2:4:0
+O:0:0:0:0
+B:BITE:ACID:3d8
+B:BITE:ACID:3d8
+B:SPIT:ACID:3d4
+B:SPIT:ACID:3d4
+F:BASEANGBAND | ANIMAL | MORTAL | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:FORCE_SLEEP | CAN_SWIM | FORCE_SLEEP | DROP_CORPSE | MOVE_BODY |
+F:IM_ACID | BASH_DOOR | ONLY_GOLD | DROP_4D2 | DROP_2D2 |
+S:1_IN_5 |
+S:SCARE | BR_ACID | BA_ACID
+D:A strange reptilian creature with eight heads spewing acid.
+
+N:731:Oathbreaker
+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:UNDEAD | BASEANGBAND | HAS_LITE
+S:1_IN_5 |
+S:BLIND | SCARE | CAUSE_3 | BA_NETH | BA_FIRE | BO_PLAS
+S:S_MONSTERS | S_DEMON
+D:He is one of the warriors of old who broke their promise to the
+D:King of Gondor. A terrible curse now haunts all of them.
+
+# New monster added by furiosity for the Theme module
+# From UnAngband
+N:732:10-headed hydra
+G:M:b
+I:120:100d15:20:97:20
+W:42:2:6500:8000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:FIRE:3d9
+B:BITE:ACID:3d9
+B:BITE:ELEC:3d9
+B:BITE:COLD:3d9
+F:BASEANGBAND | ANIMAL | MORTAL | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:FORCE_SLEEP | CAN_SWIM | FORCE_SLEEP | DROP_CORPSE | MOVE_BODY |
+F:IM_ACID | IM_ELEC | IM_FIRE | IM_COLD | BASH_DOOR |
+F:ONLY_GOLD | DROP_4D2 | DROP_2D2 |
+S:1_IN_4 |
+S:SCARE
+D:A strange reptilian creature with ten multicoloured heads.
+
+# New monster added by furiosity for the Theme module
+# From UnAngband
+N:733:12-headed hydra
+G:M:W
+I:120:100d27:20:110:20
+W:47:2:7000:10000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:FIRE:4d12
+B:BITE:FIRE:4d12
+B:BITE:POISON:4d12
+B:BITE:POISON:4d12
+F:BASEANGBAND | ANIMAL | MORTAL | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:FORCE_SLEEP | CAN_SWIM | FORCE_SLEEP | DROP_CORPSE | MOVE_BODY |
+F:IM_FIRE | IM_POIS | RES_PLAS | BASH_DOOR | ONLY_GOLD | DROP_4D2 | DROP_2D2 |
+S:1_IN_4 |
+S:SCARE | BA_FIRE | BA_POIS | BO_FIRE | BO_PLAS |
+D:A strange reptilian creature with twelve smouldering heads.
+
+# New monster added by furiosity for the Theme module
+# From UnAngband
+N:734:13-headed hydra
+G:M:v
+I:120:36d100:20:120:20
+W:51:2:7500:15000
+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:BASEANGBAND | ANIMAL | MORTAL | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:FORCE_SLEEP | CAN_SWIM | FORCE_SLEEP | DROP_CORPSE |
+F:IM_FIRE | IM_POIS | RES_PLAS | NO_CONF | NO_SLEEP | BASH_DOOR |
+F:ONLY_GOLD | DROP_4D2 | DROP_2D2 | DROP_2D2 | SMART |
+S:1_IN_3
+S:SCARE | BA_FIRE | BA_POIS | BO_FIRE | BO_PLAS | S_KIN
+D:A strange reptilian creature with thirteen smouldering heads dripping venom.
+
+# New monster added by furiosity for the Theme module
+N:735:14-headed hydra
+G:M:B
+I:120:40d100:20:130:20
+W:55:2:8000:20000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:FIRE:4d10
+B:BITE:ACID:4d10
+B:BITE:ELEC:4d10
+B:BITE:COLD:4d10
+F:BASEANGBAND | ANIMAL | MORTAL | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:FORCE_SLEEP | CAN_SWIM | FORCE_SLEEP | DROP_CORPSE |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | IM_POIS | RES_PLAS | SMART |
+F:NO_CONF | NO_SLEEP | BASH_DOOR | ONLY_GOLD | DROP_4D2 | DROP_4D2 |
+S:1_IN_3
+S:SCARE | BA_FIRE | BA_POIS | BA_COLD | BA_ELEC | BA_ACID
+S:S_KIN |
+D:A cunning reptilian creature with fourteen multicoloured heads.
+
+# New monster added by furiosity for the Theme module
+N:736:15-headed hydra
+G:M:U
+I:120:44d100:20:140:20
+W:59:2:8500:25000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:FIRE:5d11
+B:BITE:ACID:5d11
+B:BITE:COLD:5d11
+B:BITE:ELEC:5d11
+F:BASEANGBAND | ANIMAL | MORTAL | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:FORCE_SLEEP | CAN_SWIM | AQUATIC | DROP_CORPSE | WILD_OCEAN |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | IM_POIS | RES_PLAS | SMART |
+F:NO_CONF | NO_SLEEP | BASH_DOOR | ONLY_GOLD | DROP_4D2 | DROP_4D2 |
+S:1_IN_3
+S:SCARE | BA_FIRE | BA_POIS | BA_COLD | BA_ELEC | BA_ACID | CONF |
+S:BO_FIRE | BO_COLD | BO_ELEC | BO_ACID | S_KIN |
+D:A cunning reptilian creature with fifteen multicoloured heads.
+
+# New monster added by furiosity for the Theme module
+N:737:Killer hydra
+G:M:D
+I:120:50d100:20:150:20
+W:65:5:9000:40000
+E:0:1:0:2:2:0
+O:0:0:0:0
+B:BITE:FIRE:6d12
+B:BITE:ACID:6d12
+B:BITE:COLD:6d12
+B:BITE:ELEC:6d12
+F:BASEANGBAND | ANIMAL | MORTAL | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:FORCE_SLEEP | CAN_SWIM | AQUATIC | DROP_CORPSE | WILD_OCEAN |
+F:IM_FIRE | IM_COLD | IM_ELEC | IM_ACID | IM_POIS | RES_PLAS | RES_NETH |
+F:RES_TELE | SMART | NO_CONF | NO_SLEEP | BASH_DOOR | ONLY_GOLD | DROP_4D2 |
+F:RES_NEXU | DROP_4D2 | RES_WATE | NO_STUN | EVIL |
+S:1_IN_3 |
+S:SCARE | BA_FIRE | BA_POIS | BA_COLD | BA_ELEC | BA_ACID | CONF |
+S:BO_FIRE | BO_COLD | BO_ICEE | BA_WATE | BO_ELEC | BO_ACID | BR_NETH |
+S:S_KIN | S_ANIMALS | S_MONSTERS |
+D:A cunning, evil reptile from the depths. It has so many heads that you
+D:give up after losing count twice.
+
+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 spellcasting. 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:hints 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 Worm
+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 | 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:JOKEANGBAND | 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, 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.
+
+# New monster added by furiosity for the Theme module
+N:745:Forest ogre
+G:O:g
+I:110:13d10:20:37:30
+W:18:2:2100:70
+E:1:1:1:2:1:1
+O:10:85:0:0
+B:HIT:HURT:3d9
+B:BITE:POISON:2d8
+F:FRIENDS |
+F:DROP_60 | WILD_TOO | WILD_WOOD | WILD_MOUNTAIN | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | IM_POIS | CAN_FLY
+F:EVIL | GIANT |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A hideous, smallish giant that is often found near or with orcs.
+D:This ogre prefers living in the forest and can pass through
+D:thick forests with ease.
+
+# New monster added by furiosity for the Theme module
+# A pet ogre without friends
+N:746:Rebel ogre
+G:O:y
+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:DROP_60 | WILD_TOO | WILD_WOOD | WILD_MOUNTAIN | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | PET |
+F:GOOD | GIANT |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A rebel among ogres, who has joined the forces of the Light to
+D:fight agains the Shadow.
+
+# New monster added by furiosity for the Theme module
+# A pet hill giant
+N:747:Rebel giant
+G:P:R
+I:110:30d15:20:45:50
+W:25:3: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 | PET |
+F:GOOD | GIANT | MALE |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:A ten foot tall humanoid with powerful muscles. This giant has
+D:abandoned the Darkness in favour of the forces of good. Thus he
+D:is a rebel and hated among his kind.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:753:Spider quylthulg
+G:Q:D
+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_SPIDER
+D:It is a pulsing flesh mound that was once a spider.
+
+# New monster added by furiosity for the Theme module
+N:754:Canine quylthulg
+G:Q:s
+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_HOUND
+D:It is a pulsing flesh mound that was once a hound.
+
+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 the
+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 Worm of Fire
+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.
+
+# New monster added by furiosity for the Theme module
+N:757:Aquatic quylthulg
+G:Q:b
+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 | AQUATIC |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_2 |
+S:BLINK | TPORT |
+S:S_HYDRA
+D:It is a pulsing flesh mound that was once a many-headed reptile.
+
+# New monster added by furiosity for the Theme module
+N:758:Adventurer quylthulg
+G:Q:w
+I:120:48d10:20:1:0
+W:45:3: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 | PET |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_2 |
+S:BLINK | TPORT |
+S:S_KIN
+D:It is a pulsing flesh mound that was once a fellow adventurer.
+
+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:bacteria that make it pulse in a foul way.
+
+# New monster added by furiosity for the Theme module
+N:760:White hulk
+G:X:w
+I:110:20d10:20:60:10
+W:20:1:5000:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:CONFUSE
+B:HIT:COLD:2d6
+B:HIT:HURT:2d6
+B:BITE:COLD:3d6
+F:EMPTY_MIND | COLD_BLOOD | IM_COLD |
+F:BASH_DOOR | KILL_WALL | DROP_SKELETON |
+F:ANIMAL | EVIL | SUSCEP_FIRE | AURA_COLD |
+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. It is adapted to extreme cold environments.
+
+# New monster added by furiosity for the Theme module
+N:761:Death hulk
+G:X:D
+I:110:99d10:20:80:10
+W:40:1:5000:1000
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:CONFUSE
+B:HIT:EXP_40:3d8
+B:HIT:HURT:3d8
+B:BITE:HURT:3d8
+F:EMPTY_MIND | COLD_BLOOD | SMART |
+F:BASH_DOOR | KILL_WALL | DROP_SKELETON |
+F:ANIMAL | EVIL | RES_NETH | HURT_LITE |
+F:IM_POIS |
+F:HURT_ROCK | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_8 |
+S:DARKNESS | S_UNDEAD
+D:This bizarre creature has glaring eyes and large mandibles capable of
+D:slicing through rock. It seems to be surrounded by an impenetrable
+D:shadow.
+
+N:762:Fundin Bluecloak
+G:k: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 Dwarven
+D:strength and priestly wisdom are a true match for any adventurer.
+
+N:763:Black Balrog
+G:U:D
+I:120:20d100:20:50:80
+W:61:4:9000:10000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:ACID:4d12
+B:HIT:ACID:4d12
+B:CRUSH:HURT:3d12
+B:TOUCH:UN_POWER
+F:FORCE_SLEEP | FORCE_MAXHP | KILL_WALL |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DEMON | IM_ACID | NO_CONF | NO_SLEEP | NONLIVING |
+F:BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:BLIND | CONF | SCARE | HASTE |
+S:BR_ACID |
+S:S_DEMON
+D:The greatest of the demons, potent in both magical might
+D:and sheer battle power. This corrupted Maia's form is
+D:surrounded by whirlpools of acid.
+
+# New monster added by furiosity for the Theme module
+N:764:Orange hulk
+G:X:o
+I:110:20d10:20:60:10
+W:20:1:5000:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:CONFUSE
+B:HIT:POISON:2d6
+B:HIT:HURT:2d6
+B:BITE:POISON:3d6
+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. It looks poisonous.
+
+# New monster added by furiosity for the Theme module
+N:765:Fire hulk
+G:X:r
+I:110:20d10:20:60:10
+W:20:1:5000:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:CONFUSE
+B:HIT:FIRE:2d6
+B:HIT:HURT:2d6
+B:BITE:FIRE:3d6
+F:EMPTY_MIND | COLD_BLOOD | IM_FIRE |
+F:BASH_DOOR | KILL_WALL | DROP_SKELETON |
+F:ANIMAL | EVIL | SUSCEP_COLD | AURA_FIRE |
+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. It is surrounded by flames.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:767:Forest hulk
+G:X:g
+I:110:20d10:20:60:10
+W:20:1:5000:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:CONFUSE
+B:HIT:POISON:2d6
+B:HIT:HURT:2d6
+B:BITE:PARALYZE:3d6
+F:EMPTY_MIND | COLD_BLOOD |
+F:BASH_DOOR | KILL_WALL | DROP_SKELETON |
+F:ANIMAL | EVIL |
+F:IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+D:This bizarre creature has glaring eyes and large mandibles capable of
+D:slicing through rock. Its preferred habitat is a forest, however.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:769:Night hulk
+G:X:b
+I:110:20d10:20:60:10
+W:20:1:5000:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:CONFUSE
+B:HIT:BLIND:2d6
+B:HIT:HURT:2d6
+B:BITE:EAT_LITE:3d6
+F:EMPTY_MIND | COLD_BLOOD |
+F:BASH_DOOR | KILL_WALL | DROP_SKELETON |
+F:ANIMAL | EVIL | HURT_LITE |
+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. It prefers to dwell in dark caves where there is
+D:no sun.
+
+# New monster added by furiosity for the Theme module
+N:770:Silver hulk
+G:X:W
+I:110:20d10:20:60:10
+W:20:1:5000:90
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:CONFUSE
+B:HIT:HALLU:2d6
+B:HIT:HURT:2d6
+B:BITE:INSANITY:3d6
+F:EMPTY_MIND | COLD_BLOOD | IM_COLD |
+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. It is an extremely confusing sight to behold.
+
+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 | SPECIAL_GENE
+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.
+
+N:773:Blue Balrog
+G:U:b
+I:120:20d100:20:50:80
+W:61:4:9000:10000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:ELEC:4d12
+B:HIT:ELEC:4d12
+B:CRUSH:HURT:3d12
+B:TOUCH:UN_POWER
+F:FORCE_SLEEP | FORCE_MAXHP | AURA_ELEC | KILL_WALL |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DEMON | IM_ELEC | NO_CONF | NO_SLEEP | NONLIVING |
+F:BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:BLIND | CONF | SCARE | HASTE |
+S:BR_ELEC |
+S:S_DEMON
+D:The greatest of the demons, potent in both magical might
+D:and sheer battle power. This corrupted Maia's form is
+D:surrounded by living lightning.
+
+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.
+
+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.
+
+# Description of Tevildo taken from UnAngband
+N:777:Tevildo, Prince of Cats
+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 and mighty servant of Morgoth. A great cat, coal-black and evil to look upon. His eyes are long and very narrow and slanted, and gleam both red and green. His great grey whiskers are as stout and sharp as needles. His purr is like the roll of drums and his growl like thunder.
+
+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 | JOKEANGBAND |
+S:1_IN_5 |
+S:CAUSE_4 |
+S:BR_CHAO
+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.
+
+# New monster added by furiosity for the Theme module
+N:780:Chaos hulk
+G:X:v
+I:110:25d10:20:80:10
+W:30:1:5000:110
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:GAZE:CONFUSE
+B:HIT:HALLU:2d6
+B:HIT:LOSE_ALL:2d6
+B:BITE:EXP_40:3d6
+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. It is surrounded by an aura of chaotic forces.
+
+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 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 Worm 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 Worm 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 Worm 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.
+
+# New monster added by furiosity for the Theme module
+N:786:Yellow hulk
+G:X:y
+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:ACID:3d4
+B:HIT:ACID:3d4
+B:HIT:ACID:3d4
+B:HIT:ACID:3d4
+F:FORCE_MAXHP | IM_ACID | HURT_LITE |
+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 drops of acid.
+
+N:787:White Balrog
+G:U:w
+I:120:20d100:20:50:80
+W:61:4:9000:10000
+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:FORCE_SLEEP | FORCE_MAXHP | AURA_COLD | KILL_WALL |
+F:ONLY_ITEM | DROP_1D2 | DROP_2D2 | DROP_GOOD |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | MOVE_BODY |
+F:EVIL | DEMON | IM_COLD | NO_CONF | NO_SLEEP | NONLIVING |
+F:BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:BLIND | CONF | SCARE | HASTE |
+S:BR_COLD |
+S:S_DEMON
+D:The greatest of the demons, potent in both magical might
+D:and sheer battle power. This corrupted Maia's form is
+D:surrounded by frost.
+
+# New monster added by furiosity for the Theme module
+N:788:Red hulk
+G:X:R
+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:FIRE:3d4
+B:HIT:FIRE:3d4
+B:HIT:FIRE:3d4
+B:HIT:FIRE:3d4
+F:FORCE_MAXHP | IM_ACID | SUSCEP_COLD |
+F:EMPTY_MIND | COLD_BLOOD | HURT_LITE |
+F:ONLY_GOLD | DROP_2D2 | AURA_FIRE |
+F:KILL_ITEM | PASS_WALL |
+F:IM_FIRE | 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 is wreathed in flames.
+
+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:0:0: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 |
+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 Worm 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 | SPECIAL_GENE |
+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 | AQUATIC |
+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.
+D:She has somehow broken through to the Lonely Isle and is killing the swans
+D:The magic of the Valar is for some reason powerless against her, and they
+D:have opened a path to Tol Eressea in case a hero wants to try their luck
+D:at destroying her.
+
+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 | BASEANGBAND | 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:Dagorrog
+G:U:u
+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:Festooned with horns and fierce claws, these monstrous forms are the
+D:shock troops of Morgoth, known for their strength and fierce weapons.
+D:A giant humanoid demon wielding a massive, heavy and sharp scythe.
+D:Feared by foes and friends alike when it flies into one of its berserk
+D:rages, the War demon will cut down anything in its path between it and
+D:you.
+
+# New monster added by furiosity for the Theme module
+N:796:Green hulk
+G:X:G
+I:115:32d10:20:80:10
+W:40:1:500000:1200
+E:0:0:0:3:0:0
+O:0:0:0:0
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+B:HIT:POISON:3d4
+F:FORCE_MAXHP | IM_ACID | HURT_LITE |
+F:EMPTY_MIND | COLD_BLOOD |
+F:ONLY_GOLD | DROP_2D2 |
+F:KILL_ITEM | KILL_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 drops of venom.
+
+# New monster added by furiosity for the Theme module
+N:797:Blue hulk
+G:X:B
+I:115:32d10:20:80:10
+W:40:1:500000:1200
+E:0:0:0:3:0:0
+O:0:0:0:0
+B:HIT:ELEC:3d4
+B:HIT:ELEC:3d4
+B:HIT:ELEC:3d4
+B:HIT:ELEC:3d4
+F:FORCE_MAXHP | IM_ACID | HURT_LITE |
+F:EMPTY_MIND | COLD_BLOOD |
+F:ONLY_GOLD | DROP_2D2 | AURA_ELEC |
+F:KILL_ITEM | KILL_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. It is surrounded by sparks.
+
+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: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.
+
+# Joke monster added by furiosity for the Theme module
+
+N:803:Invisible Horror
+G:.:v
+I:100:4d9:20:1:0
+W:20:5:0:300
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:CHARGE:TERRIFY
+B:CHARGE:TERRIFY
+B:CHARGE:TERRIFY
+B:CHARGE:TERRIFY
+F:CHAR_CLEAR | ATTR_CLEAR | FORCE_MAXHP | SMART |
+F:INVISIBLE | COLD_BLOOD | EMPTY_MIND | WEIRD_MIND |
+F:PASS_WALL | UNDEAD | EVIL | NONLIVING | HURT_LITE |
+F:IM_POIS | RES_TELE | NO_FEAR | NO_STUN | NO_SLEEP |
+F:CAN_SWIM | NO_TARGET | AI_ANNOY | NO_CUT | CAN_SPEAK |
+F:JOKEANGBAND
+S:1_IN_2
+S:SCARE
+D:You can't see it.
+
+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
+D:risen from the dead to continue his foul plots and schemes.
+
+# New monster added by furiosity for the Theme module
+N:805:Silver wraith
+G:W:B
+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:EAT_LITE:1d8
+B:TOUCH:EXP_40
+F:FORCE_SLEEP | DROP_60 |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:EVIL | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_8 |
+S:HOLD | SCARE | CAUSE_2 | DARKNESS
+D:An almost imperceptible silvery shimmer in the air. It
+D:seems to absorb light wherever it goes.
+
+# New monster added by furiosity for the Theme module
+# Pet barrow wight, basically.
+N:806:Adventurer wraith
+G:W:U
+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 | DROP_60 | PET |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | CAN_FLY |
+F:GOOD | UNDEAD | IM_COLD | IM_POIS |
+F:NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_8 |
+S:HOLD | SCARE | CAUSE_2 | DARKNESS
+D:A fellow adventurer wraith.
+
+N:807:Balrog Captain
+G:U:G
+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_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.
+
+# New monster added by furiosity for the Theme module
+N:809:Vampire orc
+G:V:D
+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 | FRIENDS |
+F:COLD_BLOOD | DROP_60 | DROP_1D2 | ORC |
+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 an orc with an aura of power. You notice a sharp set of front
+D:teeth.
+
+# New monster added by furiosity for the Theme module
+N:810:Vampire yeek
+G:V:w
+I:110:16d12: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 | STUPID |
+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 yeek with an aura of power. You notice a sharp set of front
+D:teeth.
+
+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:Greater Balrog
+G:U:v
+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 | SMART |
+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 | ARROW_4 | MISSILE
+D:Appearing as a giant, clawed and winged humanoid with a scaly red body
+D:and massive fangs dripping a foul green liquid, the Greater Balrog is a
+D:dreadful enemy from the lowest depths of Morgoth's realm. They are often
+D:the commanders of vast demon and dragon armies.
+
+# New monster added by furiosity for the Theme module
+N:813:Vampire ogre
+G:V:o
+I:110:35d12: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 | FRIENDS | STUPID |
+F:COLD_BLOOD | DROP_60 | DROP_1D2 | GIANT |
+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 an ogre with an aura of power. You notice a sharp set of front
+D:teeth.
+
+# New monster added by furiosity for the Theme module
+N:814:Vampire troll
+G:V:u
+I:110:35d12: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 | TROLL | STUPID |
+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 troll with an aura of power. You notice a sharp set of front
+D:teeth.
+
+# New monster added by furiosity for the Theme module
+N:815:Vampire dwarf
+G:V:R
+I:110:30d12: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 dwarf with an aura of power. You notice a sharp set of front
+D:teeth.
+
+# New monster added by furiosity for the Theme module
+N:816:Vampire elf
+G:V:G
+I:110:30d12: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 | SMART |
+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 an elf with an aura of power. You notice a sharp set of front
+D:teeth.
+
+# New monster added by furiosity for the Theme module
+N:817:Vampire gnome
+G:V:B
+I:110:15d12: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 | STUPID |
+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 gnome with an aura of power. You notice a sharp set of front
+D:teeth.
+
+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 spellcaster. So old that even he cannot
+D:remember his own name, his power and evil are undeniable. He believes
+D:unshakably 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 | DROP_CORPSE | DROP_SKELETON |
+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.
+
+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 | AQUATIC |
+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:A leader among the Thunderlords of good, 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:o
+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 | EMPTY_MIND | ANIMAL |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND
+S:1_IN_1 |
+S:S_MONSTERS | 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.
+
+# New monster added by furiosity for the Theme module
+N:823:Vampire adventurer
+G:V:U
+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 | PET |
+F:OPEN_DOOR | BASH_DOOR | REGENERATE | CAN_FLY |
+F:GOOD | 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 an adventurer with an aura of power. You notice a sharp set
+D:of front teeth.
+
+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 | AQUATIC |
+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.
+
+# New monster added by furiosity for the Theme module
+N:826:Snow-frog
+G:R:w
+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:COLD:2d4
+F:RAND_50 | WILD_TOO | WILD_SHORE | WILD_SWAMP |
+F:BASH_DOOR | DROP_CORPSE | IM_COLD | WILD_WASTE |
+F:ANIMAL | MORTAL | BASEANGBAND
+D:It is a strange frog adapted to cold environments.
+
+# New monster added by furiosity for the Theme module
+N:827:Swamp lizard
+G:R:o
+I:110:3d4:20:4:15
+W:5:2:100:8
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+B:SPIT:POISON:2d4
+F:ANIMAL | CAN_SWIM | WILD_TOO | WILD_WOOD | WILD_SWAMP |
+F:DROP_CORPSE | HAS_EGG | IMPRESED | IM_POIS |
+F:MORTAL | BASEANGBAND
+D:It is a small lizard that usually lives in the
+D:marshlands. It looks poisonous.
+
+# New monster added by furiosity for the Theme module
+N:828:Giant silver frog
+G:R:W
+I:110:3d4:20:4:15
+W:5:2:100:8
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+B:SPIT:EAT_LITE:2d4
+F:ANIMAL | CAN_SWIM | WILD_TOO | WILD_WOOD | WILD_SWAMP |
+F:DROP_CORPSE | IM_POIS | HURT_LITE |
+F:MORTAL | BASEANGBAND
+D:A frog that seems to emit a strange dark light.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:831:Blue lizard
+G:R:B
+I:110:3d4:20:4:15
+W:5:2:100:8
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d2
+B:SPIT:ELEC:2d4
+F:ANIMAL | CAN_SWIM | WILD_TOO | WILD_WOOD | WILD_SWAMP |
+F:DROP_CORPSE | HAS_EGG | IMPRESED | IM_ELEC |
+F:MORTAL | BASEANGBAND
+D:It is a small lizard with powerful jaws which cause
+D:sparks to fly as they snap shut.
+
+# New monster added by furiosity for the Theme module
+N:832:Death dragonfly
+G:F:D
+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:EXP_40:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | RES_NETH |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_NETH
+D:The size of a large bird, this fly carries with it the
+D:stench of decay.
+
+# New monster added by furiosity for the Theme module
+N:833:Giant swamp dragonfly
+G:F:o
+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:TERRIFY:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_NUKE
+D:The size of a large bird, this fly comes from a dark swamp.
+
+# New monster added by furiosity for the Theme module
+N:834:Giant red dragonfly
+G:F:r
+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 | AURA_FIRE |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | IM_FIRE |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_FIRE
+D:The size of a large bird, this fly is surrounded by flames.
+
+# New monster added by furiosity for the Theme module
+N:835:Giant forest dragonfly
+G:F:g
+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:PARALYZE:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | IM_ACID | IM_POIS |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_DARK
+D:The size of a large bird, this fly prefers to dwell in
+D:the forest, and is rather stealthy.
+
+# New monster added by furiosity for the Theme module
+N:836:Giant blue dragonfly
+G:F:b
+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:ELEC:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | IM_ELEC |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_ELEC
+D:The size of a large bird, this fly crackles with sparks.
+
+# New monster added by furiosity for the Theme module
+N:837:Giant brown dragonfly
+G:F:u
+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:HURT:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | NO_CUT |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_SHAR
+D:The size of a large bird, this fly dives from the earth
+D:into the air, and back.
+
+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:w
+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.
+
+# New monster added by furiosity for the Theme module
+N:841:Giant silver dragonfly
+G:F:W
+I:100:3d8:12:20:50
+W:20:2:150:70
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:EAT_LITE:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | NO_CUT |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_INER
+D:The size of a large bird, this fly moves slowly.
+
+# New monster added by furiosity for the Theme module
+N:842:Giant violet dragonfly
+G:F:v
+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:CONFUSE:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | NO_CUT |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_CHAO
+D:The size of a large bird, this fly is surrounded by an
+D:aura of raw chaos.
+
+# New monster added by furiosity for the Theme module
+N:843:Giant pink dragonfly
+G:F:u
+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:LOSE_STR:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | NO_CUT |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BR_PLAS
+D:The size of a large bird, this fly seems to scorch the
+D:very air around it.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:845:Aquatic dragonfly
+G:F:B
+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:HURT:1d6
+F:FORCE_SLEEP | RAND_50 | RAND_25 |
+F:WEIRD_MIND | BASH_DOOR | CAN_FLY | WILD_TOO | WILD_SWAMP |
+F:ANIMAL | NO_CUT | AQUATIC | RES_WATE |
+F:MORTAL | BASEANGBAND
+S:1_IN_10 |
+S:BA_WATE
+D:The size of a large bird, this fly prefers watery regions.
+
+# New monster added by furiosity for the Theme module
+N:846:Giant red mouse
+G:r:r
+I:110:1d3:8:4:20
+W:4:1:200:3
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:FIRE:1d2
+F:RAND_25 | SUSCEP_COLD | ANIMAL | IM_FIRE |
+F:DROP_CORPSE | MORTAL | BASEANGBAND | CAN_SWIM |
+S:MULTIPLY
+D:It is about three feet long with large teeth and red fur.
+D:It is a corrupted creature of Melkor.
+
+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 | AQUATIC |
+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_NUKE | 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 worm of power is seldom
+D:encountered in our world. It can crush stars with its might.
+
+# New monster added by furiosity for the Theme module
+N:848:Giant blue mouse
+G:r:b
+I:110:1d3:8:4:20
+W:4:1:200:3
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:ELEC:1d2
+F:RAND_25 | ANIMAL | IM_ELEC |
+F:DROP_CORPSE | MORTAL | BASEANGBAND | CAN_SWIM |
+S:MULTIPLY
+D:It is about three feet long with large teeth and blue fur.
+D:Its teeth produce sparks on impact.
+
+# New monster added by furiosity for the Theme module
+N:849:Giant yellow mouse
+G:r:y
+I:110:1d3:8:4:20
+W:4:1:200:3
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:ACID:1d2
+F:RAND_25 | ANIMAL | IM_ACID |
+F:DROP_CORPSE | MORTAL | BASEANGBAND | CAN_SWIM |
+S:MULTIPLY
+D:It is about three feet long with large teeth and yellow fur.
+D:Its teeth produce a corrosive substance.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:851:Giant pink rat
+G:r:R
+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:LOSE_STR:1d4
+F:RAND_25 | ANIMAL | DROP_SKELETON |
+F:DROP_CORPSE | MORTAL | BASEANGBAND | CAN_SWIM |
+S:MULTIPLY
+D:It is a rodent of unusual size with no fur covering it.
+
+# New monster added by furiosity for the Theme module
+N:852:Giant tree rat
+G:r:G
+I:110:2d3:8:12:20
+W:9:1:250:3
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:POISON:1d4
+B:BITE:HURT:2d4
+F:RAND_25 | ANIMAL | DROP_SKELETON | IM_POIS | CAN_FLY |
+F:DROP_CORPSE | MORTAL | BASEANGBAND | CAN_SWIM |
+S:MULTIPLY
+D:A large rat that lives in the trees.
+
+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 | NEUTRAL | NO_TARGET |
+F:ONLY_ITEM | DROP_4D2 | DROP_GOOD | AURA_COLD |
+F:SMART | OPEN_DOOR | BASH_DOOR | KILL_BODY |
+F:ANIMAL | GOOD | WILD_ONLY | WILD_GRASS | WILD_GRASS |
+F:WILD_WASTE | WILD_WOOD | WILD_SHORE |
+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.
+
+# New monster added by furiosity for the Theme module
+N:854:Polar bear
+G:q:w
+I:110:10d10:10:35:10
+W:9:1:2000:27
+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 | DROP_SKELETON | DROP_CORPSE
+F:ANIMAL | MORTAL | BASEANGBAND | IM_COLD | WILD_TOO |
+D:A huge white bear, accustomed to extremely cold temperatures.
+
+# New monster added by furiosity for the Theme module
+N:855:Blue bear
+G:q:b
+I:110:10d10:10:35:10
+W:9:1:2000:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:2d4
+B:CLAW:HURT:2d4
+B:BITE:HURT:2d6
+F:WEIRD_MIND | BASH_DOOR | FRIENDS | DROP_SKELETON | DROP_CORPSE
+F:ANIMAL | HURT_LITE | IM_COLD |
+F:MORTAL | BASEANGBAND
+D:A huge blue-black bear with thick fur. It can withstand
+D:extremely cold temperatures and usually hunts at night,
+D:and always in packs.
+
+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 | ATTR_MULTI |
+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 offspring of Morgoth and an ogress. 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.
+
+# New monster added by furiosity for the Theme module
+N:857:Old bear
+G:q:W
+I:110:20d10:10:35:255
+W:15:1:2000:25
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:4d4
+B:CLAW:HURT:4d4
+B:BITE:HURT:4d6
+F:WEIRD_MIND | BASH_DOOR | DROP_SKELETON | DROP_CORPSE
+F:ANIMAL | NEUTRAL | NO_TARGET | WILD_ONLY | WILD_WOOD |
+F:WILD_GRASS | WILD_SHORE | MORTAL | BASEANGBAND
+D:An experienced bear whose fur is silvery white. It just
+D:wants to eat its dinner in peace.
+
+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 | AQUATIC |
+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 ancestors of the Thunderlord eagles. She will try to
+D:help you.
+
+# New monster added by furiosity for the Theme module
+N:859:Teddy bear
+G:q:v
+I:110:10d10:10:35:10
+W:9:1:2000:25
+E:0:1:0:2:1:0
+O:0:0:0:0
+F:WEIRD_MIND | BASH_DOOR | NONLIVING | JOKEANGBAND | NEVER_BLOW |
+F:NO_CUT | NO_FEAR | NO_CONF | RES_TELE | NO_STUN | PET |
+S:1_IN_2
+S:CONF | BLINK |
+D:A cute cuddly creature made of a strange soft material. It has
+D:been animated by a jokester wizard, and searches for a child to
+D:play with.
+
+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.
+D:Grond, the mighty Hammer of the Underworld, cries defiance as he strides
+D:towards you to crush you to a 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.
+
+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.
+
+N:865:Dwarven warrior
+G:k: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.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:867:Fire bear
+G:q:R
+I:110:10d10:10:35:10
+W:13:2:2000:30
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d4
+B:CLAW:FIRE:1d4
+B:BITE:HURT:3d6
+F:WEIRD_MIND | BASH_DOOR | FRIENDS | DROP_SKELETON | DROP_CORPSE
+F:ANIMAL | IM_FIRE | SUSCEP_COLD |
+F:MORTAL | BASEANGBAND
+D:A fierce bear trained to withstand the hottest environments.
+
+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:R
+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.
+
+# New monster added by furiosity for the Theme module
+N:873:Aquatic bear
+G:q:B
+I:110:10d10:10:35:10
+W:15:1:2000:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:CLAW:HURT:1d4
+B:CLAW:CONFUSE:1d4
+B:BITE:PARALYZE:1d6
+F:WEIRD_MIND | BASH_DOOR | FRIENDS | DROP_SKELETON | DROP_CORPSE
+F:ANIMAL | RES_WATE | CAN_SWIM | AQUATIC |
+F:MORTAL | BASEANGBAND
+D:A strange bear the prefers to live in the water.
+
+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:The leader of the horsemen of the apocalypse, before you lies Death.
+D:A bony skeleton in a huge dark robe wielding a great scythe, Death rides
+D:a horse of purest black and comes to bring your death.
+
+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.
+
+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:B
+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 | BASEANGBAND
+D:A green-skinned humanoid with a fishtail. Beware - there are rumours
+D:of adventures losing their minds under the fearsome 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:~:R
+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:~:R
+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.
+
+# New monster added by furiosity for the Theme module
+N:886:Swamp naga
+G:n:o
+I:110:11d8:20:40:120
+W:10:3:1800:60
+E:0:0:0:0:1:0
+O:50:0:50:0
+B:CRUSH:HURT:2d10
+B:BITE:POISON:2d4
+F:FEMALE | CAN_SWIM | WILD_TOO | WILD_SHORE |
+F:RAND_25 | DROP_90 | AQUATIC | IM_POIS |
+F:TAKE_ITEM | BASH_DOOR | DROP_CORPSE | IM_ACID |
+F:EVIL | MORTAL | BASEANGBAND
+D:A large orange snake with a woman's torso. She prefers to live
+D:in the swamp and can withstand marshland elements.
+
+# New monster added by furiosity for the Theme module
+N:887:Ocean naga
+G:n:b
+I:110:11d80:20:40:120
+W:30:10:1800:60
+E:0:0:0:0:1:0
+O:50:0:50:0
+B:CRUSH:HURT:4d10
+B:BITE:ACID:3d6
+F:FEMALE | CAN_SWIM | WILD_TOO | WILD_SHORE |
+F:DROP_90 | AQUATIC | WILD_OCEAN | IM_ACID |
+F:TAKE_ITEM | BASH_DOOR | DROP_CORPSE | RES_WATE |
+F:EVIL | MORTAL | BASEANGBAND
+S:1_IN_8
+S:BA_WATE | BO_ICEE |
+D:A large dark blue snake with a woman's torso. She prefers to live
+D:in deep water.
+
+# New monster added by furiosity for the Theme module
+N:888:Snail
+G:w:U
+I:30:12d9:1:5:255
+W:1:2:1:0
+E:0:0:0:0:1:0
+O:0:0:0:0
+B:BITE:HURT:1d1
+F:ANIMAL | EMPTY_MIND | CAN_SWIM | WILD_ONLY | WILD_GRASS |
+F:DROP_CORPSE | MORTAL | BASEANGBAND | NEUTRAL | NO_TARGET |
+D:A harmless shell-bearing gastropod mollusc
+D:crawling slowly on the dungeon floor.
+D:It is a favourite prey of birds.
+
+N:889:Whale
+G:~:D
+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:g
+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:~:o
+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:~:W
+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.
+
+# New monster added by furiosity for the Theme module
+N:900:Brown naga
+G:n:u
+I:110:14d8:20:40:120
+W:30:2:1800:80
+E:0:0:0:0:1:0
+O:50:0:50:0
+B:CRUSH:HURT:2d10
+B:BITE:LOSE_CHR:3d6
+F:FEMALE | CAN_SWIM | WILD_TOO | WILD_SHORE |
+F:RAND_25 | DROP_60 | NO_CUT | WILD_GRASS |
+F:TAKE_ITEM | BASH_DOOR | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND
+S:1_IN_7
+S:BR_SHAR
+D:A large brown snake with a woman's torso. It burrows deep into
+D:the ground.
+
+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:It is said that Morgoth created trolls in mockery of the Ents.
+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 | JOKEANGBAND
+D:The biggest white shark who has ever lived, it is hunting for you now.
+
+# New monster added by furiosity for the Theme module
+N:904:Silver naga
+G:n:W
+I:110:18d11: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:POISON:1d8
+B:BITE:COLD:1d8
+F:FEMALE |
+F:RAND_25 | DROP_60 | DROP_1D2 | DROP_CORPSE
+F:OPEN_DOOR | BASH_DOOR | CAN_SWIM | IM_POIS |
+F:EVIL | MORTAL | BASEANGBAND | IM_COLD |
+S:1_IN_7
+S:BO_ICEE | BO_COLD |
+D:A giant snake-like figure with a woman's torso, partially covered
+D:in silvery scales.
+
+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 | BASEANGBAND |
+F:HAS_LITE | PET | GOOD
+D:An aquatic elf trained in all forms of combat.
+
+N:906:Aquatic elven mage
+G:h:o
+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 | BASEANGBAND |
+F:HAS_LITE | PET | GOOD
+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:~:U
+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:Hatchling 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:~:u
+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 evil
+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.
+
+# New monster added by furiosity for the Theme module
+N:920:Night naga
+G:n:v
+I:110:30d15:20:75:120
+W:25:2:0:60
+E:0:0:0:0:1:0
+O:20:0:80:0
+B:CRUSH:TERRIFY:2d8
+B:CRUSH:HURT:2d8
+B:BITE:BLIND:1d8
+B:BITE:HURT:1d8
+F:FEMALE | CAN_SWIM |
+F:FORCE_SLEEP | CAN_FLY | HURT_LITE |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 | DROP_CORPSE |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR |
+F:EVIL | NO_CONF | NO_SLEEP | BASEANGBAND |
+S:1_IN_4 |
+S:BLIND | DARKNESS
+D:A dark snake-like form with the torso of a beautiful woman, it
+D:prefers to roam in the night.
+
+# New monster added by furiosity for the Theme module
+N:921:Tree naga
+G:n:G
+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 | CAN_SWIM |
+F:ONLY_ITEM | DROP_90 | DROP_2D2 | DROP_CORPSE |
+F:INVISIBLE | OPEN_DOOR | BASH_DOOR | IM_POIS |
+F:EVIL | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:HEAL | BR_POIS | BA_POIS | DARKNESS
+D:A green snake-like form with the torso of a beautiful woman, it
+D:dwells in the trees.
+
+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 hound with a finned tail and large, muscular flippers for hind legs.
+D:It has a rubbery skin instead of fur.
+
+N:924:Gaurrog
+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.
+
+# New monster added by furiosity for the Theme module
+N:925:Adventurer naga
+G:n:U
+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 | NO_STUN | PET |
+F:EVIL | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_4 |
+S:HEAL | BLIND | CONF | DARKNESS
+D:A large brown snake with a woman's torso. She is a fellow
+D:adventurer.
+
+# New monster added by furiosity for the Theme module
+N:926:White mold
+G:m:w
+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:COLD:4d4
+F:NEVER_MOVE | SUSCEP_FIRE |
+F:STUPID | EMPTY_MIND |
+F:IM_COLD | IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange white growth on the dungeon floor; it seems to sparkle
+D:with ice.
+
+# New monster added by furiosity for the Theme module
+N:927:Silver mold
+G:m:W
+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:EAT_LITE:4d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange silvery growth on the dungeon floor; it seems to glow with
+D:a dark light.
+
+N:928:Mathilde
+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 DarkGod 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 | GOOD |
+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 | GOOD |
+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 | GOOD |
+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 | GOOD |
+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 | GOOD |
+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_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:k: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:Apprentice 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 Worm
+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 Worm
+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 | AQUATIC |
+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 | AQUATIC |
+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 | AQUATIC |
+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 | AQUATIC |
+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 | AQUATIC |
+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:w
+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:N: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 | SUSCEP_FIRE |
+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:N: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 | SUSCEP_FIRE |
+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_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 of Power.
+
+N:948:Akhorahil the Blind
+G:N: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 | SUSCEP_FIRE |
+F:COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | 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_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:N: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 | SUSCEP_FIRE |
+F:INVISIBLE | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | 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 |
+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:N: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_COLD | IM_POIS | SUSCEP_FIRE |
+F:HURT_LITE | NO_CONF | NO_SLEEP | BASEANGBAND | NO_CUT
+S:1_IN_3 |
+S:BLIND | HOLD | SCARE | CAUSE_3 |
+S: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:N: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 | SUSCEP_FIRE |
+F:SMART | COLD_BLOOD | OPEN_DOOR | BASH_DOOR | MOVE_BODY |
+F:EVIL | UNDEAD | 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_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:N: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 | SUSCEP_FIRE |
+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:N: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_COLD | IM_POIS | SUSCEP_FIRE |
+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_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:N: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 | SUSCEP_FIRE |
+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 | AQUATIC |
+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 | AQUATIC |
+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 | AQUATIC |
+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 | AQUATIC |
+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 | AQUATIC |
+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:,:r
+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 the Unhappy
+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.
+
+# New monster added by furiosity for the Theme module
+N:962:Pink 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:LOSE_STR:4d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F: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 glow with
+D:an eerie pink aura.
+
+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:0:0:730:0
+E:0:1:1:2:1:1
+O:0:0:0:1
+F:MALE | CAN_SPEAK
+F:FORCE_MAXHP | NEVER_GENE | WILD_ONLY | WILD_TOWN |
+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 who misplaces his sword once in a while.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:975:Tree mold
+G:m:G
+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:POISON:4d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange green growth that likes to live in
+D:the trees.
+
+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: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.
+
+# New monster added by furiosity for the Theme module
+N:979:Blue mold
+G:m:B
+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:HALLU:4d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND |
+F:IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a strange blue growth on the dungeon floor; it seems to glow with
+D:a puzzling aura.
+
+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 | AQUATIC |
+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 | CAN_SWIM | MALE |
+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:Doppleganger
+G:@:w
+I:110:1d1:1:1:1
+W:1:1:0:0
+E:0:0:0:0:0:0
+O:0:0:0:0
+F:AI_ANNOY | NEVER_BLOW | DOPPLEGANGER | JOKEANGBAND | 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_UNIQUE
+D:A woman of mind-shattering beauty, none can match her beauty. She is perfect,
+D:and totaly evil. She loves nothing but herself and her evilness 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...
+
+# New monster added by furiosity for the Theme module
+N:983:Adventurer mold
+G:m:U
+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:HURT:4d4
+B:SPORE:HURT:4d4
+F:NEVER_MOVE |
+F:STUPID | EMPTY_MIND | PET |
+F:IM_FIRE | IM_POIS | CAN_SWIM |
+F:NO_CONF | NO_SLEEP | NO_FEAR | BASEANGBAND | NO_CUT
+D:It is a fellow adventurer, mold though it is.
+
+# New monster added by furiosity for the Theme module
+# Based on novice paladin
+N:984:Gnome paladin
+G:l:w
+I:110:7d8:20:20:20
+W:15:2:900:40
+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 | FRIENDS |
+F:MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_9 |
+S:SCARE | CAUSE_2 | DARKNESS |
+D:A paladin of short stature. He considers you a spy for Morgoth.
+
+N:985:Bandobras Took
+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:Ungorrog
+G:U:g
+I:110:52d10:20:40:80
+W:51:3:2100:2500
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:3d4
+B:STING:POISON:3d4
+B:SPIT:FIRE:3d4
+B:SPIT:ELEC:3d4
+F:FORCE_SLEEP | FORCE_MAXHP | ESCORTS |
+F:ONLY_ITEM | DROP_GREAT | AURA_FIRE | AURA_ELEC |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | NONLIVING
+F:BASEANGBAND
+S:1_IN_9 |
+S:BLIND | CONF | SCARE | HOLD
+S:S_DEMON | BO_COLD | BO_ACID
+D:The most powerful of the spider demons, Morgoth's corrupted
+D:spiders. It looks like a huge bloated spider, with claws
+D:that sparkle with all the elements.
+
+N:993:Faunungol
+G:U:B
+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:STING:POISON:3d4
+B:SPIT:ELEC:3d4
+B:CRUSH:HURT:6d8
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_90 | AURA_ELEC |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL | IM_ELEC |
+F:EVIL | DEMON | IM_FIRE | NO_CONF | NO_SLEEP | NONLIVING |
+F:BASEANGBAND
+S:1_IN_9 |
+S:BLIND | CONF |
+D:One of the spider demons, spawn of Ungoliant. It looks like
+D:a giant bloated spider surrounded by blazing sparks.
+
+N:994:Naurungol
+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:STING:POISON:3d4
+B:SPIT:FIRE:3d4
+B:HIT:LOSE_STR: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 |
+F:BASEANGBAND
+S:1_IN_9 |
+S:BLIND | CONF |
+D:One of the spider demons, spawn of Ungoliant. It looks like
+D:a giant bloated spider glowing with malevolent flames.
+
+N:995:Sererrog
+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 | SCARE |
+S:S_DEMON
+D:A corrupted Maia of female form, whose skin drips with blood.
+D:It has several arms, and fights with three grim daggers.
+
+N:996:Red Balrog
+G:U:R
+I:120:20d100:20:50:80
+W:61:4: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 | SCARE | HASTE |
+S:BR_FIRE |
+S:S_DEMON
+D:The greatest of the demons, potent in both magical might
+D:and sheer battle power. This corrupted Maia's form is
+D:surrounded by roaring 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:Morgulrog
+G:U:o
+I:110:24d100:20:50:80
+W:42:2:0:1000
+E:1:1:1:2:1:1
+O:0:50:50:0
+B:HIT:HURT:3d4
+B:HIT:UN_BONUS:3d4
+B:HIT:UN_POWER:1d5
+F:FORCE_SLEEP | FORCE_MAXHP |
+F:ONLY_ITEM | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | POWERFUL |
+F:EVIL | DEMON | NO_CONF | NO_SLEEP |
+F:IM_FIRE | IM_POIS | IM_ELEC | IM_COLD |
+F:BASEANGBAND
+S:1_IN_9 |
+S:BLIND | CONF | SCARE | HASTE | HEAL | S_KIN
+S:TRAPS | SLOW | HOLD | SHRIEK | BLINK
+D:A twisted elemental spirit, this creature serves the Dark with
+D:perversions of the magics of nature.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# Based on dark elven lord
+N:1001:Gnome lord
+G:l:s
+I:110:8d10:20:20:20
+W:20:2:900:40
+E:1:1:1:2:1:1
+O:0:70:25:0
+B:HIT:HURT:3d8
+B:HIT:HURT:3d5
+F:MALE | EVIL | 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_5 |
+S:HASTE | BLIND | CONF | DARKNESS | MISSILE
+D:A lord among gnomes.
+
+N:1002:Great Worm 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.
+
+# New monster added by furiosity for the Theme module
+# Based on mystic
+N:1003:Gnome mystic
+G:l:o
+I:120:25d10:30:40:5
+W:33:3:1000:400
+E:1:1:1:2:1:1
+O:30:0:60:10
+B:KICK:HURT:8d2
+B:KICK:HURT:8d2
+B:KICK:HURT:8d2
+B:KICK:HURT:8d2
+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_ANIMAL
+D:A mystic of short stature. He is quite skilled at unarmed
+D:combat and even has some control over the forces of nature.
+
+N:1004:Great Worm 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:B
+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.
+
+# New monster added by furiosity for the Theme module
+# Based on Priest
+N:1007:Gnome priest
+G:l:r
+I:110:10d8:20:22:40
+W:15:1:1000:25
+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 priest of short stature. He looks comically solemn.
+
+N:1008:Black midge
+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 an evil relative of the moth, native to marshlands.
+
+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 | JOKEANGBAND | 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.
+
+# New monster added by furiosity for the Theme module
+# Based on Master thief
+N:1011:Gnome rogue
+G:l:b
+I:130:15d10:20:30:40
+W:28:2:1000:350
+E:1:1:1:2:1:1
+O:90:10:0:0
+B:HIT:HURT:1d7
+B:HIT:HURT:2d3
+B:HIT:EAT_GOLD:5d5
+B:HIT:EAT_ITEM:5d6
+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:A little gnome, fast and stealthy. He is eyeing your backpack.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# Based on hardened warrior
+N:1014:Gnome warrior
+G:l:u
+I:110:15d11:20:40:40
+W:22:1:1000:60
+E:1:1:1:2:1:1
+O:0:100:0:0
+B:HIT:HURT:4d3
+B:HIT:HURT:4d3
+F:MALE |
+F:DROP_1D2 |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | DROP_SKELETON | DROP_CORPSE |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE | KILL_BODY |
+D:A warrior of short stature but great ferocity.
+
+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.
+
+# New monster added by furiosity for the Theme module
+# Multiplying gnome mage. Heh.
+N:1016:Wizard leprawn
+G:l:W
+I:90:7d8:20:20:20
+W:14:2:900:50
+E:1:1:1:2:1:1
+O:20:0:80:0
+B:HIT:HURT:1d5
+F:MALE |
+F:FORCE_SLEEP | DROP_60 |
+F:OPEN_DOOR | BASH_DOOR | DROP_SKELETON |
+F:EVIL | MORTAL | BASEANGBAND | HAS_LITE
+S:1_IN_4 |
+S:DARKNESS | BO_COLD |
+S:S_MONSTER
+S:MULTIPLY
+D:A short little gnome, waving his hands in the air.
+
+# New monster added by furiosity for the Theme module
+N:1017:Kender
+G:l:y
+I:115:8d9:20:20:20
+W:15:3:900:40
+E:1:1:1:2:1:1
+O:25:25:25:25
+B:TOUCH:EAT_ITEM:5d5
+B:TOUCH:EAT_ITEM:5d5
+B:TOUCH:EAT_ITEM:5d5
+B:TOUCH:EAT_ITEM:5d5
+F:JOKEANGBAND | WILD_TOO | WILD_GRASS | WILD_WOOD |
+F:MORTAL | GOOD | HAS_LITE | FORCE_SLEEP |
+F:DROP_60 | DROP_GREAT | OPEN_DOOR | RES_TELE |
+D:A funny, smiling humanoid of short stature, with a pony-
+D:tail and an enormous pouch. He is very polite, but also
+D:very curious. So curious, in fact, that the contents of
+D:your backpack are making their way into his pouch!
+
+# New monster added by furiosity for the Theme module
+# Based on Human warrior
+N:1018:Adventurer gnome
+G:l:U
+I:110:9d8:10:10:3
+W:15:3:1000:0
+B:HIT:HURT:5d8
+B:HIT:HURT:5d8
+F:BASEANGBAND | WILD_TOO | WILD_GRASS | WILD_WOOD |
+F:MORTAL | GOOD | HAS_LITE | FORCE_SLEEP | NO_FEAR |
+F:PET | FRIENDS | DROP_60 | OPEN_DOOR | BASH_DOOR |
+D:A fellow adventurer of rather short stature.
+
+# New monster added by furiosity for the Theme module
+N:1019:Tree cat
+G:f:g
+I:120:12d10:40:40:0
+W:12:2:1500:40
+E:0:1:0:2:1:0
+O:0:0:0:0
+B:BITE:HURT:1d8
+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 | MORTAL | BASEANGBAND |
+F:IM_POIS | CAN_FLY |
+D:A large cat that prefers to dwell in the trees and is very
+D:adept at navigating through the thickest forests while it
+D:stalks its prey.
+
+# New monster added by furiosity for the Theme module
+N:1020:Night cat
+G:f:b
+I:120:12d10:40:40:0
+W:15: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 | MORTAL | BASEANGBAND |
+F:EMPTY_MIND | KILL_BODY | HURT_LITE |
+D:A huge dark shape pouncing on you from the darkness, this cat
+D:mostly hunts at night and is extremely good at evading detection.
+
+# New monster added by furiosity for the Theme module
+N:1021:Leopard
+G:f:u
+I:130:12d10:40:40:0
+W:18: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:A large cat with sleek brown spotted fur. It is fast and lithe,
+D:and none escape its notice.
+
+# New monster added by furiosity for the Theme module
+N:1022:Cheshire cat
+G:f:G
+I:120:12d10:40:40:0
+W:20: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 | SMART | PET |
+F:MORTAL | JOKEANGBAND | IM_POIS | WEIRD_MIND | INVISIBLE |
+D:A large cat with a huge smile. In fact, sometimes the smile
+D:is all you see of it.
+
+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:v
+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. The last of its kind,
+D:it guards its treasure jealously.
+
+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, it is 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:Elenwe the Lost
+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 | FEMALE | SPECIAL_GENE
+S:1_IN_4 |
+S:BLIND | CONF | BRAIN_SMASH |
+S:BR_COLD | BO_COLD | BA_NETH | S_UNDEAD | S_DEMON
+D:She was once the wife of Turgon and mother of Idril Celebrindal. She was
+D:lost when the Noldor crossed the Grinding Ice after Feanor and his sons
+D:betrayed Fingolfin's host by burning the white ships of Alqualonde.
+D:Melkor corrupted her spirit and granted her balrog form, and she has
+D:guarded this pass ever since.
+
+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.
+
+# New monster added by furiosity for the Theme module
+N:1036:Sanctimonious-looking preacher
+G:t:w
+I:110:3d3:10:1:255
+W:0:1:1500:0
+E:1:1:1:2:1:1
+O:0:0:0:0
+B:INSULT:*
+F:MALE | RAND_50 | CAN_SPEAK | NEUTRAL | NO_TARGET |
+F:ONLY_GOLD | DROP_60 | DROP_SKELETON | DROP_CORPSE |
+F:OPEN_DOOR | BASH_DOOR | WILD_TOWN | WILD_ONLY |
+F:MORTAL | BASEANGBAND | HAS_LITE | AI_ANNOY |
+D:He thinks you are not pious enough. He will try to
+D:get you to change your ways.
+
+# New monster added by furiosity for the Theme module
+N:1037:Weary-looking traveller
+G:t:v
+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 | NEUTRAL | NO_TARGET |
+F:ONLY_ITEM | DROP_90 | DROP_SKELETON | DROP_CORPSE |
+F:TAKE_ITEM | OPEN_DOOR | BASH_DOOR | WILD_TOWN | WILD_ONLY |
+F:MORTAL | BASEANGBAND | HAS_LITE
+D:He's wearing fancy clothes of foreign make. He looks tired.
+
+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 | CAN_SPEAK |
+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:0:0:730:0
+E:0:1:1:2:1:1
+O:0:0:0:1
+F:FEMALE | CAN_SPEAK | WILD_ONLY | WILD_TOWN |
+F:FORCE_MAXHP | NEVER_GENE
+F:NEVER_MOVE | NEVER_BLOW | GOOD | NO_TARGET
+F:MORTAL | BASEANGBAND | UNIQUE | NEUTRAL | NO_TARGET | NO_DEATH
+D:She seems to seek someone, you may help.
+
+N:1042:Thrain, the King Under the Mountain
+G:k: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_TARGET | 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_NUKE | 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_NUKE | 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!
+
+### Theme monsters, cont. ###
+
+N:1077:Swamp wight
+G:W:o
+I:110:25d10:20:40:10
+W:45:3:0:400
+E:0:0:0:0:0:0
+O:0:50:50:0
+B:HIT:HURT:1d8
+B:HIT:POISON: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_3 | DARKNESS
+D:An evil spirit from the marshlands, related to the mewlips.
+
+N:1078:Knight of the Swan
+# Basically a high-elven ranger with more melee and no distance attacks.
+G:p:w
+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
+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
+D:A stately man dressed in armour emblazoned with a picture of a beautiful
+D:white swan, from Dol Amroth of old.
+
+# Infernal Device -- created for the Orthanc special level in Isengard by Burb Lulls
+# Heavily tweaked by furiosity for the Theme module
+N:1079:Infernal Device
+G:*:R
+I:110:75d20:20:80:50
+W:38:3:7000:1500
+E:0:0:0:0:0:0
+O:0:0:0:0
+B:EXPLODE:HURT:20d20
+B:EXPLODE:HURT:20d20
+B:EXPLODE:HURT:20d20
+B:EXPLODE:HURT:20d20
+F:FORCE_MAXHP | NEVER_MOVE | IM_FIRE | IM_POIS | BASEANGBAND
+F:NO_FEAR | NO_STUN | NO_CUT | NO_SLEEP | NO_CONF | RES_TELE
+F:RES_NETH | AURA_FIRE | STUPID | EMPTY_MIND | NONLIVING
+F:SPECIAL_GENE | FORCE_DEPTH | SUSCEP_COLD
+D:An ensorcelled machine of Saruman's creation, this gnarled
+D:totem of blackened steel defends Orthanc from intruders. \ No newline at end of file
diff --git a/lib/mods/theme/edit/ra_info.txt b/lib/mods/theme/edit/ra_info.txt
new file mode 100644
index 00000000..2220a4f0
--- /dev/null
+++ b/lib/mods/theme/edit/ra_info.txt
@@ -0,0 +1,1981 @@
+# 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
+
+# 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
+C:0:0:0:0
+Z:detect doors and traps
+
+# Magestaves can get % to life - Theme
+N:514
+X:10:1
+T:6:0:255
+W:10:1:45
+C:-40:-40:0:3
+F:LIFE
+
+# Magestaves, armour, lights and jewelry can get ability to store a spell - Theme
+N:515
+X:10:1
+T:6:0:255
+T:30:0:255
+T:31:0:255
+T:32:0:255
+T:33:0:255
+T:34:0:255
+T:35:0:255
+T:36:0:255
+T:37:0:255
+T:38:0:255
+T:39:0:255
+T:40:0:255
+T:45:0:255
+W:5:1:15
+C:0:0:0:0
+F:SPELL_CONTAIN | WIELD_CAST
+
+# High-level soft and hard armour and DSM can get nether immunity - Theme, adapted from FuryMod
+N:516
+X:50:1
+T:36:11:255
+T:37:15:255
+T:38:0:255
+W:30:1:90
+C:0:0:0:0
+F:IM_NETHER
+
+# Light weapons (no broken ones) can gain sentience - Theme, adapted from FuryMod
+N:517
+X:20:1
+T:21:1:3
+T:22:2:4
+T:23:4:10
+T:24:1:1
+T:24:7:7
+W:10:1:60
+C:0:0:0:0
+F:LEVELS
+
+# 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 \ No newline at end of file
diff --git a/lib/mods/theme/edit/re_info.txt b/lib/mods/theme/edit/re_info.txt
new file mode 100644
index 00000000..c1fb01c8
--- /dev/null
+++ b/lib/mods/theme/edit/re_info.txt
@@ -0,0 +1,179 @@
+# 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.
+
+# 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
+
+# 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/mods/theme/edit/readme.txt b/lib/mods/theme/edit/readme.txt
new file mode 100644
index 00000000..4c0ecbe7
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/s_bilbo.map b/lib/mods/theme/edit/s_bilbo.map
new file mode 100644
index 00000000..12c857e2
--- /dev/null
+++ b/lib/mods/theme/edit/s_bilbo.map
@@ -0,0 +1,58 @@
+# Bilbo's Trail level with Thror's map and a few surprises. Copied from Cyclone vault and tweaked to suit the dungeon.
+# You NEED a digger to pass this level, though flight/climbing will do in a pinch.
+# There is no excuse for being on BDw10 without a digger. :-P
+# Map by furiosity <furiosity@gmail.com>
+
+%:special.txt
+
+# Grass
+F:,:89:0
+
+F:+:33:0
+
+# Grass with Thror's Map
+F:1:89:0:0:0:0:209
+
+# Random monster and random object on grass
+F:2:89:0:*13:*15
+
+# Random trap on grass
+F:3:89:0:0:0:0:0:*
+
+### Previous adventurers
+F:a:89:0:0:391
+F:b:89:0:0:392
+F:c:89:0:0:393
+F:d:89:0:0:394
+F:e:89:0:0:395
+F:f:89:0:0:396
+F:g:89:0:0:397
+F:h:89:0:0:398
+
+### Dungeon Design
+
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMX
+D:XMMMM,g*,,,,,,MMM2,,,,,,,,MMM,,3TMMM,,M,3,M,3,M,,,M+,,,,,,,,,,,,,MM,,,,,,,,,,,,,,,,,,,,T,MX
+D:XMM,,,MMM,,,MMM,,,MMM,,,MMM,,,MMM,,M,,M,M,M,M,M,M,,MMMMMMMMMMMMM,,MM,,MMMMMMMMMMMMMMMMMMTMX
+D:XM,,MMM,,,MMM,,3MMM,,,MMM,,,MMM,,,,M,,M,M,M,M,M,M,,,MM,,,*,,,,,MM,3MM,,,,,a,,,,,,,c,,,*M,MX
+D:XMMMM,,,MMM,,,MMM,,,MMM,,,MMM,,,,,,M,,M,M,M,M,M,M,,,,M,a,b,c,,,,M,,,M,,,,,,,,,,3,3,,,,,M,MX
+D:XMM,,,MMM,3,MMM,,,MMM,,,MMM,,,,,,,,M,,M,M,M,M,M,M,,,,M,,33,,,,,MM,,MMMM,,,,,,,,,2,,,,,,M,MX
+D:XM,,MMM,3,MMM,,,MMM,,,MMM,,,,,,,,,,M,,,,M,3,M,3,M,,,MM,,,2,,,MMM,MMM,,MMM,,,,,,3,3,,,,,M,MX
+D:XM,MM,,,MMM,,,MMM,,,MMM,,,,,,,,,MM+MMMMMMMMMMMMMM,,MM,,,,33,MMM,MMMMMM,,MMM,,,,,,,,,,,,M,MX
+D:XM,,3,MMM,,,,,,,,,MMM,,,,,,,,,,MM,,M,,,,,3,,,,TM,,MM,,,,,,MMM,,MMM,,MTM,,,MM,,,,b,,,,,*M,MX
+D:XMMMM,MMMMMMMMMMMMM,,,,,,,3,,,,M,h,M,MMMMMMMMMM,,MM,,,,,MMM,,MMM,,h,,MMM,,,MMMMMMMMMMMMM,MX
+D:XM>1M,,,,,,3,,,,,,MMM,*g,323,,,M,23T,,,,,3h,,MMMMM,,,,MMT,,MMT,,,,*3,,MMM,,+,,,,,,,,,,,+,MX
+D:XMM,MMMMMMMMMMMM,MM,,,,,,,3,,,,M,*,M,MMMMMMMMMM,,MM,,,,,MMM,,MMM,,2,,MMM,,,MMMMMMMMMMMMM,MX
+D:XM,,,,MMM,,,,,,,,,MMM,,,,,,,,,,MM,,M,,,,,3,,,,TM,,MM,,,,,,MMM,,MMM,,MTM,,,MM,,,,e,,,,,*M,MX
+D:XM,MMM,,MMM,,,MMM,,,MMM,,,,,,,,,MM+MMMMMMMMMMMMMM,,MM,,,,33,MMM,MMMMMM,,MMM,,,,3,3,,,,,M,MX
+D:XM,,MMM,,,MMM,,,MMM,,,MMM,,,,,,,,,,M,,,,M,3,M,3,M,,,MM,,,2,,,MMM,MMM,,MMM,,,,,,,2,,,,,,M,MX
+D:XMM,3,MMM,,,MMM,,,MMM,,,MMM,,,,,,,,M,,M,M,M,M,M,M,,,,M,,33,,,,,MM,,MMMM,,,,,,,,3,3,,,,,M,MX
+D:XMMMM,,,MMM,,,MMM,,,MMM,,,MMM,,,,,,M,,M,M,M,M,M,M,,,,M,d,e,f,,,,M,,,+,,,,,,,,,,,,,,,,,,M,MX
+D:XMg3MMM,3,MMM,,,MMM,,,MMM,,,MMM,,,,M,,M,M,M,M,M,M,,,MM,,,*,,,,,MM3,MM,,,,d,,,,,,,f,,,,*M,MX
+D:XMM*,,MMM,,,MMM,,,MMM,,,MMM,,,MMM,,M,,M,M,M,M,M,M,,MMMMMMMMMMMMM,,MM,,MMMMMMMMMMMMMMMMMM,MX
+D:XMMMM2,,,,3,,,MMM,,,,,,,,,MMM,,3MMMM,,M,3,M,3,M,,,M+,,,,,,,,,,,,,MM,,,,,,,,,,,,,,,,,,,,M,MX
+D:XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMX
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+P:22:90 \ No newline at end of file
diff --git a/lib/mods/theme/edit/s_bridge.map b/lib/mods/theme/edit/s_bridge.map
new file mode 100644
index 00000000..dd1367ef
--- /dev/null
+++ b/lib/mods/theme/edit/s_bridge.map
@@ -0,0 +1,104 @@
+# Special level "Bridge Of Khazad-Dum" in Moria (lvl50)
+# Made by Burb Lulls, reworked later for Theme by furiosity <furiosity@gmail.com>
+
+%:special.txt
+
+# chasm
+F:-:87:0
+
+# rubble
+F:;:49:0
+
+# Random monster (up to 5 levels ood) on normal floor
+F:a:1:0:*55
+
+# Random monster (up to 11 levels ood) on normal floor
+F:b:1:0:*61
+
+# Random monster (up to 9 levels ood)
+F:c:1:0:*59
+
+# Random monster (up to 40 levels ood)
+# Random object (up to 20 levels ood
+# These are behind granite walls, only go after them if you really want to!
+F:d:1:0:*90:*70
+
+# Random monster (up to 3 levels ood)
+F:e:1:0:*53
+
+# Random monster (up to 7 levels ood)
+F:f:1:0:*57
+
+### Guardian -- Durin's Bane on normal floor
+F:@:1:0:872
+
+### Escorts (since these can't appear due to the SPECIAL_GENE flag on Durin's bane)
+
+# Mornungol on normal floor
+F:g:1:0:720
+
+# Ungorrog on normal floor
+F:h:1:0:992
+
+# Sererrog on normal floor
+F:i:1:0:995
+
+# Naurungol on normal floor
+F:j:1:0:994
+
+# Faunungol on normal floor
+F:k:1:0:993
+
+# Morgulrog on normal floor
+F:m:1:0:999
+
+# Helcungol on normal floor
+F:n:1:0:648
+
+### Dungeon Design
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X----------------------------------------------------------------------------LLLLLLLLLLLLLL-----------------------------------X
+D:X----------------------------------------------------------------------------LXXXXXXXXXXXXLLLLLLLLLLLLLLLLLLLLLLLLL-----------X
+D:X----------------------------------------------------------------------------LX;........;XXXXXXXXXXXXXXXXXXXXXXXXXL-----------X
+D:X----------------------------------------------------------------------------LX...c...f..........e.....f......c..XL-----------X
+D:X----------------------------------------------------------------------------LX;.........XXXXXXXXXXXXXXXXDDXXXXDDXL-----------X
+D:X----LLLLLLLLLLLLLL--------------------------LLLLL---------------------------LXXXXXXXXX..XLLLLLLLLLLLLLLX..D.cX..XL-----------X
+D:X--LLLXXXXXXXXXXXXLLL----------------------LLXXXXXLL-----------LLLLLLLL------LLLLLLLLLX..XXLLLLLLLLLLLLLXXXXXXXDDXL-----------X
+D:X-LLXXX..........XXXLLL------------------LLXXX..aXXXLLL-----LLLXXXXXXXLL-------------LXX..XXLLLLLLLLLLLLX..D..D..XL-----------X
+D:XLXXX..............XXXLL----------------LXXX;......XXXLL--LLXXXX;.f..XXLLLLLLLLLLLLLLLLXX..XXXXXXXXXXXXLXDDXXXXDDXL-----------X
+D:XLX;.................XXL--LLLLLLLLLLLLLLLX...XXXXa..;XXLLLXXX......X..XXXXXXXXXXXXXXXXXXXX.;XX;......;XLX..Xb.D..XL-----------X
+D:XLXXX................;XL--LXXXXXXXXXXXXXXDDXXXLLXXX.;.XXXXX....XXXXXX.D............;XXXXX...XX...XX...XLXDDXXXXXXXL-----------X
+D:XLLLXXX............X..XL--LX;...........a..a;XLLLLXXX..f;...;XXXLLLLXXX...f.....e...XXXXX..XXX...XX...XLX..D.bD..XL-----------X
+D:X--LLLXXXXX..XXXXXXX..XLLLLX.................XLLLLLLXX..D..XXXLLLLLLLLX......a......D............XX...XLXDDXXXXDDXL-----------X
+D:X----LLLLLXDDXLLLLLX..XXXXXXa................XLLLLLLLXXXX...XLLLLLLLLLX...c.....f...XXXXXXXXXX..bXXc..XLX..Db.X..XL-----------X
+D:X--------LX..XLLLLLX.......D........f........XLLLLLLLLLLXX.cXLLLLLLLLLX;...........;XLLLLLLLLX.XXXXXX.XLXXXXXXXDDXLLLLL-------X
+D:X--LLLLLLLX.eXLLLLLXXXXXXXXX;...............;XLLLLLLLLLLXXXDXLLLLLLLLLXXDXXXXXXXXXXXXLLLLLLLLX..XXXXf.XLX...a....XXXXXLL------X
+D:X--LXXXXXXX..XXXXXXXXXXLLLLXXXXXXXXXDDXXXXXXXXLLLLLLLLLXX..bXLLLLLLLLLX..XLLLLLLLLLLLLLLLLLLLX..eXX...XLX..b..b..D...XXLL-----X
+D:X--LX..e..............XLLLLLLLLXXXXX..XXXXXXLLLLLLLLLLXX...XXLLLLLLLLLX##XLLLLLLLLLLLLLLLLLLLX;......;XLX....e...XX..fXXL-----X
+D:X--LXDDXXXXXXXXXXXXX..XLLLLLLXXX;..f......;XLLLLLLLLLXXc.XXXLLLLLLLXXXX..XXXXLLLLLLLLLLLLLLLLXXXX..XXXXLXXXXXXXXXXXX...XL-----X
+D:X--LX..XLLLLLLLLLLLX.eXLLLLLXX.D.......c...XLLLLLLLXXX..XXLLLLLLLXXX........XXXLXXXXXXXXXXXXXXXXXDDXXXXXXXXXXXLLLLLXX.eXL-----X
+D:X--LX..XLLXXXXXLLLLXDDXLLLLXX.eX;...b.....;XLLLLLLLX..fXXLLLLLLLLX...d.....d..XLX.e..b...e...c...f...c...b...XLLLLXX..XXLL----X
+D:X--LX..XLLXXccXLLLLX..XLLLXX..XXXXXXXXXXXXXXLXXXXXXXDDXXXXXXLLLLLXX.....d....XXLXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXLXXXX..XXXXL----X
+D:X--LX..XLLXX..XLLLLX..XLLXX..XXLLLLLLLLLLLLLLX;....f..f...;XLLLLLLXX;......;XXLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLX;a..f..;XL----X
+D:X--LX..XLLXXDDXXXXXX..XLXX..XXLLLLLLLLLLLLLLLX.............XLLLLLLXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXLLLLXXXX..XXXXL----X
+D:X--LX.aXLLXX..........XLX..XXLLLLLLLLLLLLLLLLX..c...cc..c..XLLLLXXX-------------------------------------XXXLLLLLLX...cXLLL----X
+D:X--LX..XLLXXXXXXXXXXXXXXX..XLLLLLLLLLLLLLLLLLX;...........;XLLLXX-----------------------------------------XXLLLLLXX.;XXL------X
+D:X--LX..XLLLLLLLLLLLLLLLLXDDXLLLLLLLLLLLLLLLLLXXXXXXXXXXXXXXXLLLX-------------------------------------------XLLLLLLX;.XLL------X
+D:X--LX..XXXXXXXXXXXXXXXXXX.eXLLLLLLLLLLLLLLLLLLLLLLLXXXL.l.XXLLLX-------------------------------------------XLLLLLLX.;XL-------X
+D:X--LX;...a.........a.......XLLLLLLLLLLLLLLLLLLLLLXXX.l.X.l.XXLLX----LLLLLLL---------------------..;....XXXXXXXXLLLX;.XL-------X
+D:X--LXXXXXXXXXXXXXDDXXXXXXXXXXLLLLLLLLLLLLLLLLLLLLX.l.XXXXXl.XXLX-LLLLX-X-;-;-X-X-X-;-X-X-X-;-X-X-X-X-X....XXaaXXLLXc;XL-------X
+D:X--LLLLLLLLLLLLLX.fXLLLLLLLLLXXXXXXXLLLLLLLLLLLLLXX.lXXXLXL.lXXXlllLLkX-X-X-X-X-;-X-X-X-;-X-X-;-X-X-X-.....XX..XXLX;.XLLLLLLL-X
+D:X------LLLLLLLLLX..XLLLLLLLLLX..d..XLLLLLLLLLLLLLLXX..lXXXXL.lXXlljjhkmmnn....................c...a........XXX..XXX..XXXXXXXLLX
+D:X------LXXXXXXXXX..XXXXXXXXXXX#####XXLLLLLLLLLLLLLLXXX.lXLXX..l#ljgiikmmmnnn....................f...e......#...........f..cXXLX
+D:X------LX;................;XX;.....;XLLLLLLLLLLXXXXX..lXXLLXL..#lj@ihkkkmnnn..................c...a........#.b..X.XXXXXXXX.fXLX
+D:X------LX....e.........a...XX..c....XLLLLLLLLLLXLl..lXXXLXLXL..#ljgiikmmmnnn....................f...e......#....X.XXXXXXXX.fXLX
+D:X------LX.......f...c......XX...e.f.XLLLLLLLLLLXL..LXXLLXXLXXL.#lljjhkmmnn....................c...a........#...........f..cXXLX
+D:X------LX....c....b....c...DD;.....;XLLLLLLLLLXXXDXXXLLLLLLLXXXXlllLLkX-X-X-X-X-X-;-X-X-X-;-X-X-X-;-X-.....XXX..XXXXXXXXXXXXLLX
+D:X------LX;................;XXXXXXXXXXLLLLLLLLLXLlelLXLLLLLLLLLLX-LLLLX-;-X-X-;-X-X-X-;-X-X-X-;-;-X-X-X....-XX..XXLLLLLLLLLLLL-X
+D:X------LXXXXXXXXXXXXXXXXXXXXLLLLLLLLLLLLLLLLLLXLl>lLXLL------LLX----LLLLLLL---------------------.......----XeeXXLL------------X
+D:X------LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLXLlllLXL--------LX-------------------------------------------XXXXLL-------------X
+D:X-----------------------------LLL-----------LLXXLLLXXL--------LXX-----------------------------------------XXLLLL--------------X
+D:X-------------------------------LL-----------LLXXXXXLL--------LLXXX-------------------------------------XXXLL-----------------X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+### Starting Location
+P:10:10 \ No newline at end of file
diff --git a/lib/mods/theme/edit/s_crypt.map b/lib/mods/theme/edit/s_crypt.map
new file mode 100644
index 00000000..3d6ce71c
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/s_death.map b/lib/mods/theme/edit/s_death.map
new file mode 100644
index 00000000..398b5fd1
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/s_doom.map b/lib/mods/theme/edit/s_doom.map
new file mode 100644
index 00000000..5be3b67d
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/s_factory.map b/lib/mods/theme/edit/s_factory.map
new file mode 100644
index 00000000..0cc3de1b
--- /dev/null
+++ b/lib/mods/theme/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:#:245:0
+
+# Copper Pillar
+F:;:244: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/mods/theme/edit/s_gates.map b/lib/mods/theme/edit/s_gates.map
new file mode 100644
index 00000000..3ac7ccbf
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/s_info.txt b/lib/mods/theme/edit/s_info.txt
new file mode 100644
index 00000000..46c5cd9b
--- /dev/null
+++ b/lib/mods/theme/edit/s_info.txt
@@ -0,0 +1,538 @@
+# 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
+
+################################## 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
+F:RANDOM_GAIN
+
+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
+F:RANDOM_GAIN
+
+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
+
+# 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: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/mods/theme/edit/s_name.map b/lib/mods/theme/edit/s_name.map
new file mode 100644
index 00000000..795d8786
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/s_orc.map b/lib/mods/theme/edit/s_orc.map
new file mode 100644
index 00000000..62852d47
--- /dev/null
+++ b/lib/mods/theme/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 Key of Orthanc
+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/mods/theme/edit/s_orthanc.map b/lib/mods/theme/edit/s_orthanc.map
new file mode 100644
index 00000000..fd14d3d4
--- /dev/null
+++ b/lib/mods/theme/edit/s_orthanc.map
@@ -0,0 +1,99 @@
+# Special level "Orthanc" in Isengard
+# Created by Burb Lulls; reworked for Theme by furiosity <furiosity@gmail.com>
+
+%:special.txt
+
+### Terrain
+
+# Fire
+F:$:205:0
+
+# Copper Pillar
+F:!:244:0
+
+# Dead Tree
+F:;:92:0
+
+# Tainted Water
+F:~:174:0
+
+# Orthanc's Door
+F:[:248:0
+
+# Ash
+F:,:93:0
+
+### Random Monsters (all lvl80)
+
+# Random monster on ash
+F:a:93:0:*40
+
+# Random monster on normal floor
+F:b:1:0:*40
+
+### Guaranteed Monsters
+
+# Ent on ash
+F:&:93:0:708
+
+# Infernal Device on normal floor
+F:2:1:0:1079
+
+# Guardian - Saruman on normal floor
+F:@:1:0:771
+
+### Guaranteed Item
+
+# The Palantir Of Orthanc on normal floor
+F:1:1:0:0:0:0:202
+
+### Dungeon Design
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X;;;;;;;;;;;;;;;;;;;;;;;,,,,,,,,,,a,4,a,,,,,,,,,,,,,,,;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;a;&,a,,,,,~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,X
+D:X;;;;;;;;;;;;;;;;;,,,,,,,,,,,,,,,,,a,a,,,,,,,,,,,,,,,,,,,,,,,,;;;;;;;;;;;;;;;;;;;;;,,,a,a,,,,~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,X
+D:X;;;;;;;;;;;;;;;,,,,,,,,XXXXXXXXXXXXXXXXXXXXXXXXXX,,,,,,,,,,,,,,,,,;;;;;;;;;;;;;,,,,,,,,,,,,~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,X
+D:X;;;;;;;;;;;;;,,,,,,XXXXX2^b^.^.^b^.^.^b^.^.^b^.2XXXXX,,,,,,,,,,,,,,,,,,;;;,,,,,,,,,,,,,,,~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,X
+D:X;;;;;;;;;;,,,,,,XXXX2.^.^.^.XX^.^.^.^.^.^.XX^.^.^.^2XXXX,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,~~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,X
+D:X;;;;;;;a,a,,,,XXX2.^.^.^b^.^XX.^.^.^b^.^.^XX.^.^b^.^.^2XXX,,,,,,,,,,,,,a,a,,,,,,,,,,,~~~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,X
+D:X;;;;;;a,7,a,XXX2^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.2XXX~~~~~,,,,,a,&,a,,,,,,,,,~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,,,,X
+D:X;;;;;;,a,aXXX2.^.^XXXXXXXXXXXXXXXXXXDDDXXXXXXXXXXXXXXXGGGGGXXX~~~~,,,,,a,a,,,,,,,,,~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,,,,,X
+D:X;;;;;;,,XXX2^.^.XXX.^.^.^.^.^.XX^.^.^2^.^.^XX.^b^.^.^XXX.^.^.XXX~~~,,,,,,,,,,,,,,,~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,,,,,,,,,X
+D:X;;;;;,,XX2.^.^bXX^.^b^.^.^.^.^XX.^!^.!^.!^.XX^.^.^.^b^.XX.^b^.^XX~~~~,,,,,,,,,,,,,~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,;;;;;,,,,,,,X
+D:X;;;;;,XX2.^.^.XX^.^.^.GGGG^.^.XX^.^.^.^.^.^XX.^.^GGGG.^.XX.^.^.^XX~~~~~~~~,,,,,,,~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,;;;;;,,,,,,,,,X
+D:X;;;;,,XX.^b^.XX^.^.^.^G$$G.^.^XX.^.^b^.^b^.XX^.^.G$$G^.^.XX.^b^.XX~~~~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,,,,;;;;,,,,,,,,,,,X
+D:X;;;,,XX2^.^.XX^.^.^.^.G$$G^b^.XX^.^.^.^.^.^XX.^b^G$$G.^b^.XX.^.^.XX~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,,,,,,,,;;;;;,,,,,,,,,,,,X
+D:X;;;,,XX^.^.^XX.^.^b^.^GGGG.^.^XX^b!^.!b^!^.XX^.^.GGGG^.^.^XX^.^b^XX.~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,a,a,,,,,,;;;;;;,,,,,,,,,,,,X
+D:X;;,,XX2.^b^XX.^.^.^.^.^.^.^.^.XX^.^.^.^.^.^DD.^.^.^.^.^.^.^XX^.^.^XX,,~~~~~~~~~~~~~~~~~~,,,,,a,a,a,&,a,,,,;;;;;;;;,,,,,,,,,,,X
+D:X;;,,XX^.!.^XX^.^.^.^.^.^.^XXXXXXXXXXXXXXXXXXXXX^.^b^.^.^b^.XX.^!^.XX,,,~~~~~~~~~~~~~~~~~~~,,a,a,&,a,a,,,,,;;;;;;;;;,,,,,,,,,,X
+D:X;,,,XX.^.^.XX.^b^.^.^b^.^XG.^.^.^.^.^.^.^.^.^.XX^.^.^.^.^.^XX^.^b^XX,,,,~~~~~~~~~~~~~~~~~~~a,a,&,&,a,,,,,,;;;;;;;;;;,,,,,,,,,X
+D:X;,,,XX^b!.^XX^.^.^.^.^.^XG.^.lllllll2lllllll.^.XX^.^GGGGG^.GG.^!^.[[,,a,,~~~~~~~~~~~~~~~~~~,a,a,a,a,,,,,,;;;;;;;;;;;;,,,,,,,,X
+D:X;;,,XX.^.^.XX.^.!.^!^.^XG.^.l!^.^.^l^l^.^.^!l.^.XX^.G$$$G.^GG^.^b^[[,,,,,,~~~~~~~~~~~~~~~~~~,a,a,a,,,,,,;;;;;;;;;;;;;;,,,,,,,X
+D:X;;,,XX^.!b^XX^.^.^b^.!.XX^.ll^.^.^.^.^.^.^.^ll.^XX.^G$$$G^.XX.^!^.[[,,,a,,~~~~~~~~~~~~~~~~~~,,,,,,,,,,,;;;;;;;;;;;;;;,,,,,,,,X
+D:X;;,,XX.^.^.XX.^.^2^!^.2GG.^l$$ll.GGG^GGG.ll$$l^.XX^.G$$$G.^XX^.^b^[[,,,,,,~~~~~~~~~~~~~~~~~~~,,,,,,,,,,;;;;;;;;;;;;;,,,,,,,,,X
+D:X;;,,XX^.4.^XX^.^.^.^.^.DD^.ll^.^GG^$@$^GG^.^ll.^XX.^G$$$G^.XX.^.^.[[,,,a,,~~~~~~~~~~~~~~~~~~~,,,,,,,,,;;;;;;;;;;;;;,,,,,,,,,,X
+D:X;;,,XX.^5^.XX.^b^2^.^b^DD.^ll.^.GG^$1$^GG.^.ll^.XX^.G$$$G.^XX^.^b^[[,,,,,~~~~~~~~~~~~~~~~~~~,,,,,,,,,;;;;;;;;;;;;;;,,,,,,,,,,X
+D:X;;,,XX^.^.^XX^.^.^.!.^2GG^.l$$ll2GGG>GGG2ll$$l.^XX.^G$$$G^.XX.^.^.[[,,,a,~~~~~~~~~~~~~~~~~~,,,,,,,,,,,;;;;;;;;;;;;,,,,,,,,,,,X
+D:X;;,,XX.^!^.XX.^.^2^.^!^XX.^ll.^.^.^GGG^.^.^.ll^.XX^.G$$$G.^XX^.!b^[[,,,,~~~~~~~~~~~~~~~~~~,,,,,,,,,,,,;;;;;;;;;;;,,,,,,,,,,,,X
+D:X;,,,XX^b^.^XX^.^.^b^.^.XG^.^l!.^.^.lll.^.^.!l^.^XX.^G$$$G^.GG.^.^.[[,a,~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,;;;;;;;;;;;;,,,,,,,,,,,X
+D:X;;,,XX.^!^.XX.^.!.^!^.^.XG^.^lllllll$lllllll^.^XX.^.GGGGG.^GG^.!b^[[,~~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,;;;;;;;;;;;;;,,,,,,,,,,X
+D:X;;,,XX^.^b^XX^.^.^.^.^.^.XG^.^.^.^.^.^.^.^.^.^XX.^b^.^.^b^.XX.^.^.XX~~~~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,;;;;;;;;;;;;,,,,,,,,,,X
+D:X;;,,XX.^!^.XX.^b^.^.^b^.^.XXXXXXXXXXXXXXXXXXXXX.^.^.^.^.^.^XX^.!b^XX~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,;;;;;;;;;;;,,,,,,,,;X
+D:X;,,,XX2.^.^XX^.^.^.^.^.^.^.^.^XX.^.^.^.^.^.XX6.^.^.^.^.^.^.XX.^.^.XX~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,,,;;;;;;;;;;,,,,,,,,,;X
+D:X;;,,,XX^b^.^XX^.^.^.^.GGGG^.^.XX^.!.^!^.!.^XX7^b^GGGG.^b^.XX.^b^.XX~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,,;;;;;;;;;;;,,,,,,,;;X
+D:X;;;,,XX2^.^.XX.^.^b^.^G$$G.^.^XX.^A2.^.26^.XXA.^.G$$G^.^.^XX^.^.^XX~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,;;;;;;;;;;;,,,,,,;;X
+D:X;;;,,,XX.^.^.XX.^.^.^.G$$G^.^FXX^2^.^C^.^2^XXB^.^G$$G.^.^XX^b^.^XX~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,;;;;;;;;;;;,,,,;;;X
+D:X;;;;,,XX2.^b^.XX.^.^.^GGGG.^.^XX.^52.^.2B^.XXC.^.GGGG^b^XX^.^.^.XX~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,,,;;;;;;;;;;;;;;;X
+D:X;;;;,,,XX2.^.^.XX.^.^b^.^.^b^.XX^.!.^!^.!.^XXE^.^.^.^.^XX^b^.^.XX~~~~~~~~~~~~~~~,,,,,,,~~~~~~~~~~~~~~~~~~,,,,a,a;;;;;;;;;;;;;X
+D:X;;;;,,,,XXX2^.^.XXX^.^.^.^.^.^XX.^.^.^.^.^.XXF.^b^.^.XXX^.^.^XXX~~~~~~~~~,,,,,a,a,,,,,,,~~~~~~~~~~~~~~~~~~,,a,&,a;;;;;;;;;;;;X
+D:X;;;;;,,,,,XXX2^b^.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.^b^.XXX~~~~~~~~~a,a,,,a,&,a,,,,,~~~~~~~~~~~~~~~~~~~~,,a,a,,,,,;;;;;;;;X
+D:X;;;;;;,,,,,,XXX2^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.2XXX~~~~~~~~,,a,&,a,,;a;a,,,,~~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,;;;;;;;X
+D:X;;;;;;;;,,,a,aXXX2.^b^.^b^.^XX.^.^b^.^b^.^XX.^b^.^b^.^2XXX~~~~~~~~,,,,,a,a,;;;;;;,,,~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,,,,,,,,;;X
+D:X;;;;;;;;;;a,&,a,XXXX2.^.^.^.XX^.^.^.^.^.^.XX^.^.^.^2XXXX~~~~~~~~~~~~,,,,,,;;;;;;,,,~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,,,,a,a,X
+D:X;;;;;;;;;;;a,a,,,,,XXXXX2^.^b^.^b^.^b^.^b^.^.^.2XXXXX~~~~~~~~~~~~~~~~~,,,;;;;;,,,,~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,a,&,aX
+D:X;;;;;;;;;;;;;;,,,,,,,,,XXXXXXXXXXXXXXXXXXXXXXXXXX,,~~~~~~~~~~~~~~~~,,,,;;;;;;;;,,,~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,a,a,X
+D:X;;;;;;;;;;;;;;;;;,,,,,,,,,,,,,,,,,a,a,,,,,,,,,,~~~~~~~~~~~~~~~,,,,;;;;;;;;;;;;;,,,~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,,,X
+D:X;;;;;;;;;;;;;;;;;;;;;,,,,,,,,,,,,a,E,a,,,,,~~~~~~~~~~~~~~~~~,,;;;;;;;;;;;;;;;;;,,~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~,X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+### Starting Location
+P:31:125 \ No newline at end of file
diff --git a/lib/mods/theme/edit/s_ship.map b/lib/mods/theme/edit/s_ship.map
new file mode 100644
index 00000000..dbbbe50d
--- /dev/null
+++ b/lib/mods/theme/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:#:246: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/mods/theme/edit/s_smaug.map b/lib/mods/theme/edit/s_smaug.map
new file mode 100644
index 00000000..f1829e6f
--- /dev/null
+++ b/lib/mods/theme/edit/s_smaug.map
@@ -0,0 +1,78 @@
+# Special level "Lower Halls" in Erebor
+# Created for Theme on 29/09/2004 by furiosity (furiosity@gmail.com)
+
+### Level features ###
+
+# Floor with dirt
+F:,:88:3
+
+# Mountain
+F:M:97:3
+F:X:97:3
+
+# Shallow Water
+F:V:84:3
+
+# down staircase
+F:>:7:3
+
+### Guaranteed monsters (on dirt)###
+
+# Creeping adamantite coins
+F:a:88:5:423
+
+# Creeping mithril coins
+F:b:88:5:239
+
+# Creeping gold coins
+F:c:88:5:195
+
+# Creeping silver coins
+F:d:88:5:117
+
+# Smaug on normal floor
+F:e:88:5:697
+
+### Guaranteed items ###
+# Cup of Thror on dirt
+F:1:88:0:0:0:0:211
+
+# The Arkenstone of Thrain on dirt
+F:2:88:0:0:0:0:3
+
+### Dungeon Design
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+D:X,,,,,,MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMX
+D:X,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,X
+D:X,M,M,M,M,M,M,MMM,M,M,M,M,M,M,M,M,M,M,M,M,M,MM,,,,,,,,,X
+D:XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,,,,,,,,,X
+D:XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,,,,,,,MMMMMX
+D:XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMdddddddddMMMMMMMX
+D:XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMddddccccddddMMMMMMMMX
+D:XMMMMMMMMMMMMMMMMMMMMMMMMMMMMMddddcccccccddddMMMMMMMMMMX
+D:XMMMMMMMMMMMMMMMMMMMMMMMMMddddaabbbbbaaaccddddMMMMMMMMMX
+D:XMMMMMMMMMMMMMMMMMMMMMMMddddaaabbbbbbbaaaccddddMMMMMMMMX
+D:XMMMMMMMMMMMMMMMMMMMMMddddcaaabbbaaabbbaaaccddddMMMMMMMX
+D:XMMMMMMMMMMMMMMMMMM1ddddccaaabbbaaaaabbbaaacddddMMMMMMMX
+D:XMMMMMMMMMMMMMMVVVMMMddddccaaabbaaeaabbaaacddddMMMMMMMMX
+D:XMMMMMMMMMMMMVVVMMMMddddccaaabbbaaaaabbbaaaccddddMMMMMMX
+D:XMMMMMMMMMMMMMVVVMMddddccccaaabbbaaabbbaaaccddddMMMMMMMX
+D:XMMMMMMMMMMMMMMVVVMMddddccccaaabbbbbbbaaaccccddddMMMMMMX
+D:XMMMMMMMMMMMMVVVMMMMMMddddcccaaabbbbbaaaccccccddddMMMMMX
+D:XMMMMMMMMMMVVVMMMMMMMMMddddcccccccccccccccccccccddddMMMX
+D:XMMMMMMMMMVVVMMMMMMMMMMMddddccccddddMMdMdMMdMM2Mddd,,,,X
+D:XMMMMMMMMMMVVVMMMMMMMMMMMMMddddddddMMMMMMMMMMMMMM,,,,,,X
+D:XMMMMMMMMMVVVMMMMMMMMMMMMMMddddddMMMMMMMMMMMMMMMMMMM,,,X
+D:XMMMMMMMVVVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,X
+D:XMMMMMMMMMVVVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,,X
+D:XMMMMMMMMMMMVVVVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,X
+D:XMMMMMMMMMMMMMVVVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,,X
+D:XMMMMMMMMMMMMMMMVVVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,X
+D:XMMMMMMMMMMMMMMMMVVVVMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,X
+D:XMMMMMMMMMMMMMMMMMMVVVVMMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,,X
+D:XMMMMMMMMMMMMMMMMMMMMVVVVMMMMMMMMMMMMMMMMMMMMMMMMMMMM,,X
+D:XMMMMMMMMMMMMMMMMMMMMMMMVVVMMMMMMMMMMMMMMMMMMMMMMMMMM,>X
+D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+### Starting Location
+P:3:3 \ No newline at end of file
diff --git a/lib/mods/theme/edit/s_thorin.map b/lib/mods/theme/edit/s_thorin.map
new file mode 100644
index 00000000..52a795e7
--- /dev/null
+++ b/lib/mods/theme/edit/s_thorin.map
@@ -0,0 +1,47 @@
+# Thorin's Trail level with Thorin's key and a bunch of dwarves.
+# Based on Lesser Vault (Cross), tweaked to suit parent dungeon.
+# Map by furiosity <furiosity@gmail.com>
+# Arena-style level (no random treasure except monster drops)
+
+%:special.txt
+
+# Grass with Thorin's key
+F:1:89:0:0:0:0:210
+
+# Grass
+F:,:89:0
+
+# Small tree
+F:t:202:0
+
+### Guaranteed monsters
+
+# Dark dwarven warrior on grass
+F:a:89:0:179
+
+# Dark dwarven smith on grass
+F:b:89:0:180
+
+# Dark dwarven lord on grass
+F:c:89:0:181
+
+# Dark dwarven priest on grass
+F:d:89:0:182
+
+### Dungeon Design
+
+D:XXXXXXXXXXXXXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXX
+D:XXTTc,d,,,,,,,,b,cTTXX
+D:XXTt,d,VXXDXXV,,b,tTXX
+D:XX,,d,VWXa,aXWV,,b,,XX
+D:XX,XXXXXXdcdXXXXXXX,XX
+D:XX,,,,,,Xc1cX,,,,,,,XX
+D:XXXXXXX,XdcdX,XXXXXXXX
+D:XX,,,c,,XbbbXWV,,a,,XX
+D:XXTt,,cVXXXXXV,,a,tTXX
+D:XX>T,,VWX,,,,,,a,cTTXX
+D:XXXXXXXXXX,XXXXXXXXXXX
+D:XXXXXXXXXXXXXXXXXXXXXX
+
+P:13:13 \ No newline at end of file
diff --git a/lib/mods/theme/edit/set_info.txt b/lib/mods/theme/edit/set_info.txt
new file mode 100644
index 00000000..d36ce59c
--- /dev/null
+++ b/lib/mods/theme/edit/set_info.txt
@@ -0,0 +1,250 @@
+# 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.
+
+# N:idx:name
+# D:description
+# P:artifact index:number of item needed:pval
+# F:flags
+
+# The Elven Gifts, took from Oangband
+
+N:0:Elven Gifts
+D:It is one of two 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 one of two 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.
+# Dagger of Samwise
+P:66:3:1
+F:STR | KILL_DRAGON | REGEN | SH_FIRE
+# Dagger of Peregrin
+P:67:3:1
+F:KILL_DEMON | IM_COLD | CON
+# Dagger of Meriadoc
+P:68:3:1
+F:KILL_UNDEAD | SH_ELEC | FLY | DEX
+
+
+# Gothmog's Armoury -- Demonologists' set
+
+N:3:Gothmog's Armoury
+D:It is one of three 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
+
+### New sets added in Theme ###
+
+# Thorin Oakenshield's gear - bards might find this useful :)
+
+N:4:Thorin's Gear
+D:It is one of three items that once belonged to
+D:Thorin Oakenshield, King under the Mountain.
+# The small metal shield of Thorin
+P:30:3:2
+F:CHR | ESP_TROLL
+# The golden harp of Thorin
+P:135:3:4
+F:INT | SUST_INT
+# The mithril helm of Thorin
+P:136:3:4
+F:WIS | SUST_WIS
+
+# Peregrin Took's gear - adapted from T-Plus by Ingeborg S. Norden
+
+N:5:Peregrin's Gear
+D:It is one of two items that once belonged to the hobbit
+D:hero, Peregrin Took.
+# Chain Mail of Peregrin Took
+P:165:2:0
+F:RES_CONF | RES_NEXUS | SUST_STR
+# Elven Cloak of Peregrin Took
+P:184:2:2
+F:DEX | SUST_DEX | CHR | SUST_CHR | SLOW_DIGEST |
+
+# Ghan-buri-Ghan's Garb - suggested by ShinesmanOffWhite in the forums
+
+N:6:Ghan-buri-Ghan's Garb
+D:It is one of two items that once belonged to
+D:the leader of the Druedain.
+# The Filthy Rag of Ghan-Buri-Ghan
+P:189:2:0
+F:IM_COLD | IM_ACID
+# The Cloak of Ghan-buri-Ghan
+P:190:2:0
+F:ESP_ALL
+
+# Requisites of the King of Gondor - suggested by ShinesmanOffWhite in the forums
+
+N:7:The Glory of the King
+D:It is one of three items belonging to the Kings of
+D:Gondor.
+# The Long Sword 'Anduril'
+P:83:3:0
+F:BRAND_ELEC | KILL_DEMON | KILL_DRAGON | SLAY_UNDEAD | IM_FIRE |
+# The Black Banner of Gondor
+P:191:3:0
+F:LITE3
+# The Golden Crown of Gondor
+P:42:3:0
+F:ESP_ALL
+
+# Saruman's stuff - suggested by ShinesmanOffWhite in the forums
+
+N:8:Saruman's Travel Gear
+D:It is one of three items belonging to the Istari wizard Saruman.
+# The Mage Staff of Saruman
+P:192:3:0
+F:REFLECT | FREE_ACT
+# The Robe of Curunir
+P:193:3:0
+F:RES_FIRE | RES_ACID | RES_POIS | RES_DARK | RES_BLIND | RES_SOUND |
+# The Palantir of Orthanc
+P:202:3:0
+F:AUTO_ID
+
+# Heirlooms of the House of Elendil - set suggested by Massimiliano Marangio in the forums
+
+N:9:Elendil's Heirlooms
+D:It is one of three items belonging to the House of Elendil.
+# The Ring of Barahir
+P:8:3:0
+F:RES_LITE | RES_BLIND |
+# The Star of Elendil
+P:2:3:1
+F:STR | INT | WIS | DEX | CON | CHR | LUCK |
+# The Rod of Annuminas
+P:199:3:0
+F:RES_FEAR | ESP_EVIL
+
+# Flame of Wrath - from Oangband
+
+N:10:Flame of Wrath
+D:It is one of two items infused with holy fire.
+# The Amulet of Annatar
+P:4:2:1
+F:WIS | IM_FIRE | RES_FEAR
+# The Morning Star 'Naurgil'
+P:115:2:4
+F:STR | SLAY_EVIL | SLAY_UNDEAD
+
+# Light/Dark Set - from Oangband
+
+N:11:Shadow Ward
+D:It is one of two items rumoured to defy the Shadow.
+# The Soft Leather Armour 'Hithlomir'
+P:168:2:2
+F:SEARCH | RES_BLIND
+# The Set of Leather Gloves 'Cammithrim'
+P:53:2:2
+F:DEX | SUST_DEX
+
+# Eorl/Rohan Set - from Oangband
+
+N:12:Eorl's Arms
+D:It is one of two items that once belonged to Eorl the Young,
+D:valiant hero of the Mark.
+# Lance of Eorlingas
+P:100:2:1
+F:FREE_ACT
+# The Metal Brigandine Armour of the Rohirrim
+P:21:2:0
+F:CON | REGEN
+
+# Gil-Galad's Set - from Oangband
+
+N:13:Gil-Galad's Battle Gear
+D:It is one of three pieces of the battle gear of Gil-Galad,
+D:mighty Elven king of old.
+# The Shield of Deflection of Gil-Galad
+P:169:3:0
+F:RES_FIRE | RES_POIS | SUST_CON
+# The Spear 'Aiglos'
+P:97:3:0
+F:SLAY_DEMON | RES_NETHER
+# The Mithril Helm of Gil-Galad
+P:26:3:0
+#Why *shouldn't* warrior-types get a chance for AUTO_ID without penalties to luck before they kill Morgy?
+F:AUTO_ID
+
+# Dwarven Heritage
+# http://wiki.t-o-m-e.net/IdeaArchive_2fNew_20Item_20Set_3a_20Heritage_20of_20Khazad
+
+N:14:Dwarven Heritage
+D:It is one of three Dwarven items of legend.
+# The Arkenstone of Thrain
+P:3:3:0
+F:SUST_STR | FREE_ACT | SUST_CON
+# Mattock of Nain
+P:174:3:6
+F:CON
+# Lochaber Axe of the Dwarves
+P:105:3:0
+F:IM_ACID | RES_SHARDS | SUST_DEX | SUST_WIS | REFLECT
+
+# Woodland Realm (bow of Legolas, staff of Thranduil, cap of Thranduil)
+
+N:15:Woodland Realm
+D:It is one of three items belonging to the Wood-elves of Mirkwood.
+# The Hard Leather Cap of Thranduil
+P:36:3:1
+F:SUST_INT | SUST_WIS | SPEED
+# The Long Bow of Legolas
+P:224:3:2
+F:SUST_DEX | SPEED
+# The Quarterstaff of Thranduil
+P:74:3:0
+# What can I say. I love archers. :P
+F:AUTO_ID
+
+# Gimli's Gear (Gimli's shield, boots and axe)
+
+N:16:Gimli's Gear
+D:It is one of three items belonging to Gimli the dwarf.
+# The Small Metal Shield of Gimli
+P:132:3:0
+F:SUST_STR | SUST_CON | SUST_INT
+# The Bearded Axe of Gimli
+P:133:3:0
+F:SLAY_EVIL | SLAY_GIANT | BRAND_FIRE
+# The Pair of Metal Shod Boots of Gimli
+P:180:3:5
+F:LUCK
+
+# N:idx:name
+# D:description
+# P:artifact index:number of item needed:pval
+# F:flags
diff --git a/lib/mods/theme/edit/special.txt b/lib/mods/theme/edit/special.txt
new file mode 100644
index 00000000..8d1c94b9
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/spiders.map b/lib/mods/theme/edit/spiders.map
new file mode 100644
index 00000000..146c152c
--- /dev/null
+++ b/lib/mods/theme/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:0:0:0:0:0:0:2
+
+# Grass with Elder aranea
+F:e:89:3:964:0:0:0:0:0:0:2
+
+# 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/mods/theme/edit/st_info.txt b/lib/mods/theme/edit/st_info.txt
new file mode 100644
index 00000000..585dc657
--- /dev/null
+++ b/lib/mods/theme/edit/st_info.txt
@@ -0,0 +1,1139 @@
+# File: st_info.txt
+
+# 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
+
+N:0:General Store
+I:100:& Wooden Torch~
+I:95:& Brass Lantern~
+I:95:& Flask~ of oil
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:90:& Strip~ of Venison
+I:70:& Pint~ of Old Winyards
+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:1:2:3:4
+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:5:6:7:8
+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:9:10:11:12
+G:3:w
+W:24
+
+N:3:Temple
+I:100:& Quarterstaff~
+I:100:& Mace~
+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:13:14:15:16
+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:17:18:19:20
+G:5:b
+W:24
+
+N:5:Magic shop
+I:100:Protection
+I:100:Levitation
+I:100:Protection
+I:100:Charisma
+I:100:Slow Digestion
+T:100:40:7
+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
+T:70:55:22
+A:0:0:1:2:3:4
+O:21:22:23:24
+G:6:r
+W:24
+
+N:6:Black Market
+A:30:0:1:2:3:4
+O:25:26:27:28
+G:7:D
+F:ALL_ITEM | MEDIUM_LEVEL
+W:24
+
+N:7:Home
+A:0:0:54:55:3:62
+O:0:0:0:0
+G:8:y
+W:240
+
+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:29:30:31:32
+G:9:o
+W:24
+
+N:9:Pet Shop
+I:100:Egg
+T:100:70:6
+I:100:& Round Seed-Cake~
+A:0:0:1:2:3:4
+O:33:34:35:36
+G:+:b
+F:MEDIUM_LEVEL
+W:12
+
+#Bree Mayor
+N:10:Mayor's Office
+A:0:0:16:0:35:0
+O:37:37:37:37
+G:+:o
+W:0
+
+#Minas Anor Inn
+N:11:The Crowing Rooster
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:71:71:71:71
+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:78:79:80:81
+G:+:B
+F:RANDOM
+W:2
+
+N:13:Library
+I:100:Identify
+I:100:Identify
+I:100:Identify
+I:100:Identify
+I:100:Identify
+# starting parchment
+T:40:8:20
+# Khuzdul
+T:35:8:105
+# Nandorin
+T:30:8:106
+# Numenorean (I)
+T:30:8:101
+# Numenorean (II)
+T:20:8:102
+# Orcish
+T:15:8:107
+# Advanced Numenorean
+T:10:8:103
+# Advanced Sindarin
+T:5:8:104
+A:2:3:14:15:16:27
+O:86:87:88:89
+G:+:U
+W:2
+
+#Minas Anor
+N:14:Castle
+A:0:0:16:35:39:0
+O:38:38:38:38
+G:+:o
+W:0
+
+N:15:Casino
+A:13:0:9:10:0:12
+O:90:91:92:93
+G:+:s
+W:0
+
+N:16:Beastmaster Shanty
+# Monstrous Compendium 1
+T:100:8:9
+# Monstrous Compendium 2
+T:100:8:10
+# Monstrous Compendium 3
+T:95:8:11
+# Monstrous Compendium 4
+T:90:8:12
+# Monstrous Compendium 5
+T:85:8:13
+# Monstrous Compendium 6
+T:80:8:14
+# Monstrous Compendium 7
+T:75:8:15
+# Monstrous Compendium 8
+T:70:8:16
+# Monstrous Compendium 9
+T:65:8:17
+# Monstrous Compendium 10
+T:60:8:18
+# Monstrous Compendium 11
+T:55:8:19
+A:18:2:21:22:3:0
+O:94:95:96:97
+G:+:g
+W:24
+
+N:17:Fighters Hall
+A:0:0:24:25:0:0
+O:98:99:100:101
+G:+:s
+W:0
+
+N:18:Tower of Magery
+A:0:0:26:27:0:0
+O:102:103:104:105
+G:+:b
+W:0
+
+N:19:Inner Temple
+# Cure Light Insanity
+T:20:72:14
+# Cure Serious Insanity
+T:15:72:15
+# Cure Critical Insanity
+T:10:72:16
+# Cure Insanity
+T:5:72:17
+A:2:3:28:29:0:0
+O:106:107:108:109
+G:+:G
+W:8
+
+N:20:Paladins Guild
+A:0:0:28:25:0:0
+O:110:111:112:113
+G:+:g
+W:0
+
+N:21:Rangers Guild
+A:0:0:31:32:0:0
+O:114:115:116:117
+G:+:u
+W:0
+
+N:22:Thunderlords' Nest
+A:0:0:33:2:34:0
+O:82:83:84:85
+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
+# Cure Light Insanity
+T:20:72:14
+# Cure Serious Insanity
+T:15:72:15
+# Cure Critical Insanity
+T:10:72:16
+# Cure Insanity
+T:5:72:17
+A:2:3:28:29:0:0
+O:55:55:55:55
+G:+:o
+W:8
+
+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
+# starting parchment
+T:40:8:20
+# Khuzdul
+T:35:8:105
+# Nandorin
+T:30:8:106
+# Numenorean (I)
+T:30:8:101
+# Numenorean (II)
+T:20:8:102
+# Orcish
+T:15:8:107
+# Advanced Numenorean
+T:10:8:103
+# Advanced Sindarin
+T:5:8:104
+A:0:0:14:15:16:2
+O:58:58:58:58
+G:+:U
+W:12
+
+N:29:The White Tree
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+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)
+# Cure Light Insanity
+T:20:72:14
+# Cure Serious Insanity
+T:15:72:15
+# Cure Critical Insanity
+T:10:72:16
+# Cure Insanity
+T:5:72:17
+A:2:3:39:0:0:0
+O:61:61:61:61
+G:+:U
+W:8
+
+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
+# Cure Light Insanity
+T:20:72:14
+# Cure Serious Insanity
+T:15:72:15
+# Cure Critical Insanity
+T:10:72:16
+# Cure Insanity
+T:5:72:17
+A:2:3:28:48:0:0
+O:64:64:64:64
+G:+:U
+W:8
+
+N:35:Sea-Dome
+# Cure Light Insanity
+T:20:72:14
+# Cure Serious Insanity
+T:15:72:15
+# Cure Critical Insanity
+T:10:72:16
+# Cure Insanity
+T:5:72:17
+A:2:3:49:35:0:0
+O:65:65:65:65
+G:+:U
+W:8
+
+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
+# Cure Light Insanity
+T:20:72:14
+# Cure Serious Insanity
+T:15:72:15
+# Cure Critical Insanity
+T:10:72:16
+# Cure Insanity
+T:5:72:17
+A:2:3:52:53:0:0
+O:67:67:67:67
+G:+:U
+W:8
+
+# 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:118:119:120:121
+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:122:123:124:125
+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:126:127:128:129
+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:130:131:132:133
+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:134:135:136:137
+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:138:139:140:141
+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:142:143:144:145
+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:146:147:148:149
+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:150:151:152:153
+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:150:151:152:153
+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:154:155:156:157
+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 Cram
+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:158:159:160:161
+G:1:U
+W:12
+F:RANDOM
+
+N:50:Dragon Hunter
+T:100:38:256
+A:0:0:1:2:3:4
+O:162:163:164:165
+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:166:167:168:169
+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:170:171:172:173
+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:174:175:176:177
+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:82:83:84:85
+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:178:179:180:181
+G:3:g
+F:RANDOM | MEDIUM_LEVEL | DEPEND_LEVEL
+F:RARE
+W:24
+
+N:57:The Museum
+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 Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:68:68:68:68
+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:182:183:184:185
+F:MEDIUM_LEVEL
+G:+:s
+W:24
+
+## Library quest in Minas Anor
+
+N:60:Library
+I:100:Identify
+I:100:Identify
+I:100:Identify
+I:100:Identify
+I:100:Identify
+I:100:Identify
+# starting parchment
+T:40:8:20
+# Khuzdul
+T:35:8:105
+# Nandorin
+T:30:8:106
+# Numenorean (I)
+T:30:8:101
+# Numenorean (II)
+T:20:8:102
+# Orcish
+T:15:8:107
+# Advanced Numenorean
+T:10:8:103
+# Advanced Sindarin
+T:5:8:104
+A:61:0:14:15:16:2
+O:210:210:210:210
+G:+:U
+W:12
+
+### New shops in Theme ###
+
+# Hunting Store
+N:61:Hunting Supply Store
+I:100:& Morphic Oil~ of #
+T:50:35:255
+T:100:46:255
+T:100:19:2
+T:100:19:12
+T:20:19:13
+T:100:19:23
+T:20:19:24
+T:50:16:255
+T:50:17:255
+T:50:18:255
+A:0:0:1:2:3:4
+O:186:187:188:189
+G:*:w
+W:24
+
+# Rune Shop
+N:62:Runic Magic Shop
+T:100:104:256
+T:100:104:256
+T:100:104:256
+T:50:105:256
+T:50:105:256
+T:50:105:256
+A:0:0:1:2:3:4
+O:190:191:192:193
+G:6:w
+W:24
+
+# based on Mining Supply store in Khazad-Dum
+N:63:Construction 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:182:183:184:185
+G:1:w
+W:24
+
+# Music store
+N:64:Music Store
+I:20:& Horn~
+I:20:& Drum~
+I:20:& Harp~
+A:0:0:1:2:3:4
+O:194:195:196:197
+G:+:o
+W:12
+
+# Rod shop
+N:65:Magic Rod Market
+T:100:67:50
+T:75:67:75
+T:75:67:75
+T:50:67:100
+T:50:67:100
+T:50:67:100
+T:50:67:100
+T:50:67:125
+T:25:67:160
+T:5:67:200
+A:0:0:1:2:3:4
+O:198:199:200:201
+F:RANDOM | MEDIUM_LEVEL | DEPEND_LEVEL
+G:6:b
+W:12
+
+# Map store
+# No stealing; will buy nothing - this is a map maker, they don't need to buy maps.
+N:66:Map store
+I:100:Map of Bree
+I:100:Map of Hobbiton
+I:50:Map of Lothlorien
+I:50:Map of Edoras
+I:50:Map of Esgaroth
+I:40:Map of Dale
+I:30:Map of Pelargir
+I:30:Map of Osgiliath
+I:30:Map of Minas Anor
+I:15:Map of Forodwaith
+I:5:Map of Middle-earth
+A:0:0:1:2:3:0
+O:202:203:204:205
+G:9:w
+W:12
+
+# Farm
+# Farms buy nothing - anything they might want, they can produce themselves.
+N:67:Farm
+I:100:& Pinch~ of Longbottom Leaf
+I:100:& Jar~ of Honey
+I:100:& Jug~ of Milk
+I:100:Apple Juice
+I:100:Water
+I:100:Salt Water
+I:20:Cure Poison
+I:20:Cure Blindness
+I:20:Cure Fear
+I:20:Cure Confusion
+A:0:0:1:2:3:4
+O:206:207:208:209
+G:1:G
+W:12
+
+### Themed Inns ###
+
+#Pelargir
+N:68:The Grey Swan
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:69:69:69:69
+G:+:w
+W:8
+
+#Caras Galadhon
+N:69:The Garden
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:70:70:70:70
+G:+:w
+W:8
+
+#Khazad Dum
+N:70:The Mithril Lode
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:72:72:72:72
+G:+:w
+W:8
+
+#Dale
+N:71:The Builder Barracks
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:73:73:73:73
+G:+:w
+W:8
+
+#Edoras
+N:72:The Horse and Ox
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:74:74:74:74
+G:+:w
+W:8
+
+#Esgaroth
+N:73:The Dancing Dragon
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:75:75:75:75
+G:+:w
+W:8
+
+#Hobbiton
+N:74:The Green Dragon
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:76:76:76:76
+G:+:w
+W:8
+
+#Osgiliath
+N:75:The Twinkling Star
+I:100:& Ration~ of Cram
+I:100:& Round Seed-Cake~
+I:100:& Strip~ of Venison
+I:100:& Pint~ of Old Winyards
+I:100:& Pint~ of Fine Ale
+A:2:4:5:6:7:0
+O:77:77:77:77
+G:+:w
+W:8
+
+### Abodes ###
+
+N:76:The House of Beorn
+A:17:63:65:0:0:0
+O:40:40:40:40
+G:+:U
+W:0
+
+N:77:Bard's Hut
+A:17:66:0:0:0:0
+O:41:41:41:41
+G:+:U
+W:0
+
+N:78:The Ranger Conclave
+A:17:63:65:0:0:0
+O:42:42:42:42
+G:+:U
+W:0
+
+N:79:Imladris
+A:17:63:65:0:0:0
+O:43:43:43:43
+G:+:U
+W:0
+
+N:80:The Hornburg
+A:17:63:65:0:0:0
+O:44:44:44:44
+G:+:U
+W:0
+
+N:81:Thranduil's Hall
+A:17:63:65:0:0:0
+O:45:45:45:45
+G:+:U
+W:0
+
+N:82:Meduseld
+A:17:0:0:0:0:0
+O:46:46:46:46
+G:+:U
+W:0
+
+N:83:The Master's House
+A:17:0:0:0:0:0
+O:47:47:47:47
+G:+:U
+W:0
+
+N:84:Bag End
+A:17:0:0:0:0:0
+O:48:48:48:48
+G:+:U
+W:0
+
+N:85:The Castle of Stars
+A:17:0:0:0:0:0
+O:49:49:49:49
+G:+:U
+W:0
+
+N:86:The Prince's Tower
+A:17:0:0:0:0:0
+O:50:50:50:50
+G:+:U
+W:0
+
+N:87:The Seat of Durin
+A:17:0:0:0:0:0
+O:39:39:39:39
+G:+:U
+W:0
+
+### The forge in Imladris
+N:88:Forge
+A:23:24:25:32:64:0
+O:43:43:43:43
+G:+:y
+W:0 \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_beorn.txt b/lib/mods/theme/edit/t_beorn.txt
new file mode 100644
index 00000000..e60c5d29
--- /dev/null
+++ b/lib/mods/theme/edit/t_beorn.txt
@@ -0,0 +1,108 @@
+# File: t_beorn.txt
+
+# Beorn's Halls map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain features ###
+
+# Beehive
+F:b:229:3
+
+# Dirt road
+F:,:230:3
+
+# Bush
+F:h:202:3
+
+# Closed gate
+F:g:231:3
+
+# Open gate
+F:+:232:3
+
+# Wooden boards (4 kinds)
+F:=:233:3
+F:[:234:3
+F:_:235:3
+F:]:236:3
+
+### Buildings ###
+
+#The House of Beorn
+F:a:74:3:0:0:0:0:0:76
+
+#The Farm
+F:f:74:3:0:0:0:0:0:67
+
+#The Beastmaster
+F:r:74:3:0:0:0:0:0:16
+
+D:######################################################################################################################################################################################################
+D:# ------TT-TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT--T-TTT------------------ #
+D:#---- ------ ------ ---------------TTTT-TThh-hh-----------------@WW@---------------TTTTT----TTT------------- #
+D:# -------------------- ---TTT---TTTT-TT---------------hhhhhhhWWhhhhhhhhh-----------TTTTT-----TTTT-------- #
+D:# -------- ---- ---------- ---TTTTT---TT-TT--------------hhhhh--@WW@--hhhhhhhhhhhhhhhh------TTT--TTTTTT------ -- #
+D:# --TTT------TT--------------hhh----@WW@---RRRRRRR----hhhhhhhhh----TTT-TTTT---T---- #
+D:#-------- ----------TTT--------------hhh----@WW@---RRRRRRRRR---------hhhhhh---TT-----TTTTT-------- #
+D:#---- -----------------T----TTT--------------hhh-----@WW@---RRNNNNNNNRR-----------hhhh---TTT-----T-------- #
+D:#---- -------- ---TTTTT-TTT--------------hhh------@WW@--ANNNNFFFNNNNA------------hhh----TT------------ #
+D:# ------ -------- -------- -----TTTTT-TT--------------hhh------@WW@--AANNFFFFFFFNNAA------------hhh----TT---TTT---- #
+D:#-- -------- ------ ---T--TTT-TT--------------hhh-------@WW@-AAANFFFEEEFFFNAAA------------hhh----TT-TTTTT---- #
+D:# ------ ---- ---- ---------TTTTT---TT--------------hhh-------@WW@--AAANFFEEEEEFFNAAA-------------hhh----TT-TTT------- #
+D:# ------------ ------ ----------TTT--TTT----------------hhh-----@WW@---AAANFFFEEEFFFNAAA------------hhh--T---TT----TTTT--- #
+D:#------ ---------------------------------TTT---------------hhh-------@WW@---AANNFFFFFFFNNAA------------hhh---TT--TT---TTTTTT-- #
+D:#---- -------- -------- -----------T---TT-----------------hhh-------@WW@---ANNNNFFFNNNNA--------------hhh--TT---TT---TTTT---- #
+D:#-- -------- ------ ---TTTTT-TT----------------hhh--------@WW@----RRNNNNNNNRR----------------hhh--T----hT---------- #
+D:# ------------ --------TTT-TTT ----------------hhh-----N--@WW@----RRRRRRRRR----------------hhh---T-----TTT-TTTT-- #
+D:#---- ---- -------------T--T@b@---------------hhh-----NRN--@WW@----RRRRRRR------------------hhh--TT----TTT-T------ #
+D:# ---------- ------ ----TTTT---TTT@b@---------------hhh---NRRRN--@WW@-----------------------------hhh-T-----TT--TT---- #
+D:# ------ ---- ---TTTTTT--TT--@b@-------------hhh-----NRN--@WW@-----------------------------hhh--TTT----TT---TTT-- #
+D:#---- ------ ---------- ------------TTTT---TTT-@b@--------------hhh-----N----@WW@-------NNN-------EEREE-------hhh--------TT--TTTTT-- #
+D:#-- ---- -------- ---------TTT--@b@------------hhh----------@WW@------NFFFFFN----EERRREE----hhh---TTT----TT---TTT---- #
+D:# ---- ---- -- ------TTTTTT----@b@-------------hhh--------@WW@-----NFFFFFN-------EEREE---hhhh---TTT-----TT------- #
+D:# -------- -- -- ---------TTT---TTT--@b@------------hhh--------@WW@----NFFFFFN---------------hhhh----TTT------TT--TTT-- #
+D:#-- -------- -------- ----TTTT-TTT--@b@-------------hhh------@WW@-------NNN---------UUUYUUUUhh---------------TT-TTTTT-- #
+D:#-- ---- ---- -------------------T-----TTT--@b@------------hhh------@WW@--------------------XXXXXXXXh----------------TT--TTT-- #
+D:# ------ ------ -------TTTTT----TT--@b@-------------hhh------@WW@---UUUYU-----------________h---------TT-----TT-------- #
+D:#-- ---------- ---- ---TTTTT--T-TT--@b@------------hhh------@WW@----XXXXXNNNNNNNNNNN________h----------TT----TTT--TTT---- #
+D:#---- ----T----T-TT--@b@-------------hhh------@WW@---_____,,,,,,,,,,,f]]]]]]]T-T---------TTT--TTT-TTTTT---- #
+D:# ---------- ----TTTT-TTT--@b@------------hhh------@WW@----]]]]]NNNNNNNNNNN----------------TTT------TT----TTT--- #
+D:# ---- ------ -----TTTTTT-TT--@b@-------------hhh-----@WW@-----F,F---------[================[-T---------TT--T---- #
+D:# ------ ---------- ------TTTT---T--@b@------------hhh----TT-@WW@----F,F---------[================[-T----TTT--TT-TTT--- #
+D:# ----------------------------------------TT--@b@-----------hhh-------@WW@-----F,F---------[================[AT----T-T--TT-TTT--- #
+D:#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,g,,,,,,,,,,,,,,,,,,,,,,,,,,WW,,,,,,,,,,,,,,,,,,,,,,,,,,,aUUUUUUUU[,,,A--T----TT--T------ #
+D:#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,+,,,,,,,,,,,,,,,,,,,,,,,,,WW,,,,,,,,,,,,,,,,,,,,,,,,,,,,aUUUUUUUU[AT,,A-TTTT-TT---TTT----- #
+D:#-- --------------------------------------------TT--@b@------------hhh----@WW@-----------,-,,,---[================[-T-,A----T-TT--TTTTT-- #
+D:# -------- ------TT-TTT--@b@-------------hhh--@WW@----------E,E--,,,,-[================[-T-,A-TTTT-TT---TTT---- #
+D:#-- -------- -------- ---TTTT-TT--@b@--------TTT---hhh@WW@----------E,E------,,[================[-T-,A---TT-TT--------- #
+D:#------ ---------- ----------TT--TT--@b@------TTT-----hh@WW@----------E,E--------hA,-----------------A,,A-TT--TTT--TTT-- #
+D:#-- ------ -------- ----TTT-----TT--@b@-------TTT---hhhWW@----------E,E---------hhA,,,A-----------A,,,A-------TT--TTT-- #
+D:# ---- ------ ---------TTTTT---TTT--@b@--------TTT-hhhhWW@---------E,E-----------hh-A,,,,,AAAA,,,,,AA---------TT TTTTT-- #
+D:# ---- ---- -------TT-TT---TTT--@b@-------------hhWW@---------E,E------------hhhh--A,,,,,,,,,A--TTT------TT---TTT-- #
+D:# -------- ------------ ------------T--TTT---@b@-------------hWWh----------R,R------------hhhh--AAAAAAAAA----TTTTT---TT---TTT-- #
+D:# -- -- ------ ----TTTTT-TT---@b@------------@WWhhh---UUUYUUU,,R-------------hhhh--------TT-----TTT---TT-------- #
+D:# ------ -------TTT---TT--@b@-----------@WW@-hhh--XXXXXXX,-,R-------------hhhhh-----TTTTT-----TTT-TT------- #
+D:# ------------ ---------------- ----------TTT--@b@------------@WWhhh---_______,-,,R---------------hhhh-----------------TT--T----- #
+D:# ------------ -------------TTTT---TTT--@b@-----------@WW@-hhh--_______,-,UUYU----------------hhhThTh----------TTT-TTT---- #
+D:# ------ ------TTTTTT---TT--@b@------------@WWhhhh--]]]]0]],-,XXXX--------------------hhhhhh--T-----TT--T----- #
+D:# ------ ---- ---TTTT---TTTT-@b@------------@WW@hhhh-----,,,,-,____---------------FFF------hhh------TT--------- #
+D:#------ ---------- ------ -----------TT--TTT-@b@-------------@WW@hhhh---------,]rr]--------------FVVVVF-----hhh-----T---------- #
+D:# ------ ------ ------ ------TTTT--TT@b@-------------@WW@--hhh---------,,,-----------------FVVVVF----hhhh---TT---TTT--- #
+D:#---- -------------- ------------ ---------TT--TTTb@-------------@WW@----hhh-----------------------------FVVVVF--hhhh----TT--TTTTT-- #
+D:#-- ---------- ---- ----------------TTT-------------@WW@------hhh-------------------N---------FFFF--hhhh----TT----T-T-- #
+D:# ---- -------- -------- ---------TTT-TTTT-----------@WWW@--------hhh----------------ENANE------------hhhh-----TT------- #
+D:# ---------- ---------- -------- ------TTTTT--TT------------@WWW@--------hhh--------------EEAAAEE----------hhhh----TTT------------ #
+D:# ------ ------ ---- ---- ----TTTTT---TT---------@WWW@-----------hhh--------------ENANE----------hhhh----TT------------ #
+D:# -------- ------ ---------TTT-TT---------@WWW@----------hhhh---------------N-----------hhh-----TT------------- #
+D:# ------ ------ ------TTTTT-TT-------@WWW@TTT---------hhhhh-----------------------hh---T-TTTT--TTTTT------ #
+D:# -- ---- -------- -------TTT---TT------@WW@TTT------------hhhhhhh------------hhhhhhhhh------TTT----TTTTT----T-- #
+D:# -------- ------------ ------ -----T---TT----@WW@----------TTT------hhhhhhhhhhhhhhhhhhhhhhh-------TT -TTT--TTT---- #
+D:# ------ -------- ---------- ------ TTTTTT--TT--@WW@-------------------------hhhhhhhhhh--------------hT--TTTTT---- #
+D:#------------ -------------- ------------ TTTTT--TTTTTWW@-------------------------------------------------TT----TTT------------- #
+D:# ---------- ------ ------ ---------- TTT-TTTThTTT------------------------------------------------hTT---------------- #
+D:#------ ------ -------- -------- ---------TTTTThhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhTh--TTTTT--------- #
+D:#------ -------- -------------- ---TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT-------------- #
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_bree.txt b/lib/mods/theme/edit/t_bree.txt
new file mode 100644
index 00000000..e5fe559a
--- /dev/null
+++ b/lib/mods/theme/edit/t_bree.txt
@@ -0,0 +1,137 @@
+# 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
+
+# Music Store
+F:d:74:3:0:0:0:0:0:64
+
+# The Museum
+F:e:74:3:0:0:0:0:0:57
+
+# Map store
+F:f:74:3:0:0:0:0:0:66
+
+# The Library
+F:g:74:3:0:0:0:0:0:13
+
+############### 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--,##f##---.-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.--------XXXXX-OOOO--OOOO---.-StS--,#0##,-ssss---,---.---^^ ^ ^ -- --- OOOO #
+D:#- --- ----- ,,CT--.ss.-sssss--XXXXX,---------OOOO.-sss---,,,,,-####--,---..-T^^ ^ ^ -- --- OO #
+D:# ---- ---- ,,CT--.B#.-SStSS--##g##,------------OO-#z#-ssssss-,,,---,----.--T^^ ^ -,- OOOOO #
+D:# ------- ---- ,,CTT-....-sssss--,,,,,,-------------OO-,--SSSSSt-sss,-,---...-T^^ ^ ^ -, OOOO #
+D:# ------- ------ ,,CCTT---..#2###-,sssss,-SSSSSSSS-----OO,--ssssss-tSS-,.....---^^ ^^ ^^^ -, OOOOOOOO #
+D:# ----- ----- ,,CCT----..,,,,,-StSSS,-ssssssss------OOO-#1##a#-sss...-----T^^ ^^ -,- OOOOO #
+D:# ---- -------- ,,CCTT----..----,sssss,-###d####--------OO,.,,.,-#7#----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/mods/theme/edit/t_cerin.txt b/lib/mods/theme/edit/t_cerin.txt
new file mode 100644
index 00000000..0a87d3bb
--- /dev/null
+++ b/lib/mods/theme/edit/t_cerin.txt
@@ -0,0 +1,98 @@
+# File: t_cerin.txt
+
+# Cerin Amroth map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain features ###
+
+# Mallorn
+F:m:243:3
+
+# Flet
+F:f:220:3
+
+# White tree
+F:w:237:3
+
+# Low hill
+F:h:213:3
+
+# Altars
+F:i:161:3
+F:j:162:3
+F:k:163:3
+F:l:165:3
+F:n:166:3
+F:o:167:3
+F:p:168:3
+F:q:169:3
+
+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:#-----------------------------------------------------------------------------------------NNNNNNNNN--------------------------------------------------------------------------------------------------#
+D:#----------------------------------------------------------------------------------------NNwwwwwwwNN-------------------------------------------------------------------------------------------------#
+D:#---------------------------------------------------------------------------------------NNwwhhhhhwwNN------------------------------------------------------------------------------------------------#
+D:#--------------------------------------------------------------------------------------NNwwhmmmmmhwwNN-----------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------------------------------------------NNwwhmmhhhmmhwwNN----------------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------------------------NNwwhmmh---hmmhwwNN---------------------------------------------------------------------------------------------#
+D:#-----------------------------------------------------------------------------------NNwwhmmh--o--hmmhwwNN--------------------------------------------------------------------------------------------#
+D:#----------------------------------------------------------------------------------ENwwhmmh-i-N-j-hmmhwwNE-------------------------------------------------------------------------------------------#
+D:#----------------------------------------------------------------------------------ENwwhmmhp-NEN-qhmmhwwNE-------------------------------------------------------------------------------------------#
+D:#----------------------------------------------------------------------------------ENwwhmmh-l-N-k-hmmhwwNE-------------------------------------------------------------------------------------------#
+D:#-----------------------------------------------------------------------------------NNwwhmmh--n--hmmhwwNN--------------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------------------------NNwwhmmh---hmmhwwNN---------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------------------------------------------NNwwhmmm@mmmhwwNN----------------------------------------------------------------------------------------------#
+D:#--------------------------------------------------------------------------------------NNwwhmm@mmhwwNN-----------------------------------------------------------------------------------------------#
+D:#---------------------------------------------------------------------------------------NNwwhm@mhwwNN------------------------------------------------------------------------------------------------#
+D:#----------------------------------------------------------------------------------------NNwww@wwwNN-------------------------------------------------------------------------------------------------#
+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:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_beorn.txt b/lib/mods/theme/edit/t_d_beorn.txt
new file mode 100644
index 00000000..75235407
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_beorn.txt
@@ -0,0 +1,75 @@
+# File: t_d_beorn.txt
+
+# Beorn's Halls map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:# HHHHHHDDHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHHDHDDDHHHHHHHHHHHHHHHHHH #
+D:#HHHH HHHHHH HHHHHH HHHHHHHHHHHHHHHDDDDHDDDDHDDHHHHHHHHHHHHHHHHH@%%@HHHHHHHHHHHHHHHDDDDDHHHHDDDHHHHHHHHHHHHH #
+D:# HHHHHHHHHHHHHHHHHHHH HHHDDDHHHDDDDHDDHHHHHHHHHHHHHHHDDDDDDD%%DDDDDDDDDHHHHHHHHHHHDDDDDHHHHHDDDDHHHHHHHH #
+D:# HHHHHHHH HHHH HHHHHHHHHH HHHDDDDDHHHDDHDDHHHHHHHHHHHHHHDDDDDHH@%%@HHDDDDDDDDDDDDDDDDHHHHHHDDDHHDDDDDDHHHHHH HH #
+D:# HHDDDHHHHHHDDHHHHHHHHHHHHHHDDDHHHH@%%@HHH@@@@@@@HHHHDDDDDDDDDHHHHDDDHDDDDHHHDHHHH #
+D:#HHHHHHHH HHHHHHHHHHDDDHHHHHHHHHHHHHHDDDHHHH@%%@HHH@@@@@@@@@HHHHHHHHHDDDDDDHHHDDHHHHHDDDDDHHHHHHHH #
+D:#HHHH HHHHHHHHHHHHHHHHHDHHHHDDDHHHHHHHHHHHHHHDDDHHHHH@%%@HHH@@@@@@@@@@@HHHHHHHHHHHDDDDHHHDDDHHHHHDHHHHHHHH #
+D:#HHHH HHHHHHHH HHHDDDDDHDDDHHHHHHHHHHHHHHDDDHHHHHH@%%@HH@@@@@@@@@@@@@HHHHHHHHHHHHDDDHHHHDDHHHHHHHHHHHH #
+D:# HHHHHH HHHHHHHH HHHHHHHH HHHHHDDDDDHDDHHHHHHHHHHHHHHDDDHHHHHH@%%@HH@@@@@@@@@@@@@@@HHHHHHHHHHHHDDDHHHHDDHHHDDDHHHH #
+D:#HH HHHHHHHH HHHHHH HHHDHHDDDHDDHHHHHHHHHHHHHHDDDHHHHHHH@%%@H@@@@@@@@@@@@@@@@@HHHHHHHHHHHHDDDHHHHDDHDDDDDHHHH #
+D:# HHHHHH HHHH HHHH HHHHHHHHHDDDDDHHHDDHHHHHHHHHHHHHHDDDHHHHHHH@%%@HH@@@@@@@@@@@@@@@@@HHHHHHHHHHHHHDDDHHHHDDHDDDHHHHHHH #
+D:# HHHHHHHHHHHH HHHHHH HHHHHHHHHHDDDHHDDDHHHHHHHHHHHHHHHHDDDHHHHH@%%@HHH@@@@@@@@@@@@@@@@@HHHHHHHHHHHHDDDHHDHHHDDHHHHDDDDHHH #
+D:#HHHHHH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHHHHHHHHHHHHHHHDDDHHHHHHH@%%@HHH@@@@@@@@@@@@@@@HHHHHHHHHHHHDDDHHHDDHHDDHHHDDDDDDHH #
+D:#HHHH HHHHHHHH HHHHHHHH HHHHHHHHHHHDHHHDDHHHHHHHHHHHHHHHHHDDDHHHHHHH@%%@HHH@@@@@@@@@@@@@HHHHHHHHHHHHHHDDDHHDDHHHDDHHHDDDDHHHH #
+D:#HH HHHHHHHH HHHHHH HHHDDDDDHDDHHHHHHHHHHHHHHHHDDDHHHHHHHH@%%@HHHH@@@@@@@@@@@HHHHHHHHHHHHHHHHDDDHHDHHHHDDHHHHHHHHHH #
+D:# HHHHHHHHHHHH HHHHHHHHDDDHDDD HHHHHHHHHHHHHHHHDDDHHHHH@HH@%%@HHHH@@@@@@@@@HHHHHHHHHHHHHHHHDDDHHHDHHHHHDDDHDDDDHH #
+D:#HHHH HHHH HHHHHHHHHHHHHDHHD@H@HHHHHHHHHHHHHHHDDDHHHHH@@@HH@%%@HHHH@@@@@@@HHHHHHHHHHHHHHHHHHDDDHHDDHHHHDDDHDHHHHHH #
+D:# HHHHHHHHHH HHHHHH HHHHDDDDHHHDDD@H@HHHHHHHHHHHHHHHDDDHHH@@@@@HH@%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHDHHHHHDDHHDDHHHH #
+D:# HHHHHH HHHH HHHDDDDDDHHDDHH@H@HHHHHHHHHHHHHDDDHHHHH@@@HH@%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHHDDDHHHHDDHHHDDDHH #
+D:#HHHH HHHHHH HHHHHHHHHH HHHHHHHHHHHHDDDDHHHDDDH@H@HHHHHHHHHHHHHHDDDHHHHH@HHHH@%%@HHHHHHH@@@HHHHHHH@@@@@HHHHHHHDDDHHHHHHHHDDHHDDDDDHH #
+D:#HH HHHH HHHHHHHH HHHHHHHHHDDDHH@H@HHHHHHHHHHHHDDDHHHHHHHHHH@%%@HHHHHH@@@@@@@HHHH@@@@@@@HHHHDDDHHHDDDHHHHDDHHHDDDHHHH #
+D:# HHHH HHHH HH HHHHHHDDDDDDHHHH@H@HHHHHHHHHHHHHDDDHHHHHHHH@%%@HHHHH@@@@@@@HHHHHHH@@@@@HHHDDDDHHHDDDHHHHHDDHHHHHHH #
+D:# HHHHHHHH HH HH HHHHHHHHHDDDHHHDDDHH@H@HHHHHHHHHHHHDDDHHHHHHHH@%%@HHHH@@@@@@@HHHHHHHHHHHHHHHDDDDHHHHDDDHHHHHHDDHHDDDHH #
+D:#HH HHHHHHHH HHHHHHHH HHHHDDDDHDDDHH@H@HHHHHHHHHHHHHDDDHHHHHH@%%@HHHHHHH@@@HHHHHHHHHHHHHHHHHDDHHHHHHHHHHHHHHHDDHDDDDDHH #
+D:#HH HHHH HHHH HHHHHHHHHHHHHHHHHHHDHHHHHDDDHH@H@HHHHHHHHHHHHDDDHHHHHH@%%@HHHHHHHHHHHHHHHHHHHHXXXXXXXXDHHHHHHHHHHHHHHHHDDHHDDDHH #
+D:# HHHHHH HHHHHH HHHHHHHDDDDDHHHHDDHH@H@HHHHHHHHHHHHHDDDHHHHHH@%%@HHHHHHHHHHHHHHHHHHH========DHHHHHHHHHDDHHHHHDDHHHHHHHH #
+D:#HH HHHHHHHHHH HHHH HHHDDDDDHHDHDDHH@H@HHHHHHHHHHHHDDDHHHHHH@%%@HHHHXXXXX@@@@@@@@@@@========DHHHHHHHHHHDDHHHHDDDHHDDDHHHH #
+D:#HHHH HHHHDHHHHDHDDHH@H@HHHHHHHHHHHHHDDDHHHHHH@%%@HHH=====,,,,,,,,,,,,=======DHDHHHHHHHHHDDDHHDDDHDDDDDHHHH #
+D:# HHHHHHHHHH HHHHDDDDHDDDHH@H@HHHHHHHHHHHHDDDHHHHHH@%%@HHHH==,==@@@@@@@@@@@HHHHHHHHHHHHHHHHDDDHHHHHHDDHHHHDDDHHH #
+D:# HHHH HHHHHH HHHHHDDDDDDHDDHH@H@HHHHHHHHHHHHHDDDHHHHH@%%@HHHHH@,@HHHHHHHHH==================HDHHHHHHHHHDDHHDHHHH #
+D:# HHHHHH HHHHHHHHHH HHHHHHDDDDHHHDHH@H@HHHHHHHHHHHHDDDHHHHDDH@%%@HHHH@,@HHHHHHHHH==================HDHHHHDDDHHDDHDDDHHH #
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDHH@H@HHHHHHHHHHHDDDHHHHHHH@%%@HHHHH@,@HHHHHHHHH==================@DHHHHDHDHHDDHDDDHHH #
+D:#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,gg,,,,,,,,,,,,,,,,,,,,,,,,,,%%,,,,,,,,,,,,,,,,,,,,,,,,,,,=HHHHHHHH=,,,@HHDHHHHDDHHDHHHHHH #
+D:#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,++,,,,,,,,,,,,,,,,,,,,,,,,,%%,,,,,,,,,,,,,,,,,,,,,,,,,,,,=HHHHHHHH=@D,,@HDDDDHDDHHHDDDHHHHH #
+D:#HH HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDHH@H@HHHHHHHHHHHHDDDHHHH@%%@HHHHHHHHHHH,H,,,HHH==================HDH,@HHHHDHDDHHDDDDDHH #
+D:# HHHHHHHH HHHHHHDDHDDDHH@H@HHHHHHHHHHHHHDDDHH@%%@HHHHHHHHHH@,@HH,,,,H==================HDH,@HDDDDHDDHHHDDDHHHH #
+D:#HH HHHHHHHH HHHHHHHH HHHDDDDHDDHH@H@HHHHHHHHDDDHHHDDD@%%@HHHHHHHHHH@,@HHHHHH,,==================HDH,@HHHDDHDDHHHHHHHHH #
+D:#HHHHHH HHHHHHHHHH HHHHHHHHHHDDHHDDHH@H@HHHHHHDDDHHHHHDD@%%@HHHHHHHHHH@,@HHHHHHHHD@,HHHHHHHHHHHHHHHHH@,,@HDDHHDDDHHDDDHH #
+D:#HH HHHHHH HHHHHHHH HHHHDDDHHHHHDDHH@H@HHHHHHHDDDHHHDDD%%@HHHHHHHHHH@,@HHHHHHHHHDD@,,,@HHHHHHHHHHH@,,,@HHHHHHHDDHHDDDHH #
+D:# HHHH HHHHHH HHHHHHHHHDDDDDHHHDDDHH@H@HHHHHHHHDDDHDDDD%%@HHHHHHHHH@,@HHHHHHHHHHHDDH@,,,,,@@@@,,,,,@@HHHHHHHHHDD DDDDDHH #
+D:# HHHH HHHH HHHHHHHDDHDDHHHDDDHH@H@HHHHHHHHHHHHHDD%%@HHHHHHHHH@,@HHHHHHHHHHHHDDDDHH@,,,,,,,,,@HHDDDHHHHHHDDHHHDDDHH #
+D:# HHHHHHHH HHHHHHHHHHHH HHHHHHHHHHHHDHHDDDHHH@H@HHHHHHHHHHHHHD%%DHHHHHHHHHH@,@HHHHHHHHHHHHDDDDHH@@@@@@@@@HHHHDDDDDHHHDDHHHDDDHH #
+D:# HH HH HHHHHH HHHHDDDDDHDDHHH@H@HHHHHHHHHHHH@%%DDDHHHHHHHHHH,,@HHHHHHHHHHHHHDDDDHHHHHHHHDDHHHHHDDDHHHDDHHHHHHHH #
+D:# HHHHHH HHHHHHHDDDHHHDDHH@H@HHHHHHHHHHH@%%@HDDDHHXXXXXXX,H,@HHHHHHHHHHHHHDDDDDHHHHHDDDDDHHHHHDDDHDDHHHHHHH #
+D:# HHHHHHHHHHHH HHHHHHHHHHHHHHHH HHHHHHHHHHDDDHH@H@HHHHHHHHHHHH@%%DDDHHH=======,H,,@HHHHHHHHHHHHHHHDDDDHHHHHHHHHHHHHHHHHDDHHDHHHHH #
+D:# HHHHHHHHHHHH HHHHHHHHHHHHHDDDDHHHDDDHH@H@HHHHHHHHHHH@%%@HDDDHH=======,H,HHHHHHHHHHHHHHHHHHHHDDDDDDDHHHHHHHHHHDDDHDDDHHHH #
+D:# HHHHHH HHHHHHDDDDDDHHHDDHH@H@HHHHHHHHHHHH@%%DDDDHH====,==,H,XXXXHHHHHHHHHHHHHHHHHHHHDDDDDDHHDHHHHHDDHHDHHHHH #
+D:# HHHHHH HHHH HHHDDDDHHHDDDDH@H@HHHHHHHHHHHH@%%@DDDDHHHHH,,,,H,====HHHHHHHHHHHHHHH@@@HHHHHHDDDHHHHHHDDHHHHHHHHH #
+D:#HHHHHH HHHHHHHHHH HHHHHH HHHHHHHHHHHDDHHDDDH@H@HHHHHHHHHHHHH@%%@DDDDHHHHHHHHH,=,==HHHHHHHHHHHHHH@VVVV@HHHHHDDDHHHHHDHHHHHHHHHH #
+D:# HHHHHH HHHHHH HHHHHH HHHHHHDDDDHHDD@H@HHHHHHHHHHHHH@%%@HHDDDHHHHHHHHH,,,HHHHHHHHHHHHHHHHH@VVVV@HHHHDDDDHHHDDHHHDDDHHH #
+D:#HHHH HHHHHHHHHHHHHH HHHHHHHHHHHH HHHHHHHHHDDHHDDDH@HHHHHHHHHHHHH@%%@HHHHDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@VVVV@HHDDDDHHHHDDHHDDDDDHH #
+D:#HH HHHHHHHHHH HHHH HHHHHHHHHHHHHHHHDDDHHHHHHHHHHHHH@%%@HHHHHHDDDHHHHHHHHHHHHHHHHHHH@HHHHHHHHH@@@@HHDDDDHHHHDDHHHHDHDHH #
+D:# HHHH HHHHHHHH HHHHHHHH HHHHHHHHHDDDHDDDDHHHHHHHHHHH@%%%@HHHHHHHHDDDHHHHHHHHHHHHHHHH@@@@@HHHHHHHHHHHHDDDDHHHHHDDHHHHHHH #
+D:# HHHHHHHHHH HHHHHHHHHH HHHHHHHH HHHHHHDDDDDHHDDHHHHHHHHHHHH@%%%@HHHHHHHHDDDHHHHHHHHHHHHHH@@@@@@@HHHHHHHHHHDDDDHHHHDDDHHHHHHHHHHHH #
+D:# HHHHHH HHHHHH HHHH HHHH HHHHDDDDDHHHDDHHHHHHHHH@%%%@HHHHHHHHHHHDDDHHHHHHHHHHHHHH@@@@@HHHHHHHHHHDDDDHHHHDDHHHHHHHHHHHH #
+D:# HHHHHHHH HHHHHH HHHHHHHHHDDDHDDHHHHHHHHH@%%%@HHHHHHHHHHDDDDHHHHHHHHHHHHHHH@HHHHHHHHHHHDDDHHHHHDDHHHHHHHHHHHHH #
+D:# HHHHHH HHHHHH HHHHHHDDDDDHDDHHHHHHH@%%%@DDDHHHHHHHHHDDDDDHHHHHHHHHHHHHHHHHHHHHHHDDHHHDHDDDDHHDDDDDHHHHHH #
+D:# HH HHHH HHHHHHHH HHHHHHHDDDHHHDDHHHHHH@%%@DDDHHHHHHHHHHHHDDDDDDDHHHHHHHHHHHHDDDDDDDDDHHHHHHDDDHHHHDDDDDHHHHDHH #
+D:# HHHHHHHH HHHHHHHHHHHH HHHHHH HHHHHDHHHDDHHHH@%%@HHHHHHHHHHDDDHHHHHHDDDDDDDDDDDDDDDDDDDDDDDHHHHHHHDD HDDDHHDDDHHHH #
+D:# HHHHHH HHHHHHHH HHHHHHHHHH HHHHHH DDDDDDHHDDHH@%%@HHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDHHHHHHHHHHHHHHDDHHDDDDDHHHH #
+D:#HHHHHHHHHHHH HHHHHHHHHHHHHH HHHHHHHHHHHH DDDDDHHDDDDD%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDHHHHDDDHHHHHHHHHHHHH #
+D:# HHHHHHHHHH HHHHHH HHHHHH HHHHHHHHHH DDDHDDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHHHHHHHHHHHHHHHH #
+D:#HHHHHH HHHHHH HHHHHHHH HHHHHHHH HHHHHHHHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHHDDDDDHHHHHHHHH #
+D:#HHHHHH HHHHHHHH HHHHHHHHHHHHHH HHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHHHHHHHHHHHHHH #
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_bree.txt b/lib/mods/theme/edit/t_d_bree.txt
new file mode 100644
index 00000000..92fde41f
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_bree.txt
@@ -0,0 +1,91 @@
+# File: t_d_bree.txt
+#
+# Destroyed Bree
+
+# original town by someone else
+# screwing up by fearoffours (fearoffours@moppy.co.uk)
+#
+# Created for ToME
+
+# Permanent rubble
+F:R:206:3
+
+############### Town Layout ###############
+
+D:###########################################################################################################=##########################################################################################
+D:#DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD=DDDDDDDDDDDDDDD VVDDDDDDDDDDDDDD--DDDDDDDD==DDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:# DDD DDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD VDDDDDDDDDDDDDD--DDDDDDDDDDDDDDDDD D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDDDDDDDDDDDD D D DDDDDDDDDDDDDDDDDDDDDDDDDD DD D D D DDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDVVDDDDDDD --DDDDDDD====DDDDD DDDDDDDDDDDDDD DDD DD DDDDDDDDDDDDDDDD #
+D:#DDDDDDD D DDDDDDDDDDDDDDDDD DDDDDDDD DD D HHDD D D DDDD D D D D D D DDHHDDDD=DDDDDDDDDDDDDDDDDDDDDDDVV===DDDDD --DDDDDHHHDDDDDD =DDDD=DDDD===DDDDDDDDDDDDDDDDDDDD DDDDDDDDDDD#
+D:# DDDD DDD DD DDDDDDDDDDDDDDDDD D DD DDDDDDDDD D D DDDDDDDDDDDDDDDDDDDDDDDDDHHHDDDDDDDDDDDD VVDDDDDDD --DDDDDDDDDDDDDDDDDDDDDDHHH DDDDDDDDDDDDDDHHHDDDDDDD=DDDDDDD #
+D:#OOOODDDDDDD D D DDDDDDDD D DDDDDDDDD D DD D DDDDDDDDDDDDD D DDDDDDDDDDDDDDDHHDDDDDDD DDDDDDDDDDDDDDD VDDDDDDD --DDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD===HDDDDDDDDDDDD #
+D:# OOOODDDDDDD DDDDDDDDDDDDDDDDDDDDDDD HH DDDDDDDDD HDD DDDDDDDDDDDDD D DDDDDHHHHHHDDDDDDDDDDDDHHHDDDDDDDDDDDDDDD VDDDDDDD --DDDDDDDDDDDD DDDDDHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD== #
+D:#--- OOOOODDDDDDDDDDDDDDDDD DDDDDDDD D DD D HHHDDHHDDDDDDDDD D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD==DDDD VVV ---DDDDDDDDDDDD DDDDDDDDD VVDDDDDHDDDDDDDDDDDDDDDDD #
+D:#------ OOOOOO DD D D DDDDDDDDDD DDD DDDDDDDDDDDHHHH = D DDDDDDDDDDDDDDDDDDDDHHHDDDDDDDDDDDDDDDDDDDDDDDD V --DDDDDDDDDDDDD DDDDHD VVVVDDDDDDDDD== DDDDDDDDDDD #
+D:#---------- OO DDD HDHHHDDD DDDDDDDDDDDDDDDDD D D DDDDHHHDDDDDDDDDDDDD D HDDDDDDD =DDDDDDDDDDDDDD===DDDDDDDDDDDDD V -- DDDDDDDDDDD DD VVWWVVVDDDDDHHH DDDDDDDDDDDDDD #
+D:#-------------- OO DDDDDDDDDDDDDDDDD H D D D DDDDDHHHDDDDDDDDDDDDDD DDD D DDDDDDDDDDDDDDDDDDDDDDH==HDDHHDDDDDDDDDDVVV --DDDDDDDDHHHHDDDDDD DDD VVVWWWWWVVVDDDDDDDDDDDDDDDDDDDDDDD#
+D:#------D-------- OOODDDDDDD D DDDDHHHDDD HHD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDD =DDDDDDDDDDDDDDDD VVV --DDDDDDDDDDDDDDDD VVWWWWWWWWWVVDDDDDDDDDDDDDDDD #
+D:#----------------- OO H DDDDDDDDDDDDDDDDD HHH D DDDD DD DDDDDDDDDDDDDDDDDDDDD DDDDDD DDDDDDDDDDDDDDDDHDDDDDDDDDDDDDDDDDD VVVVVVVDDDDDDDDDDDDDDDD VVWWWWWWWWWWWVDDDDDD DDDDDDDD #
+D:#------------------- OOOOO DD DDDDDDDDDD DDDDDDDD D DDDDDDDDDDD DDD DDD DD DDD DDDDD DDDDDDDDDDDDDDDDD=DDDDDDDDDDDDDDDD--VVVV VVVVVDDDDDDD VVWWWWWWWWWWWVVDDDDDDDDD DDDDD #
+D:###------------------ OOH D HH DDD DDDDDDDDD DDDDDDDDDDDDDDDDD DDDD DDD ,,,,,,,,,,,,,,,HH,DDDDDDDDDDDDDDDDDDDDD==DDDDDDDDD -- VVVVV VV VVVVVVVWWWWWWWVVVDDDDDDD DDDDDDDD #
+D:#CC####------------------ OOODDDDDDD DDD DDDDDDDDD DD D DDDD DD DDDD,,,,CCCCCCCCCCCCCCCCCC,,,,,,,H,DDDDDDDDDDDDDDDDDDDDDDD --DDDDDDD VVVVVV VVVVWWVVVDDDDDDDDDDDDDDDDDDDDDDD #
+D:#TTCCCC###----D----------,, OO DDDDDDDDDDDDDDD=DDDDDDDDDDDDDDDDDDDDDDD DD ,,,CCCCCCTTTDDTTTDD TTDDTCCCCCCCCCC,,,,DDDDDDD=DDDDD==DDDDDDDD --DDDDDDDDDDDDDDDDDDDDDDDVVVVDDDDDD===HHHDDDD=DDDDDD #
+D:#--TTTTCCC###---------,,,-- OO = DDD D DDDDDDDD DD D DDD DDDD,CCCCTTTTTD---$----------TT T TDDDTCCCCC,,,,,DDDDDDDDDDDDDD H ^--DDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:#------TTTCCC##-----,,------ ODDDDDDDDDDDDDDDD DDDDDDDDD DDDDDDDD DD ,,CCCTTDDD....$$$........D.----H-----TDDTTCCCCCC,,DDDDDDDDDDDDDD^^^ --DDDDDDDDDDDDD==DD DDDDDDDDDD DDDDDDDDDDDDDDDDDD #
+D:#---------TTTCC##,,,-------- OO D DDHHHHDDDDDD DDDDDDDDDD D DD ,CCC TD....--ssss-sss-D.--H.......H..--$--TTDDDTCCC,,,DDDDDDD ^^ --DDDHHHD DDDDDDDD DDDDDDDDDDDDDDD DDDDD =DDDDDDD #
+D:#------------TC,,###- --- OODDDDDDD DDDD H =DDDDDDD DD D,CCTTT.DDH=---S.S=$S$S#-.------DD-$,,.....------TDDDCCC,,DDDDDDD^^ --DDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDD=DDDDDDDD #
+D:#-------------,TTCCC### -- OOO DDD DDD DDDD D D DD DD ,,CCTD...=,,,,H-ssss.ss-...-ss=s--sssss,---...H..---TDDDCCT,H ^^DDDDDDD--DDDDDDDDDDDD DDDDD DDDDDDDDDDDD==DDDDDDDDDDD #
+D:#--------- ,,,--TTTCCC -- OODDDDDDDDDDDHDDDDDDDDDDDDDDDDDD D ,,CCCTT..-------,,#.,##,-#-.--sss.s$$$ssss,--$HH--..$----TDCCTT, ^^^DDDDDDD --DDDDDDDDDDDD===DDDDDD DDDDDDDDDDDDDDDDDDDDDDDD #
+D:#-------- ,, ,,----TTTCCDDDDDDD O =DDDDDDDDD DDDDDHHDDDDD DDDDCCC D=.H-sssSss$S,,,------.--S.SSSS=SS$$--,----DD.-....--=DCCTT,^^^DDDDDDD --DDDD=HHHDDDD DDDDDD DDDDDDDDDD=DDDDDDDDDDDDDDD#
+D:#------ ,, ,DDDDDDDDDDDDDDDD OO DDDDDDDDDD DHDDDDDDDD DDD,CCTDT.=.---ss$sSSS---,,,--H-.--ss,s$s=sss=s--,-$-.==-$--..--TDCCT^^^^ ^^DDDDDDD --=DDDDDDDDDDDDDD DD DD =DDDDDDDDDDDDDDDD #
+D:#------- ,, ,,DDDDDDDDHHHDDDDD OODDDDDDDDDDDDDHDDDDDDDDDDD D ,CCTT...-----s,,-sss#-,,-,DD-$.--.ssssHsss,s#--,-...-------..-==TC^^^ ^DDDDDDD ----DDDDDDDDDDDDDDDDDDDD DDDDDD DDDDDDDD #
+D:#------- {,DDDDDDDDDDDDDDDD = OODDDDDDD DDDDDDDDDD DDD,CTT..-DD,H,,####$###$,-$-,,--.--#####=.#$#H----..-DD---H---$.--D^^^^ ^DDDDDDD--- --DDDDDDDDDDDDDDDDDD DDDDDD DDDD #
+D:#-=-------DDDDDDD DDDDDHDDDDDDDDDDDOOO D DDDDDDDD DDD D ,CT..,,,,----,,,,,#,,------,H-$--#.-#.##.##$-...$,---.sss----.--T^^ ^DDDDDDD -- --DDDDDDD HDDDDDDDDDDDDDDDDDDDDDDD===DDDDDD#
+D:#----------DDDDDDDDDDDDDDDDDDDDDDD OO DDDDDDDD DDD,CCT.,ss$sss#----,$---DD---#-,==-----.----#....-,--,,sStSSS--$.-D^^^ ^DDDDDDD -- -----DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD#
+D:#-------------DDDDDDD HDDDDDDD = OODDDDDDDDDDDDDDDD D ,CCTT.-StS,$S----,-----$s$sH,--..-=H-----....--,,,$,,-.s.sss#--.-D^^^ ^ --DDDDDDD --DDDDDDD ,,DDDDDDDDDD DDDD #
+D:#----------=DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDOOOOO DD D ,CTT-.--s=s,ss#-$,-----S$SSSS--.------$-..==,,,-sssss-,##==#---.-T^^ ^DDDDDDD --DDDD==D -----DDDDDDD ,,DDDDDDDDDDDDDDDDDDDDDDD #
+D:#--------DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD OO D DD D DD,CCT-..-#####,#---HH---$sss$s,-...$...H.$--$XXX,-$ss---,------..=-^^^ ^ ^^ ^ ---DDDDDDD D---D ,,, HDDDDDDDDDDDDDDDD #
+D:#- ----DDDDDDD =DHHDDDDDDDDDDDDD =DDDDDDD OOOOOOOO$ DD $D ,C OOOO--OOOOO----,----######-.---.,----,-UUUUs-SSS$--,--H--..---^^ ^ ^ H ---DDDDDHHDDDDDDDDD -----,DDDDDDD OOOOOOOOOOOOOO#
+D:#- ----DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDOOOOOOO==,CT-OssOOOO---OOOOOOOOO--OOOO--.-D-.-s$s--,XXXX,-=sss-$-,---$$-=D^^DDDDDDDDDDDDDD--DDDDDDDHHHDDDDDD --DDDDDDD OOHDDDDDDD #
+D:#- -----DDDDDDDDDDDDHHDD =DDDDD DDDDDDDDDDDDDDDD OOOOOOOOSt.---------XXX$-OOOO--OOOO---.SStS--,####,--s$s-=-,DD-.--=^^ ^ ^ --DDDDDDDDDDHHHDDDDDDDDDD--- OOOO HDDDDDDD #
+D:#- --- -----DDDDDDHHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,,CT--.ss.-$sHs$---$XXs,---------OOOO.-s$s---,,,,,-###.--,---..-T^^ ^ ^DDDDDDD --DDDDDDDHDDDDDDDDDDDDDDDD --- OOHDDDDDDDDDDDDDDDD #
+D:# ---D ----DDDDDDDDDDDDDDDDDDDDDDD = =DDDDDDD ,,CT--.s#.-SSt,S-HH##H#,---H-$------OO-#$#-ss,,ss-,,,---,---D.--DH^ ^DDDDDDD -,-DDDDDDDDDDDDDDDDDDDDDDD OOOOODDDDHHHDDDDDDDDD #
+D:# ------- ---- HDDDDDDDDDDDDDDDDDDD DDDDDDDDDDD ,,CTT-..$$-=sss$--,,,,,,---------DDH-OO-,--SSS$St-ss-,-$$--...-T^^ ^ ^DDDDDDD -,DDDDDDD =DDDDDDDDDDDDDDOOOODDDDDDDDDDDDHD DDDDDDD#
+D:# ------- ------DDDDDDDDD DD DDDDDDDDDDD DDDDDDDDDD ,,CCTD---.$#,#$,-,sss,,S-S,SSSSS--$---OO,--s-ss----S--,.....---^^ ^^ ^^^ -,DDDDDDDDDDDDDDDD OOOOOOOODDDDDDDDDDDDD DDDDDDDDD #
+D:# -----DDDDDDD -----DDDDDDDDDDDDDDDD =DDDDDDDDDDDDDDDD ,,CCT----..,,,,,-St$SS,-s,s,s$ss--==--OOO-#,##-$-ss....-==--T^^DDDDDDD ^^ -,-DDDDDDDDDDDDDD OOOOODDDDDDDDDDDDDDDDDDD DDDDDDDDDDD #
+D:# ---- --------DDDDDDDDDDDDDDDDDDDDDDHHDDDDDDDD H ,,CCTD--DD $--$$sss$,-##,##,#=--------OO,.,,.,-##.----DDDT^^ ^ ^^DDDDDDD-,DDDDDDD OOOOODDDDDDD DDDDD HDDDDDDDDD===DDDD#
+D:# ---------------DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD D ,,,CCTTTT--..---,##,###,--,,,--HH-----...OOOOOOOOOOOTTDDDCC^^^ ^^ ^^DDDDDDD ,-DDDDDDD OOODDDDDDDDDDDDDDDDDDDDDDDHHDDDDDDDDDDDDDD #
+D:#DDDDDDD --------DDDDDDDD DDDDDDDDDDDDDDHHDDDDDDDDDDDDDDDDDDH,,CCCC --.DD.....HH,-,==--,--==..H..--H----TDDTTOCCCCC,,^^^^^^^^^=^DDDDDDD -.- OOOOODDDDDD DDDDDDDDDDDDDDDDHHHDDDDDDDDDDDD #
+D:# ----------DDDDDDDDDDDDDD = HDDDDDHHHDDDDDDDDDDDDDDD ,, ,,CCCTT-------D--.....$....H.-------TTTDDTTCCCCO,,,,, ^^^^^^^DDDDDDD -.- ODDDDDDDDDDDDDDDDDDHHDD DDDDDDDDDDDDDDDDDDDD #
+D:#DDDDDDD -----D-DDDDDDDDDDDDDDDDDDDDDDDDDDDHHDDDDDDDDDDDDDDDDD ,, ,,CCTTT TTDTTDD-D--...------DTTTDDDCCCCCC,,,,OOOHDDDDDDDDDDDDDDDD -.- OOOODDDDDDDDDDDDD DDHHHDDDDDDDDD DDDDDD D #
+D:#DDDDDDD ----DDDDDDDDDDDDDDDDHHDDDDDDDDDDDDDHHH =DDDDDDD ,,, ,CCCCCCCCCCTTDDT--==-TTTTDTCCCCCC,,,,,, OOODDDDDDDDDDDDDDDD OOOOOOOOODDDDDDDD DDDDDDDDDDDDDDDDHHDDDDDDDDDDDDDDDDDDDD #
+D:# ^^ ----DDDDDDDDDDDDDDDDDDDDDDDDDDDHDDDDDDDDDDDDDDDDDDD ,, ,H,,,,,,,,CCCCCTTTDDTCCCCC,,,,,,DDDDDDDDDDDDDDOODDDDDDD DDDDDDDDDOOO--DDDDDDDDDDDDDDDDDDDDDDDDDDDDHDDDDDD DDDDD DHHDDDDDDDD #
+D:# ^^^^^DDDDDDDDDDDDDDDD DDDDDDDDDDDD DDDDDDDDDDDDDDDDD ,,,DDDDDDDDDDDDDD,,,,,CCCCCC,,,,, HDDDDDDD D OOOODDDDDDD OOO--DDDDDDDHHHDDDDDDDDDD DDDHHDDDD DDDDDDDDDDDD #
+D:# ^^^^^^^^^DDDDDDDDDDHHHHDDDDDDDDDDDHD DDDHHHDDDDDDDDDDDDDD ,,DDDDDDDDDDDDDDDD ,,,,,,DDDDDDDDDDDDDDDDDDDDDDD OOOOOOO OOO---DDDDDDDDDDDDDDDD HDDDDDDDDDDDDDD DDDDDDDDDDDDD #
+D:# ^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,,DDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDOOOOOOO----DDDDD=== DDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:# ^^^^^^^^^^^^^^^DDDDDDDDD DDDDDDDDDDDDDDDDDDDD HH =DDDDDDD ,,,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD =DDDDDDDDDDDDDDDDDDDDDDD ..------DDDDDDDDDDDDDDDDDDDDDDDDDDDD DDD DDDDDDDDDHHHDDDDDDDD #
+D:# ^^^^^^^^^^^^^^DDDDDDDDDDDDDDDD DDDDDDDDDDD DDDDDDDDDDD ,,,=DDDDDDDDDDDDDDDD HH......DDDDDDDDDDDDDDDDDDDDDDD ....-----DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHH==DDDDDD #
+D:# ^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDD DDDDDDDDDDDD DDDDDDDDDDDD,,,DDDDDDD ........... ............DDDDDDD....=.......-------,,,,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDHHDDD #
+D:#^^^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,,DDDDDDD .HHDDDDDDDDDDDDDDDDDDDDDDD .........--------HH----DDDDDDD,,,,,,, ,,,,,,,,,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD==DDD #
+D:# ^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDD DDDDDDDD DDDDDDDDDDDDDDD ,, H .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHH==DDDDDDDDDDD ,,,,,,DDDDDDD,,,,,,,,,DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+D:# ^^^^^^^^^^^^^^DDDDDDDDDDDDD DDDDD DD DDDDDDDDDDDDDDDDDDDDD ,, HH... HDDDDDDDDDDDDDD=DHHHDDDDDDDDD DDDDDDDDD DDD DDDDDDDDDDDDDDDDDDDDDDDDDDD,,,,,DDDDDDD ------ ---DDDDDDD #
+D:# ^^^^^^H^^^^^^^^DDDDDDD DDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDD,, .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD ,,,,,,,,------- -----DDDDDDD#
+D:# ^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDHHHDDDDDDDDDDDDDDDDDDDDD , ..HDDDDDDDDDDDDDD==DDDDDDDDDDDDDDDDDDDDDDDDDDHHDHDDDDDHDDDDD DDDD DDDDDDDDDDDDDDDDDDDDDDDD H---------------- #
+D:# ^^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDD ,, ..DDDDDDDDDDDDDDDDDDHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD---------D--------DDDDDDD#
+D:# ^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDD DDDDHHHD DDDDDDDDDDDDD , ..DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDHDDDDDDDDDDDD====DDDDDDDDDDDDDDDDDDDDDDDDD -----------------DDDDDDD #
+D:# ^^^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDHHHD DDDDDDDDDDDDDDDDDDDDDDDD ,,.DDDDDDDDDDDDDDDDDDD ===DDDDDD DDDDHHDDDDDDDDDDHH==DDDDDDDDDDDDHHHDDDDDDDDDDDDD DDDDDDDDDDDDD --- ---- ----DDDDDDD#
+D:# ^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDD DDDDDDDDDDDDDDDDDDDDDDDDDD ,.DDDDDDDDDDDD==DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDD===DDDDDDDDDDDDDDDDDDDD - -- --DDD==DD #
+D:# ^^^^^^^^^^^^^^DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDD .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHHDD==DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHHHDDDDDDDDDDDDDDDDDDDDDDDDDDHHHDDD #
+D:# ^^^^^^^^^^^^^^ HDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD .DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDHHHHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD #
+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/mods/theme/edit/t_d_cerin.txt b/lib/mods/theme/edit/t_d_cerin.txt
new file mode 100644
index 00000000..02d9ca05
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_cerin.txt
@@ -0,0 +1,75 @@
+# File: t_d_cerin.txt
+
+# Cerin Amroth map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNNNNNNNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDDDDDDDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^^^^^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^DDDDD^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^DD^^^DD^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^DD^HHH^DD^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^DD^HHHHH^DD^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHENDD^DD^HCH=HCH^DD^DDNEHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHENDD^DD^HH===HH^DD^DDNEHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHENDD^DD^HCH=HCH^DD^DDNEHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^DD^HHHHH^DD^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^DD^HHH^DD^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^DDD@DDD^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^DD@DD^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDD^D@D^DDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHNNDDD@DDDNNHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_dale.txt b/lib/mods/theme/edit/t_d_dale.txt
new file mode 100644
index 00000000..1162c294
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_dale.txt
@@ -0,0 +1,75 @@
+# File: t_d_dale.txt
+
+# Dale map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# @@@@@@@@@@@@@@@@ #
+D:# @@%%%%%%%%%%%%%%%%@@ #
+D:# @@@%%%%%%%%%%%%%%%%%%%%@ #
+D:# @%%%%%%%%%%%%%%%%%%%%%%%%@ #
+D:# @%%%%%%%%%%%%%%%%%%%%%%%@$$$$$$$$$$$$$$$ #
+D:# @@@@%%%%%%%%%%%%%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# @%%%%%%%%%%%@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# @%@@@@@@@@@@@@HHStSSSSSHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @@@@@@ #
+D:# @%#HHHHHHHHHHHHHsssssssHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @@%%%%%%@ #
+D:# @%#HHHHHHHHHHHHH#######HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @@%%%%%%%%%%@@@ #
+D:# @%#HHHHHHHHHHHHH###@###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH @%%%%%%%%%%%%%%%@@ #
+D:# @%#HHHHHHHHHHH$HHHH@HHHH$HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%%%%%%%@ #
+D:# ##HHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==================@ #
+D:#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@,,,,,,,,,,,,,,,,,,@@@@@@@@@@@@@@@@@@@@@#
+D:# #HHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHSSSDSHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@==================@ #
+D:# #HHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HEHsssssHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%%%%%%%@@@ #
+D:# #HHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HH#H###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@%%%%%%%%%%%%%%@ #
+D:# #HHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@H##@##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@%%%%%%%%%@@ #
+D:# #HHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@HHH$HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@@ #
+D:# #HHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# #HHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHSSSHHHHHHHHHHHHHHH@HHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# #HHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHsssssHHHHHHHHHHHHHH@HHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHH$########HHHHHHHHHHHHH@HHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# $HHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHH$########HHHHHHHHHHHH@HHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# $HHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHH@HHHHHHHHHHHHHHH,@@@@@HHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$HHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@$HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH #
+D:#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$H@HHHHHHHHHHHHHHUUUUUUYUUUUHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHHHXXXXXXXXXXXHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@HHHHHHHHHHHH===========HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@$HHHHHHHHHH=====@=====HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# #HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$@@@@@@@@@@@@@@@@@HHHHHH$HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HH#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHH#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHD#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHDD#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:# HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_edoras.txt b/lib/mods/theme/edit/t_d_edoras.txt
new file mode 100644
index 00000000..17d7abbd
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_edoras.txt
@@ -0,0 +1,75 @@
+# File: t_d_edoras.txt
+
+# Edoras map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#####,,,,,,,#####HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########,,,,,,,,,,,,,,,#########HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH######,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#######HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#####,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#####HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########,,,,,,,#####$$################################HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#####HHHHHHH,,,HHHH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHH,,,HHHHHH,,HHHHHHHHHHHHHHHHHHHH,,,,,,,,,,,,,,,,,,H###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH####HHHHHHHHHH,,,HHHHHHHHH,,HHHHHHHHHHHHHHHHHHH,################,HHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHHHHHHHH,,,,,,,,,,,,,,,,HHHHHH,################,HHHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,,,HHHHHHHHHH,#########,HHHH,,HHHHH,################,HHHHHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HH,,HHHHHHHHHH,#########,HHHHH,,HHHH,################,HHHHHHHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHH,,HHHHHHHHHH,#########,HHHHHH,,,,,,,,,,,,,,,,,,,,,,,HHHHHHHHHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHHH,,HHHHHHHHHH,#########,HHHHHHHHHHHHHHHHHHHHHHHHH,,HH,,HH######HH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHHHH,,HHHHHHHHHH,,,,,,,,,,,HHHHHHHHHHHHHHHHHHHHHHHHHH,,HH,,H######HHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HH,,,,,,,,,,,,,,,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHH######,,,,,HH,,######HHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHH,$#######$,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH######HHHH,,HH,,HHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHH,#$#####$$,HHHHHHHHHHH####################HHHH######HHHHH,,HH,,HHHH######HH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHHH,####$####,HHHHHHHHH####HHH,,,,,,,,,,,,,####HHHHHHHHHHHHHH,,HH,,,,,######HHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHHHH,########$,HHHHHHH####HHHH,,HHHHHHHHHHH,,,,###HHHHHH######,,,HH,,HH######HHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHHHHH,,,,,,,,,,,HHHHH####HHHHHH,,HHHHHHHHHHHHH,,,,###HHHH######HH,,HH,,HHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHHHHHH,HHHHHHHHHHHHHH###HHHHHHHH,,HHHHHHHHHHHHHHH,,,,###HH######HHH,,HH,,,,######HHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHH,,,HHHHHHHHHH,HHHHHHHHHHH###HHHHHHHHHH,,HHHHHHHHHHHHHHHHH,,,,##HHHHHHHHHHH,,HH,,H######HHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHH,,,H,,,,,,,,,,,,,HHHHHHHH##HHHHHHHHHHHH,,HHHHHHHHHHHHHHHHHHH,,,##HHHH######,,,HH,,######HHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHH,,,HH,$$#########,HHHHHH###HHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHHHHHH,,##HHH######HH,,,,,,,HHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHH,,,,,,,$$#########,HHHHH##HHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHHHHHHH,,##HH######HHH######HHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHH,,,,,,,##$########,HHHH###HHHHHHHHHHHHHHH,,#########HHHHHHHHHHHHHH,,##HHHHHHHHHH######HHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHH,,,HHH,###########,HHH###HHHHHHHHHHHHHHHH,,HHHHH######HHHHHHHHHHHHH,,##HHHHHHHHH######HHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHH,,,,HHH,,,,,,,,,,,,,HHH##HHHHHHHHHHHHHHHHH,,HHHHHHHHH####HHHHHHHHHHHH,,##HHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########CCCC########$############HHHHHHHHHHHHHHHHH,,HHHHHHHHHHH###HHHHHHHHHHH,,###############$############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########CCCC########$############HHHHHHHHHHHHHH$$$,,###HHHHHHHHH###HHHHHHHHHH,,##############$#############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########CCCC#########$###########HHHHHHHHHHHHHH#$###$##HHHHHHHHHH##HHHHHHHHHHH,,#############$#############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########CCCC##########$###$######HHHHHHHHHHHHHH#####$$#HHHHHHHHHH##HHHHHHHHHHH,,##############$############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########CCCC###########$##$######HHHHHHHHHHHHHH########HHHHHHHHHH##HHHHHHHHHHHH,,#############$############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########CCCC####$#######$########HHHHHHHHHHHHHH###$####HHHHHHHHHH##HHHHHHHHHHHH,,##############$#####$#####HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########CCCC#############$########HHHHHHHHHHHHH########HHHHHHHHHH##H#######HHHH,,##############$###########HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########CCCC##############$#######HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HH#######,,,,,,#############$$$#$########HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHH,,,,,,,,,HHHHHHHHHHHHHHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHH###HH#######,,,,,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHH##,,HHHHHH#######HHHH###HHHHHHHHHHHHHHHHHHHHHHHHH###HHH#######HHH,,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHH##,,,,,,,,#######HHHHH###HHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHHHHHH,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHH##,,,,,,,#######HHHHHH###HHHHHHHHHHHHHHHHHHHH####HHHHHHHHHHHHHHHH,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHH##,HHHHHH#######HHHHHHHH###HHHHHHHHHHHHHHHH####HHH$######HHHHHHH,,,##HHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHH##,,HHHHHHHHHHHHHHHHHHHHHH###################HHHHH$######HHHHHH,,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHH##,,HHHHHHHHHHHHHHHHHHHHHHHHH###########HHHHHHHHH$######HHHHH,,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHH##,,HHHHHHHHHHHH####$$$HHHHHHHHHHHHHHHHH#######HHHH,,,,HHHH,,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHH##,,,HHHHHHHHHH#######HHHHHHHHHHHHHHHHH#######HHHHHHH,,H,,,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHH##,,,,HHHHHHHH#######HHHHHHHHHHHHHHHHH#######HHHHHHHH,,,,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHHHHH##,,,,HHHHHHHHHH,HHHHHHHHH#######HHHHHHH,,,,,HHHHHHH,,,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHHHHH###,,,,HHHHHHHH,HHHHHHHHH###$#$$HHHHHHHHHHH,,HHHHH,,,,##HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHHHHHH###,,,,HHHHHH,HHHHHHHHH#######HHHHHHHHHHHH,,HH,,,,###HHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHHHHHHH####,,,,HHH,HHHHHHHHHHHH,HHHHHHHHHHHHHHHH,,,,,###HHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHHHHHHHHH###,,,,,,,HHHHHHHHHHH,HHHHHHHHHHHH,,,,,,####HHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHHHHHHHHHHHHHHHHH######,,,,,,,,,,,,,,,,,,,,,,,,,,,,#####HHHHHHHHHHHHHHHHHHHHHHHHHH##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##HHH==============HHHHHH######,,,,,,,,,,,,,,,,,,,######HHHHHH=================HHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHH=================HHHH###########,,,,,#########HHHHHH===================HHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHH==================HHHHHHHHHHHH,,,,,HHHHHHHHHHH======================HHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHH================================================================HHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHH===========================================================HHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH####HHH=====================================================HHHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###HHHH==============================================HHHHHHH###HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH####HHHHH=======================================HHHHHH#####HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#####HHHHHHH==========================HHHHHHHHHH#####HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH######HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#########HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH###########HHHHHHHHHHHHHHHH############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH########################HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_esga.txt b/lib/mods/theme/edit/t_d_esga.txt
new file mode 100644
index 00000000..319eff22
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_esga.txt
@@ -0,0 +1,75 @@
+# File: t_d_esga.txt
+
+# Esgorath map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%==%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%===,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%===,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%===,,,,SSS,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%===,,,,,,ssss,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%===,,,,,,,,$#@##,,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%===@,,,,,,,,,,,,@,,,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%=%%%%===,,,,@,,,##@#$,,,@,,,,,,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%=%%%%===,,,,,,,,@@@@@@,,,,,@,,,,,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%=%%%%===,,,,,,,,,,S,S,,,@,,,,,@,,##@##,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%=%%%%===,,,,,,,t,,,sssss,,,@,,,,,@@@@@@@@,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%=%%%%===,,,,SSS,S,,,,,##5##,,,@,,,,,,,,,,,,@,,,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%=%%%%===,,,,,,,sssss,,,,,,,@,,,,,@,,,,,,,,,,,,@,,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%=%%%%===,,,,,,,,,,##@##,,,,,,,@,,,,@,,,,,,,,,,,,,@,,SStSS,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%=,,,,,,,,,,,,,,@,,,,,,,,,@,,,@,,SSSStSSS,SS,@,,sssss,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%=%%%%=,,,,$#@$$,,,,@,,,,,,,,,@,,@,,,$ssssss,sss,@,,$#,##,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%=,,,,,@@@@@@@@@@@@@@@@@@,@,,,,$$###@$$###,@,,,,@,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%=%%%%=,,,,,,,,,,,@,,,,,,,,,,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%=,,,,,,,,,,@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@,,,,,,,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%=%%%%=,,,,,,,,,@,,,,StS,,,,,,,=========,,,,,,,@,,,,,,,,,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%=,#$@$$,,@,,,ssss,,,,,==%%%%%%%%==,,,,,,,@,,,,,,,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%=%%%%=,,@@@@@@,,$#,##,,,,=%%%%%%%%%%%%=,,,,,,,@,#$@$#,,,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%=,,,,,,,@,,,@,,,,,=%%%%%%%%%%%%%%=,,,,,,,@,,@,,,,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%=%%%%=,,,,,,,@@@@,,,,=%%%%%%%%%%%%%%%%=,,,,,,,@,@,,SS,,,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%=,,,,,,,@,,,,,,=%%%%%%%%%%%%%%%%%=,,,,,,,@,,,sss,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%=%%%%=,,,,,,,@,,,,,=%%%%%%%%%%%%%%%%%%=,,,,,,,@,,$#@##,,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%=,,,,,,,@,,,,,=%%%%%%%%%%%%%%%%%=,,,,,,,,@,,,@,,,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%=%%%%=,,,,,,,@,,,,,=%%%%%%%%%%%%%%%=@,,,,,,,,,@,,@,,,,,,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%========@,,,,,=%%%%%%%%%%%%%=,,@,,,,,,,,,@,@,,,,,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%=%%%%=@@@@@@@@,,,,,=%%%%%%%%%%=,,,,,@,,,,,,,,,@,,,$#@##,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%%%%%========@,,,,,=%%%%%%%==,,,,,,,@,,,,,,,,,@,,,,@,,,,,,,=%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#%%%%%%%%%%%%%%%%%%%%%%%=%%%%=,,,,,,,@,,,,,====CCC==,,,,,,S,,StSSSS,,,@,,,@,,,,,,,,=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#H%%%%%%%%%%%%%%%%%%%%%%%%%%%%=,,,,,,,@,,,,,,,==CCC==,,,,,s,sssssss,,,,@,@,,,,,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HH%%%%%%%%%%%%%%%%%%%%%%%=%%%%=,,,,,,,@,,,,,,,==CCC==,,,,#$,#@$$##,,@@@@,,,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=,,,,,,,@,,,,,,,==CCC==,,,,,$$,@@@@@@@,,,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHH%%%%%%%%%%%%%%%%%%%%%%%%=%%%%=,,,,,,,@,,,,,,,==CCC==,,,,,,,,@,,,,,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%=,,,,,,,,@@@,,,,,==CCC==,,,,,,,,@,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHH%%%%%%%%%%%%%%%%%%%%%%%%%%%==,@@@@@@@,,,@,,,,,==CCC==,,,,,,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHH%%%%%%%%%%%%%%%%%%%%%%%%==@@@@,,,,@,tSSS@,,,,,==CCC==,,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHH%%%%%%%%%%%%%%%%%%%%%%==@@@@=====,@sssss@,,,,,,==CCC====%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHH%%%%%%%%%%%%%%%%%%%==@@@@==%%%==,@#####@,,,,,,,==CCC%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHH%%%%%%%%%%%%%%%==@@@@==%%%=%%%==@##,##@,,,,,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHH%%%%%%%%%%%%==@@@@==%%%%%%%%%%%==@@@@@@,,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHH%%%%%%%%%%==@@@@==%%%%%%%%%%=%%%==,,,,,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHH%%%%%%==@@@@==%%%%%%%%%%%%%%%%%%==,===%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHH%%%==@@@@==%%%%%%%%%%%%%%%%%%=%%%==%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHH==@@@@@==%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHHHH@@==%%%%%%%%%%%%%%%%%%%%%%%%%%%%=%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHHHH==%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHSSSSHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHssssHHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHH$#@##HHHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHHHHHHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHHHHHHHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHHHHHHHHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHHHHHHHHHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHHHHHHHHHHHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHHHHHHHHHHHHH%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_gond.txt b/lib/mods/theme/edit/t_d_gond.txt
new file mode 100644
index 00000000..18bb7c10
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_gond.txt
@@ -0,0 +1,100 @@
+# File: t_d_gond.txt
+
+# Destroyed Gondolin: Your failure has left the city in ruins.
+# Created by Mynstral (mynstral@thehelm.com)
+
+# Decoration = Straight Road (B)
+F:":66:3
+
+# Decoration = Straight Road (W)
+F:$:70:3
+
+
+# Town Layout
+
+D:######################################^^^^^^^^^^^^$ ####
+D:######################################^^^^^^^^^^^^$ ####
+D:######################################^^^^^^^^^^^^$ ####
+D:######################################^^^^^^^^^^^^$ ####
+D:######################################^^^^^^^^^^^^$ ####
+D:####################################^^^^^^^^^^^$ ####
+D:##################################^^^^^^^ $ $ $ ###
+D:#################################^^^^^^^^$ C C $ CC$ CC CC C CC$C C$ $ ###
+D:################################^^^$^^^^^^^ C $C$C$#$######$#$#####$#####CC$ CC$ $ $ ###
+D:###############################^^^^^^^^^^^^ C$$$ C# ###### DD D ####$D$D D D ###$###CC C $ ^^^^^ ###
+D:##############################^^^^^^$^^^$ C $####$##$D DDD $.$## $.DD DDD#$$#####C $ $^^^^^^^ $ ###
+D:#############################^^^^^^^^^^ # $##########D $######### .D##########$ $ ^^^^^^^^^^^^ ###
+D:############################^^$$^^ $ ############ ..$###########$ ..D############C $ ^^^^^^^^^^^^^^$ ###
+D:############################ C #############D$ .###%%#####%%### .D#############C$ ^^^^^^^^^^^^^ ###
+D:########################### $$ $ #############DD .#%%%%#$###%%$%# $.$$############# $ ^^^^^^^^^^^ ###
+D:########################### $ . $ $$ C#$###########D$ .%%###$###%% .D$############C $ $ ^^^^^^^ $ $ ###
+D:########################## $ ##### $$ $ #### $ $ C######%%%%### ..%## $##$ ..D####%%%%#####C$ ####### # $ ^^^^ ###
+D:########################## $ #$##### $ ##$## ####### C#$####D DD%%#D$ # .$# D$$%D DD######C $## ##$#$$ ###
+D:####$#################$#%# ### ###$ ### $ ####### ###### DD%#$ D#%DD D####$ ##$#### ###
+D:####%$#%###$#$#####$#$%%%# $####$### #$##$ ####### $######D $ $$..$######$ ###
+D:#####%$##%#%##$##%$%%%#%## #####$### # ### #########$ ######D $ .. DD DD ..$$D######C$ $ ###
+D:#####$%$%$%%%##$%$%%%#$$## $######$# $ ##$ $##$#### $$ $ $C#######$% ..DD D####### DD ..%%#$##### C # $ ###
+D:######%##%%#%%#%%%%%#$#### . $ $ $# $# #######$## D ##### $###### DD ########## $ ## ## ### $ $$ ###
+D:######%%%#%%%$%$%$%%$%#### .. $ C$####### .. D###### $# # ##### .. ########$ #$#######$###$#### ###
+D:#######$%%%%$%%%$%%$#%%### $ #####D DD .. D###$.$#$# #$## .###D ..DDD$#####C$ $ #### # #######$ ###
+D:########$%%$%%$%%%$#%###### # ###### $ $## #####$ C# D ..D## ..## $$ ..# $ .. D DD# $ $$#$#######$## ###
+D:#########$#%%%%$%%########## #$#$####$ ############ $#D $ .$#$ ## $ ..$ $.. ..## D $# # ..D#C $ $ ###$### $## ###
+D:##########$#################### # ####### ############ $ #D $ $####$ D## $# $ ..# @ @ ## .#$##$ $ $D# ## #$#$#$## @ @ ###
+D:#### $#################### ####$# ##$ #####$####### $C#D # #$ $. D## @@@ @ .##D ..$## $ .D #$C #### $ @@@@@@ ###
+D:#### ####$$$C##%#LL#""## #######$ ## $##$#$ ## C### $ ###$ . D ### $ @%@@..###D D .#### ###$ @ @ @@@@%%%%@@@ ###
+D:#### $####$##$C# $#LL#""# # . $ $ ###D $##$ D###.### .#####%@ @###.## $###$ $### $ @@ @@%%%%%%%@ ###
+D:##### ################### . $ C### ## #$ D## ..##.$##%$%%#..## ..# D ## $ D###C @@@@%%%%%%%%%@ ###
+D:# $#### D# .#..#%%#$%#..# .#D $## .. @%%%%%%%%%%%%@@ ###
+D:#.. ..#%#%#%# $ @%%%%%%%%%%%%@ @ ###
+D:# .##$# D# .#.$#%%#%$#..# .#D ####$ .. @@%%%%%%%%%%%@@ @ $ ###
+D:############ ########## # . . . $###D ..#$###$ ## ..##..##%%%##..## ..## #$ # $ $##C @%%%%%%%%%%@@@@ ^^###
+D:#### #### K# C# #LL# "# $ . $ . . ### ##$# DD###.### .##### $###.###D $### . D#$# @@@%%%%%%%@@@@@@ $ ^^###
+D:#### ### K CC %%# L# "## ##### # $ ## #### # # #### C$##D .. DD### ..###DD .###$ DD####$C @@%%%%%@@ @ @ ^^^^###
+D:#### ####KK#CC#%%#L # "### ## ###### ###$ ###$# $# ##### $#DD D## $ .##D ..$# # D #####C$ @@%%%%@ $ $^^^^^^###
+D:######## ###################### ######### $####$#$### # ## $# C$D ..D## $#$ .$$ ## .#$##$ .D##### $ DD @@@@@@@ ^^^^^^^###
+D:####### #####$$$$#$#### #### ### ###$# $ ## ########$# # # # %% %% .DD## $ $.##$$ ## $$## D #$# $###### DD DD DD DD DD ^^^^^^^^^###
+D:########$$$$$#$$$$$##### ## $##$###### ##$# ### $### $## $ C$$..%%# $ #$% D####.#########$#$####D ..######C D DD DDD D D DD DDD ^^^^^^^^^###
+D:######$$$#$$$$#$#$##$#$### $ C D..%#$ .###% . DD#################D D .D$#####$ DD DDD########DDD DDDD ^^^^^^^###
+D:#######$#$##$$$#$#$$$$#### C#D .%###.$# $ D D### #########DD D #$###$ DDD###DD D D### D DD $ ^^^^^^###
+D:######$$$$$$#$#$#$$$$$$$# $ $ $$ $C##D.%##$ # $% ..D D ####### D D ### DDD##DD D ##DDDDDD ^^^^###
+D:#####$$$$#$$$#$$$$$$$$$### ###### # $$### $###$$ $C#D.%%#####%% $ $$ DD#C DD#DD D DD## D DD $ ^^^^###
+D:#####$$$#$$$#####$$$$$$### ## ####$ $ #$## ##### ##$ $ ..%%###%%$ ..D D$ $DDD$ $ $ .DD $ $ D D$ $. D$ $ D ##D DDD ^^^^###
+D:####$$$###$########$#$$$$# ###$#####$ ## ####### # C %%%$% .DD$######$#$D$D $DDD#$###$#$#DDD $$ $ $ D#DD D DD##D ^^^^^^###
+D:###$$#################$### # ## ## # ###########$# C$DD .DD###%%%%%%####$ . $#$##%%%%%%### D .DD#C $ DDD##DDD D D##DD D ^^^^^^^###
+D:#######################$## $#### #$ ############# $# .D##%%% .%$%###DDD###%%% .%%%#$D$ .D#$ DDDD D D D###DD D $ ^^^^^^^###
+D:########################### $ # $ $ C$# .D#%% ..%%#######%% $.%%#D D#$ DD D DD# ## ## DDD DD ^^^^^###
+D:########################### $ $# C#D $ ..####### $ ..DD#$ DD D DDD DDD DD D ^^^^###
+D:############################ ##$ #$ $#D ..D#%% $ $..%%#######%$ $ ..%%#D .. D$# $ DDDD DD D D $ $ ^^^^^^###
+D:############################ ######### C ##DD .D##%%% .%%%#########%%% $.%%%##D . $## DDD D DD D ^^^^^^^^^###
+D:############################# #####$# $#D DD DD ##%%%%%%## #### #####%%%%%%###D DD D##$C ^^^^^^^^^^^^###
+D:############################## $ ###$ #### DD DDD ##### # ######$ # ########DDDD $D$D ###$C $ ^^^^^^^^^^^^^^###
+D:############################### ### $$ C$ $#$#$##DDD $D$ DD##############D D$$D D D## #$#CC$$ $^^^^^^^^^^^^^^^^^###
+D:################################ $ # CC $ ##$# #$#####$###$#$#$#####$###$###$C$C C$ $ ^^^^^^^^^^^^^^^^^###
+D:################################# $ $ $ # $ C$C$ CCC$CC C$$C$CCC C $ #C C C$ $C C $ ^^^^^^^^^^^^^^^^^^####
+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/mods/theme/edit/t_d_helm.txt b/lib/mods/theme/edit/t_d_helm.txt
new file mode 100644
index 00000000..ef922a16
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_helm.txt
@@ -0,0 +1,75 @@
+# File: t_d_helm.txt
+
+# Helm's Deep map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:#DD,,,,DDDDDDDDDDDDDDDDDDDDDDDD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDD,,,,DDDDDDDDDDDDDDDDDDDDDDDHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDD,,,,DDDDDDDDDDDDDDDDDDDDDDHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#@DDDD,,,,DDDDDDDDDDDDDDDDDDDDHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#%@DDD,,,,DDDDDDDDDDDDDDDDDDDHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#%%@DDDD,,,,DDDDDDDDDDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#%%%@DDD,,,,DDDDDDDDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#%%%%@DDDD,,,,DDDDDDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#%%%%%@DDD,,,,DDDDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#@%%%%%@DDDD,,,,DDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#D@%%%%%@DDD,,,,DDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DD@%%%%%@DDDD,,,,DDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^HHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDD@%%%%%@DDD,,,,DDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^HHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDD@%%%%%@@DDD,,,,DHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^HHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDD@@%%%%%@HHH,,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^HHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDD@%%%%%@@HHHH,,,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^HHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDD@@%%%%%@@HHHHH,,,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDD@@%%%%%@@@HHHHH,,,,,,HHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDDDD@@@%%%%%@@@@HHHHH,,,,,HHHHHHHHHHHHHHHHHHHHHHH^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDDDDDDD@@%%%%%%@@@@@HHHH,,,,,HHHHHHHHHHHHHHHHHHH^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDDDDDDHHH@@@%%%%%%%%@@@@HHH,,,,,HHHHHHHHHHHHHHH^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDDDDDHHHHHHHH@%%%%%%%%%%@@@HHH,,,,,HHHHHHHHHHH^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDDDDDHHHHHHHHH@@@@@%%%%%%%%@@@HHH,,,,HHHHHHHH^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDDDDHHHHHHHHHHHHHHH@@@@@%%%%%%@HHHHH,,,,HHHH^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDDDDHHHHHHHHHHHHHHHHHHHH@@@%%%%%@HHHHHH,,,H^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@@@HHHH,,,,^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%@@@@HH,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%@@@@,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@%%%%%%%@H,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@%%%%%@HH,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$$##^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^@%%%%%@H,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH$$@@##^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^HH@%%%%@H,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@##^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^HH@%%%@HH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@#$HHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^HHH@%%%@HH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##@@$$HHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^HHH@%%%%@H,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##@@@@HHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^HHHH@%%%%@H,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##@@@@HHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^HHHHHH@%%%@H,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##@@#$HHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#DDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^HHHHH@%%%%@H,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##@@##HHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^HHHHH@%%%%@H,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH##@@##HHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^HHHHH@%%%%%@H,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHH##@@##HHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^HHHHHH@%%%%@HH,,HHHHHHHHHHHHHHHHHHHHH@@@@HHH##@@##HHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^HHHHHH@%%%@HHH,,HHHHHHHHHHHHHHHHHHHH@%%%%@^##@@##@@HHHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^HHHHH@%%%%@HHH,,HHHHHHHHHHHHHHHHHH@%%%%%%%##@@##%%%@HHHHHHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^^HHHHHH@%%%@HHHHH,,HHHHHHHHHHHHHHH@%%%%@@@@##@@##%%%%@@@@@HHHHHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHH^^^^^^HHHHHH@%%%%@HHHHHH,,HHHHHHHHHHHHH@%%%%@^^^##@@##^@%%%%%%%%@@@HHHHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^HHHHHHHHH^HHHHHHHHHHHHHHH^^^^^^HHHHHHH@%%%%@HHHHHHH,,,HHHHHHHHHHH@%%%@^^^##@@##^^^@@@@%%%%%%%@@HHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^HHHHHHHHHH^^^^^^^HHHHHHHHHHHH^^^^^^^HHHHHH@%%%@HHHHHHHHH,,,,HHHHHHHH@%%%@^^^##@@####^^^^^^@@@%%%%%%@@HHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^HHHHHHHHHHH^^^^^^^^^^HHHHHHH^^^^^HHHHHHHH@%%%@HHHHHHHHHHH,,,,HHHHH@%%%@^^^##@@@@@@####^^^^^^^@%%%%%%@HHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^HHHHHHHHHHH^^^^^^^^^^^^^^^^HHH^^^^^^^HHHHHH@%%%%@HHHHHHHHHHHHH,,,,,$$$@%%%@^##@@@@@@@@@@####^^^^^@@@%%%%@HHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#HHHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^HHHHHHH@%%%@HHHHHHHHHHHHHHH,,,@@%%%%$#$#@@@@@@@@@@@@@@####^^^^^^@%%%@HHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#HHHHHHHHHHHH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^HHHHHHH@%%%%@HHHHHHHHHHHHHH$$$@%%%%@@@@@@@@@@@@@@@@@@@@@@####^^^@%%%%@H^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^HHHHHHH@%%%%@HHHHHHHHHHHHHHH$%%%%#####@@@@@@@@@@@@@@@@@@@@@###$^@%%@HH^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^HHHHH@%%%%%@HHHHHHHHHHHH@%%%%@^^##@@@@@@@@$###@@@@@@@@@@@@#$^@%%@H^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^HHHH@%%%%%%%@@@@@@@@@@%%%%%^^@@@@@@@@@@$#####@@@@@@@@@@##^^@%%@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^HH@%%%%%%%%%%%%%%%%%%@^^@@@@@@@@@@@$$$###@@@@@@@@@##^^@%%%@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^HH@@@@@%%%%%%%%%%@^^^@@@@@@@@@@@@$$####@@@@@@@@##^^^^@%%%%@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^H@@@@@@@@@@^^^$$@@@@@@@@@@@@####@@@@@@@@##^^^^^^@%%%%%@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^HHHHHH^^^^$###@@@@@@@@@@@@@@@@@@@@@##^^^^^^^@%%%%%@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#$##@@@@@@@@@@@@@@@@@##^^^^^^^^^@@@%%%%@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####@@@@@@@@@@@@@##^^^^^^^^^^^^@%%%@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####@@@@@@@@@##^^^^^^^^^^^^^^@@%%%@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####@@@@@##^^^^^^^^^^^^^^^^^@%%%@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####@@##^^^^^^^^^^^^^^^^^^^@%%%@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####^^^^^^^^^^^^^^^^^^^@%%%@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_henn.txt b/lib/mods/theme/edit/t_d_henn.txt
new file mode 100644
index 00000000..4fa033c4
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_henn.txt
@@ -0,0 +1,75 @@
+# File: t_d_henn.txt
+
+# Henneth Annun map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%%%%%%%%%%^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%%DD%%%%%^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+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:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^=@@@@@@@@@@@@@@@@@@%%%%%%%%@@@@@@@@@@@@@@@@@@@@=^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^#
+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:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^lllll@@%%%%%%%%@llllllDDDDDDDDDDDDDDDDDDDDDDDDDD^^^^^^^^^^^^^^^DD^D^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^CC^^^^^^^^^^^^^^^^^^^^^^^^^^lllllllllll%llllll^^^^^^^^^^^^^^^^^^^^^^^^^D%%%%%%%DD^D^D^D^^^^^DDD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^CCCCCCCCCCCCCCCCCCCCCCCCCfffffffflllllll%llllllffffff^^^^^^^^CCCCCCCCC^^^^%%%%%DD^^^^^^^^^^^^DD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^CCCCCCCCCCCCCCCCCCCCCCCCCffffffffffllllll%lllffffffffffCCCC^^CCCCCCCCCCCCC^^^^^DDDDDDDDDDDDDDD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^CCCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffffffffCCCCCCCCCCCCCCCCCCCC^C^^^^^^^^^^DDDDDD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^CCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffffffffffCCC^CCCCCCCCCCCCCCC^^CCCCC^C^^^D^^^^^DD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^CCCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffffffffCCCC^CCCCCCCCCCCCCCCCC^^^^CCCCC^^^CCC^^^D^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^CCCCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffffffCCCCC^CCCCCCCCCCCCCCC^CCCCCCCCC^^CCCCCC^D^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@^^^^^^^^^^CCCCCCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffCCCCCCC^CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC^D^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_hobb.txt b/lib/mods/theme/edit/t_d_hobb.txt
new file mode 100644
index 00000000..9bc22a10
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_hobb.txt
@@ -0,0 +1,75 @@
+# File: t_d_hobb.txt
+
+# Hobbiton map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:#DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD==========================,=======ccccc==,,=========================================================================================#
+D:#D==============================================================DDHHHHHHHHHHHHHHHHHHHHHHHH,,HHHHHHH==D==D,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHHHHHHHHHHHHHHH,,HHHHHHHH====D,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHHHHHHHHHHHHHH,,HHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHH,,DHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHH,,DHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHHHHHHHHH,,C$###########$HHH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHHH,,,,,,,,,,,,,,,,,,,,,#HHH,,DHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHH,,HHH#,,,,,,,,,,,,,,,,#HHH,,DHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#D==============================================================DDHHHHHHHHHH,,HHH#,,,==========,,,#HHH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHH,,HHH#,,,==========,,,#HHH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHH,,HHH#,,,,=========,,,,,,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHH,,HHH#,,,==========,,,#HH,,=HHHHDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHH,,HH#,,,==========,,,#HH,,HHDDHDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHH,,HH#,,,,,,,,,,,,,,,,#HH,,HDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHH,,H,,,,,,,,,,,,,,,,,#HH,,=DDHHHHHHDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHH,,,#################HH,,=DDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHHH,,,,,,,,,,,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHH,,=HH#####HH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHH,,=HH#######,,$#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHH,,HHHHHHHHHHHDHH,,=HH######$$$##HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHDDDD,,HHHHHHHHDDDH,,=HHH###########HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHDDHH,,HHHHHHHHHHH,,=HHH#####HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHDDH,,HHHHHDDDHH,,=HHH#####HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHDDHH,,HHHHHDHHH,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHDDDH,,HHDDHHH,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHDDDDH,,DDDHH,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHHHDDDH,,HH=,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHHHHDDDH,,,,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHHHHHDDHH,,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHHHHDDDH,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHHHHDDH=,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHHHHDDH,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHHHHDDH,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHHHDDDH,,=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHHHHHDHH,,=H$$############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHDDDHDDDH,,HH$#############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#=================================================================HHHHHHHHHHHHHHHHHHDHDDDDH,,,,,#############HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#==========================================================================================,,HH$C############@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#
+=:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%a,,a%$#############%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+=:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%a,,a%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+=:#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%a,,a%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+=:#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=,,@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH====HHH,,HHHHHHHH===HHHHH===HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDHHHHHHDDHHHHHHDDHHHHH====HHHHHH======HH,,HHHHHHH=====HHH=====HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDHHHDHHHHHHHDHHDHHH======HHHH========H,,HHHHHH=======HH==H==HHHDHHHDHHHHDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDHHHHHDDHHHHHHDDHHH===HH===HHH===HH===H,,HHHHHH===H===HHHHHHHHDDDDHDDDDHDHHDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
+=:#,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===HH===HHHHHH==H,,H==HHHHH=HH=HHHH=HH=HDDDDHDDHHHHHHDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH======HHHHHHHH==,,H===HHHHH==HHHHH====HHDHHHHDDHHHDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH====HHHHHHHHHH=,,H===HHHHHHHHHHHHH==HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,,H==HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=====HHHHH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=======HHHH,,HHH==HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH====H====HHH,,HH====HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,,H======HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===HH,,H==HH==HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=====H,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==H==H,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,,HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+=:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_imlad.txt b/lib/mods/theme/edit/t_d_imlad.txt
new file mode 100644
index 00000000..c1c10fd7
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_imlad.txt
@@ -0,0 +1,75 @@
+# File: t_d_imlad.txt
+
+# Imladris map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:# @^^^@ @@%%%%%%%%%%^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%%%^#
+D:# @^^^@ @@%%%%%%%%%%^^^^^^^^^^^^^^^^^^^^^^^^HHH#####HHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%%%%^#
+D:# @^^^@ @@%%%%%%%%%%^^^^^^^^^^^^^^^^^^^^HHHHHH#####HHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%%%%^^#
+D:# @^^^@ @@%%%%%%%%%%^^^^^^^^^^^^^^^^HHHH###HH#####HHHHHHHHHHHHHHHHHHHHHHHDDDDDDDD DDDDDDD^^^^^^^^^^^^^^^^^^^^^^^^^^^^%%%%^^^#
+D:# @^^^@ @@%%%%%%%%%%^^^^^^^^^^^^^^HH######HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDD DDDDDD^^^^^^^^^^^^^^^^^^^^^^^^^^%%%%^^^^#
+D:# @^^^@ @@%%%%%%%%%%^^^^^^^^HHHHH#######HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDD DDDDDDD^^^^^^^^^^^^^^^^^^^^^^^^^%%%%^^^^^#
+D:# @^^^@ @@%%%%%%%%%%^^^^^^^^^HHH####HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDD DDDDDDDDDDDD^^^^^^^^^^^^^^^^^^^^^%%%^^^^^^^#
+D:# @^^^@ @@%%%%%%%%%%^^^^^^%%^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDD DDDDDDDDDDDD^^^^^^^^^^^^^^^^^^%%%^^^^^^^^^#
+D:# @^^^@ @@%%%%%%%%%%^^^^^^^%^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDDDDDDDDDDD DDDD^^^^^^^^^^^^^^^^^^^^^^%%%%^^^^^^^^^^#
+D:# @^^^@ @@%%%%%%%%%^^^^^^%@^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDD^^^^^^^^^^^^^^^^^^^^%%%%^^^^^^^^^^^^#
+D:# @^^^@ @@%%%%%%%%%^^^^^^%@^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDDDDDDDDDDDDD DDDDDDD^^^^^^^^^^^^^^^^%%%%^^^^^^^^^^^^^^#
+D:# @^^^@ @@%%%%%%%%%%%%C%%@@^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDDDDDDDD^^^^^^^^^^,^^@%%%%^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@%%%%%%%%%%CC%@@@@^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDDDDDDDD^^^^^,,,,^^@%%%%^^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@%%%%%%%%CC%%@@@@@^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDDDDDDDD^^,,^^^@@%%%%^^^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@%%%%%%CC%%%%%@@@@^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDDDDDDDDDDDDDDDD,,^^^^@%%%%%^^^^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@%%%%CC%%%%%%%@@@@@^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHCCCCCCCCCCCCCCCCCCCCCCCHHHHDDDDDDDDDDDDDD,,,^^^@@%%%%@^^^^^^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@%%CC%%%%%%%%%%@@@@@@^^HHHHHHHHHHHHHHHHHHHHHHHHHCCCCCCCCCCCCCCCCCCCCCCCHHHHHHHHHHHHDDDHH,,^^^@@%%%%%@^^^^^^^^^^^^^^^^^^^^^^#
+D:# DDDDDD @^^^@ @^^CC%%%%%%%%%%%%%@@@@@^^HHHHHHHHHHHHHHHHHHHHHHHHCCCCCCCCCCCCCCCCCCCCCCCHHHHHHHHHHHHH,,,,^^^@@%%%%%^^^^D^^^^^^^^^^^^^^^^^^^^#
+D:# DDDDDDD DDD @^^^@ @^^^^^^^%%%%%%%%%%%%%%%%@@@@^^HHHHHHHHHH,,,,,,,,,,,,,CCCCCCCCCCCCCCCCCCCCCCC,HHHHHHHHHH,,^^^^^@@%%%%%^^^DDDDD^^^^^^^^^^^^^^^^^^^#
+D:# DDDD DDD @^^^@^@@^^@^^^^@ @@%%%%%%%%%%%%%%%%%%@@^^^^HHHH,,,,,,,,,,,,,,,,CCCCCCCCCCCCCCCCCCCCCCC,HHHHHHHHH,,^^^@@@%%%%%@^^DDD DDD^^^^^^^^^^^^^^^^^^#
+D:# DDD DDD D DDD @^^^^^^^^^^^@ @@@%%%%%%%%%%%%%%%%%@@@^^,,,,,,HHHHHHHHHHHHH,CCCCCCCCCCCCCCCCCCCCCCC,HHHHHH,,,^^^@@%%%%%%%@^^DDD DDD^^^^^^^^^^^^^^^^^^^#
+D:# DDD D DDD D DD @^^@ @@@@@%%%%%%%%%%%%%%@#,,,,,^^^HHHHHHHHHHHH,CCCCCCCCCCCCCCCCCCCCCCC,HHHHH,,^^^@@%%%%%%%%@^^DDD DDD^^^^^^^^^^^^^^^^^^^^#
+D:# DDD D DDDDDDD DD @@@@@%%%%%%%%%%%#,,,,,#@@^^^^^^^^HHHHHHCCCCCCCCCCCCCCCCCCCCCCC,H,,,,^^^@@%%%%%%%%%@^^DD DDD^^^^^^^^^^^^^^^^^^^^^^#
+D:# DD DDDDDD,,DD,,DD,, @@@@%%%%%%%#,,,,,#%%%@@@@@@@^^HHHHHH,HHHHHHHHHHHHHHHHHHHH,,,,^^^^@@%%%%%%%%%%@^^DD DDD^^^^^^^^^^^^^^^^^^^^^^^#
+D:# DD DDD DD ,,,DD,,DD,,D,, ,,,,,,, @@@@%%%#,,,,,#%%%%%%%%%%@@@^^^^^^,,,,,,,,,,,,,,,,,,,,,,^^^^@@@%%%%%%%%%%@@^^DD DDD^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# DDD D ,,DDD DDD DDDDD,, ,,,,,,,,,,,,,,, @@%#,,,,,#%%%%%%%%%%%%%%@@@@@^^^^^^^^^^^^^^^^^^^^^^^^@@@%%%%%%%%%%%@@@^^DD DDD^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# DDDD ,,DD DDDD,,,,,, ,,,, ,,,,, ,,,,,,,,,,@#,,,,,#%%%%%%%%%%%%%%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@%%%%%%%%%%%%@@@@^^DD DD^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# D DDD D,,DD D DD DD,,,,^^,,,, ,,,, ,,,, ,,,, ,,,, ,,,,,,,,@@#@@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@@^^^^DD DD^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# DDD D,,DD DD DD,,,,^^^^^^,,,, ,,,, ,,,,, ,,,, ,,,,@@@ @@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@^^^^DDD DD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# D,,DDD D,,,,^^@@^^^^@ @^@,,,,, ,,,, @@@@@@%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%@@@@DDDDDDDD DD^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# ,,,,,,DDDD DDDD DDDD,,,,^^@ @^^^@ ,,,,,,,,,, @@@@@@@@%%%%%%%%%%%%%%%%%%%%%@@@@@@ DDDDDDDDD^^@@^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# ,,,,^^^^^^DDD DDD^^,,,,^^@ @^^^^^@ ,,,,, @@@@@@@@@@@@@@@@@@@@@@@ @^@DDDD^^@ @^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# ,,,,,^^^^@ @^^^DDDDDDDD^^^^,,,,^^@ @^^^^^^@ ,,, @^^^@ ^^^^^^^^^^^^^^^^^^^^^^#
+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:# @^^,,,, #
+D:# @^^,,,, #
+D:# @^^,,,, #
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_khaz.txt b/lib/mods/theme/edit/t_d_khaz.txt
new file mode 100644
index 00000000..50e8b124
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_khaz.txt
@@ -0,0 +1,79 @@
+# Town Name: Destroyed Khazad-Dum
+# by fearoffours (fearoffours@moppy.co.uk)
+#
+# Created for ToME
+
+# Rocky ground
+F:o:207: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 HHHHHHHH # o ; oooooooooooooooooooooooo##ooo############################ CCCCCCC #
+D:#^^^^^^################################# ## ; o ; HH=====HH H o ; o $#o################################### CCCCCCC#
+D:#^^^^^^################################ ### o ### HH====HHHH ## o ###; o ##H ######################################## CCCCCC#
+D:#^^^^^################################# ### o ### ### ## HH=====HHo ### o ### o #HH==H $$###################################### CCCCCC#
+D:#^^^^^^^################################ ###### ;; o ######### HHH=====Ho ###### o ## ### o ## #H=H #$##################################### ; CCCCCC#
+D:#^^^^^^^^################################ # ## #### o ; ## # # HHHHHHHHH ####### # #### # o ## ###==# ###$#$################################## CCCCCC #
+D:#^^^^^^^################################### ## ## o ; ;### # HHHHo # # ##o # ## ; o # ##== ;#$##$$################################# CCCCCCC #
+D:#^^^^^^^^^################################ ## HHHHHHHH ### o ### o # # o ## # #$################################## CCCCCC ; #
+D:#^^^^^^^^^################################# HH======HH o o o #$ ################################## CCCCCC #
+D:#^^^^^^^^^^################################ ; HH=======HHoooooooo o o oooooooooooo HHHH $################################# CCCCCC ; #
+D:#^^^^^^^^^^^^^########################### HHH=====HH o o o o ===== H ################################# CCCCCCC #
+D:#^^^^^^^^^^^^^^^########################## HHHHHHHHHHHHHH== o ### # o o ;### o ### # H= =HH #$############################## CCCCCCC ; #
+D:#^^^^^^^^^^^^############################# H===HH #####; o # ## o o # ### o # # # == ; ###############################; CCCCCCC ; #
+D:#^^^^^^^^^############################### HH===H; ## ## # o #### ## o ; o ####### o ### #H HHH ############################ CCCCCCC #
+D:#^######################################### H=HH; ## ### oooooooo #### # #o o ###### oooooo ###HH== o ; $#############################; CCCCCCC #
+D:#################ooo###################### HH ; ### # o # ## o o ##### o ## H==H# $$#############oo############## CCCCC; #
+D:####oo##ooo#####o#######o###ooo########### ## ; o ## o o ### o ; ###==HH ;; $########oo###oooooo#####oo###### CCCCC ; #
+D:#ooooooooooooooooooooooooooooooo##oooooooo ; o ; o o o ; # ;; ; oooooooooooo##oooooooo##oooooooooo CCCCC ; #
+D:#ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooHHH=ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo CCCCCC #
+D:#oooooooooooooooooo##oooooo##oooooo##ooooo HH ; o o HH ==H ; $oooooooo#oooooooooooooooooooooooo CCCCC #
+D:#^#######################oo####ooooo####### ##### ==H=H # # o o H===### # ######o####oo####oo######oo######; CCCCC #
+D:#^^#######################o########### #### HH===HH# oH o H =H## # ## ## ## $$####o####################oo#### CCCCC #
+D:#^####################################### ## ### ;; ; HH==HH##### oH ===HH ### ### ## #### $####oo######################### CCCCCC #
+D:#^^######################################## ####### ; #H##### oH o # #### # # ### $$$###o############oo########## CCCCCCC #
+D:#^^^###############ooo################### ### # ; ;# ## oH HH o =H ##### ; ### ## ;; $#####oo########ooooo####### CCCCCCCC #
+D:#####oo##oo#####oooooooo##############oo## ### ; #### o HH o = H ### ### $#######oooo##oooooo####### CCCCCCCCC ; #
+D:#ooooooooooo###ooooooooooooooooooooooooooo # o H o= # ooooooooooooooooooooooooooo CCCCCCCC #
+D:#oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo=ooo=ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo CCCCCCCC ; #
+D:#^ooooooooooooooooo###oooooooooo##oooooooo o #o H o o HH # ooooooo####ooooooooooooo CCCCCCCC #
+D:#^^#o###ooo#####ooo#####ooooo########oo# # # ; o ### o H =o ;### o H ### o$#########ooooo###oo# CCCCCCCC ; #
+D:#^^^^^###o################oo############# # # # ; o ###### # H= o ## # o #### ; $###########oo######## CCCCCCCC ; #
+D:#^^^^^^^##################################$ ; #### ooooooo ##### o ; H o ###### ooooo H ####### ######################## CCCCCCCCC #
+D:#^^^^^^^^^^##############################$$ HH; ## ##### o ; ####### o Ho ### # o === # ## # o ; #$######################## CCCCCCCC #
+D:#^^^^^^^^^^^^^^^######################### $ HH == H ; ## ## o #### o H ##### o H== ### # ############################ CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^^###################$$ HHH===HH ## ; o ### o o # # o ; H ### ; ;; $$########################## CCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^#################### $ HHH===HH ; o ;; o ; o o H ; $########################### CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^######################$ HH==H H ooooooooooo o o oooooooooooo ;; ;$ ############################ CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^^^^^#################$$$ ; H==H o ; ; o o ; H o $############################ CCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^##############$ ### o # # o ### o ###; == H o #### $$############################ CCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^############$$$ ### # o HHH H # ### o ## # o # # # HH== Ho # ## $ ######################## CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^##########$ $ ### # #;; ; o HHHH ## #### o ## #### o ### ### ==Ho ####### $########################### CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^##########$$ ## ### o ;H H #### ## H H o ### # # o # ## ### HH= HH ####### ############################ CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^########### ### o ; ;## ## HHH o # ### o ##### ; ==HH #### ; ############################## CCCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^^^^^^############## $ ### ; o ## HHHH o # # o H ## H o=H ### $############################## CCCCCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^^^^^^#################$ o H HoHH H Ho H H HHHHH === $#########oo###oo################# CCCCCCCCCC #
+D:#^^^^^^^^^^^^^^^^^^#############o#######oooooooooooooooooooooo ## HH H o H H H H$ HH===H=H ooooooooooooooooo$oooooooooooooooooo################## CCCCCCCC ; #
+D:#^^^^^^^^^^^^^^^^^############ooooooooooo# ### $$$ $$ # ### $$ $ o $$## HH$ = ==H#== H HH ====$$ $ ## $ oooooo##oooooooooo################### CCCCCCC #
+D:#^^^^^^^^^^^#################ooooooooooo####$######$ ## $$### ######### ###$$####o $$##### #ooo# ### $#=#=#$$$$ H ==##$#$ ##### ####################ooo##################### CCCCCCC #
+D:#^^^^^^^^^^###################oo######oo### ########### ######## ## #### ###########oooo########ooo ##########$#H##H H ######$# ###### ####################oo###################### CCCCCCC #
+D:#^^^^^^^##########oo####oo####ooo######### ############ ######### ###^^^ #########ooooo########oo##################H################### #########^######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/mods/theme/edit/t_d_lori.txt b/lib/mods/theme/edit/t_d_lori.txt
new file mode 100644
index 00000000..967441e9
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_lori.txt
@@ -0,0 +1,75 @@
+# File: t_d_lori.txt
+
+# Caras Galadhon map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+D:######################################################################################################################################################################################################
+D:# ,,, #
+D:# ,,,,,,,,,,,,,,,,,,,,,,,,, #
+D:# ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, #
+D:# ,,,,,,,,,,,CCCCCCCCCCCCCCCCCCCCCC,,,,,,,,,,,, #
+D:# ,,,,,,,,,CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC,,,,,,,, #
+D:# ,,,,,,,CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC,,,,,,,y #
+D:# ,,,,,,CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC,,,,,, #
+D:# ,,,,,CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC,,,,,, #
+D:# ,,,,,CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC,,,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDD,DDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCC,,,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCCCCCDDDDD D,@,D DDDDDCCCCCCCCCCCCCCCCCCCC,,,, #
+D:# ,,,,,CCCCCCCCCCCCCCCCCCCDDD D,D DDDCCCCCCCCCCCCCCCCCCC,,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCCDDD D,,,D DDDCCCCCCCCCCCCCCCCCC,,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCDDD DDDDDDD,,,DDDDDDDDD DDDCCCCCCCCCCCCCCCC,,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCDDD DDD,,,,,,,,,,,,,,,,,,,DDD D,DDCCCCCCCCCCCCCCCCC,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCD= DDD,,,,,,,,,,,,,,,,,,,,,,,,,DD D,=,DDCCCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCCDD,D DD,,,,DDDDDDDDD,,,DDDD,DDDDD,,,,D D,,,D DDCCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCCDD,=,D D,,,DDD D,,,D ,=, DD,,,D D,,DD DDCCCCCCCCCCCCCCC,,, #
+D:# ,,,,CCCCCCCCCCCCCCDD D,,,D D,,D D,,,D , DD,,D,,D DCCCCCCCCCCCCCCC,,, #
+D:# ,,,,CCCCCCCCCCCCCCDD DD,,D D,,D D,,,D D,,,D DCCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCDD D,,D,,D D,,,D DD,,,D DCCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCD D,,,D DD,,,,,DD D,,D,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCD D,,,DDD D,,D,,,D,,D DD,,D D,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,,,CCCCCCCCCCCCCCD D,,=,,,,DD D,,DD,,,DD,,D D,,,D D,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCDD D,,D,DDD,,,D D,,D D,,,D D,,D DD,,DD D,,D DCCCCCCCCCCCCCC,, #
+D:# ,,,CCCCCCCCCCCCCCD D,,D DD,,DD D,,D D,,,D D,,D,,,D ,,,D DCCCCCCCCCCCCCC,, #
+D:# ,,,CCCCCCCCCCCCCDD D,,D D,,,DD,,D D,,,D DD,,D ,=,,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCD D,,D DD,,,,DD DD,DD DD,,,,D , D,,D DCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCD D,,D D,,,,,DDD-----DD,,,DD,,D D,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,CCCCCCCCCCCCCCDD D,,D D,,DD,,,DD-----DD,DD D,,D D,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,CCCCCCCCCCCCCCDDD D,,D D,,D D,,---@@@---DD D,,D D,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,CCCCCCCCCCCCCCDD,DDDDDD,,DDDDDDDDDDDDD,,DDDDDDD--@@%@@--DDDDDDDD,,DDDDDDDDDDD,,DDDDDDDD,DDCCCCCCCCCCCCCC,,, #
+D:# ,,CCCCCCCCCCCCCCD,=,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--@%=%@--,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,=,DCCCCCCCCCCCCCC,,, #
+D:# ,,CCCCCCCCCCCCCCDD,DDDDDD,,DDDDDDDDDDDDD,,DDDDDDD---@%@@@-DDD D,,DDDDDDDDDDD,,DDDDDDDD,DDCCCCCCCCCCCCCC,,, #
+D:# ,,CCCCCCCCCCCCCCDDD D,,D D,,D DD,-----%%@,,,DD D,,D D,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCD D,,, D,,D D,DDD--,-@%%@D,,,DD,,D D,,D DCCCCCCCCCCCCCC,, #
+D:# ,,,CCCCCCCCCCCCCCD D,,=, D,,D,,D DD,=,D@%%@DD,,,DD ,,,D DCCCCCCCCCCCCCC,, #
+D:# ,,,CCCCCCCCCCCCCCD D,,D D,,,D DD,DD@%%%@ D,,,,DD ,=,,D DDCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCD D,,D DD,,,DD D,,,D @@%%@@DDD,,,DD ,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCD D,,D DD,,,DD,,D D,,,D @%%%@ DD,,,DD D,,D DDCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCD D,,D D,,,DD D,,D D,,,D D,@%%@ DD,,,D D,,D DCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCDD D,,D DDD,,DD D,,DD,,,D D,,D@%%@ DD,,D,,D DCCCCCCCCCCCCCCC,, #
+D:# ,,,CCCCCCCCCCCCCCCD D,,DD,,,,D D,,D,,,DD,,D @%%%@ DD,,DD DDCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCDD D,,,,,DD D,,,,,,,,D @%%@@ D,,D,,DD DDCCCCCCCCCCCCCCC,, #
+D:# ,,,CCCCCCCCCCCCCCCD D,,,DD DD,,,=,D @%%%@ D,,D DD,,D DDCCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCCD DD,,D,,D D,,,,D D,@%%@ D,,D DD,, DDCCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCCDD D,,,D D,,D , D,,,D D,,D@%%@,,D ,=,DDCCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCCDDD,=,D D,,DD,=,DDDDDDDDD,,,DDDDDDD,,,,@%%%@D ,DCCCCCCCCCCCCCCCC,,, #
+D:# ,,,CCCCCCCCCCCCCCCCCDD,D D,,,,,,,,,,,,,,,,,,,,,,,,,,DDD%@@%%@ DDCCCCCCCCCCCCCCCC,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCDD DDD,,,,,,,,,,,,,,,,,,,,,,D @%%%@@%%@@ DDCCCCCCCCCCCCCCCCC,,, #
+D:# $$=,,,,CCCCCCCCCCCCCCCCCDDD DDDDDDDDD,DD,,,DDDDDDD @%%=%%@@%%%@ DDCCCCCCCCCCCCCCCCCC,,, #
+D:# $## ,,,CCCCCCCCCCCCCCCCCCDDD ,=,D,,,D @%%%@ @%%@DCCCCCCCCCCCCCCCCCC,,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCCCDDD , D,,,DDDDD @@ DD@%%@CCCCCCCCCCCCCCCCC,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCCCCCDDDD D,,,,,,,,D DDDDDC@%%%@CCCCCCCCCCCCCC,,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCCCCCCCDDDDDDD D,,,,,,,,DDDDDDDCCCCCC@@%%@@CCCCCCCCCC,,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDD,,,CCCCCCCCCCCCCCC@%%%@CCCCCCC,,,,, #
+D:# ,,,,CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC,,,CCCCCCCCCCCCCCCCC@%%@CCCCC,,,,, #
+D:# ,,,,,CCCCCCCCCCCCCCCCCCCCCCCCCCCCC,,,CCCCCCCCCCCCCCCCCCC@%%@CC,,,,, #
+D:# ,,,,,,CCCCCCCCCCCCCCCCCCCCCCCCC,,,CCCCCCCCCCCCCCCCCCCC@%%%@,,, #
+D:# ,,,,,,CCCCCCCCCCCCCCCCCCCCC,,,CCCCCCCCCCCCCCCCCCCC,,@@%%@@ #
+D:# ,,,,,,,CCCCCCCCCCCCCCCCC,,,CCCCCCCCCCCCCCCC,,,,,,,,@%%%@ #
+D:# ,,,,,,,,,,CCCCCCCCCCC,,,CCCCCCCCCCC,,,,,,,,, @%%@ #
+D:# ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, @%%%@ #
+D:# ,,,,,,,,,,,,,,,,,,,,,,, @@%%%@ #
+D:######################################################################################################################################################################################################
diff --git a/lib/mods/theme/edit/t_d_mina.txt b/lib/mods/theme/edit/t_d_mina.txt
new file mode 100644
index 00000000..4018efc6
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_mina.txt
@@ -0,0 +1,79 @@
+# 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
+
+############### Town Layout ###############
+
+D:######################################################################################################################################################################################################
+D:#^^########------------------HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=@@@@@@@HHHHHHHHHHHHHHHHHH=@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==#
+D:#^^^----#####H=------##==-------HHHHHHHHHH===^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=@@%%%%%@@HHHHHHHHHHHHHH===@@%%%%%%%@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=,,,HHHHHHHHHHHHHHHHHHHHHHHH=#
+D:#^^^----------#$##---###=###-------HHHHHHHH=^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHHHHHHHHHHHH@%%%%%%%@@@@@HHHHHHHHHH==@@%%@@@@@@%%%%%%@@@@HHHHHHHHHDDDHHHDDDHHHHHHHHHHHHH=,,HHHHHHHHHHHHHHHHHHHHHHHH===#
+D:#^^----ssss-----#$#--------###==-----HHHHHHHH^^^^^^HHHHHHHHHHHHHHHHHHHHHHHDDHHHHHHHHHHHHH==@%%%%%%@@%%%@@@HHHHHHHHHH@%%@@HHHH@@@@@@%%%%@@HHHHHHHHHDDDDDHHHHHHHHHHHHH===,,HHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^^^---St-$=--------=--#ssss--###-------HHHH==^^^^^^^^HHHHHHHHHH==HHHHHHHHHH===HHHHHHHHH===@@%%%%%%%%@%%%@@@@@@@@=@@@%@@HHHHHHHHHH@@@@%%@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHH,HHHHHHHHHHH==HHHHHHHHHHHHHHHH#
+D:#^^----s=ss----OO---##--#StSS--===##=-----HHHH=^^^^^^^^HHHHHHHHH===HHHHHHHHHHH===HHHHHHHH===@@%@%,@@@@@@%%%%%%%%@@@%%%@HHHHHHHHHHHHHH@@%%%%@@HHHHHHHHHHHHHHHHHHHHHHHHH,,HHHHHHHHHHH==HDDDHHHHHHHHHHHH#
+D:#^^----##-#-----OOO--#---#s--$=----###------HHHH^^^^^^^^HHHHHHHHHH==HHHHHHHHHHHHHHHHHHHHHH===@@@@@HHHH=@@@@@@@@%%%%%@@@HHHHHHHHHHHHHH=@@@@%%@@@HHHHHHHHHHDDHHHHHHHHH,,,HHHHHDDDHHHHH==HHHHHHHHHHHHHH=#
+D:#^=---------------OO---#-####-#------HHHH----===^^^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=@%%%@@HHHHHHHHHHHHHHHHH@@%%%@@HHHHHHHHHHHHHHHHH===,O,HHHHHHHHHDDDDHHHHHHH=HHHHHHHHHHH=#
+D:#^=StSSSS-----ss---OO---##-----OOOOO---###----===^^^^^^^^^^^HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@HHHHHHHHHHHHHHHHH===@@%%%@@HHHHHHHHHHHHHHHHH==OOHHHHHHHHHHHHHHHHHHHHHHH=HHHHHHHH==#
+D:#^^ss-H==----Ssss---H=O--#-=--$#O-O-OO---##----===HD^^^^^^^HHHHHHHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHDDDHHHHHHHHHHHHHHHHHHHHHHHHHHH=@@%%%@HHHHHHHHHHHHHHHHH==OOHHHHHHHHHHHHHHHHHHHHHHHHHHDDHHHHHHH===#
+D:#^=#-$###---sH=$--=--OOO--=#-OOOOOOO-OOO--##----HHHH^^^^^HHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==@@%%%@@HHHHHHHHHHHHHHHHH=OOHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^^^-------##sssSss---HHO--#OOO--s--OOOOO--###=--HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@@HHHH=@@%%%@@HHHHHHHHHHHHHHHHH=OOHHHHHHHHHHHHHHHHHHHHHHDDDHHHHHHHHHHH=#
+D:#^^^##$$##---##H=H--s--OOO-OOO--StS#-OOOO----#==--HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==@@%%%%%%%%@@@@@@@%@@@@HHHHHHHHHHHHHHHHH=OOHHHHHHHHHHHHHHHHHHHHHHH=HHHHHHHHHHHH==#
+D:#^HH^----###---##--ssS--OOO--##-ssss--OOOOOO--#$---HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH====HHHHHHHHDDDDHHHH=@%%%%%%%%%%%%@@%%@@HHHH==OOOHHHHHHHHHHHHHHOOHHHHHHHHHHHHHHHHHH==HHHHHHHHHHHHH===#
+D:#^^--------#$-----ssSs#--H=#-#--#H=H=#-OOHHHO--##-HHHHHHHHHHHHHH----HHHHHHHHHHHHHHHHHHHHHHHH=========HHHHHHHHHHHHHHHHHHH@%%%%@@@@%%%%%%%@@HHHH==OOOOOHHHHHHHH=OOOOOHHHHHHHHHHHH=HHHHHHHHHHDDDHHHHHHHH#
+D:#^=-----------#--#s-$$=-OO-O--#---------OOOOOO--==-----HHHH===--------HHHHHHHHHHHHHHHHHHHHHHHHH====HHHHHHHHHHHHDHHHHHH=@@%%%@@==@@%%%@@@@HHHH===OO==OOHHHH===OOHHHHHHHHHHHHHHHHHH=HHHHHHHHHHHHHHHHH==#
+D:#^^----HHH----$---###--OOOOOO-#--#sss#$=-OOOOOO,#====---HHHH=---#-------HHHHHHHHHHHHHHHHHHHHHHHH==HHHHHHHHHHHHHHHHHH==@@%%%@@HHHH@@@@@HHHHHHHH=OO-===OOOOOOOOOHHHHHHHHHHHHHHHHHHH=HHHHHHHHHHHHHHHH===#
+D:#^^-------------##-#-#-O#-OOO--#--ssssss----OOO,,,,,#----==-HHssH=--$----HHHHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHDDDHHHHHH=@%%%@@HHHHHHHHHHHHHH===-O$HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==HHHHHHH=#
+D:#^^-------------$#---O-O-t-OOO-=#-#SS-##--OOOOOO,##,##--------sssssssHHH-#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHHHHHH@%%@@HHHHHHHHHHHHHH==--OO-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===HHHHH=#
+D:#^^^-=H=--HH-----##-OOO-sssOOHH-#--ssssss-OOOOOO--#,#$$##-----SS$---SS-----HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDHHHHHHH@@%%@HHHHHHHHHHHHHH==--O$-HHHHHHHHHHH==HHHHHHHHDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^^^--H=----------#OOO$H#-$=OOO-#--ss#sss--OHHOOO==HHH=##==---s===H=ss--O---HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==@@%%%@HHHHHHHHHHHHHH==-OO--HHHHHHHHHHHHHHHHHHHHHHH==HHHHH===HHHHHHHHHHHHHHHHHHHHH#
+D:#^^--###--HHHH--HH--O-------OOHHH--####HHH-OOOOOO-##,###,,##--sss=H=Hs--HHH-HHHHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHH=@%%%@@HHHHHHHHHHHHHH=--O--HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==HHHHHHHHHHHHHHHH=#
+D:#^^^-##H=H-------OOO#--SSStS---OHHHHH----HHHOOOO---#HHHHHH=#=-ssss$-#=--O-----HHHHHHHHHHHHHHHHHHHHHHHHH==HHHHHHH===@@%%@@HHHHHHHHHHHHHH=--O$-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==#
+D:#^^-####$-------OOO-#--sssss-OOO-##---#s--OOOOO--T-#,--HHHHH--H=H##HH#--O---HH-HHHHHHHHHHHHHHHHHHHH==HHHHHHHHHHHH==@%%%@HHHHHHHHHHHHHH=--OO--HHHHHHHHHH==HHHHHHHHDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^^#######------OO--##-###$-HHHO-##-ssss---OOOO-TT-=HH-HHH,#----HHH,,,,,O------HHHHHHHHHH===HHHHHHHHH====HHHHHHHH=@@%%@@HHHHHHHHHHHHHH--OO--HHHHHHHHHHHHHHHHHHHHHDH==HHHHDDDHHHHHHHHHHHHHHHHHHHHHHHH=#
+D:#^^^###H=H=#----OOO--#---$---OOO--HHS#-$--OOOOHHHT-#---HH,,###----------O---HH--HHHHHHHHH==H=HHHHHHHHHH===HHHHHHHH@%%%@@HHHHHHHHHH===--OO--HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==HHHHHHHHHHHHHHHHHH==#
+D:#^^#####$--####-O$---##-StSSS-HHO-#-sss-$=OOOOO--T-#=---,,##==-s-ssH==--O--------HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@%%@@HHHHHHHHHH===--OD$--HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=#
+D:#^^^#########---OOOHHH#-sss-$=--O---ssss-HHOOOOO---==,HHH#==---SSSH=H==-O--HHH-HH-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=@@@%%%@HHHHHHHHHH===--O$O--HHHHHHHHH=HHHHHHHHHHHHHHHHHHHDDDHHHHHHHHHHHH===HHHHHH===#
+D:#^^###HHH###----OOO#--#-###H##OOO-#-##H#---OOOOOO--##==#H#-----s$-ssss--O----------HHHHHHHH===HHHHHHHHHHHHHHHHHHHH@%%%%@@HHHHHHHHHH=---OOO--HHHHHHHHHHHHHHHHHHH=HHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHH#
+D:#^^^##$##-------OOO---#-#--^--OOO-#----#----OO--$$OO=OOOOOOOO--####--$#-O-----------HHHHHHHH==HHHHHHHHHHHHHHHHH==@@%%%@@HHHHHHHH=-H----OO--HHHHHHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHHHHHHH===HHHHHHHH=#
+D:#^^######-##OOO$OOO-^^^^^^^^-^MMM^^^^^^^^#^^^OOOOOO-$OOOO-$OOHOOOOOOOHHOO----HHH----HHHHHHHHHH=HHHHHHHHHHHHHHHH@@@%%%@@HHHHHHHH=H-HOOOOOO--HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDD=HHHH==H===HHHHHHHH=#
+D:#^^####H==HHO$OOOO######H#####III##-#########^OOHHHOOOOOOO#OOOOOOOOOO#OOOOO-------HHHHHHHHHHHHHHHHHHHHHHHHHHH=@@%%%%@@HHHHHHHH=---OOOOOO--HHHHHHHHHHHHHHHHHHHHHHH====HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=#
+D:#^=######-##OOOO-##-####HH#HH#III#####-##--##^OOOO$-OOOOOOOOHHHOO-OOOOOOOOOO------H--HHHHHHHHHHHHHHHHHHHHHHHHH@%%%%@@HHHHHHHH----OOO$$O----HHHHHHHHHHHHHHHHHHHHHHH====HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^=###-H##-OOOOOOOO-^#^^^^^#H^MM#^^^^#^^#^^-#H=OOH=OOOOO$-OOOOHOOOOOOOOOOOOOOO--HH---HHHHHHHHHHHHHHHHHHHHHHHHH@%%%@@HHHH==H--HD-OOO$-----HHHHHHHHHH=HHHHHHHHHHHHHHHH=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==#
+D:#^^^#######-----OOO---H-sHH#--OOO##--##-----$--OOOOOOOOOOOOOO-----------OOO-$-O----HHHHHHHHHHHHHHHH===---------##@%%%$##=--------OOOOO-----HHHHHHHHHHHHHHHHH=HHHHHHHHHHHHHHHHHHHHHHHHH=====HHHHHHHHHH#
+D:#^^#########----OHO---#-sSss--OOO---ssss#--OOOOOO--##==###---HH---ss-#-#O-OOOOO-HH--HHHHHHHHHHHHHH===--OOOO-----######$--------OOOOOO-----HHHHHHHHHHHHHDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHH=#
+D:#^^####$-H=H#---OO$--$--sts##-O-$-#-SSHSS-OOOOOO---##=##H==---------t-S-O-OO$OO----HHHHHHHHHHHHHH===--OOOOOOOOOOOOO#OOOO$OOOOOOOOOOO-HHH-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=DDHHHH==HHHHHHHHHHH#
+D:#^^###########--OOO--##-sS=$--OOO-$-s-#---H=OOO--T==#=#,,,##=#--H-sssss-O--OOOOO---HHHHHHHHHHHHHH==--$OOOOOOOOOOOOOOOOOOOOOOOOO#HOO-----HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=HDDDHHHHH=#
+D:#^^#$--$--##----OOO--#--####-OOO--#-sssss-OOHOO-TT-#,,,,-HH==-----sHH=H-$---O$-O---HHHHHHHHHHHHHH=--OOOHH-OOOOOOO$HHO#OOOOOOOOOOO------HHHHHHHHH=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH====HHHHHHHHH=#
+D:#^^#######------O-$-##-------OO---#-###H#-OOOOO-TT-=#----H==#=----###$--O-HHOOOOHH--HHHHHHHHHH===--OOOO---------#######------------H--HHHHHHHHHHHHHHHHHHH=HHHHHDDHHHHHHDDHHHHHHHHHHHHHDDDHHHHHHHHH===#
+D:#^^-###$-#-HH---OOO-#--S-ss##OOO-#--------OOOOO--T-##-----HHH----H------H----OOOO----HHHHHHHHHH=--OOOH----DD---##@%%%@##-D-----H---HHHHHHHHHHHH===HHHHHHHHHHHHHHHH=HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==#
+D:#^^^-###---------OO-#-#stss--OOO-#-ssssss--O$-$O---#,--HH,H#----sss-#s--O-----OOOO--H-HHHHHHHH---OOOO---HHHHHHHH=@%%%@@HHHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHHHH=HHHHHHHHHHHHH==HHHHHHHHHH#
+D:#^^--####---------OOO--#s#s-OOO--=$StS##---OH=HOO-#==-,,#=##----S--#HH--O------OO$-----HHHH==--OOOOO----HHHHHHHH=@%%%@@HHHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=#
+D:#^^---#HH--HH-----#OOO--##--OOO-###ssssss--OOOOOO-#,,,##==#------$sH=H--O--H----OO$#----HHHH--OOOOO----HHHHHHHHHH@%%%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^^^--#------H==-$--H=HHH--OO----#-###H##-OO$-OO--#=####HHH-HH--ss#-ss-OO----D---OOOO----HH--OOOOO---===HHHHHHHH=@@%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=@@@@HHHHHHHHHHHHHHHHHHHH===#
+D:#^^^-------------#---OOO---OOO--#=------##OOOOOO##HHHH=-HH--HH--#-##-HHH-$-HH-----$--H----OOOOOO-HH--HHHHHHHHHH===@@%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=@@@@@@HHHHHHHHHHDDHHHHHHHH===#
+D:#^^----------H==H-----H=HHOOO-----ss------OO$-O,,##=HHH--------HHH-----HHH--H--HHH-OOOOOOOO$OO-----HHHHHHHHHHHHHH==@@@%%%%@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@@@@@@@%@@@HHHHHHHHHHHHDDDDHHHH==#
+D:#^^^----HHHH-$-##-#ss--OOO#OO-##=-#Ss----OOOH-O,=#HHH---HHHHH-------HH-O--------=---OOO#OOOOO--HHHH===HHHHHHHHHHHHHH=@@%%%%@@HHHHHHHHHHHHHHHHHHHH@@@@@HHHHHHHHHH@@@@@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^^^---------##---ss-s--OOOO----=sst#DD-OOOOOO--#=-----=------ssss-s--O$---H--HHHH---OOOOO-H--HH==HHHHHHHHHHHHHHHHH===@@%%%%@@@HHHHHHHHHHHHHH==@@@%%%@@@HHHHHHHH@@@%@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHH=#
+D:#^^--------##$---sstss#--OOO-##=ssS-#--OOO#OO--##=----HHHH-----#S--$$-OH-----HHHH==----------HHHHHHHHHHHHHHHHHHHHHHHHH=@@@%%%%@@@@@HHHHHHHH=@@@@%%%%%%%@@===@@@@@%@@%@@@%@@HHHHHHHHHHHHHHHHHHHHHHHH==#
+D:#^^-HHHH-###-----#Ss-$--OOOOO###sSs#--OOOOOO--##-----HHHH==---ssssss--O-----HHHHHHHHHHH-HH-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@@@%%%%%%@@@@@@@@@@@%%%%%@@@%%%@@@@@%%%%@@@@=@@@@HHHHHHHHHHHHHHHHHHHHHHHH===#
+D:#^^^######----#---#s#--OOO-OOO--Ss#--#OHH#---##-#HHHHHHHHHHH--##--##-OO--DHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===@@%%%%%%%%%%%%%%%%%@@@@@=@@@%%%%%%%@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHDDHHHHH=#
+D:#^^^-------ss-#S---#--OOO---HH#--#--OOOO$--#-------HHHHHHHHHH--------O-----HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=@@@@@@@@@@@@@@@@@@@HHHH===@@@@@@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^^-sssss-#sssts-----OOO--#--OOO---OOOOO---#H=-HHHHHHHHHHHHHH=------OO-HHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===#
+D:#^^-SSStH--#sSH=s#--OOO--#----HOOOOOOHH--#-------HHHH==HHH==HHH=HH--O----HHHHHDDHHHHHHHHHHHHHHHHHHHHH======HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^^-s=H=----#ss-$--OO----#--H=-OHOOO------------HHHHHHHH=HHH==HH---O---HHHHHHHDDDHHHHH====HHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHDDHHHHHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHH==#
+D:#^^^#####----##---OO--###--XXX-OOO----#-------HHHHHHHHHHHHH=HHHH==---HHHHHHHHHHHHHHHHHHHHHHHHDDHHHHDDD==HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDHHHHH=H===HHHHHHHHHHHHHHHH=======HHHHHH===HHHHHHHHHHHHHH#
+D:#^^^------------OOO--###--XXX#----=H==H----HHHHHHHHHDDDDHH=HHH=HHHHHHHHHHHHDDDHHHHHHHHHDDHHHDDHHHHH=DD=HHHHHHDDHHHHHH===HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDDHHHHHHHHHHHHH==H==HHHHHHHH====HHHHHHHHHH===#
+D:#^^--SStSSHHH--OO-HHH#--XXHH#---#H=H-HHH-HHHHHHHDDDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHHHHHHHHHHHH====HHHHHHHHHHHHHHHHHHH====HHHHHHHHHHHHHHHHHHHH==H==HHHHHH=====HDDHHHHHDDDHHHH=#
+D:#^^^-sss-$----------#--###-----H=#-----HHHHHHHHHDDDDH=HH=HHH=HHH=HHHDDDHHHHHHHHHHDDHHHHHDDDHHHHHHHHHHHHHHHHDDDHHHHHHHHH===HHHHHHHHHHHHHHHHHHHH===HHHHHHHHHDDHDDDHHHHH=====HHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#^^--####-------###--------###-#-#---HHHHHHHHHHHHHHHHHH=HH=HH=HHHHHHDDDDHHHHHHHHH==HHHDDDHHHHHH==H=====HHDDHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH==#
+D:#^^^----------##-----##-#-#-H==---HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH====HHHHHHHHHHHHH====HHHHHHHHHHHHHHHHHDDDHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH===#
+D:#^^^------##-#--##-###--#-------HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHDDDHHHHHH=====HHHHHHHHHHHHH===HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=#
+D:#^^######-#-----------------HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH=#
+D:######################################################################################################################################################################################################
diff --git a/lib/mods/theme/edit/t_d_osgili.txt b/lib/mods/theme/edit/t_d_osgili.txt
new file mode 100644
index 00000000..bb21607b
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_osgili.txt
@@ -0,0 +1,78 @@
+# File: t_d_osgili.txt
+
+# Osgiliath map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+#Anduin river
+F:a:227:3
+
+D:######################################################################################################################################################################################################
+D:# #################@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@########################## #
+D:# ######@@@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@@@@@@@@@@@@@@@#### ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#
+D:# #####@@@@@@@########@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@#####@@@#####@@@#####@@###### ,,, #
+D:# ####@@@@@@@@@,########@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@#####,,,#####,,,#####,@@@@@##### ,,, #
+D:# ####@@@@@@@@@@,,########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@#####@@@#####@@@#####@,@@@@@@@##### ,,, #
+D:# ####@@@@########,@,@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@,@@@@@@@,@@@@@@@,@@@@,,,,,@@@@@#### ,,, #
+D:# ####@@@@@,########,@,@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@,,,,@@@@,,,,@@@@,,,,@@@@@,,,,@@@#### ,,, #
+D:# $###@@@@@@@,@########@@,@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@#####@@@#####@@@#####@@@@#####@@@#### ,,, #
+D:# $$$@@@@@@@@@@@,,@@@@@@@@@,########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@#####,,,#####,,,#####,,,@#####@@@@#####,,, #
+D:# $,,=@@@@@@########,@@@@@@@@,########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@#####@@@#####@@@#####@@@@#####@@@@@@@#,,, #
+D:# ##=,,D@@@@@########,@@@@@@@@,########@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@,@@@@@@@,@@@@@@@,@@@@@@@@,@@@@@@@@=,,=### #
+D:# ###@D,,D@@@@########@,@@@@@@@,@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@,,,@@@@@,,,,@@@@,@@@@@@@,@@@@@@@=,,= ### #
+D:# ##@@@@D,,D@@@@@@@@@@@@@,@@@@D@@,@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@#####@@@#####@@@D@@@@#####@@@@=,,= ## #
+D:# ##@@@@@@D,,D@@@@@@@@@@@@@,@@DDD@@,@########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@#####,,,#####,,DDD,,,#####@@@=,,= ## #
+D:# ##@@@@@#@@D,,D@@@@@########,@@D@@@@,########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@#####@@@#####@@@D@@@@#####@@=,,= ## #
+D:# ##@@@@@@#@@@D,,D@@@@########,@@@@@@@,########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@,@@@@@@@,@@@@@@@@@@@@,@@@=,,= DDDDD ## #
+D:# ##@@@@@@###@@@D,,D@@@########@,@@@@@@,@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@,@@@@@@@,@@@@@@,,,,,@@@=,,= DDDDDDD ## #
+D:# ##@@@@@@@###@@@@D,,D@@@@@@@@@@@@,@@@@@,@@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@#####@@@#####@@#####@@@@=,,= DDDDD ## #
+D:# ##@@@@@@@#####@@@@D,,D@@@@@@@@@@@@,,,@@@,@########@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@#####,,,#####@@#####@@@=,,= ## #
+D:# ##@@@@@@@@#####@@@@@D,,D@@@@@@########,@@@,########@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@#####@@@#####@@#####@@=,,=,,,,,,,,,,,,,,,,,,,## #
+D:# ##@@@@@@###########@@@D,,D@@@@@########,@@@,########@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@,@@@@@@@,@@@@@@,@@@=,,= ,,$$,,,#####,,###,, ## #
+D:# ##@@@@@@###########@@@@D,,D@@@@########@,@,@@@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@,,@@@@@@@@@,,,,,,@@@=,,= ,,#$$,,#####,,###,, ## #
+D:# ##@@@#################@@D,,D@@@@@@@@@@@@@,,,@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@#####@@@@@@@#####@@@@=,,= H ,,###############,, ## #
+D:# ##@@@@#################@@@D,,D@@@@@@@########,@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@#####,,,,,,,#####@@@=,,= HH ,,###############,, ## #
+D:# ##@@@@#################@@@@D,,D@@@@@@########,@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@#####@@@@@@@#####@@=,,= HHH ,,,#############,,, ## #
+D:# ##@@@@@@@@@@@@@@@@@@@@@@@@@@D,,D@@@@@########@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@@@@@@@@@@@@@=,,= HHHH ,,,#############,,, ## #
+D:# ################@@@@@@@@@@@@@@=,,=@@@@@@@@@@@@@@@@@@@@=##aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@##=@@@@@@@@@@@@@@@@@=,,= HHHHH ,,,#########$$$$,,, ## #
+D:# ##D DDDDDDDDDDDDDDDD,,DDDDDDDDDDDDDDDDDDDDDD#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa###DDDDDDDDDDDDDDDDDDD,,= ,,,###########$$,,, #
+D:# ##D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,$$###########$$,, #
+D:# ##D,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,$############$$,, #
+D:# ##D,,cccccccccccDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD,,DDD#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#DDDDDDDDDDDDDDDDDDD,,= ,,$##############,, CC #
+D:# ##D,,c %%%%% c@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=,,=@=##@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@##=@@@@@@@@@@@@@@@@=,,=DDDDDDD ,,,##############,, #
+D:# ##D,,c %%=%=%% c@@@######@@@@@@@@@@@@@@@@@D@@@@@D,,D@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@@@@@@@@@=,,=D,,,,,D ,,$$#############,, #
+D:# ##D,,c%%%%%%%%%c@@@######@@@@@@@@@@@@@@@@DDD@@@D,,D@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@=HHHHHHHHH=@@@@=,,=D,%%%,D ,,$$#############,, ## #
+D:# ##D,,c %%=%=%%,c@@@######@@@@@@@@@@@@@@@@@D@@@D,,D@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@HHHHHHHHHHHHH@@@=,,=D,%%%DD ,,$$############$$, ## #
+D:# ##D,,c %%%%% ,c@@@######@@@@@@@@######@@@@@@D,,D@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@=HHHHHHHHH=@@@@=,,=D,,,,,,,,,,############$$$, ## #
+D:# ##D,,ccccccccc,@@@@@@,@@@@@@@@@@@######@@@@@D,,D@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@@@@@@@@@@@=,,=DDDDDDD ,,,###########$$,,, ## #
+D:# ##D,,,,,,,,,,,,@@@@@@,@@@@@DD@@@@######@@@@D,,D##########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=DDDDDDDDDDDDDDDDDDDDDD,, ,,,#############,,, ## #
+D:# ################@@@@@@,@@@DDDD@@@######@@@D,,D@##########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,#############,,, ## #
+D:# ##@@@@@@@@@@@@@@######,@@@DD@@@@@@,@@@@@D,,D@@##########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=DDDDDDDDDDDDDDDDDDDDDDD,, ,,#############$$,, ## #
+D:# ##@@@@@@@@@@@@@@######@,@@@@@@@@@,@@@@@D,,D@@@##,####,##@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@@@@@@@@@@@@@@@=,,=HHHHH ,,###############,, ## #
+D:# ##@@@######@@@@######@@,@@######,@@@@D,,D@@@@@@,@@@@,@@@=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@=HHHHHHHHHHH=@@@@@@=,,=HHHH ,,###,,#$$##,,#$$,, ## #
+D:# ##@@@######@@@@######@@@,@######,@@@D,,,,,,,,,,,,,,,,,,,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@HHHHHHHHHHHHH@@@@@@@=,,=HHH ,,$$#,,$$###,,$$#,, ## #
+D:# ##@@######@@@@@@,@@@@@@@,######,@@D,,D@@@@@@@@@@@@@@@@@=#########aaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@=HHHHHHHHHHH=@@@@@@@@=,,=HH ,,,,,,,,,,,,,,,,,,,## #
+D:# ##@######@@@@@@@,@@@@@@,######,@D,,D@@@@@@@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=,,=H , ## #
+D:# ##@@@,@@@@@@@@@@,@@@@@@@,,,,,,,D,,=DDDDDDDDDDDDDDDDDD=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD,,= , DDDDD ## #
+D:# ##@@@,@@@@@@@@@,@@@@@@@@,@@@@D,,,,,,,,,,,,,,,,,,,,,,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, DDDDDDD ### #
+D:# ##@@@@,######@,@######,@@@@D,,=DD,DDDDDDDDDDDDDDDDD=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD,, DDDDD ## #
+D:# ##@@@,######@,@######,@@@D,,D@@D,D@@@@@@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@=,,= ## #
+D:# ##@@,######@,@######,@@D,,D@@@D,D@@@@@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@#####@@@@@@@@@@@@@@@@@@@@#####@@@=,,= ## #
+D:# ##@,######@,@######,@D,,D#####,#####@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@#####,,,,,,,,,,,,,,,,,,,,#####@@@@=,,= ### #
+D:# ##@,,,@@@@,@@@,,,,@D,,D@#k@=H,H=@m#@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@#####@@@@@@@@@@@@@@@@@@@@#####@@@@@=,,= ## #
+D:# ##@@@,,,,,,,,@@@@D,,D@@#@@#H,H#@@#@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@,@@@@@@@@@@@@@@@@@@@@@@@@,,,,@@@@@=,,= ## #
+D:# ###@@@@@@@D@@@@D,,D@@@#@@#H,H#@@#@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@#####@@@@@@@@=@@@@@@@@@@@@@@#####@@@@=,, =##$ #
+D:# ###@@@@DDD@@D,,D@####@##H,H##@####@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@#####@@@@@@@@@@@@@@@,,,,,,,,#####@@@@@=,,,,, #
+D:# ###@@@D@@D,,D@@#@@@@# H,H #@@@@#@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@#####@@@@@@@@@@@@@@,@#####@@#####@@@@@##$$ #
+D:# ###@@@D,,D@@@#@@@@# H,H #@@@@#@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@,@@@@@@@@@@,,,,,,@@#####@@@@@@@@@#### #
+D:# ##$=,,D@@@@####@##H,H##@####@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@#####@@@@@@@,@#####@@#####@@@@@@@#### #
+D:# ,=@@@@@@@@#@@#H4H#@@#@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@#####@@,,,,,@@#####@@@@@@@@@@@@#### #
+D:# $##@@@@@@@#@@#####@@#@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@#####@,#####@@#####@@@@@@@@@#### #
+D:# ###@@@@@#l@@@@@@@H#@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@,@@,@#####@@@@@@@@@@@@@##### #
+D:# ###@@@###########@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@@@@@,@@@#####@@@@@@@@@@#### #
+D:# ###@@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@@@@@@@@@@@@@@@@@@@@@@@@@@###### #
+D:# ###############@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@############################## #
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_pelar.txt b/lib/mods/theme/edit/t_d_pelar.txt
new file mode 100644
index 00000000..246f2d34
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_pelar.txt
@@ -0,0 +1,78 @@
+# File: t_d_pelar.txt
+
+# Pelargir map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+#Anduin river
+F:a:227:3
+
+D:######################################################################################################################################################################################################
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@##%%%%%@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH# #
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####%%%%%@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@######%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@###%%%%%@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@####%%%%%@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@####@@@@#####%%%%%@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@####@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@####@@@@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%###@@@@@@@DDDD@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%##@@@@DDDD@DDDD@@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%@@@DDDD@@@DDDD@@@@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%@@DDDD@@@@@@@@@@@@@@@@@@@@#####%%%%%@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%@DDDD@@@@###@@@@@@#######@@@@@#####%%%%%@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%@@@@@@@@@####@@@@@#######@@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%@@@@@@@@@@####@@@@#######@@@@@@,,,,#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%@@@@@@@@@@@####@@@@@@@@@@@@@,,,,@@@@@@##%%%%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%#@@@@@@@@@@@@####@@@@@@@@@,,,,@@@@@@@@@#%%%%###%%%%%@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%##@@#######@@@@#######@@,,,,@@@@@@@@@@@#%%%%#@@#####%%%%%@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#######@@@@#####,,,,,@@@@DDD@@@@@@#%%%%#@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#######@@@@###,,,.@@@@@@DDDDD@@@@#%%%%#@@@@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#######@@@@#,,,##,###@@@@DDD@@@@#%%%%#@@@@####@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@@@@@@@@@@,,,,##,,,####@@@@@@@#%%%%#,@@@@####@@@@@@@@#####%%%%%@@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@@@@@@@,,,,@@@##,,,,,,####@@@#%%%%#@@,@@@####@@@@@@@@@,@#####%%%%%@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@@@@,,,,@@@@@@##,,,,,,,,,####%%%%#@@@@,,,,@@@@@@@@@@@@@,@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@,,,,@@@DDD@@@##,,,,,,,,,,,,,,,,#@@@@@@@@,,,,@@@@@@@@@@,,,,,,,#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@,,,,@@@@@DDDDD@,##,,,,,,,,,,,,,,,$###@@@@@@@@@,,,@@@@@@@@,@@@@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHHHHHHHHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,,,,,,,,,,,,,@@@@@@@@@DDD@@@##,,,,,,,,,,,,,,,,,,####@@@@@@,@@,,,@@@@@,@@@@@@@@@@@@#####%%%%%@@@HHHHHHHHHHHHHHHHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@@@@@@@@@@@@@@##,,,,,,@####,,,,,,,,,,####@@@,@@@@@,@#######@@@@@@@@@@@@#####%%%%%@@@HHHHHHHHHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@#######################,,,,,########,,,,,,,,,,,####,@@@@@@,#######@@@@@@@@@@@@@@@#####%%%%%@HHHHHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,%%%%%%%%%%%%%%%%%%%%%%%%%%%,,,,,,##########,,,,,,,,,,,,,CCCC@@#############@@@@@@@#####@@@#####%%%%%@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH,%%%%%%%%%%%%%%%%%%%%%%%%%%%,,,,,,,########,,,,,,,,,,,,,,,,,,,,#############,,,,,,,,,$@@@@@@@@#####%%%@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%%%%%%%%%%%%%%%%%%%%%%%,,,,,,,,,####,,,,,,,,,,aaaaaa$###@@#############@@@@@@@#####@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@#####################CC,,,,,,,,,,,,,,,,,,,,,,aaaaaaaaa@@@@@#######@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@@@@@@@@@,,,,,CC,,,,,,,,,,,,,,,,,,,,####aaaaaaaa@@@@#######@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#####@@@@,@@@@@CC,,,,,,,,,,,,,,,,,,###@@@@@aaaaaaaa@@@@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#####,,@,@@@@@@CC,,,,,,,,,,,,,,,,,###@@@@@####aaaaaaaa@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#####@@,@@####@CC,,,,,,,,,,,,,,,,aaa##@@@@#######@aaaaaaaa@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@,@@@@,,,####@CC,,,,,,,,,####aaaaaaa##@@@#######@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@,@@@@,@@####@CC,,,,,$$###@@##aaaaaaa##@@@@@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@##@@@@@@,@@@@@@CC,,####@@@@@@@##aaaaaaa##@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#@@@$@@@@,@@@@@CC,###@@@@@@@@@@##aaaaaaa##@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@####$@@@@@,@@@@#,###@@@@@@@@@@@@##aaaaaaa##aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@,@@@@@@@@,@@@,,@@@@@#######@@@@##aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@,@@@@@,@@,,,,@,@@@@@#######@@@@@#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@@$,@@@@@,##$$$@,@@@@#######@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@##@@@@@@@,@##$##@@,@@@#######@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#####@@@,@@#####@@@,@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@,@@@@,@@@@@@@@@@@@,@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@,@@@,@@@@@@@@@@@@@@,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#####@,@@####$@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@###@,@@#@@@@@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@###@@@,@@###@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@,@@@@,@@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@#@@@@@,@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@#@@@,aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@@@@#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####@@@#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@##########aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@#######aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH@%%%%%@##aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_d_thrand.txt b/lib/mods/theme/edit/t_d_thrand.txt
new file mode 100644
index 00000000..156dbb43
--- /dev/null
+++ b/lib/mods/theme/edit/t_d_thrand.txt
@@ -0,0 +1,75 @@
+# File: t_d_thrand.txt
+
+# Thranduil's Halls map and destruction by furiosity <furiosity@zionmainframe.net>
+
+# NB! This file assumes usage of the following files from the 'theme' module:
+# f_info.txt and t_pref.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+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:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+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:# ^^^^^^^^^%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# ^^^^^^%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%^^^^^^^^^^^^^^^^^^^^^^^^CCC^^^^^^#
+D:# ^^^^^%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%^^^^^^^^^^^^^^^^^^^CCC^^^^^#
+D:# ^^^^^^^^^^^^%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%^^^^^^^^^^^^^^^CCC^^^^#
+D:# ^^^^^^%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%^^^^^^^^^^^CCC^^^#
+D:# ^^^^%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%^^^^^^^^^CCC^^#
+D:# ^^^^^^^%%%%%%%%%%%%%%%%%%%%%%%%%%%%%^^^^CCC^#
+D:# ^^^^^^^^%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_dale.txt b/lib/mods/theme/edit/t_dale.txt
new file mode 100644
index 00000000..5c2dcf6c
--- /dev/null
+++ b/lib/mods/theme/edit/t_dale.txt
@@ -0,0 +1,96 @@
+# File: t_dale.txt
+
+# Dale (rebuilding) map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain features ###
+
+#light post
+F:l:221:3
+
+# Wooden boards (4 kinds)
+F:=:233:3
+F:[:234:3
+F:_:235:3
+F:]:236:3
+
+### Buildings ###
+
+# Bard's Hut
+F:b:74:3:0:0:0:0:0:77
+
+# Construction Supply Store
+F:c:74:3:0:0:0:0:0:63
+
+# Builder Barracks
+F:i:74:3:0:0:0:0:0:71
+
+D:######################################################################################################################################################################################################
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# #
+D:# @@@@@@@@@@@@@@@@ #
+D:# @@WWWWWWWWWWWWWWWW@@ #
+D:# @@@WWWWWWWWWWVWWWWWWWWW@ #
+D:# @WWWWWWWWWWWVVVWWWWWWWWWW@ #
+D:# @WWWWWWWWWWWVWWWWWWWWWWW@############### #
+D:# @@@@WWWWWWWWWWWWWWWWW@@@-------------------------------------------------------------------------------------- #
+D:# @WWWWWWWWWWW@@@@@@------------------------------------------------------------------------------------------ #
+D:# @W@@@@@@@@@@@@--StSSSSS--------------------------------------------TTT----------------------------------------- @@@@@@ #
+D:# @W#-------------sssssss-------------------------------------------TTTTT------------------------------------------- @@WWWWWW@ #
+D:# @W#-------------#######--------------------------------------------TTT----------------------------------------- @@WWWWWWWWWW@@@ #
+D:# @W#-------------###c###------------------------------------------------------------------------------------------------- @WWWWWWWWWWWWWWW@@ #
+D:# @W#-----------l----@----l-----------------------------------------------------------------------------------------------------@WWWWWWWWWWWWWWWW@ #
+D:# ##----------------@---------------------------------------l-----------------------------------------------------------------]]]]]]]]]]]]]]]]]]@ #
+D:#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@,,,,,,,,,,,,,,,,,,@@@@@@@@@@@@@@@@@@@@@#
+D:# #-----------------@---------------------------------------@----SSStS------------------------------------------------------@]]]]]]]]]]]]]]]]]]@ #
+D:# #------------------@---------------------------------------@-E-sssss-----------------------------------------------------@WWWWWWWWWWWWWWWW@@@ #
+D:# #-------------------@---------------------------------------@--#####------------------------------------------------------@@@WWWWWWWWWWWWWW@ #
+D:# #--------------------@---------------------------------------@-##b##---------------------------------------------------------@@@WWWWWWWWW@@ #
+D:# #---------------------@---------------------------------------@@@@---l----------------------------------------------------------@@@@@@@@@@ #
+D:# #----------------------@-------------------------------------@----@---------------------------------------------------------------------- #
+D:# #-----------------------@-----------------SSS---------------@------@---------------------------------------------------------------------- #
+D:# #------------------------@--------------sssss--------------@--------@---------------------------------------------------------------------- #
+D:# #-------------------------@---------#########-------------@----------@---------------------------------------------------------------------- #
+D:# #--------------------------@--------#########------------@------------@---------------------------------------------------------------------- #
+D:# #---------------------------@------@---------------,@@@@@--------------@---------------------------------------------------------------------- #
+D:# #----------------------------@@@@@@@------------------------------------@---------------------------------------------------------------------- #
+D:# #--------------------------------------------l---------------------------@---------------------------------------------------------------------- #
+D:# #-------------------------------------------------------------------------@---------------------------------------------------------------------- #
+D:# #--------------------------------------------------------------------------@l--------------------------------------------------------------------- #
+D:#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#
+D:# #--------------------------------------------------------------------------l-@--------------UUUUUUYUUUU-----------------------------------------------------------#
+D:# #-----------------------------------------------------------------------------@-------------XXXXXXXXXXX-----------------------------------------------------------#
+D:# #------------------------------------------------------------------------------@------------___________-----------------------------------------------------------#
+D:# #-------------------------------------------------------------------------------@l----------]]]]]i]]]]]-----------------------------------------------------------#
+D:# #-------------------------------------------------------------------------------l@@@@@@@@@@@@@@@@@------l---------------------------------------------------------#
+D:# --#---------------------------------@@@@@---------------------------------------------------------------------------------------------------------------------------#
+D:# ----#--------------------------------@WWWWW@--------------------------------------------------------------------------------------------------------------------------#
+D:# -----T#-------------------------------@WWWWWWW@-------------------------------------------------------------------------------------------------------------------------#
+D:# -------TT#------------------------------@WWWWWWWWW@------------------------------------------------------------------------------------------------------------------------#
+D:# ----------------------------------------------@WWWWWWWWWWW@-----------------------------------------------------------------------------------------------------------------------#
+D:# --------------------------------------------@WWWWWWWWW@------------------------------------------------------------------------------------------------------------------------#
+D:# ------------------------------------------@WWWWWWW@-------------------------------------------------------------------------------------------------------------------------#
+D:# ----------------------------------------@WWWWW@--------------------------------------------------------------------------------------------------------------------------#
+D:# --------------------------------------@@@@@---------------------------------------------------------------------------------------------------------------------------#
+D:# --------------------------------------------------------------------------------------------------------------------------------------------------------------------#
+D:# ------------------------------------------------------------------------------------------------------------------------------------------------------------------#
+D:# ------------------------------------------------------------------------------------------------------------------------------------------------------------------#
+D:# -------------------------------------------------------------------------------------------------------------------------------------------------------------------#
+D:# -----------------------------------------------------------------------------------------------------------------------------------------------------------------------#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_edoras.txt b/lib/mods/theme/edit/t_edoras.txt
new file mode 100644
index 00000000..a4257661
--- /dev/null
+++ b/lib/mods/theme/edit/t_edoras.txt
@@ -0,0 +1,117 @@
+# File: t_edoras.txt
+
+# Edoras map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain feature ###
+
+# Stable
+F:m:240:3
+
+### Buildings ###
+
+#Meduseld
+F:k:74:3:0:0:0:0:0:82
+
+#Inn
+F:i:74:3:0:0:0:0:0:72
+
+#Map store
+F:a:74:3:0:0:0:0:0:66
+
+#Music store
+F:c:74:3:0:0:0:0:0:64
+
+#The Library
+F:g:74:3:0:0:0:0:0:13
+
+#Rune shop
+F:r:74:3:0:0:0:0:0:62
+
+#The Beastmaster
+F:b:74:3:0:0:0:0:0:16
+
+#Fighters Hall
+F:d:74:3:0:0:0:0:0:17
+
+#Tower of Magery
+F:h:74:3:0:0:0:0:0:18
+
+#Inner Temple
+F:j:74:3:0:0:0:0:0:19
+
+#Paladins Guild
+F:l:74:3:0:0:0:0:0:20
+
+#Rangers Guild
+F:n:74:3:0:0:0:0:0:21
+
+D:######################################################################################################################################################################################################
+D:#-----------------------------------------------------------------------------------------#####OOOOOOO#####,,,,,,,,----------------------------------------------------------------------------------#
+D:#-------------------------------------------------------------------------------###########OOOOOOOOOOOOOOO#########,,,,,,,,,,,,,,,,,-----------------------------------------------------------------#
+D:#----------------------------------------------------------------------------######OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#######------------,,---------------------------------------------------------------#
+D:#-------------------------------------------------------------------------#####OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO######----------,,-------------------------------------------------------------#
+D:#---------------------------------------------------------------------###########OOOOOOO#######################################---------,,-----------------------------------------------------------#
+D:#------------------------------------------------------------------#####-------OOO----OO--------------------------------------###---------,,---------------------------------------------------------#
+D:#----------------------------------------------------------------###----------OOO------OO--------------------OOOOOOOOOOOOOOOOOO-###---------,,-------------------------------------------------------#
+D:#-------------------------------------------------------------####----------OOO---------OO-------------------O################O---###---------,,-----------------------------------------------------#
+D:#-----------------------------------------------------------###-----------OOO----------OOOOOOOOOOOOOOOO------O################O-----###---------,,---------------------------------------------------#
+D:#---------------------------------------------------------###-----------OOOOO----------O####2####O----OO-----O################O-------###---------,,-------------------------------------------------#
+D:#-------------------------------------------------------###-----------OOO--OO----------O#########O-----OO----O###1########i###O---------###---------,,-----------------------------------------------#
+D:#-----------------------------------------------------###-----------OOO----OO----------O#########O------OOOOOOOOOOOOOOOOOOOOOOO-----------###---------,,---------------------------------------------#
+D:#----------------------------------------------------###-----------OOO-----OO----------O####2####O-------------------------OO--OO--######--###---------,---------------------------------------------#
+D:#---------------------------------------------------###-----------OOO------OO----------OOOOOOOOOOO--------------------------OO--OO-######----###-------,---------------------------------------------#
+D:#--------------------------------------------------###-----------OOO--OOOOOOOOOOOOOOOOO-----------------------------#####6OOOOO--OO4#####------##-------,,-------------------------------------------#
+D:#-------------------------------------------------###-----------OOO---O####3####O-----------------------------------######----OO--OO------------##--------,,-----------------------------------------#
+D:#------------------------------------------------###-----------OOO----O#########O-----------####################----######-----OO--OO----######--##---------,,---------------------------------------#
+D:#-----------------------------------------------###-----------OOO-----O#########O---------####---OOOOOOOOOOOOO####--------------OO--OOOOO######---##----------,,-------------------------------------#
+D:#----------------------------------------------###-----------OOO------O####3####O-------####----OO-----------OOOO###------#####9OOO--OO--5#####----##-----------,,-----------------------------------#
+D:#---------------------------------------------###-----------OOO-------OOOOOOOOOOO-----####------OO-------------OOOO###----######--OO--OO-----------##-------------,,---------------------------------#
+D:#--------------------------------------------###-----------OOO--------O--------------###--------OO---------------OOOO###--######---OO--OOOO0#####---##-------------,---------------------------------#
+D:#-------------------------------------------###-----------OOO----------O-----------###----------OO-----------------OOOO##-----------OO--OO-######----##------------,---------------------------------#
+D:#-------------------------------------------##-----------OOO-OOOOOOOOOOOOO--------##------------OO-------------------OOO##----#####aOOO--O-######----##------------,---------------------------------#
+D:#------------------------------------------##-----------OOO--O###########O------###-------------OO---------------------OO##---######--OOOOOOO---------##-----------,---------------------------------#
+D:#------------------------------------------##----------OOOOOOOr#########rO-----##---------------OO----------------------OO##--######---#c##g#---------##-----------,---------------------------------#
+D:#------------------------------------------##----------OOO---O###########O----###---------------OO#########--------------OO##----------######---------##-----------,---------------------------------#
+D:#------------------------------------------##----------OOO---OOOOOOOOOOOOO---###----------------OO-----######-------------OO##---------######----------##----------,,--------------------------------#
+D:#------------------------------------------##---------OOOO-------------------##-----------------OO---------####------------OO##------------------------##-----------,--------------------------------#
+D:#------------------------------------------###########MMMM#####################-----------------OO-----------###-----------OO############################-----------,--------------------------------#
+D:#------------------------------------------###########IIII#####################--------------###kk###---------###----------OO############################-----------,--------------------------------#
+D:#------------------------------------------###########IIII#####################--------------########----------##-----------OO###########################-----------,,-------------------------------#
+D:#------------------------------------------###########IIII#####################--------------########----------##-----------OO###########################------------,-------------------------------#
+D:#------------------------------------------###########IIII#####################--------------########----------##------------OO##########################------------,-------------------------------#
+D:#------------------------------------------###########IIII#####################--------------########----------##------------OO##########################------------,,------------------------------#
+D:#------------------------------------------###########IIII######################-------------########----------##-#######----OO##########################-------------,,-----------------------------#
+D:#------------------------------------------###########MMMM######################------------------------------##--######dOOOOOO##########################--------------,-----------------------------#
+D:#-------------------------------------------##--------OOOOOOOOO----------------###---------------------------###--######dOOOOOO##----------------------##--------------,-----------------------------#
+D:#-------------------------------------------##-------------##OO------#######----###-------------------------###---#######---OOO##----------------------##--------------,-----------------------------#
+D:#-------------------------------------------##-------------##OOOOOOOO#######-----###-----------------------###---------------OO##----------------------##--------------,-----------------------------#
+D:#-------------------------------------------##--------------##OOOOOOO#######------###--------------------####----------------OO##----------------------##--------------,,----------------------------#
+D:#--------------------------------------------##-------------##O------#######--------###----------------####---#######-------OOO##---------------------##-----------------,,--------------------------#
+D:#--------------------------------------------##-------------##OO----------------------###################-----#######------OOO##----------------------##------------------,,-------------------------#
+D:#---------------------------------------------##-------------##OO-------------------------###########---------###n###-----OOO##----------------------##--------------------,-------------------------#
+D:#---------------------------------------------##--------------##OO------------#######-----------------#######----OOOO----OOO##----------------------##---------------------,-------------------------#
+D:#----------------------------------------------##--------------##OOO----------#######-----------------#######-------OO-OOOO##----------------------##----------------------,-------------------------#
+D:#-----------------------------------------------##--------------##OOOO--------###h###-----------------###l###--------OOOOO##----------------------##-----------------------,-------------------------#
+D:#-----------------------------------------------###--------------##OOOO----------O---------#######-------OOOOO-------OOOO##----------------------##------------------------,,------------------------#
+D:#------------------------------------------------###--------------###OOOO--------O---------#######-----------OO-----OOOO##----------------------##--------------------------,------------------------#
+D:#-------------------------------------------------###---------------###OOOO------O---------###j###------------OO--OOOO###----------------------##---------------------------,,-----------------------#
+D:#--------------------------------------------------###----------------####OOOO---O------------O----------------OOOOO###-----------------------##-----------------------------,-----------------------#
+D:#---------------------------------------------------###------------------###OOOOOOO-----------O------------OOOOOO####------------------------##------------------------------,-----------------------#
+D:#----------------------------------------------------###-------------------######OOOOOOOOOOOOOOOOOOOOOOOOOOOO#####--------------------------##-------------------------------,--TTT------------------#
+D:#------------------------------------------------------##---mmmmmmmmmmmmmm------######OOOOOOOOOOOOOOOOOOO######------mmmmmmmmmmmmmmmmm----###--------------------------------,----TTT----------------#
+D:#-------------------------------------------------------###---mmmmmmmmmmmmmmmmm----###########OOOOO#########------mmmmmmmmmmmmmmmmmmm----###---------------------------------,-#####-TT--------------#
+D:#---------------------------------------------------------###---mmmmmmmmmmmmmmmmmm------------OOOOO-----------mmmmmmmmmmmmmmmmmmmmmm---###-----------------------------------,,7####-TT--------------#
+D:#-----------------------------------------------------------###---mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmbmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm---###---------------------------------TTTT--#####-TT--------------#
+D:#-------------------------------------------------------------###----mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm---###------------------------------------TTTT----TTT----------------#
+D:#---------------------------------------------------------------####---mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm-----###----------------------------------------TTT--TTT-----------------#
+D:#------------------------------------------------------------------###----mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm-------###-------------------------------------------------------------------#
+D:#--------------------------------------------------------------------####-----mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm------#####---------------------------------------------------------------------#
+D:#-----------------------------------------------------------------------#####-------mmmmmmmmmmmmmmmmmmmmmmmmmm----------#####------------------------------------------------------------------------#
+D:#--------------------------------------------------------------------------######--------------------------------#########---------------------------------------------------------------------------#
+D:#-----------------------------------------------------------------------------###########----------------############--------------------------------------------------------------------------------#
+D:#-----------------------------------------------------------------------------------########################-----------------------------------------------------------------------------------------#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_esga.txt b/lib/mods/theme/edit/t_esga.txt
new file mode 100644
index 00000000..6665e914
--- /dev/null
+++ b/lib/mods/theme/edit/t_esga.txt
@@ -0,0 +1,105 @@
+# File: t_esga.txt
+
+# Esgaroth map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain features ###
+
+#Wooden board - horizontal
+F:a:233
+
+# Wooden board - vertical
+F:b:234
+
+#Light wooden board - horizontal
+F:c:235
+
+#Wooden plank
+F:e:241
+
+### Buildings ###
+
+#The Dancing Dragon
+F:i:74:3:0:0:0:0:0:73
+
+#The Master's House
+F:k:74:3:0:0:0:0:0:83
+
+#The Library
+F:l:74:3:0:0:0:0:0:13
+
+#The Music Store
+F:m:74:3:0:0:0:0:0:64
+
+#The Hunter store
+F:n:74:3:0:0:0:0:0:61
+
+D:######################################################################################################################################################################################################
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWccc,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWccc,,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWccc,,StSSS,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWccc,,,,,sssss,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWccc,,UUUUU,##9##,,,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWeeeO,,,,XXXXX,,,O,,,,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWbWWWWccc,,,,O,,,##6##,,,O,,UUUUU,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWbWWWWccc,,,,,,,,OOOOOO,,,,,O,,XXXXX,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWbWWWWccc,,,,,,,,SSStS,,,O,,,,,O,,##4##,,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWbWWWWccc,,,,,,,,,,,sssss,,,O,,,,,OOOOOOOO,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWbWWWWccc,,,,SSStS,,,,,##5##,,,O,,,,,,,,,,,,O,,,,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWbWWWWccc,,,,,,,sssss,,,,,,,O,,,,,O,,,,,,,,,,,,O,,,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWbWWWWccc,,,UUUUU,,##2##,,,,,,,O,,,,O,,,,,,,,,,,,,O,,SStSS,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWc,,,,,XXXXX,,,,O,,,,,,,,,O,,,O,,SSSStSSSSSS,O,,sssss,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWbWWWWc,,,,##3##,,,,O,,,,,,,,,O,,O,,,sssssssssss,O,,##0##,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWc,,,,,OOOOOOOOOOOOOOOOOO,O,,,,#####k#####,O,,,,O,,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWbWWWWc,,,,,,,,,,,O,,,,,,,,,,OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOeWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWc,,,UUUUU,,O,,,,,,,,,,,,,,,,,,,,,,,,,,,,,O,,,,,,,,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWbWWWWc,,XXXXX,,O,,SSStS,,,,,,,eeeeeeeee,,,,,,,O,,,UUUUU,,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWc,#####,,O,,sssss,,,,,eeWWWWWWWWee,,,,,,,O,,XXXXX,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWbWWWWc,,OOOOOO,,##m##,,,,eWWWWWWWWWWWWe,,,,,,,O,##n##,,,,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWc,,,,,,,O,,,O,,,,,eWWWWWWWWWWWWWWe,,,,,,,O,,O,,,,,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWbWWWWc,,,,,,,OOOO,,,,eWWWWWWWWWWWWWWWWe,,,,,,,O,O,,SSSSS,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWc,,,,,,,O,,,,,,eWWWWWWWWWWWWWWWWWe,,,,,,,O,,,sssss,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWbWWWWc,,,,,,,O,,,,,eWWWWWWWWWWWWWWWWWWe,,,,,,,O,,#####,,,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWc,,,,,,,O,,,,,eWWWWWWWWWWWWWWWWWe,,,,,,,,O,,,O,,,,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWbWWWWc,,,,,,,O,,,,,eWWWWWWWWWWWWWWWeO,,,,,,,,,O,,O,,UUUUU,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWaaaaaaaaO,,,,,eWWWWWWWWWWWWWe,,O,,,,,,,,,O,O,,XXXXX,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWbWWWWeOOOOOOOO,,,,,eWWWWWWWWWWe,,,,,O,,,,,,,,,O,,,##1##,,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWWWWWaaaaaaaaO,,,,,eWWWWWWWee,,,,,,,O,,,,,,,,,O,,,,O,,,,,,,cWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#WWWWWWWWWWWWWWWWWWWWWWWbWWWWc,,,,,,,O,,,,,eeccMMMcc,,,,,,SSSStSSSS,,,O,,,O,,,,,,,,cWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#-WWWWWWWWWWWWWWWWWWWWWWWWWWWWc,,,,,,,O,,,,,,,aaIIIaa,,,,,sssssssss,,,,O,O,,,,,,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#--WWWWWWWWWWWWWWWWWWWWWWWbWWWWc,,,,,,,O,,,,,,,aaIIIaa,,,,####i####,,OOOO,,,,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#--WWWWWWWWWWWWWWWWWWWWWWWWWWWWWc,,,,,,,O,,,,,,,aaIIIaa,,,,,,,,OOOOOOO,,,,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#---WWWWWWWWWWWWWWWWWWWWWWWWbWWWWc,,,,,,,O,,,,,,,aaIIIaa,,,,,,,,O,,,,,,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#---WWWWWWWWWWWWWWWWWWWWWWWWWWWWWa,,,,,,,,OOO,,,,,aaIIIaa,,,,,,,,O,,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#----WWWWWWWWWWWWWWWWWWWWWWWWWWWaa,OOOOOOO,,,O,,,,,aaIIIaa,,,,,,,eeeWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#-----WWWWWWWWWWWWWWWWWWWWWWWWaaOOOO,,,,OStSSSO,,,,,aaIIIaa,,,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#-----WWWWWWWWWWWWWWWWWWWWWWaaOOOOaaccc,OsssssO,,,,,,aaIIIccccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#------WWWWWWWWWWWWWWWWWWWaaOOOOaaWWWcc,O#####O,,,,,,,ccMMMWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#-------WWWWWWWWWWWWWWWaaOOOOaaWWWbWWWccO##l##O,,,,,,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#-------WWWWWWWWWWWWaaOOOOaaWWWWWWWWWWWccOOOOOO,,,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#-------WWWWWWWWWWaaOOOOaaWWWWWWWWWWbWWWcc,,,,,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#--------WWWWWWaaOOOOaaWWWWWWWWWWWWWWWWWWcc,cccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#--------WWWaaOOOOaaWWWWWWWWWWWWWWWWWWbWWWccWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#--------aaOOOOOaaWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#----------OOaaWWWWWWWWWWWWWWWWWWWWWWWWWWWWbWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#----------aaWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#---SSSSS--WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#---sssss---WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#---##7##----WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#-------------WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#--------------WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#---------------WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#----------------WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#------------------WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#-------------------WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW#
+D:#----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#
+D:#----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_gondol.txt b/lib/mods/theme/edit/t_gondol.txt
new file mode 100644
index 00000000..66807868
--- /dev/null
+++ b/lib/mods/theme/edit/t_gondol.txt
@@ -0,0 +1,216 @@
+# 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
+
+# Force elven monsters
+f:ELVEN
+
+# Town Layout
+
+D:######################################################################################################################################################################################################
+D:#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#
+D:###################################################################################################################OOOOO##############################################################################
+D:###################################################################################################################OOOOO##############################################################################
+D:######################################^^^^^^^^^^^^^,,, OOOOO ####
+D:####################################^^^^^^^^^^^^,,,, OOOOO ####
+D:##################################^^^^^^^^,,,,,,,, OOOOOOOOOOOOOOOOOOOOOOOOOOO ####
+D:###########B#####################^^^^^^^^^,,,,,, OOOOOOOOCCCCCCCCCCCCCCCCCCCCCCCCCOOOOOOOO ,,,,,,, ,,,,###
+D:################################^^^^^^^^^^^,,, OOOOOOOCCCCCCCC#######################CCCCCCCCOOOOOO ,,,,,,,,,,, ,,,,,###
+D:###############################^^^^^^^^^^^^,, OOOOCCCCCCC########TTTTTTTT#####TTTTTTTT########CCCCCCOOO ,,,,,^^^^^,,,, ,,,,###
+D:##############################^^^^^^^^^^^,,, OCCCC########TTTTTT........#####......TTTTTT########CCCCOOO ,,,,^^^^^^^,,,,,, ,,,,,###
+D:#############################^^^^^^^n^^,,, OOCC###########T..........#########..........T###########CCOO ,,,,^^^^^^^^^^^^,,,,,,,,,,,###
+D:############################^^^^^^,,,,,, ......................... OOCC############T........#############........T############CCOO ,,,,^^^^^^^^^^^^^^,,,,,,,,,###
+D:############################,,,,,,,,, ......................... OCC#############T.......###WW#####WW###.......T#############CCO ,,,,^^^^^^^^^^^^^,,,,,,,,,###
+D:###########################,,,,,, ... . ... OC#############TT.......#WWWW##h##WWWW#.......TT#############CO ,,,,,^^^^^^^^^^^,,,,,,,,,###
+D:########################### ... . #### ... #### OOC#############T..........VV###.###VV..........T#############COO ,,,,,,^^^^^^^,,, ,,,,,###
+D:########################## ... ##### ####5#### OCC######WWWW###T...........V##...##V...........T####WWWW#####CCO######### ,,,,,,^^^^,,, ,,,,###
+D:########################## ######### ... ##### ####### OC######TTTTWW#TT............#.....#............TT#WWTTTT######CO######### ,,,,,,,,,, ,,,,###
+D:####W###################W# ######### ... ##### ####### OC#####l...TTW#T.................................T#WTT...T#####CO######### ,,,,,,,, ,,,,,,###
+D:####WWWW#############WWWW# ######### ... ##### ####### OC######T...............................................T######CO ,,,,,,,,,,,,,,,,,,,,,,,,,,,,, ,,,,,,,,###
+D:#####WWWWWWW#####WWWWWWW## ######### ... ##### ######### OOC######TTT.................TTTTTTTTT.................TTT######CO , ,,,,,,,,,, ,,,,,,,,,###
+D:#####WWWWWWWWWWWWWWWWWWW## ####w#### ... ##### ######5###### OCC#######WW..............TTTT#######TTTT..............WW#######CCO , ### ,,,,,,,,,,,,,,,,###
+D:######WWWWWWWWWWWWWWWWW### . ... ##...## OC##########............TTT#############TTT............##########CO , ########## ,,, ,,,,,,,,###
+D:######WWWWWWWWWWWWWWWWW### ........... ... OC########TT...........TT#################TT...........TT########CO , ################### ,,,,,###
+D:######WWWWWWWWWWWWWWWWW### ... ... OC#####TTTT...........TT###..###aaa###..###TT...........TTTT#####CO , ############### ,,,,,,###
+D:########WWWWWWWWWWWWW###### ######### ... ############# ... OC#TTTTT..............T##.....#!...Z#.....##T..............TTTTT#CO , ############## ,,,,###
+D:###########WWWWWWW########## ######### ... ############# ... OC#T...........###...TT##.....#.....#.....##TT...###...........T#CO , ############## ,,,###
+D:############################### ######### ... ############# ... OC#T.........####....T##......#.....#......##T....####.........T#CO , ########### ,,,,###
+D:####--####KK#CC#VV#LL#""## ######### ... ############# ... OCC#TT.......####.....T##...................##T.....####.......TT#CCO , #### , ## ,,,###
+D:####--####KK#CC#VV#LL#""## ######### ... ######9###### ... OC###T......####....TTT###.................###TTT....####......T###CO , , VVVVV ,,,,###
+D:####--####KK#CC#VV#LL#""# ... . ... OC###T......d###...TT###.###....#####....###.###TT...###b......T###CO ,,,,,,,,,,,,,,, VVWWWVV ,,,###
+D:######################### ... . ... OC###T......d###...T##.....##..##WWW##..##.....##T...###b......T###CO , VVVWWWWWV ,,,,###
+D:#..............................................................................................####...T#.......#..#WV#VW#..#.......#T...####...........O , VVVVWWWWWWWV ,,,,,###
+D:#.x.........y.....................................................................................................#W#V#W#..............................,,,, VWWWWWWWWWWV ,,,,###
+D:#..............................................................................................####...T#.......#..#WV#VW#..#.......#T...####...........O , VVWWWWWWWVVV ,,,,,###
+D:######################### . ... . . OC###T......c###...T##.....##..##WWW##..##.....##T...####......T###CO , VWWWWWVVVV ,,,,,^^###
+D:####--####KK#CC#VV#LL#""# . ... . . OC###T......####...TT###.###....#####....###.###TT...####....TTT###CO , VVWWWVV ,,,,,,^^###
+D:####--####KK#CC#VV#LL#""## ####1#### ... ######2###### ##3## OC###T..............TTT###.................###TTT....####...TT#####CO , VVWWV ,,,,,,^^^^###
+D:####--####KK#CC#VV#LL#""### ######### ... ############# ##### OCC#TT................T##...................##T.....####...TT#####CCO , VVVV ,,,,,,^^^^^^###
+D:############################### ########1...... ############# ##### OOC#T.................T##......#.....#......##T....####....T######COO , ------TT------------ ,,,,,^^^^^^^###
+D:###########$$$$$$$########## ######### ... ############# ##### OC#T...VV...VV.......TT##.....##...##.....##TT...###.......f#####CO ,------TT-TTT--TT-TT-TT---- ,,,,^^^^^^^^^###
+D:########$$$$$$$$$$$$$###### ######### ... ############# ##### OC#T..VV#...#VV.......T####.#####m#####.####T..............f#####CO -,--TTTTTTTTTTTTTTT-TT-TTT---,,,,^^^^^^^^^###
+D:######$$$$$$$$$$$$$$$$$### ... OC#T..V###.###V.......TTT#################TTT.............T######CO---,--TT-TTT########TTT-TTTT----,,,,,^^^^^^^###
+D:######$$$$$$$$$$$$$$$$$### ... OC#TT.V##4.j##V.........TTT#############TTT...............TT#####CO---,---TTT###TTTTTT###TT-T-TT----,,,,,^^^^^^###
+D:######$$$$$$$$$$$$$$$$$$## ... OC##T.V#######V...........TTTT#######TTTT..................TT####CO---,--TTT##TTT----TTT##TTTTTT------,,,,,^^^^###
+D:#####$$$$$$$$$$$$$$$$$$$## ######### ... ############# OCC#T.VV#####VV.............................................TTT#CCO---,---TT#TT--------TT##T-T-TT------,,,,^^^^###
+D:#####$$$$$$$#####$$$$$$$## ######### ... ############# OOC#T..VV###VV.....TTTTTTTTTTT.......TTTTTTTTTTT..............T#COO---,,,,,,,,,,,,,,,,,,,e#TT-TTT------,,,,^^^^###
+D:####$$$$#############$$$$# #########.........7############ OC#T...VVVVV....TTT#########TTT...TTT#########TTT............T#CO---------T#TT--------TT##TTT-------,,,,^^^^^^###
+D:####$###################$# ######### ... ############# OC#TT..........TT###VVVVVV####TT.TT####WWWWWW###TT..........TT#CO-------TTT##TTT----TTT##TTTTT-----,,,,^^^^^^^###
+D:########################## ######### ... ############# OC##T..........T##VVV....VVV###TTT###WWW....WWW##T..........T##CO------TTTTT###TTTTTT###TTTTT-----,,,,,^^^^^^^###
+D:########################### ... OCC#T..........T#VV........VV#######WW........WW#T..........T#CCO-----TTTT-TTT########TTTTTT------,,,,,,,^^^^^###
+D:########################### #...# OOC#TT.......................i#####k.......................TT#COO -T---TTTTT-TTTTTTTTTT-TT------ ,,,,,,,z^^^###
+D:############################ ##...## OC##TT........T#VV........VV#######WW........WW#T........TT##CO ------TTTT-TT--TT---T----- ,,,,,,,^^^^^^###
+D:############################ ####6#### OCC##TT.......T##VVV....VVV#########WWW....WWW##T.......TT##CCO ------TTT-TTTT--T----- ,,,,,,,^^^^^^^^^###
+D:############################# ####### OOCC##TTTT...TTT###VVVVVV#############WWWWWW###TTT...TTTT##CCOO ------T-------- ,,,,,,^^^^^^^^^^^^###
+D:############################## ##### OOCC####TTTTTTTTT###########################TTTTTTTTT####CCOO ,,,,,,^^^^^^^^^^^^^^###
+D:############################### ##### OOCCCC######TTTTTTTTTTT##############TTTTTTTTTT######CCCCOO ,,,,,,^^^^^^^^^^^^^^^^^###
+D:################################ ##### OOOOCCCCCC#####################################CCCCCCOOOO ,,,,,,,,,,^^^^^^^^^^^^^^^^^###
+D:################################# ##### OOOOOOCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCOOOOOO ,,,,,,,,,,^^^^^^^^^^^^^^^^^^####
+D:################################## ####### OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO ,,,,,,,,,,,^^^^^^^^^^^^^^^^^^^^^####
+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/mods/theme/edit/t_helm.txt b/lib/mods/theme/edit/t_helm.txt
new file mode 100644
index 00000000..0ecaf913
--- /dev/null
+++ b/lib/mods/theme/edit/t_helm.txt
@@ -0,0 +1,73 @@
+# File: t_helm.txt
+
+# Helm's Deep map by furiosity <furiosity@zionmainframe.net>
+
+#The Hornburg
+F:k:74:3:0:0:0:0:0:80
+
+D:######################################################################################################################################################################################################
+D:#TT,,,,TTTTTTTTTTTTTTTTTTTTTTTT^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTT,,,,TTTTTTTTTTTTTTTTTTTTTTT----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTT,,,,TTTTTTTTTTTTTTTTTTTTTT--------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#@TTTT,,,,TTTTTTTTTTTTTTTTTTTT-------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#W@TTT,,,,TTTTTTTTTTTTTTTTTTT-----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#WW@TTTT,,,,TTTTTTTTTTTTTTTT---------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#WWW@TTT,,,,TTTTTTTTTTTTTT--------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#WWWW@TTTT,,,,TTTTTTTTTTTT-------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#WWWWW@TTT,,,,TTTTTTTTTT------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#@WWWWW@TTTT,,,,TTTTTTTT--------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#T@WWWWW@TTT,,,,TTTTTTTT-------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TT@WWWWW@TTTT,,,,TTTTTT------------------------------------------^^^^^^^---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTT@WWWWW@TTT,,,,TTTT------------------------------------------^^^^^^^^-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTT@WWWWW@@TTT,,,,T------------------------------------------^^^^^^^^-------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTT@@WWWWW@---,,,,-----------------------------------------^^^^^^^-----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTT@WWWWW@@----,,,,,------------------------------------^^^^^^^----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTT@@WWWWW@@-----,,,,,--------------------------------^^^^^^^^-----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTTT@@WWWWW@@@-----,,,,,,---------------------------^^^^^^^-------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTTTTT@@@WWWWW@@@@-----,,,,,-----------------------^^^^^^---------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTTTTTTTT@@WWWWWW@@@@@----,,,,,-------------------^^^^^^^----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTTTTTTT---@@@WWWWWWWW@@@@---,,,,,---------------^^^^^^^------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTTTTTT--------@WWWWWWWWWW@@@---,,,,,-----------^^^^^^^------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTTTTTT---------@@@@@WWWWWWWW@@@---,,,,--------^^^^^^^------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTTTTT---------------@@@@@WWWWWW@-----,,,,----^^^^^^--------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTTTTT--------------------@@@WWWWW@------,,,-^^^^^^------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTTTT-------------------------@WWWWW@@@----,,,,^^^----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTT-----------------------------@WWWWWW@@@@--,,,-------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTTTTT-------------------------------@WWWWWWWW@@@@,,,----------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTT-----------------------------------@@@@@WWWWWWW@-,,,---------------------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTT----------------------------------------@@@WWWWW@--,,,---------------------------------------####^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTTT------------------------------------------^^@WWWWW@-,,,-------------------------------------##OO##^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTTT-----------------------------------------^^^^--@WWWW@-,,,------------------------------------##OO##^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTT-----------------------------------------^^^^^^--@WWW@--,,------------------------------------##OO##-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTTT----------------------------------------^^^^^^---@WWW@--,,-----------------------------------##OO##--------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TTT-----------------------------------------^^^^^^---@WWWW@-,,,---------------------------------##OO##--------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TT------------------------------------------^^^^^----@WWWW@-,,---------------------------------##OO##----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TT----------------------------------------^^^^^^------@WWW@-,,--------------------------------##OO##-----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#TT----------------------------------------^^^^^^-----@WWWW@-,,-------------------------------##OO##------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^---------------------------------^^^^^^^-----@WWWW@-,,------------------------------##OO##--------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^--------------------------------^^^^^^-----@WWWWW@-,,-----------------------------##OO##----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^------------------------------^^^^^^------@WWWW@--,,---------------------@@@@---##OO##------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^-----------------------------^^^^^^------@WWW@---,,--------------------@WWWW@^##OO##@@-----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^---------------------------^^^^^^^-----@WWWW@---,,------------------@WWWWWWW##OO##WWW@----------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^-------------------------^^^^^^^------@WWW@-----,,---------------@WWWW@@@@##OO##WWWW@@@@@------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^--------------------------^^^^^^------@WWWW@------,,-------------@WWWW@^^^##OO##^@WWWWWWWW@@@---------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^---------^---------------^^^^^^-------@WWWW@-------,,,-----------@WWW@^^^##OO##^^^@@@@WWWWWWW@@------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^----------^^^^^^^------------^^^^^^^------@WWW@---------,,,,--------@WWW@^^^##OO####^^^^^^@@@WWWWWW@@---------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^-----------^^^^^^^^^^-------^^^^^--------@WWW@-----------,,,,-----@WWW@^^^##OOOOOO####^^^^^^^@WWWWWW@-------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^-----------^^^^^^^^^^^^^^^^---^^^^^^^------@WWWW@-------------,,,,,###@WWW@^##OOOOOOOOOO####^^^^^@@@WWWW@-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#-------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------@WWW@---------------,,,OO########OOOOOOOOOOOOOO####^^^^^^@WWW@---^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------@WWWW@--------------###OOOOOOOOOOOOOOOOOOOOOOOOOOO####^^^@WWWW@-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-------@WWWW@---------------##########OOOOOOOOOOOOOOOOOOOOO####^@WW@--^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----@WWWWW@------------@WWWW@^^##OOOOOOOO####OOOOOOOOOOOO##^@WW@-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----@WWWWWWW@@@@@@@@@@WWWWW^^##OOOOOOOO######OOOOOOOOOO##^^@WW@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--@WWWWWWWWWWWWWWWWWW@^^##OOOOOOOOOk####kOOOOOOOOO##^^@WWW@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--@@@@@WWWWWWWWWW@^^^##OOOOOOOOOO######OOOOOOOO##^^^^@WWWW@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-@@@@@@@@@@^^^##OOOOOOOOOOOO####OOOOOOOO##^^^^^^@WWWWW@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^------^^^^####OOOOOOOOOOOOOOOOOOOOO##^^^^^^^@WWWWW@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####OOOOOOOOOOOOOOOOO##^^^^^^^^^@@@WWWW@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####OOOOOOOOOOOOO##^^^^^^^^^^^^@WWW@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####OOOOOOOOO##^^^^^^^^^^^^^^@@WWW@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####OOOOO##^^^^^^^^^^^^^^^^^@WWW@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####OO##^^^^^^^^^^^^^^^^^^^@WWW@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^####^^^^^^^^^^^^^^^^^^^@WWW@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_henn.txt b/lib/mods/theme/edit/t_henn.txt
new file mode 100644
index 00000000..49e91867
--- /dev/null
+++ b/lib/mods/theme/edit/t_henn.txt
@@ -0,0 +1,96 @@
+# File: t_henn.txt
+
+# Henneth Annun map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain features ###
+
+# Low hill
+F:h:213:3
+
+# Swift waterfall
+F:f:238:3
+
+# Slippery ledge
+F:l:239:3
+
+### Buildings ###
+
+# Ranger Conclave
+F:k:74:3:0:0:0:0:0:78
+
+# Fighters Hall
+F:a:74:3:0:0:0:0:0:17
+
+# Rangers Guild
+F:b:74:3:0:0:0:0:0:21
+
+D:######################################################################################################################################################################################################
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhWWWWWWWWWWhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhWWTTWWWWWhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhh^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhWWWWWTWWWh^hhhhhhhhhhhh^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^hhhhhhhhhhh#
+D:#hhhhhhhhhhhh^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhh^hWWWWWTWWWhhhhhhhhhhhh^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^hhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^WhWWWTWWhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^W^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^hh^^^@@@@@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^hh^@@,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^h^^@,,,,,,@@^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^h^^^@,,,,,,,,,@@^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^@@,,,,,,,,,,^@^hh^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,^^^^^^hhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^#
+D:#hhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^^,^h#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^h^^@,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@^@,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@^@,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^@,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^hhhhhhhhhhh^,^hhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^@@,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^h^h^^@@@a,,,,,,,,,,,,,,,,,,,,,,,,@^^^^hhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^hhhhh^,^hhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^h^^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,b@@^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@^^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhh^hhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhh^^,^hhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhh^hhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^@@@3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,k@^^hhhhhhhhhh^^hhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@^^hhhhh^hhhhhhhhhh^^h^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^@@^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^^hhhhhh^hhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^@^^@2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^hhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^@@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@^^hhhhhhhhhhhhhhhhhhhh^,^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^^hhhhhhhhhhhh^h^hhhhh^,,^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@@@^^^hh^hhhhhhhhhh^h^hhh^^,,^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,^^^^^^^^^^^^^h^hhhhhhhh^^,,^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^^^^,^,^,^,^,^,^,^,^,^,^,^,^,^^^^@,,,,,,,,,,,,,,,,^^^^^^^^^^^^^^^^^^,,,,,,,,,,,^^^^^^^^^^,,^^,hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,,,,^,^,^,^,^,^,^,^,^,^,^,^,^,,,^^^,,,,,,,,,,,,,,,^^^^,,,,,,,,,,,,,,,,,,^^^^^^^,,,,,,,,,,,,,,^^hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,^,,,,,,,,,,,,,,,,,,,TTTTTTTTTTTTTTTTTTTTTTTTTT^^^^^^^^^^^^^7hTThThhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,hh^^CC^^^^^^^^^^^^^^^^^^^^^^^^^^,,,lllllllllllllll^^^^^^^^^^^^^^^^^^^^^^^^^TWWWWWWWTT^T^ThThhhhhTTThhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,hhh^CCCCCCCCCCCCCCCCCCCCCCCCCffffffffllllllllllllllffffff^^^^^^^^CCCCCCCCC^^^^WWWWWTThhhhhhhhhhhhTThhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,hhhh^CCCCCCCCCCCCCCCCCCCCCCCCCffffffffffllllllllllffffffffffCCCC^^CCCCCCCCCCCCC^^^^^TTTTTTTTTTTTTTThhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,hhhhh^CCCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffffffffCCCCCCCCCCCCCCCCCCCC^C^^^^^^^^^^TTTTTThhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,hhhhhh^CCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffffffffffCCC^CCCCCCCCCCCCCCC^^CCCCC^C^^^T^^^^^TThhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,hhhhhhh^CCCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffffffffCCCC^CCCCCCCCCCCCCCCCC^^^^CCCCC^^^CCC^^^Thhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,hhhhhhhh^CCCCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffffffCCCCC^CCCCCCCCCCCCCCC^CCCCCCCCC^^CCCCCC^Thhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh^^,hhhhhhhhh^CCCCCCCCCCCCCCCCCCCCCCCCCffffffffffffffffffffffffffffCCCCCCC^CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC^Thhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_hobb.txt b/lib/mods/theme/edit/t_hobb.txt
new file mode 100644
index 00000000..6043f188
--- /dev/null
+++ b/lib/mods/theme/edit/t_hobb.txt
@@ -0,0 +1,108 @@
+# File: t_hobb.txt
+
+# Hobbiton map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain features ###
+
+#Wooden board
+F:a:235:3
+
+#Small tree
+F:b:202:3
+
+#Field
+F:c:181:3
+
+#Stable
+F:d:240:3
+
+#Low hill
+F:h:213:3
+
+#Mallorn
+F:m:243:3
+
+### Buildings ###
+
+# Farm
+F:f:74:3:0:0:0:0:0:67
+
+# Green Dragon
+F:g:74:3:0:0:0:0:0:74
+
+# Bag End
+F:i:74:3:0:0:0:0:0:84
+
+# Beastmaster
+F:j:74:3:0:0:0:0:0:16
+
+D:######################################################################################################################################################################################################
+D:#bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbhhhhhhhhhhhhhhhhhhhhhhhhhhihhhhhhhccccchhOOhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb------------------------OO-------ccmccTOO------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-----------------------OO--------ccccTOO-------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------------------OO-------------OO--------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb---------------------OO---------------OO-------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb--------------------OO---------------OO--------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-------------------OO-----------------OOb------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb------------------OO-----------------OOb-------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-----------------OO##############---OO---------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-----------OOOOOOOOOOOOOOOOOOOOO#---OOb--------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------OO---#OOOOOOOOOOOOOOOO#---OOb--------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------OO---#OOOddddddddddOOO#---OO---------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------OO---#OOOddddddddddOOO#---OO---------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------OO---#OOOOddddddddjOOOOOOOO----------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------OO---#OOOddddddddddOOO#--OOb----TT---------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-----------OO--#OOOddddddddddOOO#--OO--TT-TTT--------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-----------OO--#OOOOOOOOOOOOOOOO#--OO-TT-------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb------------OO-OOOOOOOOOOOOOOOOO#--OObTT------T------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-------------OOO#################--OObTT-------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb--------------OO------------------OOOOOOOOOOOO-------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb---------------OO-----------------OOb--#####--OO-----------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-----------------OO---------------OOb--#######ff##---------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb------------------OO-----------T--OOb--###########---------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb---------------TTTTOO--------TTT-OOb---###########---------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------------TT--OO-----------OOb---#####---------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb------------------TT-OO-----TTT--OOb---#####---------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb------------------TT--OO-----T---OOb-----------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-------------------TTT-OO--TT---OOb------------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-------------------TTTT-OOTTT--OOb-------------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb---------------------TTT-OO--bOOb--------------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------------------TTT-OOOOOb---------------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-----------------------TT--OOOb----------------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------------------TTT-OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------------------TT-bOOb------------------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------------------TT-OOb-------------------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------------------TT-OOb-------------------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb---------------------TTT-OOb-------------------------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb----------------------T--OOb-##############----------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb-----------------TTT-TTT-OO--##############----------------------------------------------------------------------------------------#
+D:#bccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbb------------------T-TTTT-OOOO1#############----------------------------------------------------------------------------------------#
+D:#bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbOO--##############@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#
+D:#VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVaOOaV##############VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#
+D:#VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVaOOaVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#
+D:#VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVaOOaVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#
+D:#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@bOO@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#
+D:#-----------------------------------------------------------------------------------------bOO--------------------------------------------------------------------------------------------------------#
+D:#-----------------------------------------------------------------------------------hhhh---OO--------hhh-----hhh-------------------------------------------------------------------------------------#
+D:#--------------------------------------------------TT-----TT------TT-----hhhh------hhhhhh--OO-------hhhhh---hhhhh------------------------------------------------------------------------------------#
+D:#-------------------------------------------------TTTT---TTTT----TTTT---hhhhhh----hhhhhhhh-OO------hhhhhhh--hh5hh--TT---TT---TT----------------------------------------------------------------------#
+D:#--------------------------------------------------TT-----TT------TT---hhh3hhhh---hhhh2hhh-OO------hhh0hhh--------TTTT-TTTT-TTTT---------------------------------------------------------------------#
+D:#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#
+D:#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#
+D:#------------------------------------------------------------------######-hhhh-hhh------hhhOO-hh-----hhhh----h-hh-TTTT-TTTT-TTTT---------------------------------------------------------------------#
+D:#------------------------------------------------------------------######--hhhhhh--------hhOO-hhh-----hh-----hhhh--TT---TT---TT----------------------------------------------------------------------#
+D:#------------------------------------------------------------------######---hhhh----------hOO-hhh-------------hh-------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------######------------------OO-hh-------#####-----------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------######--------hhhhh-----OO----------#####-----------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------######-------hhhhhhh----OO---hh-----g####-----------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------------------hhhh4hhhh---OO--hhhh----#####-----------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------------------------------OO-hhhhhh---#####-----------------------------------------------------------------------------------------#
+D:#-------------------------------------------------------------------------------------hhh--OO-hh6hhh-------------------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------------------------hhhhh-OO--------------------------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------------------------hh7hh-OO--------------------------------------------------------------------------------------------------------#
+D:#------------------------------------------------------------------------------------------OO--------------------------------------------------------------------------------------------------------#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_imlad.txt b/lib/mods/theme/edit/t_imlad.txt
new file mode 100644
index 00000000..af06a709
--- /dev/null
+++ b/lib/mods/theme/edit/t_imlad.txt
@@ -0,0 +1,90 @@
+# File: t_imlad.txt
+
+# Rivendell/Imladris map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain features ###
+
+# Swift waterfall
+F:f:238:3
+
+### Buildings ###
+
+# Imladris
+F:a:74:3:0:0:0:0:0:79
+
+# Forge
+F:b:74:3:0:0:0:0:0:88
+
+# Stable (Beastmaster)
+F:c:74:3:0:0:0:0:0:16
+
+D:######################################################################################################################################################################################################
+D:# @^^^@ @@WWWWWWWWWW^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^WWW^#
+D:# @^^^@ @@WWWWWWWWWW^^^^^^^^^^^^^^^^^^^^^^^^---#####----------------------TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^WWWW^#
+D:# @^^^@ @@WWWWWWWWWW^^^^^^^^^^^^^^^^^^^^------#####----------------------TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT^^^^^^^^^^^^^^^^^^^^^^^^^^^^^WWWW^^#
+D:# @^^^@ @@WWWWWWWWWW^^^^^^^^^^^^^^^^----###--##b##-----------------------TTTTTTTT TTTTTTT^^^^^^^^^^^^^^^^^^^^^^^^^^^^WWWW^^^#
+D:# @^^^@ @@WWWWWWWWWW^^^^^^^^^^^^^^--######------------------------------TTTTTTTTTTTT TTTTTT^^^^^^^^^^^^^^^^^^^^^^^^^^WWWW^^^^#
+D:# @^^^@ @@WWWWWWWWWW^^^^^^^^-----####c##----------------------------------TTTTTTTTTTT TTTTTTT^^^^^^^^^^^^^^^^^^^^^^^^^WWWW^^^^^#
+D:# @^^^@ @@WWWWWWWWWW^^^^^^^^^---####----------------------------------TTTTTTTT TTTTTTTTTTTT^^^^^^^^^^^^^^^^^^^^^WWW^^^^^^^#
+D:# @^^^@ @@WWWWWWWWWW^^^^^^WW^-----------------------------------------TTTTTTTTTTTTTTT TTTTTTTTTTTT^^^^^^^^^^^^^^^^^^WWW^^^^^^^^^#
+D:# @^^^@ @@WWWWWWWWWW^^^^^^^W^------------------------------------TTTTTTTTTTTTTTTTTTTTTTTT TTTT^^^^^^^^^^^^^^^^^^^^^^WWWW^^^^^^^^^^#
+D:# @^^^@ @@WWWWWWWWW^^^^^^W@^^------------------------------------TTTTTTTTTTTTTTTTTTTTTTTTTT TTTTT^^^^^^^^^^^^^^^^^^^^WWWW^^^^^^^^^^^^#
+D:# @^^^@ @@WWWWWWWWW^^^^^^W@^^-------------------------------------TTTTTTTTTTTTTTTTTTTTTTTTTT TTTTTTT^^^^^^^^^^^^^^^^WWWW^^^^^^^^^^^^^^#
+D:# @^^^@ @@WWWWWWWWWWWWfWW@@^^-----------------------------------------------------TTTTTTTTTTTTTTTTTTTTT^^^^^^^^^^,^^@WWWW^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@WWWWWWWWWWffW@@@@^^-----------------------------------------------------TTTTTTTTTTTTTTTTTTTTT^^^^^,,,,^^@WWWW^^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@WWWWWWWWffWW@@@@@^^------------------------------------------------------TTTTTTTTTTTTTTTTTTTTT^^,,^^^@@WWWW^^^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@WWWWWWffWWWWW@@@@^^^------------------------------------------------------TTTTTTTTTTTTTTTTTTT,,^^^^@WWWWW^^^^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@WWWWffWWWWWWW@@@@@^^^^-----------------------------#################-------TTTTTTTTTTTTTT,,,^^^@@WWWW@^^^^^^^^^^^^^^^^^^^^^#
+D:# @^^^@ @@WWffWWWWWWWWWW@@@@@@^^----------------------------#################---------------TTT--,,^^^@@WWWWW@^^^^^^^^^^^^^^^^^^^^^^#
+D:# TTTTTT @^^^@ @^^ffWWWWWWWWWWWWW@@@@@^^------------------------#######################-------------,,,,^^^@@WWWWW^^^^T^^^^^^^^^^^^^^^^^^^^#
+D:# TTTTTTT TTT @^^^@ @^^^^^^^WWWWWWWWWWWWWWWW@@@@^^----------,,,,,,,,,,,,,a#####################a,----------,,^^^^^@@WWWWW^^^TTTTT^^^^^^^^^^^^^^^^^^^#
+D:# TTTT TTT @^^^@^@@^^@^^^^@ @@WWWWWWWWWWWWWWWWWW@@^^^^----,,,,,,,,,,,,,,,,a#####################a,---------,,^^^@@@WWWWW@^^TTT TTT^^^^^^^^^^^^^^^^^^#
+D:# TTT TTT T TTT @^^^^^^^^^^^@ @@@WWWWWWWWWWWWWWWWW@@@^^,,,,,,-------------,#######################,------,,,^^^@@WWWWWWW@^^TTT TTT^^^^^^^^^^^^^^^^^^^#
+D:# TTT T TTT T TT @^^@ @@@@@WWWWWWWWWWWWWW@#,,,,,^^^------------,---#################---,-----,,^^^@@WWWWWWWW@^^TTT TTT^^^^^^^^^^^^^^^^^^^^#
+D:# TTT T TTTTTTT TT @@@@@WWWWWWWWWWW#,,,,,#@@^^^^^^^^------,--#################---,-,,,,^^^@@WWWWWWWWW@^^TT TTT^^^^^^^^^^^^^^^^^^^^^^#
+D:# TT TTTTTT,,TT,,TT,, @@@@WWWWWWW#,,,,,#WWW@@@@@@@^^------,--------------------,,,,^^^^@@WWWWWWWWWW@^^TT TTT^^^^^^^^^^^^^^^^^^^^^^^#
+D:# TT TTT TT ,,,TT,,TT,,T,, ,,,,,,, @@@@WWW#,,,,,#WWWWWWWWWW@@@^^^^^^,,,,,,,,,,,,,,,,,,,,,,^^^^@@@WWWWWWWWWW@@^^TT TTT^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# TTT T ,,TTT TTT TTTTT,, ,,,,,,,,,,,,,,, @@W#,,,,,#WWWWWWWWWWWWWW@@@@@^^^^^^^^^^^^^^^^^^^^^^^^@@@WWWWWWWWWWW@@@^^TT TTT^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# TTTT ,,TT TTTT,,,,,, ,,,, ,,,,, ,,,,,,,,,,@#,,,,,#WWWWWWWWWWWWWWWWWWWW@@@@@@@@@@@@@@@@@@@@@@@@WWWWWWWWWWWW@@@@^^TT TT^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# T TTT T,,TT T TT TT,,,,^^,,,, ,,,, ,,,, ,,,, ,,,, ,,,,,,,,@@#@@@@@WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW@@@@^^^^TT TT^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# TTT T,,TT TT TT,,,,^^^^^^,,,, ,,,, ,,,,, ,,,, ,,,,@@@ @@@@@WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW@@@^^^^TTT TT^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# T,,TTT T,,,,^^@@^^^^@ @^@,,,,, ,,,, @@@@@@WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW@@@@TTTTTTTT TT^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# ,,,,,,TTTT TTTT TTTT,,,,^^@ @^^^@ ,,,,,,,,,, @@@@@@@@WWWWWWWWWWWWWWWWWWWWW@@@@@@ TTTTTTTTT^^@@^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# ,,,,^^^^^^TTT TTT^^,,,,^^@ @^^^^^@ ,,,,, @@@@@@@@@@@@@@@@@@@@@@@ @^@TTTT^^@ @^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# ,,,,,^^^^@ @^^^TTTTTTTT^^^^,,,,^^@ @^^^^^^@ ,,, @^^^@ ^^^^^^^^^^^^^^^^^^^^^^#
+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:# @^^,,,, #
+D:# @^^,,,, #
+D:# @^^,,,, #
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_info.txt b/lib/mods/theme/edit/t_info.txt
new file mode 100644
index 00000000..8a525ede
--- /dev/null
+++ b/lib/mods/theme/edit/t_info.txt
@@ -0,0 +1,125 @@
+# 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 Gondolin
+?:[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 Caras Galadhon
+?:[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
+
+# Beorn's Halls
+?:[AND [EQU $TOWN 6] [EQU $TOWN_DESTROY6 1] ]
+%:t_d_beorn.txt
+?:[AND [EQU $TOWN 6] [NOT [EQU $TOWN_DESTROY6 1] ] ]
+%:t_beorn.txt
+?:1
+
+# Cerin Amroth
+?:[AND [EQU $TOWN 7] [EQU $TOWN_DESTROY7 1] ]
+%:t_d_cerin.txt
+?:[AND [EQU $TOWN 7] [NOT [EQU $TOWN_DESTROY7 1] ] ]
+%:t_cerin.txt
+?:1
+
+# Dale
+?:[AND [EQU $TOWN 8] [EQU $TOWN_DESTROY8 1] ]
+%:t_d_dale.txt
+?:[AND [EQU $TOWN 8] [NOT [EQU $TOWN_DESTROY8 1] ] ]
+%:t_dale.txt
+?:1
+
+# Edoras
+?:[AND [EQU $TOWN 9] [EQU $TOWN_DESTROY9 1] ]
+%:t_d_edoras.txt
+?:[AND [EQU $TOWN 9] [NOT [EQU $TOWN_DESTROY9 1] ] ]
+%:t_edoras.txt
+?:1
+
+# Esgaroth
+?:[AND [EQU $TOWN 10] [EQU $TOWN_DESTROY10 1] ]
+%:t_d_esga.txt
+?:[AND [EQU $TOWN 10] [NOT [EQU $TOWN_DESTROY10 1] ] ]
+%:t_esga.txt
+?:1
+
+# Helm's Deep
+?:[AND [EQU $TOWN 11] [EQU $TOWN_DESTROY11 1] ]
+%:t_d_helm.txt
+?:[AND [EQU $TOWN 11] [NOT [EQU $TOWN_DESTROY11 1] ] ]
+%:t_helm.txt
+?:1
+
+# Henneth Annun
+?:[AND [EQU $TOWN 12] [EQU $TOWN_DESTROY12 1] ]
+%:t_d_henn.txt
+?:[AND [EQU $TOWN 12] [NOT [EQU $TOWN_DESTROY12 1] ] ]
+%:t_henn.txt
+?:1
+
+# Hobbiton
+?:[AND [EQU $TOWN 13] [EQU $TOWN_DESTROY13 1] ]
+%:t_d_hobb.txt
+?:[AND [EQU $TOWN 13] [NOT [EQU $TOWN_DESTROY13 1] ] ]
+%:t_hobb.txt
+?:1
+
+# Imladris
+?:[AND [EQU $TOWN 14] [EQU $TOWN_DESTROY14 1] ]
+%:t_d_imlad.txt
+?:[AND [EQU $TOWN 14] [NOT [EQU $TOWN_DESTROY14 1] ] ]
+%:t_imlad.txt
+?:1
+
+# Osgiliath
+?:[AND [EQU $TOWN 15] [EQU $TOWN_DESTROY15 1] ]
+%:t_d_osgili.txt
+?:[AND [EQU $TOWN 15] [NOT [EQU $TOWN_DESTROY15 1] ] ]
+%:t_osgili.txt
+?:1
+
+# Pelargir
+?:[AND [EQU $TOWN 16] [EQU $TOWN_DESTROY16 1] ]
+%:t_d_pelar.txt
+?:[AND [EQU $TOWN 16] [NOT [EQU $TOWN_DESTROY16 1] ] ]
+%:t_pelar.txt
+?:1
+
+# Thranduil's Halls
+?:[AND [EQU $TOWN 17] [EQU $TOWN_DESTROY17 1] ]
+%:t_d_thrand.txt
+?:[AND [EQU $TOWN 17] [NOT [EQU $TOWN_DESTROY17 1] ] ]
+%:t_thrand.txt
+?:1 \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_khazad.txt b/lib/mods/theme/edit/t_khazad.txt
new file mode 100644
index 00000000..73e93956
--- /dev/null
+++ b/lib/mods/theme/edit/t_khazad.txt
@@ -0,0 +1,114 @@
+# 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
+
+# Seat of Durin
+F:k:74:3:0:0:0:0:0:87
+
+# Inn
+F:n:74:3:0:0:0:0:0:70
+
+# Eagles
+F:p:74:3:0:0:0:0:0:22
+
+# 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:#p#######################oo####ooooo####### ### ### o o ###; ### ######o####oo####oo######oo######; CCCCC #
+D:#^^#######################o########### ##### ##### o o ##### ##### ######o####################oo#### CCCCC #
+D:#^####################################### #######;; ; ####### o k 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### #####n## ###### ####################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/mods/theme/edit/t_lorien.txt b/lib/mods/theme/edit/t_lorien.txt
new file mode 100644
index 00000000..a4a63172
--- /dev/null
+++ b/lib/mods/theme/edit/t_lorien.txt
@@ -0,0 +1,185 @@
+# File: t_lorien.txt
+
+# Caras Galadhon map by furiosity <furiosity@zionmainframe.net>
+# Original Lothlorien map by Akhronath <zzhou22876@aol.com>
+
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+# 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
+
+### Additional terrain features ###
+
+# Flet
+F:o:220:3
+
+# Fosse (dry moat)
+F:p:242:3
+
+#Mallorn
+F:m:243:3
+
+#Small tree
+F:l:202:3
+
+###################### 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:69
+
+# 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
+F:i:74:3:0:0:0:0:0:21
+
+# Nest
+F:j:74:3:0:0:0:0:0:22
+
+# Hunter store
+F:k:74:3:0:0:0:0:0:61
+
+# Museum
+F:q:74:3:0:0:0:0:0:57
+
+# Music shop
+F:r:74:3:0:0:0:0:0:64
+
+# Force elven monsters
+f:ELVEN
+
+# Town Layout
+
+D:######################################################################################################################################################################################################
+D:# OOO #
+D:# OOOOOOOOOOOOOOOOOOOOOOOOO #
+D:# OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO #
+D:# OOOOOOOOOOOppppppppppppppppppppppOOOOOOOOOOOO #
+D:# OOOOOOOOOpppppppppppppppppppppppppppppppppppOOOOOOOO #
+D:# OOOOOOOppppppppppppppppppppppppppppppppppppppppppppOOOOOOOy #
+D:# OOOOOOpppppppppppppppppppppppppppppppppppppppppppppppppppOOOOOO #
+D:# OOOOOppppppppppppppppppppppppppppppppppppppppppppppppppppppppOOOOOO #
+D:# OOOOOpppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppOOOOO #
+D:# OOOOppppppppppppppppppppppppTTTTTTTToxTTTTTTTTTpppppppppppppppppppppppOOOOO #
+D:# OOOOpppppppppppppppppppppTTTTT lo9ol TTTTTppppppppppppppppppppOOOO #
+D:# OOOOOpppppppppppppppppppTTT lol TTTpppppppppppppppppppOOOO #
+D:# OOOOppppppppppppppppppTTT l,,,l TTTppppppppppppppppppOOOO #
+D:# OOOOpppppppppppppppppTTT lllllll,,,lllllllll TTTppppppppppppppppOOOO #
+D:# OOOOppppppppppppppppTTT lll,,,,,,,,,,,,,,,,,,,lll loTTpppppppppppppppppOOO #
+D:# OOOOppppppppppppppppTz lll,,,,,,,,,,,,,,,,,,,,,,,,,ll lodoTTppppppppppppppppOOO #
+D:# OOOppppppppppppppppTTol ll,,,,lllllllll,,,llllolllll,,,,l l,,ol TTpppppppppppppppOOO #
+D:# OOOppppppppppppppppTTogol l,,,lllo l,,,l o0o ll,,,l l,,ll TTpppppppppppppppOOO #
+D:# OOOOppppppppppppppTT lo,,l l,,l oko l,,,l o ll,,l,,l TpppppppppppppppOOO #
+D:# OOOOppppppppppppppTT ll,,l l,,l o l,,,l l,,,l TpppppppppppppppOOO #
+D:# OOOppppppppppppppTT l,,l,,l l,,,l ll,,,l TpppppppppppppppOOO #
+D:# OOOpppppppppppppppT l,,,l ll,,,,,ll l,,l,,l TppppppppppppppOOO #
+D:# OOOpppppppppppppppT l,o,lll l,,l,,,l,,l ll,,l l,,l TppppppppppppppOOO #
+D:# OOOOppppppppppppppT l,o2o,,,ll l,,ll,,,ll,,l l,,,l l,,l TppppppppppppppOOO #
+D:# OOOppppppppppppppTT l,,lolll,,,l l,,l l,,,l l,,l ll,,ll l,,l TppppppppppppppOO #
+D:# OOOppppppppppppppT l,,l ll,,ll l,,l l,,,l l,,l,,,l o,,l TppppppppppppppOO #
+D:# OOOpppppppppppppTT l,,l l,,,ll,,l l,,,l ll,,l o6o,,l TppppppppppppppOOO #
+D:# OOOppppppppppppppT l,,l ll,,,,ll mm,mm ll,,,,l o l,,l TpppppppppppppOOO #
+D:# OOOppppppppppppppT l,,l o l,,,,,lmm-----mm,,,ll,,l l,,l TppppppppppppppOOO #
+D:# OOppppppppppppppTT l,,l oro l,,ll,,,mm-----mm,ll l,,l l,,l TppppppppppppppOOO #
+D:# OOppppppppppppppTTl l,,l o l,,l l,,---@@@---mm l,,l l,,l TppppppppppppppOOO #
+D:# OOppppppppppppppTlollllll,,lllllllllllll,,lllllmm--@@V@@--mmllllll,,lllllllllll,,llllllllolTppppppppppppppOOO #
+D:# OOppppppppppppppToio,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--@VWV@--,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,ohoTppppppppppppppOOO #
+D:# OOppppppppppppppTlollllll,,lllllllllllll,,lllllmm---@V@@@-mml l,,lllllllllll,,llllllllolTppppppppppppppOOO #
+D:# OOppppppppppppppTTl l,,l l,,l mm,-----VV@,,,ll l,,l l,,l TppppppppppppppOOO #
+D:# OOOppppppppppppppT l,,o l,,l l,mmm--o-@VV@m,,,ll,,lo l,,l TppppppppppppppOO #
+D:# OOOppppppppppppppT l,o4o l,,l,,l mmobom@VV@ll,,,lloqo o,,l TppppppppppppppOO #
+D:# OOOppppppppppppppT l,ol l,,,l mmomm@VVV@ l,,,,lo o5o,l TTpppppppppppppOOO #
+D:# OOOpppppppppppppppT l,,l ll,,,ll l,,,l @@VV@@lll,,,ll o,l TppppppppppppppOOO #
+D:# OOOppppppppppppppT l,,l ll,,,ll,,l l,,,l @VVV@ ll,,,ll l,,l TTpppppppppppppOOO #
+D:# OOOpppppppppppppppT l,,l l,,,ll l,,l l,,,l l,@VV@ ll,,,l l,,l TppppppppppppppOOO #
+D:# OOOppppppppppppppTT l,,l lll,,ll l,,ll,,,l l,,l@VV@ ll,,l,,l TpppppppppppppppOO #
+D:# OOOpppppppppppppppT l,,ll,,,,l l,,l,,,ll,,l @VVV@ ll,,ll TTppppppppppppppOOO #
+D:# OOOpppppppppppppppTT l,,,,,ll l,,,,,o,,l @VV@@ l,,l,,ll TTpppppppppppppppOO #
+D:# OOOpppppppppppppppT l,,,ll ll,,o1ol @VVV@ l,,l ll,,l TTpppppppppppppppOOO #
+D:# OOOppppppppppppppppT ll,,l,,l l,,,ol l,@VV@ l,,l ll,o TTpppppppppppppppOOO #
+D:# OOOppppppppppppppppTT lo,,l l,,l o l,,,l l,,l@VV@,,l owoTTpppppppppppppppOOO #
+D:# OOOppppppppppppppppTTlofol l,,llo3olllllllll,,,lllllll,,,,@VVV@l oTppppppppppppppppOOO #
+D:# OOOpppppppppppppppppTTol l,,,,o,,,,,,,,,,,,,,,,,,,,,lllv@@VV@ TTppppppppppppppppOOO #
+D:# OOOOpppppppppppppppppTT lll,,,,,,,,,,,,,,,,,,,,,,l @VVV@@VV@@ TTpppppppppppppppppOOO #
+D:# ##7OOOOpppppppppppppppppTTT llllllllloll,,,lllllll @VWaWV@@VVV@ TTppppppppppppppppppOOO #
+D:# B## OOOppppppppppppppppppTTT ocol,,,l @VVV@ @VV@TppppppppppppppppppOOOO #
+D:# OOOOpppppppppppppppppppTTT o l,,,lllll @@ TT@VV@pppppppppppppppppOOO #
+D:# OOOOpppppppppppppppppppppTTTT l,,,,,,,,l TTTTTp@VVV@ppppppppppppppOOOO #
+D:# OOOOpppppppppppppppppppppppTTTTTTT l,,,,,,,,TTTTTTTpppppp@@VV@@ppppppppppOOOO #
+D:# OOOOppppppppppppppppppppppppppppTTTTTTTTOOOppppppppppppppp@VVV@pppppppOOOOO #
+D:# OOOOpppppppppppppppppppppppppppppppppOOOppppppppppppppppp@VV@pppppOOOOO #
+D:# OOOOOpppppppppppppppppppppppppppppOOOppppppppppppppppppp@VV@ppOOOOO #
+D:# OOOOOOpppppppppppppppppppppppppOOOpppppppppppppppppppp@VVV@OOO #
+D:# OOOOOOpppppppppppppppppppppOOOppppppppppppppppppppOO@@VV@@ #
+D:# OOOOOOOpppppppppppppppppOOOppppppppppppppppOOOOOOOO@VVV@ #
+D:# OOOOOOOOOOpppppppppppOOOpppppppppppOOOOOOOOO @VV@ #
+D:# OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO @VVV@ #
+D:# OOOOOOOOOOOOOOOOOOOOOOO @@VVV@ #
+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/mods/theme/edit/t_minas.txt b/lib/mods/theme/edit/t_minas.txt
new file mode 100644
index 00000000..cea53634
--- /dev/null
+++ b/lib/mods/theme/edit/t_minas.txt
@@ -0,0 +1,141 @@
+# 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:2
+
+?: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
+
+# 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--###-##d##------###----- ^^^^^^^^^^ @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-#,-----,#--#####7##--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/mods/theme/edit/t_osgili.txt b/lib/mods/theme/edit/t_osgili.txt
new file mode 100644
index 00000000..ea74d660
--- /dev/null
+++ b/lib/mods/theme/edit/t_osgili.txt
@@ -0,0 +1,136 @@
+# File: t_osgili.txt
+
+# Osgiliath map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain features ###
+
+#Anduin river
+F:a:227:3
+
+#Small tree
+F:b:202:3
+
+#Wooden board
+F:c:235:3
+
+#light post
+F:d:221:3
+
+# Altars
+F:k:161:3
+F:l:162:3
+F:m:163:3
+F:n:165:3
+F:w:166:3
+F:x:167:3
+F:y:168:3
+F:z:169:3
+
+### Buildings ###
+
+# The Twinkling Star inn
+F:e:74:3:0:0:0:0:0:68
+
+# The Castle of Stars
+F:f:74:3:0:0:0:0:0:85
+
+# Map store
+F:g:74:3:0:0:0:0:0:66
+
+# Museum
+F:h:74:3:0:0:0:0:0:57
+
+# Soothsayer
+F:i:74:3:0:0:0:0:0:12
+
+# Library
+F:j:74:3:0:0:0:0:0:13
+
+# Casino
+F:o:74:3:0:0:0:0:0:15
+
+# Fighters Hall
+F:p:74:3:0:0:0:0:0:17
+
+# Tower of Magery
+F:q:74:3:0:0:0:0:0:18
+
+# Inner Temple
+F:r:74:3:0:0:0:0:0:19
+
+# Paladins Guild
+F:u:74:3:0:0:0:0:0:20
+
+# Rangers Guild
+F:v:74:3:0:0:0:0:0:21
+
+D:######################################################################################################################################################################################################
+D:#---------------------------------------------------------#################@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##########################---------------------------------------------------------#
+D:#OOOOOOOOOOOOOOOOOOOOOOOOOOOO-------------------------######,,,,,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@,,,,,,,,,,,,,,,,,,,,,,,,####-------------------------OOOOOOOOOOOOOOOOOOOOOOOOOOOOOO#
+D:#---------------------------OO--------------------#####,,,,,,,########,,,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,#####,,,#####,,,#####,,######---------------------OOO----------------------------#
+D:#----------------------------OO-----------------####,,,,,,,,,O7#######,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@,,#####OOO#####OOO#####O,,,,,#####-----------------OOO-----------------------------#
+D:#-----------------------------OO--------------####,,,,,,,,,,OO########,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,#####,,,#####,,,#####,O,,,,,,,#####-------------OOO------------------------------#
+D:#------------------------------OO----------####,,,,########O,O,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,O,,,,,,,O,,,,,,,O,,,,OOOOO,,,,,####----------OOO-------------------------------#
+D:#-------------------------------OO-------####,,,,,Og#######O,O,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,OOOO,,,,OOOO,,,,OOOO,,,,,OOOO,,,####------OOO---------------------------------#
+D:#--------------------------------OO---####,,,,,,,O,########,,O,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,#####,,,#####,,,#####,,,,#####,,,####---OOO----------------------------------#
+D:#---------------------------------OO###,,,,,,,,,,,OO,,,,,,,,,O########,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@,,,,#####OOO#####OOO#####OOO,#####,,,,#####OOO-----------------------------------#
+D:#---------------------------------#OOd,,,,,,########O,,,,,,,,O3#######,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@,#####,,,#####,,,#####,,,,#####,,,,,,,#OOO------------------------------------#
+D:#--------------------------------##dOOb,,,,,#######2O,,,,,,,,O########,,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,O,,,,,,,O,,,,,,,O,,,,,,,,O,,,,,,,,dOOd###----------------------------------#
+D:#-------------------------------###,bOOb,,,,########,O,,,,,,,O,,,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,OOO,,,,,OOOO,,,,O,,,,,,,O,,,,,,,dOOd--###---------------------------------#
+D:#------------------------------##,,,,bOOb,,,,,,,,,,,,,O,,,,T,,O,,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,#####,,,#####,,,T,,,,#####,,,,dOOd-----##--------------------------------#
+D:#-----------------------------##,,,,,,bOOb,,,,,,,,,,,,,O,,TTT,,O,########,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@,,#####OOO#####OOTTTOOO#####,,,dOOd-------##-------------------------------#
+D:#----------------------------##,,,,,#,,bOOb,,,,,########O,,T,,,,Oo#######,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,#####,,,#####,,,T,,,,#####,,dOOd---------##------------------------------#
+D:#---------------------------##,,,,,,#,,,bOOb,,,,#######5O,,,,,,,O########,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@,,,,O,,,,,,,O,,,,,,,,,,,,O,,,dOOd---TTTTT---##-----------------------------#
+D:#--------------------------##,,,,,,###,,,bOOb,,,########,O,,,,,,O,,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@,,,,O,,,,,,,O,,,,,,OOOOO,,,dOOd---TTTTTTT---##----------------------------#
+D:#-------------------------##,,,,,,,###,,,,bOOb,,,,,,,,,,,,O,,,,,O,,,,,,,,,,,,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,#####,,,#####,,#####,,,,dOOd-----TTTTT-----##---------------------------#
+D:#------------------------##,,,,,,,#####,,,,bOOb,,,,,,,,,,,,OOO,,,O,########,,,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,#####OOO#####,,#####,,,dOOd-----------------##--------------------------#
+D:#-----------------------##,,,,,,,,#####,,,,,bOOb,,,,,,########O,,,O0#######,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,#####,,,#####,,#####,,dOOdOOOOOOOOOOOOOOOOOOO##-------------------------#
+D:#----------------------##,,,,,,###########,,,bOOb,,,,,#######6O,,,O########,,,,,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,O,,,,,,,O,,,,,,O,,,dOOd-O.###..#####..###.O-##------------------------#
+D:#----------------------##,,,,,,###########,,,,bOOb,,,,########,O,O,,,,,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,OO,,,,,,,,,OOOOOO,,,dOOd--O.###..#####..###.O-##------------------------#
+D:#----------------------##,,,#################,,bOOb,,,,,,,,,,,,,OOO,,,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,#####,,,,,,,#####,,,,dOOd-F-O.###############.O--##-----------------------#
+D:#---------------------##,,,,#################,,,bOOb,,,,,,,#######9O,,,,,,,,,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@,,#####OOOOOOO#####,,,dOOd-FF-O.###############.O--##-----------------------#
+D:#---------------------##,,,,########q########,,,,bOOb,,,,,,########O,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,#####,,,,,,,#####,,dOOd-FFF-O..#############..O--##-----------------------#
+D:#---------------------##,,,,,,,,,,,,,,,,,,,,,,,,,,bOOb,,,,,########,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@,,,,,,,,,,,,,,,,,,,,,dOOd-FFFF-O..#############..O---##----------------------#
+D:#--------------------################,,,,,,,,,,,,,,dOOd,,,,,,,,,,,,,,,,,,,,d##@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,##d,,,,,,,,,,,,,,,,,dOOd-FFFFF-O..#############..O---##----------------------#
+D:#--------------------##T-------------bbbbbbbbbbbbbbbbOObbbbbbbbbbbbbbbbbbbbbb############################################bbbbbbbbbbbbbbbbbbbOOd--------O..#############..O----##---------------------#
+D:#--------------------##TOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.###############.O----##---------------------#
+D:#--------------------##TOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.###############.O----##---------------------#
+D:#--------------------##TOOcccccccccccbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbOObbb############################################bbbbbbbbbbbbbbbbbbbOOd--------O.f##############.O----##---------------------#
+D:#--------------------##TOOc--VVVVV--c,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,dOOd,d##@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@##d,,,,,,,,,,,,,,,,dOOdbbbbbbb-O.f##############.O----##---------------------#
+D:#--------------------##TOOc-VVdWdVV-c,,,######,,,,,,,,,,,,,,,,,T,,,,,bOOb,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,,,,,,,,,,,,,,dOOdbOOOOOb-O.f##############.O----##---------------------#
+D:#--------------------##TOOcVVVWWWVVVc,,,######,,,,,,,,,,,,,,,,TTT,,,bOOb,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,dAAAAAAAAAd,,,,dOOdbOVVVOb-O.###############.O----##---------------------#
+D:#--------------------##TOOc-VVdWdVVOc,,,######,,,,,,,,,,,,,,,,,T,,,bOOb,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,AAAAAAAAAAAAA,,,dOOdbOVVVbb-O.###############.O----##---------------------#
+D:#--------------------##TOOc--VVVVV-Oc,,,##h###,,,,,,,,######,,,,,,bOOb,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,dAAAAAAAAAd,,,,dOOdbOOOOOOOO..#############..O----##---------------------#
+D:#--------------------##TOOcccccccccO,,,,,,O,,,,,,,,,,,######,,,,,bOOb,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,,,,,,,,,,,,,,,,dOOdbbbbbbb-O..#############..O----##---------------------#
+D:#--------------------##TOOOOOOOOOOOO,,,,,,O,,,,,TT,,,,######,,,,bOOb##########,,,@aaaaaaaaaaaaaaaaaaaaaaaa###########dbbbbbbbbbbbbbbbbbbbbbbOO---------O..#############..O---##----------------------#
+D:#--------------------################,,,,,,O,,,TTTT,,,##pp##,,,bOOb,##########,,,@aaaaaaaaaaaaaaaa@@######OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO..#############..O---##----------------------#
+D:#---------------------##,,,,,,,,,,,,,,######O,,,TT,,,,,,O,,,,,bOOb,,##########,,,@aaaaaaaaaaaa######OOOOOO###########dbbbbbbbbbbbbbbbbbbbbbbbOO--------O.###############.O---##----------------------#
+D:#---------------------##,,,,,,,,,,,,,,######,O,,,,,,,,,O,,,,,bOOb,,,##1####e##,,@aaaaaaa######OOOOOO#######aaaaaaaa@,,,,,,,,,,,,,,,,,,,,,,,,,dOOdFFFFF-O.###############.O--##-----------------------#
+D:#----------------------##,,,######,,,,######,,O,,######O,,,,bOOb,,,,,,O,,,,O,,,d########OOOOOO#######aaaaaaaaaaaaaa@,,,,,,,dRRRRRRRRRRRd,,,,,,dOOdFFFF-O.###..#####..###.O--##-----------------------#
+D:#----------------------##,,,######,,,,##jj##,,,O,######O,,,bOOOOOOOOOOOOOOOOOOOOOOOOOOOO#######aaaaaaaaaaaaaaaaaaaa@,,,,,,,RRRRRRRRRRRRR,,,,,,,dOOdFFF-O.###..#####..###.O-##------------------------#
+D:#-----------------------##,,######,,,,,,O,,,,,,,O######O,,bOOb,,,,,,,,,,,,,,,,,d#########aaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,,dRRRRRRRRRRRd,,,,,,,,dOOdFF-OOOOOOOOOOOOOOOOOOO##-------------------------#
+D:#------------------------##,##i###,,,,,,,O,,,,,,Or#####O,bOOb,,,,,,,,,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,dOOdF-O------------------##-------------------------#
+D:#------------------------##,,,O,,,,,,,,,,O,,,,,,,OOOOOOObOOdbbbbbbbbbbbbbbbbbbd####################################dbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbOOd-O------TTTTT------##--------------------------#
+D:#-------------------------##,,,O,,,,,,,,,O,,,,,,,,O,,,,bOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO-----TTTTTTT----###--------------------------#
+D:#--------------------------##,,,,O######,O,######O,,,,bOOdbbObbbbbbbbbbbbbbbbbd####################################dbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbOO------TTTTT-----##---------------------------#
+D:#---------------------------##,,,O######,O,######O,,,bOOb,,bOb,,,,,,,,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,dOOd-------------##----------------------------#
+D:#----------------------------##,,O######,O,######O,,bOOb,,,bOb,,,,,,,,,,,,,,@@@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@,,,#####,,,,,,,,,,,,,,,,,,,,#####,,,dOOd-----------##-----------------------------#
+D:#-----------------------------##,O##u###,O,##v###O,bOOb####bOb####,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,#####OOOOOOOOOOOOOOOOOOOO#####,,,,dOOd--------###------------------------------#
+D:#------------------------------##,OOO,,,,O,,,OOOO,bOOb,#k,,dOd,,m#,,,,,,,,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@,,#####,,,,,,,,,,,,,,,,,,,,#####,,,,,dOOd------##--------------------------------#
+D:#-------------------------------##,,,OOOOOOOO,,,,bOOb,,#,,#NON#,,#,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,O,,,,,,,,,,,,,,,,,,,,,,,,OOOO,,,,,dOOd----##---------------------------------#
+D:#--------------------------------###,,,,,,,T,,,,bOOb,,,#,,#NON#,,#,,,,,,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,#####,,,,,,,,d,,,,,,,,,,,,,,#####,,,,dOO-d###----------------------------------#
+D:#----------------------------------###,,,,TTT,,bOOb,####,##NON##,####,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,#####,,,,,,,,,,,,,,,OOOOOOOO#####,,,,,dOOOOO-----------------------------------#
+D:#------------------------------------###,,,T,,bOOb,,#y,,,#-NON-#,,,z#,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@,,,#####,,,,,,,,,,,,,,O,#####,,#####,,,,,####OOOOO--------------------------------#
+D:#--------------------------------------###,,,bOOb,,,#w,,,#-NON-#,,,x#,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,,,O,,,,,,,,,,OOOOOO,,#####,,,,,,,,,####-----OOOOO------------------------------#
+D:#----------------------------------------###dOOb,,,,####,##NON##,####@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,#####,,,,,,,O,#####,,#####,,,,,,,####---------OOOOO----------------------------#
+D:#------------------------------------------OOOd,,,,,,,,#,,#N4N#,,#,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,,#####,,OOOOO,,#####,,,,,,,,,,,,####-------------OOOOO--------------------------#
+D:#-----------------------------------------OOO###,,,,,,,#,,#####,,#,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,#####,O#####,,#####,,,,,,,,,####-------------------OOOO------------------------#
+D:#----------------------------------------OOO---###,,,,,#l,,,,,,,n#,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@,,,,,,,,,O,,O,#####,,,,,,,,,,,,,#####------------------------OOOO---------------------#
+D:#---------------------------------------OOO------###,,,###########,,@@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@@@,,,,,,,,,,,,,O,,,#####,,,,,,,,,,####-------------------------------OOOO------------------#
+D:#OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO--------###,,,,,,,,,,,,,,@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@,,,,,,,,,,,,,,,,,,,,,,,,,,,######-------------------------------------OOOOOOOOOOOOOOOOOOOO#
+D:#--------------------------------------------------###############@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@##############################------------------------------------------------------------#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_pelar.txt b/lib/mods/theme/edit/t_pelar.txt
new file mode 100644
index 00000000..0057518b
--- /dev/null
+++ b/lib/mods/theme/edit/t_pelar.txt
@@ -0,0 +1,105 @@
+# File: t_pelar.txt
+
+# Pelargir map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+### Additional terrain features ###
+
+#Anduin river
+F:a:227:3
+
+### Buildings ###
+
+# The Grey Swan inn
+F:b:74:3:0:0:0:0:0:68
+
+# The Prince's Tower
+F:c:74:3:0:0:0:0:0:86
+
+# Music store
+F:d:74:3:0:0:0:0:0:64
+
+# Rune Shop
+F:e:74:3:0:0:0:0:0:62
+
+# Hunting Store
+F:f:74:3:0:0:0:0:0:61
+
+# Library
+F:i:74:3:0:0:0:0:0:13
+
+# Casino
+F:j:74:3:0:0:0:0:0:15
+
+# Beastmaster
+F:k:74:3:0:0:0:0:0:16
+
+D:######################################################################################################################################################################################################
+D:#-------------------------------------------------@VVVVV@##VVVVV@@-----------------------------------------------------------------------------------------------------------------------------------# #
+D:#-------------------------------------------------@VVVVV@####VVVVV@----------------------------------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@######VVVV@@@-------------------------------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,###VVVVV@@-----------------------------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,####VVVVV@@---------------------------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,,,####VVVVV@@@------------------------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,,,,,#####VVVVV@@@---------------------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,####,,,,#####VVVVV@@-------------------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,####,,,,,,#####VVVVV@@@----------------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,###7,,,,,,,,,#####VVVVV@@@-------------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,,,,,,TTTT,,,,,,#####VVVVV@@@----------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,,,TTTT,TTTT,,,,,,,#####VVVVV@@@-------------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,,TTTT,,,TTTT,,,,,,,,,#####VVVVV@@@----------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,TTTT,,,,,,,,,,,,,,,,,,,,#####VVVVV@@@@------------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,TTTT,,,,###,,,,,,#######,,,,,#####VVVVV@@----------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,,,,,,,,####,,,,,#######,,,,,,,#####VVVVV@@@-------------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,,,,,,,,,####,,,,###2###,,,,,,OOOO#####VVVVV@@@----------------------------------------------------------------------------------------#
+D:#-------------------------------------------------@VVVVV@####,,,,,,,,,,,####,,,,,,,O,,,,,OOOO,,,,,,##VVVVVVVV@@@-----------------------------------------------------------------------------------aa#
+D:#-------------------------------------------------@VVVVV@####,,,,,,,,,,,,####,,,,,,,O,OOOO,,,,,,,,,#VVVV###VVVVV@@@@----------------------------------------------------------------------------aaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#######,,,,#######,,OOOO,,,,,,,,,,,#VVVV#@@#####VVVVV@@------------------------------------------------------------------------aaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#######,,,,##4##OOOOO,,,,TTT,,,,,,#VVVV#,,,,,#####VVVVV@@@-----------------------------------------------------------------aaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#######,,,,###OOO.,,,,,,TTTTT,,,,#VVVV#,,,,,,,,,#####VVVVV@@@-----------------------------------------------------------aaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,###3###,,,,#OOO##.###,,,,TTT,,,,#VVVV#,,,,####,,,,,#####VVVVV@@@-----------------------------------------------------aaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,,O,,,,,,,OOOO##...####,,,,,,,#VVVV#O,,,,####,,,,,,,,#####VVVVV@@@@-----------------------------------------------aaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,,,O,,,OOOO,,,##......####,,,#VVVV#,,O,,,5###,,,,,,,,,O,#####VVVVV@@------------------------------------------aaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,,,,OOOO,,,,,,##.........####VVVV#,,,,OOOO,,,,,,,,,,,,,O,,,#####VVVVV@@@------------------------------------aaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,OOOO,,,TTT,,,##................#,,,,,,,,OOOO,,,,,,,,,,OOOOOOO#####VVVVV@@@------------------------------aaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,OOOO,,,,,TTTTT,.##...............####,,,,,,,,,OOO,,,,,,,,O,,,,,,,,,#####VVVVV@@@-----------------------aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------OOOOOOOOOOOOO,,,,,,,,,TTT,,,##..................####,,,,,,O,,OOO,,,,,O,,,,,,,,,,,,#####VVVVV@@@----------------aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,,,,,,,,,,,,,,##......,#cc#..........####,,,O,,,,,O,###d###,,,,,,,,,,,,#####VVVVV@@@---------aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@#######################.....########...........####O,,,,,,O#######,,,,,,,,,,,,,,,#####VVVVV@-----aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------OVVVVVVVVVVVVVVVVVVVVVVVVVVV......c########c.............####,,#############,,,,,,,#####,,,#####VVVVV@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------OVVVVVVVVVVVVVVVVVVVVVVVVVVV.......########................OOOO6###########9OOOOOOOOO#,,,,,,,,#####VVV@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVVVVVVVVVVVVVVVVVVVVVVVV.........#cc#...............#####,O#############O,,,,,,#####,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@#######################.......................####,,,,,,O,,#######,OO,,,,,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,,,,,,,,,OOOOO##....................#####,,,,,,,,,O,###e###O,,,,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#####,,,,O,,,,,##..................###,,,,,,,,,,,,,,OOOOOOOO,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,####bOO,O,,,,,,##.................###,,,,,#######,,,,,,,,,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#####,,O,,####,##................aaa##,,,,#######,,,,,,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,O,,,,OOO1###,##.........####aaaaaaa##,,,#######,,,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,O,,,,O,,####,##.....#####,,##aaaaaaa##,,,,,,,,,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,##f##,,,O,,,,,,##..####,,,,,,,##aaaaaaa##,,,,,,,,,,,,,######aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#####,,,,O,,,,,##O###,,,,,,,,,,##aaaaaaa##,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#####,,,,,O,,,,#O###,,,,,,,,,,,,##aaaaaaa##,,,######aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,O,,,,,,,,O,,,OO,,,,,#######,,,,##aaaaaaa#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,O,,,,,.,,OOOO,O,,,,,#######,,,,,##aaaaaaa##aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,##k##,,,,,O##j##,O,,,,#######,,,,,,##aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#####,,,,O,#####,,O,,,#######,,,,,,###aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#####,,,O,,#####,,,O,,,,,,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,O,,,,O,,,,,,,,,,,,O,,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,O,,,O,,,,,,,,,,,,,,O,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,##i##,OOO0####,,,,,,,,O,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#####,O,,#####,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,#####,O,,#####,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,O,,,,O,,,,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,###,,,O,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,###,,,O,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,,,,,O#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####,,,#####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@##########aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@#######aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@####aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:#-------------------------------------------------@VVVVV@##aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_pref.txt b/lib/mods/theme/edit/t_pref.txt
new file mode 100644
index 00000000..1159e817
--- /dev/null
+++ b/lib/mods/theme/edit/t_pref.txt
@@ -0,0 +1,145 @@
+# 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
+
+### Flowers for Theme ###
+
+#Elanor (yellow)
+F:E:79:3
+
+#Fumellar (red)
+F:F:80:3
+
+#Anemones (purple)
+F:A:81:3
+
+#Niphredil (white)
+F:N:82:3
+
+#Iris (blue)
+F:R:83:3
+
+### Destroyed town features ###
+
+# Dead/burnt tree
+F:D:92:3
+
+# Ash
+F:H:93:3
+
+# Fire
+F:=:205:3
+
+# Permanent rubble
+F:$:206:3
+
+# Tainted water
+F:%:174:3 \ No newline at end of file
diff --git a/lib/mods/theme/edit/t_thrand.txt b/lib/mods/theme/edit/t_thrand.txt
new file mode 100644
index 00000000..cce9d684
--- /dev/null
+++ b/lib/mods/theme/edit/t_thrand.txt
@@ -0,0 +1,103 @@
+# File: t_thrand.txt
+
+# Thranduil's Halls map by furiosity <furiosity@zionmainframe.net>
+# NB! The additional terrain features and stores (if any) assume usage of the following files from the 'theme' module:
+# f_info.txt, t_pref.txt, st_info.txt, and ba_info.txt
+# Please download the module and refer to the files for the terrain feature definitions http://modules.t-o-m-e.net/
+# Don't forget to modify the maximum number of terrain features, etc. in misc.txt as well.
+
+#Wooden board - horizontal
+F:a:233:3
+
+#Mallorn
+F:m:243:3
+
+#Force Elven monsters
+f:ELVEN
+
+### Buildings ###
+
+# Thranduil's Hall
+F:b:74:3:0:0:0:0:0:81
+
+# Beastmaster
+F:d:74:3:0:0:0:0:0:16
+
+# Hunter
+F:e:74:3:0:0:0:0:0:61
+
+# Music Store
+F:f:74:3:0:0:0:0:0:64
+
+# Map store
+F:g:74:3:0:0:0:0:0:66
+
+#The Library
+F:h:74:3:0:0:0:0:0:13
+
+D:######################################################################################################################################################################################################
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@@@@@@@@^^^^^^^^^^^^^^^^^^^^^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@,,,,,,,,@^^^^^^^^^^^^^^^^^^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@,,,,,,,,@^^^^^^^^^^^^^^^^^^^@,,,,,,,,,,,,,,A,,,,,,,,,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@,,,3,,,,@^^^^^^^^^^^^^^^^^^^@,,,,TTT,,,,,A,b,A,,,,,,TTT,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,,,,@^^^^^^^^^^^^^^^^^^^@,,,TTmTT,,,,,F,F,,,,,,TTmTT,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,,,,,,,,@^^^^^^^^^^^^^^^^^^^@,,,,TTT,,,,,,R,R,,,,,,,TTT,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^,,,,,,^^^@@@@@@@,,,^^^^^^^^^^^^^^^^^^@,,,,,,,NE,,,,F,F,,,,,EN,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^,,,,,,,^^^^^^^^^^^^^^^^,,^^^^^^^^^^^^^^^^^@,,,,,,,,NE,,,R,R,,,,EN,,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^,,,,,,^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^@,,,,,,,,,NE,,F,F,,,EN,,,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^,,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^@@@@@@,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,@^^^^^^^^^^^^^^^^@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^@,,,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^@,,@@@@@@@@@@@,@@@@@@@@@@@@@@@,^^^^^^^^^^^^@@@@,,,,,,,@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^@,,,,2,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^,,,^^^^^^^^^^^,^^^^^^^^^^^^^^^^,^^^^^^^^@@@,,,,,,,,,,,,,,,@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^@,,,,,,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^,,,,,,,,^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^,^^^^^^^@,,,,,,,,,,,,,,,,,,5@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^@@@@@@@@@,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,^^,,,,,,^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^,,,,,,,,,,,,,,,,,,,,,,,,,@@@^^^^^^^@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^^^^^^@@@@,,,,,,,@@@@,,^^^^@@@@@,,,,,,h@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@@@@@@@^^^^^^,,^^,,,,,,,,,,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,,,,,,,,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@@@@@4,,,,,,@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^,,^^^^^,,,,^^^^^^^^^^^^^^^^^^@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^,,,,^^^^^^^^^^^^^^^@9,,,,,,,6@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^^^@@@@@@^^,^^^^^^^^^^^^,,,,,,,,,,,,,,,,,,,,,,,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^@e,,,,,,,^^^^^^^^^^^^^^^^^,,^^^^^^^^^^^@@@@@@@@@^,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@@@@@@@^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#VVVVVV@@@@@@@@@@@@@@@@@@@,,,,,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,@@@@@^^^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#VVVVVVVVVVVVVVVVVVVVVVVVaaa,,,@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@,,,,d@@@@^^^^^^^^^^^^^^^,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#VVVVVVVVVVVVVVVVVVVVVaaa,,,,aaaVVVV@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@@@0,,,,,,,,,,,,,,,,,,,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:#@@@@@@@@@@@@@@VVVVaaa,,,,aaaVVVVVVVVVVVVVV@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@@@@@@@^^^^^^^^^^^^^^,,,,,,^^^^^^^^^^^^^^@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @@@aaa,,,aaaVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,^^^^^^^^^^^@,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@aaa@@@@@@@VVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,^^^^^^^@,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@@@@VVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,,^^,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@VVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,,,,,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^,,,,,,,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@,,,,,f@^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@,,,,@^^^^^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@@@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@,,,,,@^^^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@g,,,,,@^^^^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@,,,,,@^^^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@@@@@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@@@@^^^^^^^^^^^^^^^^^^^^^^^^@,,,,,,@^^^^^^^^^^^^^^^^^#
+D:# @,,,,,@ @@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@^^^^^^^^^^^^^^^^^^^^@,,,,,,@^^^^^^^^^^^^^^^#
+D:#@,,,,,@ @@@@@@@@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@@^^^^^^^^^^^^^^^^^@,,,,,,@^^^^^^^^^^^#
+D:#,,,,,@ @@@@@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@^^^^^^^^^^^^^^^^@,,,,@^^^^^^^^^#
+D:#,,,,@ @@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@@^^^^^^^^^^^^,,,,MMM^^^^^^#
+D:#,,,@ @@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@^^^^^^^^^^^^III^^^^^#
+D:#,,@ @@@@@@@@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@^^^^^^^^III^^^^#
+D:#,@ @@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@^^^^^^III^^^#
+D:#@ @@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@@@@^^III^^#
+D:# @@@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVV@@@@MMM^#
+D:# @@@@@@@@VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV#
+D:###################################################################################################################################################################################################### \ No newline at end of file
diff --git a/lib/mods/theme/edit/thieves.map b/lib/mods/theme/edit/thieves.map
new file mode 100644
index 00000000..ba025ff6
--- /dev/null
+++ b/lib/mods/theme/edit/thieves.map
@@ -0,0 +1,64 @@
+# Floor
+F:.:1:6
+
+# Dark floor
+F:,:1:4
+
+# Permanent wall
+F:X:61:4
+
+# Lit permanent wall
+F:x:61:6
+
+# Magically locked door
+F:M:38:22
+
+# Locked 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:0:0:0:0:0:0:2
+
+# Floor with Bandit
+F:b:1:6:150:43:*:0:0:0:0:2
+
+# Dark floor with novice rogue
+F:f:1:4:44:0:0:0:0:0:0:2
+
+# 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:XXXXxa..xb..x...x...xXXXX,,,,,,,X
+D:XXXXx...x...xa..x...xXXXX,,,,,,,X
+D:XXXXx..ax...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,,,xa.ax.a.x...x...xXXXXXXXXXXXX
+D:xf,fx...x...x...x...xXXXXXXXXXXXX
+D:xxxxxxxxxxxxxxxxxxxxxXXXXXXXXXXXX
+
+# Starting position
+P:4:4
+
diff --git a/lib/mods/theme/edit/thrain.map b/lib/mods/theme/edit/thrain.map
new file mode 100644
index 00000000..8adc41be
--- /dev/null
+++ b/lib/mods/theme/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:2
+F:2:1:0:952:0:0:0:0:0:61:2
+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/mods/theme/edit/tr_info.txt b/lib/mods/theme/edit/tr_info.txt
new file mode 100644
index 00000000..7f9e4df9
--- /dev/null
+++ b/lib/mods/theme/edit/tr_info.txt
@@ -0,0 +1,815 @@
+# 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
+
+#
+# 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:100: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:Whoa!
+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/mods/theme/edit/trolls.map b/lib/mods/theme/edit/trolls.map
new file mode 100644
index 00000000..e5d104fd
--- /dev/null
+++ b/lib/mods/theme/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:0:0:0:0:0:0:2
+
+# Floor with stone troll
+F:s:89:3:401:0:0:0:0:0:0:2
+
+# Floor with algroth
+F:a:89:3:424:0:0:0:0:0:0:2
+
+# Floor with Bert
+F:b:89:3:493:0:0:0:0:0:0:2
+
+# Floor with Bill
+F:i:89:3:494:0:0:0:0:0:0:2
+
+# 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/mods/theme/edit/v_info.txt b/lib/mods/theme/edit/v_info.txt
new file mode 100644
index 00000000..964f2b54
--- /dev/null
+++ b/lib/mods/theme/edit/v_info.txt
@@ -0,0 +1,2282 @@
+# 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
+
+### 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 (checkerboard)
+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/mods/theme/edit/volcano.txt b/lib/mods/theme/edit/volcano.txt
new file mode 100644
index 00000000..1b89cf3d
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/edit/w_info.txt b/lib/mods/theme/edit/w_info.txt
new file mode 100644
index 00000000..6ca43cfd
--- /dev/null
+++ b/lib/mods/theme/edit/w_info.txt
@@ -0,0 +1,141 @@
+# 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
+# Updated to include a more accurate location of Angband/Gondolin and many new features by furiosity for Theme
+
+W:D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+W:D:Xg=====================gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:Xgggggg==============gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:Xggggggg==============ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:Xgggggggggg===========ggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggX
+W:D:XWWAAWCCCWWWW===========WWWgggWWWWWgggggggggWWWWWWWWWWggggWWWWWWWWWWWWWggggWWWWWWWWggggWWWWWWWWWggggX
+W:D:XWWAAAACCCCWWWWAA========WWWWWWWWWWWWWggggggggWWWWWWWWWWWWWWWWWWWWWWWWWWggWWWWWWWWWWWWWWWWWWWWWWEEEEX
+W:D:XWWWWAAAAAW%AAAA========WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW.EEEEEEEEX
+W:D:XWWWWWWAAWAAAWW===========WWWWWWW...WWWWWWWWWW..........WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW.EEEEEEEEEEEX
+W:D:XWWWWWWWWWWWWWW==========..WWW....WWWWWWWW................WWWWGGGGWWWWWWWW......WWWWW.EEEEEEEEEEEEEEX
+W:D:XTM.MMMMWWWW===...====...........HHHH..................GGGGGGGWWWDWWWW.........WWWW.EEEEEEEEEEEEEEEEX
+W:D:XT.2MMM=======.BB.=............HHHHHHHH..&&&&&......GGGGGGGGGGGGG.................tEEEEEtEEEEttEEEEEX
+?:[EQU $TOWN_DESTROY2 1]
+W:M:0:1
+W:D:XT.PMMM=======.BB.=............HHHHHHHH..&&&&&......GGGGGGGGGGGGG.................tEEEEEtEEEEttEEEEEX
+?:1
+W:D:XMMMMM======..BB.===.............HHHH.......&&&&&&GG.._._.......................ttttttttttttttttttttX
+W:D:X======...._.BB._..._............................&....._._TTT................GG........tttttttttttttX
+W:D:X=====...._..BB.._.._..HHHH................&....&&&~~~~.TT_TTTT..........G.....GGG........tttttt^^^^X
+W:D:X===....._...BB..._._..HHH.......HHHHHH.....&&&&._&&..~.TTT_T_TT..^D......GGGGGG..............ttt^^^X
+W:D:X===...._..TBBB...._._..H.=....HHH|HH..........__.&&__~.TTTT_o_TT.D8........_.....................^^X
+W:D:X===...._....BB....._..HH.=_....H.|..........__...&&..~.TTTTTT_TT.=........_........................X
+W:D:X===..._...BBBB...._....HHHH__....|........._.....&&..~.TTTTTT____a........_........................X
+W:D:X===...._..TTBB..._........._.....|H......._......&q..~6TTTTT&&&T._........._.......................X
+W:D:X===....==..TBBB._......HH.._.TT..|.HH...._..TTT._&&..~..*TT&I&&&T._........._......................X
+W:D:X====...==.....==......HHd+-_TTTT-x1HH---------_Lq&&---------------_........_.......................X
+W:D:X=====..========....H....H|..____H|uH....._..._.._&...~..TTTTTTTTTT._........_......................X
+W:D:X==============....HHH....|..._TTH|......._.._.._&&^...~..TTTTTTTTT.._........_....................tX
+W:D:X======..==....BB...H.....|..._..H|HH...._.._...&&.....~..TTTTTTTT...._......_....................ttX
+W:D:X======........BB.........[---_...|.H...._._..^^&&^.I._~..TTTTTTTT....._......_..................tttX
+W:D:X======........BB............._[--|....._.._..^^&&___I.~~.TTTTTTTT......_.._._..................ttttX
+W:D:X======........BB....B......._....|....._._...^^&&.....~~.TTTTT.T........__.._.................tttttX
+W:D:X=======.....TBBB..BBBB....._.....|....._....^^&&&....~~...TTTT.............._...............tttttttX
+W:D:X==========..TBBBT........._......|...._.....^^&&.....~~...TTTT..............._...._.......TtTttttttX
+W:D:X==========..TTTBBTBB....._.......|II__.....^)^&&....~~....TTTTTTT............._.__._=...TTTTTTtttttX
+W:D:X==========...TTBTT......_........___II_..._^^^&&....~~..TTTTTTTTTT............._....==...T==TTtttttX
+W:D:X==========.....T.T....._........_.....____^___R__...~~.TTTTTTTTTT...................========TTtttttX
+W:D:X===========.=........._........_..........^^&&&_!7!~~..TT&TTTTTTT..................=========TttttttX
+W:D:X================....__........_..ttt......^M&&._!4!~~..TTTTTTTTT...................=========...ttttX
+W:D:X=================.==E........_....tt.....TTMM&..!!!~.....TTTTT..................^^.========......ttX
+W:D:X===================EE........._.........TTMMM.......~~.........................^^^^.=======.......tX
+W:D:X===================E==......._..........TTM)MTTTTT._..~~~~;;....................^^^.==...==.......tX
+W:D:X===================E==......_...........T&MWMMTTTT_I_.....~;;..................^^^^^.=...=........tX
+W:D:X=====================......_.ttt........T&M5MTTTTTT..__I~~;;...................^^^^^^.............tX
+W:D:X=====================.....=_.tt.........&&MMMTTTTT.....~...........................^^.............tX
+W:D:X======================...==..ttt........&&_&&&.._.......~~........................................tX
+W:D:X=======================.===..............^_^....._........~.......................................tX
+W:D:X===========================..............._......._......~........................................tX
+W:D:X==========================.tt......._._.._........_....H=HH...SSS.................................tX
+W:D:X==========================.tt._.._t_...__.........._..H===H.SSSS.................................ttX
+W:D:X==========================..__.__._._._..&&&b....._....H=HH..SS.A.A.............................tttX
+W:D:X============================....._..._.__.&&&......_....~~.....AAOAAA..A..AA..A..A..AA.........ttttX
+W:D:X===========================.............._&&&^^....._....~~I...AA''AAAAAAAAAAAAAAAAAAAAAAA......tttX
+W:D:X===========================.......^.&.^^&&&&&&&......__.~~III...AAVVVVVV$AVVVAVAA_"""_"""""".....ttX
+W:D:X===========================.....^^.&.&_&^&.&^&_&&&9...._..~~II..AAVVUVVVVVVVVVVA_"""""_"""""".....tX
+W:D:X===========================....^....__^^...^^^._^&&........~~.c.AAVVUUVVVVVVVAA_"""""""_"""".......X
+W:D:X===========================..^^^_.._.......^^._.^^&&&........~~.AAVVVVVVVVVVAA""_""""""_"""""""....X
+W:D:X============================.^T_.__._........_&^^^.&&&&&......e.AAsAAVAAAVVV"""""_""""_"""""""....DX
+W:D:X============================.T^_..._....HH.._&&&^^.._&&&&.&...~.AAA""A"A""""""""=="""_"""""""""".DDX
+W:D:X============================.T^._.HHHHHHHH...__...._^&^&&&&&.~..AA""""""""""""======_"""""""""""DDDX
+W:D:X===========================.^^._...H.H........._.._.^&^^._^3.~..sA"""_"_"""""=====""""""""""""DDDDDX
+W:D:X==========================..^^.._.....===.=====_._&^...._...^.~.AA"__"_"__======"""""AA""""DDDDDDDDX
+W:D:X==========================.H...._...===========_&&^...._._...~~.AA_""""""""""""_""A"AADDDDDDDDDDDDDX
+W:D:X========================.HHH=...=_.==========&^^^^.._._..._.~~..AAA""AAA""A"""AA_AAAADDDDDDDDDDDDDDX
+W:D:X======================....=====.==============^.^._=._.....~~...AAAAAAAAAAAAAAAAAAADDDDDDDDDDDDDDDDX
+W:D:X==============================================.^^^==....~~~~....................DDDDDDDDDDDDDDDDDDDX
+W:D:X===============================================..====~~~f~...............DDDDDDDDDDDDDDDDDDDDDDDDDDX
+W:D:X===============================================.==^==_............DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDX
+W:D:X=================================================^^===........DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDX
+W:D:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+# Definition of all the entrances to the dungeons
+# W:E:<dungeon>:<y>:<x>
+
+#Orodruin
+W:E:5:50:70
+
+# Numenor
+W:E:7:25:1
+
+# Paths of the Dead
+W:E:16:49:38
+
+# Illusory Castle
+W:E:17:15:96
+
+# Maze
+W:E:18:57:28
+
+# Erebor
+W:E:20:15:66
+
+# 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:28:92
+
+# The Withered Heath
+W:E:27:10:65
+
+# The Grinding Ice
+W:E:29:1:1
+
+# Dol Guldur
+W:E:23:33:58
+
+# The Northern Waste
+W:E:31:5:44
+
+# The Blue Mountains
+W:E:32:29:18
+
+# Dol Amroth
+W:E:33:60:48
+
+# Angmar
+W:E:34:13:48
+
+# Near Harad
+W:E:35:64:97
+
+# Isengard
+W:E:36:42:43
+
+# Tol Eressea
+W:E:37:64:1
+
+# Utumno
+W:E:38:1:99
+
+# Starting position
+W:P:35:21 \ No newline at end of file
diff --git a/lib/mods/theme/edit/wf_info.txt b/lib/mods/theme/edit/wf_info.txt
new file mode 100644
index 00000000..764462b7
--- /dev/null
+++ b/lib/mods/theme/edit/wf_info.txt
@@ -0,0 +1,384 @@
+# 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
+
+# <feat> is the feature from f_info that will be used to represent the tile
+# in the map
+
+# <terrain_idx> is one of the TERRAIN_foo defines of defines.h
+
+# define idx feature r_info flag
+
+# TERRAIN_EDGE 0 /* Edge of the World */ none
+# TERRAIN_TOWN 1 /* Town */ WILD_TOWN
+# TERRAIN_DEEP_WATER 2 /* Deep water */ WILD_OCEAN
+# TERRAIN_SHALLOW_WATER 3 /* Shallow water */ WILD_SHORE
+# TERRAIN_SWAMP 4 /* Swamp */ not implemented
+# TERRAIN_DIRT 5 /* Snow */ WILD_WASTE
+# TERRAIN_GRASS 6 /* Grass */ WILD_GRASS
+# TERRAIN_TREES 7 /* Trees */ WILD_WOOD
+# TERRAIN_DESERT 8 /* Desert */ not implemented
+# TERRAIN_SHALLOW_LAVA 9 /* Shallow lava */ WILD_VOLCANO
+# TERRAIN_DEEP_LAVA 10 /* Deep lava */ WILD_VOLCANO
+# TERRAIN_MOUNTAIN 11 /* Mountain */ WILD_MOUNTAIN
+
+# <character> is the character used to reference this wf_info entry on the
+# wilderness map file
+
+# X: list of f_info features used by the plasma generator to create the levels
+# corresponding to that wilderness tile (like, lots of mountain, some
+# trees, a bit of water...)
+
+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:79: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:82: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:83:96:96
+
+N:4:Caras Galadhon
+D:Lothlorien's chief city
+W:1:4:0:203:1:4
+X:88:88:96:96:96:96:199:96:80:96:96:96:96:96:96:89:89:89
+
+N:5:grass
+D:a plain of grass
+W:20:0:0:89:6:.
+X:89:89:89:89:89:89:199:89:81:89:89:89:89:89:89:89:96:96
+
+N:6:forest
+D:a forest
+W:40:0:0:96:7:T
+X:88:88:96:96:96:96:199:96:82:96:96:96:96:96:96:89:89:89
+
+N:7:road
+D:a west-east road
+W:5:0:12:200:6:-
+X:200:200:88:88:89:89:199:89:89:89:79:89:89:89:89:89:96:96
+
+N:8:road
+D:a north-south road
+W:5:0:3:200:6:|
+X:200:200:88:88:89:89:199:89:89:89:79:89:89:89:89:89:96:96
+
+N:9:mountain
+D:a mountain chain
+W:60: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:200:6:+
+X:200:200:88:88:89:199:89:89:89:89:79:89:89:89:89:89:96:96
+
+N:11:road
+D:a west-east-north road
+W:5:0:13:200:6:z
+X:200:200:88:88:89:199:89:89:89:89:79:89:89:89:89:89:96:96
+
+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:90: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:35:0:0:225:3:_
+X:187:187:187:84:84:84:84:84:222:222:84:84:84:84:84:1:88:89
+
+N:15:Mirkwood
+D:The Forest of Mirkwood
+W:13:1001:0:7:7:*
+X:88:88:96:96:82:96:96:96:96:79:96:96:96:96:96:89:89:89
+
+N:16:Barad-Dur
+D:Barad-Dur
+W:34:1002:0:7:11:$
+X:1:1:94:94:94:94:212:94:94:94:94:94:94:94:94:94:212:212
+
+N:17:Angband
+D:The Pits of Angband
+W:67:1003:0:7:11:%
+X:94:94:90:90:90:90:90:90:90:90:90:90:90:90:90:90:92:92
+
+N:18:mountain
+D:a mountain
+W:60:0:0:97:11:^
+X:1:1:89:89:94:96:96:97:97:97:97:97:97:97:97:97:97:97
+
+N:19:desert
+D:a desert
+W:44:0:0:91:5:D
+X:91:91:91:91:91:91:91:91:91:91:91:91:91:91:91:91:91:92
+
+N:20:jungle
+D:a jungle
+W:40:0:0:202:7:t
+X:88:88:202:96:202:96:202:96:81:96:202:96:202:96:96:89:89:89
+
+#The Dead Marshes replace 'swamp'
+# Ugly: I used a non-wilderness terrain_idx to force meaner monsters -furiosity
+N:21:Dead Marshes
+D:a part of the Dead Marshes
+W:127:0:0:223:174:S
+X:223:223:223:223:223:223:223:223:223:223:84:223:210:223:208:208:102:223
+
+N:22:glacier
+D:a glacier
+W:45:0:0:90:5: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:15:0:0:228:6:,
+X:1:1:88:88:228:228:228:228:228:79:228:228:228:228:228:228:96:96
+
+N:24:Moria
+D:The Doors of Moria
+W:50: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
+
+N:28:dark pit
+D:a dark pit
+W:60:0:0:87:5:C
+X:87:87:87:87:87:87:87:87:87:214:214:214:214:214:214:214:214:214
+
+N:29:low hill
+D:a low hill
+W:15:0:0:213:6:H
+X:1:1:89:89:88:96:96:213:213:213:213:213:213:213:213:213:213:213
+
+N:30:dark mountain
+D:a dark mountain
+W:80:0:0:214:11:A
+X:214:214:214:214:214:214:214:214:214:214:214:214:214:214:214:214:214:214
+
+N:31:blue mountain
+D:a blue mountain
+W:50:0:0:215:11:B
+X:1:1:89:89:88:96:96:215:215:83:215:215:215:215:215:215:215:215
+
+N:32:grey mountain
+D:a grey mountain
+W:40:0:0:216:11:G
+X:1:1:89:89:88:96:96:216:216:216:80:216:216:216:216:216:216:216
+
+N:33:Mount Doom
+D:a part of Mount Doom
+W:127:0:0:217:11:U
+X:217:217:217:217:217:217:217:217:217:217:217:217:217:217:217:217:217:217
+
+N:34:Mallorn forest
+D:a Mallorn forest
+W:5:0:0:243:7:!
+X:88:88:243:243:96:96:243:199:82:243:243:96:243:96:243:243:243:243
+
+N:35:Redhorn Pass
+D:Redhorn Pass
+W:30:0:0:218:6:R
+X:94:94:90:90:90:90:90:94:90:90:90:94:90:90:90:90:95:95
+
+N:36:Morannon
+D:Morannon (the Black Gate of Mordor)
+W:34:0:0:224:11:O
+X:224:224:224:224:224:224:224:224:224:224:224:224:224:224:224:224:224:224
+
+N:37:evergreen wood
+D:an evergreen wood
+W:40:0:0:202:7:E
+X:94:94:219:219:219:219:219:219:82:219:219:219:219:219:219:89:94:94
+
+N:38:Rivendell
+D:the valley of Rivendell
+W:5:14:0:96:1:L
+X:88:97:202:96:202:97:202:96:81:97:202:96:202:96:96:89:97:89
+
+N:39:Gorgoroth
+D:the valley of terror
+W:90:0:0:91:5:V
+X:91:91:91:86:94:94:94:86:91:91:94:94:86:94:94:86:91:94
+
+N:40:Northern Waste
+D:the northern wastelands
+W:30:0:0:93:5:W
+X:91:90:90:91:90:91:90:91:90:91:91:91:90:91:90:91:91:91
+
+N:41:crossroads
+D:a crossroads
+W:5:0:15:200:6:x
+X:200:200:88:88:89:199:89:89:89:89:79:89:89:89:89:89:96:96
+
+N:42:north-south-east road
+D:a north-south-east road
+W:5:0:7:200:6:[
+X:200:200:88:88:89:199:89:89:89:89:79:89:89:89:89:89:96:96
+
+N:43:swamp
+D:a swamp
+W:45:0:0:226:3:I
+X:226:226:174:84:84:226:174:226:84:84:174:226:226:174:226:174:84:174
+
+N:44:Nurn
+D:the valley of Nurn
+W:50:0:0:94:6:"
+X:94:93:94:89:93:89:94:89:93:94:94:93:89:93:89:93:94:94
+
+N:45:The Brown Lands
+D:the Brown Lands
+W:30:0:0:174:5:;
+X:174:174:89:89:174:174:89:89:174:94:93:174:174:94:174:89:89:94
+
+N:46:Udun
+D:the valley of Udun
+W:50:0:0:86:6:'
+X:94:93:86:86:94:93:86:86:94:93:86:86:94:93:86:86:94:93
+
+N:47:Anduin
+D:Anduin, the great river
+W:35:0:0:227:3:~
+X:187:227:187:84:84:227:84:84:222:227:84:84:227:84:84:227:88:94
+
+### New Towns ###
+
+# Beorn's Halls
+N:49:Beorn's Halls
+D:the dwelling of Beorn the Shape-changer
+W:1:6:0:89:1:6
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+# Cerin Amroth
+N:50:Cerin Amroth
+D:a place of peace
+W:1:7:0:89:1:7
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+# Dale
+N:51:Dale
+D:a city of Men, being rebuilt
+W:1:8:0:91:1:8
+X:91:91:91:91:91:91:91:91:91:91:91:91:91:91:91:91:91:91
+
+# Edoras
+N:52:Edoras
+D:the capital of Rohan
+W:1:9:0:203:1:9
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+# Esgaroth
+N:53:Esgaroth
+D:the city of Lake-Men
+W:1:10:0:203:1:a
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+# Helm's Deep
+N:54:Helm's Deep
+D:the great fortress of the Rohirrim
+W:1:11:0:89:1:b
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+# Henneth Annun
+N:55:Henneth Annun
+D:a Ranger hideout
+W:1:12:0:89:1:c
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+# Hobbiton
+N:56:Hobbiton
+D:a Hobbit village
+W:1:13:0:203:1:d
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+# Osgiliath
+N:57:Osgiliath
+D:a stronghold of Men
+W:1:15:0:203:1:e
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+# Pelargir
+N:58:Pelargir
+D:the great city at the mouth of Anduin
+W:1:16:0:203:1:f
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+# Thranduil's Halls
+N:59:Thranduil's Halls
+D:the hidden realm of the Wood-elves
+W:1:17:0:213:1:o
+X:88:88:89:89:89:89:79:96:96:96:96:96:96:96:96:96:96:96
+
+### Cirith Ungol a 'defined' feature to allow it to have two entrances
+N:60:Cirith Ungol
+D:the dreaded Spider Pass
+W:25:1009:0:7:6:s
+X:91:91:91:86:94:94:94:86:91:91:94:94:86:94:94:86:91:94
+
+### Bree Swamp
+N:61:Bree Swamp
+D:The Bree Swamp
+W:1:0:0:225:6:u
+X:187:187:187:84:84:84:84:84:222:222:84:84:84:84:84:1:88:89
+
+### Orc Cave a 'defined' feature to allow it to have two entrances
+N:62:Orc Cave
+D:a dark tunnel leading to an Orc Cave
+W:15:1019:0:7:6:q
+X:1:1:88:88:89:89:89:89:89:89:89:89:89:89:89:89:96:96 \ No newline at end of file
diff --git a/lib/mods/theme/edit/wights.map b/lib/mods/theme/edit/wights.map
new file mode 100644
index 00000000..b67963ac
--- /dev/null
+++ b/lib/mods/theme/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:0:0:0:0:0:0:2
+
+# Floor with barrow wight
+F:b:88:3:499:0:0:0:0:0:0:2
+
+# Floor with Emperor Wight
+F:e:88:3:604:0:0:0:0:0:0:2
+
+# 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/mods/theme/edit/wolves.map b/lib/mods/theme/edit/wolves.map
new file mode 100644
index 00000000..0e3030ea
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/book-0.txt b/lib/mods/theme/file/book-0.txt
new file mode 100644
index 00000000..5f317ff2
--- /dev/null
+++ b/lib/mods/theme/file/book-0.txt
@@ -0,0 +1,86 @@
+
+ Ted Sandyman's Important Compendium 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/mods/theme/file/book-1.txt b/lib/mods/theme/file/book-1.txt
new file mode 100644
index 00000000..b1aa102f
--- /dev/null
+++ b/lib/mods/theme/file/book-1.txt
@@ -0,0 +1,83 @@
+
+ Ted Sandyman's Important Compendium 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 a 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/mods/theme/file/book-10.txt b/lib/mods/theme/file/book-10.txt
new file mode 100644
index 00000000..995c2453
--- /dev/null
+++ b/lib/mods/theme/file/book-10.txt
@@ -0,0 +1,75 @@
+ ------------------------
+ | Monstrous Compendium 2 |
+ | Mostly never-moving |
+ ------------------------
+
+ *** Floating eyes (e) ***
+
+Strange disembodied eyes, floating around the dungeon. Some
+of them are comparatively harmless (such as the [[[[[oFloating eyes],
+[[[[[RRadiation eyes], [[[[[rBloodshot eyes], and [[[[[vDisenchanter eyes]),
+others moderately dangerous (such as the [[[[[DEvil eyes], [[[[[sGauths],
+[[[[[gGas spores], and [[[[[BSpectators]), yet others extremely deadly -
+these include the [[[[[UBeholders], [[[[[uUndead beholders], [[[[[bEyes of the]
+[[[[[bdeep], and the [[[[[yBeholder hive-mothers]. Whatever you do, do
+not get too close to them
+
+
+ *** Jellies (j) ***
+
+You will sometimes encounter large quavering piles of flesh
+just lying around the dungeon or making their way along the
+walls. They all have different defense mechanisms, which with
+time an adventurer will learn to recognise by looking at their
+colour. Among the less dangerous types are [[[[[wWhite jellies], [[[[[rRed]
+[[[[[rjellies], [[[[[bBlue jellies], [[[[[vGrape jellies], [[[[[gGreen jellies], [[[[[WSilver]
+[[[[[Wjellies], [[[[[yYellow jellies], [[[[[oSpotted jellies], and [[[[[uRot jellies].
+Considerably more dangerous are the [[[[[DUndead masses] and [[[[[UOchre]
+[[[[[Ujellies], as well as [[[[[DBlack puddings]. Among the moving jellies,
+watch out for [[[[[gGreen oozes], [[[[[bBlue oozes], [[[[[DBlack oozes], [[[[[GGelatinous]
+[[[[[Gcubes], [[[[[RGibbering mouthers], and [[[[[BAcidic cytoplasms].
+
+ *** Molds (m) ***
+
+Molds are strange growths on the dungeon floor which have
+developed defense mechanisms to deal with anyone who tries
+to disturb them. They come in many varieties and prefer
+different habitats, and some of them can be quite dangerous
+because they have rudimentary mastery of the elements. The
+most common varieties of mold are the [[[[[sGrey mold], the [[[[[rRed]
+[[[[[rmold], the [[[[[oHairy mold], the [[[[[gGreen mold], the [[[[[uBrown mold],
+the [[[[[yYellow mold], the [[[[[wWhite mold], and the [[[[[WSilver mold].
+More rare are [[[[[vDisenchanter molds], [[[[[bShimmering molds], [[[[[RPink]
+[[[[[Rmolds], [[[[[GTree molds], and [[[[[BBlue molds]. A very rare and dire
+creature is the [[[[[DDeath mold] - unwary adventurers beware! The
+[[[[[UAdventurer molds] are friendly towards their fellows, but
+not very strong.
+
+ *** Mushroom patches (,) ***
+
+You'd think it safe to eat them - you'd be wrong. Mushroom
+patches are masters of disguise and can cause quite a bit
+of damage to unwary adventurers. Fortunately, most of them
+are extremely weak and can be destroyed quickly if you can
+just get close enough. The commonly found varieties include
+the [[[[[sgrey], [[[[[Bclear], [[[[[vpurple], [[[[[ospotted], [[[[[yyellow], [[[[[Rshrieker], [[[[[Ddark],
+[[[[[wwhite], [[[[[ubrown], [[[[[Wsilver], and [[[[[Ggreen] mushroom patches. More rare
+are [[[[[gshambling mounds], [[[[[Bmagic mushroom patches], [[[[[rblood sprouts],
+and [[[[[bmemory mosses].
+
+ *** Quylthulgs (Q) ***
+
+Strange pulsing mounds of flesh that looks utterly harmless.
+As with any other monster, however, looks are very deceiving
+when it comes to the [[[[[yQuylthulgs]. They are expert at asking
+for other creatures' help, as weall as conveyance magic. The
+only friendly type is the [[[[[wAdventurer quylthulg] - these have
+many friends among their own kin. The [[[[[vNexus quylthulg] is
+unmatched in conveyance magic; you will have trouble getting
+close to it. The other quylthulgs usually prefer to call for
+aid, and you can tell what kind of creatures are likely to
+come to its call, usually. There are [[[[[DSpider], [[[[[sCanine], [[[[[bAquatic],
+[[[[[uRotting], [[[[[gDraconic], [[[[[rDemonic], [[[[[UGreater rotting], [[[[[Ggreater]
+[[[[[GDraconic], [[[[[RGreater demonic], and [[[[[BMaster quylthulgs].
+
+
diff --git a/lib/mods/theme/file/book-101.txt b/lib/mods/theme/file/book-101.txt
new file mode 100644
index 00000000..e45865a7
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/book-102.txt b/lib/mods/theme/file/book-102.txt
new file mode 100644
index 00000000..3b737923
--- /dev/null
+++ b/lib/mods/theme/file/book-102.txt
@@ -0,0 +1,4 @@
+gimli means star
+kadar means city
+izindi means starlight
+lomi means night
diff --git a/lib/mods/theme/file/book-103.txt b/lib/mods/theme/file/book-103.txt
new file mode 100644
index 00000000..0ffeea37
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/book-104.txt b/lib/mods/theme/file/book-104.txt
new file mode 100644
index 00000000..3b60e8ca
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/book-105.txt b/lib/mods/theme/file/book-105.txt
new file mode 100644
index 00000000..f3667d64
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/book-106.txt b/lib/mods/theme/file/book-106.txt
new file mode 100644
index 00000000..81c4043c
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/book-107.txt b/lib/mods/theme/file/book-107.txt
new file mode 100644
index 00000000..1abc6d8e
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/book-11.txt b/lib/mods/theme/file/book-11.txt
new file mode 100644
index 00000000..5d211818
--- /dev/null
+++ b/lib/mods/theme/file/book-11.txt
@@ -0,0 +1,51 @@
+ ------------------------
+ | Monstrous Compendium 3 |
+ | Insects |
+ ------------------------
+
+ *** Ants (a) ***
+
+Ants are worker six-legged insects, and adventurers will
+encounter many different kinds of these on their travels.
+The [[[[[uSoldier ants] and [[[[[oGiant army ants] are just your
+garden-variety ants, and there are also [[[[[UTermites], which
+breed very quickly, and [[[[[BAquatic ants], which have adapted
+to living underwater. The majority of ants, however, come
+from different families, with rudimentary mastery of the
+elements - they vary from [[[[[wwhite], [[[[[rred], [[[[[sgrey], [[[[[Wsilver],
+[[[[[Dblack], and [[[[[yyellow] to [[[[[bblue], [[[[[Rfire], [[[[[gtree], and
+[[[[[Ggreen].
+
+ *** Flying insects (I) ***
+
+There are many different kinds of flying insects in Middle-
+earth - some, like [[[[[BButterflies], are harmless, others are
+only slightly dangerous (like [[[[[WMoths], [[[[[uInsect swarms], and
+[[[[[sGiant fleas]). Yet others are serious nuisances due to their
+explosive breeding, among them are [[[[[gNeekerbreekers], [[[[[GGiant]
+[[[[[Gfruit flies], [[[[[DGiant black midges] and [[[[[wGiant white midges].
+Adventurers should be wary of [[[[[yHummerhorns], [[[[[oKiller bees],
+[[[[[rGiant fireflies], and [[[[[UFlies of Mordor].
+
+ *** Dragonflies (F) ***
+
+Large insects with dazzling wings, the dragonflies inhabit
+many different areas on Middle-earth. Among them are giant
+[[[[[Baquatic], [[[[[ygold], [[[[[sblack], [[[[[Ubronze], [[[[[Ggreen], [[[[[wwhite], [[[[[oswamp], [[[[[rred],
+[[[[[gforest], [[[[[bblue], [[[[[ubrown], [[[[[Wsilver], [[[[[vviolet], and [[[[[Rpink] ones -
+all with rudimentary command of some of the elements. The
+[[[[[DDeath dragonflies] are worth watching out for.
+
+ *** Spiders (S) ***
+
+Ugly eight-legged creatures, the spiders tend to travel in
+packs, and are thus to be reckoned with. The weaker species
+include the [[[[[BPhase spiders], [[[[[DCave spiders], [[[[[WGiant spiders], and
+[[[[[UWood spiders] - these are only dangerous to very inexperienced
+adventurers. The [[[[[RGiant fire ticks], uGiant brown ticks], [[[[[oGiant]
+[[[[[otarantulas], [[[[[wGiant white ticks], [[[[[sGiant grey scorpions], [[[[[rGiant]
+[[[[[rred scorpions], and [[[[[yGiant yellow scorpions] are more dangerous
+than their smaller cousins. The most dangerous of all spiders
+are the aquatic [[[[[gMurk dwellers], the [[[[[rAraneas], the [[[[[bDriders], the
+dreadful [[[[[GMirkwood spiders], and the powerful [[[[[vElder araneas].
+
diff --git a/lib/mods/theme/file/book-12.txt b/lib/mods/theme/file/book-12.txt
new file mode 100644
index 00000000..07b5267d
--- /dev/null
+++ b/lib/mods/theme/file/book-12.txt
@@ -0,0 +1,68 @@
+ ------------------------
+ | Monstrous Compendium 4 |
+ | Animals |
+ ------------------------
+
+
+ *** Cattle (c) ***
+
+Mostly domesticated four-legged beasts of burden, these
+animals will not bother you unless you bother them first.
+In fact, [[[[[wSheep], [[[[[uCows], [[[[[sHorses], [[[[[WMearas], [[[[[yPonies], [[[[[DOxen],
+[[[[[rKine of Araw], and [[[[[UDeer] will tend to ignore adventurers
+completely. [[[[[oBoars], however, are a different matter. They
+have strong legs and fierce tusks, which they will use
+against anyone who stumbles into their territory.
+
+ *** Canines (C) ***
+
+Four-legged animals with shaggy fur. Most of them are not
+very dangerous - in fact, the [[[[[sScruffy little dogs] will
+not do much except growl, and [[[[[yJackals] can be deadly against
+very weak adventurers, but will not hold their own against
+even moderately experienced ones. [[[[[oFoxes], [[[[[uWolves], [[[[[wWhite]
+[[[[[wwolves], and [[[[[BBlink dogs] can be a nuisance, but not overmuch.
+The evil [[[[[gWolf chieftains], [[[[[DWerewolves], [[[[[DWargs], [[[[[RHellhounds],
+and [[[[[rGreater Hellhounds], however, are very dangerous.
+
+ *** Felines (f) ***
+
+Lithe, four-legged animals with sleek fur and sharp claws.
+The [[[[[WScrawny cat] lives in the town and tends to walk by
+itself. The [[[[[UWild cats], [[[[[gTree cats], and [[[[[bNight cats] are all
+quite menacing, but only dangerous in packs. The powerful
+[[[[[DPanthers], [[[[[oTigers], [[[[[wSnow tigers], and [[[[[ySabre-tooth tigers]
+are not to be toyed with. The [[[[[uLeopards] and [[[[[RLions] are the
+strongest of all the feline monsters.
+
+ *** Quadrupeds (q) ***
+
+Four-legged creatures of varying breeds, these monsters
+are all quite large and fierce - their bulk alone is
+enough to intimidate you. The weakest are the [[[[[yApes], who
+make a lot of noise but aren't very dangerous. The [[[[[WOld]
+[[[[[Wbears] just want everyone to leave them alone. There are
+many other different kinds of bears, among them the
+[[[[[uCave bears], [[[[[rWar bears], [[[[[UGrizzly bears], [[[[[wPolar bears],
+[[[[[bBlue bears], [[[[[RFire bears], [[[[[BAquatic bears], and [[[[[DWerebears].
+The [[[[[sMumakil] are gigantic elephantine forms, stomping all
+over the dungeon and waving their tusks threateningly. The
+[[[[[GNight mares] are fearsome skeletal horses with glowing eyes.
+The [[[[[gCatoblepas] are strange ox-like forms with huge heads but
+thin, weak necks. The [[[[[oRust monsters] are weird, small animals
+that look perpetually hungry.
+
+ *** Birds (B) ***
+
+There are many different types of birds on Middle-earth,
+and the [[[[[RChaffinches], [[[[[WSparrows], [[[[[BNightingales], [[[[[UThrushes],
+[[[[[WRavens], [[[[[oGulls], [[[[[rEagles], and [[[[[rGreat eagles] will not harm
+anyone who does not harm them first. The [[[[[wSwans] hold a very
+special place with the Valar, and woe be to anyone who
+knowingly harms a Swan. However, not all birds are harmless.
+The [[[[[RKirinki] can wake up entire towns with their shrill cry,
+The [[[[[sCrows] and [[[[[DCrebain] are ever hungry for carrion, as are
+[[[[[GGorcrows]. The [[[[[uHunting hawks] seek prey without regard for
+friend or foe. The [[[[[DWinged Horrors] are not quite birds, they
+are Great Eagles corrupted by Morgoth to serve his ends.
+
diff --git a/lib/mods/theme/file/book-13.txt b/lib/mods/theme/file/book-13.txt
new file mode 100644
index 00000000..faa7071b
--- /dev/null
+++ b/lib/mods/theme/file/book-13.txt
@@ -0,0 +1,54 @@
+ ------------------------
+ | Monstrous Compendium 5 |
+ | Fliers and Crawlers |
+ ------------------------
+
+
+ *** Bats (b) ***
+
+Small flying creatures with webbed wings and sharp teeth.
+The [[[[[ofruit bats] are the weakest of them all. Other kinds
+include [[[[[Utan], [[[[[ubrown], [[[[[wsnow], [[[[[sgrey], [[[[[Wsilver], [[[[[yyellow], [[[[[Ggreen],
+[[[[[rred dragon], [[[[[bblue dragon], and [[[[[vdisenchanter bats]. The most
+dangerous of this race the [[[[[BMongbats], [[[[[dVampire bats], [[[[[gBats]
+[[[[[gof Gorgoroth], and [[[[[RDoombats].
+
+ *** Rodents (R) ***
+
+Small chittering creatures with quick feet and sharp teeth.
+The [[[[[Uwild rabbits] are utterly harmless unless angered, as
+are [[[[[oSquirrels]. The various giant mice ([[[[[wwhite], [[[[[Bsilver], [[[[[rred],
+[[[[[bblue], and [[[[[yyellow]) and rats ([[[[[Wwhite], [[[[[sgrey], [[[[[Rpink], and [[[[[Gtree])
+are somewhat dangerous, as they tend to run in large packs.
+The [[[[[urock moles], [[[[[Dwererats], and [[[[[gsquirrels of Mirkwood] are
+among the more dangerous rodents.
+
+
+ *** Snakes (J) ***
+
+Fast and stealthy creatures that slither along the floor. The
+[[[[[Ueels] are harmless, except when angered. The large [[[[[bblue], [[[[[Wsilver],
+[[[[[vpurple], [[[[[Rred], [[[[[sgrey], [[[[[yyellow], [[[[[ubrown], and [[[[[wwhite] snakes are
+somewhat dangerous. The [[[[[rRattlesnakes], [[[[[oCopperhead] snakes,
+[[[[[DBlack mambas], and [[[[[gKing cobras] are quite dangerous and fierce.
+The aquatic [[[[[BElectric eels] and [[[[[GLimlugs] (sea serpents) are
+among the most dangerous of their kind.
+
+ *** Beetles (K) ***
+
+Very large insects with tough carapaces. Most of them are not
+too dangerous. They come in the following varieties: [[[[[wwhite],
+[[[[[ubrown], [[[[[rred], [[[[[sgray], [[[[[oorange], [[[[[bblue], [[[[[Wsilver], [[[[[Ggreen], [[[[[Utree],
+[[[[[Rfire], [[[[[gstag], [[[[[Baquatic], [[[[[yslicer], [[[[[viridescent], and [[[[[Ddeath watch].
+
+
+ *** Reptiles (R) ***
+
+The majority of reptiles found on Middle-earth are not very
+dangerous - these include the [[[[[ynewts], [[[[[ucave lizards], [[[[[Urock lizards],
+[[[[[bnight lizards], [[[[[Bblue lizards], [[[[[oswamp lizards], [[[[[rgiant pink frogs],
+[[[[[ggiant green frogs], [[[[[wsnow-frogs], and [[[[[Wgiant silver frogs]. The
+[[[[[Rgiant salamanders] and [[[[[Ggiant turtles] should be approached
+with caution, while the [[[[[sbasilisks] and [[[[[Dgreater basilisks] are
+extremely dangerous and should not be toyed with!
+
diff --git a/lib/mods/theme/file/book-14.txt b/lib/mods/theme/file/book-14.txt
new file mode 100644
index 00000000..eb79aa38
--- /dev/null
+++ b/lib/mods/theme/file/book-14.txt
@@ -0,0 +1,55 @@
+ ------------------------
+ | Monstrous Compendium 6 |
+ | Morgoth's minions |
+ ------------------------
+
+ *** Orcs (o) ***
+
+Squat, swarthy creatures rumoured to have been Elves once.
+The [[[[[Usnotlings] and [[[[[osnaga] are the lowest form of Orc, weak
+but dangerous in packs. There are different breeds of Orc,
+depending on their dwelling-place: [[[[[uHill orcs], [[[[[GCave orcs],
+and [[[[[Dblack orcs]. The [[[[[sHalf-orcs] are a cross-breed of Orcs
+and humans. The [[[[[BUruk-hai] are a stronger breed, they can
+bear the light of the sun. The [[[[[wElite] uruks are the most
+dangerous kind of Orc there is.
+
+ *** Ogres (O) ***
+
+Monstrous and destructive creatures of legend and folklore.
+The [[[[[wOgrillon] is a cross-breed of an Orc and an Ogre, and
+the [[[[[oHalf-ogre] is a cross between an Ogre and a human. The
+garden-variety [[[[[UOgres] are more common than their [[[[[gForest]-
+dwelling, [[[[[sMountain]-dwelling, and [[[[[uCave]-dwelling cousins.
+The [[[[[DBlack ogres] shy away from the sun, the [[[[[BMerrows] live
+in or near water. There are some [[[[[yRebel ogres] who have
+turned against their plundering fellows and will try to aid
+adventurers.
+
+ *** Giants (P) ***
+
+Enormous humanoids with powerful muscles, aligned with evil.
+There are [[[[[WStone giants], [[[[[BStorm giants], [[[[[rFire giants], [[[[[wFrost]
+[[[[[wgiants], and [[[[[UHill giants] - while very large and dangerous,
+they pale in comparison to the much stronger [[[[[uCyclops], [[[[[bCloud]
+[[[[[bgiants], and the terrifying [[[[[sRock giants]. The [[[[[yLesser titans]
+and [[[[[oGreater titans] are in a class of their own, their power
+and magical abilities unmatched by any. The [[[[[RRebel giants] are
+those who have broken with the forces of Darkness.
+
+ *** Trolls (T) ***
+
+Lumbering evil creatures originated in mockery of the Ents.
+The [[[[[UHalf-trolls] are bizarre crosses between trolls and Men.
+The [[[[[uCave-trolls], [[[[[wSnow-trolls], [[[[[sHill trolls], [[[[[gForest trolls],
+[[[[[BWater trolls], and [[[[[WStone trolls] are dangerous and usually
+move in large groups. Even more dangerous are the [[[[[oAlgroths],
+the [[[[[yOlog-hai], and the [[[[[BScrags]. The [[[[[RWar trolls], [[[[[rEldraks], and
+[[[[[bEttins] can only be described as killing machines.
+
+ *** Yeeks (y) ***
+
+Small humanoid figures whose origin lies outside Middle-earth.
+There are [[[[[Bblue], [[[[[ubrown], [[[[[Ddark], [[[[[wwhite], [[[[[sgray], and [[[[[yyellow]
+Yeeks. The [[[[[gmaster yeeks] are somewhat proficient at magic, and
+[[[[[Uadventurer yeeks] will try to help you. \ No newline at end of file
diff --git a/lib/mods/theme/file/book-15.txt b/lib/mods/theme/file/book-15.txt
new file mode 100644
index 00000000..21983f48
--- /dev/null
+++ b/lib/mods/theme/file/book-15.txt
@@ -0,0 +1,68 @@
+ ------------------------
+ | Monstrous Compendium 7 |
+ | Dragons and Worms |
+ ------------------------
+
+ *** Worms (w) ***
+
+Large slimy masses of worms crawl all around dungeons.
+There are [[[[[Bclear], [[[[[rred], [[[[[bblue], [[[[[ggreen], [[[[[wwhite], [[[[[yyellow],
+[[[[[Dnether], and [[[[[vdisenchanter] worm masses. Additionally,
+there are [[[[[Ugiant slugs] and [[[[[ugiant leeches] which are more
+dangerous than the worm masses. The [[[[[vpurple worms] and
+[[[[[ysandworms] are even more dangerous, and then there are
+the [[[[[uwereworms], huge wormlike shapes dripping acid.
+
+ *** Dragon worms (w) ***
+
+These will eventually grow into dragons, unless killed.
+There are [[[[[Rred], [[[[[Bblue], [[[[[Ggreen], [[[[[Wwhite], [[[[[sblack], [[[[[ygold],
+[[[[[ubronze], and [[[[[vmulti-hued ones].
+
+ *** Hatchling dragons (d) ***
+
+The [[[[[opseudo-dragon] is a small relative of the dragon that
+inhabits dark caves. The hatchling dragons are newly-born,
+still soft, with eyes unaccustomed to light and scales
+shimmering with a hint of colour: [[[[[rred], [[[[[bblue], [[[[[ggreen],
+[[[[[wwhite], [[[[[sblack], [[[[[Ubronze], [[[[[ygold], and [[[[[vmulti-hued]. There are
+also the aquatic [[[[[WHatchling dragon turtles].
+
+
+ *** Young dragons (d) ***
+
+The young dragons sport still-tender scales, coloured more
+brightly than those of the Hatchling dragons: [[[[[rred], [[[[[bblue],
+[[[[[ggreen], [[[[[wwhite], [[[[[sblack], [[[[[Ubronze], [[[[[ygold], and [[[[[vmulti-hued].
+The [[[[[Wyoung dragon turtles] live in or near water.
+
+ *** Drakes (d) ***
+
+These dragons are already mature, and their scales are very
+brightly covered, according to the elements each dragon
+draws its power from. There are [[[[[rFire-drakes], [[[[[bBlue drakes],
+[[[[[gGreen drakes], [[[[[wCold-drakes], [[[[[sBlack drakes], [[[[[UBronze drakes],
+[[[[[yGolden drakes], [[[[[vMulti-hued drakes], [[[[[vBalance drakes], [[[[[vLaw]
+[[[[[vdrakes], [[[[[vChaos drakes], [[[[[uCrystal drakes], [[[[[oEthereal drakes],
+[[[[[GShadow drakes], and [[[[[sMature dragon turtles].
+
+
+ *** Ancient dragons (D) ***
+
+These dragons are very old, cunning, and powerful. They are
+considerably larger than the drakes, and they too draw their
+colouring from their elements: [[[[[rred], [[[[[bblue], [[[[[ggreen], [[[[[wwhite],
+[[[[[sblack], [[[[[Ubronze], [[[[[ygold], and [[[[[vmulti-hued]. In addition, there
+are the [[[[[GDeath drakes], [[[[[UGreat crystal drakes], [[[[[oAncient ethereal]
+[[[[[odrakes], [[[[[BSky drakes], and [[[[[WAncient dragon turtles].
+
+ *** Greater dragons (D) ***
+
+The mightiest and most powerful of dragonkind, these beasts
+are older than time itself. Their very names instill fear into
+your very soul: the [[[[[rGreat Worm of Fire], the [[[[[bGreat Storm Worm],
+the [[[[[gGreat Swamp Worm], the [[[[[wGreat Ice Worm], the [[[[[sGreat Bile Worm],
+the [[[[[UGreat Worm of Perplexity], the [[[[[yGreat Worm of Thunder],
+the [[[[[vGreat Worm of Many Colours], the [[[[[GFastitocalon], the [[[[[vGreat]
+[[[[[vWorm of Balance], the [[[[[RDracolisk], the [[[[[vGreat Worm of Chaos], the
+[[[[[GDracolich], the [[[[[BGreat Worm of Law], and the [[[[[vGreat Wyrm of Power].
diff --git a/lib/mods/theme/file/book-16.txt b/lib/mods/theme/file/book-16.txt
new file mode 100644
index 00000000..e19b7b0b
--- /dev/null
+++ b/lib/mods/theme/file/book-16.txt
@@ -0,0 +1,43 @@
+ -------------------------
+ | Monstrous Compendium 8 |
+ | Depth dwellers, hybrids |
+ -------------------------
+
+ *** Nagas (n) ***
+
+Giant snake-like figures with a woman's torso. There are
+[[[[[ggreen], [[[[[Rred], [[[[[Dblack], [[[[[Bwater], [[[[[oswamp], [[[[[ubrown], [[[[[Wsilver],
+[[[[[vnight], [[[[[Gtree], [[[[[sdark], [[[[[bocean], [[[[[yguardian], and [[[[[wspirit nagas].
+The [[[[[Uadventurer nagas] are friendly to their fellows.
+
+ *** Hydras (M) ***
+
+Strange reptilian creatures with several heads, guarding
+treasure hoards. There are [[[[[u2-headed], [[[[[o3-headed], [[[[[y4-headed],
+[[[[[g5-headed], [[[[[w6-headed], [[[[[G7-headed], [[[[[s8-headed], [[[[[r9-headed],
+[[[[[b10-headed], [[[[[R11-headed], [[[[[W12-headed], [[[[[v13-headed], [[[[[B14-headed],
+[[[[[U15-headed], as well as [[[[[DKiller hydras].
+
+ *** Hybrids (H) ***
+
+These creatures are all crosses between two (or sometimes
+more) different beings. The [[[[[wWhite harpies], [[[[[DBlack harpies],
+and [[[[[oOwlbears] are not very dangerous. The [[[[[UHippogryphs],
+[[[[[uGriffons], [[[[[RGorgimaerae], [[[[[BBehemoths], [[[[[rChimaerae], [[[[[yManticores],
+and [[[[[GSphinxes] are more dangerous and deadly. The [[[[[bGorgons],
+[[[[[WHeadless], [[[[[gSwamp things], and [[[[[vJabberwocks] are the deadliest.
+
+ *** Depth dwellers (~) ***
+
+These creatures inhabit the oceans, lakes, and seas of Arda.
+Along with the [[[[[BSand mites], [[[[[BBox jellyfish], [[[[[GBarracudas], and
+[[[[[RPiranhas] there are [[[[[yStargazers], [[[[[gOctopi], [[[[[WGlobefish], [[[[[sPikes],
+[[[[[sSwordfish], and [[[[[sFlounders]. More dangerous are [[[[[rGiant crayfish],
+[[[[[RGiant piranhas], and [[[[[gGiant squid]. Among the larger depth-
+dwellers, there are the [[[[[WHammerhead sharks], [[[[[oTiger sharks],
+[[[[[wWhite sharks], [[[[[oSeahorses], and [[[[[gGiant octopi]. The largest and
+more dangerous creatures include the [[[[[DWhales], [[[[[UElder stargazers],
+[[[[[wGreat white sharks], [[[[[uUndead stargazers], [[[[[GLesser Krakens],
+[[[[[wKiller whales], and [[[[[GGreater Krakens]. The most dangerous ocean
+dweller is the [[[[[vLeviathan].
+
diff --git a/lib/mods/theme/file/book-17.txt b/lib/mods/theme/file/book-17.txt
new file mode 100644
index 00000000..13aa439f
--- /dev/null
+++ b/lib/mods/theme/file/book-17.txt
@@ -0,0 +1,47 @@
+ ------------------------
+ | Monstrous Compendium 9 |
+ | Undead Creatures |
+ ------------------------
+
+ *** Mewlips (i) ***
+
+Mewlips are evil cannibal spirits from the marshlands.
+They come in different varieties: [[[[[wclear], [[[[[sgray], [[[[[oorange],
+[[[[[rbloodshot], [[[[[ggreen], [[[[[bblue], [[[[[ubrown], [[[[[Wstone], [[[[[yyellow], [[[[[Rpink],
+[[[[[Gtree], [[[[[Bair], [[[[[Uplague], and [[[[[Ddeath].
+
+
+ *** Golems (g) ***
+
+Massive animated statues made from different materials.
+There are [[[[[obronze], [[[[[wbone], [[[[[ueog], [[[[[Bmithril], [[[[[siron], [[[[[baquatic],
+[[[[[Wstone], [[[[[Uclay], [[[[[Rflesh], and [[[[[ffire golems], in addition to the
+more cunning [[[[[ycolbrans], [[[[[DPukelmen], [[[[[gdrolems], [[[[[Gcolossus], and
+[[[[[ssilent watchers].
+
+ *** Skeletons (s) ***
+
+There are skeletal forms of just about any monster that once
+inhabited Middle-earth. Some such forms, however, have never
+been alive in the first place - they are horrible abominations
+animated by powerful wizards. Among them are the [[[[[wice skeletons],
+[[[[[sflying skulls], [[[[[Dcrypt creeps], [[[[[yhand druj], [[[[[oskull druj], and
+[[[[[reye druj].
+
+ *** Zombies (z) ***
+
+Zombie forms of all living creatures may appear in the dungeons.
+However, some zombie-like undead from before the First Age do
+exist: [[[[[Ughouls], [[[[[ygreater mummies], [[[[[Dghoulkings], [[[[[ughasts], and
+[[[[[Rrotting corpses].
+
+ *** Vampires (V) ***
+
+There are vampire forms of just about any race - [[[[[Whuman], [[[[[Gelf], [[[[[Dorc],
+[[[[[wyeek], [[[[[oogre], [[[[[utroll], [[[[[Rdwarf], and [[[[[Bgnome]. The [[[[[sOriental vampires]
+come from beyond the land of Rhun. There are [[[[[bVampire lords], [[[[[rElder]
+[[[[[rvampires], and [[[[[gMaster vampires]. The [[[[[UAdventurer vampires] are those
+who have denounced the way of the Dark and attempt to follow a path
+of Light as best they can.
+
+
diff --git a/lib/mods/theme/file/book-18.txt b/lib/mods/theme/file/book-18.txt
new file mode 100644
index 00000000..36cbee04
--- /dev/null
+++ b/lib/mods/theme/file/book-18.txt
@@ -0,0 +1,55 @@
+ --------------------------
+ | Monstrous Compendium 10 |
+ | Demons and other Horrors |
+ --------------------------
+
+ *** Minor Demons (u) ***
+
+These demons (roeg) are corrupted forms of natural creatures.
+The individual types of demons are as follows: [[[[[bLimrog] (fish),
+[[[[[UCaborrog] (frog), [[[[[DLygrog] (snake), [[[[[oDraugrog] (wolf), [[[[[rHurog] (dog),
+[[[[[sSarnrog] (stone), [[[[[gNarrog] (rat), [[[[[yAewrog] (bird), [[[[[BRawrog] (lion),
+and [[[[[RAdanrog] (human).
+
+ *** Major Demons (u) ***
+
+The major demons are of two kinds - the spider demons, spawn of
+Ungoliant, and the Balroeg, corrupted Maiar. The spider demons
+are as follows: [[[[[gUngorrog] (power spider demon), [[[[[WHelcungol] (ice),
+[[[[[rNaurungol] (fire), [[[[[sMornungol] (black), and [[[[[BFaunungol] (cloud).
+There are also the [[[[[BGaurroeg] (water demons), [[[[[oMorgulroeg] (magic
+demons), [[[[[uDagorrog] (war demons), [[[[[ySererrog] (blood demons), and
+the [[[[[UMenelroeg] (sky demons). The Balroeg can be [[[[[RRed], [[[[[DBlack],
+[[[[[wWhite], and [[[[[bBlue] - commanded by [[[[[GBalrog Captains] and [[[[[vGreater]
+[[[[[vBalroeg].
+
+ *** Greater Undead (G) ***
+
+The ghosts are incorporeal beings with awesome magical powers.
+Some of them aren't very impressive, like the [[[[[gGreen glutton]
+[[[[[gghost] and the [[[[[sPoltergeist]. Others are deadlier - [[[[[wGhosts],
+[[[[[BPhantom beasts], [[[[[BDrowned souls], [[[[[BPhantom warriors], the [[[[[DShadows],
+[[[[[DPossessors], [[[[[DShades], and [[[[[USpectres]. The friendly [[[[[WSpirits] will
+try to help adventurers. The [[[[[bBanshee], [[[[[GSpirit troll], [[[[[vPhantom],
+[[[[[uHeadless ghost], and [[[[[uMoaning spirit] are all to be reckoned with.
+In a class of their own are the [[[[[oDreads], [[[[[rDreadlords], and the
+[[[[[yDreadmasters].
+
+ *** Liches (L) ***
+
+Yet another kind of powerful undead. Many things can take the form
+of a lich, but the [[[[[oLich], [[[[[GCrypt thing], [[[[[WIron lich], [[[[[rMaster lich],
+[[[[[uMonastic lich], [[[[[UDemilich], [[[[[BArchlich], [[[[[DBlack reaver], and [[[[[sLesser]
+[[[[[sblack reaver] all deserve special mention.
+
+
+ *** Wraiths (W) ***
+
+Wraiths are incorporeal beings frequently found in graveyards and
+at the sites of unavenged murders. There are [[[[[wWhite wraiths], [[[[[sGrey]
+[[[[[swraiths], [[[[[WSilver wraiths], [[[[[GNether wraiths], and [[[[[DBlack wraiths].
+The [[[[[UAdventurer wraiths] will try to help you. Fierce and magical
+are the many wights: [[[[[BBarrow wights], [[[[[gForest wights], [[[[[bGrave wights],
+[[[[[oSwamp wights], and [[[[[rEmperor wights]. The most dangerous among the
+wraith beings are the [[[[[uRevenant], [[[[[DNightwalker], [[[[[DNightcrawler], and
+[[[[[DNightwing]. \ No newline at end of file
diff --git a/lib/mods/theme/file/book-19.txt b/lib/mods/theme/file/book-19.txt
new file mode 100644
index 00000000..a36285f0
--- /dev/null
+++ b/lib/mods/theme/file/book-19.txt
@@ -0,0 +1,47 @@
+ -------------------------
+ | Monstrous Compendium 11 |
+ | Elemental creatures |
+ -------------------------
+
+ *** Vortices (v) ***
+
+These strange creatures are like localised tornadoes,
+carrying with them the various elements: [[[[[rfire], [[[[[benergy],
+[[[[[ggas], [[[[[wcold], [[[[[sacid], [[[[[ushards], [[[[[vnexus], [[[[[ymana], [[[[[Uconfusion],
+[[[[[Wslowness], [[[[[vchaos], [[[[[Gnether], [[[[[Rplasma], and [[[[[Btime]. Among
+these, the nastiest by far are the [[[[[vstorms of unmagic],
+as well as [[[[[oshimmering], [[[[[Ddeath], and [[[[[vaether] vortices.
+
+ *** Spirits (E) ***
+
+These are the spirits contained in various elements, they
+are weaker than the proper elementals. There are [[[[[rFire]
+[[[[[rspirits], [[[[[bWater spirits], [[[[[uEarth spirits], and [[[[[BAir spirits]
+alongside [[[[[WWill'o the wisps] and [[[[[yInvisible stalkers].
+
+ *** Elementals (E) ***
+
+Towering masses of raw elements, twisting and shaking all
+in their wake: [[[[[rfire], [[[[[bwater], [[[[[uearth], [[[[[Bair], [[[[[wice], [[[[[omagma],
+[[[[[Uconfusion], [[[[[Ddark], [[[[[Rsmoke], [[[[[gooze], [[[[[sslowness], [[[[[Gtime], and
+[[[[[vchaos].
+
+ *** Hulking figures (X, Y) ***
+
+These are strange hulking shapes that puzzle anyone who
+comes across them. They include the [[[[[sXaren], the [[[[[uXorn],
+[[[[[UUmber hulk], [[[[[DDeath hulk], [[[[[wWhite hulk], [[[[[oOrange hulk], [[[[[rFire]
+[[[[[rhulk], [[[[[gForest hulk], [[[[[bNight hulk], [[[[[WSilver hulk], [[[[[vChaos hulk],
+[[[[[yYellow hulk], [[[[[RRed hulk], [[[[[GGreen hulk], and [[[[[Bblue hulk]. The
+[[[[[sSasquatch] and [[[[[wYeti] resemble humans, but they are still
+quite strange.
+
+ *** Zephyr Hounds (Z) ***
+
+They are dog-like figures wreathed in the elements. They
+tend to move in packs, and are among the most vicious of
+all dungeon dwellers. The hounds may be: [[[[[Bclear], [[[[[rfire],
+[[[[[benergy], [[[[[gair], [[[[[wcold], [[[[[sacid], [[[[[vmulti-hued], [[[[[Ddark], [[[[[olight],
+[[[[[uearth], [[[[[Baquatic], [[[[[vnexus], [[[[[rwater], [[[[[Rplasma], [[[[[yvibration],
+[[[[[vchaos], [[[[[Gnether], [[[[[uimpact], [[[[[Winertia], [[[[[Ugravity], [[[[[Gethereal],
+[[[[[Btime], and [[[[[vaether]. \ No newline at end of file
diff --git a/lib/mods/theme/file/book-2.txt b/lib/mods/theme/file/book-2.txt
new file mode 100644
index 00000000..bfadd6ce
--- /dev/null
+++ b/lib/mods/theme/file/book-2.txt
@@ -0,0 +1,90 @@
+
+ Ted Sandyman's Important Compendium 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/mods/theme/file/book-20.txt b/lib/mods/theme/file/book-20.txt
new file mode 100644
index 00000000..21418fe9
--- /dev/null
+++ b/lib/mods/theme/file/book-20.txt
@@ -0,0 +1,192 @@
+#####R /----------------------------------------\
+#####R < Adventurer's guide to the Middle-earth >
+#####R \----------------------------------------/
+
+Summary:
+*****/abook-20.txt*1[(a) The main towns and dungeons]
+*****/bbook-20.txt*2[(b) Other towns]
+*****/cbook-20.txt*3[(c) Other strange and frightening places]
+*****/dbook-20.txt*4[(d) Equipment issues]
+*****/ebook-20.txt*5[(e) Macros]
+
+#####GIntroduction:
+
+Middle-earth is vast and mysterious, full of dangers and rewards for
+the brave adventurer.
+
+New adventurers should know that pressing < and > can switch
+the wilderness view between a normal scale and a larger map. This map
+makes travelling faster, but you cannot enter wilderness dungeons from
+it, and chances of being ambushed are high. The wariest of adventurers
+will choose to take the well-worn roads when such roads are available,
+and grass fields when there are no roads. Beware of Belegaer and the
+Dead Marshes!
+
+
+~~~~~1
+#####G(a) The main towns and dungeons
+
+You start in a small village named Bree in the western part of 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.
+Lothlorien is instantly recognisable by its Mallorn trees. The chief city of this
+land and the abode of Galadriel and Celebron is Caras Galadhon. 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 may want to pay a visit
+to the land of Mordor, which is east of Minas Anor. You cannot pass through
+the Black Gate of Mordor without a means of climbing mountains, for the
+gate is guarded heavily, night and day. The only ways into Mordor are through
+the dreaded Spider Pass (Cirith Ungol), or the circuitous route through the
+valley of Nurnen and the horrid plains of Gorgoroth.
+
+After Mordor you should finally travel to Gondolin, the hidden town of the
+Noldor. From this city, you will be able to attack Angband, the
+dungeon of Morgoth, which is north-east of Gondolin. This city is well-
+protected by what remains of the Echoriath, and there is but one route
+into it that does not force you to travel by the sea. To be sure of finding
+Gondolin, travel north along the Blue Mountains, then take the north-eastern
+route from the second northernmost peak.
+
+
+~~~~~2
+#####G(b) Other towns
+
+To the west of Bree, in the middle of a land called the Shire, there is a
+small village called Hobbiton, inhabited by Halflings. Nested in some low
+hills, it isn't hard to find, and their hospitality is legendary.
+
+To the east of Bree, there is Rivendell - the hidden valley of the Elves,
+where Elrond lives in the Last Homely House (Imladris). Beyond the Misty
+Mountains, near the entrance to Mirkwood Forest, there is the house of
+Beorn.
+
+When you are in Lothlorien, be sure to pay your respects to the Valar at
+Cerin Amroth, which is immediately to the north of Caras Galadhon.
+
+Edoras, the capital of Rohan (home of the horse-lords, the Rohirrim) can be
+found to the south of Caras Galadhon. It is a well-protected fortress, second
+only to the fortress of Helm's Deep, which stands watch against Isengard.
+
+There are two cities near Minas Anor - Pelargir (the great city at the
+mouth of the Anduin River) and Osgiliath, the citadel of Stars. Pelargir is
+to the south-west of Minas Anor, and Osgiliath is to the north-east of
+Gondor's capital. Rumour has it that there is a Ranger outpost somewhere in
+Ithilien, immediately to the north of Osgiliath.
+
+If you travel north towards the Lonely Mountain, you will come upon
+Esgaroth, the city of Lake-men. Near Esgaroth is the construction site for
+Dale, and if you follow the river, you may find yourself a guest in the
+Halls of Thranduil, King of the Wood-Elves.
+
+Deep in the heart of the Misty Mountains, in a heavily guarded area that
+cannot be access but through the Mines of Moria, lies Khazad-dum, the
+greatest city ever built by Dwarves.
+
+~~~~~3
+#####G(c) Other strange and frightening places
+
+The Old Forest to the west of Bree (and east of Hobbiton) is what remains
+of the big forests of the First Age, but it has been corrupted. It is said
+that it is guarded by a living tree.
+
+You may also wish to investigate the Orc Caves east of Bree; they are
+another place suitable for those finished with the Barrow-Downs. They also
+are rumoured to hold great mysteries - these are the same caves where Bilbo
+had once found the One Ring of power.
+
+Durin's Bane, the Balrog of Moria, guards the Mines of Moria, to the south-east
+of Bree. The Mines are also the only means of access to the Dwarven
+stronghold of Khazad-dum.
+
+Nearly at the edge of land, there is a magical Maze. Many adventurers
+that ventured there never came back. It is rumoured that a Minotaur is
+lurking down there, guarding an ancient and powerful artifact.
+Bring along digging equipment and some means to recall.
+
+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. Any adventurer going
+there should be sure to bring equipment that enables underwater
+breathing, however - without it, you shall perish.
+
+Many other strange places wait to be explored by the valiant adventurer,
+but their locations are secret. You will have to find them yourself!
+Among them are the Grinding Ice of the Helcaraxe, Forodwaith, the caves
+beneath Isengard, the land of Rhun, and many others.
+
+
+~~~~~4
+#####G(d) 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.
+
+~~~~~5
+#####G(e) 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/mods/theme/file/book-200.txt b/lib/mods/theme/file/book-200.txt
new file mode 100644
index 00000000..75577e1a
--- /dev/null
+++ b/lib/mods/theme/file/book-200.txt
@@ -0,0 +1,5 @@
+4
+35
+21
+6
+0
diff --git a/lib/mods/theme/file/book-201.txt b/lib/mods/theme/file/book-201.txt
new file mode 100644
index 00000000..fdcae8aa
--- /dev/null
+++ b/lib/mods/theme/file/book-201.txt
@@ -0,0 +1,5 @@
+4
+3
+11
+8
+0
diff --git a/lib/mods/theme/file/book-202.txt b/lib/mods/theme/file/book-202.txt
new file mode 100644
index 00000000..3c61caee
--- /dev/null
+++ b/lib/mods/theme/file/book-202.txt
@@ -0,0 +1,5 @@
+4
+50
+34
+4
+0
diff --git a/lib/mods/theme/file/book-203.txt b/lib/mods/theme/file/book-203.txt
new file mode 100644
index 00000000..c169a628
--- /dev/null
+++ b/lib/mods/theme/file/book-203.txt
@@ -0,0 +1,5 @@
+4
+60
+56
+3
+0
diff --git a/lib/mods/theme/file/book-204.txt b/lib/mods/theme/file/book-204.txt
new file mode 100644
index 00000000..37b88559
--- /dev/null
+++ b/lib/mods/theme/file/book-204.txt
@@ -0,0 +1,5 @@
+4
+1
+1
+99
+64
diff --git a/lib/mods/theme/file/book-205.txt b/lib/mods/theme/file/book-205.txt
new file mode 100644
index 00000000..fecab40c
--- /dev/null
+++ b/lib/mods/theme/file/book-205.txt
@@ -0,0 +1,5 @@
+4
+51
+50
+3
+0
diff --git a/lib/mods/theme/file/book-206.txt b/lib/mods/theme/file/book-206.txt
new file mode 100644
index 00000000..ed1f1c17
--- /dev/null
+++ b/lib/mods/theme/file/book-206.txt
@@ -0,0 +1,5 @@
+4
+66
+18
+1
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-207.txt b/lib/mods/theme/file/book-207.txt
new file mode 100644
index 00000000..0a9fb804
--- /dev/null
+++ b/lib/mods/theme/file/book-207.txt
@@ -0,0 +1,5 @@
+4
+25
+21
+3
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-208.txt b/lib/mods/theme/file/book-208.txt
new file mode 100644
index 00000000..1a853553
--- /dev/null
+++ b/lib/mods/theme/file/book-208.txt
@@ -0,0 +1,5 @@
+4
+63
+53
+1
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-209.txt b/lib/mods/theme/file/book-209.txt
new file mode 100644
index 00000000..040cf93c
--- /dev/null
+++ b/lib/mods/theme/file/book-209.txt
@@ -0,0 +1,5 @@
+4
+57
+62
+3
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-210.txt b/lib/mods/theme/file/book-210.txt
new file mode 100644
index 00000000..7c1f25d5
--- /dev/null
+++ b/lib/mods/theme/file/book-210.txt
@@ -0,0 +1,5 @@
+4
+55
+19
+1
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-211.txt b/lib/mods/theme/file/book-211.txt
new file mode 100644
index 00000000..3806768d
--- /dev/null
+++ b/lib/mods/theme/file/book-211.txt
@@ -0,0 +1,5 @@
+4
+67
+16
+1
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-212.txt b/lib/mods/theme/file/book-212.txt
new file mode 100644
index 00000000..c2891872
--- /dev/null
+++ b/lib/mods/theme/file/book-212.txt
@@ -0,0 +1,5 @@
+4
+63
+51
+1
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-213.txt b/lib/mods/theme/file/book-213.txt
new file mode 100644
index 00000000..0716c26e
--- /dev/null
+++ b/lib/mods/theme/file/book-213.txt
@@ -0,0 +1,5 @@
+4
+45
+46
+3
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-214.txt b/lib/mods/theme/file/book-214.txt
new file mode 100644
index 00000000..d99dd3d8
--- /dev/null
+++ b/lib/mods/theme/file/book-214.txt
@@ -0,0 +1,5 @@
+4
+61
+16
+3
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-215.txt b/lib/mods/theme/file/book-215.txt
new file mode 100644
index 00000000..fc5b3ed3
--- /dev/null
+++ b/lib/mods/theme/file/book-215.txt
@@ -0,0 +1,5 @@
+4
+48
+21
+1
+0 \ No newline at end of file
diff --git a/lib/mods/theme/file/book-216.txt b/lib/mods/theme/file/book-216.txt
new file mode 100644
index 00000000..4acc56af
--- /dev/null
+++ b/lib/mods/theme/file/book-216.txt
@@ -0,0 +1,5 @@
+4
+1
+1
+99
+6
diff --git a/lib/mods/theme/file/book-22.txt b/lib/mods/theme/file/book-22.txt
new file mode 100644
index 00000000..99dd4e90
--- /dev/null
+++ b/lib/mods/theme/file/book-22.txt
@@ -0,0 +1,56 @@
+ -----------------------
+ | Artifact Lore Vol. IV |
+ | Axes |
+ -----------------------
+
+The Great Axe of Durin
+ The twin massive axe heads of this ancient demon's dread gleam with
+ mithril inlay, which tell sagas of endurance, invoking the powers of
+ Khazad-dum to protect the wearer and slay all evils found underground.
+
+The Great Axe of Eonwe
+ The axe of Eonwe, leader of the Hosts of the West before the gates of
+ Thangorodrim, strikes with icy wrath at the undead, disperses hosts of
+ evil at a word, and grants Maia-like powers of body and mind.
+
+The Broad Axe 'Barukkheled'
+ A royal heirloom of the southern coast, strong in combat against evil
+ creatures of the earth.
+
+The Bearded Axe of Gimli
+ "Gimli sensed the Dead behind following, but he continued on after
+ Aragorn." The trusty axe of Gimli son of Gloin, one of the Nine
+ Walkers of old.
+
+The Light War Axe 'Cam-tal-crist'
+ The Petty-dwarves of Bathak forged this blade, and it shares their thirst
+ for blood.
+
+The Broad Axe 'Orchast'
+ Forged by the dwarves of Khazad-dum in a time of desperation, this axe
+ turned many a battle against the invading orcs.
+
+The Hatchet of the Night
+ Found on an unmarked grave after a violent storm, this hatchet has a
+ sinister aura of darkness and decay.
+
+The Slaughter Axe 'Lavandagnir'
+ Used by the orcs in their battle at Dagor Bragollach against the elves,
+ this axe has a bloodthirst for nature.
+
+The Light War Axe of Helcar
+ Crafted of purest ice and held solid by powerful spells, this icy axe
+ delivers a chill of death to its victims.
+
+The Slaughter Axe 'Dramborleg'
+ The great axe of Tuor, Thudder-Sharp is its name. The axe that smote both
+ a heavy dint as of a club and cleft as a sword. When it was swung by the
+ hands of Tuor, it sang like the rush of eagle's wings in the air and took
+ death as it fell. Its name alone instills fear in Balrogs and other
+ corruptions of Morgoth.
+
+The Slaughter Axe 'Garachoth'
+ A ghastly axe with the soul of a demon lord trapped inside, this horrifying
+ creation reverberates with the screams of the damned. As you gaze into its
+ glassy, translucent blade, it seems that endless sulphurous wastelands
+ stretch away from you into the distance, obscured, by sheets of fire.
diff --git a/lib/mods/theme/file/book-23.txt b/lib/mods/theme/file/book-23.txt
new file mode 100644
index 00000000..5d5c22f9
--- /dev/null
+++ b/lib/mods/theme/file/book-23.txt
@@ -0,0 +1,81 @@
+ ----------------------
+ | Artifact Lore Vol. V |
+ | Missile Weapons |
+ ----------------------
+
+The Long Bow 'Belthronding'
+ The great bow of Beleg, made of black yew and strung with elven hair that
+ faintly shines a pale clear gold.
+
+The Long Bow of Bard
+ The great yew bow of grim-faced Bard, who shot the mightiest arrow that
+ songs record.
+
+The Light Crossbow 'Cubragol'
+ A crossbow that grants fiery speed to he who finds it, and from which
+ shoot bolts that blaze with flame unquenchable.
+
+The Sling of the Thain
+ This sling was crafted by Faramir I, Thain of the Shire, just in case the
+ nasties of his father's stories ever dare to enter the Shire again.
+
+The Sling of Farmer Maggot
+ This ordinary seeming leather sling has been raised to legendary status
+ amongst generations of hobbit children. Farmer Maggot's ability to notice
+ and strike any mushroom thief anywhere within his patch almost keeps young
+ poachers at bay, but once they get within range they soon flee for less
+ painful pastures, frequently with rounded pebbles stinging their
+ backsides...
+
+The Heavy Crossbow of Umbar
+ A great brazen arbalest with arms of gleaming steel, shooting quarrels
+ with speed and power for those brave enough to risk betrayal.
+
+The Short Bow of Amrod
+ This bow, and its twin, belonged to Feanor's last two twin sons, Amrod and
+ Amras, who both hunted with the Green-elves for a time. Like the twins,
+ the bows are similar, for both protect their wielders from the elementsand
+ strength, while the other gives quickness and subtlety.
+
+The Short Bow of Amras
+ This bow, and its twin, belonged to Feanor's last two twin sons, Amrod and
+ Amras, who both hunted with the Green-elves for a time. Like the twins,
+ the bows are similar, for both protect their wielders from the elementsand
+ subtlety, while the other gives endurance and strength.
+
+The Light Crossbow of Brand
+ The bow of Brand, last King of Dale. It was given to him as a gift by the
+ King under the Mountain of Erebor, and has access to an especially secret
+ realm of Dwarven lore.
+
+The Seeker Arrow of Bard
+ Deadliest of arrows, imbued with elemental strength, this shaft is feared
+ especially by the wyrmkin.
+
+The Seeker Arrow of Gondor
+ An arrow that was created to rid the world of demons.
+
+The Seeker Bolt of Feanor
+ Made during the war against Morgoth by Feanor, this powerful bolt is the
+ bane of Morgoth's power, and has especial strength against those foes who
+ are already dead.
+
+The Silver Bolt 'Dailir'
+ The beloved dart of Beleg Cuthalion. It never failed to be found unharmed,
+ until it broke when Beleg fell upon it while he was carrying Turin
+ Turambar away from an Orc-camp, the night Beleg met his end. Turin remade
+ the bolt and kept it to his dying day in memory of his friend.
+
+The Metal Boomerang of Beor
+ Beor's boomerang makes its wielder as agile as the winds, and as hard to
+ harm.
+
+The Metal Boomerang 'Glimdrir'
+ A powerful boomerang that makes one agile and fast, with a thirst for evil
+ and undead creatures, but demands its wielder not teleport, for fear of
+ desertion.
+
+The Long Bow of Legolas
+ The great bow of Legolas, one of the Nine Walkers of old. Handcrafted specially
+ for Thranduil's son in Lothlorien, this bow gives clarity of sight and agility
+ to the wielder.
diff --git a/lib/mods/theme/file/book-24.txt b/lib/mods/theme/file/book-24.txt
new file mode 100644
index 00000000..86d998b7
--- /dev/null
+++ b/lib/mods/theme/file/book-24.txt
@@ -0,0 +1,59 @@
+ -----------------------
+ | Artifact Lore Vol. VI |
+ | Instruments |
+ -----------------------
+
+The Harp of Thorin
+ This magical instrument once belonged to Thorin Oakenshield, a mighty
+ dwarf warrior of old. The sounds emanating from it once gave a frightened
+ hobbit a glimpse of beauty and helped allay his fears, and to this day the
+ harp preserves its magical powers to encourage when all seems horribly
+ wrong.
+
+The Harp of Maglor
+ This harp that once belonged to Maglor makes those who use it seem more
+ forceful and convincing. It is also said that those who have used it
+ found themselves walking faster, as if to an unheard beat.
+
+The Drum of the Sky
+ The drum is decorated with the images of the stars, the clouds, the Sun
+ guided by Arien and the Moon with Tilion. It imparts to the wearer an
+ echo of the beauty of the sky, and protects him from the elements day or
+ night. The beat of the drum marks the passage of time, and will make time
+ pass differently for the wearer.
+
+The Harp of Daeron
+ A pretty harp that makes those who play it beautiful, wise and fast.
+
+The Drum of the Druedain
+ The fabled Drum of the Druedain that will protect those who play it from
+ darkness and poison attacks. It also aids in the seeing of warm blooded
+ creatures.
+
+The Horn of Rohan
+ A horn carved from the bones of the Dragon of Ered-Mithrin, this heirloom
+ of the House of Eorl bestows to its user the gifts of courage and command.
+
+The Horn of Helm
+ Heedless of cold, fearless of darkness -- besiegers fled at the wind of
+ the solitary coming of King Helm Hammerhand, proclaimed by a single
+ horn-blast in the dead of winter.
+
+The Horn of Boromir
+ The great horn made of the horns of kine of Araw. It is inlaid with silver
+ and gold signs; when blown, it can be heard for miles over. The horn gives
+ courage and endurance to its wearer, provided that secrecy is not desired.
+ "Loud and clear it sounds in the valleys of the hills... and then let all
+ the foes of Gondor flee!"
+
+The Harp of Tom Bombadil
+ This small, serviceable wooden harp once belonged to Tom Bombadil - a
+ mysterious figure whose song prevented the Wight-King from attacking Frodo
+ and his companions. Its music still inspires boldness and strengthens the
+ life force of all who play it.
+
+The Horn 'Valaroma'
+ This heavenly instrument, wrought from gleaming silver, most often appears
+ in the hands of the Valarin huntsman Orome; yet he may lend mortal
+ champions the horn from time to time. Its music inspires courage and
+ clarity of mind in pure-hearted beings, but drives evil ones far away. \ No newline at end of file
diff --git a/lib/mods/theme/file/book-25.txt b/lib/mods/theme/file/book-25.txt
new file mode 100644
index 00000000..a8cfdadf
--- /dev/null
+++ b/lib/mods/theme/file/book-25.txt
@@ -0,0 +1,98 @@
+ ------------------------
+ | Artifact Lore Vol. VII |
+ | Body Armour |
+ ------------------------
+
+The Leather Scale Mail 'Thalkettoth'
+ A tunic and skirt sewn with thick, overlapping scales of hardened leather
+ whose wearer moves with agility and assurance.
+
+The Robe of Incanus
+ Gandalf's long, flowing robe. It provides insight and allows the wearer
+ to see things not seen by all.
+
+The Robe of Great Luck
+ Once created by a powerful wizard this robe to grant him incredible
+ luck... It seems he forgot to wear it.
+
+The Hard Leather Armour of Himring
+ Contained within this studded cuirass of pliable leather is the memory of
+ unvanquished Himring, defiant fortress surrounded by the legions of
+ Morgoth.
+
+The Soft Leather Armour 'Hithlomir'
+ Familiar with the secret ways hidden in darkness, this leather cuirass is
+ truly more than it appears.
+
+The Robe of Belegaer
+ This pearl-trimmed blue robe was created by a Maia loremaster in Ulmo's
+ service. No ocean storm can harm its wearer or anything he carries; the
+ pressure and darkness of deep water cannot hinder him, either.
+
+The Leather Jerkin of Tom Bombadil
+ This garment was once the property of Tom Bombadil - a strange being
+ rumoured to be older than Arda itself. It may be the explanation for how
+ Tom could always turn up when he was most needed.
+
+The Filthy Rag of Ghan-buri-Ghan
+ The wrappings of a leader among the wild Men of Druadan forest. It
+ contains a multitude of tiny pockets filled with small darts dripping with
+ venom.
+
+The Robe of Curunir
+ The white robe of the Istari wizard Curunir, known on Middle-earth as
+ Saruman the White and Saruman of Many Colours. Imbued with cold and
+ lightning used in the creation of the Fire of Orthanc, it grants the
+ wearer some mastery over these elements.
+
+The Galvorn Plate Mail of Eol
+ A suit of imperishable galvorn, with conquerable strength to endure evil
+ and disruptive magics, that protects the life force of its wearer like
+ nothing else can. Eol the Dark Elf made it in secret for his son, but
+ Maeglin left Nan Elmoth with his mother Aredhel and never got to wear it.
+
+The Full Plate Armour of Isildur
+ A gleaming steel suit covering the wearer from neck to foot, with runes of
+ warding and stability deeply engraved into its surface.
+
+The Metal Brigandine Armour of the Rohirrim
+ A stiff suit of armour composed of small metal plates sewn to an inner
+ layer of heavy canvas, and covered with a second layer of cloth. Within it
+ is the spirit of Eorl the Young, matchless in combat.
+
+The Mithril Chain Mail 'Belegennon'
+ This wondrous suit of fine-linked chain shimmers as though of pure silver.
+ It stands untouched amidst the fury of the elements, and a power of
+ concealment rests within.
+
+The Mithril Plate Mail of Celeborn
+ A shimmering suit of true-silver, forged long ago by dwarven smiths of
+ legend. It gleams with purest white as you gaze upon it, and mighty are
+ it powers to protect and banish.
+
+The Chain Mail of Arvedui
+ A hauberk, leggings, and sleeves of interlocking steel rings, well padded
+ with leather. You feel strong and tall as Arvedui, last king of Arnor, as
+ you put it on.
+
+The Augmented Chain Mail of Caspanion
+ A hauberk, leggings, and sleeves of interlocking steel rings,
+ strategically reinforced at vital locations with a second layer of chain.
+ Magics to enhance body and mind lie within, and no door can hope to resist
+ the wearer.
+
+The Chain Mail of Peregrin Took
+ This sturdy mail shirt was a gift from the nobility of Gondor to the
+ halfling Peregrin Took. It enables a warrior to fight more capably and
+ cling to life when others would be killed. It also reveals enemies
+ (especially trolls) hiding in the dark, and terrifies anyone who threatens
+ the wearer.
+
+The Multi-Hued Dragon Scale Mail 'Lothronfaun'
+ A massive suit of heavy dragon scales deeply saturated with many colours.
+ It throbs with angry energies. May-cloud it is called, after the element
+ lightning which courses with it with unusual vigour.
+
+The Power Dragon Scale Mail 'Loknare'
+ A mighty suit of dragon armour, set with the scales of dragons of both Law
+ and Chaos, and with power over both. Loknare means Dragonblaze. \ No newline at end of file
diff --git a/lib/mods/theme/file/book-26.txt b/lib/mods/theme/file/book-26.txt
new file mode 100644
index 00000000..a042b8b5
--- /dev/null
+++ b/lib/mods/theme/file/book-26.txt
@@ -0,0 +1,56 @@
+ -------------------------
+ | Artifact Lore Vol. VIII |
+ | Cloaks |
+ -------------------------
+
+The Cloak 'Holcolleth'
+ This elven-grey mantle possesses great powers of tranquility and of
+ concealment, and grants the wearer the knowledge and understanding of the
+ Sindar.
+
+The Cloak of Thingol
+ A sable-hued cloak, with glowing elven-runes to restore magic showing calm
+ and clear as moonlight on still water.
+
+The Cloak of Thorongil
+ A cloak of shimmering green and brown that grants sight beyond sight and
+ shakes off holding magics, worn by Aragorn son of Arathorn in his youth as
+ he adventured under the name of Thorongil.
+
+The Cloak 'Colannon'
+ A crystal-blue cape of fine silk worn by a silent messenger of the forces
+ of Law. Somehow, its wearer is always able to escape trouble.
+
+The Shadow Cloak of Luthien
+ The opaque midnight folds, inset with a multitude of tiny diamonds, of
+ this cloak swirl around you and you feel a hint, a fragment of the
+ knowledge and power to restore that lay in Luthien, the most beautiful
+ being that ever knew death.
+
+The Shadow Cloak of Tuor
+ From the ruin of Gondolin did Tuor escape, through secret ways and
+ travail, shielded by his cloak from a multitude of hostile eyes.
+
+The Elven Cloak of Peregrin Took
+ This simple-looking cloak, dyed in hues that blend into the woodlands, was
+ a gift from the elves of Lothlorien to the halfling Peregrin Took. Its
+ wearer has an uncanny knack for making friends, escaping bonds, moving
+ among enemies completely unseen - and finding food.
+
+The Cloak 'Menelcol'
+ This deep-blue velvet cloak, embroidered with silvery stars, sheds
+ acelestial light that reveals hidden things and bestows unearthly beauty.
+ It also wards off damage from elements of the skies, and allows the wearer
+ to fly unscathed even through airless space.
+
+The Cloak of Ghan-buri-Ghan
+ This dark-coloured cloak once belonged to the leader of the Druedain. As
+ you put it on, you feel safer from attempts to waylay you on your travels.
+
+The Cloak of Valinor
+ A magical cloak that grants the wearer ultimate protection.
+
+The Elven Cloak of Mellyrn
+ Bearing the same lyrical name as the great trees of Lothlorien and containing
+ in its close-woven folds the speed and skill of the Galadhrim, this grey cloak
+ is ideal for those who travel in forests.
diff --git a/lib/mods/theme/file/book-27.txt b/lib/mods/theme/file/book-27.txt
new file mode 100644
index 00000000..3e958d90
--- /dev/null
+++ b/lib/mods/theme/file/book-27.txt
@@ -0,0 +1,75 @@
+ -----------------------
+ | Artifact Lore Vol. IX |
+ | Shields and Boots |
+ -----------------------
+
+The Small Mithril Shield of Thorin
+ Invoking the strength and endurance of Thorin, King under the Mountain,
+ this little metal shield is proof against the Element of Earth.
+
+The Large Leather Shield of Celegorm
+ This shield emblazoned with a multitude of creatures not seen for ages
+ once protected Celegorm, lord of Himlad; around it lies a mystic balance
+ that contains the conflicts of the elements.
+
+The Large Metal Shield of Anarion
+ The great metal-bound shield of Anarion, son of Elendil, who Sauron found
+ himself powerless to wither or diminish.
+
+The Small Metal Shield of Gimli
+ A gift from the King of Rohan to Gimli the Dwarf, this shield combines the
+ cunning and stamina of Gimli Elf-friend, Gimli the Lock-bearer.
+
+The Shield of Deflection of Gil-galad
+ The legendary shield of Gil-Galad, who fought his way to the gates of the
+ Dark Tower, and with whom came light even to Gorgoroth.
+
+The Large Leather Shield of the Haradrim
+ A great shield from the far lands of the South, whose wielder will go
+ charging into battle heedless of danger, with the strength and endurance
+ of a madman. Nor will he fear poison, for the Southron barbarians handle
+ poisoned darts naturally.
+
+The Mithril Shield of Earendil
+ A shining shield, once borne by the great mariner Earendil, "scored with
+ runes to keep all wounds and harm from him".
+
+The Large Metal Shield of Erkenbrand
+ Tall and strong stood Erkenbrand, Lord of the Westfold, as he rode to combat
+ the forces of Isengard. The valour of Helm Hammerhand lived again in him.
+ This shield is painted red according to Rohan custom and it grants magical
+ protection against enemy projectiles, as well as lets the wearer sense
+ approaching enemy hordes.
+
+The Pair of Soft Leather Boots of Wormtongue
+ The pair of boots used by Grima son of Galmod, also named the Wormtongue - a
+ treacherous but persuasive counsellor, ever ready to betray, sneak, lie,
+ cheat and steal - but never ready to actually fight.
+
+The Pair of Hard Leather Boots of Feanor
+ This wondrous pair of leather boots once sped Feanor, creator of the
+ Silmarils and the mightiest of the Eldar, along the Grinding Ice and to
+ Middle-earth at last.
+
+The Pair of Soft Leather Boots 'Dal-i-thalion'
+ A pair of high-laced shoes, strong against the powers of corruption and
+ withering, that grant the wearer extraordinary agility.
+
+The Pair of Metal Shod Boots of Thror
+ Sturdy footwear of leather and steel as enduring as the long-suffering
+ Dwarven King-in-exile who wore them. Of dwarven make, the wearer of these
+ boots will be completely at home in the mountains.
+
+The Pair of Hard Leather Boots of Nevrast
+ Footgear made of bear leather and set with opals, which grant the wearer
+ silent, hasted movement.
+
+The Pair of Metal Shod Boots of Gimli
+ A set of iron-shod boots stamped by Gimli's combat prowess, a peerless
+ ally to those journeying through halls of stone under mountains.
+
+The Pair of Metal Shod Boots of the Machine
+ A massive pair of adamantite boots studded with gold, the final and greatest
+ product of the petty-dwarven magical forge. Despite the great powers they
+ contain, they are heavy and awkward enough to make quite a racket whenever
+ you move.
diff --git a/lib/mods/theme/file/book-28.txt b/lib/mods/theme/file/book-28.txt
new file mode 100644
index 00000000..b55cf360
--- /dev/null
+++ b/lib/mods/theme/file/book-28.txt
@@ -0,0 +1,102 @@
+ ----------------------
+ | Artifact Lore Vol. X |
+ | Headgear and Gloves |
+ ----------------------
+
+The Hard Leather Cap of Thranduil
+ The hunting cap of King Thranduil, to whose ears come all the secrets of
+ his forest domain.
+
+The Metal Cap of Thengel
+ A ridged helmet made of steel, and embossed with scenes of valour in
+ fine-engraved silver. It grants the wearer nobility, clearness of thought
+ and understanding.
+
+The Steel Helm of Hammerhand
+ A great helm as steady as the heroes of the Westdike. Mighty were the
+ blows of Helm, the Hammerhand!
+
+The Dragon Helm of Dor-Lomin
+ The legendary dragon helm of Turin Turambar, an object of dread to the
+ servants of Morgoth.
+
+The Iron Helm 'Holhenneth'
+ A famous helm of forged iron granting extraordinary powers of mind and
+ awareness.
+
+The Iron Helm of Gorlim
+ A headpiece, gaudy and barbaric, that betrayed a warrior when he most
+ needed succour.
+
+The Metal Cap of Thorin
+ Mighty was Thorin Oakenshield as he emerged from the Gate of the Lonely
+ Mountain on the day of the Battle of the Five Armies, clad in shining
+ armour, part of which was this helm. He gleamed like gold in the dying
+ fire of the day, red light leapt from his eyes, and his foes were
+ terrified at the mere sight of him.
+
+The Iron Helm of Knowledge
+ This helm, designed by Petty-Dwarves ages ago to act as the brain of a
+ long lost project, is made of finest glass. Its light banishes all
+ secrets, and makes audible whispers from the deceased.
+
+The Metal Cap of Celebrimbor
+ This once belonged to Celebrimbor, maker of the Rings of Power. One who
+ knows both fire and acid, from the business of forging and engraving, will
+ fear neitheraware of Sauron before Sauron became aware of him, when Sauron
+ put on the One Ring for the first time.
+
+The Steel Helm of Gil-galad
+ The shining helm that Gil-galad, legendary Elven-king, wore in battle.
+
+The Iron Crown of Beruthiel
+ The midnight-hued steel circlet of the sorceress-queen Beruthiel, which
+ grants extraordinary powers of sight and awareness at a terrible physical
+ cost.
+
+The Golden Crown of Gondor
+ The shining winged circlet brought by Elendil from dying Numenor, emblem
+ of Gondor though an age of the world.
+
+The Jewel-Encrusted Crown of Numenor
+ A crown of massive gold, set with wondrous jewels of thought and warding,
+ worn by the kings of ancient Numenor. Its wearer may go into battle
+ always knowing what he faces - unless his own folly blinds him to the
+ nature and magnitude of the task.
+
+The Set of Leather Gloves 'Cambeleg'
+ A hero's handgear that lends great prowess in battle.
+
+The Set of Leather Gloves 'Cammithrim'
+ These gloves glow so brightly as to light the way for their owner and cast
+ magical bolts with great frequency.
+
+The Set of Gauntlets 'Paurhach'
+ A fiery set of gauntlets that can even shoot fire from the user's hands.
+
+The Set of Gauntlets 'Paurnimmen'
+ A set of handgear so icy as to be able to fire frost bolts.
+
+The Set of Gauntlets 'Pauraegen'
+ A set of handgear with sparks surrounding it, able to fire bolts of
+ electricity.
+
+The Set of Gauntlets 'Paurnen'
+ A set of handgear so corrosive that it may fire bolts of acid.
+
+The Set of Gauntlets 'Camlost'
+ A pair of gauntlets that sap combat ability, named after the empty hand of
+ Beren that once clasped a Silmaril.
+
+The Set of Cesti of Fingolfin
+ The hand-sheathing of Fingolfin, warrior-king of Elves and Men, who gave
+ Morgoth seven mighty wounds and pain that will last forever.
+
+The Set of Gauntlets of Eol
+ The iron-shod gauntlets of the Dark Elven smith Eol, tingling with magics
+ that he could channel in battle.
+
+The Set of Cesti 'Skycleaver'
+ The handgear of a legendary dragonslaying hero. The wearer of these wyrmskin
+ gauntlets will be versed in all aerial ways, and will fear no dragon that walks
+ or flies. \ No newline at end of file
diff --git a/lib/mods/theme/file/book-29.txt b/lib/mods/theme/file/book-29.txt
new file mode 100644
index 00000000..750c68cf
--- /dev/null
+++ b/lib/mods/theme/file/book-29.txt
@@ -0,0 +1,75 @@
+ -----------------------
+ | Artifact Lore Vol. XI |
+ | Rumoured Item Sets |
+ -----------------------
+
+Rumour has it that if an item set is complete, the
+wearer shall gain additional magical powers.
+
+Gothmog's Armoury
+ The Demonblade of Gothmog
+ The Demonshield of Gothmog
+ The Demonhorn of Gothmog
+
+The Elven Gifts
+ Phial of Galadriel
+ Sting
+
+The Dragon Slayer
+ The Long Bow of bard
+ The Black Arrow of Bard
+
+The Trinity
+ The Dagger of Samwise
+ The Dagger of Meriadoc
+ The Dagger of Peregrin
+
+Thorin's Gear
+ The Small Metal Shield of Thorin
+ The Harp of Thorin
+ The Metal Cap of Thorin
+
+Peregrin's Gifts
+ The Chain Mail of Peregrin Took
+ The Elven Cloak of Peregrin Took
+
+Ghan-buri-Ghan's Garb
+ The Filthy Rag of Ghan-Buri-Ghan
+ The Cloak of Ghan-buri-Ghan
+
+The Glory of the King
+ The Long Sword 'Anduril'
+ The Black Banner of Gondor
+ The Golden Crown of Gondor
+
+Saruman's Travel Gear
+ The Mage Staff of Saruman
+ The Robe of Curunir
+ The Palantir of Orthanc
+
+Elendil's Heirlooms
+ The Ring of Barahir
+ The Star of Elendil
+ The Rod of Annuminas
+
+Flame of Wrath
+ The Amulet of Carlammas
+ The Morning Star 'Naurgil'
+
+Shadow Ward
+ The Soft Leather Armour 'Hithlomir'
+ The Set of Leather Gloves 'Cammithrim'
+
+Eorl's Arms
+ Lance of Eorlingas
+ The Metal Brigandine Armour of the Rohirrim
+
+Gil-Galad's Battle Gear
+ The Shield of Deflection of Gil-Galad
+ The Spear 'Aiglos'
+ The Steel Helm of Gil-Galad
+
+Dwarven Heritage
+ Arkenstone of Thrain
+ Mattock of Nain
+ Lochaber Axe of the Dwarves \ No newline at end of file
diff --git a/lib/mods/theme/file/book-30.txt b/lib/mods/theme/file/book-30.txt
new file mode 100644
index 00000000..bfd20631
--- /dev/null
+++ b/lib/mods/theme/file/book-30.txt
@@ -0,0 +1,58 @@
+ ------------------------
+ | Artifact Lore Vol. XII |
+ | Light sources |
+ ------------------------
+
+The Phial of Galadriel
+ A small crystal phial, with the light of Earendil's Star contained inside.
+ Its light is imperishable, and near it darkness cannot endure.
+
+The Star of Elendil
+ The shining Star of the West, a famed heirloom of Elendil's house.A white
+ diamond set as a star in a silver fillet to be bound at the forehead.
+
+The Arkenstone of Thrain
+ A great globe seemingly filled with moonlight, the famed Heart of the
+ Mountain, which splinters the light that falls upon it into a thousand
+ glowing shards.
+
+The Anchor of Space-Time
+ A powerful stone that provides a strong light for any who wields it. It is
+ rumoured that it may even protect the wearer from the passing of the time.
+
+The Key of Orthanc
+ The key to the tower of Saruman, which fills your mind with images of
+ knowledge and dreadful understanding. It is not a regular key - it is a
+ perfectly round stone that is meant to fit into a hole. It can be used to
+ light your way in the dungeon as well.
+
+The Feanorian Lamp of Taniquetil
+ This holy lamp once belonged to a seeress who warned Ar-Pharazon ofthe
+ dire fate Numenor would suffer for daring to break the ban against
+ mortals' entering the Blessed Realms alive. The king had the seeress
+ executed and discarded her possessions--but rumors persist that a source
+ of pure vision, untainted by Sauron's darkness, lies hidden away somewhere
+ on Arda.
+
+The Palantir of Orthanc
+ A shining white ball of unbreakable crystal, the ancient Palantiri were
+ used by kings of Numenor and later by the Exiles for rapid communication
+ between distant lands. Nothing is hidden from one who gazes into a
+ Palantirobserver, as was Sauron when Saruman tried to spy on him with this
+ particular Palantir.
+
+The Palantir of Minas Ithil
+ A shining white ball of unbreakable crystal, the ancient Palantiri were
+ used by kings of Numenor and later by the Exiles for rapid communication
+ between distant lands. This Palantir, however, was taken by Sauron long
+ ago, and mastered to his evil uses, to the destruction of all others who
+ would gaze into it.
+
+The Black Banner of Gondor
+ A large banner of pure black, strangely gleaming with a dark light that is
+ faint and at the same time so bright it attracts attention.
+
+The Pearl 'Nimphelos'
+ It was a gift from the Falas-Elves to the Naugrim who built Menegroth, the
+ abode of Thingol and Melian. It shines like starlight on the waves of the
+ sea.
diff --git a/lib/mods/theme/file/book-31.txt b/lib/mods/theme/file/book-31.txt
new file mode 100644
index 00000000..15f677e9
--- /dev/null
+++ b/lib/mods/theme/file/book-31.txt
@@ -0,0 +1,63 @@
+ -------------------------
+ | Artifact Lore Vol. XIII |
+ | Amulets and Rings |
+ -------------------------
+
+The Amulet of Carlammas
+ A fiery circle of bronze, with mighty spells to ward off evil.
+ It is one of two items infused with holy fire.
+
+The Amulet of Ingwe
+ The ancient heirloom of Ingwe, high lord of the Vanyar, against whom
+ nothing of evil could stand.
+
+The Necklace 'Nauglamir'
+ A carencet of gold, set with a multitude of shining gems of Valinor.
+ Despite its size, its weight seems as that of gossamer.
+
+The Blue Stone 'Coimir'
+ Called 'Life-jewel' by the Vanyar of old, this flawless sapphire pendant
+ bears potent runes that preserve body and soul.
+
+The Elfstone 'Elessar'
+ This green gem glows with inner light. Aragorn son of Arathorn wore it at
+ the Battle of the Pelennor Fields, and he was himself given the name of
+ 'Elessar' by the people of Gondor because of this.
+
+The Jewel 'Evenstar'
+ A pure white jewel, the last gift of Queen Arwen Undomiel to Frodo
+ Baggins, intended to be worn around his neck on the chain that had once
+ borne the One Ring.
+
+The Necklace of Girion
+ A necklace of emeralds, green as the grass. It once belonged to Girion,
+ King of Dale, and was given to the Dwarves of the Lonely Mountain as
+ payment for a mithril mail shirt for Girion's son. It seems to have
+ overgrown with a strange moss over the years.
+
+The Amulet of Faramir
+ A slim neckpiece of True-silver, with quiet spells of Ithilien to aid and
+ protect the wearer.
+
+The Ring of Barahir
+ A ring shaped into twinned serpents with eyes of emerald meeting beneath a
+ crown of flowers, an ancient treasure of Isildur's house.
+
+The Ring of Power 'Narya'
+ The Ring of Fire, set with a ruby that glows like flame. Narya is one of
+ the three Rings of Power created by the Elves and hidden by them from
+ Sauron.
+
+The Ring of Power 'Nenya'
+ The Ring of Adamant, with a pure white stone as centrepiece. Nenya is one
+ of the three Rings of Power created by the Elves and hidden by them from
+ Sauron.
+
+The Ring of Power 'Vilya'
+ The Ring of Sapphire, with clear blue gems that shine like stars,
+ glittering untouchable despite all that Sauron ever wrought. Vilya is one
+ of the three Rings of Power created by the Elves and hidden by them from
+ Sauron.
+
+Rumour speaks of other rings of Power, too, as well as of a single ring
+whose name is foul and history treacherous. \ No newline at end of file
diff --git a/lib/mods/theme/file/book-32.txt b/lib/mods/theme/file/book-32.txt
new file mode 100644
index 00000000..d06e2ce2
--- /dev/null
+++ b/lib/mods/theme/file/book-32.txt
@@ -0,0 +1,37 @@
+ ------------------------
+ | Artifact Lore Vol. XIV |
+ | Tools and Trapkits |
+ ------------------------
+
+The Sceptre of Numenor
+ The chief mark of royalty in Westernesse, it is said to have perishedin
+ the fall of Numenor. It once belonged to Ar-Pharazon the Golden and the
+ evilness of that fallen King still courses through it.
+
+The Rod of Annuminas
+ The chief mark of royalty in Arnor, possibly the oldest remaining work of
+ Men. It came from Numenor, but it belonged to the Lords of Andunie of whom
+ the first was Valandil son of Silmarien. It was passed down to Elendil and
+ thus survived the downfall of Numenor.
+
+The Dwarven Pick of Erebor
+ A pick that provides a magical light to see by while tunnelling.
+
+The Mattock of Nain
+ Wielded by Nain of the Iron Hills at the Battle of Azanulbizar, this great
+ mattock brought victory to the Dwarves over Azog's Orcs - though Nain
+ himself fell at the last, even with victory already assured.
+
+The Bolt Trap Set of the Edain
+ A trap that can almost never be detected. Its missiles may be mere
+ pebbles, but fired at an incredibly high velocity to penetrate even the
+ toughest hide or armour.
+
+The Device Trap Set of the Noegyth Nibin
+ A magical trap, armed with a wand. Unaccountably, its victims keep on
+ coming back for more.
+
+The Arrow Trap Set of the Naugrim
+ A snare set not for animals, or people, but for demons alone, and
+ enchanted so that whenever the demon sets foot or claw into the (hidden)
+ pentagram, its hide is immediately pierced by many magical crossbow bolts. \ No newline at end of file
diff --git a/lib/mods/theme/file/book-33.txt b/lib/mods/theme/file/book-33.txt
new file mode 100644
index 00000000..4846c707
--- /dev/null
+++ b/lib/mods/theme/file/book-33.txt
@@ -0,0 +1,21 @@
+ ---------------------------------------------------------------
+| ^ |
+| ^^^ |
+| ------------------------> ^^^^^ |
+| ^^^^^^^ |
+| ^^^^^^^^^ |
+| ^^^^^^^^^^^ |
+| ^^^^^^^^^^^^^ |
+| ^^^^^^^^^^^^^^^ |
+| ^^^^^^^^^^^^^^^^ |
+| ~~ |
+| ~~ |
+| ~~ |
+| ~~ |
+| ~~ |
+| ~~ |
+| ~~ |
+| ~~ |
+| |> \ |- \/ ~~ |
+| | /\ | \ ~~ |
+ --------------------------------------------------------------- \ No newline at end of file
diff --git a/lib/mods/theme/file/book-4.txt b/lib/mods/theme/file/book-4.txt
new file mode 100644
index 00000000..29501076
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/book-6.txt b/lib/mods/theme/file/book-6.txt
new file mode 100644
index 00000000..83740e08
--- /dev/null
+++ b/lib/mods/theme/file/book-6.txt
@@ -0,0 +1,171 @@
+ ----------------------
+ | Artifact Lore Vol. I |
+ | Edged Weapons |
+ ----------------------
+
+The Main Gauche of Azaghal
+ The weapon of Azaghal when he wounded Glaurung. It is deadly when
+ fighting dragons and is said to make the breaths of fire completely
+ harmless.
+
+The Main Gauche of Maedhros
+ A short thrusting blade with a large guard worn by Maedhros the Tall,
+ eldest son of Feanor, and wielded with his left hand after the loss of his
+ right hand in the pits of Thangorodrim.
+
+The Broken Dagger 'Angrist'
+ The knife Beren Barahir`s son took from Curufin and with which he cut out
+ a Silmaril from the crown of Morgoth, The blade snapped and broke when he
+ tried to gain one more and the splint hit Morgoth at his chin. It was
+ made of Telchar the dwarf from Nogrod and it could cut through stone. Even
+ broken, it retains power.
+
+The Dagger of Samwise
+ A fiery dagger finely balanced for deadly throws.
+ It is one of the 3 legendary daggers.
+
+The Dagger of Peregrin
+ A frosty dagger finely balanced for deadly throws.
+ It is one of the 3 legendary daggers.
+
+The Dagger of Meriadoc
+ A dagger covered in sparks and finely balanced for deadly throws.
+ It is one of the 3 legendary daggers.
+
+The Dagger of Rilia
+ A large stiletto dagger that glistens with odourless poison, to which the
+ wearer seems oddly immune.
+
+The Dagger 'Belangil'
+ A frosty dagger surrounded in a nimbus of ice with a hilt of elk horn and
+ an edge to wound the wind.
+
+The Bastard Sword 'Calris'
+ This sword has runes of power incused on its ornate hilt, and a single
+ blood channel that gleams coldly blue as you grasp this mighty weapon of
+ peril.
+
+The Broad Sword 'Aranruth'
+ The beautiful sword of Thingol with a hilt of gold and silver inlay,
+ glistening icily enough to freeze the hearts of demons. You feel supple
+ and lightfooted as you hold it.
+
+The Broad Sword 'Glamdring'
+ This fiery, shining blade earned its sobriquet "Foe-Hammer" from dying
+ orcs who dared to come near hidden Gondolin. Inscribed upon the guard in
+ Cirth is the followinggd daedheloth, dam an Glamhoth." - "Turgon King of
+ Gondolin wields, has and holdsthe sword Glamdring, foe of Morgoth's realm,
+ hammer to the Din-horde."
+
+The Broad Sword 'Orcrist'
+ This coldly gleaming blade is called simply "Biter", by orcs who came to
+ know its power all too well.
+
+The Two-Handed Sword 'Anglachel'
+ A giant sword once wielded by mighty Turin, and a great dragonbane which
+ bathed in Glaurung's blood. Its blade once shone brightly, it can cleave
+ iron as though it is old wood. Beleg Cuthalion chose this sword from the
+ armoury of Thingol in Doriath. Turin son of Hurin slew his friend Beleg
+ with it by accident, and since then the blade is black and dull.
+
+The Two-Handed Sword 'Zarcuthra'
+ Dark and deadly runes stand stark against the naked steel of this awesome
+ weapon, and you feel a stunning power of slaying and rending as you slowly
+ approach.
+
+The Dark Sword 'Mormegil'
+ A foul, twisted sword with blackened spines and knobs, whose very name is
+ a curse upon the lips of Elves and Men.
+
+The Cutlass 'Gondricam'
+ Famed sea-defender of Lebennin. A short, slightly curved chopping blade
+ with a perfect edge shining cleanly in the sun, an object of hate to the
+ men of Umbar who met it in combat.
+
+The Executioner's Sword 'Crisdurian'
+ A giant's weapon, with a long blade tall and straight thrusting out from a
+ massive double-pronged hilt. On its blade are written doomspells against
+ both the living and undead.
+
+The Long Sword 'Ringil'
+ The weapon of Fingolfin, High King of the Noldor; it shines like a column
+ of ice lit by light unquenchable. Morgoth came but unwillingly to meet it
+ of old; his lame foot will remind him of its might should he meet it again.
+
+The Long Sword 'Anguirel'
+ Forged of black galvorn by the Dark-Elven smith Eol, this blade has the
+ living lightning trapped inside.
+
+The Long Sword 'Elvagil'
+ The "Singing Blade", whose wearer can slay Orcs and Trolls in the hidden
+ and secret places of the earth.
+
+The Rapier 'Forasgil'
+ A slender, tapered blade whose wielder strikes icy blows with deadly
+ accuracy.
+
+The Sabre 'Careth Asdriag'
+ An heirloom of the Lords of Rhun far to the east, and a name of dismay to
+ creatures natural and unnatural.
+
+The Short Sword 'Sting'
+ The perfect size for Bilbo, and stamped forever by the courage he found in
+ Mirkwood, this sturdy little blade grants the wearer combat prowess and
+ survival abilities they did not know they had. The blade is inscribed with
+ Tengwar writing "Sting is my name, I am the spiders' bane."
+
+The Scimitar 'Haradekket'
+ A damascened scimitar that seems wondrously easy to hold. Famed in song
+ as the "Sickle of Harad", and a deadly foe to the undead.
+
+The Short Sword 'Gilettar'
+ A stubby blade worn by Beren, whose horn sounded of old in the glades of
+ Brethil.
+
+The Blade of Chaos 'Daedheloth'
+ This weapon of wrath is named after the realm of Morgoth Bauglir. Cursed
+ with a violent anger, dives hungrily into the flesh of its enemies. It
+ gathers shadows of death into its owner as they inflict wounds that will
+ never heal.
+
+The Long Sword 'Sereghathol'
+ Blood-Sword it is called, after its thirst for the blood of foes.
+
+The Long Sword of Angmar
+ Dark flames wreath the naked steel of the Witch-King of Angmar. A mighty
+ curse to all those who wield it apart from its master, the torture of the
+ wraithworld awaits those who dare.
+
+The Broken Sword 'Narsil'
+ These are the shards of the mighty blade of Isildur, which deprived the
+ dark lord Sauron of The One Ring of Power. Legend has it that the sword
+ that was broken shall be reforged. You can barely make out a Tengwar
+ inscription on the pommel, reading "Narsil essenya, macil meletya;Telchar
+ carneron Navarotesse." - "Narsil is my name, a mighty sword;Telchar made
+ me in Nogrod."
+
+The Long Sword of Dernhelm
+ Eomer's sister Eowyn once wielded this shining sword when she battled
+ the Witch-King of Angmar, hiding her true identity to join the battle.
+ Others are less likely to notice the wielder immediately. Eowyn's sword
+ also allowed her to move swiftly, sense powerful enemies, inflict terrible
+ wounds, and partly withstand the worst attacks of the Nazgul.
+
+The Bluesteel Blade 'Ancanaur'
+ "The Jaws of Fire", this is the sword that Feanor forged in secret to do
+ battle against his enemies. The sword which threatened Fingolfin, the
+ sword which in the end did not prevent its owner's untimely death.
+
+The Broad Sword 'Guthwine'
+ The sword of Eomer, son of Eomund, leader of the Riders of the Mark. As
+ one flashed this sword with Anduril during a battle long ago. Legendary
+ are its powers in the rallying of desperate troops.
+
+The Long Sword 'Herugrim'
+ The ancient blade of Theoden, King of the Mark. It was taken from the king
+ by Grima the Wormtongue, and hidden from him in a dusty chest. It was once
+ instrumental in restoring the King's wits from evil bewitchment.
+
+The Bluesteel Blade of Ephel Duath
+ This filthy orc-blade is famed for vile deeds of torture and blood, and its
+ wielder will never cease to fear treachery.
diff --git a/lib/mods/theme/file/book-7.txt b/lib/mods/theme/file/book-7.txt
new file mode 100644
index 00000000..ea7d0df2
--- /dev/null
+++ b/lib/mods/theme/file/book-7.txt
@@ -0,0 +1,83 @@
+ -----------------------
+ | Artifact Lore Vol. II |
+ | Polearms |
+ -----------------------
+
+The Spear of Melkor
+ The mighty spear used once by Melkor to slay the trees of Valinor.
+
+The Beaked Axe of Hurin
+ Wielded by Hurin Thalion in the Fifth Battle of Beleriand, this troll-bane
+ smoked in the black blood of Gothmog's guards.
+
+The Beaked Axe of Dain Ironfoot
+ The narrow axe head of this weapon, finely balanced by a crow's beak,
+ would pierce even the armour of Smaug, and its wielder becomes aware of
+ the minds of their enemies.
+
+The Glaive of Pain
+ The massive chopper that crowns this glaive glows blood-red and black;
+ fell spells of annihilation swirl and dance as you swing death's myrmidon
+ down.
+
+The Halberd 'Osondir'
+ Lordly and tall did Osondir stand against the wrath of giants, and
+ clear-eyed in barrows fell, wielding a halberd glowing ruby red.
+
+The Pike 'Til-i-arc'
+ Within this long thrusting spear lie the spirits of frost giants and fire
+ demons, who war forever, trapped by magely spells.
+
+The Spear 'Aiglos'
+ The mighty spear of Gil-galad, famed as "Snow-point" in the songs of
+ Elves, against which all the foul corruptions of Sauron dashed in vain.The
+ spear is inscribed with Tengwar runes, reading "Gil-galad ech vae
+ vaegannen matha, / Aith heleg nin i orch gostatha; / Nin cniel na
+ nguruthos / Hon ess nn istathawell-made spear,/ The Orc will fear my point
+ of ice; / When he sees me, in fear of death / he will know my name.
+
+The Spear of Caradhras
+ A magical spear, rumoured to have been forged by Orome himself in the
+ coldest reach of the cruel Redhorn.
+
+The Spear 'Nimloth'
+ A thin spike of thrice-forged steel caps a straight sylvan shaft cut from
+ a legendary tree; spells to break the will of the undead and strike cold
+ fear into the hearts of foes lie on this perfectly balanced spear.
+
+The Lance of Eorlingas
+ "Forth Eorlingas!". To the field of Cormallen came Eorl the Young to save
+ beleaguered Gondor, and from his lance fled massive trolls and dire wolves.
+ It is one of two items that once belonged to Eorl the Young, valiant hero
+ of the Mark.
+
+The Battle Axe of Balli Stonehand
+ The twin blades of this weapon were forged in Belegost, and powerful
+ forces to resist and endure lie ready for he who shall wield it once more.
+
+The Battle Axe 'Lotharang'
+ A superbly crafted double-bladed axe that slays the creatures of earth and
+ allows rapid recovery from their blows.
+
+The Lochaber Axe of the Dwarves
+ A massive axe with twin razor-sharp heads, so large that it usually
+ requires two hands to wield, intricately engraved in gold with spells to
+ ward off the elements and smite evil.
+
+The Trident of Osse
+ The awesome weapon is imbued with some of the power of the Vala Ulmo, Lord of
+ Waters. It allows the wearer to laugh in scorn at the dread powers of the
+ undead, and be utterly in command of the element of water.
+
+The Scythe 'Avavir'
+ With elemental powers whose struggles turn this weapon red and purest
+ white, this shining reaper bears within it a power of going forth and
+ returning.
+
+The Lochaber Axe of Gothmog
+ The black axe of Gothmog, which struck Fingon at Nirnaeth. Mighty spells
+ of evil make it unsafe in any hands but of the original wielder.
+
+The Lochaber Axe 'Lhugdagnir'
+ Forged by the Dwarves to defend their home of Khazad-dum from dragons,
+ this axe has been lost to time... until now.
diff --git a/lib/mods/theme/file/book-8.txt b/lib/mods/theme/file/book-8.txt
new file mode 100644
index 00000000..bf99a12a
--- /dev/null
+++ b/lib/mods/theme/file/book-8.txt
@@ -0,0 +1,101 @@
+ ------------------------
+ | Artifact Lore Vol. III |
+ | Hafted Weapons |
+ ------------------------
+
+The Mighty Hammer 'Grond'
+ The mighty Hammer of the Underworld, blackened by doomspells of
+ shattering, whose wielder holds the lives of all Morgoth's servants in his
+ hand.
+
+The Flail 'Totila'
+ A flail whose head befuddles those who stare as you whirl it around, and
+ becomes a fiery comet as you bring it down.
+
+The Two-Handed Flail 'Thunderfist'
+ The long-lost weapon of Kzurin, Dwarven champion of ancient Belegost, with
+ runes of strength in its handle, and flames and sparks that roar and
+ crackle around its massive head.
+
+The Morning Star 'Maegnas-in-sereg'
+ You feel strong and firm of foot as you whip the chain-suspended spiked
+ orb around - and bathe it in the blood of your foes.
+
+The Morning Star 'Naurgil'
+ A famed battle-lord of old, with a ruddy head, coloured as embers are that
+ can yet rise up in wrath.
+
+The Mace 'Taratol'
+ A great ridged mace that calls around you a nimbus of living lightning;
+ you remain utterly untouched even as fat sparks arc around your fingers
+ and eyebrows.
+
+The War Hammer of Gamil Zirak
+ Gamil Zirak was a great craftsman, the master of Telchar of Nogrod. This
+ weapon was rescued from Thingol's treasury, and it is rumoured that its
+ wielder need not fear dragons or other fell beings of the Dark.
+
+The Quarterstaff 'Nar-i-vagil'
+ Named for a fiery star and set with gems of great worth binding mystic
+ virtues of protection and thought.
+
+The Quarterstaff 'Eriril'
+ The radiant golden staff of an Istari of legend, this wizard's companion
+ grants keen sight and the knowledge of many hidden things.
+
+The Quarterstaff of Olorin
+ A staff tall and sturdy, with rough-hewn runes that invoke the element of
+ Earth, and which strikes down all creatures who live in the shadow of
+ mountains.
+
+The Mace of Disruption 'Nguruthos'
+ A weapon so massive it seems beyond the strength of mortals, yet you feel
+ the might of giants within you as you heft it. As you grip the handle of
+ ebony and steel, coronas of fire blaze and mighty spells to preserve magic
+ activate around you. It is named 'Fear of Death' - you wield the Fear of
+ Dragons and the Despair of the Undead.
+
+The Lucerne Hammer 'Turmil'
+ Wielded by the High Priest of Meneltarma, this great mace gleams coldly as
+ though moonlit, and it can strike as mighty a blow spiritually as
+ physically.
+
+The Whip of Gothmog
+ With this unbearably bright whip of flame, the Balrog Gothmog has become
+ known for never having lost in combat.
+
+The Whip 'Lasher'
+ A powerful whip that is deadly against orcs. It poisons your foes and is
+ said to go "snicker snack".
+
+The Ball-and-Chain of Fundin Bluecloak
+ The weapon of one of the great dwarven priests, with powers to preserve
+ body, soul and enchantments, and the bane of those who seek life beyond
+ death.
+
+The Lead-Filled Mace 'Dolcrist'
+ This mighty bludgeon brings destruction to all around it, and is the bane
+ of dragons and magic. Skull-cleaver (or head-cleaver) it is called.
+
+The Quarterstaff of Radagast
+ Rumored to be a gift from Yavanna to Radagast the Brown, this
+ plain-seeming oak staff shields its bearer against the elements and helps
+ him fight off malicious beasts (such as the goblins' Warg-steeds). It
+ also grants exceptional insight into Nature magic, and can even cause
+ trees or healing herbs to spring forth at the bearer's command.
+
+The Mage Staff of Saruman
+ A white quarterstaff that faintly gleams a pale white, it once belonged to
+ one of the most powerful beings on Middle-Earth, Saruman the White.
+ Saruman fell into shadow, and the staff left him during his travels. As
+ you wield it, you become much more attuned to magic.
+
+The Quarterstaff of Thranduil
+ The carven oak staff of the King of the Woodland Realm, this weapon is a
+ fighter's best friend in a forest.
+
+The Mage Staff of Forochel
+ A shaft of pure, invincible crystal cut from the heart of one of the great
+ glaciers ringing the Ice-Bay of Forochel. While you hold it, your mind feels
+ as clear as the winter sky.
+
diff --git a/lib/mods/theme/file/book-9.txt b/lib/mods/theme/file/book-9.txt
new file mode 100644
index 00000000..878d4b74
--- /dev/null
+++ b/lib/mods/theme/file/book-9.txt
@@ -0,0 +1,99 @@
+ ------------------------
+ | Monstrous Compendium 1 |
+ | Humanoids |
+ ------------------------
+
+ *** Townsfolk (t) ***
+
+These are the monsters you will find in the various towns all across
+Middle-earth. Most of them are neutral to the player, though those who
+are not neutral are quite harmless, unless you take them on before you
+are strong enough. The townsfolk include [[[[[oAimless-looking merchants],
+[[[[[UPitiful-looking beggars], [[[[[ySinging, happy drunks], [[[[[uMangy-looking lepers],
+[[[[[GVillage idiots], [[[[[WBlubbering idiots], [[[[[RBoil-covered wretches], [[[[[gWoodsmen],
+[[[[[wSanctimonious-looking preachers], [[[[[vWeary-looking travellers],
+[[[[[DFilthy street urchins], [[[[[rMean-looking mercenaries],
+[[[[[bAgents of the black market], and [[[[[BBattle-scarred veterans].
+
+ *** Humans (p) ***
+
+These are the various humans inhabiting Middle-earth, from all trades
+and professions, of varying degrees of experience. The novices and
+apprentices are just what their titles imply: new to the craft and thus
+easier to deal with than more advanced masters. Depending on the
+player's race, some of these may be neutral or even coaligned. The
+trades include [[[[[brogues], [[[[[uwarriors], [[[[[rmages], [[[[[omystics], [[[[[Rsorcerers], [[[[[Wrangers],
+[[[[[Gdruids], [[[[[wpaladins], [[[[[ymindcrafters], and [[[[[gpriests].
+
+These also include various subraces of humans from different parts of
+Middle-earth, including the moderately dangerous [[[[[Ueasterlings], [[[[[rVariags],
+[[[[[UCorsairs of Umbar], [[[[[uHaradrim], [[[[[sWainriders], and [[[[[gDunlendings], the very
+dangerous [[[[[DBlack Numenoreans] and [[[[[DOathbreakers], as well as the friendly
+(yet deadly when roused) [[[[[BWavelords].
+
+
+ *** Elves and Halflings (h) ***
+
+The Halflings are a shy race, so there are not that many of them - only
+the [[[[[sScruffy-looking hobbits] and [[[[[UHalfling slingers]. Very stealthy and
+good shots, Halflings. They make excellent burglars.
+
+The [[[[[BMermaids] are an enigma, some argue that they are simply drowned
+Elven maidens, others say they're half-human, half-fish. Whatever they
+are, they are quite dangerous to the unwary adventurer. Another odd
+race are the [[[[[yMind flayers], whose mind powers are legendary and ability
+to deprive others of their sanity unmatched.
+
+The majority of Elven monsters an adventurer will encounter in the
+dungeons will be Dark Elves, all good at a particular profession, and
+much better at these professions than their human counterparts. There
+are simple [[[[[DDark elves] who live by their wits and a bit of magic, and
+then there are [[[[[vDark elven warlocks], [[[[[RDark elven sorcerers], [[[[[GDark elven]
+[[[[[Gdruids], [[[[[gDark elven priests], [[[[[uDark elven warriors], and [[[[[rDark elven]
+[[[[[rmages], all commanded by [[[[[DDark elven lords].
+
+Not all Elves you meet will be hostile and expert at the Dark arts,
+however, among them the [[[[[bAquatic elven warriors], [[[[[WElven archers],
+[[[[[wHigh-elven rangers], and [[[[[oAquatic elven mages].
+
+ *** Dwarves (k) ***
+
+A hardy and stout race, the Dwarves make formidable opponents to any
+adventurer who encounters them. They, too, practice a variety of trades,
+though their spectrum is not as wide-ranging as that of humans or elves.
+The [[[[[DDark dwarven lords] rule over the [[[[[gDark dwarven priests], [[[[[uDark dwarven]
+[[[[[uwarriors], and [[[[[wDark dwarven smiths].
+
+The Dwarves do not trust magic, but a subrace of them - the Petty-dwarves
+- do practice it, you will see both plain [[[[[sPetty-dwarves] and [[[[[RPetty-dwarf]
+[[[[[Rmages].
+
+Some adventurers who know the correct incantation will be able to summon
+[[[[[Udwarven warriors] to their aid.
+
+ *** Gnomes, leprawns, and their kin (l) ***
+
+The gnomes are a less-well-known race on Middle-earth; they are related
+to the Petty-dwarves, but they are not nearly as stout as Dwarves. The
+gnomes are quite cunning and smart, but not very strong. Their [[[[[sgnome lords]
+command the [[[[[ugnome warriors], [[[[[bgnome rogues], [[[[[rgnome priests], [[[[[Rgnome mages],
+[[[[[wgnome paladins], and [[[[[ognome mystics].
+
+A race closely related to gnomes are the leprawns - they are even smaller
+than gnomes, and much weaker. However, leprawns breed very quickly and an
+unwary adventurer might be overrun if he is not careful! The [[[[[vmalicious]
+[[[[[vleprawns] are a nuisance, the [[[[[Wwizard leprawns] are a dangerous nuisance,
+and one is not advised to underestimate the [[[[[Ddeath leprawns].
+
+There also exist [[[[[Gcheerful leprawns] and [[[[[Uadventurer gnomes], who have broken
+with the traditions of their races, and might agree to help other adventurers.
+
+A strange race from the depths are the [[[[[glizard men] with their [[[[[rlizard kings] -
+they are quite strong and command powerful magic. These creatures prefer to
+live in swamps and shallow water areas, however, so you will not encounter
+them in the dungeons often.
+
+
+
+
+
diff --git a/lib/mods/theme/file/bravado.txt b/lib/mods/theme/file/bravado.txt
new file mode 100644
index 00000000..13dec95a
--- /dev/null
+++ b/lib/mods/theme/file/bravado.txt
@@ -0,0 +1,105 @@
+103
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+cackles evilly.
+cackles diabolically.
+says: 'Surrender, miserable flea!'
+says: 'You shall not pass!'
+says: 'Let's see if you are worthy!'
+laughs devilishly.
+says: 'Flee while you can, gnat!'
+says: 'You are about to die, worm!'
+says: 'Read your prayers!'
+hisses: 'Die!'
+says: 'You don't have a chance!'
+says: 'Fear my wrath, fool!'
+says: 'Feel my fury!'
+stares at you darkly.
+gives you a contemptuous glance.
+says: 'Prepare to meet your destiny!'
+says: 'Perish, mortal!'
+says: 'Your puny efforts make me laugh!'
+says: 'Hell hath no fury like mine!'
+says: 'You should have fled while you had the chance.'
+screams: 'Die by my hand!'
+says: 'Your last wish?'
+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: 'Aye, you will make fine eating, when you have hung a bit.'
+says: 'Surrender or die!'
+says: 'Savor thy breath, it be thine last.'
+says: 'Prepare do die!'
+says: 'You shall be bruised and battered to pieces when I'm through!'
+says: 'And ever so my foes shall fall!'
+says: 'Begone now ere my arrows fly!'
+says: 'Hammer and tongs! Knocker and gongs!'
+whispers nasty things.
+says: 'I shall flatten you!'
+says: 'I could spare you, but why?'
+says: 'I'll slaughter you slowly...'
+says: 'Nothing can save you now!'
+eyes your money pouch covetously.
+says: 'I bet I can shoot better than you...'
+says: 'I hope you enjoy pain!'
+says: 'Give me your best blow!'
+spouts torrents of obscenities.
+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!'
+attempts to read a Scroll of Curse Weapon at you!
+snickers: 'Now, I strike a blow for *our* side!'
+says: 'I love the smell of fresh blood.'
+says: 'I shall torture you slowly.'
+chuckles evilly.
+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: 'I will prepare something particularly uncomfortable for you.'
+says: 'I shall smite thee with extreme prejudice!'
+says: 'Verily, it is too late for thee.'
+says: 'You'll die as you lived, in a flash of the blade.'
+brags: 'My power is beyond compare!'
+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: 'A mere mortal dares challenge my might?'
+says: 'Too bad you are unlucky. But even that would not help you now.'
+sings: 'Swish, smack! Whip crack!'
+says: 'Think of it this way: you are fated to die here. DIE!'
+says: 'There is no escape and that's for sure.'
+says: 'This is the end; I won't take anymore.'
+says: 'Say goodbye to the world you live in.'
+says: 'Your weapon is no match for mine!'
+brags: 'I'm a friend of the Boss's, I am.'
+says: 'I can't help but laugh at your pathetic attempts.'
+lets out a truly awful cry of rage!
+says: 'My innocent victims are slaughtered with wrath and despise!'
+says: 'I have found you, and there is no place to run.'
+laughs at your wild swings.
+says: 'And damn'd be him that first cries: Hold, enough!'
+says: 'I can smell your blood!'
+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 Eru 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 are already dead!'
+snickers: 'You and what army could harm me?'
+says: 'You're almost not worth killing... almost!'
+leaps towards you with death in its eye.
+sings: 'Clash, crash! Crush, smash!'
+says: 'Another adventurer? I just got through picking my teeth with the last.'
+says: 'Your two ears will decorate my belt.'
+says: 'Consider yourself warned.'
+says: 'I don't want to hurt you, I only want to kill you.'
+says: 'I am fated never to die by the hand of a mortal. Just give up.'
+screams: 'I'm out to destroy and I will cut you down!'
diff --git a/lib/mods/theme/file/chainswd.txt b/lib/mods/theme/file/chainswd.txt
new file mode 100644
index 00000000..4755391c
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/dam_huge.txt b/lib/mods/theme/file/dam_huge.txt
new file mode 100644
index 00000000..62f2ba60
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/dam_lots.txt b/lib/mods/theme/file/dam_lots.txt
new file mode 100644
index 00000000..1784d67c
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/dam_med.txt b/lib/mods/theme/file/dam_med.txt
new file mode 100644
index 00000000..fc1db066
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/dam_none.txt b/lib/mods/theme/file/dam_none.txt
new file mode 100644
index 00000000..a6c20248
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/dam_xxx.txt b/lib/mods/theme/file/dam_xxx.txt
new file mode 100644
index 00000000..afceffe1
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/dead.txt b/lib/mods/theme/file/dead.txt
new file mode 100644
index 00000000..f32dae4d
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/death.txt b/lib/mods/theme/file/death.txt
new file mode 100644
index 00000000..f033fa56
--- /dev/null
+++ b/lib/mods/theme/file/death.txt
@@ -0,0 +1,351 @@
+349
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+Tangado a chadad!
+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!
+Prepare to fire!
+Who knocked?
+Hello again, poor Yorick.
+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.
+YASD
+Who turned off the light..?
+Join the army, see the world, they said...
+This one's for the YASD hall of fame.
+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?
+#&%#&#%*#*&%!!!!!
+I must away, ere break of day...
+No time to make a testament?
+Ugh!
+Aargh!
+Aaagghhh!
+I'm melting!
+Oof..
+Oh!
+Did somebody knock?
+I hear the tramp of doom.
+To fly would be folly.
+The wind came down from mountains cold.
+Cut the bridges! To arms! To arms!
+Eeek!
+Aah!
+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.
+O! wandering folk, the summons heed!
+I go now to the halls of waiting.
+This is the End, my only friend.
+It's all over.
+This is a bitter adventure, if it must end so.
+Why does everything happen to me?
+I'm going down.
+I leave now all gold and silver, and go where it is of no worth.
+But sad or merry, I must leave it now. Farewell!
+His armour is shivered, his splendour is humbled.
+Roads go ever ever on.
+Yet feet that wandering have gone, turn at last to home afar.
+I should've listened to my mother...
+Presumed dead.
+What was that noise?
+For blood ye shall render blood.
+It's just one of those days...
+I see a bright light...
+The Shadow does not hold sway yet.
+I let you hit me!
+...and in the Darkness bind them.
+I didn't want to live anyway.
+-<sob>-
+One Ring to rule them all, One Ring to find them...
+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.
+Leithio i philinn!
+Somehow I don't feel like killing anymore.
+Help me! I am undone!
+Fire the arrows!
+This fell sergeant, Death, is strict in his arrest...
+The rest is silence.
+Guh!
+Sedho, hodo.
+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.
+Be still, lie still.
+There-is-no life in-the-cold, in-the-dark.
+Look, behind you!!!
+Here - in-the-void only death.
+I have kept no hope for myself.
+I don't believe this!
+Oops.
+I Aear can ven na mar.
+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!
+The Sea calls us home.
+Don't worry. I've got a plan.
+It didn't look so tough.
+Run away!
+Aiya Earendil Elenion Ancalima!
+AGAIN!?!?!
+I don't like this dungeon...
+Maybe this wasn't such a good idea.
+My God will protect me.
+You wouldn't dare!
+Hail Earendil brightest of the Stars!
+Farewell sweet earth and northern sky.
+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!
+Mourn not overmuch! Mighty was the fallen.
+When winter comes without a spring that I shall ever see.
+Cover me.
+Watch this.
+O! Rowan dead, upon your head your hair is dry and grey.
+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...
+Out of doubt, out of dark, to the day's rising.
+Surrender? Never!
+I'm sure reinforcements will get here on time. They promised.
+Hope he rekindled, and in hope ended.
+I have a very bad feeling about this.
+Over death, over dread, over doom lifted.
+I feel I could cast 'Speak with Dead' and talk to myself.
+Oh, that's just a light wound.
+Out of loss, out of life, unto long glory.
+I thought you were on MY side...
+Next time, try talking!
+Now for wrath, now for ruin and a red nightfall!
+Death in the morning and at day's ending...
+For into darkness fell his star...
+Where'd everybody go?
+Caveyard! Paveyard!
+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 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!
+Thy nuncle was dead as a lump o' lead.
+A bump o' the boot on the seat.
+I'm the hero of this story! I CAN'T die!
+I thought heroes were supposed to win!
+I stood upon the bridge alone.
+You've fallen and can't get up!
+Dark is the path appointed for thee.
+Sure don't look good...
+Thy heart shall then rest in the forest no more.
+Hey - I've got lawyers.
+Thanks, I needed that.
+The Winter comes, the bare and leafless Day.
+Where now the horse and the rider?
+Fatality!
+Brutality!
+Who shall gather the smoke of the dead wood burning?
+For our days are ending and our years failing.
+I saw him walk in empty lands, until he passed away.
+All is lost. Monks, monks, monks!
+All my possessions for a moment of time!
+His cloven shield, his broken sword, they to the water brought.
+It is death, my boy: they have deceived me.
+Everyone dances with the Grim Reaper.
+His limbs they laid to rest.
+I will not say the Day is done, nor bid the Stars farewell.
+I am innocent, innocent, innocent!
+Watch where you're pointing with that sword! You nearly...
+The shadow lies upon my tomb...
+My cup runneth over...
+Of course I know what I am doing.
+It looked harmless.
+I have drained my cup to the bitter dregs.
+Si man i yulma nin enquantuva?
+Look! I'm flying!
+Namarie!
+I'll be back... as soon as I can.
+Fell deeds awake: fire and slaughter!
+Cold be hand and heart and bone...
+Yes! Yes! YES! YES! YY... AAARRRGGGHH!
+...cold be sleep under stone.
+Never more to wake on stony bed...
+Been nice knowing you.
+But I just got a little prick!
+Till the Sun fails and the Moon is dead.
+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!
+In the black wind the stars shall die...
+Out into the barren lands.
+Where is my spellbook of Hand of Doom?
+Let me handle this.
+No problem. That's easy.
+But I have double resist!
+So what?
+Tell me this is an illusion... please!
+I hate the Random Number Generator.
+Oh. I didn't know it could resist that.
+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.
+I knew I should have played with stupid monsters on.
+I knew I shouldn't have let monsters learn from their mistakes.
+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.
+OK, turning that option on was a bad idea.
+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.
+Ha, ha, wrong again.
+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.
+This was supposed to be easy.
+I don't get it.
+But I read the manual!
+I was born for dying.
+The higher you walk, the farther you fall.
+Where's your crown, King Nothing?
+*sigh*
+My descendants will avenge me.
+I should have listened to my ancestors.
+I should have read the help files.
+There's a time to live, and a time to die, when it's time to meet the maker.
+One ill turn deserves another.
+Only the good die young, all the evil seem to live forever.
+That's not what the Wand of Death was supposed to do.
+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.
+*whimper*
+No, this really isn't happening.
+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!
+*bang*
+Flames? Not yet, I think.
+This can't be real.
+Someone call the Shiriff!
+And now the dreams end.
+Awake! Awake! Fear, Fire, Foes! Awake!
+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!
+I didn't know it was a Scroll of Aggravate Monster!
+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.
+'New blood joins this earth...'
+But my Saving Throw is Good!
+I really only like writing poetry.
+What is Time, friend or foe?
+Time waits for none.
+Why didn't I start a Necromancer?
+Flash before my eyes: now it's time to die.
+Gaaaah! This is most frustrating.
+I hate summoners.
+I'm not dead yet. I still have five hit points.
+To sleep, perchance to dream.
+I rolled a 20. How could that be a miss?
+This thing all things devours.
diff --git a/lib/mods/theme/file/error.txt b/lib/mods/theme/file/error.txt
new file mode 100644
index 00000000..5829ffbc
--- /dev/null
+++ b/lib/mods/theme/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!
+I don't have any options, oh my!
+Disk error. (a)bort, (r)etry, (f)ail?
diff --git a/lib/mods/theme/file/mondeath.txt b/lib/mods/theme/file/mondeath.txt
new file mode 100644
index 00000000..e1dc7967
--- /dev/null
+++ b/lib/mods/theme/file/mondeath.txt
@@ -0,0 +1,334 @@
+332
+****** BUFFER LINE *********************************** DO NOT REMOVE *******
+'I must away, ere break of day...'
+'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!'
+'I hear the tramp of doom.'
+'Who knocked?'
+'What's a burrahobbit?'
+'Ouch.'
+'Et tu, Brute! Then fall, Caesar!'
+'To fly would be folly.'
+'I told you to be careful with that sword...'
+'The wind came down from mountains cold.'
+'Cut the bridges! To arms! To arms!'
+'O! wandering folk, the summons heed!'
+'Who turned off the light..?'
+'I go now to the halls of waiting.'
+'I leave now all gold and silver, and go where it is of no worth.'
+'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?'
+'#&%#&#%*#*&%!!!!!'
+'This is a bitter adventure, if it must end so.'
+'No time to make a testament?'
+'Ugh!'
+'Aargh!'
+'Aaagghhh!'
+'I'm melting!'
+'Oof..'
+'Oh!'
+'Did somebody knock?'
+'But sad or merry, I must leave it now. Farewell!'
+'His armour is shivered, his splendour is humbled.'
+'Roads go ever ever on.'
+'Yet feet that wandering have gone, turn at last to home afar.'
+'Eeek!'
+'Aah!'
+'I hate it when that happens.'
+'One direct hit can ruin your whole day.'
+'Oh no!'
+'Not me!'
+'Presumed dead.'
+'Oh no, not again.'
+'Another one bites the dust.'
+'Goodbye.'
+'Help me!'
+'Farewell, cruel world.'
+'Blackheart! I have done thy bidding.'
+'For blood ye shall render blood.'
+'This is the End, my only friend.'
+'It's all over.'
+'The Shadow does not hold sway yet.'
+'Why does everything happen to me?'
+'I'm going down.'
+'...and in the Darkness bind them.'
+'One Ring to rule them all, One Ring to find them...'
+'Hail Sauron, Lord of the Ring, Lord of the Earth!'
+'Leithio i philinn!'
+'Fire the arrows!'
+'Sedho, hodo.'
+'Be still, lie still.'
+'What was that noise?'
+'There-is-no life in-the-cold, in-the-dark.'
+'It's just one of those days...'
+'I see a bright light...'
+'Here - in-the-void only death.'
+'I let you hit me!'
+'I have kept no hope for myself.'
+'I didn't want to live anyway.'
+'-<sob>-'
+'I Aear can ven na mar.'
+'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.'
+'The Sea calls us home.'
+'Somehow I don't feel like killing anymore.'
+'Help me! I am undone!'
+'Farewell sweet earth and northern sky.'
+'This fell sergeant, Death, is strict in his arrest...'
+'The rest is silence.'
+'Guh!'
+'Mourn not overmuch! Mighty was the fallen.'
+'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.'
+'When winter comes without a spring that I shall ever see.'
+'Out of doubt, out of dark, to the day's rising.'
+'Look, behind you!!!'
+'Hope he rekindled, and in hope ended.'
+'Over death, over dread, over doom lifted.'
+'I don't believe this!'
+'Oops.'
+'Can't you take a joke?'
+'Out of loss, out of life, unto long glory.'
+'Oops, sorry... didn't mean to disturb you.'
+'I never get to have any fun!'
+'Stop!'
+'Now for wrath, now for ruin and a red nightfall!'
+'Don't worry. I've got a plan.'
+'It didn't look so tough.'
+'Run away!'
+'Death in the morning and at day's ending...'
+'AGAIN!?!?!'
+'I don't like this dungeon...'
+'Maybe this wasn't such a good idea.'
+'My God will protect me.'
+'You wouldn't dare!'
+'For into darkness fell his star...'
+'Caveyard! Paveyard!'
+'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?'
+'Thy nuncle was dead as a lump o' lead.'
+'Surrender? Never!'
+'I'm sure reinforcements will get here on time. They promised.'
+'A bump o' the boot on the seat.'
+'I have a very bad feeling about this.'
+'I stood upon the bridge alone.'
+'I feel I could cast Speak with Dead and talk to myself.'
+'Oh, that's just a light wound.'
+'Dark is the path appointed for thee.'
+'I thought you were on MY side...'
+'Thy heart shall then rest in the forest no more.'
+'The Winter comes, the bare and leafless Day.'
+'Where now the horse and the rider?'
+'Uhh... oh-oh...'
+'Gee, where'd everybody go?'
+'Who shall gather the smoke of the dead wood burning?'
+'Ay! Ay! Ay!'
+'Ohe! Ohe! Ohe!'
+'Et tu, Caesar! Then fall, Brute!'
+'Even the best laid plans...'
+'For our days are ending and our years failing.'
+'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!'
+'I saw him walk in empty lands, until he passed away.'
+'His cloven shield, his broken sword, they to the water brought.'
+'I'm the hero of this story! I CAN'T die!'
+'His limbs they laid to rest.'
+'I've fallen and I can't get up!'
+'I'll be back for my revenge.'
+'Sure don't look good...'
+'I will not say the Day is done, nor bid the Stars farewell.'
+'I'll be back...'
+'The shadow lies upon my tomb...'
+'Thanks, I needed that.'
+'My cup runneth over...'
+'I have drained my cup to the bitter dregs.'
+'Oh, basely done! I had hoped for better of thee!'
+'I am death incarnate! NOTHING can harm me!'
+'Si man i yulma nin enquantuva?'
+'Namarie!'
+'All is lost. Monks, monks, monks!'
+'All my possessions for a moment of time!'
+'Fell deeds awake: fire and slaughter!'
+'Cold be hand and heart and bone...'
+'Everyone dances with the Grim Reaper.'
+'...cold be sleep under stone.'
+'Never more to wake on stony bed...'
+'I am innocent, innocent, innocent!'
+'Watch where you're pointing with that sword! You nearly...'
+'Till the Sun fails and the Moon is dead.'
+'In the black wind the stars shall die...'
+'Of course I know what I am doing.'
+'It looked harmless.'
+'Out into the barren lands.'
+'Where is my spellbook of Hand of Doom?'
+'Look! I'm flying!'
+'But I have double resist!'
+'I'll be back... as soon as I can.'
+'Oh. I didn't know it could resist that'
+'This was supposed to be easy.'
+'Yes! Yes! YES! YES! YY... AAARRRGGGHH!'
+'I don't get it.'
+'But I read the manual!'
+'Been nice knowing you.'
+'But I just got a little prick!'
+'*sigh*'
+'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!'
+'*bang*'
+'You have robbed my revenge of sweetness.'
+'I'll see your descendants later!'
+'Let me handle this.'
+'No problem. That's easy.'
+'*whimper*'
+'So what?'
+'Tell me this is an illusion... please!'
+'I hate the Random Number Generator.'
+'Oh, great.'
+'Trust me.'
+'CHARGE!'
+'What do you mean, how many hit points do I have?'
+'What do you mean, my GOI expired?'
+'One ill turn deserves another.'
+'But, Sharkey!'
+'But I'm famous!'
+'Awake! Awake! Fear, Fire, Foes! Awake!'
+'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.'
+'I really only like writing poetry.'
+'That unworthy hand! That unworthy hand!'
+'I am dying.'
+'I like happy things. I'm really calm and peaceful.'
+'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.'
+'Won't you look at all the pretty flowers?'
+'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.'
+'Gaaaah! This is most frustrating.'
+'What treachery is this?'
+'Greetings, Death, I'm yours to take away.'
+'I was born for dying.'
+'The higher you walk, the farther you fall.'
+'Down here, we all float.'
+'New blood joins this earth...'
+'You labeled me, I'll label you, so I dub thee unforgiven.'
+'See you in the Void!'
+'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.'
+'No, no, no. It's not fair.'
+'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.'
+'This didn't just happen.'
+'You hit me for HOW MUCH damage?'
+'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?'
+'You cheated!'
+'Life down there is just a strange illusion.'
+'My life suffocates, planting seeds of hate.'
+'I'm not dead yet. I still have five hit points'
+'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!'
+'Oh well, it's back to square one in HMa.'
+'Flames? Not yet, I think.'
+'Great. Now I can finish my memoirs.'
+'And now the dreams end.'
+'Sharkey made me do it.'
+'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.'
+'This is just gruesome.'
+'When the dream dies, the nightmare begins.'
+'This thing all things devours.'
+'This can't be real.'
+'No, this really isn't happening.'
+'I believe my kingdom will come'
diff --git a/lib/mods/theme/file/monfear.txt b/lib/mods/theme/file/monfear.txt
new file mode 100644
index 00000000..fbff8538
--- /dev/null
+++ b/lib/mods/theme/file/monfear.txt
@@ -0,0 +1,63 @@
+61
+*********** BUFFER LINE ********************** DO NOT REMOVE *****************
+says: 'I am too young to die.'
+says: 'Ok, ok!'
+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...'
+begs for you to spare its life.
+says: 'All my possession for a moment of time!'
+says: 'Hey, it was only a joke, all right?'
+says: 'Stop!'
+grovels at your feet.
+says: 'Money? Sure, take it all back!'
+screams: 'Cowards! Why did you not protect me?'
+sobs: 'I didn't MEAN it...'
+begs: 'Spare me and I'll get you Ringil! Really!'
+whimpers and grovels.
+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!'
+sobs: 'All right! I apologise! I really, really do!'
+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?'
+cringes and whimpers.
+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!'
+prays fervently to any Gods that are listening.
+cries: 'Spare me!'
diff --git a/lib/mods/theme/file/monspeak.txt b/lib/mods/theme/file/monspeak.txt
new file mode 100644
index 00000000..221326be
--- /dev/null
+++ b/lib/mods/theme/file/monspeak.txt
@@ -0,0 +1,517 @@
+# 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:63:Smeagol
+27
+snickers.
+grovels.
+picks his nose.
+pines for his precious.
+searches his pockets.
+eats some slimy creatures.
+mutters: 'My preciouss, where iss my preciouss?'
+shouts: 'No Master Hobbitsisisisis!'
+cries: 'The ring was ours for agesisisisis!'
+says: 'Smeagol sneaking! ME! Shneeeakingsisis!'
+screams: 'Nassty Hobbitsisisisis...'
+says: 'Come on, quickly, follow Smeagol!'
+coughs: 'Gollum! Gollum!'
+says: 'Every way is guarded, silly foolsis!'
+says: 'Nassty Bagginsis, stole my precious.'
+says: 'She will kill them, oh yes she will preciouss.'
+says: 'Does it guess easy? It must have a competition with us, preciouss!'
+hisses: 'What has it got in its pocketses?'
+whimpers: 'We've lost it, we have.'
+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:102:Lurtz, Uruk Captain of the White Hand
+N:140:Lagduf, the Snaga
+N:170:Radbug, the Goblin
+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:370:Muzgash, the Snaga
+N:373:Azog, King of the Uruk-Hai
+N:399:Mauhur, the Uruk
+17
+fingers his blade and grins evilly.
+says, 'I'll bet your innards would taste real sweet...'
+says, 'I love the smell of fresh blood.'
+wonders aloud how many experience points you're worth.
+sings, 'Clap! Snap! the black crack!'
+sings, 'Grip, grab! Pinch, nab!'
+sings, 'And down down to Goblin-town you go, my lad!'
+sings, 'Clash, crash! Crush, smash!'
+sings, 'Hammer and tongs! Knocker and gongs!'
+sings, 'Pound, pound, far underground!'
+sings, 'Ho, ho! my lad!'
+sings ' Swish, smack! Whip crack!'
+sings 'Batter and beat! Yammer and bleat!'
+sings, 'Work, work! Nor dare to shirk,'
+sings, 'While Goblins quaff, and Goblins laugh,'
+sings, 'Round and round far underground...'
+sings, 'Below, my lad!'
+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:137:Grima Wormtongue, Agent of Saruman
+3
+says, 'Lathspell I name you, Ill-news; and Ill-news is an ill guest they say.'
+says, 'Forbid his staff!'
+yells, 'You lie!'
+3
+screams, 'Let me go, let me go!'
+says, 'My messages are useless now!'
+hisses, 'You told me to; you made me do it!'
+
+N:412:Lotho Sackville-Baggins, Betrayer of the Shire
+N:374:Bill Ferny
+N:437:Harry Goatleaf, Gatekeeper of Bree
+5
+whines and snickers.
+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
+5
+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 till it hurts!'
+says, 'Do not force me to put an arrow in your skull...'
+3
+begs you to spare his life.
+says, 'But I'm a GOOD guy, really!'
+says, 'Money? Sure, take it all back!'
+
+N:254:Maedhros the Tall
+N:263:Maglor the Mighty Singer
+N:268:Celegorm the Fair
+N:274:Caranthir the Dark
+N:276:Curufin the Crafty
+N:318:Amrod, Son of Feanor
+N:319:Amras, Son of Feanor
+8
+says, 'I swore an oath which none shall break.'
+says, 'I sword an oath which none should take.'
+says, 'I swore by the name of even Iluvatar.'
+says, 'I called the Everlasting Dark upon me if I kept it not.'
+says, 'I named Manwe my witness.'
+says, 'I named Varda, and hallowed Taniquetil, my witnesses.'
+says, 'I will pursue with vengeance and hatred.'
+says, 'None shall keep or hold a Silmaril from me.'
+3
+says, 'I accept my fate.'
+says, 'This must be payment for the Kinslaying.'
+says, 'The blood of my kin burns my hands.'
+
+N:169:Brodda, the Easterling
+N:291:Ulfast, Son of Ulfang
+N:413:Ulwarth, Son of Ulfang
+N:392:Sangahyando of Umbar
+N:380:Angamaite of Umbar
+N:573:Lorgan, Chief of the Easterlings
+N:987:Uldor the Accursed
+N:990:Ulfang the Black
+11
+fingers his blade and grins evilly.
+snickers, 'Now, I strike a blow for *our* side!'
+calls your mother nasty names.
+belches and spits.
+scratches his armpits.
+says, 'I love the smell of fresh blood.'
+says, 'Yeeha! Another idiot to slaughter!'
+wonders aloud how many experience points you're worth.
+says, 'My brain's on fire with the feeling to kill!'
+says, 'I shall torture you slowly.'
+says, 'I shall have my way with your women!'
+3
+whimpers, 'No, I will really fight on your side!'
+says, 'Morgoth made me do it.'
+says, 'Please don't kill me!'
+
+#This next may be unnecessarily evil... :-]
+N:393:It
+4
+says, 'Nyah, nyah, betcha can't find me!'
+magically summons mighty undead opponents!
+casts a spell and cackles evilly.
+magically summons special opponents!
+2
+howls, 'I'll be back!'
+whimpers, 'They said this invisibility thing was fool-proof!'
+
+N:441:Tom Bombadil
+8
+sings, 'Hey dol! merry dol! ring a dong dillo!'
+sings, 'Old Tom Bombadil water-lilies bringing...'
+sings, 'Goldberry, Goldberry, merry yellow berry-o!'
+sings, 'Hey! Come derry dol! Can you hear me singing?'
+says, 'Eat earth! Dig deep! Drink water! Go to sleep!'
+says, 'Bombadil is talking!'
+sings, 'Hey! Come derry dol! Hop along, my hearties!'
+sings, 'Old Tom Bombadil is a merry fellow!'
+0
+
+#N:467:Beorn, the Shape-Changer
+6
+says, 'They don't look dangerous!'
+laughs a great rolling laugh.
+says, 'O ho, so you've had trouble with THEM, have you?'
+says, 'I don't need your service, thank you.'
+asks, 'Where are they? Killed, eaten, gone home?'
+admonishes, 'I would have given them more than fireworks!'
+0
+
+N:489:Varda, Lady of the Stars
+N:490:Vaire, the Weaver
+N:492:Irmo of Lorien
+N:497:Este, the Gentle
+N:498:Nienna, the Sorrowful
+N:506:Nessa the Lithe
+N:511:Orome, the Huntsman of the Valar
+N:515:Vana, the Ever-young
+56
+gazes southwards.
+reveals: The Helcaraxe is in the north-west.
+gazes northwards.
+reveals: Don't bring any glassware to the Helcaraxe.
+gazes eastwards.
+reveals: The Wastelands of the North are guarded by the Hunter.
+gazes westwards.
+reveals: The Withered Heath is in the Grey Mountains.
+sings softly.
+reveals: Utumno has not been destroyed completely.
+whispers something.
+reveals: The Blue Wizards are trapped in Utumno.
+weeps quietly.
+reveals: Utumno is in the north-east.
+looks at you sadly.
+reveals: There is an artifact that will let you identify items.
+turns away from you.
+reveals: There is a powerful artifact guarded in Near Harad.
+says, 'Don't you have a quest to complete, child?'
+reveals: The Castle of Dol Amroth is in the south.
+gazes southwards.
+reveals: A strange creature guards the Maze in the south.
+gazes northwards.
+reveals: Cirith Ungol is poisonous.
+gazes eastwards.
+reveals: There is a passage to The Lonely Isle in the southwest.
+gazes westwards.
+reveals: Saruman waits for you beneath Isengard.
+sings softly.
+reveals: To get to Khazad-Dum, you must pass through Moria.
+whispers something.
+reveals: There is a snowy passage through the Misty Mountains.
+weeps quietly.
+reveals: The Witch-Realm of Angmar will poison and disenchant.
+looks at you sadly.
+reveals: There is a great treasure in the Blue Mountains.
+turns away from you.
+reveals: You must bring a Map and a Key to Smaug's lair in Erebor.
+says, 'Don't you have a quest to complete, child?'
+reveals: Beware of the Deep Marshes.
+gazes southwards.
+reveals: Level 95 of Angband is a special level.
+gazes northwards.
+reveals: Level 85 of Angband is a special level.
+gazes eastwards.
+reveals: Level 70 of Angband is a special level.
+gazes westwards.
+reveals: Level 21 of the Orc Caves is a special level.
+sings softly.
+reveals: Level 35 of Moria is a special level.
+whispers something.
+reveals: The last level of Mount Doom never changes.
+weeps quietly.
+reveals: Mirkwood forest will lead you to the Heart of the World.
+looks at you sadly.
+reveals: There is a small water cave in Moria.
+0
+
+N:493:Bert the Stone Troll
+N:494:Bill the Stone Troll
+N:495:Tom the Stone Troll
+7
+asks, 'What are yer?'
+asks, 'What's a burrahobbit got to do with my pockets?'
+muses, 'You wouldn't make above a mouthful.'
+taunts, 'You're a fat fool!'
+says, 'You're a booby.'
+says, 'That'll teach yer!'
+says, 'I won't take that from you!'
+0
+
+N:505:Groo the Wanderer
+0
+1
+says: 'A fray! A fray!'
+
+N:523:N:523:Ingeborg, the Runemistress
+6
+says: 'Hey, I just had a neat idea!'
+says: 'Ringil? Never heard of it where I come from!'
+scribbles busily on a stack of parchment.
+mutters something in an unknown tongue.
+cuddles something warm and fuzzy.
+offers you a hug.
+3
+shrieks: 'Oh my gods, I'm doomed!'
+protests: 'Awww, I just wanted to hug you...'
+protests: 'Hey, it's only a game--please don't hurt me!'
+
+N:531:Lindal Lossehelin
+N:552:Erianyth, the Sorceress
+N:557:Karrazix the Brave
+N:564:Sir Physt
+N:572:Slappy, Abbess of Pain
+5
+asks, 'Hey, you got a Cloak of Air I can borrow?'
+states matter-of-factly, 'You're not as tough as I am.'
+asks, 'Are you going to DiTL this one?'
+brags, 'My CON is higher than yours.'
+gloats, 'I got Boots of Speed from that stupid princess.'
+2
+screams, 'See you on the t-o-m-e.net forums!'
+screams, 'ToME rules!'
+
+N:535:Zizzo, Last of the Yeeks
+N:569:Nick LeYeek, Second Last of the Yeeks
+N:595:Orfax, son of Boldor
+N:596:Boldor, King of the Yeeks
+5
+wonders aloud about your sexual orientation.
+spouts torrents of obscenities.
+shouts, 'YEEK! YEEK! YEEK!'
+wonders aloud how many experience points you're worth.
+says, 'I'll teach you to respect yeeks!'
+2
+sobs, 'I didn't MEAN it...'
+whimpers and moans.
+
+N:660:Eol, the Dark Elf
+5
+says, 'I acknowledge not your law.'
+says, 'I care nothing for your secrets.'
+says, 'I came not to spy on you but to claim my own.'
+screams, 'My son you shall not withhold from me!'
+screams, 'You shall not hold what is mine!'
+1
+'Here you may yet die the same death as I.'
+
+N:697:Smaug the Golden
+12
+says, 'Well, thief! I smell you and I feel your air. I hear your breath.'
+says, 'Come along! Help yourself, there is plenty and to spare.'
+says, 'You have nice manners for a thief and a liar.'
+wonders: 'Who are you and where do you come from, may I ask?'
+says, 'Lucky numbers don't often come off.'
+says, 'Some nasty scheme of those Lake-men, or I'm a lizard.'
+brags, 'I kill where I wish and none dare resist.'
+brags, 'I laid low the warriors of old whose like is not in the world today.'
+brags, 'Now I am old and strong, strong, strong!'
+brags, 'My armour is like tenfold shields, my teeth are swords, my claws spears.'
+brags, 'The shock of my tail is a thunderbolt, my wings a hurricane!'
+brags, 'I am armoured above and below with iron scales and hard gems.'
+2
+wails, 'No, no, no! Not the Black Arrow!'
+admits defeat.
+
+N:714:Quickbeam, the Ent
+2
+says, 'I like birds, even when they chatter.'
+shouts, 'Ra-hoom-rah!'
+0
+
+N:715:Glaurung, Father of the Dragons
+5
+calls you a thankless fosterling.
+taunts, 'Captain foolhardy!'
+calls you The Usurper of Nargothrond!
+says, 'If thou wilt be slain, I will slay thee gladly.'
+lectures, 'They lie who say we do not honour the valour of foes.'
+1
+lets out a bloodcurdling scream.
+
+N:771:Saruman of Many Colours
+3
+says, 'One ill turn deserves another.'
+brags, 'Whoever strikes me shall be accursed.'
+admonishes, 'Meddle not in policies which you do not understand.'
+3
+says, 'Spare me your pity. I would hate you even more for it.'
+croaks, 'Do not expect me to wish you health and long life.'
+lets out a shrill shriek
+
+N:850:Carcharoth, the Jaws of Thirst
+N:840:Draugluin, Sire of All Werewolves
+7
+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.
+says, 'Bad adventurer! No more living for you!'
+snarls and howls.
+1
+cringes and whimpers.
+
+N:818:The Mouth of Sauron
+6
+asks, 'Anyone with authority to treat with me? Not thou at least!'
+says, 'This time thou hast stuck out thy nose too far.'
+says, 'Thou shalt see what comes to him who defy Sauron the Great.'
+says, 'Take swift counsel with what little wit is left to you.'
+says, 'And now thou shalt endure the slow torment of years!'
+says, 'Do not bandy words in your insolence with the Mouth of Sauron!'
+1
+screams, 'This CAN'T be happening!'
+shouts, 'Kill me if you want, the Boss will get you!'
+
+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
+8
+brags, 'My power is beyond compare!'
+snorts, 'A mere mortal dares challenge my might?'
+says, 'Not another one! I just finished chewing on the last!'
+wonders aloud how many experience points you're worth.
+says, 'Hell shall claim your remains!'
+yawns at your pathetic efforts to kill him.
+says, 'Minions, slaughter this fool!'
+says, 'Set thine house in order, for thou shalt die...'
+2
+screams, 'This CAN'T be happening!'
+shouts, 'Kill me if you want, the Boss will get you!'
+
+N:853:Huan, Wolfhound of the Valar
+1
+asks, 'Have you seen Carcharoth? I seek him.'
+0
+
+N:861:Dark God, the Mighty Coder of Hell
+6
+says 'Hullo'.
+emits a low 'hmmmm'.
+screams 'I came from the Hell for YOU!'.
+laughs out loudly.
+mutters something about bugs.
+asks you about the new version.
+1
+screams 'ToME rules!'.
+
+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:934:Fangorn the Treebeard
+5
+says: 'The night stretches out on the Isengard!'
+says: 'Trolls are strong, Ents are STRONGER!'
+says: 'Hoom, hm, ah well.'
+says: 'Saruman will now stop using his axes on the trees...'
+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:954:The Witch-King of Angmar
+2
+says, 'Do you not know Death when you see it?'
+says, 'Thou fool. No living man may hinder me!'
+0
+
+N:969:Princess
+4
+cries.
+screams, 'Help me!'.
+whines, 'I need your help, great hero!'.
+implores you to help her.
+1
+screams 'I didn't REALISE I was STANDING on the stairs, OKAY???'.
+
+N:1010:The Insane Player
+4
+screams, 'There can be only one!'
+makes a chattering noise with his teeth.
+screams, 'Morgoth is MY quarry! GO AWAY!!!'
+scrapes some slime off the wall and eats it, repeating 'grow mold!'
+2
+whimpers, 'You sure looked harmless.'
+sobs uncontrollably.
+
+N:1036:Sanctimonious-looking preacher
+5
+screams 'And the streets shall flow with the blood of the non-believers!'
+says: 'Eru is the One. There is no mistaking it, nor arguing with it.'
+says: 'I have never killed, but I have read many obituaries with pleasure.'
+hisses, 'Repent, evil-doer! Ere it is too late.'
+yells, 'And He will execute vengeance in anger and fury upon the heathen!'
+3
+says: 'Okay, I admit it. Religion is basically guilt, with different holidays.'
+whispers, 'Walk with those seeking Truth. Run from those who think they've found it.'
+says, 'The light of Valinor is with me.'
+
+N:1039:Improv, the mighty MoLD
+2
+screams 'Your code is ugly!'
+mutters something about bugs.
+1
+screams 'ToME rules!'. \ No newline at end of file
diff --git a/lib/mods/theme/file/news.txt b/lib/mods/theme/file/news.txt
new file mode 100644
index 00000000..67691fb4
--- /dev/null
+++ b/lib/mods/theme/file/news.txt
@@ -0,0 +1,23 @@
+#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##\
+#W ,-. _,-= /"._ ,-. #w ," #W `:' `:#D#####W[JW] ,. #G #R "Theme"
+#W," `," `=._ `" -#w; #G The #W\#D#####W;"' V \#W __,-. ,-=.
+#W / " `-. #w / #GTroubles of #W `" `-.#W__,-""._," "--," >-=-
+#W ,-"`. #w _' #G Middle Earth#w "._ #W` _/
+ _," #w"=.".
+ _," #ohttp://www.t-o-m-e.net #w`._,=-._ _,-.
+ _._ ,"._," #w"-." \,-.
+-" `-" #w`-
+ ToME maintained by darkgod Module by furiosity
+ #Bdarkgod@t-o-m-e.net #Bfuriosity@gmail.com
+#y One Ring to rule them all,
+#y One Ring to find them,
+#y One Ring to bring them all,
+#y And in the Darkness bind them
+#y In the Land of Mordor, where the Shadows lie.
+#B [J.R.R. Tolkien] \ No newline at end of file
diff --git a/lib/mods/theme/file/news2.txt b/lib/mods/theme/file/news2.txt
new file mode 100644
index 00000000..67691fb4
--- /dev/null
+++ b/lib/mods/theme/file/news2.txt
@@ -0,0 +1,23 @@
+#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##\
+#W ,-. _,-= /"._ ,-. #w ," #W `:' `:#D#####W[JW] ,. #G #R "Theme"
+#W," `," `=._ `" -#w; #G The #W\#D#####W;"' V \#W __,-. ,-=.
+#W / " `-. #w / #GTroubles of #W `" `-.#W__,-""._," "--," >-=-
+#W ,-"`. #w _' #G Middle Earth#w "._ #W` _/
+ _," #w"=.".
+ _," #ohttp://www.t-o-m-e.net #w`._,=-._ _,-.
+ _._ ,"._," #w"-." \,-.
+-" `-" #w`-
+ ToME maintained by darkgod Module by furiosity
+ #Bdarkgod@t-o-m-e.net #Bfuriosity@gmail.com
+#y One Ring to rule them all,
+#y One Ring to find them,
+#y One Ring to bring them all,
+#y And in the Darkness bind them
+#y In the Land of Mordor, where the Shadows lie.
+#B [J.R.R. Tolkien] \ No newline at end of file
diff --git a/lib/mods/theme/file/rart_f.txt b/lib/mods/theme/file/rart_f.txt
new file mode 100644
index 00000000..50a55b93
--- /dev/null
+++ b/lib/mods/theme/file/rart_f.txt
@@ -0,0 +1,86 @@
+85
+The Bag of Tricks
+Pandora's Box
+The Deck of Wild Magic
+The Box of Horrors
+The Thirteenth Eye of Sauron
+Saruman'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 Boulder of Entmoot
+The Voodoo Doll of Ghan-buri-ghan
+The Mirror of Alternate Dimensions
+The Ebon Cube of Darkness
+The Diamond Prism of Light
+The Gem of Chaos
+The Pentagram of Lore
+The Fourteenth Eye of Sauron
+The Space-Time Continuum
+The Shard of Pottery that is Not Junk
+Curunir's Alteration Manual
+Gandalf's Tome of Unconventional Warfare
+The Tome of Collected Strange Magic
+The Pendulum of Immortality
+The Black Candle of Ered Mithrin
+Smeagol's Lost Toy
+The Staff of the Ithryn Luin
+The Tome of Chaos
+The Dagger ``Thanc''
+The Tome of Elven Household Magic
+a Piece of the Mage Staff of Olorin
+a Parchment titled ``Magic for the Layman''
+The Shopping Bag of Gaffer Gamgee
+The Bag of Magic Toys
+The Skull of Ancient Wisdom
+The Box of Wizardry
+The Potion of Winning
+Radagast's Summoning Manual
+The Gem of Time
+The Lost Works of Melian
+The Crystal Ball of Godly Sights
+The Box of Many Wonders
+Carcharoth's Favourite Toy
+The Hat of Radagast
+The Bottomless Bottle
+a Parchment titled ``How to Win the Game''
+The Wand Construction Kit
+The Clay Tablets of Antiquity
+The Blood of Death
+Darkgod's Voodoo Doll of the Player
+The Skeleton of Nob
+The Magical Honey Jar
+a Parchment titled ``Concerning Hobbits''
+a Parchment titled ``On Beren and Luthien''
+The Fifteenth Eye of Sauron
+The Sixteenth Eye of Sauron
+The Seventeenth Eye of Sauron
+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 Telepathy
+The Ultimate Slime Mold
+Saruman's Big Book of Brutality
+The Shopping List of Barliman
+a Parchment titled ''There And Back Again''
+The Portable Soldier
+The Torch of Isengard
+One Rune to Rule them All
+a Parchment titled ''Famous Last Words''
+The Fire of Orthanc
+Radagast's Compendium of Strange Behaviour
+The Antique Acorn of Watchfulness
+The Horn of Ages
+Gandalf's Portable Pandemonium
+The Perfect Symbiote
+Curunir's Book of Impossible Occurences
+a Parchment titled ''Of the Kinslaying Wars''
+The Altruistic Assassin
+The Pipe of Buckland
+The True Totem of an Ostrich
+The Collapsible Crutch
diff --git a/lib/mods/theme/file/rart_s.txt b/lib/mods/theme/file/rart_s.txt
new file mode 100644
index 00000000..cc84631f
--- /dev/null
+++ b/lib/mods/theme/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 Boulder
+a Voodoo Doll
+a Mirror
+a Black Cube
+a Prism
+a Violet Gem
+a Pentagram
+a Glass Eyeball
+something weird
+a Shard of Pottery
+a Green Tome
+a Black Tome
+a White Tome
+a Pendulum
+a Black Candle
+a Tiny Doll
+a Black Staff
+a Violet Tome
+a Shiny Dagger
+a Gray Tome
+a Broken Stick
+a Parchment
+a Big Bag
+a Small Bag
+a Broken Skull
+a Blue Box
+a Gray Bottle
+an Orange Tome
+a Shiny Gem
+an Ancient Gray Tome
+a Crystal Ball
+a Golden Box
+a Broken Bone
+a Soft Leather Hat
+a Broken Bottle
+an Arcane Parchment
+many Small Wooden Sticks
+some Clay Tablets
+a Smoky Vial
+a Doll
+a Human Skeleton
+a Clay Jar
+an Arcane Parchment
+a Strange Parchment
+a Large Eye
+a Small Eye
+a Decayed Eye
+a Large Crystal Ball
+a Mud-Stained Parchment
+a Medallion
+a Jewel-Encrusted Skull
+a Slimy Mold
+a Battered Book
+a Small Note
+an Old Parchment
+a Small Figurine
+a Torch
+a Rune
+a Singed Parchment
+a Sphere
+a Red Book
+an Acorn
+a Strange Horn
+a Music-Box
+a Blue Mold
+a Torn Book
+a Greasy Parchment
+a Blackened Figurine
+a Pipe
+an Avian Figurine
+a Crutch
+
diff --git a/lib/mods/theme/file/readme! b/lib/mods/theme/file/readme!
new file mode 100644
index 00000000..90b6bbf2
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/rumors.txt b/lib/mods/theme/file/rumors.txt
new file mode 100644
index 00000000..2790cafd
--- /dev/null
+++ b/lib/mods/theme/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.
+Losto Caradhras, sedho, hodo, nuitho i 'ruith!
+Ando Eldarinwa a lasta quettanya, Fenda Casarinwa!
+Andelu i ven.
+Le aphadar aen.
+Nai tiruvantel ar varyuvantel i Valar tielyanna nu vilya.
+Throw a Potion of Blindness at a monster and it cannot cast any spells!
+Gu kibum kelkum-ishi, burzum-ishi. Akha - gum-ishi ashi gurum.
+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 Level 1 is nothing to be proud of.
+There are Black Market stores hidden deep in the dungeon.
+What a pity, you cannot read it!
+You will encounter a dark, tall stranger...
+A Mithril mail will not rust.
+An Galvorn mail cannot be destroyed by the elements.
+A Rusty Chain Mail cannot rust any further.
+Nedin dagor hen ú-'erir ortheri. Natha daged dhaer!
+A Eruchin, u-dano i faelas a hyn an uben tanatha le faelas!
+Tangado a chadad!
+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.
+Faeg i-varv din na lanc a nu ranc!
+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:
+Togo hon dad! Dago hon!
+Za dashu snaku Zigur, Durbgu nazgshu, Durbgu dashshu!
+Forth Eorlingas!
+Try inscribing the name of the first monster killed by it in the weapon!
+I Aear can ven na mar.
+Aiya Earendil Elenion Ancalima!
+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).
+FRODO LIVES!!!
+A Elbereth! Gilthoniel!
+Hail Earendil brightest of the Stars!
+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.
+Havo dad and pass the damn Lembas.
+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".
+The Sea calls us home.
+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!
+Their armour is weak at the neck... and beneath the arm.
+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.
+Prepare to fire!
+Show them no mercy, for you shall receive none.
+Iorhael, lasto beth nin, tolo dan nan galad!
+A Potion of Detonations is also known as nitroglycerin.
+They cannot win this fight. They are all going to die!
+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.
+There-is-no life in-the-cold, in-the-dark. Here - in-the-void only death.
+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.
+May your blood-stained horn fall upon the enemy-heads!
+May the Valar protect you on your path under the sky.
+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...
+Nadath na i moe cerich.
+No animal is interested in sex if it is mortally scared.
+There is much yet you have to do.
+You are being followed.
+There is a plenty of Longswords around 1000'.
+The Road is very dangerous.
+Sleep Caradhras, be still, lie still, hold your wrath!
+The Shadow does not hold sway yet.
+There are often stairways in graveyards: bad people are carried to hell...
+Only a god of Thunder could ride a lightning bolt!
+The world is changed; I can feel it in the water...
+I can feel it in the earth, I can smell it in the air.
+They say the Valar have returned to walk upon Middle-earth.
+Weapons of Flame will light your way.
+Nauthannen i ned ol reniannen.
+Gandalf was here.
+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.
+Ignore the previous rumour.
+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.
+I thought I had strayed into a dream.
+One level further down somebody is getting killed, right now.
+Gate of Elves listen to my word, Threshold of Dwarves!
+Bashing a creature may sometimes stun it.
+Nin o Chithaeglir lasto beth daer; rimmo nin Bruinen dan in Ulaer!
+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!
+You are fated to never die by the hand of a mortal being.
+The Hru hits you. The Hru hits you. The Hru hits you. The Hru hits you.
+-more-
+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.
+You are fated to find something special on Level 99.
+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 is better.
+A Ring of Speed? Phooey! Try looking for a Ring of *Speed*!
+Noro lim, Asfaloth, noro lim!
+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}?
+I amar prestar aen, han mathon ne nen han mathon ne chae a han noston ned 'wilith.
+If I cancel tomorrow the undead will thank me today.
+Hellfire will burn your soul...
+You are fated to die on level 1.
+Why doesn't Detect Monsters show invisible monsters? 'Cos you can't see 'em!
+You are fated to find the Long Sword 'Ringil' on level 5.
+There's slime all over the walls.
+*** LOW HITPOINT WARNING! ***
+You cruelly stab the helpless, sleeping Software bug!
+Tangado haid! Leithio i philinn!
+It looked harmless.
+Spirit, hatch that painted spirit of the lamb sparrow.
+Gone insane from the pain that sure they know: for who the flange sound?
+'Ere, 'oo are you?
+Not to be never, never not to see, so as to dub the thee unforgiven.
+A Morphic Oil of Abomination will turn you into Morgoth himself.
+A Morphic Oil of Abomination will kill you.
+You are fated to win this game.
+The eternal death of eons of the foreigner of the lie can die not absolutely.
+They say that the dark mists of Morgoth can both bestow and remove the curse.
diff --git a/lib/mods/theme/file/sample.txt b/lib/mods/theme/file/sample.txt
new file mode 100644
index 00000000..5ff826f7
--- /dev/null
+++ b/lib/mods/theme/file/sample.txt
@@ -0,0 +1,5 @@
+3
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+line # 1
+line # 2
+line # 3
diff --git a/lib/mods/theme/file/sfail.txt b/lib/mods/theme/file/sfail.txt
new file mode 100644
index 00000000..ca4d9cb5
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/silly.txt b/lib/mods/theme/file/silly.txt
new file mode 100644
index 00000000..c5eca172
--- /dev/null
+++ b/lib/mods/theme/file/silly.txt
@@ -0,0 +1,301 @@
+299
+******************BUFFER LINE -- DO NOT REMOVE!*****************************
+Borg
+little Morgoth
+Sauron
+Ur-Vile
+Wizard of Yendor
+Smurf
+Moose
+Osama bin Laden
+Space Invader
+Agent
+Morpheus
+Hydralisk
+Infested Terran
+Asha'man
+Aes Sedai
+little lamb
+IRA Terrorist
+Nazi
+Klingon
+Tea-kettle
+Vorlon
+Giant flea
+Killer penguin
+Tractor trailer
+Giant were-penguin
+Zergling
+Dark Templar
+Dragon Reborn
+George Bush
+Saddam Hussein
+Death Orb
+Hru
+Greater Titan
+Lesser Titan
+Deodorant
+Minion of DarkGod, the Mighty Coder of Hell
+Mouth of Sauron
+Devil
+Anti-Christ
+Dark Avenger
+Evil Computer
+Evil Genius
+Satan
+Sea Folk Windfinder
+Fire Hydrant
+Multi-hued elephant
+Martian
+Seanchan
+Eminem
+Madonna
+Dementor
+Insurance salesman
+Moldoux, the Defenceless Mold
+Great Wyrm of Power
+Great Wyrm of Balance
+Typewriter
+Great Wyrm of Law
+Great Crystal Drake
+Munchkin
+Mad Scientist
+Great Wyrm of Chaos
+Great Bile Wyrm
+Great Hell Wyrm
+Great Wyrm of Nothing
+Great Wyrm of Toxic Waste
+Batman
+Damane
+Incredible Hulk
+Amazing Spider-Man
+Man in Black
+Matrix agent
+Secret agent
+Spy
+Harry Potter
+Vacuum cleaner
+Lawnmower
+Star-Spawn of Cthulhu
+Killer Banana
+Fear Mold
+Oracle
+Trinity
+Undead beholder
+Beholder Hive-mother
+Gauth
+Dumbledore
+Voldemort
+Maia
+Vala
+Elf
+Human
+Gnome
+Half-Troll
+Pope
+Skywalker cronie
+Jedi knight
+Dark Jedi
+Sith Lord
+Vulcan
+Existentialist philosopher
+Midichlorian
+Scientologist
+Jehovah's witness
+Skinhead
+Feanorian
+Umbarite
+Druj
+Yogi
+Karateka
+President
+King
+Prime Minister
+Trashman
+Teacher
+Cheshire Cat
+Mad Hatter
+March Hare
+Voodoo doll
+Rag doll
+Scarecrow
+Stormcrow
+Stone idol
+Hacker
+Samurai
+Dragkhar
+Phantom of the Opera
+Stone Dog
+Warrior of the Dawn
+Maiden of the Spear
+t-o-m-e.net forum poster
+Chimpanzee
+Messenger of Eru
+Grohlm
+G. I. Joe
+Aiel Wise One
+Alley cat
+Keanuholic
+Battlemage
+Saboteur
+Shadowkiller
+Cyborg
+Lemming
+Green Goblin
+Robocop
+Alien
+Lurker
+Iron Fist
+Trapper
+Amulet of Yendor
+Xeroc
+Catwoman
+Christian
+Muslim
+Extremist
+Forsaken
+Carnivore
+Xenu
+King of Arnor
+False prophet
+Emperor
+Rider of Rohan
+Draco Malfoy
+Cheeky bastard
+Ghost of Frodo Baggins
+Red Rose Knight
+Unholy cow
+Demonic Quylthulg
+Draconic Quylthulg
+Rotting Quylthulg
+Greater Demonic Quylthulg
+Lizard King
+Greater Draconic Quylthulg
+Greater Rotting Quylthulg
+Wookie
+Pit Fiend
+Muad' Dib
+Ent
+Huorn
+Werepotato
+Nycadaemon
+Alien queen
+Greater Balrog
+Lesser Balrog
+Doppleganger
+Evil dead
+Death incarnate
+Greater Kraken
+Death mold
+Rhinoceros
+Leviathan
+Software Bug
+Blob
+Cyberdemon
+Body snatcher
+Ghost of your past
+Biology professor
+Frankenstein
+Brain that would not die
+Breather
+Neekerbreeker
+Creature from the Black Lagoon
+Loch Ness monster
+Creeping death
+Creeping unknown
+Caligula
+Man of Haleth
+Budweiser frog
+Bride of Frankenstein
+Terminator
+T-800
+Purple space chicken
+Simpsons character
+UN weapons inspector
+Green giant
+Noldorin mercenary
+Squint-eyed rogue
+Egomaniac
+Russian mafia
+Godfather
+UFO
+Superman
+Hound of the Baskervilles
+Evil wizard
+Streetcar
+Karaoke machine
+Arch-vile
+Pain elemental
+Lesser kraken
+Murk dweller
+Killer tomato
+Chaos beastman
+Random Number Generator
+RNG
+Yakuza
+Wolverine
+Hunchback of Notre Dame
+Lovecraftian nightmare
+Shakespeare's imitator
+Morlock
+King Kong
+Protoss probe
+Shai'tan
+Baba Yaga
+Saucepan
+Ethereal dragon
+Pseudo-dragon
+Floating brain of Hitler
+Marshmallow Man
+High-elven ranger
+Swamp Thing
+Thing
+Teenage Frankenstein
+Teenage Werewolf
+Cellular automaton
+Strawman
+Logic gate
+Xenophobe
+Unspeakable horror
+Unknown terror
+Spirit of Roger Wilco
+Long sword 'Ringil'
+E.T.
+Zoolander
+Intel(R) Pentium processor
+Pac-Man
+Gangster
+Waterboy
+Long sword 'Mormegil'
+Wizard of Oz
+Battlecruiser
+Ultralisk
+Believer
+Heretic
+Loser
+Moron
+Libido
+Super-ego
+Imbecile
+Nebuchadnezzar
+Tomb raider
+Zelda
+Super Mario
+Hammer of Thor
+Wrath of the gods
+Betrayer of Turin
+Traitor of Gondolin
+Balrog
+Lesser Maia
+Greater Maia
+Cheerleader
+Model T-1000 Terminator
+Terminatrix
+Dragonlance
+Ancient multi-hued dragon
+Hillside strangler
+Texas chainsaw
+Pampers commercial
+Marilith
+White Balrog
+Bus driver
diff --git a/lib/mods/theme/file/smeagol.txt b/lib/mods/theme/file/smeagol.txt
new file mode 100644
index 00000000..22cb7038
--- /dev/null
+++ b/lib/mods/theme/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 preciouss, where iss my preciouss?'
+shouts: 'No Master Hobbitsisisisis!'
+cries: 'The ring was ours for agesisisisis!'
+says: 'Smeagol sneaking! ME! Shneeeakingsisis!'
+screams: 'Nassty Hobbitsisisisis...'
+says: 'Come on, quickly, follow Smeagol!'
+coughs: 'Gollum! Gollum!'
+says: 'Every way is guarded, silly foolsis!'
+says: 'Nassty Bagginsis, stole my precious.'
+says: 'She will kill them, oh yes she will preciouss.'
+says: 'Does it guess easy? It must have a competition with us, preciouss!'
+hisses: 'What has it got in its pocketses?'
+whimpers: 'We've lost it, we have.'
+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/mods/theme/file/smeagolr.txt b/lib/mods/theme/file/smeagolr.txt
new file mode 100644
index 00000000..cc13c96f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/speakpet.txt b/lib/mods/theme/file/speakpet.txt
new file mode 100644
index 00000000..c6707e45
--- /dev/null
+++ b/lib/mods/theme/file/speakpet.txt
@@ -0,0 +1,53 @@
+51
+******** BUFFER LINE *********************************** DO NOT REMOVE *******
+admonishes: 'You need not be frightened like a rabbit.'
+says: 'Nice boots.'
+says: '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.'
+wonders: 'What is all this uproar in the forest tonight?'
+says: 'Hullo! You came pretty quick - where were you hiding?'
+says: 'Havo dad, and pass the damn Lembas.'
+says: 'Go on, go on! I will do the stinging!'
+says: 'The Eagles! The Eagles! The Eagles are coming!'
+says: 'If ever you are passing my way, do not wait to knock.'
+wonders aloud: 'Have I read about you in the ToME forums?'
+sings: 'Merry is May-time, and merry our meeting.'
+says: 'Hey, where did you find that thing?'
+muses: 'By the light of the Trees of Valinor, what IS that thing?'
+yells: 'After Morgoth to the ends of the earth!'
+says: 'Oops. I have slime mold on my wisp of vapor.'
+says: 'Show them no mercy, for you shall receive none.'
+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: 'Their armour is weak at the neck... and beneath the arm.'
+says: 'Keep that demon blade away from me.'
+lectures: 'Gil-galad was an Elven king.'
+lectures: 'Earendil was a mariner that tarried in Arvernien.'
+says: 'The One Ring. That sure is a silly name...'
+says: 'Meddle not in the affairs of Wizards.'
+says: 'If Lokkak asks, I am not here.'
+sings: 'Together we will take the road beneath the bitter rain.'
+admonishes: 'What is with all this Dwarvish racket?'
+says: 'This is one for the Day in the Life archives!'
+recites: 'Seven stars and seven stones, and one white tree.'
+recites: 'All that is gold does not glitter.'
+says: 'Very fancy. Bet you cannot do it again.'
+says: '10AU says I can behead that snaga!'
+recites: 'Not all those who wander are lost.'
+sings: 'With a ping and a pong the fiddle-strings broke!'
+says: 'Were you looking for these?'
+sings: '...the cow jumped over the Moon'
+sings: 'Now let the fun begin! Let us sing together!'
+says: 'Kill, kill, kill. Let us play chess.'
+hums a song peppered with derry-dols and merry-dols.
+says: 'ToME just switched to an HMO.'
+says: 'Do you like my outfit?'
+says: 'Your shoelaces are untied.'
+asks: 'What did you go near them for?'
+says: 'You should post about that on the ToME forums.'
+says: 'A wand of rockets. Yeah, that's the ticket.'
+says: 'Where did everybody go?'
diff --git a/lib/mods/theme/file/timefun.txt b/lib/mods/theme/file/timefun.txt
new file mode 100644
index 00000000..ca3f642f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/file/timenorm.txt b/lib/mods/theme/file/timenorm.txt
new file mode 100644
index 00000000..611da496
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/ability.txt b/lib/mods/theme/help/ability.txt
new file mode 100644
index 00000000..17d89dbc
--- /dev/null
+++ b/lib/mods/theme/help/ability.txt
@@ -0,0 +1,113 @@
+|||||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*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
+~~~~~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/mods/theme/help/advanced.hlp b/lib/mods/theme/help/advanced.hlp
new file mode 100644
index 00000000..8595712b
--- /dev/null
+++ b/lib/mods/theme/help/advanced.hlp
@@ -0,0 +1,14 @@
+|||||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]
+ *****/ddebug.txt*0[(d) Debug commands]
+ *****/eversion.txt*0[(e) Version information] A history of ToME's roots
+
+ *****/zhelp.hlp*0[(z) Main Help menu]
+
diff --git a/lib/mods/theme/help/attack.txt b/lib/mods/theme/help/attack.txt
new file mode 100644
index 00000000..c72e9725
--- /dev/null
+++ b/lib/mods/theme/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.
+ 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/mods/theme/help/automat.txt b/lib/mods/theme/help/automat.txt
new file mode 100644
index 00000000..0ba56d19
--- /dev/null
+++ b/lib/mods/theme/help/automat.txt
@@ -0,0 +1,503 @@
+|||||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, 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/mods/theme/help/birth.txt b/lib/mods/theme/help/birth.txt
new file mode 100644
index 00000000..9e7da8dd
--- /dev/null
+++ b/lib/mods/theme/help/birth.txt
@@ -0,0 +1,659 @@
+|||||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).
+These stats will increase automatically every 5 levels, until the limit for
+each statistic has been reached or until you reach level 50 (whichever
+comes first.) Note that this automatic gain is given to
+a character only once, so if you restart a character from an old savefile,
+he will be considered a descendant of the original character and will not start
+gaining stats every 5 levels until he reaches the point where the original
+character died.
+
+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_dragon.txt*0[Dragon] *****r_hielf.txt*0[High-Elf] *****r_rohank.txt*0[Rohan Knight]
+ *****r_dunad.txt*0[Dunadan] *****r_hobbit.txt*0[Hobbit] *****r_eagle.txt*0[Eagle]
+ *****r_dwarf.txt*0[Dwarf] *****r_human.txt*0[Human] *****r_troll.txt*0[Troll]
+ *****r_elf.txt*0[Elf] *****r_druadan.txt*0[Druadan] *****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] *****r_demon.txt*0[Demon]
+~~~~~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_zomb.txt*0[Zombie] *****rm_vamp.txt*0[Vampire]
+
+Dragon subraces:
+
+ *****rm_red.txt*0[Red] *****rm_blue.txt*0[Blue]
+ *****rm_black.txt*0[Black] *****rm_white.txt*0[White]
+ *****rm_green.txt*0[Green] *****rm_ether.txt*0[Ethereal]
+
+Demon subraces:
+
+ *****rm_narrog.txt*0[Narrog] *****rm_drarog.txt*0[Draugrog]
+ *****rm_aewrog.txt*0[Aewrog] *****rm_lygrog.txt*0[Lygrog]
+ *****rm_hurog.txt*0[Hurog] *****rm_limrog.txt*0[Limrog]
+ *****rm_sarnrog.txt*0[Sarnrog] *****rm_rawrog.txt*0[Rawrog]
+ *****rm_cabrog.txt*0[Caborrog] *****rm_adanrog.txt*0[Adanrog]
+
+~~~~~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_archer.txt*0[Archer] *****c_mimic.txt*0[Mimic] *****c_rogue.txt*0[Rogue]
+ *****c_ascet.txt*0[Ascetic] *****c_mindcr.txt*0[Mindcrafter] *****c_runecr.txt*0[Runecrafter]
+ *****c_assass.txt*0[Assassin] *****c_monk.txt*0[Monk] *****c_sorcer.txt*0[Sorceror]
+ *****c_axemas.txt*0[Axemaster] *****c_necro.txt*0[Necromancer] *****c_stonewr.txt*0[Stonewright]
+ *****c_bard.txt*0[Bard] *****c_palad.txt*0[Paladin] *****c_swordm.txt*0[Swordmaster]
+ *****c_clairv.txt*0[Clairvoyant] *****c_peacemag.txt*0[Peace-mage] *****c_symbia.txt*0[Symbiant]
+ *****c_pr_drk.txt*0[Dark-Priest] *****c_polear.txt*0[Polearmmaster] *****c_thaum.txt*0[Thaumaturgist]
+ *****c_demono.txt*0[Demonologist] *****c_posses.txt*0[Possessor] *****c_trapper.txt*0[Trapper]
+ *****c_druid.txt*0[Druid] *****c_pr_eru.txt*0[Priest(Eru)] *****c_unbel.txt*0[Unbeliever]
+ *****c_geoman.txt*0[Geomancer] *****c_pr_mand.txt*0[Priest(Mandos)] *****c_wainrid.txt*0[Wainrider]
+ *****c_hafted.txt*0[Haftedmaster] *****c_pr_man.txt*0[Priest(Manwe)] *****c_warper.txt*0[Warper]
+ *****c_lorema.txt*0[Loremaster] *****c_pr_ulmo.txt*0[Priest(Ulmo)] *****c_warmage.txt*0[War-mage]
+ *****c_mage.txt*0[Mage] *****c_pr_varda.txt*0[Priest(Varda)] *****c_warrio.txt*0[Warrior]
+ *****c_mercen.txt*0[Mercenary] *****c_ranger.txt*0[Ranger]
+
+#####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_tulkas.txt*0[Tulkas]
+ *****g_manwe.txt*0[Manwe Sulimo] *****g_aule.txt*0[Aule]
+ *****g_varda.txt*0[Varda Elentari] *****g_ulmo.txt*0[Ulmo]
+ *****g_yavann.txt*0[Yavanna Kementari] *****g_mandos.txt*0[Mandos]
+ *****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
+Dragon No No No Yes Yes Yes
+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
+Druadan Yes Yes Yes No No Yes
+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
+Eagle No No No Yes Yes Yes
+Troll Yes No No No No No
+Wood Elf Yes Yes No Yes Yes Yes
+Yeek Yes Yes Yes Yes Yes Yes
+Easterling Yes Yes No No No No
+Demon 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%
+ Dragon +3 +2 +2 -2 +2 -5 9 +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%
+ Druadan -2 -3 +2 +3 +2 -2 9 +15%
+ 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%
+ Eagle +6 +2 +1 -2 +3 +6 12 +200%
+ Troll +4 -4 -2 -4 +3 -6 12 +37%
+ Wood Elf +2 +2 -3 +5 0 +1 7 +30%
+ Yeek -5 -5 -5 -5 -5 -5 6 -75%
+ Easterling +2 -2 -2 -2 +2 -1 10 +40%
+ Demon 0 0 0 0 0 -1 10 +70%
+~~~~~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 +1 +1 +2 -3 -6 -4 +80%
+ Vampire +3 +2 -3 -2 +1 -4 +1 +100%
+ Zombie +2 -6 -6 +1 +4 -5 +3 +45%
+
+
+#####R Dragons:
+ Red +3 0 0 0 0 0 +0 +0%
+ Black 0 0 0 0 0 +3 +0 +0%
+ Green 0 0 0 0 +3 0 +0 +0%
+ Blue 0 0 0 +3 0 0 +0 +0%
+ White 0 +3 0 0 0 0 +0 +0%
+ Ethereal 0 0 +3 0 0 0 +0 +0%
+
+#####R Demons:
+ (Narrog) -1 +1 +1 +1 -1 -2 +1 +20%
+ (Aewrog) -2 0 0 0 +3 0 +1 +0%
+ (Hurog) 0 0 0 0 0 -1 +0 -10%
+ (Sarnrog) +2 -1 -2 -2 +2 -1 +2 +20%
+ (Caborrog) -1 +2 +1 0 0 -3 +2 +10%
+ (Draugrog) +1 +1 +1 +1 +1 -1 +0 -20%
+ (Lygrog) -3 +5 +5 +5 -1 -6 +2 +40%
+ (Limrog) -2 +1 +1 +3 -1 -1 +1 +50%
+ (Rawrog) +2 +1 +1 -1 +2 +1 +2 +30%
+ (Adanrog) +1 +1 +1 +1 +1 +1 +3 +50%
+
+~~~~~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
+ Wainrider +5 -2 -2 +2 +2 -1
+
+ Clairvoyant -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
+ Sniper +2 +2 0 +2 +1 +1
+
+ Assassin +2 +1 -2 +3 +1 -1
+ Rogue +2 +1 -2 +3 +1 -1
+ Mercenary +2 +1 -2 +3 +1 -1
+
+ Ascetic +1 -2 +1 +1 0 +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(Mandos)-1 -3 +3 -1 0 +2
+ Priest(Manwe) -1 -3 +3 -1 0 +2
+ Priest(Ulmo) -1 -3 +3 -1 0 +2
+ Priest(Varda) -1 -3 +3 -1 0 +2
+ Stonewright -1 -3 +3 -1 0 +2
+
+ Trapper 0 +2 +2 +2 0 +4
+ Peace-mage 0 +2 +2 +2 0 +4
+
+
+~~~~~10|Character|Ability tables
+~~~~~74|Tables|Ability Tables
+#####R=== Ability Tables ===
+
+~~~~~78|Races|Ability table
+#####GRaces:
+#####B Dsrm Dvce Save Stlh Srch Prcp HtH Misl Infra
+ Beorning -6 -8 -6 -2 -1 +5 +25 +5 30 feet
+ Dark Elf +5 +15 +20 +3 +8 +12 -5 +10 50 feet
+ Dragon +5 +5 +5 -10 +5 +5 +5 -20 50 feet
+ Dunadan +4 +5 +5 +2 +3 +13 +15 +10 None
+ Dwarf +2 +9 +10 -1 +7 +10 +15 +0 50 feet
+ Elf +5 +6 +6 +2 +8 +12 -5 +15 30 feet
+ Ent +5 +5 +20 -6 +5 +4 +15 +5 50 feet
+ Gnome +10 +12 +12 +3 +6 +13 -8 +12 40 feet
+ Half-Elf +2 +3 +3 +1 +6 +11 -1 +5 20 feet
+ Half-Ogre -3 -5 -5 -2 -1 +5 +20 +0 30 feet
+ High-Elf +4 +20 +20 +4 +3 +14 +10 +25 40 feet
+ Hobbit +15 +18 +18 +5 +12 +15 -10 +20 40 feet
+ Human +0 +0 +0 +0 +0 +10 +0 +0 None
+ Druadan +5 +0 +0 +5 +15 +15 +0 +5 30 feet
+ Maia +0 +0 +0 +0 +0 +10 +0 +0 None
+ Orc -3 -3 -3 -1 +0 +7 +12 -5 30 feet
+ Petty Dwarf +3 +5 +10 +1 +5 +10 +9 +0 50 feet
+ RohanKnight +10 +5 +5 -8 +1 +1 +5 +5 None
+ Eagle +6 0 +10 -16 +30 +10 + 0 +0 50 feet
+ Troll -5 -8 -8 -2 -1 +5 +20 -10 30 feet
+ Wood Elf +5 +6 +6 +5 +8 +12 -5 +40 40 feet
+ Yeek -5 -5 -10 +0 -5 +0 -10 -10 20 feet
+ Easterling +0 -5 -1 +0 +0 +10 +5 +5 None
+ Demon +0 +0 +0 +0 +0 +0 +0 +0 30 feet
+~~~~~79|Race Modifiers|Ability table
+#####GRace Modifiers:
+#####B Dsrm Dvce Save Stlh Srch Prcp HtH Misl Infra
+ Classical +0 +0 +0 +0 +0 +0 +0 +0 +0 feet
+ Barbarian -2 -10 +2 -2 +0 +1 +12 +5 +0 feet
+ Hermit +5 +10 +5 +3 +4 +10 -5 -5 +10 feet
+ Lost Soul +0 +0 +0 +0 +0 +0 +0 +0 +0 feet
+ Skeleton -5 -5 +5 -1 -1 +8 +8 +0 +10 feet
+ Spectre +2 +8 +7 +2 +2 +7 -5 -2 +30 feet
+ Vampire +0 +0 +0 +0 +0 +0 +0 +0 +0 feet
+ Zombie -2 -2 +5 -1 -1 +2 +5 +0 +10 feet
+
+#####R Dragons:
+ Red +0 +0 +0 -2 +2 +0 +5 +0 +10 feet
+ Black +2 +0 +1 +0 +0 +0 +0 +0 +0 feet
+ Green +0 +0 +2 +0 +0 +0 +0 +0 +0 feet
+ Blue +3 +0 +2 +1 +0 +0 +0 +0 +10 feet
+ White +0 +2 +0 +0 +0 +0 +0 +0 +0 feet
+ Ethereal +0 +0 +1 +5 +2 +2 +0 +0 +30 feet
+
+#####R Demons:
+ (Narrog) +2 +0 +1 +2 +2 +20 +0 +0 +10 feet
+ (Aewrog) +0 +0 +1 -2 +0 +10 +0 +0 +10 feet
+ (Hurog) +2 +0 +2 +0 +5 +30 +0 +0 +20 feet
+ (Sarnrog) -1 +0 +0 -8 -2 -10 +5 +0 +0 feet
+ (Caborrog) +0 +0 +1 -10 +2 +10 +0 +0 +0 feet
+ (Draugrog) +2 +0 +2 -20 +5 +30 +0 +0 +20 feet
+ (Lygrog) +5 +5 +5 +10 +5 +50 +0 +0 +30 feet
+ (Limrog) +2 +2 +2 +2 +2 +30 +0 +0 +20 feet
+ (Rawrog) +0 +0 +4 -3 +0 +10 +10 +10 +0 feet
+ (Adanrog) +1 +1 +1 +1 +1 +20 +15 +10 +20 feet \ No newline at end of file
diff --git a/lib/mods/theme/help/c_archer.txt b/lib/mods/theme/help/c_archer.txt
new file mode 100644
index 00000000..a115e6f2
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_ascet.txt b/lib/mods/theme/help/c_ascet.txt
new file mode 100644
index 00000000..cc2a2ab4
--- /dev/null
+++ b/lib/mods/theme/help/c_ascet.txt
@@ -0,0 +1,46 @@
+~~~~~01|Ascetic
+~~~~~02|Classes|Ascetic
+#####R=== Ascetics ===
+
+#####GDescription
+An Ascetic is a monk who has forsaken all worldly
+things, including deity worship and magic. Ascetics
+must survive by their fists and their natural
+ability to resist magic. Despite not worshipping any
+gods, Ascetics are highly spiritual, and can achieve
+excellent saving throws in addition to their magic-
+repelling abilities.
+
+#####GStarting Stat Modifiers
+Strength +1
+Intelligence -2
+Wisdom +1
+Dexterity +1
+Constitution +0
+Charisma +1
+Bonus Blows 0
+Hit Die +d8
+Exp Penalty 40%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.900]
+ Archery 1.000 [0.700]
+ Barehand-combat 1.000 [0.900]
+ Antimagic 1.000 [1.000]
+Sneakiness 1.000 [0.700]
+ Stealth 1.000 [1.000]
+ Disarming 1.000 [0.900]
+ Dodging 0.000 [1.000]
+Spirituality 1.000 [0.700]
+Monster-lore 1.000 [1.100]
+ Corpse-preservation 1.000 [0.700]
+
+#####GStarting Equipment
+An ascetic begins the game with:
+ a potion of healing
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 1
+Extra Max Blow(1) 5 \ No newline at end of file
diff --git a/lib/mods/theme/help/c_assass.txt b/lib/mods/theme/help/c_assass.txt
new file mode 100644
index 00000000..4269e3e8
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_axemas.txt b/lib/mods/theme/help/c_axemas.txt
new file mode 100644
index 00000000..cdd6ba88
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_bard.txt b/lib/mods/theme/help/c_bard.txt
new file mode 100644
index 00000000..23ba42ca
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_clairv.txt b/lib/mods/theme/help/c_clairv.txt
new file mode 100644
index 00000000..7d537486
--- /dev/null
+++ b/lib/mods/theme/help/c_clairv.txt
@@ -0,0 +1,47 @@
+~~~~~01|Clairvoyant
+~~~~~02|Classes|Clairvoyant
+#####R=== Clairvoyants ===
+
+#####GDescription
+Clairvoyants are masters of the mind, they are
+especially proficient at Mindcraft, Divination,
+and the Mind school of magic. They sacrifice
+breadth of ability for additional favour with
+their deity, so it makes sense to pick a deity
+to worship when playing a clairvoyant character.
+It is not, however, a requirement.
+
+#####GStarting Stat Modifiers
+Strength -5
+Intelligence +3
+Wisdom +0
+Dexterity +1
+Constitution -2
+Charisma +1
+Bonus Blows 0
+Hit Die +d0
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.300]
+ Weaponmastery 0.700 [0.500]
+Sneakiness 1.000 [0.900]
+Magic 1.000 [0.900]
+ Magic-Device 1.000 [1.200]
+ Spell-power 0.000 [0.600]
+ Divination 1.000 [1.000]
+ Mind 1.000 [1.000]
+Spirituality 1.000 [1.000]
+ Prayer 0.000 [1.000]
+ Mindcraft 1.000 [0.700]
+Monster-lore 0.000 [0.500]
+
+#####GStarting Equipment
+A clairvoyant begins the game with:
+ A book of Beginner Cantrips
+ A quarterstaff
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 1 \ No newline at end of file
diff --git a/lib/mods/theme/help/c_demono.txt b/lib/mods/theme/help/c_demono.txt
new file mode 100644
index 00000000..98b0bc9b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_druid.txt b/lib/mods/theme/help/c_druid.txt
new file mode 100644
index 00000000..0b0493e2
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_geoman.txt b/lib/mods/theme/help/c_geoman.txt
new file mode 100644
index 00000000..47855875
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_hafted.txt b/lib/mods/theme/help/c_hafted.txt
new file mode 100644
index 00000000..7e8a3f89
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_lorema.txt b/lib/mods/theme/help/c_lorema.txt
new file mode 100644
index 00000000..35c2093b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_mage.txt b/lib/mods/theme/help/c_mage.txt
new file mode 100644
index 00000000..949d3bcc
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_mercen.txt b/lib/mods/theme/help/c_mercen.txt
new file mode 100644
index 00000000..75129d56
--- /dev/null
+++ b/lib/mods/theme/help/c_mercen.txt
@@ -0,0 +1,49 @@
+~~~~~01|Mercenary
+~~~~~02|Classes|Mercenary
+#####R=== Mercenaries ===
+
+#####GDescription
+Mercenaries are daring swashbucklers, masters of the blade. They
+do not have the magical abilities Rogues have, but they are just
+as stealthy and much deadlier with a light sword. They do gain
+some magical mastery over time, but only in the Temporal school.
+
+#####GStarting Stat Modifiers
+Strength +2
+Intelligence +1
+Wisdom -2
+Dexterity +3
+Constitution +1
+Charisma -1
+Bonus Blows 0
+Hit Die +d6
+Exp Penalty 25%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.700]
+ Weaponmastery 1.000 [0.900]
+ Sword-mastery 1.000 [0.700]
+ Critical-hits 1.000 [0.700]
+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]
+ Temporal 0.000 [1.000]
+Spirituality 1.000 [0.700]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GStarting Equipment
+A mercenary begins the game with:
+ a rapier
+ a cloak
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra-Max-Blow(1) 10
+Extra-Max-Blow(1) 15 \ No newline at end of file
diff --git a/lib/mods/theme/help/c_mimic.txt b/lib/mods/theme/help/c_mimic.txt
new file mode 100644
index 00000000..b9378a03
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_mindcr.txt b/lib/mods/theme/help/c_mindcr.txt
new file mode 100644
index 00000000..c4ed2747
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_monk.txt b/lib/mods/theme/help/c_monk.txt
new file mode 100644
index 00000000..87730f18
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_necro.txt b/lib/mods/theme/help/c_necro.txt
new file mode 100644
index 00000000..f3a5ad2c
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_pacif.txt b/lib/mods/theme/help/c_pacif.txt
new file mode 100644
index 00000000..7ec7e7d1
--- /dev/null
+++ b/lib/mods/theme/help/c_pacif.txt
@@ -0,0 +1,10 @@
+~~~~~01|Pacifist
+~~~~~02|Classes|Pacifist
+#####R=== Pacifists ===
+
+#####GDescription
+A Pacifist is someone who prefers not to use violence to
+achieve their ends. There are two types of Pacifists:
+
+*****c_peacemag.txt*0[Peace-mage]
+*****c_trapper.txt*0[Trapper] \ No newline at end of file
diff --git a/lib/mods/theme/help/c_palad.txt b/lib/mods/theme/help/c_palad.txt
new file mode 100644
index 00000000..b4cc650b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_peacemag.txt b/lib/mods/theme/help/c_peacemag.txt
new file mode 100644
index 00000000..f75664f3
--- /dev/null
+++ b/lib/mods/theme/help/c_peacemag.txt
@@ -0,0 +1,46 @@
+~~~~~01|Peace-mage
+~~~~~02|Classes|Peace-mage
+#####R=== Peace-mages ===
+
+#####GDescription
+A Pacifist is someone who prefers not to use violence to
+achieve their ends. The peace-mages choose to use defensive
+spells and rely on symbiosis to protect themselves. They
+may also use spells provided by their deities.
+
+#####GStarting Stat Modifiers
+Strength 0
+Intelligence +2
+Wisdom +2
+Dexterity +2
+Constitution 0
+Charisma +4
+Bonus Blows 0
+Hit Die 0
+Exp Penalty 0%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Sneakiness 0.000 [0.600]
+ Stealth 0.000 [0.600]
+ Disarming 1.000 [0.600]
+ Dodging 1.000 [0.700]
+Magic 1.000 [0.600]
+ Spell-power 0.000 [0.500]
+ Meta 0.000 [0.700]
+ Conveyance 1.000 [0.700]
+ Divination 0.000 [0.700]
+ Temporal 0.000 [0.700]
+ Nature 1.000 [1.000]
+Spirituality 0.000 [0.700]
+ Prayer 0.000 [0.500]
+Monster-lore 1.000 [0.900]
+ Symbiosis 1.000 [0.600]
+
+#####GStarting Equipment
+A peace-mage begins the game with:
+ a scroll of summon never-moving pet
+
+#####GInnate Abilities:
+#####BAbility Character level
+Perfect casting 15 \ No newline at end of file
diff --git a/lib/mods/theme/help/c_polear.txt b/lib/mods/theme/help/c_polear.txt
new file mode 100644
index 00000000..8ea2f04a
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_posses.txt b/lib/mods/theme/help/c_posses.txt
new file mode 100644
index 00000000..2d67a883
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_pr_drk.txt b/lib/mods/theme/help/c_pr_drk.txt
new file mode 100644
index 00000000..fa99f89f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_pr_eru.txt b/lib/mods/theme/help/c_pr_eru.txt
new file mode 100644
index 00000000..ff5a0126
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_pr_man.txt b/lib/mods/theme/help/c_pr_man.txt
new file mode 100644
index 00000000..3f83e8af
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_pr_mand.txt b/lib/mods/theme/help/c_pr_mand.txt
new file mode 100644
index 00000000..df98e911
--- /dev/null
+++ b/lib/mods/theme/help/c_pr_mand.txt
@@ -0,0 +1,49 @@
+|||||oy
+~~~~~01|Priest - Mandos
+~~~~~02|Classes|Priest - Mandos
+~~~~~03|Mandos|Priest - Mandos
+#####R=== Priests of Mandos ===
+
+#####GDescription
+*****g_mandos.txt*0[Mandos] is the Doomsman of the Valar, Lord of
+the House of the Dead and he recalls the souls of the fallen ones.
+A Master of Spirit, he knows all that is going to happen, except
+that which lies in the will of Eru himself. Manwe calls upon Mandos
+to pass judgment upon the Ainur and Eru's children alike. The priests
+of Mandos fight righteously to rid the world of evil.
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Bonus Blows 0
+Hit Die +d2
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+ Hafted-mastery 0.000 [0.400]
+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]
+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 Varda begins the game with:
+ A spellbook of Tears of Luthien
+ A mace \ No newline at end of file
diff --git a/lib/mods/theme/help/c_pr_ulmo.txt b/lib/mods/theme/help/c_pr_ulmo.txt
new file mode 100644
index 00000000..6f82b368
--- /dev/null
+++ b/lib/mods/theme/help/c_pr_ulmo.txt
@@ -0,0 +1,50 @@
+|||||oy
+~~~~~01|Priest - Ulmo
+~~~~~02|Classes|Priest - Ulmo
+~~~~~03|Ulmo|Priest - Ulmo
+#####R=== Ulmo's Priests ===
+
+#####GDescription
+*****g_ulmo.txt*0[Ulmo] is the Lord of the Waters, second in power only
+to Manwe, and the most wrathful of the Valar after Tulkas Astaldo. He
+made a great contribution to the War of Wrath when he led Tuor to Gondolin,
+and he is ever watchful for the contrivances of Melkor Bauglir. His
+preferred weapon type is the polearm, and his priests learn to become
+very proficient with it. A priest of Ulmo is like unto living
+water, channelled through Ulmo's magic.
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Bonus Blows 0
+Hit Die +d2
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+ Polearm-mastery 0.000 [0.400]
+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]
+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 Ulmo begins the game with:
+ A spellbook of Song of Belegaer
+ A trident \ No newline at end of file
diff --git a/lib/mods/theme/help/c_pr_varda.txt b/lib/mods/theme/help/c_pr_varda.txt
new file mode 100644
index 00000000..f6473df4
--- /dev/null
+++ b/lib/mods/theme/help/c_pr_varda.txt
@@ -0,0 +1,50 @@
+|||||oy
+~~~~~01|Priest - Varda
+~~~~~02|Classes|Priest - Varda
+~~~~~03|Varda|Priest - Varda
+#####R=== Varda's Priests ===
+
+#####GDescription
+*****g_varda.txt*0[Varda] is called Elentari, which means The Queen
+of the Stars. She set the stars in the sky, for which the Eldar of
+Middle-earth revered her, calling her by the name of Elbereth. Her
+priests battle the darkness with light, and also have access to the
+Mana and Meta schools of magic by the grace of Varda. Varda prefers
+it if her servants do not get too close to the Shadow, and thus she
+grants a bonus in Archery to her priests.
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Bonus Blows 0
+Hit Die +d2
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+ Archery 0.000 [0.400]
+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]
+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 Varda begins the game with:
+ A spellbook of Light of Valinor
+ A mace \ No newline at end of file
diff --git a/lib/mods/theme/help/c_priest.txt b/lib/mods/theme/help/c_priest.txt
new file mode 100644
index 00000000..1eb7bcbb
--- /dev/null
+++ b/lib/mods/theme/help/c_priest.txt
@@ -0,0 +1,17 @@
+|||||oy
+~~~~~01|Priests
+#####R=== Priests ===
+
+#####GDescription
+There are 10 separate classes of Priest:
+
+*****c_pr_eru.txt*0[Priest(Eru)]
+*****c_pr_man.txt*0[Priest(Manwe)]
+*****c_pr_ulmo.txt*0[Priest(Ulmo)]
+*****c_pr_mand.txt*0[Priest(Mandos)]
+*****c_pr_varda.txt*0[Priest(Varda)]
+*****c_druid.txt*0[Druid]
+*****c_palad.txt*0[Paladin]
+*****c_stonewr.txt*0[Stonewright]
+*****c_pr_drk.txt*0[Dark-Priest]
+*****c_mindcr.txt*0[Mindcrafter]
diff --git a/lib/mods/theme/help/c_ranger.txt b/lib/mods/theme/help/c_ranger.txt
new file mode 100644
index 00000000..81af7715
--- /dev/null
+++ b/lib/mods/theme/help/c_ranger.txt
@@ -0,0 +1,56 @@
+|||||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 can, however,
+learn the ability to summon aid using totems.
+
+They have access to the schools of *****m_divin.txt*0[Divination] 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.700]
+ 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]
+ 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.900]
+ Summoning 0.000 [0.300]
+
+
+#####GInnate Abilities:
+#####BAbility Character level
+Ammo creation 2
+
+#####GStarting Equipment
+A Ranger begins the game with:
+ a Short Sword
+ a Short Bow
+ some Arrows
diff --git a/lib/mods/theme/help/c_rogue.txt b/lib/mods/theme/help/c_rogue.txt
new file mode 100644
index 00000000..b42df3d5
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_runecr.txt b/lib/mods/theme/help/c_runecr.txt
new file mode 100644
index 00000000..8388eff9
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_sniper.txt b/lib/mods/theme/help/c_sniper.txt
new file mode 100644
index 00000000..008202d8
--- /dev/null
+++ b/lib/mods/theme/help/c_sniper.txt
@@ -0,0 +1,50 @@
+~~~~~01|Sniper
+~~~~~02|Classes|Sniper
+#####R=== Snipers ===
+
+#####GDescription
+Snipers are stealthy archers who kill from a safe
+distance and aren't very good at hand-to-hand combat.
+They have some ability in the Conveyance school of
+magic, which can let them disappear quickly if the
+need should arise.
+
+#####GStarting Stat Modifiers
+Strength +2
+Intelligence +1
+Wisdom +0
+Dexterity +2
+Constitution +1
+Charisma +1
+Bonus Blows 0
+Hit Die +d4
+Exp Penalty 30%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 1.000 [0.500]
+ 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 2.000 [1.000]
+ Stealth 1.000 [0.700]
+ Disarming 1.000 [1.000]
+ Backstab 1.000 [0.700]
+Magic 1.000 [0.600]
+ Magic-Device 1.000 [1.100]
+ Conveyance 1.000 [0.500]
+Spirituality 0.000 [0.500]
+ Prayer 0.000 [0.500]
+Monster-lore 0.000 [0.500]
+
+#####GStarting Equipment
+A sniper begins the game with:
+ a short bow
+ some ammo
+
+#####GInnate Abilities:
+#####BAbility Character level
+Ammo creation 2 \ No newline at end of file
diff --git a/lib/mods/theme/help/c_sorcer.txt b/lib/mods/theme/help/c_sorcer.txt
new file mode 100644
index 00000000..8a33184f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_stonewr.txt b/lib/mods/theme/help/c_stonewr.txt
new file mode 100644
index 00000000..7de1c0ef
--- /dev/null
+++ b/lib/mods/theme/help/c_stonewr.txt
@@ -0,0 +1,50 @@
+|||||oy
+~~~~~01|Stonewright
+~~~~~02|Classes|Stonewright
+~~~~~03|Aule|Stonewright
+#####R=== Aule's Priests ===
+
+#####GDescription
+*****g_aule.txt*0[Aule] the Smith is concerned with the substance of
+Arda; rock and metal. His priests serve him best by forging better
+weapons to use in the fight against the Dark, and his most beloved
+weapon is the axe, which his followers will find much easier to use
+than other types of weapons. Aule's realms are earth and fire, and
+his priests have access to these realms by the grace of their Vala.
+
+#####GStarting Stat Modifiers
+Strength -1
+Intelligence -3
+Wisdom +3
+Dexterity -1
+Constitution +0
+Charisma +2
+Bonus Blows 0
+Hit Die +d2
+Exp Penalty 20%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Combat 2.000 [0.700]
+ Weaponmastery 1.000 [0.700]
+ Axe-mastery 0.000 [0.400]
+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]
+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
+Extra Max Blow(1) 10
+
+#####GStarting Equipment
+A priest serving Aule begins the game with:
+ A spellbook of Firebrand
+ A light war axe \ No newline at end of file
diff --git a/lib/mods/theme/help/c_summon.txt b/lib/mods/theme/help/c_summon.txt
new file mode 100644
index 00000000..a3eca1de
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_swordm.txt b/lib/mods/theme/help/c_swordm.txt
new file mode 100644
index 00000000..a3b5ed05
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_symbia.txt b/lib/mods/theme/help/c_symbia.txt
new file mode 100644
index 00000000..9bbd92fd
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_thaum.txt b/lib/mods/theme/help/c_thaum.txt
new file mode 100644
index 00000000..653e84fa
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_trapper.txt b/lib/mods/theme/help/c_trapper.txt
new file mode 100644
index 00000000..eb0badbe
--- /dev/null
+++ b/lib/mods/theme/help/c_trapper.txt
@@ -0,0 +1,46 @@
+~~~~~01|Trapper
+~~~~~02|Classes|Trapper
+#####R=== Trappers ===
+
+#####GDescription
+A Pacifist is someone who prefers not to use violence to
+achieve their ends. The trappers are not averse, however,
+to destroying monsters via traps and using corpses they
+find to create totems in order to summon aid. They
+may also use spells provided by their deities.
+
+#####GStarting Stat Modifiers
+Strength 0
+Intelligence +2
+Wisdom +2
+Dexterity +2
+Constitution 0
+Charisma +4
+Bonus Blows 0
+Hit Die 0
+Exp Penalty 0%
+
+#####GStarting Skills:
+#####BSkill Start Level Skill Point Gains
+Sneakiness 0.000 [0.600]
+ Stealth 0.000 [0.600]
+ Disarming 1.000 [0.600]
+ Dodging 1.000 [0.700]
+Magic 1.000 [0.600]
+ Spell-power 0.000 [0.500]
+ Nature 1.000 [1.000]
+Spirituality 0.000 [0.700]
+ Prayer 0.000 [0.500]
+Monster-lore 1.000 [0.900]
+ Summoning 1.000 [0.600]
+
+#####GStarting Equipment
+A trapper begins the game with:
+ a catapult trap set
+ some iron shots
+
+#####GInnate Abilities:
+#####BAbility Character level
+Trapping 1
+Ammo creation 10
+Perfect casting 15 \ No newline at end of file
diff --git a/lib/mods/theme/help/c_unbel.txt b/lib/mods/theme/help/c_unbel.txt
new file mode 100644
index 00000000..feec5723
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_wainrid.txt b/lib/mods/theme/help/c_wainrid.txt
new file mode 100644
index 00000000..cf3c3d69
--- /dev/null
+++ b/lib/mods/theme/help/c_wainrid.txt
@@ -0,0 +1,49 @@
+~~~~~01|Wainrider
+~~~~~02|Classes|Wainrider
+#####R=== Wainriders ===
+
+#####GDescription
+Proud warriors riding majestic chariots. They are most effective
+with short, thrusting blades. They are highly spiritual, but their
+deity must be Melkor Bauglir, they do not have a choice when they
+embark upon their adventure. They are better skilled in magic than
+other warrior classes, although their magical ability is limited to
+the Udun school.
+
+#####GStarting Stat Modifiers
+Strength +5
+Intelligence -2
+Wisdom -2
+Dexterity +2
+Constitution +2
+Charisma -1
+Bonus Blows 3
+Hit Die +d9
+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.700]
+ Critical-hits 0.000 [0.500]
+ Archery 1.000 [0.600]
+Sneakiness 1.000 [0.900]
+ Disarming 1.000 [0.900]
+Magic 2.000 [0.800]
+ Magic-Device 1.000 [1.150]
+ Udun 0.000 [0.400]
+Spirituality 1.000 [0.900]
+ Prayer 1.000 [0.700]
+Monster-lore 0.000 [0.500]
+
+#####GStarting Equipment
+A Wainrider begins the game with:
+ a spellbook of Curse
+ a potion of Corruption
+ a Shadow Blade
+
+#####GInnate Abilities:
+#####BAbility Character level
+Extra Max Blow(1) 1
+Extra Max Blow(2) 1 \ No newline at end of file
diff --git a/lib/mods/theme/help/c_warper.txt b/lib/mods/theme/help/c_warper.txt
new file mode 100644
index 00000000..55d16be5
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/c_warrio.txt b/lib/mods/theme/help/c_warrio.txt
new file mode 100644
index 00000000..942b34bb
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/command.txt b/lib/mods/theme/help/command.txt
new file mode 100644
index 00000000..04a63fd8
--- /dev/null
+++ b/lib/mods/theme/help/command.txt
@@ -0,0 +1,1231 @@
+|||||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*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*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".
+ 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.
+~~~~~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 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. 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. 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
+ 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).
+~~~~~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/mods/theme/help/corspoil.txt b/lib/mods/theme/help/corspoil.txt
new file mode 100644
index 00000000..baac4fae
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/debug.txt b/lib/mods/theme/help/debug.txt
new file mode 100644
index 00000000..1fa0efd5
--- /dev/null
+++ b/lib/mods/theme/help/debug.txt
@@ -0,0 +1,277 @@
+|||||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)
+ r (unused) *****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.
+~~~~~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/mods/theme/help/defines.txt b/lib/mods/theme/help/defines.txt
new file mode 100644
index 00000000..147e61a1
--- /dev/null
+++ b/lib/mods/theme/help/defines.txt
@@ -0,0 +1,616 @@
+|||||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 ('!') */
+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
+~~~~~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
+~~~~~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/mods/theme/help/dungeon.txt b/lib/mods/theme/help/dungeon.txt
new file mode 100644
index 00000000..20426e2a
--- /dev/null
+++ b/lib/mods/theme/help/dungeon.txt
@@ -0,0 +1,703 @@
+|||||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
+ & (unused)
+
+~~~~~05|Monsters
+#####G Monsters
+
+ $ Creeping Coins , Mushroom Patch
+ a Giant Ant A Maia/Vala
+ b Giant Bat B Bird
+ c Cattle 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 Mewlip I Insect
+ j Jelly J Snake
+ k Dwarf 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 Strange Humanoid
+ 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.
+
+
+~~~~~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 Guldur 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/mods/theme/help/dunspoil.txt b/lib/mods/theme/help/dunspoil.txt
new file mode 100644
index 00000000..2da6d6b7
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/experien.hlp b/lib/mods/theme/help/experien.hlp
new file mode 100644
index 00000000..5e0bb9eb
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/explore.hlp b/lib/mods/theme/help/explore.hlp
new file mode 100644
index 00000000..0c302ab8
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/fatespoi.txt b/lib/mods/theme/help/fatespoi.txt
new file mode 100644
index 00000000..2815129e
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/foot.aux b/lib/mods/theme/help/foot.aux
new file mode 100644
index 00000000..47328799
--- /dev/null
+++ b/lib/mods/theme/help/foot.aux
@@ -0,0 +1,4 @@
+</TT></PRE>
+</FONT>
+</body>
+</html>
diff --git a/lib/mods/theme/help/g_aule.txt b/lib/mods/theme/help/g_aule.txt
new file mode 100644
index 00000000..89399179
--- /dev/null
+++ b/lib/mods/theme/help/g_aule.txt
@@ -0,0 +1,61 @@
+|||||oy
+~~~~~01|Gods|Aule
+~~~~~02|Aule
+#####R === Aule the Smith ===
+
+Aule the Smith is the inventor and smith of the Valar.
+His most faithful followers are those of the class *****c_stonewr.txt*0[Stonewright].
+
+#####GThe benefits of worshipping Aule the Smith
+1. As you increase your piety, Aule will grant you a boost to your accuracy
+ and damage to a maximum of +5.
+2. As your piety increases, he will grant you resistance to fire.
+3. If you are praying to him at the time, there is a chance that he will
+ cast Stone Skin on you.
+4. Aule likes Dwarves, Petty-dwarves, Gnomes, and Dark Elves.
+4. Your piety will increase slightly more if you are wielding an axe or a hammer.
+
+#####GThe disadvantages of worshipping Aule the Smith
+1. He doesn't like it if you kill dwarves of any kind.
+2. Your piety will decrease over time unless you are a Dwarf, Petty-dwarf,
+ Gnome, or Dark-Elf.
+3. He will completely abandon you if you wear The One Ring.
+~~~~~~03|Aule|Prayers
+#####GAule's Magic
+Worshipping Aule the Smith gives the adventurer access to a set of special
+spells that come directly from the hands of Aule. These spells use your piety
+to cast rather than your spellpoints, and the level of spells that Aule 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 "The Earth Tome of Aule" which
+contains instructions for the procedure for each of the prayers Eru will
+grant. There are four prayers all told, which are:
+1. [[[[[BFirebrand] (Level 1)
+ Imbues your melee weapon with fire to deal more damage.
+ At level 15 it spreads over a 1 radius zone around your target.
+ At level 30 it deals holy fire damage.
+2. [[[[[BEnchant Weapon] (Level 10)
+ Tries to enchant a weapon to-hit.
+ At level 5 it also enchants to-dam.
+ At level 45 it enhances the special powers of magical weapons.
+ The might of the enchantment increases with level.
+3. [[[[[BEnchant Armour] (Level 15)
+ Tries to enchant a piece of armour.
+ At level 20 it also enchants to-hit and to-dam.
+ At level 40 it enhances the special powers of magical armour
+ The might of the enchantment increases with level.
+4. [[[[[BChild of Aule] (Level 20)
+ Summons a levelled Dwarven warrior 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 his specific magic, Aule 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/3 the Prayer skill level.
+ *****m_fire.txt*0[Fire School] at 3/5 the Prayer skill level.
+The spells from these schools are all cast using your normal spellpoints.
diff --git a/lib/mods/theme/help/g_eru.txt b/lib/mods/theme/help/g_eru.txt
new file mode 100644
index 00000000..113875b3
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/g_mandos.txt b/lib/mods/theme/help/g_mandos.txt
new file mode 100644
index 00000000..5e21553c
--- /dev/null
+++ b/lib/mods/theme/help/g_mandos.txt
@@ -0,0 +1,56 @@
+|||||oy
+~~~~~01|Gods|Mandos
+~~~~~02|Mandos
+#####R === Mandos ===
+
+Mandos is the Doomsman of the Valar, rarely seen but much revered. His most faithful
+followers are those of the class *****c_pr_mand.txt*0[Priest(Mandos)].
+
+#####GThe benefits of worshipping Mandos
+1. His followers are granted resistance to nether forces.
+2. If your piety is sufficiently high, and you are praying to him, Mandos will prevent
+ the space-time continuum from being disrupted around you.
+3. If you are very pious and praying, Mandos will grant you immunity to nether.
+4. He likes it if you kill vampires.
+5. He adores it if you kill vampire elves.
+6. Your piety will slowly increase over time if you are not praying.
+7. Mandos likes High Elves and Lost Souls.
+
+#####GThe disadvantages of worshipping Mandos
+1. Mandos doesn't like Vampires and Demons.
+2. He hates being disturbed, so praying will make you lose piety very quickly.
+3. He hates it if you kill living elves of any kind.
+4. He *hates* it if you kill elves who are friendly to you.
+5. He absolutely detests it if you kill friendly spirits.
+6. He will completely abandon you if you wear The One Ring.
+~~~~~~03|Mandos|Prayers
+#####GMandos' Magic
+Worshipping Mandos gives the adventurer access to a set of special spells that come
+directly from the hands of Mandos. These spells use your piety to cast rather than
+your spellpoints, and the level of spells that Mandos 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 Mandos" which
+contains instructions for the procedure for each of the prayers Mandos will
+grant. There are four prayers all told, which are:
+1. [[[[[BTears of Luthien] (Level 5)
+ Calls upon the spirit of Luthien to ask Mandos for healing and succour.
+2. [[[[[BFeanturi] (Level 10)
+ Channels the power of Mandos to cure fear and confusion.
+ At level 20 it restores lost INT and WIS
+ At level 30 it cures hallucinations and restores a percentage of lost sanity.
+3. [[[[[BTale of Doom] (Level 25)
+ Allows you to predict the future for a short time.
+4. [[[[[BCall to the Halls] (Level 30)
+ Summons a leveled spirit from the Halls of Mandos to fight for 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, Mandos 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_divin.txt*0[Divination School] at 1/3 the Prayer skill level.
+ *****m_tempo.txt*0[Temporal School] at 1/4 the Prayer skill level.
+The spells from these schools are all cast using your normal spellpoints.
diff --git a/lib/mods/theme/help/g_manwe.txt b/lib/mods/theme/help/g_manwe.txt
new file mode 100644
index 00000000..4bbc85fd
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/g_melkor.txt b/lib/mods/theme/help/g_melkor.txt
new file mode 100644
index 00000000..d6033e72
--- /dev/null
+++ b/lib/mods/theme/help/g_melkor.txt
@@ -0,0 +1,65 @@
+|||||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 it if you quaff Potions of Corruption.
+ 8. Melkor likes the sacrifice of corpses and books at his altars.
+ 9. Melkor likes the permanent sacrifice of your own health at his altars.
+10. Melkor hates elves.
+11. Melkor grants access to the *****m_udun.txt*0[Udun] school of magic.
+121. 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/mods/theme/help/g_tulkas.txt b/lib/mods/theme/help/g_tulkas.txt
new file mode 100644
index 00000000..2c05292a
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/g_ulmo.txt b/lib/mods/theme/help/g_ulmo.txt
new file mode 100644
index 00000000..59c3d6e9
--- /dev/null
+++ b/lib/mods/theme/help/g_ulmo.txt
@@ -0,0 +1,58 @@
+|||||oy
+~~~~~01|Gods|Ulmo
+~~~~~02|Ulmo
+#####R === Ulmo ===
+
+Ulmo is the Lord of Waters, he controls all the lakes, rivers, seas, and oceans
+on Arda. His most faithful followers are those of the class *****c_pr_ulmo.txt*0[Priest(Ulmo)].
+
+#####GThe benefits of worshipping Ulmo
+1. Ulmo's followers can breathe underwater.
+2. If your piety is sufficiently high and you are praying, Ulmo will grant
+ you resistance to poison.
+3. If you are very pious and praying, Ulmo will grant you the ability
+ to breathe without air.
+4. Ulmo likes it if you wield or carry tridents.
+5. If you are not praying, your piety will increase automatically over time.
+6. Ulmo likes the Edain, Dunedain, Druedain, and Rohirrim.
+
+#####GThe disadvantages of worshipping Ulmo
+1. He doesn't like it if you kill aquatic creatures.
+2. He hates it if you kill good, friendly, or unique aquatic creatures.
+3. He doesn't like it if you use magic involving any kind of fire.
+4. Ulmo hates Easterlings, Orcs, and Demons.
+5. He will completely abandon you if you wear The One Ring.
+~~~~~~03|Ulmo|Prayers
+#####GUlmo's Magic
+Worshipping Ulmo gives the adventurer access to a set of special spells that come
+directly from the hands of Ulmo. These spells use your piety to cast rather than
+your spellpoints, and the level of spells that Ulmo 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 "Water Tome of Ulmo" which
+contains instructions for the procedure for each of the prayers Ulmo will
+grant. There are four prayers all told, which are:
+1. [[[[[BSong of Belegaer] (Level 1)
+ Channels the power of the Great Sea into your fingertips.
+ Sometimes it can blast through its first target.
+2. [[[[[BDraught of Ulmonan] (Level 15)
+ Fills you with a draught with powerful curing effects, prepared by Ulmo himself.
+ At spell level 1 it cures blindness, poison, cuts and stunning
+ At spell level 10 it restores drained STR, DEX and CON
+ At spell level 20 it removes parasites and unwanted mimicry
+3. [[[[[BCall of the Ulumuri] (Level 20)
+ Summons a leveled water spirit or elemental to fight for you.
+4. [[[[[BWrath of Ulmo] (Level 30)
+ Conjures up a sea storm.
+ At spell level 30 it turns into a more forceful storm.
+
+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, Ulmo 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_water.txt*0[Water School] at 3/5 the Prayer skill level.
+ *****m_nature.txt*0[Nature School] at 1/2 the Prayer skill level.
+The spells from these schools are all cast using your normal spellpoints. \ No newline at end of file
diff --git a/lib/mods/theme/help/g_varda.txt b/lib/mods/theme/help/g_varda.txt
new file mode 100644
index 00000000..a5d28475
--- /dev/null
+++ b/lib/mods/theme/help/g_varda.txt
@@ -0,0 +1,54 @@
+|||||oy
+~~~~~01|Gods|Varda
+~~~~~02|Varda
+#####R === Varda Elentari ===
+
+Varda Elentari is the greatest Queen of the Valar. Her most faithful followers
+are those of the class *****c_pr_varda.txt*0[Priest(Varda)].
+
+#####GThe benefits of worshipping Varda Elentari
+1. She grants her followers permanent light of radius 1.
+2. As you increase your piety, Varda will grant you resistance to light
+ while praying.
+3. Your piety will increase while you are in lit areas.
+4. Varda loves when you use spells involving light.
+
+#####GThe disadvantages of worshipping Varda Elentari
+1. Your piety will decrease while you are in dark places.
+2. Varda dislikes evil races (Orcs, Trolls, Dragons, and Demons).
+3. Your piety will slowly decrease while praying.
+4. She will completely abandon you if you wear The One Ring.
+~~~~~~03|Varda|Prayers
+#####GVarda Elentari's Magic
+Worshipping Varda Elentari gives the adventurer access to a set of special
+spells that come directly from the hands of Varda. These spells use your piety
+to cast rather than your spellpoints, and the level of spells that Varda 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 "Shining Tome of Varda" which
+contains instructions for the procedure for each of the prayers Varda will
+grant. There are four prayers all told, which are:
+1. [[[[[BLight of Valinor] (Level 1)
+ Lights up a room.
+ At spell level 3 it starts damaging monsters.
+ At spell level 15 it starts creating a more powerful kind of light.
+2. [[[[[BCall of Almaren] (Level 10)
+ Banishes evil beings.
+ At spell level 20 it dispels evil beings.
+3. [[[[[BEvenstar] (Level 30)
+ Maps and lights the whole level.
+ At spell level 40 it maps and lights the whole level, in addition to
+ letting you know yourself better and identifying your whole pack.
+4. [[[[[BStar Kindler] (Level 35)
+ Does multiple bursts of light damage. The power increases with level.
+
+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, Varda 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/4 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/mods/theme/help/g_yavann.txt b/lib/mods/theme/help/g_yavann.txt
new file mode 100644
index 00000000..6e6937ca
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/gambling.txt b/lib/mods/theme/help/gambling.txt
new file mode 100644
index 00000000..62352600
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/general.txt b/lib/mods/theme/help/general.txt
new file mode 100644
index 00000000..17dc187c
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/gods.txt b/lib/mods/theme/help/gods.txt
new file mode 100644
index 00000000..60427e7e
--- /dev/null
+++ b/lib/mods/theme/help/gods.txt
@@ -0,0 +1,42 @@
+|||||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 Theme, there are nine 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 greatest of the Valar, watching from atop Taniquetil.
+3. *****g_ulmo.txt*0[Ulmo] - the second mightiest of the Valar, lord of all waters on Arda.
+4 *****g_varda.txt*0[Varda Elentari] - Manwe's spouse, the most beloved by the Elves.
+5. *****g_yavann.txt*0[Yavanna Kementari] - the Earth Queen who created plants and animals.
+6. *****g_aule.txt*0[Aule the Smith] - the builder and inventor of the Valar.
+7. *****g_tulkas.txt*0[Tulkas] - another of the Valar, Tulkas values strength and courage.
+8. *****g_mandos.txt*0[Mandos] - the Doomsman of the Valar, wise and powerful.
+9. *****g_melkor.txt*0[Melkor Bauglir] - the Dark Enemy himself, once the most powerful of the Valar.
diff --git a/lib/mods/theme/help/head.aux b/lib/mods/theme/help/head.aux
new file mode 100644
index 00000000..92e979d3
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/help.hlp b/lib/mods/theme/help/help.hlp
new file mode 100644
index 00000000..7c4b431f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/index.txt b/lib/mods/theme/help/index.txt
new file mode 100644
index 00000000..c89a70b3
--- /dev/null
+++ b/lib/mods/theme/help/index.txt
@@ -0,0 +1,636 @@
+|||||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*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]
+ *****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_ascet.txt*02[Ascetic]
+ *****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]
+ *****g_aule.txt*02[Aule]
+ *****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_archer.txt*02[Archer]
+ *****c_ascet.txt*02[Ascetic]
+ *****c_assass.txt*02[Assassin]
+ *****c_axemas.txt*02[Axemaster]
+ *****c_bard.txt*02[Bard]
+ *****c_clairv.txt*02[Clairvoyant]
+ *****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_mercen.txt*02[Mercenary]
+ *****c_mimic.txt*02[Mimic]
+ *****c_mindcr.txt*02[Mindcrafter]
+ *****c_monk.txt*02[Monk]
+ *****c_necro.txt*02[Necromancer]
+ *****c_pacif.txt*02[Pacifist]
+ *****c_palad.txt*02[Paladin]
+ *****c_peacemag.txt*02[Peace-mage]
+ *****c_polear.txt*02[Polearmmaster]
+ *****c_posses.txt*02[Possessor]
+ *****c_pr_eru.txt*02[Priest - Eru]
+ *****c_pr_mand.txt*02[Priest - Mandos]
+ *****c_pr_man.txt*02[Priest - Manwe]
+ *****c_pr_ulmo.txt*02[Priest - Ulmo]
+ *****c_pr_varda.txt*02[Priest - Varda]
+ *****c_ranger.txt*02[Ranger]
+ *****c_rogue.txt*02[Rogue]
+ *****c_runecr.txt*02[Runecrafter]
+ *****c_sorcer.txt*02[Sorceror]
+ *****c_sniper.txt*02[Sniper]
+ *****birth.txt*77[Stat Bonuses]
+ *****c_stonewr.txt*02[Stonewright]
+ *****c_summon.txt*02[Summoners]
+ *****c_swordm.txt*02[Swordmasters]
+ *****c_symbia.txt*02[Symbiant]
+ *****c_trapper.txt*02[Trapper]
+ *****c_thaum.txt*02[Thaumaturgist]
+ *****c_unbel.txt*02[Unbeliever]
+ *****c_wainrid.txt*02[Wainrider]
+ *****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_clairv.txt*02[Clairvoyant]
+ *****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]
+ *****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]
+ *****r_demon.txt*01[Demon]
+ *****rm_adanrog.txt*01[Adanrog]
+ *****rm_aewrog.txt*01[Aewrog]
+ *****rm_cabrog.txt*01[Caborrog]
+ *****rm_drarog.txt*01[Draugrog]
+ *****rm_hurog.txt*01[Hurog]
+ *****rm_limrog.txt*01[Limrog]
+ *****rm_lygrog.txt*01[Lygrog]
+ *****rm_narrog.txt*01[Narrog]
+ *****rm_rawrog.txt*01[Rawrog]
+ *****rm_sarnrog.txt*01[Sarnrog]
+ *****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]
+ *****r_dragon.txt*01[Dragon]
+ *****rm_black.txt*01[Black]
+ *****rm_blue.txt*01[Blue]
+ *****rm_ether.txt*01[Ethereal]
+ *****rm_green.txt*01[Green]
+ *****rm_red.txt*01[Red]
+ *****rm_white.txt*01[White]
+ *****r_druadan.txt*01[Druadan]
+ *****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]
+ *****r_eagle.txt*01[Eagle]
+ *****m_earth.txt*02[Earth Magic]
+ *****r_easterl.txt*01[Easterling]
+ *****r_elf.txt*01[Elf]
+ *****r_ent.txt*01[Ent]
+ *****spoil_faq.txt*24[Erebor spoiler]
+ *****g_eru.txt*02[Eru]
+ *****g_eru.txt*03[Prayers]
+ *****c_pr_eru.txt*03[Priest - Eru]
+ *****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_aule.txt*01[Aule]
+ *****g_eru.txt*01[Eru]
+ *****g_mandos.txt*01[Mandos]
+ *****g_manwe.txt*01[Manwe]
+ *****g_melkor.txt*01[Melkor]
+ *****birth.txt*30[Piety]
+ *****tome_faq.txt*23[Quest - Spoilers]
+ *****g_tulkas.txt*01[Tulkas]
+ *****g_ulmo.txt*01[Ulmo]
+ *****g_varda.txt*01[Varda]
+ *****g_yavann.txt*01[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]
+
+~~~~~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_mandos.txt*02[Mandos]
+ *****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]
+ *****c_mercen.txt*02[Mercenary]
+ *****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_pacif.txt*02[Pacifist]
+ *****c_palad.txt*01[Paladin]
+ *****c_peacemag.txt*02[Peace-mage]
+ *****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_mandos.txt*0[Priest(Mandos)]
+ *****c_pr_man.txt*01[Priest - Manwe]
+ *****c_pr_ulmo.txt*0[Priest(Ulmo)]
+ *****c_pr_varda.txt*0[Priest(Varda)]
+ *****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_demon.txt*02[Demon]
+ *****r_dragon.txt*02[Dragon]
+ *****r_dunad.txt*02[Dunadan]
+ *****r_dwarf.txt*02[Dwarf]
+ *****r_eagle.txt*02[Eagle]
+ *****r_easterl.txt*02[Easterling]
+ *****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_druadan.txt*02[Druadan]
+ *****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_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]
+ *****spoil_faq.txt*23[Secret Valley]
+ *****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*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_sniper.txt*02[Sniper]
+ *****c_sorcer.txt*01[Sorceror]
+ *****rm_spec.txt*01[Spectre]
+ *****spoiler.hlp*01[Spoilers]
+ *****corspoil.txt*02[Corruptions]
+ *****dunspoil.txt*02[Dungeons]
+ *****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]
+ *****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]
+ *****c_stonewr.txt*0[Stonewright]
+ *****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]
+ *****whattome.txt*01[ToME - a General Description]
+ *****dungeon.txt*07[Town]
+ *****c_trapper.txt*02[Trapper]
+ *****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]
+ *****g_ulmo.txt*02[Ulmo]
+ *****c_unbel.txt*01[Unbeliever]
+ *****c_unbel.txt*03[Antimagic]
+~~~~~86
+*****/Vindex.txt*86[V]
+ *****rm_vamp.txt*01[Vampire]
+ *****g_varda.txt*02[Varda]
+ *****tome_faq.txt*16[Void jumpgates]
+ *****dungeon.txt*23[Void jumpgates]
+~~~~~87
+*****/Windex.txt*87[W]
+ *****magic.txt*02[Wands]
+ *****c_wainrid.txt*02[Wainrider]
+ *****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/mods/theme/help/inscrip.txt b/lib/mods/theme/help/inscrip.txt
new file mode 100644
index 00000000..517c81c2
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/luckspoi.txt b/lib/mods/theme/help/luckspoi.txt
new file mode 100644
index 00000000..4b4007c2
--- /dev/null
+++ b/lib/mods/theme/help/luckspoi.txt
@@ -0,0 +1,129 @@
+|||||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 (+2);
+Beorning (+1);
+Half-Ogre, Dark Elf, Ent (-2);
+Orc (-3);
+Troll (-4);
+Petty Dwarf, 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.
+
+All Morgul items have -10 luck.
+Weapons of Telchar give a bonus to luck.
+Weapons of the Glamhoth are unlucky.
+Lucky amulets give a bonus to luck.
+Unlucky amulets are, well, unlucky.
+
+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 Broken Dagger 'Angrist' (+5)
+The Long Sword 'Anduril' (+4)
+The Long Sword of Dernhelm (+5)
+The Long Bow of Bard (+2)
+The Mage Staff of Manwe (+12)
+The Golden Harp of Thorin (+2)
+The Harp of Maglor (+3)
+The Drum of the Sky (+2)
+The Harp of Daeron (+1)
+The Long Sword of Tulkas (+10)
+The Robe of Great Luck (+60)
+The Heavy Crossbow of Orome (+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 Elven Cloak of Peregrin Took (+3)
+The Ring 'Fuin' (+15)
+The Blue Stone 'Coimir' (+2)
+The Elven Cloak of Mellyrn (+4)
+The Set of Cesti 'Skycleaver' (+1)
+The Robe of Belegaer (+5)
+The Harp of Tom Bombadil (+4)
+The Horn 'Valaroma' (+4)
+The Sceptre of Numenor (-3)
+The Rod of Annuminas (+4)
+The Lucerne Hammer of the Eruchin (+10)
+The Trident of Ulmo (+10)
+The Broad Axe of Aule (+10)
+The Rapier of Vaire (+10)
+The Long Bow of Irmo (+5)
+The Sling of Nessa (+5)
+The Boomerang of Varda (+5)
+The Ring of Power of the Blacklocks (+3)
+
+#####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/mods/theme/help/m_air.txt b/lib/mods/theme/help/m_air.txt
new file mode 100644
index 00000000..ee9fa8d0
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_convey.txt b/lib/mods/theme/help/m_convey.txt
new file mode 100644
index 00000000..91ed8556
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_demono.txt b/lib/mods/theme/help/m_demono.txt
new file mode 100644
index 00000000..cb32d360
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_divin.txt b/lib/mods/theme/help/m_divin.txt
new file mode 100644
index 00000000..df92c11b
--- /dev/null
+++ b/lib/mods/theme/help/m_divin.txt
@@ -0,0 +1,38 @@
+|||||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 Eru Iluvatar or Mandos also gives the ability to cast spells from
+the divination school at a level of 2/3 and 1/3, respectively, of your prayer
+level. E.g. if the skill "Spirituality: Prayer" is at level 12, a worshipper
+of Eru can cast up to level 8 divination school spells, whereas a worshipper
+of Mandos can cast up to level 4 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/mods/theme/help/m_earth.txt b/lib/mods/theme/help/m_earth.txt
new file mode 100644
index 00000000..2f2bd9c2
--- /dev/null
+++ b/lib/mods/theme/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 Tulkas, Yavanna Kementari or Aule also gives the ability to cast
+spells from the earth school, at a level of 4/5, 1/2, or 1/3, 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
+and a worshipper of Aule can cast up to level 3 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/mods/theme/help/m_fire.txt b/lib/mods/theme/help/m_fire.txt
new file mode 100644
index 00000000..826a1d9a
--- /dev/null
+++ b/lib/mods/theme/help/m_fire.txt
@@ -0,0 +1,53 @@
+|||||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.
+
+Worshipping Aule also gives the ability to cast spells from the fire school
+at a level of 3/5 of your prayer level. E.g. if the skill "Spirituality:
+Prayer" is at level 15, you can cast up to level 9 fire school spells.
+
+#####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/mods/theme/help/m_geoman.txt b/lib/mods/theme/help/m_geoman.txt
new file mode 100644
index 00000000..97c1ac1a
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_mana.txt b/lib/mods/theme/help/m_mana.txt
new file mode 100644
index 00000000..23d8e680
--- /dev/null
+++ b/lib/mods/theme/help/m_mana.txt
@@ -0,0 +1,40 @@
+|||||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 Eru Iluvatar or Varda Elentari also gives the ability to cast spells
+from the mana school at a level of 1/2 or 1/4, respectively, of your prayer level.
+E.g. if the skill "Spirituality: Prayer" is at level 10, Eru worshippers can cast
+up to level 5 mana school spells whereas Varda worshippers can cast up to level 3
+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/mods/theme/help/m_meta.txt b/lib/mods/theme/help/m_meta.txt
new file mode 100644
index 00000000..7bf1471b
--- /dev/null
+++ b/lib/mods/theme/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 Manwe Sulimo or Varda Elentari 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, Manwe and Varda worshippers
+alike 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/mods/theme/help/m_mimic.txt b/lib/mods/theme/help/m_mimic.txt
new file mode 100644
index 00000000..79d39521
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_mind.txt b/lib/mods/theme/help/m_mind.txt
new file mode 100644
index 00000000..b6c1196f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_mindcr.txt b/lib/mods/theme/help/m_mindcr.txt
new file mode 100644
index 00000000..4f420656
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_music.txt b/lib/mods/theme/help/m_music.txt
new file mode 100644
index 00000000..0f84f08f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_nature.txt b/lib/mods/theme/help/m_nature.txt
new file mode 100644
index 00000000..e57c5704
--- /dev/null
+++ b/lib/mods/theme/help/m_nature.txt
@@ -0,0 +1,54 @@
+|||||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 Yavanna Kementari or Ulmo 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, Yavanna and Ulmo worshippers alike 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.
+6. [[[[[GGrow Athelas] (school level 30)
+ Cures the Black Breath
+
+#####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/mods/theme/help/m_necrom.txt b/lib/mods/theme/help/m_necrom.txt
new file mode 100644
index 00000000..8de4fd37
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_symbio.txt b/lib/mods/theme/help/m_symbio.txt
new file mode 100644
index 00000000..b7e04632
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_tempo.txt b/lib/mods/theme/help/m_tempo.txt
new file mode 100644
index 00000000..64340ee5
--- /dev/null
+++ b/lib/mods/theme/help/m_tempo.txt
@@ -0,0 +1,42 @@
+|||||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 Yavanna Kementari or Mandos also gives the ability to cast spells
+from the temporal school at a level of 1/6 or 1/4, respectively, of your prayer
+level. E.g. if the skill "Spirituality: Prayer" is at level 12, a worshipper
+of Yavanna can cast up to level 2 temporal school spells, whereas a worshipper
+of Mandos can cast up to level 3 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/mods/theme/help/m_thaum.txt b/lib/mods/theme/help/m_thaum.txt
new file mode 100644
index 00000000..253d52be
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_udun.txt b/lib/mods/theme/help/m_udun.txt
new file mode 100644
index 00000000..a903d9f9
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/m_water.txt b/lib/mods/theme/help/m_water.txt
new file mode 100644
index 00000000..2eb4cc5a
--- /dev/null
+++ b/lib/mods/theme/help/m_water.txt
@@ -0,0 +1,34 @@
+|||||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 Yavanna Kementari or Ulmo also gives the ability to cast spells
+from the water school at a level of 1/2 or 3/5, respectively, of your prayer
+level. E.g. if the skill "Spirituality: Prayer" is at level 10, a Yavanna
+worshipper can cast up to level 5 water school spells, whereas an Ulmo
+worshipper can cast up to level 6 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/mods/theme/help/macrofaq.txt b/lib/mods/theme/help/macrofaq.txt
new file mode 100644
index 00000000..035f674b
--- /dev/null
+++ b/lib/mods/theme/help/macrofaq.txt
@@ -0,0 +1,2346 @@
+|||||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.
+
+#####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----------------------------------------------------------------------
+
+(Content removed because it was obsolete. Inscribe-on-pickup is now the default.)
+
+#####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 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.6 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.7 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.
+
+#####B{=g}
+This inscription will cause an item of the same kind to be picked up
+from the floor without prompting.
+
+~~~~~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
+alert_hitpoint
+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/mods/theme/help/magic.hlp b/lib/mods/theme/help/magic.hlp
new file mode 100644
index 00000000..382451c3
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/magic.txt b/lib/mods/theme/help/magic.txt
new file mode 100644
index 00000000..14fa6570
--- /dev/null
+++ b/lib/mods/theme/help/magic.txt
@@ -0,0 +1,142 @@
+|||||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] *****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].
+
+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/mods/theme/help/newbie.hlp b/lib/mods/theme/help/newbie.hlp
new file mode 100644
index 00000000..2dda8dbc
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/option.txt b/lib/mods/theme/help/option.txt
new file mode 100644
index 00000000..00ee15a4
--- /dev/null
+++ b/lib/mods/theme/help/option.txt
@@ -0,0 +1,576 @@
+|||||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.
+
+#####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 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.
+
+#####GItems always sell for 0 gold [no_selling]
+ Disables selling items back to shops for money. The value of gold found in the
+ dungeon is increased to compensate.
+
+~~~~~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 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"]).
+
+#####GAudible bell (on errors, etc) [ring_bell]
+ Attempt to make a "bell" noise when various errors occur.
+
+~~~~~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.
+
+#####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.
+
+#####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.
+
+#####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.
+~~~~~3
+#####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.
+
+#####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.
+
+#####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.
+~~~~~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.
+
+#####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/mods/theme/help/r_beorn.txt b/lib/mods/theme/help/r_beorn.txt
new file mode 100644
index 00000000..b000403f
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_demon.txt b/lib/mods/theme/help/r_demon.txt
new file mode 100644
index 00000000..7d54d187
--- /dev/null
+++ b/lib/mods/theme/help/r_demon.txt
@@ -0,0 +1,31 @@
+~~~~~01|Demon
+~~~~~02|Races|Demon
+#####R=== Demons ===
+
+#####GDescription
+Demons are minor servants of the Dark. They are natural creatures
+that have been corrupted and twisted by Melkor to serve his ends.
+All demons have an intrinsic understanding of the forces of Dark,
+they resist darkness and fear, and have a firm hold on their life
+forces. Most of a demon's ability depends on their subrace, of
+which there are ten. As a corrupted race, demons will gain and
+lose corruptions randomly as they gain experience.
+
+#####GStat Modifiers
+Strength 0
+Intelligence 0
+Wisdom 0
+Dexterity 0
+Constitution 0
+Charisma -1
+Hit Dice Sides 10
+Exp Penalty +70%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Sneakiness 0.000 [0.000]
+ Stealth 0.000 [0.000]
+ Disarming +0.500 [0.000]
+Magic
+ Magic-Device +0.050 [0.000]
+Spirituality -5.000 [0.000] \ No newline at end of file
diff --git a/lib/mods/theme/help/r_dragon.txt b/lib/mods/theme/help/r_dragon.txt
new file mode 100644
index 00000000..097d6462
--- /dev/null
+++ b/lib/mods/theme/help/r_dragon.txt
@@ -0,0 +1,33 @@
+~~~~~01|Dragons
+~~~~~02|Races|Dragons
+#####R=== Dragons ===
+
+#####GDescription
+Dragons are majestic creatures of power traditionally aligned with
+the forces of evil. However, some may choose to become adventurers.
+They may not wield any weapons, bows, or play musical instruments.
+However, they are able to fly from birth, and gain resistances to
+some of the elements (depending on their subrace). Dragons have to
+choose a subrace specific to their race. Dragons make very good
+Mages, but they cannot become Geomancers, because they are unable
+to wield mage staves. As winged beings, Dragons may not wear cloaks.
+
+#####GStat Modifiers
+Strength +3
+Intelligence +2
+Wisdom +2
+Dexterity -2
+Constitution +2
+Charisma -5
+Hit Dice Sides 9
+Exp Penalty +150%
+
+#####GRacial Skills
+Combat 0.000 [0.000]
+ Barehand-combat 0.000 [0.400]
+Sneakiness 0.000 [0.000]
+ Stealth -10.000 [0.000]
+ Disarming 1.000 [0.000]
+Magic 0.000 [0.000]
+ Magic-Device 0.500 [0.000]
+Spirituality -4.000 [0.000] \ No newline at end of file
diff --git a/lib/mods/theme/help/r_drkelf.txt b/lib/mods/theme/help/r_drkelf.txt
new file mode 100644
index 00000000..3f0758e5
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_druadan.txt b/lib/mods/theme/help/r_druadan.txt
new file mode 100644
index 00000000..bf063956
--- /dev/null
+++ b/lib/mods/theme/help/r_druadan.txt
@@ -0,0 +1,32 @@
+~~~~~01|Druedain
+~~~~~02|Races|Druadan
+#####R=== Druedain ===
+
+#####GDescription
+An ancient branch of the race of Men, they are somewhat weaker, but wiser
+and better at disguise. They 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.
+
+#####GStat Modifiers
+Strength -2
+Intelligence -3
+Wisdom 2
+Dexterity 3
+Constitution -2
+Charisma -2
+Hit Dice Sides 9
+Exp Penalty +15%
+
+#####GRacial Skill Modifiers:
+#####BSkill Start Mod Skill Point Gains Mod
+Combat 0.000 [0.000]
+ Weaponmastery 1.000 [0.000]
+ Archery 0.800 [0.000]
+ Boomerang-mastery 0.000 [0.250]
+Sneakiness 0.100 [0.000]
+ Stealth 1.000 [0.000]
+ Disarming -0.200 [0.000]
+Magic 0.000 [0.000]
+ Magic-Device -0.300 [0.000]
+Spirituality -1.000 [0.000] \ No newline at end of file
diff --git a/lib/mods/theme/help/r_dunad.txt b/lib/mods/theme/help/r_dunad.txt
new file mode 100644
index 00000000..79b85049
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_dwarf.txt b/lib/mods/theme/help/r_dwarf.txt
new file mode 100644
index 00000000..6c5a9b80
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_eagle.txt b/lib/mods/theme/help/r_eagle.txt
new file mode 100644
index 00000000..cd59aaaf
--- /dev/null
+++ b/lib/mods/theme/help/r_eagle.txt
@@ -0,0 +1,32 @@
+~~~~~01|Eagle
+~~~~~02|Races|Eagle
+#####R=== Eagles ===
+
+#####GDescription
+"The Eagles are coming! The Eagles are coming!" Ever has this cry signified
+the Great Eagles of Manwe coming to a battlefield or a dire emergency to aid
+the forces of good. Majestic magical birds ever faithful to Manwe Sulimo,
+though they may choose to worship other Valar during their time on Arda. They
+have been given many gifts by Manwe, not the least important of which is the
+ability to resist the elements as they mature. They cannot wield weapons,
+bows, or play musical instruments, however. The Eagles' greatest enemies are
+the dragons, and they learn to detect the presence of dragons fairly early.
+As winged creatures, Eagles may not wear cloaks.
+
+#####GStat Modifiers
+Strength +6
+Intelligence +2
+Wisdom +1
+Dexterity -2
+Constitution +3
+Charisma +6
+Hit Dice Sides 12
+Exp Penalty +200%
+
+#####GRacial Skills
+Combat 0.000 [0.000]
+ Barehand-combat 1.000 [0.300]
+Sneakiness 3.000 [0.000]
+ Stealth -16.000 [0.000]
+ Disarming 6.000 [0.000]
+Spirituality 5.000 [0.000] \ No newline at end of file
diff --git a/lib/mods/theme/help/r_easterl.txt b/lib/mods/theme/help/r_easterl.txt
new file mode 100644
index 00000000..2370d260
--- /dev/null
+++ b/lib/mods/theme/help/r_easterl.txt
@@ -0,0 +1,34 @@
+~~~~~01|Easterling
+~~~~~02|Races|Easterling
+#####R=== Easterlings ===
+
+#####GDescription
+From the time of the Dagor Bragollach, the Easterlings have stood as
+the treacherous race of men. Swarthy and stout, these humans cannot
+be stunned, get a penchant for landing on their feet, and are not
+confused easily, once they have gained some experience. They are equally
+good at all kinds of weapons, being trained in combat from an early age.
+They do not trust magic, however, and have little skill in it.
+
+#####GStat Modifiers
+Strength +2
+Intelligence -2
+Wisdom -2
+Dexterity -2
+Constitution +2
+Charisma -1
+Hit Dice Sides 10
+Exp Penalty +40%
+
+#####GSkill bonuses (supplementary to existing skills)
+Combat 1.000 [0.100]
+ Weaponmastery 0.500 [0.100]
+ Sword-mastery 0.000 [0.100]
+ Axe-mastery 0.000 [0.100]
+ Hafted-mastery 0.000 [0.100]
+ Polearm-mastery 0.000 [0.100]
+ Archery 2.000 [0.200]
+Sneakiness 1.000 [0.300]
+ Stealth 1.000 [0.200]
+ Disarming 1.000 [0.200]
+Spirituality 2.500 [0.300] \ No newline at end of file
diff --git a/lib/mods/theme/help/r_elf.txt b/lib/mods/theme/help/r_elf.txt
new file mode 100644
index 00000000..6b4ceb3b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_ent.txt b/lib/mods/theme/help/r_ent.txt
new file mode 100644
index 00000000..1b3e047e
--- /dev/null
+++ b/lib/mods/theme/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. They cannot
+be poisoned, however, and their bark is very tough. 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/mods/theme/help/r_gnome.txt b/lib/mods/theme/help/r_gnome.txt
new file mode 100644
index 00000000..8272e8a5
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_hafelf.txt b/lib/mods/theme/help/r_hafelf.txt
new file mode 100644
index 00000000..ea748440
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_hafogr.txt b/lib/mods/theme/help/r_hafogr.txt
new file mode 100644
index 00000000..2a173e98
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_hielf.txt b/lib/mods/theme/help/r_hielf.txt
new file mode 100644
index 00000000..3317a67b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_hobbit.txt b/lib/mods/theme/help/r_hobbit.txt
new file mode 100644
index 00000000..d9fafb08
--- /dev/null
+++ b/lib/mods/theme/help/r_hobbit.txt
@@ -0,0 +1,39 @@
+~~~~~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 no 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. They do not
+wear shoes, as their feet grow naturally thick, leathery soles which make shoes
+a chore to put on and take off.
+
+#####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/mods/theme/help/r_human.txt b/lib/mods/theme/help/r_human.txt
new file mode 100644
index 00000000..57606764
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_maia.txt b/lib/mods/theme/help/r_maia.txt
new file mode 100644
index 00000000..d4a6c635
--- /dev/null
+++ b/lib/mods/theme/help/r_maia.txt
@@ -0,0 +1,20 @@
+~~~~~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. 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/mods/theme/help/r_orc.txt b/lib/mods/theme/help/r_orc.txt
new file mode 100644
index 00000000..c4bcf691
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_pettyd.txt b/lib/mods/theme/help/r_pettyd.txt
new file mode 100644
index 00000000..c8b8eba3
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_rohank.txt b/lib/mods/theme/help/r_rohank.txt
new file mode 100644
index 00000000..03391d4b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_troll.txt b/lib/mods/theme/help/r_troll.txt
new file mode 100644
index 00000000..f35e5aa6
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/r_wodelf.txt b/lib/mods/theme/help/r_wodelf.txt
new file mode 100644
index 00000000..1cb4bb55
--- /dev/null
+++ b/lib/mods/theme/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.
+They are more dangerous but less wise than High-Elves.
+
+#####GStat Modifiers
+Strength +2
+Intelligence +2
+Wisdom -3
+Dexterity +5
+Constitution 0
+Charisma +1
+Hit Dice Sides 7
+Exp Penalty +30%
+
+#####GRacial Skills
+Combat 0.000 [0.000]
+ Archery 4.000 [0.000]
+ Weaponmastery 1.000 [0.200]
+Sneakiness 0.800 [0.000]
+ Stealth 5.000 [0.000]
+ Disarming 0.500 [0.000]
+Magic 0.000 [0.000]
+ Magic-Device 0.600 [0.000]
+Spirituality 3.000 [0.000]
+
+#####GInnate Abilities:
+#####BAbility Character level
+Tree-walking 1
diff --git a/lib/mods/theme/help/r_yeek.txt b/lib/mods/theme/help/r_yeek.txt
new file mode 100644
index 00000000..bdc7dcc5
--- /dev/null
+++ b/lib/mods/theme/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.
+
+#####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/mods/theme/help/rm_adanrog.txt b/lib/mods/theme/help/rm_adanrog.txt
new file mode 100644
index 00000000..5e4a46b9
--- /dev/null
+++ b/lib/mods/theme/help/rm_adanrog.txt
@@ -0,0 +1,31 @@
+~~~~~01|Adanrog
+~~~~~02|Race Modifiers|Adanrog
+#####R=== Adanroeg (Man-demons) ===
+
+#####GDescription
+The greatest of all the lesser demons, these are humanoid
+figures wreathed in living flame. Their most obvious
+advantage is that they are completely immune to fire from
+birth, because they are born in fire and all carry a small
+part of the Flame of Udun inside them. They are equally
+good at magic and combat. With time, they learn to channel
+the forces of darkness to temporarily assume Balrog form.
+
+#####GStat Modifiers
+Strength +1
+Intelligence +1
+Wisdom +1
+Dexterity +1
+Constitution +1
+Charisma +1
+Hit Dice +3 sides
+Exp Penalty +50%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Combat 0.000 [0.300]
+Magic 0.000 [0.300]
+
+#####GStarting Equipment
+An Adanrog character begins the game with:
+ Some rations
+ Some torches \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_aewrog.txt b/lib/mods/theme/help/rm_aewrog.txt
new file mode 100644
index 00000000..28d8013f
--- /dev/null
+++ b/lib/mods/theme/help/rm_aewrog.txt
@@ -0,0 +1,35 @@
+~~~~~01|Aewrog
+~~~~~02|Race Modifiers|Aewrog
+#####R=== Aewroeg (Bird Demons) ===
+
+#####GDescription
+The Aewroeg are birds corrupted by Morgoth into foul
+birdlike beings with dazzling wings. They are somewhat
+physically weak, but expert at befuddling enemies so
+that they believe they'd never seen anything so fair.
+They can fly, and their charisma is naturally sustained.
+With experience, they learn to resist fire and to
+prevent holding magics. They also have an innate ability
+to flap their wings to create mesmerising illusions.
+Good magic users, they are particularly skilled at the
+Mind school.
+
+#####GStat Modifiers
+Strength -2
+Intelligence 0
+Wisdom 0
+Dexterity 0
+Constitution 0
+Charisma +3
+Hit Dice +1 sides
+Exp Penalty +0%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Magic 0.000 [0.200]
+Mind 1.000 [0.300]
+
+#####GStarting Equipment
+An Aewrog character begins the game with:
+ Some rations
+ Some torches
+ A spellbook of Charm \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_barb.txt b/lib/mods/theme/help/rm_barb.txt
new file mode 100644
index 00000000..57d793ff
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/rm_black.txt b/lib/mods/theme/help/rm_black.txt
new file mode 100644
index 00000000..1570389c
--- /dev/null
+++ b/lib/mods/theme/help/rm_black.txt
@@ -0,0 +1,31 @@
+~~~~~01|Black
+~~~~~02|Race Modifiers|Black
+#####R=== Black Dragons ===
+
+#####GDescription
+These dragons are utterly in command of the element of acid. They
+resist acid very well and learn to become impervious to it with
+experience. Their glistening coats are so beautiful that those who
+look upon them cannot help but marvel at their majesty. The more
+experience they gain, the more charismatic they become. They have
+an innate ability to spit acid collected at the bottom of their
+stomach, and they have access to the Water school of magic.
+
+#####GStat Modifiers
+Strength 0
+Intelligence 0
+Wisdom 0
+Dexterity 0
+Constitution 0
+Charisma +3
+Hit Dice +0 sides
+Exp Penalty +0%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Water 1.000 [0.600]
+
+#####GStarting Equipment
+A Black dragon character begins the game with:
+ Some rations
+ Some torches
+ A spellbook of Geyser \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_blue.txt b/lib/mods/theme/help/rm_blue.txt
new file mode 100644
index 00000000..d6d0bbeb
--- /dev/null
+++ b/lib/mods/theme/help/rm_blue.txt
@@ -0,0 +1,31 @@
+~~~~~01|Blue
+~~~~~02|Race Modifiers|Blue
+#####R=== Blue Dragons ===
+
+#####GDescription
+These dragons are utterly in command of the element of Earth.
+As such, they have an innate resistance to electricity, which
+with time becomes full immunity, as they can use the power of
+the Earth to neutralize electrical discharges. They have access
+to the Earth school of magic and can conjure up showers of
+blazing sparks to dazzle their opponents. They are especially
+agile and lithe, and their dexterity will increase with experience.
+
+#####GStat Modifiers
+Strength 0
+Intelligence 0
+Wisdom 0
+Dexterity +3
+Constitution 0
+Charisma 0
+Hit Dice +0 sides
+Exp Penalty +0%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Earth 1.000 [0.600]
+
+#####GStarting Equipment
+A Blue dragon character begins the game with:
+ Some rations
+ Some torches
+ A spellbook of Stone Skin
diff --git a/lib/mods/theme/help/rm_cabrog.txt b/lib/mods/theme/help/rm_cabrog.txt
new file mode 100644
index 00000000..eee1dada
--- /dev/null
+++ b/lib/mods/theme/help/rm_cabrog.txt
@@ -0,0 +1,35 @@
+~~~~~01|Caborrog
+~~~~~02|Race Modifiers|Caborrog
+#####R=== Caborroeg (Frog Demons) ===
+
+#####GDescription
+These demons are frogs that have been corrupted by Melkor.
+Small humanoids with quick, darting eyes and a penchant
+for emitting loud croaks. As such, they are not very
+strong or stealthy - however, they are quite fast and
+smart. With experience, they learn to develop a special
+protective layer around themselves and do not even need
+to wear armour. Expert magic users, their intelligence
+increases with experience, and they have access to the
+Conveyance and Temporal schools of magic.
+
+#####GStat Modifiers
+Strength -1
+Intelligence +2
+Wisdom +1
+Dexterity 0
+Constitution 0
+Charisma -3
+Hit Dice +2 sides
+Exp Penalty +10%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Dodging 1.000 [0.400]
+Magic 0.000 [0.200]
+Conveyance 0.000 [0.300]
+Temporal 0.000 [0.300]
+
+#####GStarting Equipment
+A Caborrog character begins the game with:
+ Some rations
+ Some torches \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_class.txt b/lib/mods/theme/help/rm_class.txt
new file mode 100644
index 00000000..81f72d87
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/rm_drarog.txt b/lib/mods/theme/help/rm_drarog.txt
new file mode 100644
index 00000000..7794f2cd
--- /dev/null
+++ b/lib/mods/theme/help/rm_drarog.txt
@@ -0,0 +1,34 @@
+~~~~~01|Draugrog
+~~~~~02|Race Modifiers|Draugrog
+#####R=== Draugroeg (Wolf Demons) ===
+
+#####GDescription
+Humanoids with long, snoutlike faces and shaggy fur around
+the ears. They learn more quickly and have more magical
+powes than their Hurog cousins. They have a tendency to
+snarl, however, which makes them quite unstealthy. They too
+learn to detect living and non-living beings around them as
+they gain experience, but they can also learn to detect the
+presence of powerful objects and beings before they even get
+near them. Good with magic, they have the ability to harness
+the power of their mind.
+
+#####GStat Modifiers
+Strength +1
+Intelligence +1
+Wisdom +1
+Dexterity +1
+Constitution +1
+Charisma -1
+Hit Dice +0 sides
+Exp Penalty -20%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Magic 0.000 [0.200]
+Spirituality 0.000 [0.200]
+Mindcraft 1.000 [0.500]
+
+#####GStarting Equipment
+A Draugrog character begins the game with:
+ Some rations
+ Some torches \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_ether.txt b/lib/mods/theme/help/rm_ether.txt
new file mode 100644
index 00000000..ee49f002
--- /dev/null
+++ b/lib/mods/theme/help/rm_ether.txt
@@ -0,0 +1,32 @@
+~~~~~01|Ethereal
+~~~~~02|Race Modifiers|Ethereal
+#####R=== Ethereal Dragons ===
+
+#####GDescription
+These dragons are powerful undead creatures, able to both fly and
+pass through solid objects with ease. Because of their previous
+experiences, they are much wiser than other dragons, and being
+undead, they know very well how to keep hold of their life force.
+As undead, they have access to the realm of chaos in the nether
+world, and can learn to breathe chaos at their enemies. Their
+wisdom increases with time, granting them access to the complex
+Meta school of magic.
+
+#####GStat Modifiers
+Strength 0
+Intelligence 0
+Wisdom +3
+Dexterity 0
+Constitution 0
+Charisma 0
+Hit Dice +0 sides
+Exp Penalty +0%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Meta 1.000 [0.600]
+
+#####GStarting Equipment
+An Ethereal dragon character begins the game with:
+ Some rations
+ Some torches
+ A spellbook of Recharge
diff --git a/lib/mods/theme/help/rm_green.txt b/lib/mods/theme/help/rm_green.txt
new file mode 100644
index 00000000..0e07d6aa
--- /dev/null
+++ b/lib/mods/theme/help/rm_green.txt
@@ -0,0 +1,30 @@
+~~~~~01|Green
+~~~~~02|Race Modifiers|Green
+#####R=== Green Dragons ===
+
+#####GDescription
+Sickly greenish vapours rise from the scales and skin of these
+dragons. They have utter mastery of environmental poisons and
+air, and so are much healthier than other dragons, and do not
+succumb to constitution-draining attacks. They have ability to
+instantly forge poison darts from the upper layer of their scales,
+and they have access to the Air school of magic.
+
+#####GStat Modifiers
+Strength 0
+Intelligence 0
+Wisdom 0
+Dexterity 0
+Constitution +3
+Charisma 0
+Hit Dice +0 sides
+Exp Penalty +0%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Air 1.000 [0.600]
+
+#####GStarting Equipment
+A Green dragon character begins the game with:
+ Some rations
+ Some torches
+ A spellbook of Noxious Cloud
diff --git a/lib/mods/theme/help/rm_herm.txt b/lib/mods/theme/help/rm_herm.txt
new file mode 100644
index 00000000..4787b8ae
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/rm_hurog.txt b/lib/mods/theme/help/rm_hurog.txt
new file mode 100644
index 00000000..10b6b922
--- /dev/null
+++ b/lib/mods/theme/help/rm_hurog.txt
@@ -0,0 +1,27 @@
+~~~~~01|Hurog
+~~~~~02|Race Modifiers|Hurog
+#####R=== Huroeg (Dog Demons) ===
+
+#####GDescription
+The Huroeg are dogs that have been corrupted by Melkor -
+Humanoid creatures with doglike snouts and elongated ears.
+One of the lowest form of demons, they have an uncanny
+sense of smell which increase with experience, so that
+these creatures learn to detect every living thing around
+them. They are very good at detecting treasure, and will
+become expert at searching for hidden passageways and traps.
+
+#####GStat Modifiers
+Strength -1
+Intelligence 0
+Wisdom 0
+Dexterity 0
+Constitution -1
+Charisma 0
+Hit Dice +0 sides
+Exp Penalty -10%
+
+#####GStarting Equipment
+A Hurog character begins the game with:
+ Some rations
+ Some torches \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_limrog.txt b/lib/mods/theme/help/rm_limrog.txt
new file mode 100644
index 00000000..bf43a87b
--- /dev/null
+++ b/lib/mods/theme/help/rm_limrog.txt
@@ -0,0 +1,33 @@
+~~~~~01|Limrog
+~~~~~02|Race Modifiers|Limrog
+#####R=== Limroeg (Fish Demons) ===
+
+#####GDescription
+Humanoid creatures with gill slits at the necks. They can
+survive underwater for extended periods of time, and with
+sufficient experience, they'll learn to breathe without any
+air at all. Somewhat weak and sickly, they are highly agile
+and masters of the Conveyance, Temporal, and Meta schools of
+magic. They also have several special abilities that enable
+them to disappear and reappear at will.
+
+#####GStat Modifiers
+Strength -2
+Intelligence +1
+Wisdom +1
+Dexterity +3
+Constitution -1
+Charisma -1
+Hit Dice +1 sides
+Exp Penalty +50%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Magic 0.000 [0.200]
+Conveyance 0.000 [0.500]
+Temporal 0.000 [0.500]
+Meta 0.000 [0.500]
+
+#####GStarting Equipment
+A Limrog character begins the game with:
+ Some rations
+ Some torches \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_lsoul.txt b/lib/mods/theme/help/rm_lsoul.txt
new file mode 100644
index 00000000..2f5e6e2c
--- /dev/null
+++ b/lib/mods/theme/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
+Waiting, 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 Waiting. 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/mods/theme/help/rm_lygrog.txt b/lib/mods/theme/help/rm_lygrog.txt
new file mode 100644
index 00000000..80b6fa45
--- /dev/null
+++ b/lib/mods/theme/help/rm_lygrog.txt
@@ -0,0 +1,27 @@
+~~~~~01|Lygrog
+~~~~~02|Race Modifiers|Lygrog
+#####R=== Lygroeg (Snake Demons) ===
+
+#####GDescription
+These slithering snakelike demons lack many physical
+advantages of their humanoid cousins, but they have
+access to lost and forgotten realms of magic that
+enable them to overcome their physical handicaps, if
+they can survive long enough to reap the benefits of
+their experience, that is. As highly magical creatures,
+they are no good at anything else.
+
+#####GStat Modifiers
+Strength -3
+Intelligence +5
+Wisdom +5
+Dexterity +5
+Constitution -1
+Charisma -6
+Hit Dice +2 sides
+Exp Penalty +40%
+
+#####GStarting Equipment
+A Lygrog character begins the game with:
+ Some rations
+ Some torches \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_narrog.txt b/lib/mods/theme/help/rm_narrog.txt
new file mode 100644
index 00000000..2f0ed5f9
--- /dev/null
+++ b/lib/mods/theme/help/rm_narrog.txt
@@ -0,0 +1,34 @@
+~~~~~01|Narrog
+~~~~~02|Race Modifiers|Narrog
+#####R=== Narroeg (Rat Demons) ===
+
+#####GDescription
+These ratlike demons have fangs that drip with venom.
+Their quick, scurrying feet enable them to gain speed
+as they gain experience. They learn to use the winglike
+appendages on their slick, black backs in order to fly.
+They are somewhat weak and sickly (though resistant to
+poison), but sly, cunning and agile. Good with magic,
+they can forge poison darts to throw at their opponents,
+and can become masters of disguise and disappearance.
+
+#####GStat Modifiers
+Strength -1
+Intelligence +1
+Wisdom +1
+Dexterity +1
+Constitution -1
+Charisma -2
+Hit Dice +1 sides
+Exp Penalty +20%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Stealth 1.000 [0.500]
+Magic 0.000 [0.200]
+Conveyance 1.000 [0.300]
+
+#####GStarting Equipment
+A Narrog character begins the game with:
+ Some rations
+ Some torches
+ A spellbook of Phase Door \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_rawrog.txt b/lib/mods/theme/help/rm_rawrog.txt
new file mode 100644
index 00000000..830a8dc3
--- /dev/null
+++ b/lib/mods/theme/help/rm_rawrog.txt
@@ -0,0 +1,30 @@
+~~~~~01|Rawrog
+~~~~~02|Race Modifiers|Rawrog
+#####R=== Rawroeg (Lion Demons) ===
+
+#####GDescription
+Tall humanoids with glorious red-gold hair and decidedly
+catlike faces, the Rawroeg are corrupted lions. Strong,
+healthy, intelligent and wise, they fear very little and
+can emit roars that instill fear into the very souls of
+their enemies. They learn to resist the elements fairly
+well as they gain experience. They make good warriors.
+
+#####GStat Modifiers
+Strength +2
+Intelligence +1
+Wisdom +1
+Dexterity -1
+Constitution +2
+Charisma +1
+Hit Dice +2 sides
+Exp Penalty +30%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Combat 0.000 [0.100]
+Weaponmastery 0.000 [0.100]
+
+#####GStarting Equipment
+A Rawrog character begins the game with:
+ Some rations
+ Some torches \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_red.txt b/lib/mods/theme/help/rm_red.txt
new file mode 100644
index 00000000..ac2ef2fc
--- /dev/null
+++ b/lib/mods/theme/help/rm_red.txt
@@ -0,0 +1,30 @@
+~~~~~01|Red
+~~~~~02|Race Modifiers|Red
+#####R=== Red Dragons ===
+
+#####GDescription
+These dragons are utterly in command of the element of fire.
+Not only are they surrounded by a fiery blaze, but they learn
+to withstand the effects of fire entirely as they become more
+experienced. They are also able to draw upon their master
+element to become even stronger as they mature. They can
+breathe fire and they have access to the Fire school of magic.
+
+#####GStat Modifiers
+Strength +3
+Intelligence 0
+Wisdom 0
+Dexterity 0
+Constitution 0
+Charisma 0
+Hit Dice +0 sides
+Exp Penalty +0%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Fire 1.000 [0.600]
+
+#####GStarting Equipment
+A Red dragon character begins the game with:
+ Some rations
+ Some torches
+ A spellbook of Globe of Light
diff --git a/lib/mods/theme/help/rm_sarnrog.txt b/lib/mods/theme/help/rm_sarnrog.txt
new file mode 100644
index 00000000..4b34f0c0
--- /dev/null
+++ b/lib/mods/theme/help/rm_sarnrog.txt
@@ -0,0 +1,30 @@
+~~~~~01|Sarnrog
+~~~~~02|Race Modifiers|Sarnrog
+#####R=== Sarnroeg (Stone Demons ===
+
+#####GDescription
+Medium-sized winged humanoids that look like they are
+made of stone - they are Druedain that have been corrupted
+by Melkor. Though clumsy and dumb, they are strong and stout.
+They learn to resist the basic elements quite well, and they
+can sustain themselves by eating stones and rocks. They have
+the ability to throw boulders at their enemies, and are good
+at digging tunnels.
+
+#####GStat Modifiers
+Strength +2
+Intelligence -1
+Wisdom -2
+Dexterity -2
+Constitution +2
+Charisma -1
+Hit Dice +2 sides
+Exp Penalty +20%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Boulder-throwing 1.000 [0.500]
+
+#####GStarting Equipment
+A Sarnrog character begins the game with:
+ Some rations
+ Some torches \ No newline at end of file
diff --git a/lib/mods/theme/help/rm_skel.txt b/lib/mods/theme/help/rm_skel.txt
new file mode 100644
index 00000000..aff6408d
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/rm_spec.txt b/lib/mods/theme/help/rm_spec.txt
new file mode 100644
index 00000000..01802465
--- /dev/null
+++ b/lib/mods/theme/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 +4
+Wisdom +4
+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/mods/theme/help/rm_vamp.txt b/lib/mods/theme/help/rm_vamp.txt
new file mode 100644
index 00000000..22ae6514
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/rm_white.txt b/lib/mods/theme/help/rm_white.txt
new file mode 100644
index 00000000..00f31694
--- /dev/null
+++ b/lib/mods/theme/help/rm_white.txt
@@ -0,0 +1,32 @@
+~~~~~01|White
+~~~~~02|Race Modifiers|White
+#####R=== White Dragons ===
+
+#####GDescription
+White dragons are extremely intelligent and calculating,
+always able to keep a cool head. They can breathe cold
+and freeze time and space long enough to divine what the
+future and present have in store, and to find out the
+abilities of objects. Their intelligence will increase as
+they gain experience. Naturally resistant to cold, they
+will learn to withstand its effects entirely with time.
+They are especially susceptible to fire attacks, however.
+
+#####GStat Modifiers
+Strength 0
+Intelligence +3
+Wisdom 0
+Dexterity 0
+Constitution 0
+Charisma 0
+Hit Dice +0 sides
+Exp Penalty +0%
+
+#####GSkill Bonuses (supplementary to existing skills)
+Divination 1.000 [0.600]
+
+#####GStarting Equipment
+A White dragon character begins the game with:
+ Some rations
+ Some torches
+ A spellbook of Sense Monsters
diff --git a/lib/mods/theme/help/rm_zomb.txt b/lib/mods/theme/help/rm_zomb.txt
new file mode 100644
index 00000000..be89162b
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/skills.txt b/lib/mods/theme/help/skills.txt
new file mode 100644
index 00000000..fe68da6e
--- /dev/null
+++ b/lib/mods/theme/help/skills.txt
@@ -0,0 +1,532 @@
+|||||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*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, and Thaumaturgy.
+~~~~~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.
+~~~~~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/mods/theme/help/spoil_faq.txt b/lib/mods/theme/help/spoil_faq.txt
new file mode 100644
index 00000000..fd3a0f42
--- /dev/null
+++ b/lib/mods/theme/help/spoil_faq.txt
@@ -0,0 +1,63 @@
+|||||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!
+
+~~~~~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 a relic and wants me to go
+#####G find it again. How many of these are there?
+
+A: You can receive up to seven 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.
+~~~~~23|Spoilers|Secret Valley
+#####G------------------------------------------------------------------------------
+#####GQ: What's this Secret Valley place in the northern part of the wilderness?
+
+This is a stay-over from regular ToME, where that valley is the location of
+Gondolin. In Theme, Gondolin is elsewhere, and the Secret Valley is simply a
+cosmetic change to avoid confusion.
+
+~~~~~24|Spoilers|Erebor
+#####G------------------------------------------------------------------------------
+#####GQ: Why can't I get past the first level of Erebor?
+
+Think back to "The Hobbit". What did Bilbo and his friends use to enter the Lonely
+Mountain? Locating the town of Dale and asking the mayor might prove profitable...
diff --git a/lib/mods/theme/help/spoiler.hlp b/lib/mods/theme/help/spoiler.hlp
new file mode 100644
index 00000000..996c0d32
--- /dev/null
+++ b/lib/mods/theme/help/spoiler.hlp
@@ -0,0 +1,17 @@
+|||||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]
+ *****/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/mods/theme/help/tome_faq.txt b/lib/mods/theme/help/tome_faq.txt
new file mode 100644
index 00000000..55f6375b
--- /dev/null
+++ b/lib/mods/theme/help/tome_faq.txt
@@ -0,0 +1,377 @@
+|||||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.
+
+#####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.
+~~~~~11|Runes
+#####G------------------------------------------------------------------------------
+#####GQ: I keep coming across "runes". What are they?
+
+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/mods/theme/help/version.txt b/lib/mods/theme/help/version.txt
new file mode 100644
index 00000000..501be7f6
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/whattome.txt b/lib/mods/theme/help/whattome.txt
new file mode 100644
index 00000000..43ebb2e1
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/help/wishing.txt b/lib/mods/theme/help/wishing.txt
new file mode 100644
index 00000000..d16f5ae9
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/note/delete.me b/lib/mods/theme/note/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/mods/theme/note/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/mods/theme/permission.txt b/lib/mods/theme/permission.txt
new file mode 100644
index 00000000..bb850954
--- /dev/null
+++ b/lib/mods/theme/permission.txt
@@ -0,0 +1,3 @@
+Permission hereby granted to use any and all parts of the Theme module in other modules, games, fan fiction, whatever, as long as it's nonprofit. Please give credit where credit is due and don't simply take my work and use it as though it was always yours. Adding something of mine to an *_info file? Include a comment that credits Theme.
+
+furiosity \ No newline at end of file
diff --git a/lib/mods/theme/pref/422color.prf b/lib/mods/theme/pref/422color.prf
new file mode 100644
index 00000000..309c36b0
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/colors.prf b/lib/mods/theme/pref/colors.prf
new file mode 100644
index 00000000..fa4d3f05
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/font-ibm.prf b/lib/mods/theme/pref/font-ibm.prf
new file mode 100644
index 00000000..91ca81bb
--- /dev/null
+++ b/lib/mods/theme/pref/font-ibm.prf
@@ -0,0 +1,365 @@
+# 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
+
+# dark pit
+F:248:0x04:0x27
+
+##### 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/mods/theme/pref/font-mac.prf b/lib/mods/theme/pref/font-mac.prf
new file mode 100644
index 00000000..d15c2e47
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/font-win.prf b/lib/mods/theme/pref/font-win.prf
new file mode 100644
index 00000000..6c54e7e5
--- /dev/null
+++ b/lib/mods/theme/pref/font-win.prf
@@ -0,0 +1,304 @@
+# 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
+
+# glass wall
+F:103:0x0E/0x1F
+
+# 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:244:0x07/0x7F
+
+# ethereal wall
+F:245:0x01/0x1F
+
+# glacial wall
+F:246:0x0E/0x7F
+
+# battlement
+F:247:0x01/0x7F
+
+# dark pit
+F:248:0x04:0x27
+
+##### 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/mods/theme/pref/font-x11.prf b/lib/mods/theme/pref/font-x11.prf
new file mode 100644
index 00000000..57828a56
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/font-xxx.prf b/lib/mods/theme/pref/font-xxx.prf
new file mode 100644
index 00000000..3e5d24ee
--- /dev/null
+++ b/lib/mods/theme/pref/font-xxx.prf
@@ -0,0 +1,469 @@
+# 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
+
+# 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/mods/theme/pref/font.prf b/lib/mods/theme/pref/font.prf
new file mode 100644
index 00000000..38614683
--- /dev/null
+++ b/lib/mods/theme/pref/font.prf
@@ -0,0 +1,40 @@
+# 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 gcu]
+%:font-gcu.prf
+
+?:[EQU $SYS mac]
+%:font-mac.prf
+
+?:[EQU $SYS win]
+%:font-win.prf
+
+?:1
diff --git a/lib/mods/theme/pref/pref-gcu.prf b/lib/mods/theme/pref/pref-gcu.prf
new file mode 100644
index 00000000..cbc80ada
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/pref-mac.prf b/lib/mods/theme/pref/pref-mac.prf
new file mode 100644
index 00000000..88e5618e
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/pref-sdl.prf b/lib/mods/theme/pref/pref-sdl.prf
new file mode 100644
index 00000000..3bc0f030
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/pref-win.prf b/lib/mods/theme/pref/pref-win.prf
new file mode 100644
index 00000000..7b1501f1
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/pref-x11.prf b/lib/mods/theme/pref/pref-x11.prf
new file mode 100644
index 00000000..f4df9376
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/pref.prf b/lib/mods/theme/pref/pref.prf
new file mode 100644
index 00000000..310e5b8a
--- /dev/null
+++ b/lib/mods/theme/pref/pref.prf
@@ -0,0 +1,297 @@
+# 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 mac]
+%:pref-mac.prf
+
+?:[EQU $SYS win]
+%:pref-win.prf
+
+?:[EQU $SYS sdl]
+%:pref-sdl.prf
+
+?:1
diff --git a/lib/mods/theme/pref/trap-xxx.prf b/lib/mods/theme/pref/trap-xxx.prf
new file mode 100644
index 00000000..f4d699a0
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/user.prf b/lib/mods/theme/pref/user.prf
new file mode 100644
index 00000000..04715ebc
--- /dev/null
+++ b/lib/mods/theme/pref/user.prf
@@ -0,0 +1,33 @@
+# 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 mac]
+%:user-mac.prf
+
+?:[EQU $SYS win]
+%:user-win.prf
+
+?:1
diff --git a/lib/mods/theme/pref/xtra-gcu.prf b/lib/mods/theme/pref/xtra-gcu.prf
new file mode 100644
index 00000000..7097658e
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/pref/xtra-xxx.prf b/lib/mods/theme/pref/xtra-xxx.prf
new file mode 100644
index 00000000..0c6186de
--- /dev/null
+++ b/lib/mods/theme/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/mods/theme/save/delete.me b/lib/mods/theme/save/delete.me
new file mode 100644
index 00000000..2e65efe2
--- /dev/null
+++ b/lib/mods/theme/save/delete.me
@@ -0,0 +1 @@
+a \ No newline at end of file
diff --git a/lib/mods/theme/theme.txt b/lib/mods/theme/theme.txt
new file mode 100644
index 00000000..f85ba157
--- /dev/null
+++ b/lib/mods/theme/theme.txt
@@ -0,0 +1,313 @@
+Chronological list of changes made in Theme module.
+
+Detailed list: http://www.zionmainframe.ca/tome/theme/changes.html
+Known bugs: http://www.zionmainframe.ca/tome/theme/bugs.html
+Credits: http://www.zionmainframe.ca/tome/theme/credits.html
+
+Theme 0.0.1 AKA "The Beginning"
+
+ - 20 new terrain features
+ - removed Alchemy [the class, the skill, and the Artifact Creation ability]
+ - removed DeathMolds [replaced with Dragons from Annals of Ea]
+ - Morgoth's corrupted races may no longer play as Lost Souls
+ - changed the intro screen and wording
+ - removed access to the Lost Hobbit quest (NEVER_GENE to both Proudfoots)
+
+Theme 0.0.2 AKA "Object Mayhem"
+
+ - removed all items relating to alchemy and portable hole
+ - changed many descriptions and item names
+ - fountainized more potions
+ - Greater Ration of Health and the Potion of Learning are no longer artifacts, but more rare.
+ - rings and amulets are all indestructible now
+ - amulets of Devotion are blessed
+ - Adamantite rods are now Tilkal rods and more rare
+ - Espadons must be wielded two-handed
+ - the Bardiche replaces the Basillard
+ - Adamantite armour is now Galvorn
+ - Paper Armour makes wearer sensitive to fire; Fur cloaks grant cold resistance.
+ - Rhino Hide Armour became Mumak Hide Armour
+ - Golden Ring Mail replaces Stone and Hide Armour
+ - adjusted leather jacket, renamed to leather jerkin
+
+Theme 0.0.3 AKA "Wordsmith"
+
+ - new rumours
+ - revamped list of silly monster names
+ - junkarts got a total facelift
+ - monster speech made more thematic all across the board
+ - more thematic last words (both monsters and player)
+
+Theme 0.0.4 AKA "Monstervore"
+(veteran *banders beware: many, many changes to monsters. Read the detailed list, the starting parchment, or take extra time to look around. 'Monstervore' is about right for the version name, in fact.)
+
+ - more neutral monsters all across the board
+ - Zangband and CthAngband monsters are gone, joke monsters greatly revamped.
+ - Angels, icky things, centipedes, and kobolds are gone.
+ - many colour changes to reduce confusion, different monsters should be easier to, uh, differentiate
+ - gnomes, leprechauns, and lizard men are now using the 'l' symbol instead of 'h'
+ - dwarves use the 'k' symbol instead of 'h'
+ - wainriders, black numenoreans, and oathbreakers are in, knights of all kinds are out.
+ - none of the 'cold' variety dragons have flight
+ - mature non-aquatic dragons have been renamed to drakes
+ - great wyrms are now great worms
+ - baby dragons are hatchling dragons
+ - Smaug no longer erroneously described as an 'uruloki' - uruloki can't fly.
+ - Nazgul are all represented by the letter N now (too many dark gray Ws as it is)
+ - renamed some uniques to be more in-theme
+ - Huan and Shadowfax are now neutral
+ - brought back aquatic elven warriors (friend), aquatic elven mages (friend), Headless, and mermaids from Z.
+ - completely new set of demons (major and minor) all in-theme, replacing the old D&D demons.
+ - the White Balrog renamed and re-described to avoid confusion due to the above.
+ - minotaurs, maulotaurs, and hippocampi are gone. The unique Maze guardian stays.
+
+Theme 0.0.5 AKA "Artifactual"
+
+ - removed joke artifacts, many ammo artifacts, the phial of undeath, and toris mejistos
+ - changed many artifact names
+ - broadened some descriptions
+ - Gurthang renamed to Angalachel (detailed explanation in changelog.txt)
+ - The sword formerly known as Vorpal Blade is sentient
+ - Sting is a short sword, not small sword
+ - Anduril now resists being shattered by Morgul beings.
+ - Angrist is a broken dagger and gives a luck bonus.
+ - Theoden King had a sword, not an axe. That axe is now of Dain Ironfoot.
+
+Theme 0.0.6 AKA "Brave New World"
+
+ - wilderness map corrected a lot to fit the Middle-Earth map, dungeon locations have changed considerably.
+ - some places on the map have names now, so you can wander through Rivendell, the Brown Lands, Udun, etc.
+ - three words: the Dead Marshes. Avoid 'em.
+ - some dungeons renamed; many new terrain features.
+ - in general, the wilderness is a lot more dangerous to travel (thanks to BlackSmurf for the inspiration).
+ - Angband levels 101-127 separated out into Utumno.
+ - 7 new dungeons, all with a final guardian and guaranteed artifact or randart.
+ - random towns in Cirith Ungol, Blue Mountains, Dol Amroth, Near Harad.
+ - Gondolin map tweaked a bit to suit new location.
+ - Lothlorien has been renamed to Caras Galadhon.
+
+Theme 0.0.7 AKA "The Forge"
+
+ - 16 new item types, some from T-Plus, some as suggested in the forum, others my own - mostly for 'game flavour' [no pun intended :-)]
+ - 37 new ego-types, many from T-Plus and Annals of Ea
+ - 35 new artifacts, many from T-Plus, some from Annals of Ea, some implemented based on forum suggestions
+ - 6 new item sets, one of my own, one from T-Plus, others as suggested in the forum
+ - over 200 new monsters:
+ - centipedes replaced by 9 types of cattle and one unique (Boar of Everholt)
+ - icky things replaced by mewlips (undead things of hobbit-lore), 14 types
+ - 59 new uniques (8 are unkillable, wilderness-only Valar, 8 are joke monsters)
+
+Theme 0.0.8 AKA "Gone to Town"
+
+ - numerous bug fixes, refer to changelog.txt for details
+ - 4 new artifacts, 4 new sets, 3 new ego types, some item and monster tweaks
+ - 14 new terrain features for town use
+ - 12 new towns, of these 7 are not visible on the wilderness map (all have parchments)
+ - Caras Galadhon map redone completely, Gondolin map tweaked to suit new location
+ - 'Rest for the night' option available for free in player home and some thematic locations
+ - 8 new store types
+ - rewritten store owner list
+
+Theme 0.0.9 AKA "Out, Out, Damned Spot!"
+
+ - a bugfix release on the heels of ToME 2.2.7; see changelog.txt for details
+ - Player can now push past neutral monsters, thanks to a lua script written by BauMog
+ - Bjorn (a joke unique based on a DiTLer) replaces Varda
+ - Player can get free dinner at some thematic locations where player race is liked
+ - Increased quality of rods at the magic rod market
+ - The Valar have an equal number of non-spoiler lines now.
+ - Renumbered the planned releases (see below)
+ - From now on bugfix releases will always come separately.
+
+Theme 0.1.0 AKA "A Farewell to Kobolds"
+
+ - several bugfixes, see changelog.txt for details
+ - only in-theme races can now be Lost Souls: Eldar and Maiar. I put half-elves among them too, as there's conflicting information on whether half-elves made it to the Halls of Mandos or not.
+ - thematised Maiar, Ents, Hobbits, Dwarves, and Wood Elves.
+ - 4 new races: Druedain (replace Kobolds), Easterlings, Roeg (minor demons), Eagles (replace Thunderlords)
+ - 6 dragon subraces
+ - 10 demon subraces
+ - Beefed up Rangers and removed ability to worship anyone but Melkor from Demonologists and Necromancers.
+ - 7 new subclasses
+
+Theme 0.1.1 AKA "Sleeping Awake"
+
+ - several bugfixes, see changelog.txt for details
+ - some tweaks to the Clairvoyants, Ascetics, and Mercenaries
+ - a new joke monster, and more monspeak.txt lines
+ - 3 new ego types
+ - some preliminary changes to lua scripts, mostly stuff from the ToME Script library and T-Plus
+
+Theme 0.1.2 AKA "Friends and Neighbours"
+
+ - a player/monster race alignment scheme based on the T-Plus code for Angel alignments
+
+Theme 0.1.3 AKA "Odds and Ends"
+
+ - tweaks to various info files
+ - added 6 Dwarven Rings of Power
+
+Theme 0.1.4 AKA "Milk and Bones"
+
+ - two lua scriptlets for players who like Ammo creation but don't like scumming for junk
+ - two lua scriptlets for some thematic fun
+
+Theme 0.5.0 AKA "Eternity of Stars"
+
+ - removed weapons belonging to the Valar from a_info (changed names)
+ - renamed existing Eternity items
+ - added 8 new Ultimate items for the Void-divers
+
+Theme 1.0.0 AKA "Shiny and New"
+
+ - Four new Valar: Varda, Aule, Ulmo, Mandos. All with their own magic schools, priest classes, and quest temples.
+ - Fully documented all the changes so far for the ingame help.
+ - Some monster and item tweaks.
+ - Numerous tweaks to the new races and classes.
+ - The Pacifists are now a class alongside warriors, mages, and priests - there are two subclasses.
+
+Theme 1.0.1. AKA "Khazad aimenu!"
+
+ - Fixed some God-related issues
+ - Some miscellaneous cosmetic fixes
+ - Updated automatiser file to work regardless of object flavour display
+ - 4 new axe types from T-Plus
+ - New ego magestaff
+
+Theme 1.0.2. AKA "Order of the Swan"
+
+ - Fixed the Elemental Mastery ego-type
+ - God quest fixes
+ - 3 new ego types, 2 new artifacts, and 1 new item set
+ - Fog on the Barrow-Downs is back, toned down.
+ - New monster: Knight of the Swan (white 'p')
+ - Mithril armour adapted from T-Plus
+
+Theme 1.0.3 AKA "Old and New"
+
+ - New special level in Erebor
+ - New amulet ego-type
+ - Axed the Rustproof and of Valour ego-types
+ - Tweaked Ranger class further
+ - (2.3.0 CVS) Axed several out-of-theme artifacts to make room for new ones from ToME3
+ - (2.3.0 CVS) War-mage class (T-Plus) is useless with the new Mage class, removed
+
+Theme 1.0.4 AKA "Jocos Et Dii Amant"
+
+ - Ulmo now dislikes the player using fire magic and Orc players (Thanks to LogrusMage)
+ - Fixed the monster flag problem which caused lua errors in Ulmo's temple dungeon
+ - Commented out Nienna and Orome in r_info
+
+Theme 1.0.5 AKA "As You Like It"
+
+ - New tiles for terrain and objects
+ - Pref file for 16x16 tileset uses ASCII characters for the *missing* monsters
+ - Pref file for 8x8 tileset uses ASCII characters for *all* monsters.
+
+Theme 1.0.6 AKA "Flame of Udun"
+
+ - Fixed a bug that prevented Ulmo from working properly
+ - Re-added the Maia restrictions for corruptions
+ - Added some new corruptions (from MM's script)
+
+Theme 1.0.7 AKA "All Shall Wait"
+
+ - Monster tiles and pref files done
+ - Renamed *thanc daggers
+ - Renamed Halls of Mandos and lifted the elves-only restrictions.
+ - Thror's map is now actually a map
+ - More corruptions from MM's script
+ - Added NO_TARGET to monsters that start out neutral
+
+Theme 1.0.8 AKA "To The Keep!"
+
+ - Several small bugfixes
+ - Some documentation updates
+ - Fixed Aule's spells to prevent insane pval increases
+ - (2.3.0 CVS) Several documentation tweaks
+ - (2.3.0 CVS) misc.lua was not called from init.lua
+
+Theme 1.0.9 AKA "Through the Darkness"
+
+ - Several small bugfixes
+ - Several documentation and in-game parchment updates
+ - Pippin's dagger (former *thanc) is now part of another set, too
+ - Graphics package updated, plus .gif files as in 2.3.0
+ - (2.3.0 CVS) Removed museums from all towns but three
+
+Theme 1.1.0 AKA "Forever There"
+
+ - Minor piety-gain tweaks to Ulmo and Melkor
+ - Took away 'normal' magic schools from Thaumaturgists
+ - Mages got .400 in Disarming
+ - Trolls got a bonus to hafted-mastery
+
+Theme 1.1.1 AKA "I Amar Prestar Aen"
+
+ - New special level by Burb Lulls
+ - Some item, monster, artifact, class, and ego tweaks
+ - Fireproofing quest is now for a rune, not potion
+ - Tweaked the Tears of Luthien spell
+
+Theme 1.1.2 AKA "Serpensortia"
+
+ - minor bugfixes/tweaks: items, races, classes
+ - (2.3.0 CVS) fixed the new jewelry crashing bug
+ - (2.3.0 CVS) removed several types of old rings and amulets
+
+Theme 1.1.3 AKA "Phoenix Song"
+
+ - Updated all files from the 2.3.0 (CVS) version to 2.3.1
+ - Removed Peregrin's dagger from the Peregrin's Gear set.
+ - Aule followers should be able to sacrifice self-made items now.
+ - The coordinates for Melkor's god quests have been fixed.
+ - Added more text to the Erebor map and key to make sure player keeps them.
+
+Theme 1.1.4 AKA "Release the River"
+
+ - Updated all files from the 2.3.1 version to 2.3.3
+ - Fixed bugs with maps, sets, jumpgates and quests.
+ - Updated a number of help files on gods and magic.
+ - Retired two ego types and reworked some others.
+ - Retired the Polymorph corruption.
+ - Replaced Carlammas with a new artifact and added a new ring ego type.
+
+Theme 1.1.5 AKA "Angles and Corners"
+
+ - Some feature flag tweaks.
+ - Orc Cave now leads out on the other side of the Misty Mountains.
+ - Two new special levels to accomodate Erebor quest items; new special final level in Isengard.
+ - Eagles' nest in Khazad-dum.
+
+Theme 1.1.6 AKA "Curses to This Mirage"
+
+ - Some monster flag tweaks.
+ - Neutral monsters forced into wilderness.
+ - Fixed player-related bugs with stat gain and starting equipment.
+
+Theme 1.1.7 AKA "Poor Madeleine"
+
+ - Some item tweaks.
+ - Removed several miscellaneous food items.
+ - 5 new item types.
+
+Theme 1.1.8 AKA "Little White Candle"
+
+ - Numerous ego item tweaks.
+ - Renamed several ego types to be more in-theme.
+ - Removed Rings of the Elements to avoid redundancy.
+ - 3 new ego types.
+
+Theme 1.1.9 AKA "Kit Bag Full of Marbles"
+
+ - Several randart tweaks.
+ - FF now offers three additional skills.
+ - Non-elves can no longer be LostSouls.
+ - Flight at birth now means mountain climbing at clvl50
+
+Theme 1.2.0 AKA "Twining Light"
+
+ - Several artifact tweaks.
+ - Removed the dwarven rings of power.
+ - 3 new artifacts.
+ - 1 new item set. \ No newline at end of file
diff --git a/lib/mods/theme/user/all.prf b/lib/mods/theme/user/all.prf
new file mode 100644
index 00000000..67671aa4
--- /dev/null
+++ b/lib/mods/theme/user/all.prf
@@ -0,0 +1,457 @@
+
+
+# Automatic option dump
+
+# Option 'Rogue-like commands'
+X:rogue_like_commands
+
+# Option 'Activate quick messages'
+Y:quick_messages
+
+# Option 'Prompt before picking things up'
+X:carry_query_flag
+
+# Option 'Use old target by default'
+X:use_old_target
+
+# Option 'Pick things up by default'
+X:always_pickup
+
+# Option 'Prompt before picking up heavy objects'
+Y:prompt_pickup_heavy
+
+# Option 'Repeat obvious commands'
+Y:always_repeat
+
+# Option 'Audible bell (on errors, etc)'
+X:ring_bell
+
+# Option 'Use color if possible (slow)'
+Y:use_color
+
+# Option 'Run past stairs'
+Y:find_ignore_stairs
+
+# Option 'Run through open doors'
+Y:find_ignore_doors
+
+# Option 'Run past known corners'
+Y:find_cut
+
+# Option 'Run into potential corners'
+Y:find_examine
+
+# Option 'Disturb whenever any monster moves'
+X:disturb_move
+
+# Option 'Disturb whenever viewable monster moves'
+Y:disturb_near
+
+# Option 'Disturb whenever map panel changes'
+Y:disturb_panel
+
+# Option 'Disturb whenever leaving trap-detected area'
+Y:disturb_detect
+
+# Option 'Disturb whenever player state changes'
+Y:disturb_state
+
+# Option 'Disturb whenever boring things happen'
+X:disturb_minor
+
+# Option 'Disturb whenever random things happen'
+X:disturb_other
+
+# Option 'Alert user to critical hitpoints'
+Y:alert_hitpoint
+
+# Option 'Alert user to various failures'
+Y:alert_failure
+
+# Option 'Get last words when the character dies'
+Y:last_words
+
+# Option 'Confirm to wear/wield known cursed items'
+Y:confirm_wear
+
+# Option 'Prompt before exiting a dungeon level'
+X:confirm_stairs
+
+# Option 'Disturb when visible pets move'
+X:disturb_pets
+
+# Option 'Automatically open doors'
+Y:easy_open
+
+# Option 'Automatically disarm traps'
+Y:easy_disarm
+
+# Option 'Automatically tunnel walls'
+Y:easy_tunnel
+
+# Option 'Auto-haggle in stores'
+Y:auto_haggle
+
+# Option 'Auto-scum for good levels'
+Y:auto_scum
+
+# Option 'Expand the power of the look command'
+Y:expand_look
+
+# Option 'Expand the power of the list commands'
+Y:expand_list
+
+# Option 'Map remembers all perma-lit grids'
+Y:view_perma_grids
+
+# Option 'Map remembers all torch-lit grids'
+Y:view_torch_grids
+
+# Option 'Allow some monsters to carry light'
+Y:monster_lite
+
+# Option 'Generate dungeons with aligned rooms'
+Y:dungeon_align
+
+# Option 'Generate dungeons with connected stairs'
+Y:dungeon_stair
+
+# Option 'Monsters chase current location (v.slow)'
+X:flow_by_sound
+
+# Option 'Monsters learn from their mistakes'
+X:smart_learn
+
+# Option 'Allow unusually small dungeon levels'
+Y:small_levels
+
+# Option 'Allow empty 'arena' levels'
+Y:empty_levels
+
+# Option 'Reduce lite-radius when running'
+X:view_reduce_lite
+
+# Option 'Avoid checking for user abort'
+X:avoid_abort
+
+# Option 'Avoid extra shimmering (fast)'
+Y:avoid_shimmer
+
+# Option 'Avoid processing special colors (fast)'
+X:avoid_other
+
+# Option 'Flush input on various failures'
+Y:flush_failure
+
+# Option 'Flush input whenever disturbed'
+X:flush_disturb
+
+# Option 'Flush input before every command'
+X:flush_command
+
+# Option 'Flush output before every command'
+Y:fresh_before
+
+# Option 'Flush output after every command'
+X:fresh_after
+
+# Option 'Flush output after every message'
+X:fresh_message
+
+# Option 'Compress messages in savefiles'
+Y:compress_savefile
+
+# Option 'Hilite the player with the cursor'
+X:hilite_player
+
+# Option 'Use special colors for torch-lit grids'
+X:view_yellow_lite
+
+# Option 'Use special colors for 'viewable' grids'
+X:view_bright_lite
+
+# Option 'Use special colors for wall grids (slow)'
+X:view_granite_lite
+
+# Option 'Use special colors for floor grids (slow)'
+X:view_special_lite
+
+# Option 'Center the view on the player (very slow)'
+X:center_player
+
+# Option 'Ingame contextual help'
+X:ingame_help
+
+# Option 'Use the old(Z) coloring scheme(reload the game)'
+X:old_colors
+
+# Option 'Automatically clear '-more-' prompts'
+X:auto_more
+
+# Option 'Player char represent his/her health'
+Y:player_char_health
+
+# Option 'Stats are represented in a linear way'
+Y:linear_stats
+
+# Option 'In option windows, just omit the select char'
+X:inventory_no_move
+
+# Option 'Allow objects to stack on floor'
+Y:testing_stack
+
+# Option 'Allow monsters to carry objects'
+Y:testing_carry
+
+# Window 'Mirror', Flag 'Display inven/equip'
+W:1:0:0
+
+# Window 'Mirror', Flag 'Display equip/inven'
+W:1:1:0
+
+# Window 'Mirror', Flag 'Display character'
+W:1:3:0
+
+# Window 'Mirror', Flag 'Show visible monsters'
+W:1:4:0
+
+# Window 'Mirror', Flag 'Display IRC messages'
+W:1:5:0
+
+# Window 'Mirror', Flag 'Display messages'
+W:1:6:0
+
+# Window 'Mirror', Flag 'Display overhead view'
+W:1:7:0
+
+# Window 'Mirror', Flag 'Display monster recall'
+W:1:8:0
+
+# Window 'Mirror', Flag 'Display object recall'
+W:1:9:0
+
+# Window 'Mirror', Flag 'Display snap-shot'
+W:1:11:0
+
+# Window 'Mirror', Flag 'Display borg messages'
+W:1:14:0
+
+# Window 'Mirror', Flag 'Display borg status'
+W:1:15:0
+
+# Window 'Recall', Flag 'Display inven/equip'
+W:2:0:0
+
+# Window 'Recall', Flag 'Display equip/inven'
+W:2:1:0
+
+# Window 'Recall', Flag 'Display character'
+W:2:3:0
+
+# Window 'Recall', Flag 'Show visible monsters'
+W:2:4:0
+
+# Window 'Recall', Flag 'Display IRC messages'
+W:2:5:0
+
+# Window 'Recall', Flag 'Display messages'
+W:2:6:0
+
+# Window 'Recall', Flag 'Display overhead view'
+W:2:7:0
+
+# Window 'Recall', Flag 'Display monster recall'
+W:2:8:0
+
+# Window 'Recall', Flag 'Display object recall'
+W:2:9:0
+
+# Window 'Recall', Flag 'Display snap-shot'
+W:2:11:0
+
+# Window 'Recall', Flag 'Display borg messages'
+W:2:14:0
+
+# Window 'Recall', Flag 'Display borg status'
+W:2:15:0
+
+# Window 'Choice', Flag 'Display inven/equip'
+W:3:0:0
+
+# Window 'Choice', Flag 'Display equip/inven'
+W:3:1:0
+
+# Window 'Choice', Flag 'Display character'
+W:3:3:0
+
+# Window 'Choice', Flag 'Show visible monsters'
+W:3:4:0
+
+# Window 'Choice', Flag 'Display IRC messages'
+W:3:5:0
+
+# Window 'Choice', Flag 'Display messages'
+W:3:6:0
+
+# Window 'Choice', Flag 'Display overhead view'
+W:3:7:0
+
+# Window 'Choice', Flag 'Display monster recall'
+W:3:8:0
+
+# Window 'Choice', Flag 'Display object recall'
+W:3:9:0
+
+# Window 'Choice', Flag 'Display snap-shot'
+W:3:11:0
+
+# Window 'Choice', Flag 'Display borg messages'
+W:3:14:0
+
+# Window 'Choice', Flag 'Display borg status'
+W:3:15:0
+
+# Window 'Term-4', Flag 'Display inven/equip'
+W:4:0:0
+
+# Window 'Term-4', Flag 'Display equip/inven'
+W:4:1:0
+
+# Window 'Term-4', Flag 'Display character'
+W:4:3:0
+
+# Window 'Term-4', Flag 'Show visible monsters'
+W:4:4:0
+
+# Window 'Term-4', Flag 'Display IRC messages'
+W:4:5:0
+
+# Window 'Term-4', Flag 'Display messages'
+W:4:6:0
+
+# Window 'Term-4', Flag 'Display overhead view'
+W:4:7:0
+
+# Window 'Term-4', Flag 'Display monster recall'
+W:4:8:0
+
+# Window 'Term-4', Flag 'Display object recall'
+W:4:9:0
+
+# Window 'Term-4', Flag 'Display snap-shot'
+W:4:11:0
+
+# Window 'Term-4', Flag 'Display borg messages'
+W:4:14:0
+
+# Window 'Term-4', Flag 'Display borg status'
+W:4:15:0
+
+# Window 'Term-5', Flag 'Display inven/equip'
+W:5:0:0
+
+# Window 'Term-5', Flag 'Display equip/inven'
+W:5:1:0
+
+# Window 'Term-5', Flag 'Display character'
+W:5:3:0
+
+# Window 'Term-5', Flag 'Show visible monsters'
+W:5:4:0
+
+# Window 'Term-5', Flag 'Display IRC messages'
+W:5:5:0
+
+# Window 'Term-5', Flag 'Display messages'
+W:5:6:0
+
+# Window 'Term-5', Flag 'Display overhead view'
+W:5:7:0
+
+# Window 'Term-5', Flag 'Display monster recall'
+W:5:8:0
+
+# Window 'Term-5', Flag 'Display object recall'
+W:5:9:0
+
+# Window 'Term-5', Flag 'Display snap-shot'
+W:5:11:0
+
+# Window 'Term-5', Flag 'Display borg messages'
+W:5:14:0
+
+# Window 'Term-5', Flag 'Display borg status'
+W:5:15:0
+
+# Window 'Term-6', Flag 'Display inven/equip'
+W:6:0:0
+
+# Window 'Term-6', Flag 'Display equip/inven'
+W:6:1:0
+
+# Window 'Term-6', Flag 'Display character'
+W:6:3:0
+
+# Window 'Term-6', Flag 'Show visible monsters'
+W:6:4:0
+
+# Window 'Term-6', Flag 'Display IRC messages'
+W:6:5:0
+
+# Window 'Term-6', Flag 'Display messages'
+W:6:6:0
+
+# Window 'Term-6', Flag 'Display overhead view'
+W:6:7:0
+
+# Window 'Term-6', Flag 'Display monster recall'
+W:6:8:0
+
+# Window 'Term-6', Flag 'Display object recall'
+W:6:9:0
+
+# Window 'Term-6', Flag 'Display snap-shot'
+W:6:11:0
+
+# Window 'Term-6', Flag 'Display borg messages'
+W:6:14:0
+
+# Window 'Term-6', Flag 'Display borg status'
+W:6:15:0
+
+# Window 'Term-7', Flag 'Display inven/equip'
+W:7:0:0
+
+# Window 'Term-7', Flag 'Display equip/inven'
+W:7:1:0
+
+# Window 'Term-7', Flag 'Display character'
+W:7:3:0
+
+# Window 'Term-7', Flag 'Show visible monsters'
+W:7:4:0
+
+# Window 'Term-7', Flag 'Display IRC messages'
+W:7:5:0
+
+# Window 'Term-7', Flag 'Display messages'
+W:7:6:0
+
+# Window 'Term-7', Flag 'Display overhead view'
+W:7:7:0
+
+# Window 'Term-7', Flag 'Display monster recall'
+W:7:8:0
+
+# Window 'Term-7', Flag 'Display object recall'
+W:7:9:0
+
+# Window 'Term-7', Flag 'Display snap-shot'
+W:7:11:0
+
+# Window 'Term-7', Flag 'Display borg messages'
+W:7:14:0
+
+# Window 'Term-7', Flag 'Display borg status'
+W:7:15:0
+
diff --git a/lib/mods/theme/user/automat.atm b/lib/mods/theme/user/automat.atm
new file mode 100644
index 00000000..03e31974
--- /dev/null
+++ b/lib/mods/theme/user/automat.atm
@@ -0,0 +1,667 @@
+-- This automatiser file was created by Feanor for the Annals of Ea module.
+clean_ruleset()
+add_ruleset
+[[
+<rule name="emptychest" type="destroy" module="Theme">
+ <and>
+ <and>
+ <tval>7</tval>
+ </and>
+ <status>empty</status>
+ </and>
+</rule>
+<rule name="junkmushrooms" type="destroy" module="Theme">
+ <and>
+ <tval>80</tval>
+ <sval min="0" max="11"></sval>
+ </and>
+</rule>
+<rule name="junkjewelry" type="destroy" module="Theme">
+ <and>
+ <or>
+ <and>
+ <tval>40</tval>
+ <sval min="0" max="0"></sval>
+ </and>
+ <and>
+ <tval>45</tval>
+ <sval min="0" max="4"></sval>
+ </and>
+ <and>
+ <tval>40</tval>
+ <status>bad</status>
+ </and>
+ <and>
+ <tval>45</tval>
+ <status>bad</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="junkpotions" type="destroy" module="Theme">
+ <and>
+ <not>
+ <ability>Trapping</ability>
+ </not>
+ <or>
+ <and>
+ <tval>71</tval>
+ <sval min="4" max="7"></sval>
+ </and>
+ <and>
+ <tval>71</tval>
+ <sval min="9" max="11"></sval>
+ </and>
+ <and>
+ <tval>71</tval>
+ <sval min="13" max="13">13</sval>
+ </and>
+ <and>
+ <tval>71</tval>
+ <sval min="15" max="21"></sval>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="junkwands" type="destroy" module="Theme">
+ <and>
+ <tval>65</tval>
+ <sval min="24" max="25"></sval>
+ </and>
+</rule>
+<rule name="junkscrolls" type="destroy" module="Theme">
+ <and>
+ <not>
+ <ability>Trapping</ability>
+ </not>
+ <or>
+ <and>
+ <tval>70</tval>
+ <sval min="0" max="0"></sval>
+ <not>
+ <subrace>Vampire</subrace>
+ </not>
+ </and>
+ <and>
+ <tval>70</tval>
+ <sval min="1" max="5"></sval>
+ </and>
+ <and>
+ <tval>70</tval>
+ <sval min="7" max="7"></sval>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="brokendagger" type="destroy" module="Theme">
+ <and>
+ <name>Broken Dagger</name>
+ <or>
+ <status>average</status>
+ <status>bad</status>
+ <status>very bad</status>
+ </or>
+ </and>
+</rule>
+<rule name="magestaffs" type="destroy" module="Theme">
+ <and>
+ <tval>6</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="instruments" type="destroy" module="Theme">
+ <and>
+ <tval>14</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="boomerangs" type="destroy" module="Theme">
+ <and>
+ <tval>15</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="shots" type="destroy" module="Theme">
+ <and>
+ <tval>16</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="35" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="45" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="arrows" type="destroy" module="Theme">
+ <and>
+ <tval>17</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="35" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="45" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="bolts" type="destroy" module="Theme">
+ <and>
+ <tval>18</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="35" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="45" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="missile" type="destroy" module="Theme">
+ <and>
+ <tval>19</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="diggers" type="destroy" module="Theme">
+ <and>
+ <tval>20</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="hafted" type="destroy" module="Theme">
+ <and>
+ <tval>21</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="polearm" type="destroy" module="Theme">
+ <and>
+ <tval>22</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="swords" type="destroy" module="Theme">
+ <and>
+ <tval>23</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="axes" type="destroy" module="Theme">
+ <and>
+ <tval>24</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="boots" type="destroy" module="Theme">
+ <and>
+ <tval>30</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="gloves" type="destroy" module="Theme">
+ <and>
+ <tval>31</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="helms" type="destroy" module="Theme">
+ <and>
+ <tval>32</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="crowns" type="destroy" module="Theme">
+ <and>
+ <tval>33</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="shields" type="destroy" module="Theme">
+ <and>
+ <tval>34</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="cloaks" type="destroy" module="Theme">
+ <and>
+ <tval>35</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="softarmour" type="destroy" module="Theme">
+ <and>
+ <tval>36</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="hardarmour" type="destroy" module="Theme">
+ <and>
+ <tval>37</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="dragonarmour" type="destroy" module="Theme">
+ <and>
+ <tval>38</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="45" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="daemonbooks" type="destroy" module="Theme">
+ <and>
+ <and>
+ <tval>115</tval>
+ <sval min="55" max="57"></sval>
+ </and>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="trapkits" type="destroy" module="Theme">
+ <and>
+ <tval>46</tval>
+ <or>
+ <status>bad</status>
+ <status>very bad</status>
+ </or>
+ </and>
+</rule>
+<rule name="classitems" type="destroy" module="Theme">
+ <or>
+ <and>
+ <tval>9</tval>
+ <and>
+ <not>
+ <skill min="1" max="50">Corpse-preservation</skill>
+ </not>
+ <not>
+ <skill min="1" max="50">Possession</skill>
+ </not>
+ <not>
+ <skill min="1" max="50">Summoning</skill>
+ </not>
+ </and>
+ </and>
+ <and>
+ <or>
+ <tval>104</tval>
+ <tval>105</tval>
+ </or>
+ <not>
+ <skill min="1" max="50">Runecraft</skill>
+ </not>
+ </and>
+ <and>
+ <tval>46</tval>
+ <not>
+ <ability>Trapping</ability>
+ </not>
+ <or>
+ <status>average</status>
+ <status>good</status>
+ </or>
+ </and>
+ <and>
+ <and>
+ <not>
+ <class>Archer</class>
+ </not>
+ <not>
+ <class>Ranger</class>
+ </not>
+ <not>
+ <class>Sniper</class>
+ </not>
+ </and>
+ <tval>11</tval>
+ </and>
+ <and>
+ <and>
+ <not>
+ <class>Archer</class>
+ </not>
+ <not>
+ <class>Ranger</class>
+ </not>
+ <not>
+ <class>Sniper</class>
+ </not>
+ </and>
+ <tval>1</tval>
+ </and>
+ </or>
+</rule>
+]]
diff --git a/lib/mods/theme/user/fierce.atm b/lib/mods/theme/user/fierce.atm
new file mode 100644
index 00000000..c49f7de3
--- /dev/null
+++ b/lib/mods/theme/user/fierce.atm
@@ -0,0 +1,761 @@
+clean_ruleset()
+add_ruleset
+[[
+<rule name="oilflask" type="destroy" module="Theme">
+ <name>flask of oil</name>
+</rule>
+<rule name="emptychest" type="destroy" module="Theme">
+ <and>
+ <and>
+ <tval>7</tval>
+ </and>
+ <status>empty</status>
+ </and>
+</rule>
+<rule name="junkmushrooms" type="destroy" module="Theme">
+ <and>
+ <tval>80</tval>
+ <sval min="0" max="11"></sval>
+ </and>
+</rule>
+<rule name="junkjewelry" type="destroy" module="Theme">
+ <and>
+ <or>
+ <and>
+ <tval>40</tval>
+ <sval min="0" max="0"></sval>
+ </and>
+ <and>
+ <tval>45</tval>
+ <sval min="0" max="4"></sval>
+ </and>
+ <and>
+ <tval>40</tval>
+ <status>bad</status>
+ </and>
+ <and>
+ <tval>45</tval>
+ <status>bad</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="junkpotions" type="destroy" module="Theme">
+ <and>
+ <not>
+ <ability>Trapping</ability>
+ </not>
+ <or>
+ <and>
+ <tval>71</tval>
+ <sval min="4" max="7"></sval>
+ </and>
+ <and>
+ <tval>71</tval>
+ <sval min="9" max="11"></sval>
+ </and>
+ <and>
+ <tval>71</tval>
+ <sval min="13" max="13">13</sval>
+ </and>
+ <and>
+ <tval>71</tval>
+ <sval min="15" max="21"></sval>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="junkwands" type="destroy" module="Theme">
+ <and>
+ <tval>65</tval>
+ <sval min="24" max="25"></sval>
+ </and>
+</rule>
+<rule name="junkscrolls" type="destroy" module="Theme">
+ <and>
+ <not>
+ <ability>Trapping</ability>
+ </not>
+ <or>
+ <and>
+ <tval>70</tval>
+ <sval min="0" max="0"></sval>
+ <not>
+ <subrace>Vampire</subrace>
+ </not>
+ </and>
+ <and>
+ <tval>70</tval>
+ <sval min="1" max="5"></sval>
+ </and>
+ <and>
+ <tval>70</tval>
+ <sval min="7" max="7"></sval>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="brokendagger" type="destroy" module="Theme">
+ <and>
+ <name>Broken Dagger</name>
+ <or>
+ <status>average</status>
+ <status>bad</status>
+ <status>very bad</status>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="magestaffs" type="destroy" module="Theme">
+ <and>
+ <tval>6</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="instruments" type="destroy" module="Theme">
+ <and>
+ <tval>14</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="boomerangs" type="destroy" module="Theme">
+ <and>
+ <tval>15</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="shots" type="destroy" module="Theme">
+ <and>
+ <tval>16</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="35" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="arrows" type="destroy" module="Theme">
+ <and>
+ <tval>17</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="35" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="bolts" type="destroy" module="Theme">
+ <and>
+ <tval>18</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="35" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="missile" type="destroy" module="Theme">
+ <and>
+ <tval>19</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="diggers" type="destroy" module="Theme">
+ <and>
+ <tval>20</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="hafted" type="destroy" module="Theme">
+ <and>
+ <tval>21</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="polearm" type="destroy" module="Theme">
+ <and>
+ <tval>22</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="swords" type="destroy" module="Theme">
+ <and>
+ <tval>23</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="axes" type="destroy" module="Theme">
+ <and>
+ <tval>24</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="boots" type="destroy" module="Theme">
+ <and>
+ <tval>30</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="gloves" type="destroy" module="Theme">
+ <and>
+ <tval>31</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="helms" type="destroy" module="Theme">
+ <and>
+ <tval>32</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="crowns" type="destroy" module="Theme">
+ <and>
+ <tval>33</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="shields" type="destroy" module="Theme">
+ <and>
+ <tval>34</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="cloaks" type="destroy" module="Theme">
+ <and>
+ <tval>35</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="softarmour" type="destroy" module="Theme">
+ <and>
+ <tval>36</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="hardarmour" type="destroy" module="Theme">
+ <and>
+ <tval>37</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="dragonarmour" type="destroy" module="Theme">
+ <and>
+ <tval>38</tval>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="40" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="45" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="49" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="daemonbooks" type="destroy" module="Theme">
+ <and>
+ <and>
+ <tval>115</tval>
+ <sval min="55" max="57"></sval>
+ </and>
+ <or>
+ <and>
+ <level min="0" max="50"></level>
+ <or>
+ <status>very bad</status>
+ <status>bad</status>
+ </or>
+ </and>
+ <and>
+ <level min="15" max="50"></level>
+ <status>average</status>
+ </and>
+ <and>
+ <level min="25" max="50"></level>
+ <status>good</status>
+ </and>
+ <and>
+ <level min="49" max="50"></level>
+ <status>very good</status>
+ </and>
+ </or>
+ </and>
+</rule>
+<rule name="trapkits" type="destroy" module="Theme">
+ <and>
+ <tval>46</tval>
+ <or>
+ <status>bad</status>
+ <status>very bad</status>
+ </or>
+ </and>
+</rule>
+<rule name="classitems" type="destroy" module="Theme">
+ <or>
+ <and>
+ <tval>9</tval>
+ <and>
+ <not>
+ <skill min="1" max="50">Corpse-preservation</skill>
+ </not>
+ <not>
+ <skill min="1" max="50">Possession</skill>
+ </not>
+ <not>
+ <skill min="1" max="50">Summoning</skill>
+ </not>
+ </and>
+ </and>
+ <and>
+ <or>
+ <tval>104</tval>
+ <tval>105</tval>
+ </or>
+ <not>
+ <skill min="1" max="50">Runecraft</skill>
+ </not>
+ </and>
+ <and>
+ <tval>46</tval>
+ <not>
+ <ability>Trapping</ability>
+ </not>
+ <or>
+ <status>average</status>
+ <status>good</status>
+ </or>
+ </and>
+ <and>
+ <and>
+ <not>
+ <class>Archer</class>
+ </not>
+ <not>
+ <class>Ranger</class>
+ </not>
+ <not>
+ <class>Sniper</class>
+ </not>
+ </and>
+ <tval>11</tval>
+ </and>
+ <and>
+ <and>
+ <not>
+ <class>Archer</class>
+ </not>
+ <not>
+ <class>Ranger</class>
+ </not>
+ <not>
+ <class>Sniper</class>
+ </not>
+ </and>
+ <tval>1</tval>
+ </and>
+ </or>
+</rule>
+]]
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/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-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..1754d0f5
--- /dev/null
+++ b/lib/pref/font-win.prf
@@ -0,0 +1,301 @@
+# 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
+
+# glass wall
+F:103:0x0E/0x1F
+
+# 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..3e5d24ee
--- /dev/null
+++ b/lib/pref/font-xxx.prf
@@ -0,0 +1,469 @@
+# 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
+
+# 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..38614683
--- /dev/null
+++ b/lib/pref/font.prf
@@ -0,0 +1,40 @@
+# 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 gcu]
+%:font-gcu.prf
+
+?:[EQU $SYS mac]
+%:font-mac.prf
+
+?:[EQU $SYS win]
+%:font-win.prf
+
+?:1
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-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..310e5b8a
--- /dev/null
+++ b/lib/pref/pref.prf
@@ -0,0 +1,297 @@
+# 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 mac]
+%:pref-mac.prf
+
+?:[EQU $SYS win]
+%:pref-win.prf
+
+?:[EQU $SYS sdl]
+%:pref-sdl.prf
+
+?:1
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..04715ebc
--- /dev/null
+++ b/lib/pref/user.prf
@@ -0,0 +1,33 @@
+# 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 mac]
+%:user-mac.prf
+
+?:[EQU $SYS win]
+%:user-win.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-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/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/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/src/.gitignore b/src/.gitignore
new file mode 100644
index 00000000..098f3b10
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,3 @@
+/harness
+/tome
+*.plist
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 00000000..942946fc
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,160 @@
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../vendor/bandit)
+
+# Add subdirectories
+ADD_SUBDIRECTORY (squelch)
+
+# Sources (common)
+SET(SRCS_COMMON
+ birth.cc
+ bldg.cc
+ cave.cc
+ cmd1.cc
+ cmd2.cc
+ cmd3.cc
+ cmd4.cc
+ cmd5.cc
+ cmd6.cc
+ cmd7.cc
+ corrupt.cc
+ device_allocation.cc
+ dice.cc
+ dungeon.cc
+ files.cc
+ gen_evol.cc
+ gen_maze.cc
+ generate.cc
+ gods.cc
+ help.cc
+ hiscore.cc
+ hooks.cc
+ init1.cc
+ init2.cc
+ joke.cc
+ levels.cc
+ loadsave.cc
+ lua_bind.cc
+ melee1.cc
+ melee2.cc
+ messages.cc
+ mimic.cc
+ modules.cc
+ monster_type.cc
+ monster1.cc
+ monster2.cc
+ monster3.cc
+ notes.cc
+ object1.cc
+ object2.cc
+ object_filter.cc
+ options.cc
+ powers.cc
+ q_betwen.cc
+ q_bounty.cc
+ q_dragons.cc
+ q_eol.cc
+ q_evil.cc
+ q_fireprof.cc
+ q_god.cc
+ q_god.cc
+ q_haunted.cc
+ q_hobbit.cc
+ q_invas.cc
+ q_library.cc
+ q_main.cc
+ q_narsil.cc
+ q_nazgul.cc
+ q_nirna.cc
+ q_one.cc
+ q_poison.cc
+ q_rand.cc
+ q_shroom.cc
+ q_spider.cc
+ q_thief.cc
+ q_thrain.cc
+ q_troll.cc
+ q_ultrae.cc
+ q_ultrag.cc
+ q_wight.cc
+ q_wolves.cc
+ quark.cc
+ quest.cc
+ randart.cc
+ range.cc
+ script.cc
+ skills.cc
+ spell_type.cc
+ spells1.cc
+ spells2.cc
+ spells3.cc
+ spells4.cc
+ spells5.cc
+ spells6.cc
+ squeltch.cc
+ status.cc
+ store.cc
+ tables.cc
+ traps.cc
+ util.cc
+ variable.cc
+ wild.cc
+ wizard1.cc
+ wizard2.cc
+ xtra1.cc
+ xtra2.cc
+ z-form.c
+ z-rand.cc
+ z-term.c
+ z-util.c
+)
+
+# Sources (PROGRAM)
+SET(SRCS_PROGRAM
+ main-gcu.c
+ main-gtk2.c
+ main-sdl.c
+ main-x11.c
+ main.c
+)
+
+# Sources (TEST)
+SET(SRCS_TESTS
+ ../tests/get_level_device.cc
+ ../tests/harness.cc
+ ../tests/lua_get_level.cc
+)
+
+ADD_LIBRARY(game
+ ${SRCS_COMMON}
+)
+
+# Need a few additional source files for Windows.
+if(WIN32)
+ SET(SRCS ${SRCS} main-win.c)
+ # Resource files require a little workaround.
+ if(MINGW)
+ # Workaround for resource compilation for mingw on CMake.
+ # See http://www.cmake.org/Bug/view.php?id=4068
+ ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/angband_rc.o
+ COMMAND windres.exe -I${CMAKE_CURRENT_SOURCE_DIR}
+ -i${CMAKE_CURRENT_SOURCE_DIR}/angband.rc
+ -o ${CMAKE_CURRENT_BINARY_DIR}/angband_rc.o)
+ SET(SRCS ${SRCS} ${CMAKE_CURRENT_BINARY_DIR}/angband_rc.o)
+ else(MINGW)
+ SET(SRCS ${SRCS} angband.rc)
+ endif(MINGW)
+endif(WIN32)
+
+# tome executable
+ADD_EXECUTABLE(tome ${EXECUTABLE_OPTIONS} ${SRCS_PROGRAM})
+TARGET_LINK_LIBRARIES(tome game squelch ${LIBS})
+
+# test harness executable
+ADD_EXECUTABLE(harness ${EXECUTABLE_OPTIONS} ${SRCS_TESTS})
+TARGET_LINK_LIBRARIES(harness game squelch ${LIBS})
+
+# Installation
+INSTALL(TARGETS tome
+ RUNTIME DESTINATION bin
+)
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/ability_type.hpp b/src/ability_type.hpp
new file mode 100644
index 00000000..ea8a921d
--- /dev/null
+++ b/src/ability_type.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Abilities.
+ */
+struct ability_type
+{
+ const char *name; /* Name */
+ char *desc; /* Description */
+
+ const char *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/ability_type_fwd.hpp b/src/ability_type_fwd.hpp
new file mode 100644
index 00000000..bade8638
--- /dev/null
+++ b/src/ability_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct ability_type;
diff --git a/src/activation.hpp b/src/activation.hpp
new file mode 100644
index 00000000..adb9d8bc
--- /dev/null
+++ b/src/activation.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Activation descriptor.
+ */
+struct activation
+{
+ char desc[80]; /* Desc of the activation */
+ u32b cost; /* costs value */
+ s16b spell; /* Spell. */
+};
diff --git a/src/alloc_entry.hpp b/src/alloc_entry.hpp
new file mode 100644
index 00000000..41ec3b66
--- /dev/null
+++ b/src/alloc_entry.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * 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
+ */
+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 */
+};
diff --git a/src/alloc_entry_fwd.hpp b/src/alloc_entry_fwd.hpp
new file mode 100644
index 00000000..167af118
--- /dev/null
+++ b/src/alloc_entry_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct alloc_entry;
diff --git a/src/angband.h b/src/angband.h
new file mode 100644
index 00000000..5fbc94e5
--- /dev/null
+++ b/src/angband.h
@@ -0,0 +1,94 @@
+#pragma once
+
+/*
+ * Copyright (c) 1989 James E. Wilson
+ *
+ * This software may be copied and distributed for educational, research, and
+ * not for profit purposes provided that this copyright and statement are
+ * included in all such copies.
+ */
+
+/*
+ * C++ guard.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * First, include the low-level includes. Be sure to edit "h-config.h"
+ * to reflect any hardware, operating system, or compiler nuances.
+ */
+#include "h-basic.h"
+
+
+/*
+ * Then, include the header files for the low-level code
+ */
+#include "z-util.h"
+#include "z-form.h"
+#include "z-term.h"
+
+
+/*
+ * Include the "Angband" configuration header
+ */
+#include "config.h"
+
+
+/*
+ * Now, include the defines and the types
+ */
+#include "defines.h"
+
+/***** Some copyright messages follow below *****/
+
+/*
+ * Note that these copyright messages apply to an ancient version
+ * of Angband, as in, from pre-2.4.frog-knows days, and thus the
+ * reference to "5.0" is rather misleading...
+ */
+
+/*
+ * UNIX ANGBAND Version 5.0
+ */
+
+
+/* Original copyright message follows. */
+
+/*
+ * ANGBAND Version 4.8 COPYRIGHT (c) Robert Alan Koeneke
+ *
+ * I lovingly dedicate this game to hackers and adventurers
+ * everywhere...
+ *
+ * Designer and Programmer:
+ * Robert Alan Koeneke
+ * University of Oklahoma
+ *
+ * Assistant Programmer:
+ * Jimmey Wayne Todd
+ * University of Oklahoma
+ *
+ * Assistant Programmer:
+ * Gary D. McAdoo
+ * University of Oklahoma
+ *
+ * UNIX Port:
+ * James E. Wilson
+ * UC Berkeley
+ * wilson@ernie.Berkeley.EDU
+ * ucbvax!ucbernie!wilson
+ */
+
+
+/*
+ * ANGBAND may be copied and modified freely as long as the above
+ * credits are retained. No one who-so-ever may sell or market
+ * this software in any form without the expressed written consent
+ * of the author Robert Alan Koeneke.
+ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
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..37856f4a
--- /dev/null
+++ b/src/angband.rc
@@ -0,0 +1,111 @@
+/* File: angband.rc */
+
+ANGBAND MENU
+{
+ POPUP "&File"
+ {
+ MENUITEM "&Save", 110
+ MENUITEM SEPARATOR
+ MENUITEM "S&how score", 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"
+ {
+ MENUITEM "Unused menu option", 410
+ MENUITEM "Activate Screensaver", 411
+ }
+
+}
+
+ANGBAND ICON "angband.ico"
+
diff --git a/src/artifact_type.hpp b/src/artifact_type.hpp
new file mode 100644
index 00000000..7a4340aa
--- /dev/null
+++ b/src/artifact_type.hpp
@@ -0,0 +1,60 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Artifact descriptor.
+ *
+ * Note that the save-file only writes "cur_num" to the savefile.
+ *
+ * Note that "max_num" is always "1" (if that artifact "exists")
+ */
+struct artifact_type
+{
+ char const *name; /* Artifact name */
+ char *text; /* Artifact description */
+
+ 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 ?*/
+};
diff --git a/src/artifact_type_fwd.hpp b/src/artifact_type_fwd.hpp
new file mode 100644
index 00000000..f3862d5a
--- /dev/null
+++ b/src/artifact_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct artifact_type;
diff --git a/src/between_exit.hpp b/src/between_exit.hpp
new file mode 100644
index 00000000..7ea686d7
--- /dev/null
+++ b/src/between_exit.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Surface-level void gates descriptor.
+ */
+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;
+};
diff --git a/src/birth.cc b/src/birth.cc
new file mode 100644
index 00000000..f3897496
--- /dev/null
+++ b/src/birth.cc
@@ -0,0 +1,3724 @@
+/*
+ * 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 "birth.hpp"
+#include "birth.h"
+
+#include "ability_type.hpp"
+#include "artifact_type.hpp"
+#include "corrupt.hpp"
+#include "cmd4.hpp"
+#include "cmd5.hpp"
+#include "dungeon_info_type.hpp"
+#include "files.h"
+#include "files.hpp"
+#include "gods.hpp"
+#include "help.hpp"
+#include "hist_type.hpp"
+#include "hooks.hpp"
+#include "init2.hpp"
+#include "mimic.hpp"
+#include "messages.hpp"
+#include "meta_class_type.hpp"
+#include "modules.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "notes.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_class.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_type.hpp"
+#include "q_rand.hpp"
+#include "skill_type.hpp"
+#include "skills.hpp"
+#include "spells2.hpp"
+#include "spells3.hpp"
+#include "spells5.hpp"
+#include "stats.hpp"
+#include "store.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <string>
+
+/*
+ * 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 const 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 const 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 const 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)
+{
+ const 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 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 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 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 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
+ {
+ value += 10;
+ }
+ }
+ }
+
+ /* 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];
+
+ /* 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);
+
+ /* 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;
+
+
+ /* 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 quest */
+ p_ptr->inside_quest = 0;
+
+ /* 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;
+}
+
+
+/*
+ * 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);
+
+ /* 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.
+ */
+static 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;
+
+
+ /* Wipe special levels */
+ wipe_saved();
+
+ /* Hack -- zero the struct */
+ static_assert(std::is_pod<player_type>::value, "Cannot memset non-POD type");
+ memset(p_ptr, 0, sizeof(player_type));
+
+ /* Level 1 is the first level */
+ p_ptr->lev = 1;
+
+ /* Not dead yet */
+ p_ptr->lives = 0;
+
+ /* 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; i++)
+ {
+ quest[i].status = QUEST_STATUS_UNTAKEN;
+ for (auto &quest_data : quest[i].data)
+ {
+ quest_data = 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 "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;
+
+ /* Clear "cheat" options */
+ cheat_peek = FALSE;
+ cheat_hear = FALSE;
+ cheat_room = FALSE;
+ cheat_xtra = FALSE;
+ cheat_know = FALSE;
+ cheat_live = FALSE;
+
+ /* Assume no winning game */
+ total_winner = 0;
+ has_won = FALSE;
+
+ /* Assume no cheating */
+ noscore = 0;
+ wizard = 0;
+
+ /* Assume no innate spells */
+ spell_num = 0;
+
+ /* Clear the fate */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ fates[i].fate = 0;
+ }
+ p_ptr->no_mortal = FALSE;
+
+ /* Player don't have the black breath from the beginning !*/
+ p_ptr->black_breath = FALSE;
+
+ /* Default pet command settings */
+ p_ptr->pet_follow_distance = 6;
+ p_ptr->pet_open_doors = FALSE;
+ p_ptr->pet_pickup_items = FALSE;
+
+ /* Body changing initialisation */
+ p_ptr->body_monster = 0;
+ p_ptr->disembodied = FALSE;
+
+ /* Wipe 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;
+
+ /* Wipe the power list */
+ for (i = 0; i < POWER_MAX; i++)
+ {
+ p_ptr->powers_mod[i] = 0;
+ }
+
+ /* No companions killed */
+ p_ptr->companion_killed = 0;
+
+ /* Inertia control */
+ p_ptr->inertia_controlled_spell = -1;
+
+ /* Automatic stat-gain */
+ p_ptr->last_rewarded_level = 1;
+}
+
+
+/* Create an object */
+void outfit_obj(int tv, int sv, int pval, int dd, int ds)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get local object */
+ q_ptr = &forge;
+ q_ptr->pval = 0;
+ q_ptr->pval2 = 0;
+
+ /* Hack -- Give the player an object */
+ object_prep(q_ptr, lookup_kind(tv, sv));
+
+ if (pval)
+ q_ptr->pval = pval;
+
+ /* These objects are "storebought" */
+ q_ptr->ident |= IDENT_MENTAL;
+ q_ptr->number = damroll(dd, ds);
+
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+}
+
+
+/*
+ * Give the player an object.
+ */
+static void player_outfit_object(int qty, int tval, int sval)
+{
+ object_type forge;
+ object_type *q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(tval, sval));
+ q_ptr->number = qty;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+}
+
+
+/*
+ * Give player a spell book.
+ */
+static void player_outfit_spellbook(cptr spell_name)
+{
+ object_type forge;
+ object_type *q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_BOOK, 255));
+ q_ptr->pval = find_spell(spell_name);
+ q_ptr->ident |= IDENT_MENTAL | IDENT_KNOWN;
+ 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;
+ cptr class_name = spp_ptr->title;
+ cptr subrace_name = rmp_ptr->title;
+
+ /*
+ * Get an adventurer guide describing a bit of the
+ * wilderness.
+ */
+ {
+ /* Hack -- Give the player an adventurer guide */
+ player_outfit_object(1, TV_PARCHMENT, 20);
+ }
+
+ /*
+ * Provide spell books
+ */
+ if (game_module_idx == MODULE_TOME)
+ {
+ if (streq(class_name, "Ranger"))
+ {
+ player_outfit_spellbook("Phase Door");
+ }
+ }
+ if (streq(class_name, "Geomancer"))
+ {
+ player_outfit_spellbook("Geyser");
+ }
+ if (streq(class_name, "Priest(Eru)"))
+ {
+ player_outfit_spellbook("See the Music");
+ }
+ if (streq(class_name, "Priest(Manwe)"))
+ {
+ player_outfit_spellbook("Manwe's Blessing");
+ }
+ if (streq(class_name, "Druid"))
+ {
+ player_outfit_spellbook("Charm Animal");
+ }
+ if (streq(class_name, "Dark-Priest"))
+ {
+ player_outfit_spellbook("Curse");
+ }
+ if (streq(class_name, "Paladin"))
+ {
+ player_outfit_spellbook("Divine Aim");
+ }
+ if (game_module_idx == MODULE_THEME)
+ {
+ /* Priests */
+ if (streq(class_name, "Stonewright"))
+ {
+ player_outfit_spellbook("Firebrand");
+ }
+ if (streq(class_name, "Priest(Varda)"))
+ {
+ player_outfit_spellbook("Light of Valinor");
+ }
+ if (streq(class_name, "Priest(Ulmo)"))
+ {
+ player_outfit_spellbook("Song of Belegaer");
+ }
+ if (streq(class_name, "Priest(Mandos)"))
+ {
+ player_outfit_spellbook("Tears of Luthien");
+ }
+
+ /* Dragons */
+ if (streq(subrace_name, "Red"))
+ {
+ player_outfit_spellbook("Globe of Light");
+ }
+ if (streq(subrace_name, "Black"))
+ {
+ player_outfit_spellbook("Geyser");
+ }
+ if (streq(subrace_name, "Green"))
+ {
+ player_outfit_spellbook("Noxious Cloud");
+ }
+ if (streq(subrace_name, "Blue"))
+ {
+ player_outfit_spellbook("Stone Skin");
+ }
+ if (streq(subrace_name, "White"))
+ {
+ player_outfit_spellbook("Sense Monsters");
+ }
+ if (streq(subrace_name, "Ethereal"))
+ {
+ player_outfit_spellbook("Recharge");
+ }
+
+ /* Demons */
+ if (streq(subrace_name, "(Aewrog)"))
+ {
+ player_outfit_spellbook("Charm");
+ }
+ if (streq(subrace_name, "(Narrog)"))
+ {
+ player_outfit_spellbook("Phase Door");
+ }
+
+ /* Peace-mages */
+ if (streq(class_name, "Peace-mage"))
+ {
+ player_outfit_spellbook("Phase Door");
+ }
+
+ /* Wainriders */
+ if (streq(class_name, "Wainrider"))
+ {
+ player_outfit_spellbook("Curse");
+ }
+ }
+
+ if (streq(class_name, "Mimic"))
+ {
+ object_type forge;
+ object_type *q_ptr = &forge;
+
+ object_prep(q_ptr, lookup_kind(TV_CLOAK, SV_MIMIC_CLOAK));
+ q_ptr->pval2 = resolve_mimic_name("Mouse");
+ q_ptr->ident |= IDENT_MENTAL | IDENT_KNOWN;
+ inven_carry(q_ptr, FALSE);
+ }
+
+ if (game_module_idx == MODULE_THEME)
+ {
+ /* Give everyone a scroll of WoR. */
+ player_outfit_object(1, TV_SCROLL, SV_SCROLL_WORD_OF_RECALL);
+
+ /* Identify everything in pack. */
+ identify_pack_fully();
+ }
+
+ if (streq(rmp_ptr->title, "Vampire"))
+ {
+ player_gain_corruption(CORRUPT_VAMPIRE_TEETH);
+ player_gain_corruption(CORRUPT_VAMPIRE_STRENGTH);
+ player_gain_corruption(CORRUPT_VAMPIRE_VAMPIRE);
+ }
+
+ process_hooks_new(HOOK_BIRTH_OBJECTS, NULL, NULL);
+ meta_inertia_control_hook_birth_objects();
+
+ {
+ /* Hack -- Give the player some food */
+ int qty = (byte)rand_range(3, 7);
+ player_outfit_object(qty, TV_FOOD, SV_FOOD_RATION);
+ }
+
+ {
+ object_type forge;
+ object_type *q_ptr = &forge;
+ /* Hack -- Give the player some torches */
+ object_prep(q_ptr, lookup_kind(TV_LITE, SV_LITE_TORCH));
+ q_ptr->number = (byte)rand_range(3, 7);
+ q_ptr->timeout = rand_range(3, 7) * 500;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /* Rogues have a better knowledge of traps */
+ if (has_ability(AB_TRAPPING))
+ {
+ t_info[TRAP_OF_DAGGER_I].known = randint(50) + 50;
+ t_info[TRAP_OF_POISON_NEEDLE].known = randint(50) + 50;
+ t_info[TRAP_OF_FIRE_BOLT].known = randint(50) + 50;
+ t_info[TRAP_OF_DAGGER_I].ident = TRUE;
+ t_info[TRAP_OF_POISON_NEEDLE].ident = TRUE;
+ t_info[TRAP_OF_FIRE_BOLT].ident = TRUE;
+
+ /* Hack -- Give the player a some ammo for the traps */
+ object_type forge;
+ object_type *q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_SHOT, SV_AMMO_NORMAL));
+ q_ptr->number = (byte)rand_range(5, 15);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* These objects are "storebought" */
+ q_ptr->ident |= IDENT_MENTAL;
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /* Hack -- Give the player some useful objects */
+ for (i = 0; i < rp_ptr->obj_num; i++)
+ outfit_obj(rp_ptr->obj_tval[i], rp_ptr->obj_sval[i], rp_ptr->obj_pval[i], rp_ptr->obj_dd[i], rp_ptr->obj_ds[i]);
+ for (i = 0; i < rmp_ptr->obj_num; i++)
+ outfit_obj(rmp_ptr->obj_tval[i], rmp_ptr->obj_sval[i], rmp_ptr->obj_pval[i], rmp_ptr->obj_dd[i], rmp_ptr->obj_ds[i]);
+ for (i = 0; i < cp_ptr->obj_num; i++)
+ outfit_obj(cp_ptr->obj_tval[i], cp_ptr->obj_sval[i], cp_ptr->obj_pval[i], cp_ptr->obj_dd[i], cp_ptr->obj_ds[i]);
+ for (i = 0; i < cp_ptr->spec[p_ptr->pspec].obj_num; i++)
+ outfit_obj(cp_ptr->spec[p_ptr->pspec].obj_tval[i], cp_ptr->spec[p_ptr->pspec].obj_sval[i], cp_ptr->spec[p_ptr->pspec].obj_pval[i], cp_ptr->spec[p_ptr->pspec].obj_dd[i], cp_ptr->spec[p_ptr->pspec].obj_ds[i]);
+}
+
+
+int dump_classes(s16b *classes, int sel, u32b *restrictions)
+{
+ int n = 0;
+
+ char buf[80];
+
+ /* 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];
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s%s", p1,
+ (n <= 25) ? I2A(n) : I2D(n - 26), p2, cp_ptr->title, mod);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ std::string desc;
+
+ desc += cp_ptr->desc;
+ if (cp_ptr->flags1 & PR1_EXPERIMENTAL)
+ {
+ desc += "\nEXPERIMENTAL";
+ }
+
+ print_desc(desc.c_str());
+
+ 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++;
+ }
+
+ return (n);
+}
+
+int dump_specs(int sel)
+{
+ int n = 0;
+
+ char buf[80];
+
+ /* 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];
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, spp_ptr->title);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ std::string desc;
+
+ desc += spp_ptr->desc;
+ if (spp_ptr->flags1 & PR1_EXPERIMENTAL)
+ {
+ desc += "\nEXPERIMENTAL";
+ }
+
+ print_desc(desc.c_str());
+
+ 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));
+ }
+ }
+
+ return (n);
+}
+
+int dump_races(int sel)
+{
+ int n = 0;
+
+ char buf[80];
+
+ /* 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];
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, rp_ptr->title);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ std::string desc;
+
+ desc += rp_ptr->desc;
+ if (rp_ptr->flags1 & PR1_EXPERIMENTAL)
+ {
+ desc += "\nEXPERIMENTAL";
+ }
+
+ print_desc(desc.c_str());
+
+ 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));
+ }
+ }
+
+ return (n);
+}
+
+
+int dump_rmods(int sel, int *racem, int max)
+{
+ int n = 0;
+
+ char buf[80];
+
+ /* 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];
+
+ if (sel == n)
+ {
+ p1 = '[';
+ p2 = ']';
+ }
+
+ /* Display */
+ if (racem[n])
+ strnfmt(buf, 80, "%c%c%c %s", p1, I2A(n), p2, rmp_ptr->title);
+ else
+ strnfmt(buf, 80, "%c%c%c Classical", p1, I2A(n), p2);
+
+ /* Print some more info */
+ if (sel == n)
+ {
+ std::string desc;
+
+ desc += rmp_ptr->desc;
+ if (rmp_ptr->flags1 & PR1_EXPERIMENTAL)
+ {
+ desc += "\nEXPERIMENTAL";
+ }
+
+ print_desc(desc.c_str());
+
+ 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));
+ }
+ }
+
+ 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;
+
+ int racem[100], max_racem = 0;
+
+ u32b restrictions[2];
+
+ char c;
+
+ char p2 = ')';
+
+ char buf[200];
+ char inp[200];
+
+ s16b *class_types;
+
+ /*** 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];
+
+ /* Display */
+ strnfmt(buf, 200, "%c%c %s", I2A(n), p2, sp_ptr->title);
+ 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];
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, sp_ptr->title, 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.");
+
+ /* 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 == '?')
+ {
+ help_race(race_info[sel].title);
+ }
+ 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];
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, rp_ptr->title, 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 == '?')
+ {
+ help_subrace(race_mod_info[racem[sel]].title);
+ }
+
+ 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 == '?')
+ {
+ help_class(class_info[class_types[sel]].title);
+ }
+ else if (c == '=')
+ {
+ screen_save();
+ do_cmd_options_aux(6, "Startup Options", FALSE);
+ screen_load();
+ }
+ else if (c == '2')
+ {
+ sel += 4;
+ if (sel >= n) sel %= 4;
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = n - 1 -( ( -sel) % 4);
+ /* C's modulus operator does not have defined
+ results for negative first values. Damn. */
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '6')
+ {
+ sel++;
+ if (sel >= n) sel = 0;
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '4')
+ {
+ sel--;
+ if (sel < 0) sel = n - 1;
+ dump_classes(class_types, sel, restrictions);
+ }
+ else if (c == '\r')
+ {
+ k = sel;
+ break;
+ }
+ else bell();
+ }
+ }
+
+ /* Set class */
+ p_ptr->pclass = class_types[k];
+
+ /* Choose class spec */
+ clear_from(15);
+
+ /* Count choices */
+ for (n = 0; n < MAX_SPEC; n++)
+ {
+ /* Found the last one ? */
+ if (!class_info[p_ptr->pclass].spec[n].title) break;
+ }
+
+ /* Only one choice = auto choice */
+ if (n == 1)
+ k = 0;
+ else
+ {
+ /* Dump classes spec */
+ sel = 0;
+ n = dump_specs(sel);
+
+ /* Get a class */
+ while (1)
+ {
+ strnfmt(buf, 200, "Choose a class specialisation (%c-%c), * for random, = for options, 8/2/4/6 for up/down/left/right: ", I2A(0), (n <= 25) ? I2A(n - 1) : I2D(n - 26-1));
+ put_str(buf, 15, 2);
+ c = inkey();
+ if (c == 'Q') quit(NULL);
+ if (c == 'S') return (FALSE);
+ if (c == '*')
+ {
+ k = randint(n) - 1;
+ break;
+ }
+ k = (islower(c) ? A2I(c) : (D2I(c) + 26));
+ if ((k >= 0) && (k < n)) break;
+ if (c == '?')
+ {
+ help_class(class_info[p_ptr->pclass].spec[sel].title);
+ }
+ 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];
+
+ /* Display */
+ c_put_str(TERM_L_BLUE, spp_ptr->title, 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 (race_flags1_p(PR1_NO_GOD))
+ {
+ p_ptr->pgod = GOD_NONE;
+ }
+ else
+ {
+ int choice[MAX_GODS];
+ int max = 0;
+
+ /* Get the list of possible gods */
+ for (n = 0; n < MAX_GODS; n++)
+ {
+ if (god_enabled(&deity_info[n]) &&
+ ((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')
+ {
+ 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 == '?')
+ {
+ help_god(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();
+ }
+
+ /* Set god */
+ p_ptr->pgod = k;
+ p_ptr->grace = 0;
+ }
+
+ /* A god that like us ? more grace ! */
+ if (race_flags1_p(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->preserve = preserve;
+ p_ptr->special = special_lvls;
+ p_ptr->astral = (race_flags2_p(PR2_ASTRAL)) ? TRUE : FALSE;
+
+ /*
+ * A note by pelpel. (remove this please)
+ * Be it the new Vanilla way (adult vs. birth options) or
+ * the old one (player_type members), it would be less confusing
+ * to handle birth-only options in a uniform fashion,the above and
+ * the following:
+ * ironman_rooms,
+ * joke_monsters,
+ * always_small_level, and
+ * fate_option
+ */
+
+
+ /* Set the recall dungeon accordingly */
+ dungeon_type = DUNGEON_BASE;
+ p_ptr->recall_dungeon = dungeon_type;
+ max_dlv[dungeon_type] = d_info[dungeon_type].mindepth;
+
+ if (p_ptr->astral)
+ {
+ /* Somewhere in the misty mountains */
+ dungeon_type = DUNGEON_ASTRAL;
+ p_ptr->wilderness_x = DUNGEON_ASTRAL_WILD_X;
+ p_ptr->wilderness_y = DUNGEON_ASTRAL_WILD_Y;
+ }
+
+ /* Clean up */
+ clear_from(10);
+
+ /*** User enters number of quests ***/
+ /* Heino Vander Sanden and Jimmy De Laet */
+
+ if (!ironman_rooms)
+ {
+ 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 random quests */
+ initialize_random_quests(v);
+ max_quests = v;
+
+ p_ptr->inside_quest = 0;
+
+ /* Init the plots */
+ {
+ 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();
+
+ /* 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++)
+ {
+ /* Reset stats */
+ p_ptr->stat_cur[i] = p_ptr->stat_max[i] = stats[i];
+
+ /* 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];
+
+
+ /* 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;
+ }
+ }
+
+ /* 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, 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();
+
+ /* Check for a keypress */
+ if (inkey_scan()) 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();
+
+ /* 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 */
+ add_note_type(NOTE_BIRTH);
+
+ /* Note player birth in the message recall */
+ message_add(" ", TERM_L_BLUE);
+ message_add(" ", TERM_L_BLUE);
+ message_add("====================", TERM_L_BLUE);
+ message_add(" ", TERM_L_BLUE);
+ message_add(" ", TERM_L_BLUE);
+
+ /* Hack -- outfit the player */
+ player_outfit();
+
+ /* Initialize random towns in the dungeons */
+ for (i = 0; i < max_d_idx; i++)
+ {
+ dungeon_info_type *d_ptr = &d_info[i];
+ int num = 0, z;
+
+ d_ptr->t_num = 0;
+ for (z = 0; z < TOWN_DUNGEON; z++)
+ {
+ d_ptr->t_idx[z] = 0;
+ d_ptr->t_level[z] = 0;
+ }
+ if (!(d_ptr->flags1 & DF1_RANDOM_TOWNS)) continue;
+
+ /* Can we add a town ? */
+ while (magik(TOWN_CHANCE - (num * 10)))
+ {
+ int lev;
+
+ d_ptr->t_idx[num] = rtown;
+ rtown++;
+
+ while (TRUE)
+ {
+ int j;
+ bool_ ok = TRUE;
+
+ lev = rand_range(d_ptr->mindepth, d_ptr->maxdepth - 1);
+
+ /* Be sure it wasnt already used */
+ for (j = 0; j < num; j++)
+ {
+ if (d_ptr->t_level[j] == lev) ok = FALSE;
+ }
+
+ /* Ok found one */
+ if (ok) break;
+ }
+ d_ptr->t_level[num] = lev;
+
+ if (wizard)
+ {
+ message_add(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;
+ }
+ }
+}
+
+
+
+
+char savefile_module[46][80];
+char savefile_names[46][30];
+char savefile_desc[46][80];
+bool_ savefile_alive[46];
+int savefile_idx[46];
+
+/*
+ * Grab all the names from an index
+ */
+int load_savefile_names()
+{
+ FILE *fff;
+ char buf[1024];
+ char tmp[50];
+ char player_base_save[32];
+ int max = 0, fd;
+
+
+ /* Build the filename */
+ strcpy(tmp, "global.svg");
+ path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Read the file */
+ fff = my_fopen(buf, "r");
+
+ /* Failure */
+ if (!fff) return (0);
+
+
+ /* Save the current 'player_base' */
+ strncpy(player_base_save, player_base, 32);
+
+
+ /*
+ * Parse, use '@' intead of ':' as a separator because it cannot exists
+ * in savefiles
+ */
+ while (0 == my_fgets(fff, buf, 1024))
+ {
+ int i = 0, start, count;
+
+ /* Check for pre-ToME 2.1.2 file */
+ count = 0;
+ i = 0;
+ while (buf[i] && buf[i] != '\n')
+ {
+ if (buf[i] == '@')
+ ++count;
+ ++i;
+ }
+
+ /* Check module if a current svg file */
+ start = 0;
+ i = 0;
+ if (count > 1)
+ {
+ while (buf[i] != '@')
+ {
+ savefile_module[max][i - start] = buf[i];
+ i++;
+ }
+ savefile_module[max][i] = '\0';
+ i++;
+ }
+ /* Default to ToME for old files */
+ else
+ {
+ savefile_module[max][0] = 'T';
+ savefile_module[max][1] = 'o';
+ savefile_module[max][2] = 'M';
+ savefile_module[max][3] = 'E';
+ savefile_module[max][4] = '\0';
+ }
+
+ if (buf[i] == '0') savefile_alive[max] = FALSE;
+ else if (buf[i] == '1') savefile_alive[max] = TRUE;
+
+ i++;
+ start = i;
+ while (buf[i] != '@')
+ {
+ savefile_names[max][i - start] = buf[i];
+ i++;
+ }
+ savefile_names[max][i - start] = '\0';
+ i++;
+ strcpy(savefile_desc[max], buf + i);
+
+ /* Build platform-dependent savefile name */
+ strncpy(player_base, savefile_names[max], 32);
+ process_player_name(TRUE);
+
+ /* Try to open the savefile */
+ fd = fd_open(savefile, O_RDONLY);
+
+ /* Still existing ? */
+ if (fd >= 0)
+ {
+ fd_close(fd);
+ max++;
+ }
+ }
+
+ my_fclose(fff);
+
+ /* Restore the values of 'player_base' and 'savefile' */
+ strncpy(player_base, player_base_save, 32);
+ process_player_name(TRUE);
+
+ return (max);
+}
+
+
+/*
+ * Save all the names from an index
+ */
+void save_savefile_names()
+{
+ FILE *fff;
+ char buf[1024];
+ char tmp[50];
+ int max = load_savefile_names(), i;
+
+
+ /* Build the filename */
+ strcpy(tmp, "global.svg");
+ path_build(buf, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Read the file */
+ fff = my_fopen(buf, "w");
+
+ /* Failure */
+ if (!fff) return;
+
+ /*
+ * Save, use '@' intead of ':' as a separator because it cannot exists
+ * in savefiles
+ */
+ fprintf(fff, "%s@%c%s@%s, the %s %s is %s\n", game_module,
+ (death) ? '0' : '1', player_base, player_name,
+ get_player_race_name(p_ptr->prace, p_ptr->pracem),
+ spp_ptr->title,
+ (!death) ? "alive" : "dead");
+
+ for (i = 0; i < max; i++)
+ {
+ if (!strcmp(savefile_names[i], player_base)) continue;
+ fprintf(fff, "%s@%c%s@%s\n", savefile_module[i],
+ (savefile_alive[i]) ? '1' : '0', savefile_names[i], savefile_desc[i]);
+ }
+
+ my_fclose(fff);
+}
+
+
+static void dump_savefiles(int sel, int max)
+{
+ int i;
+
+ char buf[40], pre = ' ', post = ')';
+
+ char ind;
+
+
+ for (i = 0; i < max; i++)
+ {
+ ind = I2A(i % 26);
+ if (i >= 26) ind = toupper(ind);
+
+ if (sel == i)
+ {
+ pre = '[';
+ post = ']';
+ }
+ else
+ {
+ pre = ' ';
+ post = ')';
+ }
+
+ if (i == 0) strnfmt(buf, 40, "%c%c%c New Character", pre, ind, post);
+ else if (i == 1) strnfmt(buf, 40, "%c%c%c Load Savefile", pre, ind, post);
+ else strnfmt(buf, 40, "%c%c%c %s", pre, ind, post, savefile_names[savefile_idx[i - 2]]);
+
+ if (sel == i)
+ {
+ if (i >= 2)
+ {
+ if (savefile_alive[i - 2]) c_put_str(TERM_L_GREEN, savefile_desc[savefile_idx[i - 2]], 5, 0);
+ else c_put_str(TERM_L_RED, savefile_desc[savefile_idx[i - 2]], 5, 0);
+ }
+ else if (i == 1) c_put_str(TERM_YELLOW, "Load an existing savefile that is not in the list", 5, 0);
+ else c_put_str(TERM_YELLOW, "Create a new character", 5, 0);
+ c_put_str(TERM_L_BLUE, buf, 6 + (i / 4), 20 * (i % 4));
+ }
+ else
+ put_str(buf, 6 + (i / 4), 20 * (i % 4));
+ }
+}
+
+
+/* Asks for new game or load game */
+bool_ no_begin_screen = FALSE;
+
+bool_ begin_screen()
+{
+ int m, k, sel, max;
+
+savefile_try_again:
+ sel = 0;
+
+ /* Grab the savefiles */
+ max = load_savefile_names();
+
+ /* Get only the usable savefiles */
+ for (k = 0, m = 0; k < max; k++)
+ {
+ s32b can_use;
+
+ can_use = module_savefile_loadable(savefile_module[k]);
+ if (can_use)
+ {
+ savefile_idx[m++] = k;
+ }
+ }
+ max = m + 2;
+ if (max > 2) sel = 2;
+
+ while (TRUE)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Let the user choose */
+ c_put_str(TERM_YELLOW, format("Welcome to %s! To play you will need a character.", game_module), 1, 10);
+ put_str("Press 8/2/4/6 to move, Return to select, Backspace to delete a savefile.", 3, 3);
+ put_str("and Esc to quit.", 4, 32);
+
+ dump_savefiles(sel, max);
+
+ k = inkey();
+
+ if (k == ESCAPE)
+ {
+ quit(NULL);
+ }
+ if (k == '6')
+ {
+ sel++;
+ if (sel >= max) sel = 0;
+ continue;
+ }
+ else if (k == '4')
+ {
+ sel--;
+ if (sel < 0) sel = max - 1;
+ continue;
+ }
+ else if (k == '2')
+ {
+ sel += 4;
+ if (sel >= max) sel = sel % max;
+ continue;
+ }
+ else if (k == '8')
+ {
+ sel -= 4;
+ if (sel < 0) sel = (sel + max - 1) % max;
+ continue;
+ }
+ else if (k == '\r')
+ {
+ if (sel < 26) k = I2A(sel);
+ else k = toupper(I2A(sel));
+ }
+ else if (((k == 0x7F) || (k == '\010')) && (sel >= 2))
+ {
+ char player_base_save[32];
+
+ if (!get_check(format("Really delete '%s'?", savefile_names[savefile_idx[sel - 2]]))) continue;
+
+ /* Save current 'player_base' */
+ strncpy(player_base_save, player_base, 32);
+
+ /* Build platform-dependent save file name */
+ strncpy(player_base, savefile_names[savefile_idx[sel - 2]], 32);
+ process_player_name(TRUE);
+
+ /* Remove the savefile */
+ fd_kill(savefile);
+
+ /* Restore 'player_base' and 'savefile' */
+ strncpy(player_base, player_base_save, 32);
+ process_player_name(TRUE);
+
+ /* Reload, gods I hate using goto .. */
+ goto savefile_try_again;
+
+ continue;
+ }
+
+ if (k == 'a')
+ {
+ /* Display prompt */
+ prt("Enter the name of the savefile that will hold this character: ", 23, 0);
+
+ /* Ask the user for a string */
+ if (!askfor_aux(player_base, 15)) continue;
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ return (TRUE);
+ }
+ if (k == 'b')
+ {
+ /* Display prompt */
+ prt("Enter the name of a savefile: ", 23, 0);
+
+ /* Ask the user for a string */
+ if (!askfor_aux(player_base, 15)) continue;
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ return (FALSE);
+ }
+ else
+ {
+ int x;
+
+ if (islower(k)) x = A2I(k);
+ else x = A2I(tolower(k)) + 26;
+
+ if ((x < 2) || (x >= max)) continue;
+
+ strnfmt(player_base, 32, "%s", savefile_names[savefile_idx[x - 2]]);
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+ return (FALSE);
+ }
+ }
+
+ /* Shouldnt happen */
+ return (FALSE);
+}
diff --git a/src/birth.h b/src/birth.h
new file mode 100644
index 00000000..41620bfa
--- /dev/null
+++ b/src/birth.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "h-basic.h"
+
+// C linkage required for these functions since main-* code uses them.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern bool_ no_begin_screen;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/birth.hpp b/src/birth.hpp
new file mode 100644
index 00000000..fb036ecc
--- /dev/null
+++ b/src/birth.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern void print_desc_aux(cptr txt, int y, int x);
+extern void save_savefile_names(void);
+extern bool_ begin_screen(void);
+extern void get_height_weight(void);
+extern void player_birth(void);
diff --git a/src/birther.hpp b/src/birther.hpp
new file mode 100644
index 00000000..f517fb9d
--- /dev/null
+++ b/src/birther.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Player information during the birth process.
+ */
+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;
+
+ char history[4][60];
+
+ bool_ quick_ok;
+};
diff --git a/src/bldg.cc b/src/bldg.cc
new file mode 100644
index 00000000..7095e8c3
--- /dev/null
+++ b/src/bldg.cc
@@ -0,0 +1,1469 @@
+/* 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 "cave_type.hpp"
+#include "cmd3.hpp"
+#include "files.hpp"
+#include "hooks.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hook_quest_fail_in.hpp"
+#include "hook_init_quest_in.hpp"
+#include "mimic.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "owner_type.hpp"
+#include "player_type.hpp"
+#include "q_library.hpp"
+#include "q_fireprof.hpp"
+#include "q_bounty.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "store.hpp"
+#include "store_action_type.hpp"
+#include "store_info_type.hpp"
+#include "store_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+/* remember building location */
+static int building_loc = 0;
+
+
+/*
+ * A helper function for is_state
+ */
+static 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];
+
+
+ for (i = 0; i < 6; i++)
+ {
+ store_action_type *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, buff);
+ c_put_str(action_color, tmp_str, 21 + (i / 2), 2 + 17 + (30 * (i % 2)));
+ }
+ }
+}
+
+
+/*
+ * 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_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 = ((race_flags1_p(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;
+
+ break;
+ }
+
+ case BACT_RUMORS: /* Listen for rumors */
+ {
+ char rumor[80];
+
+ get_rnd_line("rumors.txt", rumor);
+ msg_format("%s", rumor);
+ msg_print(NULL);
+
+ break;
+ }
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Display quest information
+ */
+static void get_questinfo(int questnum)
+{
+ int i;
+
+
+ /* Print the quest info */
+ prt(format("Quest Information (Danger level: %d)", quest[questnum].level), 5, 0);
+
+ prt(quest[questnum].name, 7, 0);
+
+ i = 0;
+ while ((i < 10) && (quest[questnum].desc[i][0] != '\0'))
+ {
+ c_put_str(TERM_YELLOW, quest[questnum].desc[i], i + 8, 0);
+ i++;
+ }
+}
+
+
+/*
+ * Request a quest from the Lord.
+ */
+static bool_ castle_quest(int y, int x)
+{
+ int plot = 0;
+
+ quest_type *q_ptr;
+
+
+ clear_bldg(7, 18);
+
+ /* Current plot of the building */
+ plot = cave[y][x].special;
+
+ /* Is there a quest available at the building? */
+ if ((!plot) || (plots[plot] == QUEST_NULL))
+ {
+ put_str("I don't have a quest for you at the moment.", 8, 0);
+ return FALSE;
+ }
+
+ q_ptr = &quest[plots[plot]];
+
+ /* Quest is completed */
+ if (q_ptr->status == QUEST_STATUS_COMPLETED)
+ {
+ /* Rewarded quest */
+ q_ptr->status = QUEST_STATUS_FINISHED;
+
+ struct hook_quest_finish_in in = { plots[plot] };
+ process_hooks_new(HOOK_QUEST_FINISH, &in, NULL);
+
+ 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;
+
+ hook_quest_fail_in in = { plots[plot] };
+ process_hooks_new(HOOK_QUEST_FAIL, &in, NULL);
+
+ return (FALSE);
+ }
+ /* No quest yet */
+ else if (q_ptr->status == QUEST_STATUS_UNTAKEN)
+ {
+ struct hook_init_quest_in in = { plots[plot] };
+ if (process_hooks_new(HOOK_INIT_QUEST, &in, NULL))
+ {
+ return (FALSE);
+ }
+
+ q_ptr->status = QUEST_STATUS_TAKEN;
+
+ /* Assign a new quest */
+ get_questinfo(plots[plot]);
+
+ /* Add the hooks */
+ 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, const char *attr, 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 const *o_ptr)
+{
+ return (wield_slot(o_ptr) == INVEN_WIELD);
+}
+
+/*
+ * compare_weapons -KMW-
+ */
+static bool_ compare_weapons(void)
+{
+ int item, i;
+
+ object_type *o1_ptr, *o2_ptr, *orig_ptr;
+
+ clear_bldg(6, 18);
+
+ o1_ptr = NULL;
+ o2_ptr = NULL;
+
+ /* Store copy of original wielded weapon in pack slot */
+ object_type *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 */
+ if (!get_item(&item,
+ "What is your first melee weapon? ",
+ "You have nothing to compare.",
+ (USE_EQUIP | USE_INVEN),
+ item_tester_hook_melee_weapon))
+ {
+ object_wipe(orig_ptr);
+ return (FALSE);
+ }
+
+ /* Get the item (in the pack) */
+ if (item >= 0)
+ o1_ptr = &p_ptr->inventory[item];
+
+ /* Get second weapon */
+ int item2;
+ if (!get_item(&item2,
+ "What is your second melee weapon? ",
+ "You have nothing to compare.",
+ (USE_EQUIP | USE_INVEN),
+ item_tester_hook_melee_weapon))
+ {
+ object_wipe(orig_ptr);
+ return (FALSE);
+ }
+
+ /* Get the item (in the pack) */
+ if (item2 >= 0) o2_ptr = &p_ptr->inventory[item2];
+
+ put_str("Based on your current abilities, here is what your weapons will do", 4, 2);
+
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ object_copy(i_ptr, o1_ptr);
+ calc_bonuses(TRUE);
+
+ list_weapon(o1_ptr, i, 2);
+ compare_weapon_aux1(o1_ptr, 2, i + 8);
+
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ if (item2 == INVEN_WIELD)
+ object_copy(i_ptr, orig_ptr);
+ else
+ object_copy(i_ptr, o2_ptr);
+ calc_bonuses(TRUE);
+
+ list_weapon(o2_ptr, i, 40);
+ compare_weapon_aux1(o2_ptr, 40, i + 8);
+
+ i_ptr = &p_ptr->inventory[INVEN_WIELD];
+ object_copy(i_ptr, orig_ptr);
+ calc_bonuses(TRUE);
+
+ object_wipe(orig_ptr);
+
+ put_str("(Only highest damage applies per monster. Special damage not cumulative)", 20, 0);
+
+ return (TRUE);
+}
+
+
+/*
+ * general all-purpose fixing routine for items from building personnel
+ * sharpen arrows, repair armor, repair weapon
+ * -KMW-
+ */
+static bool_ fix_item(int istart, int iend, int ispecific, bool_ iac,
+ int ireward, bool_ set_reward)
+{
+ int i;
+
+ int j = 9;
+
+ int maxenchant = (p_ptr->lev / 5);
+
+ object_type *o_ptr;
+
+ char out_val[80], tmp_str[80];
+
+ bool_ repaired = FALSE;
+
+ clear_bldg(5, 18);
+ strnfmt(tmp_str, 80, " Based on your skill, we can improve up to +%d", maxenchant);
+ prt(tmp_str, 5, 0);
+ prt("Status", 7, 30);
+
+ for (i = istart; i <= iend; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+ if (ispecific > 0)
+ {
+ if (o_ptr->tval != ispecific)
+ continue;
+ }
+
+ if (o_ptr->tval)
+ {
+ object_desc(tmp_str, o_ptr, FALSE, 1);
+
+ if ((o_ptr->name1 && (o_ptr->ident & 0x08)))
+ strnfmt(out_val, 80, "%-40s: beyond our skills!", tmp_str);
+ else if (o_ptr->name1)
+ strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str);
+ else
+ {
+ if ((iac) && (o_ptr->to_a <= -3))
+ {
+ strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str);
+ }
+ else if ((iac) && (o_ptr->to_a < maxenchant))
+ {
+ o_ptr->to_a++;
+ strnfmt(out_val, 80, "%-40s: polished -> (%d)", tmp_str, o_ptr->to_a);
+ repaired = TRUE;
+ }
+ else if ((!iac) && ((o_ptr->to_h <= -3) || (o_ptr->to_d <= -3)))
+ {
+ strnfmt(out_val, 80, "%-40s: beyond repair, buy a new one", tmp_str);
+ }
+ /* Sharpen a weapon */
+ else if ((!iac) && ((o_ptr->to_h < maxenchant) ||
+ (o_ptr->to_d < maxenchant)))
+ {
+ if (o_ptr->to_h < maxenchant)
+ o_ptr->to_h++;
+ if (o_ptr->to_d < maxenchant)
+ o_ptr->to_d++;
+ strnfmt(out_val, 80, "%-40s: sharpened -> (%d,%d)", tmp_str,
+ o_ptr->to_h, o_ptr->to_d);
+ repaired = TRUE;
+ }
+ else
+ strnfmt(out_val, 80, "%-40s: in fine condition", tmp_str);
+ }
+ prt(out_val, j++, 0);
+ }
+ }
+
+ if (!repaired)
+ {
+ msg_print("You don't have anything appropriate.");
+ msg_print(NULL);
+ }
+ else
+ {
+ msg_print("Press the spacebar to continue");
+ msg_print(NULL);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ p_ptr->update |= (PU_BONUS);
+ }
+ clear_bldg(5, 18);
+
+ return (repaired);
+}
+
+
+/*
+ * Research Item
+ */
+static bool_ research_item(void)
+{
+ clear_bldg(5, 18);
+ return (identify_fully());
+}
+
+
+
+/*
+ * 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;
+ }
+
+ /* check gold */
+ if (bcost > p_ptr->au)
+ {
+ msg_print("You do not have the gold!");
+ msg_print(NULL);
+ return FALSE;
+ }
+
+ if (!bcost) set_reward = TRUE;
+
+ switch (bact)
+ {
+ case BACT_RESEARCH_ITEM:
+ {
+ paid = research_item();
+ break;
+ }
+
+ case BACT_TOWN_HISTORY:
+ {
+ town_history();
+ break;
+ }
+
+ case BACT_RACE_LEGENDS:
+ {
+ race_legends();
+ break;
+ }
+
+ case BACT_QUEST1:
+ {
+ 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:
+ {
+ show_highclass(building_loc);
+ break;
+ }
+
+ case BACT_IN_BETWEEN:
+ case BACT_CRAPS:
+ case BACT_DICE_SLOTS:
+ case BACT_GAMBLE_RULES:
+ {
+ gamble_comm(bact);
+ break;
+ }
+
+ case BACT_REST:
+ case BACT_RUMORS:
+ case BACT_FOOD:
+ {
+ paid = inn_comm(bact);
+ break;
+ }
+
+ case BACT_RESEARCH_MONSTER:
+ {
+ paid = !research_mon();
+ break;
+ }
+
+ case BACT_COMPARE_WEAPONS:
+ {
+ paid = compare_weapons();
+ break;
+ }
+
+ case BACT_ENCHANT_WEAPON:
+ {
+ paid = fix_item(INVEN_WIELD, INVEN_WIELD, 0, FALSE,
+ BACT_ENCHANT_WEAPON, set_reward);
+ break;
+ }
+
+ case BACT_ENCHANT_ARMOR:
+ {
+ paid = fix_item(INVEN_BODY, INVEN_FEET, 0, TRUE,
+ BACT_ENCHANT_ARMOR, set_reward);
+ break;
+ }
+
+ /* needs work */
+ case BACT_RECHARGE:
+ {
+ if (recharge(80)) paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_IDENTS:
+ {
+ identify_pack();
+ msg_print("Your possessions have been identified.");
+ msg_print(NULL);
+ paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_STAR_HEAL:
+ {
+ hp_player(200);
+ set_poisoned(0);
+ set_blind(0);
+ set_confused(0);
+ set_cut(0);
+ set_stun(0);
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ p_ptr->black_breath = FALSE;
+ }
+ paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_HEALING:
+ {
+ hp_player(200);
+ set_poisoned(0);
+ set_blind(0);
+ set_confused(0);
+ set_cut(0);
+ set_stun(0);
+ paid = TRUE;
+ break;
+ }
+
+ /* needs work */
+ case BACT_RESTORE:
+ {
+ if (do_res_stat(A_STR, TRUE)) paid = TRUE;
+ if (do_res_stat(A_INT, TRUE)) paid = TRUE;
+ if (do_res_stat(A_WIS, TRUE)) paid = TRUE;
+ if (do_res_stat(A_DEX, TRUE)) paid = TRUE;
+ if (do_res_stat(A_CON, TRUE)) paid = TRUE;
+ if (do_res_stat(A_CHR, TRUE)) paid = TRUE;
+ break;
+ }
+
+ case BACT_ENCHANT_ARROWS:
+ {
+ paid = fix_item(0, INVEN_WIELD, TV_ARROW, FALSE,
+ BACT_ENCHANT_ARROWS, set_reward);
+ break;
+ }
+
+ case BACT_ENCHANT_BOW:
+ {
+ paid = fix_item(INVEN_BOW, INVEN_BOW, TV_BOW, FALSE,
+ BACT_ENCHANT_BOW, set_reward);
+ break;
+ }
+
+ case BACT_RECALL:
+ {
+ p_ptr->word_recall = 1;
+ msg_print("The air about you becomes charged...");
+ paid = TRUE;
+ break;
+ }
+
+ case BACT_TELEPORT_LEVEL:
+ {
+ if (reset_recall(FALSE))
+ {
+ p_ptr->word_recall = 1;
+ msg_print("The air about you becomes charged...");
+ paid = TRUE;
+ }
+ break;
+ }
+
+ case BACT_MIMIC_NORMAL:
+ {
+ set_mimic(0, 0, 0);
+ paid = TRUE;
+ break;
+ }
+
+ case BACT_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_DROP_ITEM:
+ {
+ quest_bounty_drop_item();
+ break;
+ }
+
+ case BACT_GET_ITEM:
+ {
+ quest_bounty_get_item();
+ break;
+ }
+
+ case BACT_LIBRARY_QUEST:
+ {
+ quest_library_building(&paid, &recreate);
+ break;
+ }
+
+ case BACT_FIREPROOF_QUEST:
+ {
+ quest_fireproof_building(&paid, &recreate);
+ break;
+ }
+
+ case BACT_EREBOR_KEY:
+ {
+ msg_print("You will need Thorin's Key and Thrain's Map"
+ " to get anywhere in Erebor. One may be found"
+ " in the Barrow-Downs. The other, in Mirkwood.");
+ break;
+ }
+
+ default:
+ printf("Unknown building action %d\n", static_cast<int>(bact));
+ 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;
+ }
+}
diff --git a/src/bldg.hpp b/src/bldg.hpp
new file mode 100644
index 00000000..0daebbee
--- /dev/null
+++ b/src/bldg.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "h-basic.h"
+#include "store_type_fwd.hpp"
+
+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 enter_quest(void);
diff --git a/src/body.hpp b/src/body.hpp
new file mode 100644
index 00000000..dd0be282
--- /dev/null
+++ b/src/body.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+/*
+ * 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
diff --git a/src/cave.cc b/src/cave.cc
new file mode 100644
index 00000000..5ff31019
--- /dev/null
+++ b/src/cave.cc
@@ -0,0 +1,4693 @@
+#include "cave.hpp"
+
+#include "cave_type.hpp"
+#include "feature_type.hpp"
+#include "hook_enter_dungeon_in.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "q_rand.hpp"
+#include "spells1.hpp"
+#include "store_info_type.hpp"
+#include "tables.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+#include <vector>
+#include <iterator>
+#include <algorithm>
+
+/*
+ * 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 const *c_ptr = &cave[y][x];
+
+ /* Forbid perma-grids */
+ if (cave_perma_grid(c_ptr)) return (FALSE);
+
+ /* Check objects */
+ for (auto const o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[o_idx];
+
+ /* Forbid artifact grids */
+ if ((o_ptr->art_name) || artifact_p(o_ptr)) return (FALSE);
+ }
+
+ /* Accept */
+ return (TRUE);
+}
+
+
+
+/*
+ * Generate visual for hallucinatory monster
+ */
+static void image_monster(byte *ap, char *cp)
+{
+ // Cached state which keeps a list of all the "live" monster race entries.
+ static std::vector<size_t> *instance = nullptr;
+
+ // First-time initialization
+ if (!instance)
+ {
+ // Create the list of "live" indexes
+ instance = new std::vector<size_t>();
+ // Start at 1 to avoid 'player'
+ for (size_t i = 1; i < max_r_idx; i++)
+ {
+ if (r_info[i].name)
+ {
+ instance->push_back(i);
+ }
+ }
+ }
+
+ // Sanity check
+ assert(instance != nullptr);
+
+ // Select a race at random
+ int n = rand_int(instance->size());
+ *cp = r_info[(*instance)[n]].x_char;
+ *ap = r_info[(*instance)[n]].x_attr;
+}
+
+
+/*
+ * Generate visual for hallucinatory object
+ */
+static void image_object(byte *ap, char *cp)
+{
+ // Cached state which keeps a list of the "live" object_kind entries.
+ static std::vector<size_t> *instance = nullptr;
+
+ // First-time initialization
+ if (!instance)
+ {
+ // Create the list of "live" indexes
+ instance = new std::vector<size_t>();
+ // Filter all the "live" entries
+ for (size_t i = 0; i < max_k_idx; i++)
+ {
+ if (k_info[i].name)
+ {
+ instance->push_back(i);
+ }
+ }
+ }
+
+ // Sanity check
+ assert(instance != nullptr);
+
+ // Select an object kind at random
+ int n = rand_int(instance->size());
+ *cp = k_info[(*instance)[n]].x_char;
+ *ap = k_info[(*instance)[n]].x_attr;
+}
+
+
+/*
+ * 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);
+ }
+}
+
+
+
+static 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(std::shared_ptr<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
+};
+
+
+static void map_info(int y, int x, byte *ap, char *cp)
+{
+ byte a;
+
+ byte c;
+
+ /**** Preparation ****/
+
+ /* Access the grid */
+ cave_type *c_ptr = &cave[y][x];
+
+
+ /* Cache some frequently used values */
+
+ /* Grid info */
+ auto info = c_ptr->info;
+
+ /* Feature code */
+ auto feat = c_ptr->feat;
+
+ /* Apply "mimic" field */
+ if (c_ptr->mimic)
+ {
+ feat = c_ptr->mimic;
+ }
+ else
+ {
+ feat = f_info[feat].mimic;
+ }
+
+ /* Access floor */
+ feature_type *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)
+ {
+ /* 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 && ((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 */
+ auto 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].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)
+ {
+ /* 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)))
+ {
+ /* 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];
+ }
+
+ else
+ {
+ /* Use normal colour */
+ }
+ }
+ }
+ }
+
+ /* Unknown grids */
+ else
+ {
+ /* Access darkness */
+ f_ptr = &f_info[FEAT_NONE];
+
+ /* Normal attr */
+ a = f_ptr->x_attr;
+
+ /* Normal char */
+ c = f_ptr->x_char;
+ }
+
+ /*
+ * Hack -- rare random hallucination
+ * Because we cannot be sure which is outer dungeon walls,
+ * the check for 'feat' has been removed
+ */
+ if (p_ptr->image && (rand_int(256) == 0))
+ {
+ /* Hallucinate */
+ image_random(ap, cp);
+ }
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+
+ /**** Layer 2 -- Objects ****/
+
+ if (feat != FEAT_MON_TRAP)
+ {
+ for (auto const o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[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 && (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];
+ auto const r_ptr = m_ptr->race();
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ /* Acquire object being mimicked */
+ object_type *o_ptr = &o_list[m_ptr->mimic_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 && (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)
+ {
+ /* 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;
+ }
+
+ /* 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];
+
+ /* Get the "player" attr */
+ if (!avoid_other && (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;
+
+ /* Show player health char instead? */
+ 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;
+ }
+ }
+
+ /* Save the info */
+ *ap = a;
+ *cp = c;
+
+ }
+}
+
+
+/*
+ * Special version of map_info, for use by HTML converter
+ * to obtain pure-ASCII image of dungeon map
+ */
+void map_info_default(int y, int x, byte *ap, char *cp)
+{
+ byte a;
+
+ byte c;
+
+ /**** Preparation ****/
+
+ /* Access the grid */
+ cave_type *c_ptr = &cave[y][x];
+
+
+ /* Cache some frequently used values */
+
+ /* Grid info */
+ auto info = c_ptr->info;
+
+ /* Feature code */
+ auto feat = c_ptr->feat;
+
+ /* Apply "mimic" field */
+ if (c_ptr->mimic)
+ {
+ feat = c_ptr->mimic;
+ }
+ else
+ {
+ feat = f_info[feat].mimic;
+ }
+
+ /* Access floor */
+ feature_type *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 */
+ auto 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 (auto const this_o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_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];
+ auto const r_ptr = m_ptr->race();
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ /* Acquire object being mimicked */
+ object_type *o_ptr = &o_list[m_ptr->mimic_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);
+ }
+ }
+ else
+ {
+ /* Visible monster */
+ if (m_ptr->ml)
+ {
+ /* 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;
+
+ }
+}
+
+
+/*
+ * Calculate panel colum of a location in the map
+ */
+static int panel_col_of(int col)
+{
+ col -= panel_col_min;
+ 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);
+}
+
+
+
+
+
+/*
+ * 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;
+
+ /* Require "seen" flag */
+ if (!(info & (CAVE_SEEN))) return;
+
+
+ /* Hack -- memorize objects */
+ for (auto const this_o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_o_idx];
+
+ /* Memorize objects */
+ o_ptr->marked = TRUE;
+ }
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ auto r_ptr = m_ptr->race();
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr = &o_list[m_ptr->mimic_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;
+ char c;
+
+ /* Redraw if on screen */
+ if (panel_contains(y, x))
+ {
+ /* Examine the grid */
+ map_info(y, x, &a, &c);
+
+ /* Hack -- Queue it */
+ Term_queue_char(panel_col_of(x), y - panel_row_prt, a, c);
+ }
+}
+
+
+
+
+/*
+ * 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;
+ char c;
+
+ /* 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);
+ }
+ }
+
+ /* 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;
+
+ 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);
+
+ /*
+ * 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;
+
+
+ /* Set up initial maps */
+ std::vector<std::vector<byte>> ma;
+ std::vector<std::vector<char>> mc;
+ std::vector<std::vector<byte>> mp;
+ for (i = 0; i < hgt + 2; i++)
+ {
+ // Nothing there.
+ ma.push_back(std::vector<byte>(wid + 2, TERM_WHITE));
+ mc.push_back(std::vector<char>(wid + 2, ' '));
+
+ // No priority.
+ mp.push_back(std::vector<byte>(wid + 2, 0));
+ }
+ assert(static_cast<int>(ma.size()) == hgt + 2);
+ assert(static_cast<int>(mc.size()) == hgt + 2);
+ assert(static_cast<int>(mp.size()) == hgt + 2);
+
+ /* Calculate scaling factors */
+ yfactor = ((cur_hgt / hgt < 4) && (cur_hgt > hgt)) ? 10 : 1;
+ xfactor = ((cur_wid / wid < 4) && (cur_wid > wid)) ? 10 : 1;
+
+ yrat = (cur_hgt * yfactor + (hgt - 1)) / hgt;
+ xrat = (cur_wid * xfactor + (wid - 1)) / wid;
+
+ /* Fill in the map */
+ for (j = 0; j < cur_hgt; ++j)
+ {
+ for (i = 0; i < cur_wid; ++i)
+ {
+ /* Location */
+ y = j * yfactor / yrat + 1;
+ x = i * xfactor / xrat + 1;
+
+ /* Extract the current attr/char at that map location */
+ map_info(j, i, &ta, &tc);
+
+ /* 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);
+ }
+ }
+
+ /* Player location in dungeon */
+ *cy = p_ptr->py * yfactor / yrat + ROW_MAP;
+ *cx = p_ptr->px * xfactor / xrat + COL_MAP;
+
+ /* 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];
+};
+
+
+
+/*
+ * 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;
+
+ int num_grids = 0;
+
+ int queue_head = 0;
+ int queue_tail = 0;
+ vinfo_type *queue[VINFO_MAX_GRIDS*2];
+
+
+ /* Make hack */
+ vinfo_hack hack;
+ memset(&hack, 0, sizeof(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 */
+ std::sort(std::begin(hack.slopes), std::end(hack.slopes));
+
+
+
+ /* Enqueue player grid */
+ queue[queue_tail++] = &vinfo[0];
+
+ /* Process queue */
+ while (queue_head < queue_tail)
+ {
+ int e;
+
+ /* Index */
+ e = queue_head;
+
+ /* Dequeue next grid */
+ queue_head++;
+
+ /* Location of main grid */
+ y = vinfo[e].grid_y[0];
+ x = vinfo[e].grid_x[0];
+
+
+ /* Compute grid offsets */
+ vinfo[e].grid_y[0] = + y;
+ vinfo[e].grid_x[0] = + x;
+ vinfo[e].grid_y[1] = + x;
+ vinfo[e].grid_x[1] = + y;
+ vinfo[e].grid_y[2] = + x;
+ vinfo[e].grid_x[2] = -y;
+ vinfo[e].grid_y[3] = + y;
+ vinfo[e].grid_x[3] = -x;
+ vinfo[e].grid_y[4] = -y;
+ vinfo[e].grid_x[4] = -x;
+ vinfo[e].grid_y[5] = -x;
+ vinfo[e].grid_x[5] = -y;
+ vinfo[e].grid_y[6] = -x;
+ vinfo[e].grid_x[6] = + y;
+ vinfo[e].grid_y[7] = -y;
+ vinfo[e].grid_x[7] = + x;
+
+
+ /* Analyze slopes */
+ for (i = 0; i < hack.num_slopes; ++i)
+ {
+ m = hack.slopes[i];
+
+ /* Memorize intersection slopes (for non-player-grids) */
+ if ((e > 0) &&
+ (hack.slopes_min[y][x] < m) &&
+ (m < hack.slopes_max[y][x]))
+ {
+ switch (i / 32)
+ {
+ case 3:
+ vinfo[e].bits_3 |= (1L << (i % 32));
+ break;
+ case 2:
+ vinfo[e].bits_2 |= (1L << (i % 32));
+ break;
+ case 1:
+ vinfo[e].bits_1 |= (1L << (i % 32));
+ break;
+ case 0:
+ vinfo[e].bits_0 |= (1L << (i % 32));
+ break;
+ }
+ }
+ }
+
+
+ /* Default */
+ vinfo[e].next_0 = &vinfo[0];
+
+ /* Grid next child */
+ if (distance(0, 0, y, x + 1) <= MAX_SIGHT)
+ {
+ if ((queue[queue_tail - 1]->grid_y[0] != y) ||
+ (queue[queue_tail - 1]->grid_x[0] != x + 1))
+ {
+ vinfo[queue_tail].grid_y[0] = y;
+ vinfo[queue_tail].grid_x[0] = x + 1;
+ queue[queue_tail] = &vinfo[queue_tail];
+ queue_tail++;
+ }
+
+ vinfo[e].next_0 = &vinfo[queue_tail - 1];
+ }
+
+
+ /* Default */
+ vinfo[e].next_1 = &vinfo[0];
+
+ /* Grid diag child */
+ if (distance(0, 0, y + 1, x + 1) <= MAX_SIGHT)
+ {
+ if ((queue[queue_tail - 1]->grid_y[0] != y + 1) ||
+ (queue[queue_tail - 1]->grid_x[0] != x + 1))
+ {
+ vinfo[queue_tail].grid_y[0] = y + 1;
+ vinfo[queue_tail].grid_x[0] = x + 1;
+ queue[queue_tail] = &vinfo[queue_tail];
+ queue_tail++;
+ }
+
+ vinfo[e].next_1 = &vinfo[queue_tail - 1];
+ }
+
+
+ /* Hack -- main diagonal has special children */
+ if (y == x) vinfo[e].next_0 = vinfo[e].next_1;
+
+
+ /* Extra values */
+ vinfo[e].y = y;
+ vinfo[e].x = x;
+ vinfo[e].d = ((y > x) ? (y + x / 2) : (x + y / 2));
+ vinfo[e].r = ((!y) ? x : (!x) ? y : (y == x) ? y : 0);
+ }
+
+
+ /* Verify maximal bits XXX XXX XXX */
+ if (((vinfo[1].bits_3 | vinfo[2].bits_3) != VINFO_BITS_3) ||
+ ((vinfo[1].bits_2 | vinfo[2].bits_2) != VINFO_BITS_2) ||
+ ((vinfo[1].bits_1 | vinfo[2].bits_1) != VINFO_BITS_1) ||
+ ((vinfo[1].bits_0 | vinfo[2].bits_0) != VINFO_BITS_0))
+ {
+ quit("Incorrect bit masks!");
+ }
+
+
+ /* 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;
+
+ /* Check for "simple" illumination */
+ if (cave[yy][xx].info & (CAVE_GLOW))
+ {
+ /* Mark as 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;
+ }
+ }
+
+ /* 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];
+
+ /* 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 */
+ auto r_ptr = m_ptr->race();
+
+ /* 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 -- Allow us to treat the "seen" array as a queue
+ */
+static int flow_head = 0;
+static int flow_tail = 0;
+
+
+/*
+ * Take note of a reachable grid. Assume grid is legal.
+ */
+static void update_flow_aux(int y, int x, int n)
+{
+ cave_type *c_ptr;
+
+ int old_head = flow_head;
+
+
+ /* Get the grid */
+ c_ptr = &cave[y][x];
+
+ /* Ignore "pre-stamped" entries */
+ if (c_ptr->when == flow_n) return;
+
+ /* Ignore "walls" and "rubble" */
+ if (c_ptr->feat >= FEAT_RUBBLE) return;
+
+ /* Save the time-stamp */
+ c_ptr->when = flow_n;
+
+ /* Save the flow cost */
+ c_ptr->cost = n;
+
+ /* Hack -- limit flow depth */
+ if (n == MONSTER_FLOW_DEPTH) return;
+
+ /* Enqueue that entry */
+ temp_y[flow_head] = y;
+ temp_x[flow_head] = x;
+
+ /* Advance the queue */
+ if (++flow_head == TEMP_MAX) flow_head = 0;
+
+ /* Hack -- notice overflow by forgetting new entry */
+ if (flow_head == flow_tail) flow_head = old_head;
+}
+
+
+/*
+ * Hack -- fill in the "cost" field of every grid that the player
+ * can "reach" with the number of steps needed to reach that grid.
+ * This also yields the "distance" of the player from every grid.
+ *
+ * In addition, mark the "when" of the grids that can reach
+ * the player with the incremented value of "flow_n".
+ *
+ * Hack -- use the "seen" array as a "circular queue".
+ *
+ * We do not need a priority queue because the cost from grid
+ * to grid is always "one" and we process them in order.
+ */
+void update_flow(void)
+{
+ int x, y, d;
+
+ /* Hack -- disabled */
+ if (!flow_by_sound) return;
+
+ /* Paranoia -- make sure the array is empty */
+ if (temp_n) return;
+
+ /* Cycle the old entries (once per 128 updates) */
+ if (flow_n == 255)
+ {
+ /* Rotate the time-stamps */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ int w = cave[y][x].when;
+ cave[y][x].when = (w > 128) ? (w - 128) : 0;
+ }
+ }
+
+ /* Restart */
+ flow_n = 127;
+ }
+
+ /* Start a new flow (never use "zero") */
+ flow_n++;
+
+
+ /* Reset the "queue" */
+ flow_head = flow_tail = 0;
+
+ /* Add the player's grid to the queue */
+ update_flow_aux(p_ptr->py, p_ptr->px, 0);
+
+ /* Now process the queue */
+ while (flow_head != flow_tail)
+ {
+ /* Extract the next entry */
+ y = temp_y[flow_tail];
+ x = temp_x[flow_tail];
+
+ /* Forget that entry */
+ if (++flow_tail == TEMP_MAX) flow_tail = 0;
+
+ /* Add the "children" */
+ for (d = 0; d < 8; d++)
+ {
+ /* Add that child if "legal" */
+ update_flow_aux(y + ddy_ddd[d], x + ddx_ddd[d], cave[y][x].cost + 1);
+ }
+ }
+
+ /* Forget the flow info */
+ flow_head = flow_tail = 0;
+}
+
+
+
+
+
+
+
+/*
+ * Hack -- map the current panel (plus some) ala "magic mapping"
+ */
+void map_area(void)
+{
+ int i, x, y, y1, y2, x1, x2;
+
+ cave_type *c_ptr;
+
+
+ /* Pick an area to map */
+ y1 = panel_row_min - randint(10);
+ y2 = panel_row_max + randint(10);
+ x1 = panel_col_min - randint(20);
+ x2 = panel_col_max + randint(20);
+
+ /* Speed -- shrink to fit legal bounds */
+ if (y1 < 1) y1 = 1;
+ if (y2 > cur_hgt - 2) y2 = cur_hgt - 2;
+ if (x1 < 1) x1 = 1;
+ if (x2 > cur_wid - 2) x2 = cur_wid - 2;
+
+ /* Scan that area */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ c_ptr = &cave[y][x];
+
+ /* All non-walls are "checked" */
+ if (!is_wall(c_ptr))
+ {
+ /* Memorize normal features */
+ if (!cave_plain_floor_grid(c_ptr))
+ {
+ /* Memorize the object */
+ c_ptr->info |= (CAVE_MARK);
+ }
+
+ /* Memorize known walls */
+ for (i = 0; i < 8; i++)
+ {
+ c_ptr = &cave[y + ddy_ddd[i]][x + ddx_ddd[i]];
+
+ /* Memorize walls (etc) */
+ if (is_wall(c_ptr))
+ {
+ /* Memorize the walls */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+ }
+ }
+ }
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+/*
+ * Light up the dungeon using "clairvoyance"
+ *
+ * This function "illuminates" every grid in the dungeon, memorizes all
+ * "objects", memorizes all grids as with magic mapping, and, under the
+ * standard option settings (view_perma_grids but not view_torch_grids)
+ * memorizes all floor grids too.
+ *
+ * Note that if "view_perma_grids" is not set, we do not memorize floor
+ * grids, since this would defeat the purpose of "view_perma_grids", not
+ * that anyone seems to play without this option.
+ *
+ * Note that if "view_torch_grids" is set, we do not memorize floor grids,
+ * since this would prevent the use of "view_torch_grids" as a method to
+ * keep track of what grids have been observed directly.
+ */
+void wiz_lite(void)
+{
+ int i, y, x;
+
+
+ /* Memorize objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx) continue;
+
+ /* Memorize */
+ o_ptr->marked = TRUE;
+ }
+
+ /* Scan all normal grids */
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ /* Scan all normal grids */
+ for (x = 1; x < cur_wid - 1; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ auto const r_ptr = m_ptr->race();
+
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ object_type *o_ptr = &o_list[m_ptr->mimic_o_idx()];
+ o_ptr->marked = TRUE;
+ }
+ }
+
+ /* Process all non-walls */
+ /* if (c_ptr->feat < FEAT_SECRET) */
+ {
+ /* Scan all neighbors */
+ for (i = 0; i < 9; i++)
+ {
+ int yy = y + ddy_ddd[i];
+ int xx = x + ddx_ddd[i];
+
+ /* Get the grid */
+ c_ptr = &cave[yy][xx];
+
+ /* Perma-lite the grid */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Memorize normal features */
+ if (!cave_plain_floor_grid(c_ptr))
+ {
+ /* Memorize the grid */
+ c_ptr->info |= (CAVE_MARK);
+ }
+
+ /* Normally, memorize floors (see above) */
+ if (view_perma_grids && !view_torch_grids)
+ {
+ /* Memorize the grid */
+ c_ptr->info |= (CAVE_MARK);
+ }
+ }
+ }
+ }
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+void wiz_lite_extra(void)
+{
+ int y, x;
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave[y][x].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ wiz_lite();
+}
+
+/*
+ * Forget the dungeon map (ala "Thinking of Maud...").
+ */
+void wiz_dark(void)
+{
+ int i, y, x;
+
+
+ /* Forget every grid */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Process the grid */
+ c_ptr->info &= ~(CAVE_MARK);
+ }
+ }
+
+ /* Forget all objects */
+ for (i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx) continue;
+
+ /* Forget the object */
+ o_ptr->marked = FALSE;
+ }
+
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+
+
+
+/*
+ * Change the "feat" flag for a grid, and notice/redraw the grid
+ */
+void cave_set_feat(int y, int x, int feat)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Change the feature */
+ c_ptr->feat = feat;
+
+ /*
+ * Handle "wall/door" grids
+ *
+ * XXX XXX XXX This assumes c_ptr->mimic doesn't mimic terrain
+ * features whose LoS behaviour is different from its own, in
+ * most cases. Level boundaries are the most notable exception,
+ * where "real" terrain is always FEAT_PERM_SOLID, and the fact
+ * is (ab)used to prevent out-of-range access to the cave array.
+ * If we were going to implement an evil dungeon type in which
+ * everything is mimicked, then this function, los(), projectable(),
+ * project_path() and maybe some functions in melee2.c might
+ * better use c_ptr->mimic when it's set -- pelpel
+ */
+ if (!cave_sight_grid(c_ptr))
+ {
+ c_ptr->info |= (CAVE_WALL);
+ }
+
+ /* Handle "floor"/etc grids */
+ else
+ {
+ c_ptr->info &= ~(CAVE_WALL);
+ }
+
+ /* Notice & Redraw */
+ if (character_dungeon)
+ {
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+}
+
+
+/*
+ * Place floor terrain at (y, x) according to dungeon info
+ */
+void place_floor(int y, int x)
+{
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+}
+
+/*
+ * This routine is used when the current feature gets convert to a floor and
+ * the possible floor types include glass which is permanent. An unpassable
+ * feature is undesirable, so the glass gets convert to molten glass which
+ * is passable.
+ */
+void place_floor_convert_glass(int y, int x)
+{
+ place_floor(y, x);
+
+ if (cave[y][x].feat == 188) cave[y][x].feat = 103;
+}
+
+/*
+ * Place a cave filler at (y, x)
+ */
+void place_filler(int y, int x)
+{
+ cave_set_feat(y, x, fill_type[rand_int(100)]);
+}
+
+
+/*
+ * Calculate "incremental motion". Used by project() and shoot().
+ * Assumes that (*y,*x) lies on the path from (y1,x1) to (y2,x2).
+ */
+void mmove2(int *y, int *x, int y1, int x1, int y2, int x2)
+{
+ int dy, dx, dist, shift;
+
+ /* Extract the distance travelled */
+ dy = (*y < y1) ? y1 - *y : *y - y1;
+ dx = (*x < x1) ? x1 - *x : *x - x1;
+
+ /* Number of steps */
+ dist = (dy > dx) ? dy : dx;
+
+ /* We are calculating the next location */
+ dist++;
+
+
+ /* Calculate the total distance along each axis */
+ dy = (y2 < y1) ? (y1 - y2) : (y2 - y1);
+ dx = (x2 < x1) ? (x1 - x2) : (x2 - x1);
+
+ /* Paranoia -- Hack -- no motion */
+ if (!dy && !dx) return;
+
+
+ /* Move mostly vertically */
+ if (dy > dx)
+ {
+ /* Extract a shift factor */
+ shift = (dist * dx + (dy - 1) / 2) / dy;
+
+ /* Sometimes move along the minor axis */
+ (*x) = (x2 < x1) ? (x1 - shift) : (x1 + shift);
+
+ /* Always move along major axis */
+ (*y) = (y2 < y1) ? (y1 - dist) : (y1 + dist);
+ }
+
+ /* Move mostly horizontally */
+ else
+ {
+ /* Extract a shift factor */
+ shift = (dist * dy + (dx - 1) / 2) / dx;
+
+ /* Sometimes move along the minor axis */
+ (*y) = (y2 < y1) ? (y1 - shift) : (y1 + shift);
+
+ /* Always move along major axis */
+ (*x) = (x2 < x1) ? (x1 - dist) : (x1 + dist);
+ }
+}
+
+
+
+/*
+ * Determine if a bolt spell cast from (y1,x1) to (y2,x2) will arrive
+ * at the final destination, assuming no monster gets in the way.
+ *
+ * This is slightly (but significantly) different from "los(y1,x1,y2,x2)".
+ */
+bool_ projectable(int y1, int x1, int y2, int x2)
+{
+ int dist, y, x;
+
+ /* Start at the initial location */
+ y = y1, x = x1;
+
+ /* See "project()" */
+ for (dist = 0; dist <= MAX_RANGE; dist++)
+ {
+ /* Check for arrival at "final target" */
+ /*
+ * NB: this check was AFTER the 'never pass
+ * thru walls' clause, below. Switching them
+ * lets monsters shoot a the player if s/he is
+ * visible but in a wall
+ */
+ if ((x == x2) && (y == y2)) return (TRUE);
+
+ /* Never pass through walls */
+ if (dist && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) break;
+
+ /* Calculate the new location */
+ mmove2(&y, &x, y1, x1, y2, x2);
+ }
+
+
+ /* Assume obstruction */
+ return (FALSE);
+}
+
+
+
+
+/*
+ * Standard "find me a location" function
+ *
+ * Obtains a legal location within the given distance of the initial
+ * location, and with "los()" from the source to destination location.
+ *
+ * This function is often called from inside a loop which searches for
+ * locations while increasing the "d" distance.
+ *
+ * Currently the "m" parameter is unused.
+ */
+void scatter(int *yp, int *xp, int y, int x, int d)
+{
+ int nx, ny;
+ int attempts_left = 5000;
+
+ /* Pick a location */
+ while (--attempts_left)
+ {
+ /* Pick a new location */
+ ny = rand_spread(y, d);
+ nx = rand_spread(x, d);
+
+ /* Ignore illegal locations and outer walls */
+ if (!in_bounds(ny, nx)) continue;
+
+ /* Ignore "excessively distant" locations */
+ if ((d > 1) && (distance(y, x, ny, nx) > d)) continue;
+
+ /* Require "line of sight" */
+ if (los(y, x, ny, nx)) break;
+ }
+
+ if (attempts_left > 0)
+ {
+ /* Save the location */
+ (*yp) = ny;
+ (*xp) = nx;
+ }
+}
+
+
+
+
+/*
+ * Track a new monster
+ */
+void health_track(int m_idx)
+{
+ /* Track a new guy */
+ health_who = m_idx;
+
+ /* Redraw (later) */
+ p_ptr->redraw |= (PR_FRAME);
+}
+
+
+
+/*
+ * 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.
+ *
+ * All disturbance cancels repeated commands, resting, and running.
+ */
+void disturb(int stop_search)
+{
+ /* Cancel auto-commands */
+ /* command_new = 0; */
+
+ /* Cancel repeated commands */
+ if (command_rep)
+ {
+ /* Cancel */
+ command_rep = 0;
+
+ /* Redraw the state (later) */
+ p_ptr->redraw |= (PR_FRAME);
+ }
+
+ /* Cancel Resting */
+ if (resting)
+ {
+ /* Cancel */
+ resting = 0;
+
+ /* Redraw the state (later) */
+ p_ptr->redraw |= (PR_FRAME);
+ }
+
+ /* 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_FRAME);
+ }
+
+ /* Flush the input if requested */
+ if (flush_disturb) flush();
+}
+
+
+
+/*
+ * Return the index of the random quest on this level
+ * (or zero)
+ */
+static 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;
+}
+
+
+
+/*
+ * 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);
+}
+
+
+/*
+ * 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;
+}
+
+/**
+ * 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-
+ */
+bool cave_floor_bold(int y, int x)
+{
+ return cave_floor_grid(&cave[y][x]);
+}
+
+/**
+ * Grid based version of "cave_floor_bold()"
+ */
+bool cave_floor_grid(cave_type const *c)
+{
+ return (f_info[c->feat].flags1 & FF1_FLOOR) && (c->feat != FEAT_MON_TRAP);
+}
+
+
+
+/**
+ * Determine if a "legal" grid is floor without the REMEMBER flag set
+ * Sometimes called "boring" grid
+ */
+bool cave_plain_floor_bold(int y, int x)
+{
+ return cave_plain_floor_grid(&cave[y][x]);
+}
+
+/**
+ * Grid based version of "cave_plain_floor_bold()"
+ */
+bool cave_plain_floor_grid(cave_type const *c)
+{
+ return
+ (f_info[c->feat].flags1 & FF1_FLOOR) &&
+ !(f_info[c->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-
+ */
+bool cave_sight_bold(int y, int x)
+{
+ return cave_sight_grid(&cave[y][x]);
+}
+
+bool cave_sight_grid(cave_type const *c)
+{
+ return !(f_info[c->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
+ */
+bool cave_clean_bold(int y, int x)
+{
+ return
+ (f_info[cave[y][x].feat].flags1 & FF1_FLOOR) &&
+ (cave[y][x].feat != FEAT_MON_TRAP) &&
+ (cave[y][x].o_idxs.empty()) &&
+ !(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
+ */
+bool cave_empty_bold(int y, int x)
+{
+ return
+ 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
+ */
+bool cave_naked_bold(int y, int x)
+{
+ return
+ (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_idxs.empty()) &&
+ (cave[y][x].m_idx == 0);
+}
+
+bool cave_naked_bold2(int y, int x)
+{
+ return
+ (f_info[cave[y][x].feat].flags1 & FF1_FLOOR) &&
+ (cave[y][x].feat != FEAT_MON_TRAP) &&
+ (cave[y][x].o_idxs.empty()) &&
+ (cave[y][x].m_idx == 0);
+}
+
+
+/**
+ * Determine if a "legal" grid is "permanent"
+ */
+bool cave_perma_bold(int y, int x)
+{
+ return cave_perma_grid(&cave[y][x]);
+}
+
+bool cave_perma_grid(cave_type const *c)
+{
+ return 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
+ */
+bool player_has_los_bold(int y, int x)
+{
+ return (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
+ */
+bool player_can_see_bold(int y, int x)
+{
+ return (cave[y][x].info & (CAVE_SEEN)) != 0;
+}
diff --git a/src/cave.hpp b/src/cave.hpp
new file mode 100644
index 00000000..64f67dba
--- /dev/null
+++ b/src/cave.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include "h-basic.h"
+#include "cave_type_fwd.hpp"
+#include "object_type_fwd.hpp"
+
+extern int distance(int y1, int x1, int y2, int x2);
+extern bool_ los(int y1, int x1, int y2, int x2);
+extern bool_ cave_valid_bold(int y, int x);
+extern bool_ no_lite(void);
+extern void map_info_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 update_flow(void);
+extern void map_area(void);
+extern void wiz_lite(void);
+extern void wiz_lite_extra(void);
+extern void wiz_dark(void);
+extern void cave_set_feat(int y, int x, int feat);
+extern void place_floor(int y, int x);
+extern void place_floor_convert_glass(int y, int x);
+extern void place_filler(int y, int x);
+extern void mmove2(int *y, int *x, int y1, int x1, int y2, int x2);
+extern bool_ projectable(int y1, int x1, int y2, int x2);
+extern void scatter(int *yp, int *xp, int y, int x, int d);
+extern void health_track(int m_idx);
+extern void monster_race_track(int r_idx, int ego);
+extern void object_track(object_type *o_ptr);
+extern void disturb(int stop_search);
+extern int is_quest(int level);
+extern int new_effect(int type, int dam, int time, int cy, int cx, int rad, s32b flags);
+extern bool cave_floor_bold(int y, int x);
+extern bool cave_floor_grid(cave_type const *c);
+extern bool cave_plain_floor_bold(int y, int x);
+extern bool cave_plain_floor_grid(cave_type const *c);
+extern bool cave_sight_bold(int y, int x);
+extern bool cave_sight_grid(cave_type const *c);
+extern bool cave_clean_bold(int y, int x);
+extern bool cave_empty_bold(int y, int x);
+extern bool cave_naked_bold(int y, int x);
+extern bool cave_naked_bold2(int y, int x);
+extern bool cave_perma_bold(int y, int x);
+extern bool cave_perma_grid(cave_type const *c);
+extern bool player_has_los_bold(int y, int x);
+extern bool player_can_see_bold(int y, int x);
diff --git a/src/cave_type.hpp b/src/cave_type.hpp
new file mode 100644
index 00000000..958ace1d
--- /dev/null
+++ b/src/cave_type.hpp
@@ -0,0 +1,65 @@
+#pragma once
+
+#include "h-basic.h"
+
+#include <cassert>
+#include <vector>
+
+/**
+ * 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 to create the
+ * singly linked list of objects. If "o_idx" is zero then there
+ * are no objects in the grid.
+ */
+struct cave_type
+{
+ u16b info = 0; /* Hack -- cave flags */
+
+ byte feat = 0; /* Hack -- feature type */
+
+ std::vector<s16b> o_idxs { }; /* Indexes of objects in this grid */
+
+ s16b m_idx = 0; /* Monster in this grid */
+
+ s16b t_idx = 0; /* trap index (in t_list) or zero */
+
+ s16b special = 0; /* Special cave info */
+ s16b special2 = 0; /* Special cave info */
+
+ s16b inscription = 0; /* Inscription of the grid */
+
+ byte mana = 0; /* Magical energy of the grid */
+
+ byte mimic = 0; /* Feature to mimic */
+
+ byte cost = 0; /* Hack -- cost of flowing */
+ byte when = 0; /* Hack -- when cost was computed */
+
+ s16b effect = 0; /* The lasting effects */
+
+ /**
+ * @brief wipe the object's state
+ */
+ void wipe() {
+ /* Reset to defaults */
+ *this = cave_type();
+ }
+
+};
diff --git a/src/cave_type_fwd.hpp b/src/cave_type_fwd.hpp
new file mode 100644
index 00000000..f2569ea6
--- /dev/null
+++ b/src/cave_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct cave_type;
diff --git a/src/cli_comm.hpp b/src/cli_comm.hpp
new file mode 100644
index 00000000..6ae53edc
--- /dev/null
+++ b/src/cli_comm.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * A structure for CLI commands.
+ */
+struct cli_comm
+{
+ cptr comm; /* Extended name of the command. */
+ cptr descrip; /* Description of the command. */
+ s16b key; /* Key to convert command to. */
+};
diff --git a/src/cli_comm_fwd.hpp b/src/cli_comm_fwd.hpp
new file mode 100644
index 00000000..6d9b76a3
--- /dev/null
+++ b/src/cli_comm_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct cli_comm;
diff --git a/src/cmd1.cc b/src/cmd1.cc
new file mode 100644
index 00000000..f4066915
--- /dev/null
+++ b/src/cmd1.cc
@@ -0,0 +1,4999 @@
+/*
+ * 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 "cmd1.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd4.hpp"
+#include "cmd5.hpp"
+#include "dungeon_info_type.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "gods.hpp"
+#include "hooks.hpp"
+#include "hook_move_in.hpp"
+#include "lua_bind.hpp"
+#include "melee1.hpp"
+#include "melee2.hpp"
+#include "mimic.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "skills.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "spells3.hpp"
+#include "tables.hpp"
+#include "traps.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "wild.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#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;
+
+ auto const r_ptr = m_ptr->race();
+
+ 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)
+{
+ /* Start with base search ability */
+ int 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 (int y = (p_ptr->py - 1); y <= (p_ptr->py + 1); y++)
+ {
+ for (int x = (p_ptr->px - 1); x <= (p_ptr->px + 1); x++)
+ {
+ /* Sometimes, notice things */
+ if (rand_int(100) < chance)
+ {
+ /* Access the grid */
+ cave_type *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);
+ }
+
+ /* 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);
+ }
+
+ /* Scan all objects in the grid */
+ for (auto const o_idx: c_ptr->o_idxs)
+ {
+ object_type * o_ptr = &o_list[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);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+
+/*
+ * 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);
+
+ /* 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_info[c_ptr->t_idx].name);
+ }
+ }
+}
+
+
+void touch_zap_player(monster_type *m_ptr)
+{
+ auto r_ptr = m_ptr->race();
+
+ if (r_ptr->flags2 & (RF2_AURA_FIRE))
+ {
+ if (!(p_ptr->immune_fire))
+ {
+ char aura_dam[80];
+
+ int 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];
+
+ int aura_damage =
+ damroll(1 + (m_ptr->level / 26), 1 + (m_ptr->level / 17));
+
+ /* Hack -- Get the "died from" name */
+ monster_desc(aura_dam, m_ptr, 0x88);
+
+ if (p_ptr->oppose_elec) aura_damage = (aura_damage + 2) / 3;
+ if (p_ptr->resist_elec) aura_damage = (aura_damage + 2) / 3;
+
+ msg_print("You get zapped!");
+ take_hit(aura_damage, aura_dam);
+ r_ptr->r_flags2 |= RF2_AURA_ELEC;
+ handle_stuff();
+ }
+ }
+}
+
+
+/*
+ * Carried monster can attack too.
+ * Based on monst_attack_monst.
+ */
+static void carried_monster_attack(s16b m_idx, bool_ *fear, bool_ *mdeath,
+ int x, int y)
+{
+ monster_type *t_ptr = &m_list[m_idx];
+
+ monster_race *r_ptr;
+
+ int ap_cnt;
+
+ int ac, rlev, pt;
+
+ char t_name[80];
+
+ cptr sym_name = symbiote_name(TRUE);
+
+ char temp[80];
+
+ bool_ blinked = FALSE, touched = FALSE;
+
+ byte y_saver = t_ptr->fy;
+
+ byte x_saver = t_ptr->fx;
+
+ object_type *o_ptr;
+
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ if (!o_ptr->k_idx) return;
+
+ r_ptr = &r_info[o_ptr->pval];
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & RF1_NEVER_BLOW) return;
+
+ /* Total armor */
+ ac = t_ptr->ac;
+
+ /* Extract the effective monster level */
+ rlev = ((o_ptr->elevel >= 1) ? o_ptr->elevel : 1);
+
+ /* Get the monster name (or "it") */
+ monster_desc(t_name, t_ptr, 0);
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ if (!t_ptr->ml)
+ {
+ msg_print("You hear noise.");
+ }
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = r_ptr->blow[ap_cnt].effect;
+ int method = r_ptr->blow[ap_cnt].method;
+ int d_dice = r_ptr->blow[ap_cnt].d_dice;
+ int d_side = r_ptr->blow[ap_cnt].d_side;
+
+ /* Stop attacking if the target dies! */
+ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver)
+ break;
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+ if (blinked) /* Stop! */
+ {
+ /* break; */
+ }
+
+ /* Extract visibility (before blink) */
+ visible = TRUE;
+
+ /* Extract the attack "power" */
+ power = get_attack_power(effect);
+
+ /* Monster hits */
+ if (!effect || check_hit2(power, rlev, ac))
+ {
+ /* Always disturbing */
+ disturb(1);
+
+ /* 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)
+ {
+ strnfmt(temp, sizeof(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)
+ {
+ auto tr_ptr = t_ptr->race();
+ /* 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);
+
+ /* 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];
+
+ auto tr_ptr = t_ptr->race();
+
+ 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;
+
+ auto r_ptr = &r_info[p_ptr->body_monster];
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & RF1_NEVER_BLOW) return;
+
+ /* Total armor */
+ ac = t_ptr->ac;
+
+ /* Extract the effective monster level */
+ rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1);
+
+ /* Get the monster name (or "it") */
+ monster_desc(t_name, t_ptr, 0);
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ if (!t_ptr->ml)
+ {
+ msg_print("You hear noise.");
+ }
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < (p_ptr->num_blow > 4) ? 4 : p_ptr->num_blow;
+ ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = r_ptr->blow[ap_cnt].effect;
+ int method = r_ptr->blow[ap_cnt].method;
+ int d_dice = r_ptr->blow[ap_cnt].d_dice;
+ int d_side = r_ptr->blow[ap_cnt].d_side;
+
+ /* Stop attacking if the target dies! */
+ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver)
+ break;
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+ if (blinked) /* Stop! */
+ {
+ /* break; */
+ }
+
+ /* Extract visibility (before blink) */
+ visible = TRUE;
+
+ /* Extract the attack "power" */
+ power = get_attack_power(effect);
+
+ /* Monster hits */
+ if (!effect || check_hit2(power, rlev, ac))
+ {
+ /* Always disturbing */
+ disturb(1);
+
+ /* 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)
+ {
+ strnfmt(temp, sizeof(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);
+
+ /* 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)
+{
+ auto const r_ptr = m_ptr->race();
+
+ /* Extract monster name (or "it") */
+ char m_name[80];
+ 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 *blow_table = ma_blows;
+ int resist_stun = 0;
+ int max = MAX_MA;
+ bool_ desc = FALSE;
+ 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);
+ }
+ martial_arts *ma_ptr = &blow_table[0];
+ martial_arts *old_ptr = &blow_table[0];
+
+ /* Extract monster name (or "it") */
+ auto const r_ptr = m_ptr->race();
+ 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);
+
+ /* Extract name */
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Describe attack */
+ 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;
+ }
+
+ bool_ done_crit;
+ *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
+ */
+static void do_nazgul(int *k, int *num, int num_blow, int weap, std::shared_ptr<monster_race> r_ptr,
+ object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ bool_ mundane;
+ bool_ allow_shatter = TRUE;
+
+ /* Extract mundane-ness of the current weapon */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* It should be Slay Evil, Slay Undead, or *Slay Undead* */
+ mundane = !(f1 & TR1_SLAY_EVIL) && !(f1 & TR1_SLAY_UNDEAD) &&
+ !(f5 & TR5_KILL_UNDEAD);
+
+ /* Some blades can resist shattering */
+ if (f5 & TR5_RES_MORGUL)
+ allow_shatter = FALSE;
+
+ /* Mega Hack -- Hitting Nazgul is REALY dangerous (ideas from Akhronath) */
+ if (r_ptr->flags7 & RF7_NAZGUL)
+ {
+ if ((!o_ptr->name2) && (!artifact_p(o_ptr)) && allow_shatter)
+ {
+ msg_print("Your weapon *DISINTEGRATES*!");
+ *k = 0;
+
+ inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* To stop attacking */
+ *num = num_blow;
+ }
+ else if (o_ptr->name2)
+ {
+ if (mundane)
+ {
+ msg_print
+ ("The Ringwraith is IMPERVIOUS to the mundane weapon.");
+ *k = 0;
+ }
+
+ /* 25% chance of getting destroyed */
+ if (magik(25) && allow_shatter)
+ {
+ msg_print("Your weapon is destroyed!");
+
+ inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* To stop attacking */
+ *num = num_blow;
+ }
+ }
+ else if (artifact_p(o_ptr))
+ {
+ if (mundane)
+ {
+ msg_print
+ ("The Ringwraith is IMPERVIOUS to the mundane weapon.");
+ *k = 0;
+ }
+
+ apply_disenchant(INVEN_WIELD + weap);
+
+ /* 1/1000 chance of getting destroyed */
+ if (!rand_int(1000) && allow_shatter)
+ {
+ msg_print("Your weapon is destroyed!");
+
+ inc_stack_size_ex(INVEN_WIELD + weap, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* To stop attacking */
+ *num = num_blow;
+ }
+ }
+
+ /* If any damage is done, then 25% chance of getting the Black Breath */
+ if (*k)
+ {
+ if (magik(25))
+ {
+ msg_print("Your foe calls upon your soul!");
+ msg_print
+ ("You feel the Black Breath slowly draining you of life...");
+ p_ptr->black_breath = TRUE;
+ }
+ }
+ }
+}
+
+
+/*
+ * Player attacks a (poor, defenseless) creature -RAK-
+ *
+ * If no "weapon" is available, then "punch" the monster one time.
+ */
+void py_attack(int y, int x, int max_blow)
+{
+ int num = 0, k, bonus, chance;
+
+ s32b special = 0;
+
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ bool_ fear = FALSE;
+
+ bool_ mdeath = FALSE;
+
+ bool_ backstab = FALSE;
+
+ bool_ vorpal_cut = FALSE;
+
+ int chaos_effect = 0;
+
+ bool_ stab_fleeing = FALSE;
+
+ bool_ do_quake = FALSE;
+
+ bool_ done_crit = FALSE;
+
+ bool_ drain_msg = TRUE;
+
+ int drain_result = 0, drain_heal = 0;
+
+ int drain_left = MAX_VAMPIRIC_DRAIN;
+
+ /* A massive hack -- life-draining weapons */
+ u32b f1, f2, f3, f4, f5, esp;
+
+ int weap;
+
+ /* Disturb the player */
+ disturb(0);
+
+ 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") */
+ char m_name[80];
+ 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);
+
+ /* 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 */
+ object_type *o_ptr = &p_ptr->inventory[INVEN_WIELD + weap];
+
+ /* Get race info */
+ auto r_ptr = m_ptr->race();
+
+ /* 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);
+ }
+
+ if (praying_to(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*/
+ if (praying_to(GOD_MELKOR))
+ {
+ int lv = get_level(MELKOR_CURSE, 100, 1);
+
+ 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))
+ {
+ do_melkor_curse(c_ptr->m_idx);
+ }
+ }
+ }
+
+ /* May it clone the monster ? */
+ if ((f4 & TR4_CLONE) && magik(30))
+ {
+ msg_format("Oh no! Your weapon clones %^s!",
+ m_name);
+ multiply_monster(c_ptr->m_idx, FALSE, TRUE);
+ }
+
+ /* Apply the player damage bonuses */
+ k += p_ptr->to_d + p_ptr->to_d_melee;
+
+ /* No negative damage */
+ if (k < 0) k = 0;
+
+ /* Message */
+ if (!(backstab || stab_fleeing))
+ {
+ /* These monsters never have flavoured combat msgs */
+ if (strchr("vwjmelX,.*", r_ptr->d_char))
+ {
+ msg_format("You hit %s.", m_name);
+ }
+
+ /* Print flavoured messages if requested */
+ else
+ {
+ char buff[255];
+
+ flavored_attack((100 * k) / m_ptr->maxhp, buff);
+ msg_format(buff, m_name);
+ }
+ }
+ else if (backstab)
+ {
+ char buf[80];
+
+ monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego);
+
+ backstab = FALSE;
+
+ msg_format
+ ("You cruelly stab the helpless, sleeping %s!",
+ buf);
+ }
+ else
+ {
+ char buf[80];
+
+ monster_race_desc(buf, m_ptr->r_idx, m_ptr->ego);
+
+ msg_format("You backstab the fleeing %s!", buf);
+ }
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.", k,
+ m_ptr->hp);
+ }
+
+ if (special) attack_special(m_ptr, special, k);
+
+ /* Damage, check for fear and death */
+ if (mon_take_hit(c_ptr->m_idx, k, &fear, NULL))
+ {
+ /* Hack -- High-level warriors can spread their attacks out
+ * among weaker foes.
+ */
+ if ((has_ability(AB_SPREAD_BLOWS)) && (num < num_blow) &&
+ (energy_use))
+ {
+ energy_use = energy_use * num / num_blow;
+ }
+ mdeath = TRUE;
+ break;
+ }
+
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+
+ touch_zap_player(m_ptr);
+
+ /* Are we draining it? A little note: If the monster is
+ dead, the drain does not work... */
+
+ if (drain_result)
+ {
+ drain_result -= m_ptr->hp; /* Calculate the difference */
+
+ if (drain_result > 0) /* Did we really hurt it? */
+ {
+ drain_heal = damroll(4, (drain_result / 6));
+
+ if (cheat_xtra)
+ {
+ msg_format("Draining left: %d", drain_left);
+ }
+
+ if (drain_left)
+ {
+ if (drain_heal < drain_left)
+ {
+ drain_left -= drain_heal;
+ }
+ else
+ {
+ drain_heal = drain_left;
+ drain_left = 0;
+ }
+
+ if (drain_msg)
+ {
+ msg_format
+ ("Your weapon drains life from %s!",
+ m_name);
+ drain_msg = FALSE;
+ }
+
+ hp_player(drain_heal);
+ /* We get to keep some of it! */
+ }
+ }
+ }
+
+ /* Confusion attack */
+ if ((p_ptr->confusing) || (chaos_effect == 3))
+ {
+ /* Cancel glowing hands */
+ if (p_ptr->confusing)
+ {
+ p_ptr->confusing = FALSE;
+ msg_print("Your hands stop glowing.");
+ }
+
+ /* Confuse the monster */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ msg_format("%^s is unaffected.", m_name);
+ }
+ else if (rand_int(100) < m_ptr->level)
+ {
+ msg_format("%^s is unaffected.", m_name);
+ }
+ else
+ {
+ msg_format("%^s appears confused.", m_name);
+ m_ptr->confused +=
+ 10 + rand_int(get_skill(SKILL_COMBAT)) / 5;
+ }
+ }
+
+ else if (chaos_effect == 4)
+ {
+ msg_format("%^s disappears!", m_name);
+ teleport_away(c_ptr->m_idx, 50);
+ num = num_blow + 1; /* Can't hit it anymore! */
+ }
+
+ else if ((chaos_effect == 5) && cave_floor_bold(y, x) &&
+ (randint(90) > m_ptr->level))
+ {
+ if (!((r_ptr->flags1 & RF1_UNIQUE) ||
+ (r_ptr->flags4 & RF4_BR_CHAO) ||
+ (m_ptr->mflag & MFLAG_QUEST)))
+ {
+ /* Handle polymorph */
+ if (do_poly_monster(y, x))
+ {
+ /* Polymorph succeeded */
+ msg_format("%^s changes!", m_name);
+
+ /* Hack -- Get new monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Oops, we need a different name... */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Hack -- Get new race */
+ r_ptr = m_ptr->race();
+
+ 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);
+ }
+}
+
+
+
+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 || (race_flags1_p(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) && praying_to(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);
+}
+
+/*
+ * 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().
+ */
+
+static 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);
+
+ /* 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);
+}
+
+/*
+ * 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];
+
+ 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 */
+ {
+ hook_move_in in = { y, x };
+ if (process_hooks_new(HOOK_MOVE, &in, NULL)) {
+ return; /* Prevent movement */
+ }
+ }
+
+ if (p_ptr->dripping_tread > 0)
+ {
+ geomancy_random_floor(y, x, FALSE);
+ p_ptr->dripping_tread -= 1;
+ if (p_ptr->dripping_tread == 0)
+ {
+ msg_print("You stop dripping raw elemental energies.");
+ }
+ }
+
+
+ /* Get the monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+ auto const mr_ptr = m_ptr->race();
+
+ 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) &&
+ ((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;
+ }
+
+ /* Don't step on known traps. */
+ else if (disarm && (c_ptr->info & (CAVE_TRDT)) && !(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ {
+ msg_print("You stop to avoid triggering the trap.");
+ energy_use = 0;
+ oktomove = FALSE;
+ }
+
+ /* Player can't enter ? soo bad for him/her ... */
+ else if (!player_can_enter(c_ptr->feat))
+ {
+ oktomove = FALSE;
+
+ /* Disturb the player */
+ disturb(0);
+
+ 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_info[feat].block);
+ c_ptr->info |= (CAVE_MARK);
+ lite_spot(y, x);
+ }
+ }
+
+ /* Notice things */
+ else
+ {
+ /* Rubble */
+ if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ 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...
+ */
+ }
+ /* Closed doors */
+ else if ((c_ptr->feat >= FEAT_DOOR_HEAD) && (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ if (easy_open_door(y, x)) return;
+ }
+
+ /* 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("There is %s.", f_info[feat].block);
+
+ if (!(p_ptr->confused || p_ptr->stun || p_ptr->image))
+ energy_use = 0;
+ }
+ }
+
+ /* Sound */
+ sound(SOUND_HITWALL);
+ }
+
+ /*
+ * 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);
+
+ /* 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;
+
+ /* 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_FRAME);
+ }
+ 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_FRAME);
+ }
+
+ /* 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 != DEFAULT_FEAT_TEXT)
+ {
+ /* Mega-hack for dungeon branches */
+ if ((feat == FEAT_MORE) && c_ptr->special)
+ {
+ msg_format("There is %s", d_info[c_ptr->special].text);
+ }
+ else
+ {
+ msg_print(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);
+
+ /* Hack -- Enter store */
+ command_new = '_';
+ }
+
+ else if (cave[y][x].feat >= FEAT_ALTAR_HEAD &&
+ cave[y][x].feat <= FEAT_ALTAR_TAIL)
+ {
+ cptr 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);
+
+ 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);
+
+ 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;
+
+ /* 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++)
+ {
+ /* 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 */
+ cave_type *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 (auto const o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type * o_ptr = &o_list[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 */
+ cave_type *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 */
+ cave_type *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);
+
+ /* 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);
+
+ /* 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);
+}
+
+
+/*
+ * 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;
+
+ 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;
+
+ /* Build a prompt (accept all spells) */
+ if (num <= 26)
+ {
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78,
+ "(Command %c-%c, ESC=exit) Select a command: ", I2A(0),
+ I2A(num - 1));
+ }
+ else
+ {
+ strnfmt(out_val, 78,
+ "(Command %c-%c, ESC=exit) Select a command: ", I2A(0),
+ '0' + num - 27);
+ }
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Show the list */
+ {
+ byte y = 1, x = 0;
+ int ctr = 0;
+ char dummy[80];
+
+ strcpy(dummy, "");
+
+ 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);
+ }
+ }
+
+ /* Get a command from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ 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 */
+ 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
+ */
+void do_cmd_integrate_body()
+{
+ if (!p_ptr->disembodied)
+ {
+ msg_print("You are already in a body.");
+ return;
+ }
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Incarnate in which body? ",
+ "You have no corpse to incarnate in.",
+ (USE_FLOOR),
+ object_filter::TVal(TV_CORPSE)))
+ {
+ return;
+ }
+
+ object_type *o_ptr = &o_list[0 - item];
+
+ if (o_ptr->sval != SV_CORPSE_CORPSE)
+ {
+ msg_print("You must select a corpse.");
+ return;
+ }
+
+ 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();
+}
+
+/*
+ * Leave a body
+ */
+bool_ do_cmd_leave_body(bool_ drop_body)
+{
+ object_type *o_ptr, forge;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ int i;
+
+
+ if (p_ptr->disembodied)
+ {
+ msg_print("You are already disembodied.");
+ return FALSE;
+ }
+
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (p_ptr->body_parts[i - INVEN_WIELD] && p_ptr->inventory[i].k_idx &&
+ cursed_p(&p_ptr->inventory[i]))
+ {
+ msg_print("A cursed object is preventing you from leaving your body.");
+ return FALSE;
+ }
+ }
+
+ if (drop_body)
+ {
+ if (magik(25 + get_skill_scale(SKILL_POSSESSION, 25) + get_skill(SKILL_PRESERVATION)))
+ {
+ o_ptr = &forge;
+ object_prep(o_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_CORPSE));
+ o_ptr->number = 1;
+ o_ptr->pval = 0;
+ o_ptr->pval2 = p_ptr->body_monster;
+ o_ptr->pval3 = p_ptr->chp;
+ o_ptr->weight = (r_ptr->weight + rand_int(r_ptr->weight) / 10) + 1;
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ o_ptr->ident |= IDENT_STOREB;
+
+ /* Unique corpses are unique */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ o_ptr->name1 = 201;
+ }
+
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+ }
+ else
+ msg_print
+ ("You do not manage to keep the corpse from rotting away.");
+ }
+
+ msg_print("Your spirit leaves your body.");
+ p_ptr->disembodied = TRUE;
+
+ /* Turn into a lost soul(just for the picture) */
+ p_ptr->body_monster = test_monster_name("Lost soul");
+ do_cmd_redraw();
+
+ return (TRUE);
+}
+
+
+bool_ execute_inscription(byte i, byte y, byte x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+
+ /* Not enough mana in the current grid */
+ if (c_ptr->mana < inscription_info[i].mana) return (TRUE);
+
+
+ /* Reduce the grid mana -- note: it can't be restored */
+ c_ptr->mana -= inscription_info[i].mana;
+
+ /* Analyse inscription type */
+ switch (i)
+ {
+ case INSCRIP_LIGHT:
+ {
+ msg_print("The inscription shines in a bright light!");
+ lite_room(y, x);
+
+ break;
+ }
+
+ case INSCRIP_DARK:
+ {
+ msg_print("The inscription is enveloped in a dark aura!");
+ unlite_room(y, x);
+
+ break;
+ }
+
+ case INSCRIP_STORM:
+ {
+ msg_print("The inscription releases a powerful storm!");
+ project(0, 3, y, x, damroll(10, 10),
+ GF_ELEC, PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM |
+ PROJECT_KILL | PROJECT_JUMP);
+
+ break;
+ }
+
+ case INSCRIP_PROTECTION:
+ {
+ return (FALSE);
+
+ break;
+ }
+
+ case INSCRIP_DWARF_SUMMON:
+ {
+ int yy = y, xx = x;
+
+ scatter(&yy, &xx, y, x, 3);
+ place_monster_one(yy, xx, test_monster_name("Dwarven Warrior"),
+ 0, FALSE, MSTATUS_FRIEND);
+
+ break;
+ }
+
+ case INSCRIP_CHASM:
+ {
+ int ii = x, ij = y;
+
+ cave_set_feat(ij, ii, FEAT_DARK_PIT);
+ msg_print("A chasm appears in the floor!");
+
+ cave_type *c_ptr = &cave[ij][ii];
+
+ if (c_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ auto const r_ptr = m_ptr->race();
+
+ 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(c_ptr->m_idx);
+ }
+ }
+ }
+
+ if (!c_ptr->o_idxs.empty())
+ {
+ /* Copy list of objects since we're going to be manipulating the list */
+ auto const object_idxs(c_ptr->o_idxs);
+
+ /* Scan all objects in the grid */
+ for (auto const this_o_idx: object_idxs)
+ {
+ bool_ plural = FALSE;
+
+ char o_name[80];
+
+ /* Acquire object */
+ object_type * o_ptr = &o_list[this_o_idx];
+
+ if (o_ptr->number > 1) plural = TRUE;
+
+ /* 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/cmd1.hpp b/src/cmd1.hpp
new file mode 100644
index 00000000..3ae44ed2
--- /dev/null
+++ b/src/cmd1.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "h-basic.h"
+#include "monster_type_fwd.hpp"
+#include "object_type_fwd.hpp"
+
+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 do_cmd_pet(void);
+extern void do_cmd_integrate_body();
+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);
diff --git a/src/cmd2.cc b/src/cmd2.cc
new file mode 100644
index 00000000..cfdeab44
--- /dev/null
+++ b/src/cmd2.cc
@@ -0,0 +1,5014 @@
+/*
+ * 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 "cmd2.hpp"
+
+#include "bldg.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd1.hpp"
+#include "dungeon_info_type.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "gods.hpp"
+#include "hook_chat_in.hpp"
+#include "hook_enter_dungeon_in.hpp"
+#include "hook_give_in.hpp"
+#include "hook_stair_in.hpp"
+#include "hook_stair_out.hpp"
+#include "hooks.hpp"
+#include "levels.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "spells3.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "trap_type.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <chrono>
+#include <thread>
+
+using std::this_thread::sleep_for;
+using std::chrono::milliseconds;
+
+void do_cmd_immovable_special(void);
+
+/*
+ * Try to bash an altar
+ */
+static bool_ do_cmd_bash_altar(int y, int x)
+{
+ msg_print("Are you mad? You want to anger the gods?");
+ return (FALSE);
+}
+
+
+/*
+ * Try to bash a fountain
+ */
+static bool_ do_cmd_bash_fountain(int y, int x)
+{
+ int bash, temp;
+
+ bool_ more = TRUE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_BASH_DOOR))
+ {
+ msg_print("You cannot do that.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("You smash into the fountain!");
+
+ /* Hack -- Bash power based on strength */
+ /* (Ranges from 3 to 20 to 100 to 200) */
+ bash = adj_str_blow[p_ptr->stat_ind[A_STR]];
+
+ /* Compare bash power to door power XXX XXX XXX */
+ temp = (bash - 50);
+
+ /* Hack -- always have a chance */
+ if (temp < 1) temp = 1;
+
+ /* Hack -- attempt to bash down the door */
+ if (rand_int(200) < temp)
+ {
+ /* Message */
+ msg_print("The fountain breaks!");
+
+ fire_ball(GF_WATER, 5, damroll(6, 8), 2);
+
+ cave_set_feat(y, x, FEAT_DEEP_WATER);
+ more = FALSE;
+ }
+
+ return (more);
+}
+
+/*
+ * Stair hooks
+ */
+static bool_ stair_hooks(stairs_direction direction)
+{
+ hook_stair_in in = { direction };
+ hook_stair_out out = { TRUE }; /* Allow by default */
+ process_hooks_new(HOOK_STAIR, &in, &out);
+ return (!out.allow);
+}
+
+
+/*
+ * 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 (stair_hooks(STAIRS_UP))
+ {
+ return;
+ }
+
+ /* Normal up stairs */
+ if ((c_ptr->feat == FEAT_LESS) || (c_ptr->feat == FEAT_WAY_LESS))
+ {
+ if (!dun_level)
+ {
+ go_up = TRUE;
+ }
+ else if ((dungeon_flags2 & DF2_ASK_LEAVE))
+ {
+ go_up = get_check("Leave this unique level forever? ");
+ }
+ else if (confirm_stairs)
+ {
+ go_up = get_check("Really leave the level? ");
+ }
+ else
+ {
+ go_up = TRUE;
+ }
+ }
+
+ /* Shaft up */
+ else if (c_ptr->feat == FEAT_SHAFT_UP)
+ {
+ if (dun_level == 1)
+ {
+ go_up = TRUE;
+ }
+ else if ((dungeon_flags2 & DF2_ASK_LEAVE))
+ {
+ go_up = get_check("Leave this unique level forever? ");
+ }
+ else if (confirm_stairs)
+ {
+ go_up_many = get_check("Really leave the level? ");
+ }
+ else
+ {
+ go_up_many = TRUE;
+ }
+ }
+
+ /* Quest exit */
+ else if (c_ptr->feat == FEAT_QUEST_EXIT)
+ {
+ leaving_quest = p_ptr->inside_quest;
+
+ if ((dungeon_flags2 & DF2_ASK_LEAVE) &&
+ !get_check("Leave this unique level forever? "))
+ return;
+
+ p_ptr->inside_quest = c_ptr->special;
+ dun_level = 0;
+ p_ptr->oldpx = 0;
+ p_ptr->oldpy = 0;
+ p_ptr->leaving = TRUE;
+
+ return;
+ }
+
+ /* Exits to previous area in flat terrains */
+ else if (!(dungeon_flags1 & DF1_FLAT) &&
+ p_ptr->prob_travel && !p_ptr->inside_quest)
+ {
+ if (d_ptr->mindepth == dun_level) return;
+
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerful force prevents your from teleporting.");
+ return;
+ }
+
+ prob_traveling = TRUE;
+
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_up = TRUE;
+ }
+ else
+ {
+ go_up = TRUE;
+ }
+ }
+ else
+ {
+ msg_print("I see no up staircase here.");
+ return;
+ }
+
+ if (go_up || go_up_many)
+ {
+
+ energy_use = 0;
+
+ /* Success */
+ if (c_ptr->feat == FEAT_WAY_LESS)
+ msg_print("You enter the previous area.");
+ else
+ msg_print("You enter a maze of up staircases.");
+
+ autosave_checkpoint();
+
+ if (p_ptr->inside_quest)
+ {
+ dun_level = 1;
+ leaving_quest = p_ptr->inside_quest;
+
+ p_ptr->inside_quest = c_ptr->special;
+ }
+
+ /* Create a way back */
+ if (go_up_many)
+ create_down_shaft = TRUE;
+ else
+ create_down_stair = TRUE;
+
+ /* New depth */
+ if (go_up)
+ dun_level--;
+ else
+ {
+ dun_level -= randint(3) + 1;
+ if (dun_level <= 0) dun_level = 0;
+ }
+
+ if (c_ptr->special && (!prob_traveling))
+ {
+ dun_level = oldl;
+ dun_level = get_flevel();
+ dungeon_type = c_ptr->special;
+ dun_level += d_info[dungeon_type].mindepth;
+ }
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+}
+
+
+/*
+ * Returns TRUE if we are in the Between...
+ */
+static bool_ between_effect(void)
+{
+ byte bx, by;
+
+
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN)
+ {
+
+ bx = cave[p_ptr->py][p_ptr->px].special & 255;
+ by = cave[p_ptr->py][p_ptr->px].special >> 8;
+
+ msg_print("You fall into the void.");
+ msg_print("Brrrr! It's deadly cold.");
+
+ swap_position(by, bx);
+
+ /* To avoid being teleported back */
+ energy_use = 100;
+
+ return (TRUE);
+ }
+
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_BETWEEN2)
+ {
+ between_exit *be_ptr = &between_exits[cave[p_ptr->py][p_ptr->px].special];
+
+ p_ptr->wild_mode = FALSE;
+ p_ptr->wilderness_x = be_ptr->wild_x;
+ p_ptr->wilderness_y = be_ptr->wild_y;
+ p_ptr->oldpx = p_ptr->px = be_ptr->px;
+ p_ptr->oldpy = p_ptr->py = be_ptr->py;
+ dungeon_type = be_ptr->d_idx;
+ dun_level = be_ptr->level;
+ p_ptr->leaving = TRUE;
+
+ return (TRUE);
+ }
+ else
+ return (FALSE);
+}
+
+/*
+ * Go down one level
+ */
+void do_cmd_go_down(void)
+{
+ cave_type *c_ptr;
+
+ bool_ go_down = FALSE, go_down_many = FALSE, prob_traveling = FALSE;
+
+ bool_ fall_trap = FALSE;
+
+ char i;
+
+ int old_dun = dun_level;
+
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+
+ /* MUST be actived now */
+ if (between_effect()) return;
+
+ /* Player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ if (p_ptr->astral && (dun_level == 98)) return;
+
+ if (c_ptr->t_idx == TRAP_OF_SINKING) fall_trap = TRUE;
+
+ /* test if on special level */
+ if ((dungeon_flags2 & DF2_ASK_LEAVE))
+ {
+ prt("Leave this unique level forever (y/n) ? ", 0, 0);
+ flush();
+ i = inkey();
+ prt("", 0, 0);
+ if (i != 'y') return;
+ }
+
+ /* Can we ? */
+ if (stair_hooks(STAIRS_DOWN))
+ {
+ return;
+ }
+
+ /* Normal up stairs */
+ if (c_ptr->feat == FEAT_SHAFT_DOWN)
+ {
+ if (!dun_level)
+ {
+ go_down = TRUE;
+
+ /* Save old player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ }
+ else
+ {
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_down_many = TRUE;
+ }
+ else
+ {
+ go_down_many = TRUE;
+ }
+ }
+ }
+
+ /* Normal stairs */
+ else if ((c_ptr->feat == FEAT_MORE) || (c_ptr->feat == FEAT_WAY_MORE))
+ {
+ if (p_ptr->prob_travel)
+ {
+ if (d_ptr->maxdepth == dun_level) return;
+ }
+ if (!dun_level)
+ {
+ go_down = TRUE;
+
+ /* Save old player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+ }
+ else
+ {
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_down = TRUE;
+ }
+ else
+ {
+ go_down = TRUE;
+ }
+ }
+ }
+
+ /* Handle quest areas -KMW- */
+ else if (c_ptr->feat == FEAT_QUEST_ENTER)
+ {
+ /* Enter quest level */
+ enter_quest();
+
+ return;
+ }
+
+ else if (!(dungeon_flags1 & DF1_FLAT) &&
+ p_ptr->prob_travel && !p_ptr->inside_quest)
+ {
+ if (d_ptr->maxdepth == dun_level) return;
+
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerfull force prevents your from teleporting.");
+ return;
+ }
+
+ prob_traveling = TRUE;
+
+ if (confirm_stairs)
+ {
+ if (get_check("Really leave the level? "))
+ go_down = TRUE;
+ }
+ else
+ {
+ go_down = TRUE;
+ }
+ }
+
+ else if (!(fall_trap))
+ {
+ msg_print("I see no down staircase here.");
+ return;
+ }
+
+ if (go_down || go_down_many)
+ {
+ energy_use = 0;
+
+ if (fall_trap)
+ msg_print("You deliberately jump through the trap door.");
+ else
+ {
+ if (c_ptr->feat == FEAT_WAY_MORE)
+ msg_print("You enter the next area.");
+ else
+ msg_print("You enter a maze of down staircases.");
+ }
+
+ autosave_checkpoint();
+
+ /* Go down */
+ if (go_down)
+ {
+ dun_level++;
+ }
+ else if (go_down_many)
+ {
+ int i = randint(3) + 1, j;
+
+ for (j = 1; j < i; j++)
+ {
+ dun_level++;
+ if (is_quest(dun_level + i - 1)) break;
+ if (d_ptr->maxdepth == dun_level) break;
+ }
+ }
+
+ /* We change place */
+ if (c_ptr->special && (!prob_traveling))
+ {
+ if (d_info[c_ptr->special].min_plev <= p_ptr->lev)
+ {
+ dungeon_info_type *d_ptr = &d_info[c_ptr->special];
+
+ /* Do the lua scripts refuse ? ;) */
+ {
+ struct hook_enter_dungeon_in in = { c_ptr->special };
+ if (process_hooks_new(HOOK_ENTER_DUNGEON, &in, NULL))
+ {
+ 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_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_FRAME);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Search */
+ search();
+}
+
+
+/*
+ * Hack -- toggle search mode
+ */
+void do_cmd_toggle_search(void)
+{
+ p_ptr->update |= (PU_BONUS);
+ p_ptr->redraw |= (PR_FRAME);
+ p_ptr->searching = !p_ptr->searching;
+}
+
+
+
+/*
+ * Determine if a grid contains a chest
+ */
+static s16b chest_check(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Scan all objects in the grid */
+ for (auto const this_o_idx: c_ptr->o_idxs)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_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_info[trap].name);
+ }
+}
+
+
+/*
+ * Attempt to open the given chest at the given location
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool_ do_cmd_open_chest(int y, int x, s16b o_idx)
+{
+ int i, j;
+
+ bool_ flag = TRUE;
+
+ bool_ more = FALSE;
+
+ object_type *o_ptr = &o_list[o_idx];
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open chests.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Attempt to unlock it */
+ if (o_ptr->pval > 0)
+ {
+ /* Assume locked, and thus not open */
+ flag = FALSE;
+
+ /* Get the "disarm" factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the difficulty */
+ j = i - o_ptr->pval;
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success -- May still have traps */
+ if (rand_int(100) < j)
+ {
+ msg_print("You have picked the lock.");
+ gain_exp(1);
+ flag = TRUE;
+ }
+
+ /* Failure -- Keep trying */
+ else
+ {
+ /* We may continue repeating */
+ more = TRUE;
+
+ if (flush_failure) flush();
+
+ msg_print("You failed to pick the lock.");
+ }
+ }
+
+ /* Allowed to open */
+ if (flag)
+ {
+ /* Apply chest traps, if any */
+ chest_trap(y, x, o_idx);
+
+ /* Let the Chest drop items */
+ chest_death(y, x, o_idx);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Original code by TNB, improvement for Angband 2.9.3 by rr9
+ * Slightly modified for ToME because of its trap implementation
+ */
+
+/*
+ * Return TRUE if the given grid is an open door
+ */
+static bool_ is_open(cave_type *c_ptr)
+{
+ return (c_ptr->feat == FEAT_OPEN);
+}
+
+
+/*
+ * Return TRUE if the given grid is a closed door
+ */
+static bool_ is_closed(cave_type *c_ptr)
+{
+ byte feat;
+
+ if (c_ptr->mimic) feat = c_ptr->mimic;
+ else feat = c_ptr->feat;
+
+ return ((feat >= FEAT_DOOR_HEAD) && (feat <= FEAT_DOOR_TAIL));
+}
+
+
+/*
+ * Return TRUE if the given grid has a trap
+ */
+static bool_ is_trap(cave_type *c_ptr)
+{
+ return ((c_ptr->info & (CAVE_TRDT)) != 0);
+}
+
+
+/*
+ * Return the number of doors/traps around (or under)
+ * the character using the filter function 'test'
+ */
+static int count_feats(int *y, int *x, bool_ (*test) (cave_type *c_ptr),
+ bool_ under)
+{
+ int d;
+
+ int xx, yy;
+
+ int count;
+
+
+ /* Clear match counter */
+ count = 0;
+
+ /* Check around (and under) the character */
+ for (d = 0; d < 9; d++)
+ {
+ /* Ignore current grid if told so -- See tables.c */
+ if ((d == 8) && !under) continue;
+
+ /* Extract adjacent (legal) location */
+ yy = p_ptr->py + ddy_ddd[d];
+ xx = p_ptr->px + ddx_ddd[d];
+
+ /* Paranoia */
+ if (!in_bounds(yy, xx)) continue;
+
+ /* Must have knowledge */
+ if (!(cave[yy][xx].info & (CAVE_MARK))) continue;
+
+ /* Not looking for this feature */
+ if (!(*test) (&cave[yy][xx])) continue;
+
+ /* Count it */
+ count++;
+
+ /* Remember the location. Only meaningful if there's
+ exactly one match */
+ *y = yy;
+ *x = xx;
+ }
+
+ /* All done */
+ return (count);
+}
+
+
+/*
+ * Return the number of chests around (or under) the character.
+ * If requested, count only trapped chests.
+ */
+static int count_chests(int *y, int *x, bool_ trapped)
+{
+ int d, count, o_idx;
+
+ object_type *o_ptr;
+
+
+ /* Count how many matches */
+ count = 0;
+
+ /* Check around (and under) the character */
+ for (d = 0; d < 9; d++)
+ {
+
+ /* Extract adjacent (legal) location */
+ int yy = p_ptr->py + ddy_ddd[d];
+ int xx = p_ptr->px + ddx_ddd[d];
+
+ /* No (visible) chest is there */
+ if ((o_idx = chest_check(yy, xx)) == 0) continue;
+
+ /* Grab the object */
+ o_ptr = &o_list[o_idx];
+
+ /* Already open */
+ if (o_ptr->pval == 0) continue;
+
+ /* No (known) traps here */
+ if (trapped && (!object_known_p(o_ptr) || !o_ptr->pval)) continue;
+
+ /* OK */
+ ++count;
+
+ /* Remember the location. Only useful if only one match */
+ *y = yy;
+ *x = xx;
+ }
+
+ /* All done */
+ return (count);
+}
+
+
+/*
+ * Convert an adjacent location to a direction.
+ */
+static int coords_to_dir(int y, int x)
+{
+ int d[3][3] =
+ {
+ {7, 4, 1},
+ {8, 5, 2},
+ {9, 6, 3} };
+
+ int dy, dx;
+
+
+ dy = y - p_ptr->py;
+ dx = x - p_ptr->px;
+
+ /* Paranoia */
+ if (ABS(dx) > 1 || ABS(dy) > 1) return (0);
+
+ return d[dx + 1][dy + 1];
+}
+
+
+/*
+ * Perform the basic "open" command on doors
+ *
+ * Assume destination is a closed/locked/jammed door
+ *
+ * Assume there is no monster blocking the destination
+ *
+ * Returns TRUE if repeated commands may continue
+ */
+static bool_ do_cmd_open_aux(int y, int x, int dir)
+{
+ int i, j;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open doors.");
+
+ return (FALSE);
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get requested grid */
+ c_ptr = &cave[y][x];
+
+ /* Jammed door */
+ if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x08)
+ {
+ /* Stuck */
+ msg_print("The door appears to be stuck.");
+ }
+
+ /* Locked door */
+ else if (c_ptr->feat >= FEAT_DOOR_HEAD + 0x01)
+ {
+ /* Disarm factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* Extract the lock power */
+ j = c_ptr->feat - FEAT_DOOR_HEAD;
+
+ /* Extract the difficulty XXX XXX XXX */
+ j = i - (j * 4);
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success */
+ if (rand_int(100) < j)
+ {
+ /* Message */
+ msg_print("You have picked the lock.");
+
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+
+ /* Experience */
+ gain_exp(1);
+ }
+
+ /* Failure */
+ else
+ {
+ /* Failure */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("You failed to pick the lock.");
+
+ /* We may keep trying */
+ more = TRUE;
+ }
+ }
+
+ /* Closed door */
+ else
+ {
+ /* Set off trap */
+ if (c_ptr->t_idx != 0) player_activate_door_trap(y, x);
+
+ /* Open the door */
+ cave_set_feat(y, x, FEAT_OPEN);
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Sound */
+ sound(SOUND_OPENDOOR);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+
+/*
+ * Open a closed/locked/jammed door or a closed/locked chest.
+ *
+ * Unlocking a locked door/chest is worth one experience point.
+ */
+void do_cmd_open(void)
+{
+ int y, x, dir;
+
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+
+ if ((p_ptr->body_monster != 0) && !(r_ptr->flags2 & RF2_OPEN_DOOR))
+ {
+ msg_print("You cannot open doors.");
+
+ return;
+ }
+
+ /* Pick a direction if there's an obvious target */
+ {
+ int num_doors, num_chests;
+
+ /* Count closed doors (locked or jammed) */
+ num_doors = count_feats(&y, &x, is_closed, FALSE);
+
+ /* Count chests (locked) */
+ num_chests = count_chests(&y, &x, FALSE);
+
+ /* There is nothing the player can open */
+ if ((num_doors + num_chests) == 0)
+ {
+ /* Message */
+ msg_print("You see nothing there to open.");
+
+ /* Done */
+ return;
+ }
+
+ /* Set direction if there is only one target */
+ else if ((num_doors + num_chests) == 1)
+ {
+ command_dir = coords_to_dir(y, x);
+ }
+ }
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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);
+ }
+ }
+
+ /* Cancel repeat unless we may continue */
+ if (!more) disturb(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;
+
+
+ /* Pick a direction if there's an obvious choice */
+ {
+ int num_doors;
+
+ /* Count open doors */
+ num_doors = count_feats(&y, &x, is_open, FALSE);
+
+ /* There are no doors the player can close */
+ if (num_doors == 0)
+ {
+ /* Message */
+ msg_print("You see nothing there to close.");
+
+ /* Done */
+ return;
+ }
+
+ /* Exactly one closeable door */
+ else if (num_doors == 1)
+ {
+ command_dir = coords_to_dir(y, x);
+ }
+ }
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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);
+}
+
+
+/*
+ * 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_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
+ */
+static 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_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_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_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_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_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_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_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_FRAME);
+
+ /* 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);
+}
+
+
+/*
+ * 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
+ */
+static bool_ do_cmd_disarm_aux(int y, int x, int dir, int do_pickup)
+{
+ int i, j, power;
+
+ cave_type *c_ptr;
+
+ cptr name;
+
+ bool_ more = FALSE;
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Access trap name */
+ if (t_info[c_ptr->t_idx].ident)
+ {
+ name = t_info[c_ptr->t_idx].name;
+ }
+ else
+ {
+ name = "unknown trap";
+ }
+
+ /* Get the "disarm" factor */
+ i = p_ptr->skill_dis;
+
+ /* Penalize some conditions */
+ if (p_ptr->blind || no_lite()) i = i / 10;
+ if (p_ptr->confused || p_ptr->image) i = i / 10;
+
+ /* XXX XXX XXX Variable power? */
+
+ /* Extract trap "power" */
+ power = t_info[c_ptr->t_idx].difficulty;
+
+ /* Extract the difficulty */
+ j = i - power;
+
+ /* Always have a small chance of success */
+ if (j < 2) j = 2;
+
+ /* Success */
+ if (rand_int(100) < j)
+ {
+ /* Message */
+ msg_format("You have disarmed the %s.", name);
+
+ /* Reward */
+ gain_exp(power);
+
+ /* Forget the trap */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT);
+
+ /* Remove the trap */
+ c_ptr->t_idx = 0;
+
+ /* Move the player onto the trap */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR))
+ move_player_aux(dir, do_pickup, 0, TRUE);
+
+ /* Remove trap attr from grid */
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+
+ /* Failure -- Keep trying */
+ else if ((i > 5) && (randint(i) > 5))
+ {
+ /* Failure */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_format("You failed to disarm the %s.", name);
+
+ /* We may keep trying */
+ more = TRUE;
+ }
+
+ /* Failure -- Set off the trap */
+ else
+ {
+ /* Message */
+ msg_format("You set off the %s!", name);
+
+ /* Move the player onto the trap */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_DOOR))
+ move_player_aux(dir, do_pickup, 0, FALSE);
+ }
+
+ /* Result */
+ return (more);
+}
+
+
+/*
+ * Disamrs the monster traps(no failure)
+ */
+void do_cmd_disarm_mon_trap(int y, int x)
+{
+ msg_print("You disarm the monster trap.");
+
+ place_floor_convert_glass(y, x);
+ cave[p_ptr->py][p_ptr->px].special = cave[p_ptr->py][p_ptr->px].special2 = 0;
+}
+
+
+/*
+ * Disarms a trap, or chest
+ */
+void do_cmd_disarm(void)
+{
+ int y, x, dir;
+
+ s16b o_idx;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+
+ /* Pick a direction if there's an obvious choice */
+ {
+ int num_traps, num_chests;
+
+ /* Count visible traps */
+ num_traps = count_feats(&y, &x, is_trap, TRUE);
+
+ /* Count chests (trapped) */
+ num_chests = count_chests(&y, &x, TRUE);
+
+ /* See if only one target */
+ if (num_traps || num_chests)
+ {
+ if (num_traps + num_chests <= 1)
+ command_dir = coords_to_dir(y, x);
+ }
+ }
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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);
+}
+
+
+/*
+ * 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(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_FRAME);
+
+ /* 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);
+}
+
+
+
+/*
+ * 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_FRAME);
+
+ /* Cancel the arg */
+ command_arg = 0;
+ }
+
+ /* Get a direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid */
+ c_ptr = &cave[y][x];
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Attack monsters */
+ if (c_ptr->m_idx)
+ {
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Open closed doors */
+ else if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Tunnel */
+ more = do_cmd_open_aux(y, x, dir);
+ }
+
+ /* Tunnel through walls */
+ else if (f_info[c_ptr->feat].flags1 & FF1_TUNNELABLE)
+ {
+ /* Tunnel */
+ more = do_cmd_tunnel_aux(y, x, dir);
+ }
+
+ /* Disarm traps */
+ else if (c_ptr->t_idx != 0)
+ {
+ /* Tunnel */
+ more = do_cmd_disarm_aux(y, x, dir, always_pickup);
+ }
+
+ /* Oops */
+ else
+ {
+ /* Oops */
+ msg_print("You attack the empty air.");
+ }
+ }
+
+ /* Cancel repetition unless we can continue */
+ if (!more) disturb(0);
+}
+
+
+/*
+ * Find the index of some "spikes", if possible.
+ *
+ * XXX XXX XXX Let user choose a pile of spikes, perhaps?
+ */
+static bool_ get_spike(int *ip)
+{
+ int i;
+
+
+ /* Check every item in the pack */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Check the "tval" code */
+ if (o_ptr->tval == TV_SPIKE)
+ {
+ /* Save the spike index */
+ (*ip) = i;
+
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Oops */
+ return (FALSE);
+}
+
+
+
+/*
+ * Jam a closed door with a spike
+ *
+ * This command may NOT be repeated
+ */
+void do_cmd_spike(void)
+{
+ int y, x, dir, item;
+
+ cave_type *c_ptr;
+
+
+ /* Get a "repeated" direction */
+ if (get_rep_dir(&dir))
+ {
+ /* Get location */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ /* Get grid and contents */
+ c_ptr = &cave[y][x];
+
+ /* Require closed door */
+ if (!((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)))
+ {
+ /* Message */
+ msg_print("You see nothing there to spike.");
+ }
+
+ /* Get a spike */
+ else if (!get_spike(&item))
+ {
+ /* Message */
+ msg_print("You have no spikes!");
+ }
+
+ /* Is a monster in the way? */
+ else if (c_ptr->m_idx)
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Message */
+ msg_print("There is a monster in the way!");
+
+ /* Attack */
+ py_attack(y, x, -1);
+ }
+
+ /* Go for it */
+ else
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Successful jamming */
+ msg_print("You jam the door with a spike.");
+
+ /* Convert "locked" to "stuck" XXX XXX XXX */
+ if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08) c_ptr->feat += 0x08;
+
+ /* Add one spike to the door */
+ if (c_ptr->feat < FEAT_DOOR_TAIL) c_ptr->feat++;
+
+ /* Use up, and describe, a single spike, from the bottom */
+ inc_stack_size(item, -1);
+ }
+ }
+}
+
+
+static void do_cmd_walk_jump(int pickup, bool_ disarm)
+{
+ int dir;
+
+ bool_ more = FALSE;
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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);
+}
+
+
+/*
+ * Try to ``walk'' using phase door.
+ */
+static void do_cmd_unwalk()
+{
+ int dir, y, x, feat;
+
+ cave_type *c_ptr;
+
+ bool_ more = FALSE;
+
+
+ if (!get_rep_dir(&dir)) return;
+
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+
+ c_ptr = &cave[y][x];
+ feat = c_ptr->feat;
+
+ /* Must have knowledge to know feature XXX XXX */
+ if (!(c_ptr->info & (CAVE_MARK))) feat = FEAT_NONE;
+
+ /* Take a turn */
+ energy_use = 100;
+ energy_use *= (p_ptr->wild_mode) ? (5 * (MAX_HGT + MAX_WID) / 2) : 1;
+
+
+ /* Allow repeated command */
+ if (command_arg)
+ {
+ /* Set repeat count */
+ command_rep = command_arg - 1;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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);
+}
+
+
+/*
+ * 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_FRAME);
+
+ /* 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);
+
+ /* 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_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_FRAME);
+
+ /* 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];
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+
+ /* Get the "bow" (if any) */
+ j_ptr = &p_ptr->inventory[INVEN_BOW];
+
+ /* Require a launcher */
+ if (!j_ptr->tval)
+ {
+ msg_print("You have nothing with which to fire.");
+ return;
+ }
+
+ /* XXX HACK */
+ if (j_ptr->tval == TV_INSTRUMENT)
+ {
+ msg_print("You cannot fire with an instrument.");
+ return;
+ }
+
+ /* Get the "ammo" (if any) */
+ o_ptr = &p_ptr->inventory[INVEN_AMMO];
+
+ item = INVEN_AMMO;
+
+ /* If nothing correct try to choose from the backpack */
+ if ((p_ptr->tval_ammo != o_ptr->tval) || (!o_ptr->k_idx))
+ {
+ /* Get an item */
+ if (!get_item(&item,
+ "Your quiver is empty. Fire which item? ",
+ "You have nothing to fire.",
+ (USE_INVEN | USE_FLOOR),
+ object_filter::TVal(p_ptr->tval_ammo)))
+ {
+ return;
+ }
+
+ /* Access the item */
+ o_ptr = get_object(item);
+ }
+
+
+ /* Get a direction (or cancel) */
+ if (!get_aim_dir(&dir)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Single object */
+ q_ptr->number = 1;
+
+ /* Reduce stack and describe */
+ inc_stack_size(item, -1);
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+
+ /* Sound */
+ sound(SOUND_SHOOT);
+
+
+ /* Describe the object */
+ object_desc(o_name, q_ptr, FALSE, 3);
+
+ /* Find the color and symbol for the object for throwing */
+ missile_attr = object_attr(q_ptr);
+ missile_char = object_char(q_ptr);
+
+
+ /* Use the proper number of shots */
+ thits = p_ptr->num_fire;
+
+ /* Use a base distance */
+ tdis = 10;
+
+ /* Base damage from thrown object plus launcher bonus */
+ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + j_ptr->to_d;
+
+ /* Actually "fire" the object */
+ bonus = (p_ptr->to_h + p_ptr->to_h_ranged + q_ptr->to_h + j_ptr->to_h);
+
+ chance = (p_ptr->skill_thb + (bonus * BTH_PLUS_ADJ));
+ if (chance < 5) chance = 5;
+
+ tmul = get_shooter_mult(j_ptr);
+
+ /* Get extra "power" from "extra might" */
+ tmul += p_ptr->xtra_might;
+
+ /* Boost the damage */
+ tdam *= tmul;
+
+ /* Add in the player damage */
+ tdam += p_ptr->to_d_ranged;
+
+ /* Base range */
+ tdis = 10 + 5 * tmul;
+
+
+ /* Take a (partial) turn */
+ energy_use = (100 / thits);
+
+ /* piercing shots ? */
+ if (p_ptr->use_piercing_shots)
+ {
+ num_pierce = (get_skill(SKILL_COMBAT) / 10) - 1;
+ num_pierce = (num_pierce < 0) ? 0 : num_pierce;
+ }
+
+ /* Start at the player */
+ by = p_ptr->py;
+ bx = p_ptr->px;
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Predict the "target" location */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Check for "target request" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ oldtdam = tdam;
+ while (TRUE)
+ {
+ /* Reset after a piercing shot */
+ tdam = oldtdam;
+
+ /* Travel until stopped */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == ty) && (x == tx)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, by, bx, ty, tx);
+
+ /* Stopped by walls/doors */
+ if (!cave_floor_bold(ny, nx)) break;
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ sleep_for(milliseconds(msec));
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ sleep_for(milliseconds(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];
+ auto const r_ptr = m_ptr->race();
+
+ /* 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;
+
+ 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;
+
+ 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;
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Throw which item? ",
+ "You have nothing to throw.",
+ (USE_INVEN | USE_FLOOR)))
+ {
+ return;
+ }
+
+ /* Access the item */
+ object_type *o_ptr = get_object(item);
+
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack - Cannot throw away 'no drop' cursed items */
+ if (cursed_p(o_ptr) && (f4 & TR4_CURSE_NO_DROP))
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to throw it.");
+
+ /* Nope */
+ return;
+ }
+
+ /* Boulder throwing */
+ if ((o_ptr->tval == TV_JUNK) && (o_ptr->sval == SV_BOULDER) && (get_skill(SKILL_BOULDER)))
+ {
+ boulder_add = get_skill_scale(SKILL_BOULDER, 80);
+ boulder_mult = get_skill_scale(SKILL_BOULDER, 6);
+ }
+
+ /* Get a direction (or cancel) */
+ if (!get_aim_dir(&dir)) return;
+
+ /* Break goi/manashield */
+ if (p_ptr->invuln)
+ {
+ set_invuln(0);
+ }
+ if (p_ptr->disrupt_shield)
+ {
+ set_disrupt_shield(0);
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /*
+ * Hack -- If rods or wands are thrown, the total maximum timeout or
+ * charges need to be allocated between the two stacks.
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval / o_ptr->number;
+
+ if (o_ptr->number > 1) o_ptr->pval -= q_ptr->pval;
+ }
+
+ /* Single object */
+ q_ptr->number = 1;
+
+ /* Reduce stack and describe */
+ inc_stack_size(item, -1);
+
+ /* Description */
+ object_desc(o_name, q_ptr, FALSE, 3);
+
+ /* Find the color and symbol for the object for throwing */
+ missile_attr = object_attr(q_ptr);
+ missile_char = object_char(q_ptr);
+
+ /* Extract a "distance multiplier" */
+ /* Changed for 'launcher' corruption */
+ mul = 10 + (2 * (p_ptr->throw_mult - 1)) + (2 * boulder_mult);
+
+ /* Enforce a minimum "weight" of one pound */
+ div = ((q_ptr->weight > 10) ? q_ptr->weight : 10);
+
+ /* Hack -- Distance -- Reward strength, penalize weight */
+ tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div;
+
+ /* Max distance of 10-18 */
+ if (tdis > mul) tdis = mul;
+
+ /* Hack -- Base damage from thrown object */
+ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d + boulder_add;
+ tdam *= p_ptr->throw_mult + boulder_mult;
+
+ /* Chance of hitting - adjusted for Weaponmasters -- Gumby */
+ chance = (p_ptr->skill_tht + (p_ptr->to_h * BTH_PLUS_ADJ));
+
+ /* Take a turn */
+ energy_use = 100;
+
+
+ /* Start at the player */
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Predict the "target" location */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Check for "target request" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+
+ /* Travel until stopped */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == ty) && (x == tx)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx);
+
+ /* Stopped by walls/doors */
+ if (!cave_floor_bold(ny, nx))
+ {
+ hit_wall = TRUE;
+ break;
+ }
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ sleep_for(milliseconds(msec));
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ sleep_for(milliseconds(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];
+ auto r_ptr = m_ptr->race();
+
+ /* Check the visibility */
+ visible = m_ptr->ml;
+
+ /* Note the collision */
+ hit_body = TRUE;
+
+ /* Did we hit it (penalize range) */
+ if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml))
+ {
+ bool_ fear = FALSE;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+
+ /* Handle unseen monster */
+ if (!visible)
+ {
+ /* Invisible monster */
+ msg_format("The %s finds a mark.", o_name);
+ }
+
+ /* Handle visible monster */
+ else
+ {
+ char m_name[80];
+
+ /* Get "the monster" or "it" */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("The %s hits %s.", o_name, m_name);
+
+ /* Hack -- Track this monster race */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack -- Track this monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+ }
+
+ /* Apply special damage XXX XXX XXX */
+ tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special);
+ tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, o_ptr->sval == SV_BOULDER ? SKILL_BOULDER : SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (tdam < 0) tdam = 0;
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.",
+ tdam, m_ptr->hp);
+ }
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(c_ptr->m_idx, tdam);
+
+ if (special) attack_special(m_ptr, special, tdam);
+
+ /* Anger friends */
+ if (!(k_info[q_ptr->k_idx].tval == TV_POTION))
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+ }
+
+ /* Stop looking */
+ break;
+ }
+ }
+
+ /* Chance of breakage (during attacks) */
+ j = (hit_body ? breakage_chance(q_ptr) : 0);
+
+ /* Potions smash open */
+ if (k_info[q_ptr->k_idx].tval == TV_POTION)
+ {
+ if ((hit_body) || (hit_wall) || (randint(100) < j))
+ {
+ /* Message */
+ msg_format("The %s shatters!", o_name);
+
+ if (potion_smash_effect(0, y, x, q_ptr->sval))
+ {
+ if (cave[y][x].m_idx)
+ {
+ char m_name[80];
+ monster_desc(m_name, &m_list[cave[y][x].m_idx], 0);
+ switch (is_friend(&m_list[cave[y][x].m_idx]))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(&m_list[cave[y][x].m_idx]);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_list[cave[y][x].m_idx].status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+ }
+
+ return;
+ }
+ else
+ {
+ j = 0;
+ }
+ }
+
+ /* Drop (or break) near that location */
+ drop_near(q_ptr, j, y, x);
+}
+
+
+/*
+ * Throw a boomerang object from the equipement(bow).
+ *
+ * Note: "unseen" monsters are very hard to hit.
+ *
+ * Should throwing a weapon do full damage? Should it allow the magic
+ * to hit bonus of the weapon to have an effect? Should it ever cause
+ * the item to be destroyed? Should it do any damage at all?
+ */
+void do_cmd_boomerang(void)
+{
+ int dir;
+
+ int j, y, x, ny, nx, ty, tx;
+
+ int chance, tdam, tdis;
+
+ int mul, div;
+
+ int cur_dis, visible;
+
+ object_type forge;
+
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ bool_ hit_body = FALSE;
+
+ byte missile_attr;
+
+ char missile_char;
+
+ char o_name[80];
+
+ s32b special = 0;
+
+ int msec = delay_factor * delay_factor * delay_factor;
+
+
+ /* Get the "bow" (if any) */
+ o_ptr = &p_ptr->inventory[INVEN_BOW];
+
+
+ /* Get a direction (or cancel) */
+ if (!get_aim_dir(&dir)) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Single object */
+ q_ptr->number = 1;
+
+ /* Description */
+ object_desc(o_name, q_ptr, FALSE, 3);
+
+ /* Find the color and symbol for the object for throwing */
+ missile_attr = object_attr(q_ptr);
+ missile_char = object_char(q_ptr);
+
+ /* Extract a "distance multiplier" */
+ /* Changed for 'launcher' corruption */
+ mul = 10 + 2 * (p_ptr->throw_mult - 1);
+
+ /* Enforce a minimum "weight" of one pound */
+ div = ((q_ptr->weight > 10) ? q_ptr->weight : 10);
+
+ /* Hack -- Distance -- Reward strength, penalize weight */
+ tdis = (adj_str_blow[p_ptr->stat_ind[A_STR]] + 20) * mul / div;
+
+ /* Max distance of 10-18 */
+ if (tdis > mul) tdis = mul;
+
+ /* Hack -- Base damage from thrown object */
+ tdam = damroll(q_ptr->dd, q_ptr->ds) + q_ptr->to_d;
+ tdam *= p_ptr->throw_mult;
+
+ /* Chance of hitting */
+ chance =
+ (p_ptr->skill_tht +
+ ((p_ptr->to_h + p_ptr->to_h_ranged) * BTH_PLUS_ADJ));
+
+ chance += get_skill(SKILL_BOOMERANG);
+
+ /* Take a turn */
+ energy_use = 100;
+
+
+ /* Start at the player */
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Predict the "target" location */
+ tx = p_ptr->px + 99 * ddx[dir];
+ ty = p_ptr->py + 99 * ddy[dir];
+
+ /* Check for "target request" */
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+
+ /* Travel until stopped */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == ty) && (x == tx)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, p_ptr->py, p_ptr->px, ty, tx);
+
+ /* Stopped by walls/doors */
+ if (!cave_floor_bold(ny, nx))
+ {
+ break;
+ }
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ sleep_for(milliseconds(msec));
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ sleep_for(milliseconds(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];
+ auto const r_ptr = m_ptr->race();
+
+ /* Check the visibility */
+ visible = m_ptr->ml;
+
+ /* Note the collision */
+ hit_body = TRUE;
+
+ /* Did we hit it (penalize range) */
+ if (test_hit_fire(chance - cur_dis, m_ptr->ac, m_ptr->ml))
+ {
+ bool_ fear = FALSE;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+
+ /* Handle unseen monster */
+ if (!visible)
+ {
+ /* Invisible monster */
+ msg_format("The %s finds a mark.", o_name);
+ }
+
+ /* Handle visible monster */
+ else
+ {
+ char m_name[80];
+
+ /* Get "the monster" or "it" */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("The %s hits %s.", o_name, m_name);
+
+ /* Hack -- Track this monster race */
+ if (m_ptr->ml) monster_race_track(m_ptr->r_idx, m_ptr->ego);
+
+ /* Hack -- Track this monster */
+ if (m_ptr->ml) health_track(c_ptr->m_idx);
+ }
+
+ /* Apply special damage XXX XXX XXX */
+ tdam = tot_dam_aux(q_ptr, tdam, m_ptr, &special);
+ tdam = critical_shot(q_ptr->weight, q_ptr->to_h, tdam, SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (tdam < 0) tdam = 0;
+
+ /* Complex message */
+ if (wizard)
+ {
+ msg_format("You do %d (out of %d) damage.",
+ tdam, m_ptr->hp);
+ }
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(c_ptr->m_idx, tdam, &fear, note_dies))
+ {
+ /* Dead monster */
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(c_ptr->m_idx, tdam);
+
+ if (special) attack_special(m_ptr, special, tdam);
+
+ /* Anger friends */
+ if (!(k_info[q_ptr->k_idx].tval == TV_POTION))
+ {
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ switch (is_friend(m_ptr))
+ {
+ case 1:
+ msg_format("%^s gets angry!", m_name);
+ change_side(m_ptr);
+ break;
+ case 0:
+ msg_format("%^s gets angry!", m_name);
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ }
+ }
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ char m_name[80];
+
+ /* Sound */
+ sound(SOUND_FLEE);
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+
+ /* Chance of breakage (during attacks) */
+ j = (hit_body ? breakage_chance(o_ptr) : 0);
+
+ /* Break the boomerang */
+ if (!(o_ptr->art_name || artifact_p(o_ptr)) &&
+ (rand_int(100) < j))
+ {
+ msg_print(format("Your %s is destroyed.", o_name));
+ inc_stack_size_ex(INVEN_BOW, -1, OPTIMIZE, NO_DESCRIBE);
+ }
+ }
+
+ /* Stop looking */
+ break;
+ }
+ }
+
+ /* Travel back to the player */
+ for (cur_dis = 0; cur_dis <= tdis; )
+ {
+ /* Hack -- Stop at the target */
+ if ((y == p_ptr->py) && (x == p_ptr->px)) break;
+
+ /* Calculate the new location (see "project()") */
+ ny = y;
+ nx = x;
+ mmove2(&ny, &nx, ty, tx, p_ptr->py, p_ptr->px);
+
+ /* Advance the distance */
+ cur_dis++;
+
+ /* Save the new location */
+ x = nx;
+ y = ny;
+
+
+ /* The player can see the (on screen) missile */
+ if (panel_contains(y, x) && player_can_see_bold(y, x))
+ {
+ /* Draw, Hilite, Fresh, Pause, Erase */
+ print_rel(missile_char, missile_attr, y, x);
+ move_cursor_relative(y, x);
+ Term_fresh();
+ sleep_for(milliseconds(msec));
+ lite_spot(y, x);
+ Term_fresh();
+ }
+
+ /* The player cannot see the missile */
+ else
+ {
+ /* Pause anyway, for consistancy */
+ sleep_for(milliseconds(msec));
+ }
+ }
+}
+
+
+static bool_ tport_vertically(bool_ how)
+{
+ /* quest? */
+ if (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_FRAME);
+ }
+
+ if (lose_hp)
+ {
+ p_ptr->chp -= lose_hp;
+ p_ptr->redraw |= (PR_FRAME);
+ }
+
+ energy_use = 100;
+ }
+}
+
+/* Can we sacrifice it ? */
+static bool item_tester_hook_sacrificable(object_type const *o_ptr)
+{
+ if (p_ptr->pgod == 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) && udun_in_book(o_ptr->sval, o_ptr->pval) <= 0)
+ {
+ return TRUE;
+ }
+ }
+
+ /* Assume not */
+ return (FALSE);
+}
+
+/*
+ * Is item eligible for sacrifice to Aule?
+ */
+static bool item_tester_hook_sacrifice_aule(object_type const *o_ptr)
+{
+ /* perhaps restrict this only to metal armour and weapons */
+ return (o_ptr->found == OBJ_FOUND_SELFMADE);
+}
+
+/*
+ * Handle sacrifices to Aule
+ */
+static void do_cmd_sacrifice_aule()
+{
+ int item;
+
+ if (!get_item(&item,
+ "Sacrifice which item? ",
+ "You have nothing to sacrifice.",
+ USE_INVEN,
+ item_tester_hook_sacrifice_aule))
+ {
+ return;
+ }
+
+ /* Increase piety by the value of the item / 10. */
+ {
+ object_type *o_ptr = get_object(item);
+ s32b delta = object_value(o_ptr) / 10;
+
+ inc_piety(GOD_ALL, delta);
+ }
+
+ /* Destroy the object */
+ inc_stack_size(item, -1);
+}
+
+/*
+ * 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();
+ return;
+ }
+ else
+ {
+ int agod = on_what - FEAT_ALTAR_HEAD + 1;
+
+ /* Not worshipping a god ? ahhhh! */
+ if (p_ptr->pgod == 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)
+ {
+ if (p_ptr->pgod == 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
+ {
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Sacrifice which item? ",
+ "You have nothing to sacrifice.",
+ (USE_INVEN),
+ item_tester_hook_sacrificable))
+ {
+ return;
+ }
+
+ object_type *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 = levels_in_book(o_ptr->sval, o_ptr->pval);
+
+ inc_piety(GOD_MELKOR, 2 * x);
+ }
+
+ /* Remove the item */
+ inc_stack_size(item, -1);
+ }
+ }
+
+ if (p_ptr->pgod == GOD_AULE)
+ {
+ do_cmd_sacrifice_aule();
+ }
+
+ }
+ }
+}
+
+
+/*
+ * scan_monst --
+ *
+ * Return a list of o_list[] indexes of items of the given monster
+ */
+std::vector<s16b> scan_monst(int m_idx)
+{
+ constexpr std::size_t max_size = 23;
+
+ /* Create output vector. */
+ std::vector<s16b> objects;
+ objects.reserve(std::min(max_size, m_list[m_idx].hold_o_idxs.size()));
+
+ /* Scan all objects in the grid */
+ for (auto const this_o_idx: m_list[m_idx].hold_o_idxs)
+ {
+ objects.push_back(this_o_idx);
+ if (objects.size() == max_size) break;
+ }
+
+ /* Result */
+ return objects;
+}
+
+
+/*
+ * Display a list of the items that the given monster carries.
+ * Returns the list of objects.
+ */
+std::vector<s16b> show_monster_inven(int m_idx)
+{
+ byte out_color[23];
+ char out_desc[23][80];
+
+ /* Default length */
+ int len = 79 - 50;
+
+ /* Maximum space allowed for descriptions */
+ int lim = 79 - 3;
+
+ /* Require space for weight */
+ lim -= 9;
+
+ /* Scan for objects on the monster */
+ std::vector<s16b> objects = scan_monst(m_idx);
+ assert(objects.size() <= 23);
+
+ /* Calculate width of object names */
+ for (std::size_t i = 0; i < objects.size(); i++)
+ {
+ object_type *o_ptr = &o_list[objects.at(i)];
+
+ /* Describe the object */
+ char o_name[80];
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Hack -- enforce max length */
+ o_name[lim] = '\0';
+
+ /* Acquire p_ptr->inventory color */
+ out_color[i] = tval_to_attr[o_ptr->tval & 0x7F];
+
+ /* Save the object description */
+ strcpy(out_desc[i], o_name);
+
+ /* Find the predicted "line length" */
+ int l = strlen(out_desc[i]) + 5;
+
+ /* Account for the weight */
+ l += 9;
+
+ /* Maintain the maximum length */
+ if (l > len) len = l;
+ }
+
+ /* Find the column to start in */
+ int col = (len > 76) ? 0 : (79 - len);
+
+ /* Output each entry */
+ std::size_t i = 0;
+ for (i = 0; i < objects.size(); i++)
+ {
+ /* Get the item */
+ object_type *o_ptr = &o_list[objects.at(i)];
+
+ /* Clear the line */
+ prt("", i + 1, col ? col - 2 : col);
+
+ /* Prepare an index --(-- */
+ char tmp_val[80];
+ strnfmt(tmp_val, 80, "%c)", index_to_label(i));
+
+ /* Clear the line with the (possibly indented) index */
+ put_str(tmp_val, i + 1, col);
+
+ /* Display the entry itself */
+ c_put_str(out_color[i], out_desc[i], i + 1, col + 3);
+
+ /* Display the weight if needed */
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ strnfmt(tmp_val, 80, "%3d.%1d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, i + 1, 71);
+ }
+ }
+
+ /* Make a "shadow" below the list (only if needed) */
+ if (i && (i < 23))
+ {
+ prt("", i + 1, col ? col - 2 : col);
+ }
+
+ return objects;
+}
+
+
+/*
+ * Steal an object from a monster
+ */
+void do_cmd_steal()
+{
+ int dir = 0, item = -1, k = -1;
+
+ bool_ done = FALSE;
+
+ /* Only works on adjacent monsters */
+ if (!get_rep_dir(&dir)) return;
+ int y = p_ptr->py + ddy[dir];
+ int x = p_ptr->px + ddx[dir];
+
+ cave_type const *c_ptr = &cave[y][x];
+
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("There is no monster there!");
+ return;
+ }
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ /* There were no non-gold items */
+ if (m_ptr->hold_o_idxs.empty())
+ {
+ 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();
+
+ std::vector<s16b> objects = show_monster_inven(c_ptr->m_idx);
+
+ /* 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 + objects.size());
+
+ /* 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) || (static_cast<std::size_t>(k) >= objects.size()))
+ {
+ bell();
+
+ break;
+ }
+
+ /* Verify the item */
+ if (ver && !verify("Try", -objects[k]))
+ {
+ done = TRUE;
+
+ break;
+ }
+
+ /* Accept that choice */
+ item = objects[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;
+ }
+
+ /* Remove from the monster's list of objects */
+ m_ptr->hold_o_idxs.erase(m_ptr->hold_o_idxs.begin() + k);
+
+ /* Rogues gain some xp */
+ if (race_flags1_p(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);
+ }
+
+ /* Create the object we're going to copy into */
+ object_type forge;
+ object_type *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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+ else
+ {
+ object_copy(o_ptr, &o_list[item]);
+
+ inven_carry(o_ptr, FALSE);
+ }
+
+ /* Delete source item */
+ o_list[item].k_idx = 0;
+ }
+
+ screen_load();
+
+ /* Take a turn */
+ energy_use = 100;
+}
+
+
+/*
+ * Give an item to a monster
+ */
+void do_cmd_give()
+{
+ /* Get a "repeated" direction */
+ int dir;
+ if (!get_rep_dir(&dir)) return;
+
+ /* Get requested location */
+ int y = p_ptr->py + ddy[dir];
+ int x = p_ptr->px + ddx[dir];
+
+ /* Get requested grid */
+ cave_type *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 */
+ int item;
+ if (!get_item(&item,
+ "What item do you want to offer? ",
+ "You have nothing to offer.",
+ USE_INVEN))
+ {
+ return;
+ }
+
+ /* Process hooks if there are any */
+ hook_give_in in = { c_ptr->m_idx, item };
+ if (!process_hooks_new(HOOK_GIVE, &in, NULL))
+ {
+ 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 */
+ struct hook_chat_in in = { c_ptr->m_idx };
+ if (!process_hooks_new(HOOK_CHAT, &in, NULL))
+ {
+ msg_print("The monster does not want to chat.");
+ }
+
+ /* No energy spent */
+}
diff --git a/src/cmd2.hpp b/src/cmd2.hpp
new file mode 100644
index 00000000..142238ab
--- /dev/null
+++ b/src/cmd2.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type_fwd.hpp"
+#include <vector>
+
+extern std::vector<s16b> show_monster_inven(int m_idx);
+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 void do_cmd_tunnel(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_immovable_special(void);
+extern void fetch(int dir, int wgt, bool_ require_los);
+extern void do_cmd_sacrifice(void);
+extern void do_cmd_steal(void);
diff --git a/src/cmd3.cc b/src/cmd3.cc
new file mode 100644
index 00000000..59e61719
--- /dev/null
+++ b/src/cmd3.cc
@@ -0,0 +1,2110 @@
+/*
+ * 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 "cmd3.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cli_comm.hpp"
+#include "files.hpp"
+#include "gods.hpp"
+#include "hook_drop_in.hpp"
+#include "hook_wield_in.hpp"
+#include "hooks.hpp"
+#include "monster1.hpp"
+#include "monster_race.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "squeltch.hpp"
+#include "store.hpp"
+#include "store_type.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+#include <algorithm>
+#include <memory>
+#include <utility>
+
+/*
+ * Display p_ptr->inventory
+ */
+void do_cmd_inven(void)
+{
+ char out_val[160];
+
+
+ /* Note that we are in "p_ptr->inventory" mode */
+ command_wrk = FALSE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Show the inventory */
+ show_inven_full();
+
+ /* Show prompt */
+ {
+ s32b total_weight = calc_total_weight();
+
+ strnfmt(out_val, 160,
+ "Inventory: carrying %ld.%ld pounds (%ld%% of capacity). Command: ",
+ total_weight / 10, total_weight % 10,
+ (total_weight * 100) / ((weight_limit()) / 2));
+ }
+
+ /* Get a command */
+ prt(out_val, 0, 0);
+
+ /* Get a new command */
+ command_new = inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+
+ /* Process "Escape" */
+ if (command_new == ESCAPE)
+ {
+ /* Reset stuff */
+ command_new = 0;
+ }
+
+ /* Process normal keys */
+ else
+ {
+ /* Mega-Hack -- Don't disable keymaps for this key */
+ request_command_inven_mode = TRUE;
+ }
+}
+
+
+/*
+ * Display equipment
+ */
+void do_cmd_equip(void)
+{
+ char out_val[160];
+
+
+ /* Note that we are in "equipment" mode */
+ command_wrk = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Display the equipment */
+ show_equip_full();
+
+ /* Show prompt */
+ {
+ s32b total_weight = calc_total_weight();
+
+ /* Build a prompt */
+ strnfmt(out_val, 160,
+ "Equipment: carrying %ld.%ld pounds (%ld%% of capacity). Command: ",
+ total_weight / 10, total_weight % 10,
+ (total_weight * 100) / ((weight_limit()) / 2));
+ }
+
+ /* Get a command */
+ prt(out_val, 0, 0);
+
+ /* Get a new command */
+ command_new = inkey();
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+
+ /* Process "Escape" */
+ if (command_new == ESCAPE)
+ {
+ /* Reset stuff */
+ command_new = 0;
+ }
+
+ /* Process normal keys */
+ else
+ {
+ /* Mega-Hack -- Don't disable keymaps for this key */
+ request_command_inven_mode = TRUE;
+ }
+}
+
+
+/*
+ * The "wearable" tester
+ */
+static bool item_tester_hook_wear(object_type const *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 *i_ptr;
+
+ cptr act;
+
+ char o_name[80];
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Wear/Wield which item? ",
+ "You have nothing you can wear or wield.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_wear))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Check the slot */
+ slot = wield_slot(o_ptr);
+
+ /* Prevent wielding into a cursed slot */
+ if (cursed_p(&p_ptr->inventory[slot]))
+ {
+ /* Describe it */
+ object_desc(o_name, &p_ptr->inventory[slot], FALSE, 0);
+
+ /* Message */
+ msg_format("The %s you are %s appears to be cursed.",
+ o_name, describe_use(slot));
+
+ /* Cancel the command */
+ return;
+ }
+
+ if ((cursed_p(o_ptr)) && (wear_confirm)
+ && (object_known_p(o_ptr) || (o_ptr->ident & (IDENT_SENSE))))
+ {
+ char dummy[512];
+
+ /* Describe it */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ strnfmt(dummy, 512, "Really use the %s {cursed}? ", o_name);
+ if (!(get_check(dummy)))
+ return;
+ }
+
+ /* Can we wield */
+ {
+ struct hook_wield_in in = { o_ptr };
+ if (process_hooks_new(HOOK_WIELD, &in, NULL))
+ {
+ 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;
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain local object */
+ object_copy(q_ptr, o_ptr);
+
+ if (slot == INVEN_AMMO) num = o_ptr->number;
+
+ /* Modify quantity */
+ q_ptr->number = num;
+
+ /* Decrease the item */
+ inc_stack_size_ex(item, -num, OPTIMIZE, NO_DESCRIBE);
+
+ /* Access the wield slot */
+ o_ptr = &p_ptr->inventory[slot];
+
+ /* Take off existing item */
+ if (slot != INVEN_AMMO)
+ {
+ if (o_ptr->k_idx)
+ {
+ /* Take off existing item */
+ (void)inven_takeoff(slot, 255, FALSE);
+ }
+ }
+ else
+ {
+ if (o_ptr->k_idx)
+ {
+ if (!object_similar(o_ptr, q_ptr))
+ {
+ /* Take off existing item */
+ (void)inven_takeoff(slot, 255, FALSE);
+ }
+ else
+ {
+ q_ptr->number += o_ptr->number;
+ }
+ }
+ }
+
+
+ /* Wear the new stuff */
+ object_copy(o_ptr, q_ptr);
+
+ /* Increment the equip counter by hand */
+ equip_cnt++;
+
+ /* Where is the item now */
+ if (slot == INVEN_WIELD)
+ {
+ act = "You are wielding";
+ }
+ else if (( slot == INVEN_BOW ) && (o_ptr->tval == TV_INSTRUMENT))
+ {
+ act = "You are holding";
+ }
+ else if (slot == INVEN_BOW)
+ {
+ act = "You are shooting with";
+ }
+ else if (slot == INVEN_LITE)
+ {
+ act = "Your light source is";
+ }
+ else if (slot == INVEN_AMMO)
+ {
+ act = "In your quiver you have";
+ }
+ else if (slot == INVEN_TOOL)
+ {
+ act = "You are using";
+ }
+ else
+ {
+ act = "You are wearing";
+ }
+
+ /* Describe the result */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("%s %s (%c).", act, o_name, index_to_label(slot));
+
+ /* Cursed! */
+ if (cursed_p(o_ptr))
+ {
+ /* Warn the player */
+ msg_print("Oops! It feels deathly cold!");
+
+ /* Note the curse */
+ o_ptr->ident |= (IDENT_SENSE);
+ o_ptr->sense = SENSE_CURSED;
+ }
+
+ /* Take care of item sets */
+ if (o_ptr->name1)
+ {
+ wield_set(o_ptr->name1, a_info[o_ptr->name1].set, FALSE);
+ }
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Recalculate hitpoint */
+ p_ptr->update |= (PU_HP);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA | PU_SPELLS);
+
+ /* Redraw monster hitpoint */
+ p_ptr->redraw |= (PR_FRAME);
+
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+}
+
+
+
+/*
+ * Take off an item
+ */
+void do_cmd_takeoff(void)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Take off which item? ",
+ "You are not wearing anything to take off.",
+ (USE_EQUIP)))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* 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_FRAME);
+}
+
+
+/*
+ * Drop an item
+ */
+void do_cmd_drop(void)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Drop which item? ",
+ "You have nothing to drop.",
+ (USE_EQUIP | USE_INVEN)))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Can we drop */
+ struct hook_drop_in in = { item };
+ if (process_hooks_new(HOOK_DROP, &in, NULL)) 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 */
+ int amt = 1;
+ 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 old_number;
+
+ bool_ force = FALSE;
+
+ char o_name[80];
+
+ char out_val[160];
+
+ /* Hack -- force destruction */
+ if (command_arg > 0) force = TRUE;
+
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Destroy which item? ",
+ "You have nothing to destroy.",
+ (USE_INVEN | USE_FLOOR | USE_AUTO)))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* See how many items */
+ int amt = 1;
+ 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)
+ {
+ /* 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;
+
+ u32b f1, f2, f3, f4, f5, esp;
+ 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);
+ }
+
+ /*
+ * Hack -- If rods or wand are destroyed, the total maximum timeout or
+ * charges of the stack needs to be reduced, unless all the items are
+ * being destroyed. -LM-
+ */
+ if ((o_ptr->tval == TV_WAND) && (amt < o_ptr->number))
+ {
+ o_ptr->pval -= o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Eru wont be happy */
+ if (f3 & TR3_BLESSED)
+ inc_piety(GOD_ERU, -10 * k_info[o_ptr->k_idx].level);
+
+ /* Eliminate the item */
+ inc_stack_size(item, -amt);
+}
+
+
+/*
+ * Observe an item which has been *identify*-ed
+ */
+void do_cmd_observe(void)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Examine which item? ",
+ "You have nothing to examine.",
+ (USE_EQUIP | USE_INVEN | USE_FLOOR)))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Description */
+ char o_name[80];
+ 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)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Un-inscribe which item? ",
+ "You have nothing to un-inscribe.",
+ (USE_EQUIP | USE_INVEN | USE_FLOOR)))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Nothing to remove */
+ if (!o_ptr->note)
+ {
+ msg_print("That item had no inscription to remove.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Inscription removed.");
+
+ /* Remove the incription */
+ o_ptr->note = 0;
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+}
+
+
+/*
+ * Inscribe an object with a comment
+ */
+void do_cmd_inscribe(void)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Inscribe which item? ",
+ "You have nothing to inscribe.",
+ (USE_EQUIP | USE_INVEN | USE_FLOOR)))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Describe the activity */
+ char o_name[80];
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("Inscribing %s.", o_name);
+ msg_print(NULL);
+
+ /* Start with nothing */
+ char out_val[80];
+ 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, sizeof(out_val)))
+ {
+ /* 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 object_filter_t const &item_tester_refill_lantern()
+{
+ using namespace object_filter;
+ static auto instance = Or(
+ TVal(TV_FLASK),
+ And(
+ TVal(TV_LITE),
+ SVal(SV_LITE_LANTERN)));
+ return instance;
+}
+
+
+/*
+ * Refill the players lamp (from the pack or floor)
+ */
+static void do_cmd_refill_lamp(void)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Refill with which flask? ",
+ "You have no flasks of oil.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_refill_lantern()))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Access the lantern */
+ object_type *j_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Refuel */
+ if (o_ptr->tval == TV_FLASK)
+ j_ptr->timeout += o_ptr->pval;
+ else
+ j_ptr->timeout += o_ptr->timeout;
+
+ /* Message */
+ msg_print("You fuel your lamp.");
+
+ /* Comment */
+ if (j_ptr->timeout >= FUEL_LAMP)
+ {
+ j_ptr->timeout = FUEL_LAMP;
+ msg_print("Your lamp is full.");
+ }
+
+ /* Decrease the item stack */
+ inc_stack_size(item, -1);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+}
+
+
+/*
+ * An "item_tester_hook" for refilling torches
+ */
+static object_filter_t const &item_tester_refill_torch()
+{
+ using namespace object_filter;
+ static auto instance =
+ And(
+ TVal(TV_LITE),
+ SVal(SV_LITE_TORCH));
+ return instance;
+}
+
+
+/*
+ * Refuel the players torch (from the pack or floor)
+ */
+static void do_cmd_refill_torch(void)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Refuel with which torch? ",
+ "You have no extra torches.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_refill_torch()))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Take a partial turn */
+ energy_use = 50;
+
+ /* Access the primary torch */
+ object_type *j_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Refuel */
+ j_ptr->timeout += o_ptr->timeout + 5;
+
+ /* Message */
+ msg_print("You combine the torches.");
+
+ /* Over-fuel message */
+ if (j_ptr->timeout >= FUEL_TORCH)
+ {
+ j_ptr->timeout = FUEL_TORCH;
+ msg_print("Your torch is fully fueled.");
+ }
+
+ /* Refuel message */
+ else
+ {
+ msg_print("Your torch glows more brightly.");
+ }
+
+ /* Decrease the item stack */
+ inc_stack_size(item, -1);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+}
+
+
+/*
+ * Refill the players lamp, or restock his torches
+ */
+void do_cmd_refill(void)
+{
+ object_type *o_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* Get the light */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* It is nothing */
+ if (o_ptr->tval != TV_LITE)
+ {
+ msg_print("You are not wielding a light.");
+ return;
+ }
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_FUEL_LITE)
+ {
+ /* It's a torch */
+ if (o_ptr->sval == SV_LITE_TORCH ||
+ o_ptr->sval == SV_LITE_TORCH_EVER)
+ {
+ do_cmd_refill_torch();
+ }
+
+ /* It's a lamp */
+ else if (o_ptr->sval == SV_LITE_LANTERN ||
+ o_ptr->sval == SV_LITE_DWARVEN ||
+ o_ptr->sval == SV_LITE_FEANORIAN)
+ {
+ do_cmd_refill_lamp();
+ }
+ }
+
+ /* No torch to refill */
+ else
+ {
+ msg_print("Your light cannot be refilled.");
+ }
+}
+
+
+/*
+ * Target command
+ */
+void do_cmd_target(void)
+{
+ /* Target set */
+ if (target_set(TARGET_KILL))
+ {
+ msg_print("Target Selected.");
+ }
+
+ /* Target aborted */
+ else
+ {
+ msg_print("Target Aborted.");
+ }
+}
+
+
+
+/*
+ * Look command
+ */
+void do_cmd_look(void)
+{
+ /* Look around */
+ if (target_set(TARGET_LOOK))
+ {
+ msg_print("Target Selected.");
+ }
+}
+
+
+
+/*
+ * Allow the player to examine other sectors on the map
+ */
+void do_cmd_locate(void)
+{
+ int dir, y1, x1, y2, x2;
+ int panel_hgt, panel_wid;
+ char tmp_val[80];
+ char out_val[160];
+
+
+ /* Retrieve size of the Angband window */
+ Term_get_size(&panel_wid, &panel_hgt);
+
+ /* Calcurate size of the dungeon map area */
+ panel_hgt = (panel_hgt - (ROW_MAP + 1)) / 2;
+ panel_wid = (panel_wid - (COL_MAP + 1)) / 2;
+
+ /* Start at current panel */
+ y2 = y1 = panel_row_min;
+ x2 = x1 = panel_col_min;
+
+ /* Show panels until done */
+ while (1)
+ {
+ /* Describe the location */
+ if ((y2 == y1) && (x2 == x1))
+ {
+ tmp_val[0] = '\0';
+ }
+ else
+ {
+ strnfmt(tmp_val, 80, "%s%s of",
+ ((y2 < y1) ? " North" : (y2 > y1) ? " South" : ""),
+ ((x2 < x1) ? " West" : (x2 > x1) ? " East" : ""));
+ }
+
+ /* Prepare to ask which way to look */
+ if ((panel_hgt == PANEL_HGT) && (panel_wid == PANEL_WID))
+ {
+ /* Avoid surprising the standard screen users */
+ strnfmt(out_val, 160,
+ "Map sector [%d,%d], which is%s your sector. Direction?",
+ y2 / panel_hgt, x2 / panel_wid, tmp_val);
+ }
+
+ /* Big screen */
+ else
+ {
+ /* Panels are measured by current map area size */
+ strnfmt(out_val, 160,
+ "Map sector [%d(%02d),%d(%02d)], which is%s your sector. Direction?",
+ y2 / panel_hgt, y2 % panel_hgt,
+ x2 / panel_wid, x2 % panel_wid, tmp_val);
+ }
+
+ /* Assume no direction */
+ dir = 0;
+
+ /* Get a direction */
+ while (!dir)
+ {
+ char ch;
+
+ /* Get a command (or cancel) */
+ if (!get_com(out_val, &ch)) break;
+
+ /* Extract the action (if any) */
+ dir = get_keymap_dir(ch);
+
+ /* Error */
+ if (!dir) bell();
+ }
+
+ /* No direction */
+ if (!dir) break;
+
+ /* Apply the motion */
+ if (change_panel(ddy[dir], ddx[dir]))
+ {
+ y2 = panel_row_min;
+ x2 = panel_col_min;
+ }
+ }
+
+ /* Recenter the map around the player */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+}
+
+
+
+
+
+
+/*
+ * The table of "symbol info" -- each entry is a string of the form
+ * "X:desc" where "X" is the trigger, and "desc" is the "info".
+ */
+static cptr ident_info[] =
+{
+ " :A dark grid",
+ "!:A potion (or oil)",
+ "\":An amulet (or necklace)",
+ "#:A wall (or secret door)",
+ "$:Treasure (gold or gems)",
+ "%:A vein (magma or quartz)",
+ /* "&:unused", */
+ "':An open door",
+ "(:Soft armor",
+ "):A shield",
+ "*:A vein with treasure",
+ "+:A closed door",
+ ",:Food (or mushroom patch)",
+ "-:A wand (or rod)",
+ ".:Floor",
+ "/:A polearm (Axe/Pike/etc)",
+ "0:An altar",
+ "1:Entrance to General Store",
+ "2:Entrance to Armory",
+ "3:Entrance to Weaponsmith",
+ "4:Entrance to Temple",
+ "5:Entrance to Alchemy shop",
+ "6:Entrance to Magic store",
+ "7:Entrance to Black Market",
+ "8:Entrance to your home",
+ "9:Entrance to Bookstore",
+ "::Rubble",
+ ";:A glyph of warding / explosive rune",
+ "<:An up staircase",
+ "=:A ring",
+ ">:A down staircase",
+ "?:A scroll",
+ "@:You",
+ "A:Angel",
+ "B:Bird",
+ "C:Canine",
+ "D:Ancient Dragon/Wyrm",
+ "E:Elemental",
+ "F:Dragon Fly",
+ "G:Ghost",
+ "H:Hybrid",
+ "I:Insect",
+ "J:Snake",
+ "K:Killer Beetle",
+ "L:Lich",
+ "M:Multi-Headed Reptile",
+ /* "N:unused", */
+ "O:Ogre",
+ "P:Giant Humanoid",
+ "Q:Quylthulg (Pulsing Flesh Mound)",
+ "R:Reptile/Amphibian",
+ "S:Spider/Scorpion/Tick",
+ "T:Troll",
+ "U:Major Demon",
+ "V:Vampire",
+ "W:Wight/Wraith/etc",
+ "X:Xorn/Xaren/etc",
+ "Y:Yeti",
+ "Z:Zephyr Hound",
+ "[:Hard armor",
+ "\\:A hafted weapon (mace/whip/etc)",
+ "]:Misc. armor",
+ "^:A trap",
+ "_:A staff",
+ /* "`:unused", */
+ "a:Ant",
+ "b:Bat",
+ "c:Centipede",
+ "d:Dragon",
+ "e:Floating Eye",
+ "f:Feline",
+ "g:Golem",
+ "h:Hobbit/Elf/Dwarf",
+ "i:Icky Thing",
+ "j:Jelly",
+ "k:Kobold",
+ "l:Louse",
+ "m:Mold",
+ "n:Naga",
+ "o:Orc",
+ "p:Person/Human",
+ "q:Quadruped",
+ "r:Rodent",
+ "s:Skeleton",
+ "t:Townsperson",
+ "u:Minor Demon",
+ "v:Vortex",
+ "w:Worm/Worm-Mass",
+ /* "x:unused", */
+ "y:Yeek",
+ "z:Zombie/Mummy",
+ "{:A missile (arrow/bolt/shot)",
+ "|:An edged weapon (sword/dagger/etc)",
+ "}:A launcher (bow/crossbow/sling)",
+ "~:A tool (or miscellaneous item)",
+ NULL
+};
+
+
+/**
+ * Sort by monster experience.
+ */
+static bool compare_monster_experience(int w1, int w2)
+{
+ /* Extract experience */
+ s32b z1 = r_info[w1].mexp;
+ s32b z2 = r_info[w2].mexp;
+
+ /* Compare experience */
+ if (z1 < z2) return true;
+ if (z1 > z2) return false;
+
+ /* Punt to index */
+ return w1 < w2;
+}
+
+/**
+ * Sort by monster level.
+ */
+static bool compare_monster_level(int w1, int w2)
+{
+ /* Extract levels */
+ byte z1 = r_info[w1].level;
+ byte z2 = r_info[w2].level;
+
+ /* Compare levels */
+ if (z1 < z2) return true;
+ if (z1 > z2) return false;
+
+ /* Punt to monster experience. */
+ return compare_monster_experience(w1, w2);
+}
+
+/**
+ * Sort by total number of kills
+ */
+static bool compare_total_kills(int w1, int w2)
+{
+ /* Extract total kills */
+ s16b z1 = r_info[w1].r_tkills;
+ s16b z2 = r_info[w2].r_tkills;
+
+ /* Compare total kills */
+ if (z1 < z2) return true;
+ if (z1 > z2) return false;
+
+ /* Punt to monster level. */
+ return compare_monster_level(w1, w2);
+}
+
+/*
+ * Sort by player kills
+ */
+static bool compare_player_kills(int w1, int w2)
+{
+ /* Extract player kills */
+ s16b z1 = r_info[w1].r_pkills;
+ s16b z2 = r_info[w2].r_pkills;
+
+ /* Compare player kills */
+ if (z1 < z2) return true;
+ if (z1 > z2) return false;
+
+ /* Punt to total number of kills. */
+ return compare_total_kills(w1, w2);
+}
+
+
+/*
+ * Hack -- Display the "name" and "attr/chars" of a monster race
+ */
+static void roff_top(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ byte a1, a2;
+
+ char c1, c2;
+
+
+ /* Access the chars */
+ c1 = r_ptr->d_char;
+ c2 = r_ptr->x_char;
+
+ /* Access the attrs */
+ a1 = r_ptr->d_attr;
+ a2 = r_ptr->x_attr;
+
+
+ /* Clear the top line */
+ Term_erase(0, 0, 255);
+
+ /* Reset the cursor */
+ Term_gotoxy(0, 0);
+
+ /* A title (use "The" for non-uniques) */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ Term_addstr( -1, TERM_WHITE, "The ");
+ }
+
+ /* Dump the name */
+ Term_addstr( -1, TERM_WHITE, r_ptr->name);
+
+ /* Append the "standard" attr/char info */
+ Term_addstr( -1, TERM_WHITE, " ('");
+ Term_addch(a1, c1);
+ Term_addstr( -1, TERM_WHITE, "')");
+
+ /* Append the "optional" attr/char info */
+ Term_addstr( -1, TERM_WHITE, "/('");
+ Term_addch(a2, c2);
+ 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, 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;
+
+ bool (*sort_by)(int,int) = nullptr;
+
+ /* 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);
+
+ /* Collect matching monsters */
+ std::vector<u16b> who;
+ for (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_ptr->name);
+ strlower(mon_name);
+
+ if (!strstr(mon_name, temp)) continue;
+ }
+
+ /* Collect "appropriate" monsters */
+ if (all || (r_ptr->d_char == sym)) {
+ who.push_back(i);
+ }
+ }
+
+ /* Nothing to recall */
+ if (who.empty())
+ {
+ 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')
+ {
+ sort_by = compare_player_kills;
+ query = 'y';
+ }
+
+ /* Sort by level */
+ if (query == 'p')
+ {
+ sort_by = compare_monster_level;
+ query = 'y';
+ }
+
+ /* Catch "escape" */
+ if (query != 'y')
+ {
+ return;
+ }
+
+
+ /* Sort if needed */
+ if (sort_by)
+ {
+ /* Sort the array */
+ std::sort(std::begin(who), std::end(who), sort_by);
+ }
+
+
+ /* Start at the end */
+ i = who.size() - 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 == '-')
+ {
+ i++;
+ assert(i >= 0);
+ if (static_cast<size_t>(i) == who.size())
+ {
+ i = 0;
+ }
+ }
+
+ /* Move to "next" monster */
+ else
+ {
+ if (i-- == 0)
+ {
+ i = who.size() - 1;
+ }
+ }
+ }
+
+ /* Re-display the identity */
+ prt(buf, 0, 0);
+}
+
+
+/*
+ * research_mon
+ * -KMW-
+ */
+bool_ research_mon()
+{
+ int i, 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;
+
+ monster_race *r2_ptr;
+
+ /* Hack -- Remember "cheat_know" flag */
+ oldcheat = cheat_know;
+
+
+ /* Get a character, or abort */
+ if (!get_com("Enter character of monster: ", &sym)) return (TRUE);
+
+ /* 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 */
+ std::vector<u16b> who;
+ for (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.push_back(i);
+ }
+ }
+
+ /* Nothing to recall */
+ if (who.empty())
+ {
+ /* Restore the "cheat_know" flag */
+ cheat_know = oldcheat;
+
+ return (TRUE);
+ }
+
+
+ query = 'y';
+
+ /* Sort by level */
+ std::sort(std::begin(who), std::end(who), compare_monster_level);
+
+
+ /* Start at the end */
+ i = who.size() - 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 == '-')
+ {
+ i++;
+ assert(i >= 0);
+ if (static_cast<size_t>(i) == who.size())
+ {
+ i = 0;
+ }
+ }
+
+ /* Move to "next" monster */
+ else
+ {
+ if (i-- == 0)
+ {
+ i = who.size() - 1;
+ }
+ }
+ }
+
+
+ /* Re-display the identity */
+ /* prt(buf, 5, 5);*/
+
+ /* 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;
+}
+
+
+/*
+ * 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 = strdup(temp);
+ }
+ else
+ {
+ cli_ptr->comm = strdup(trigger);
+ }
+
+ /* First try copying everything across. */
+ cli_ptr->key = num;
+ cli_ptr->descrip = nullptr;
+ if (descr) {
+ cli_ptr->descrip = strdup(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.
+ */
+static 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 */
+ res = askfor_aux_with_completion(buf, len);
+
+ /* 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);
+ 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);
+ Term_fresh();
+ return;
+ }
+ }
+
+ /* Now restore the screen to dump it */
+ Term_load_from(save);
+
+ 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/cmd3.hpp b/src/cmd3.hpp
new file mode 100644
index 00000000..48677b77
--- /dev/null
+++ b/src/cmd3.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "h-basic.h"
+
+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);
diff --git a/src/cmd4.cc b/src/cmd4.cc
new file mode 100644
index 00000000..4b6c040c
--- /dev/null
+++ b/src/cmd4.cc
@@ -0,0 +1,4415 @@
+/*
+ * 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 "cmd4.hpp"
+
+#include "artifact_type.hpp"
+#include "cave_type.hpp"
+#include "corrupt.hpp"
+#include "dungeon_info_type.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "levels.hpp"
+#include "messages.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "notes.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "squeltch.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+#include <memory>
+#include <string>
+#include <vector>
+#include <algorithm>
+
+/*
+ * 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_FRAME | PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER | PW_M_LIST);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MESSAGE | PW_OVERHEAD | PW_MONSTER | PW_OBJECT);
+
+ /* Hack -- update */
+ handle_stuff();
+
+
+ /* Redraw every window */
+ for (j = 0; j < 8; j++)
+ {
+ /* Dead window */
+ if (!angband_term[j]) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Redraw */
+ Term_redraw();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- change name
+ */
+void do_cmd_change_name(void)
+{
+ char c;
+
+ int mode = 0;
+
+ char tmp[160];
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Forever */
+ while (1)
+ {
+ /* keep mode below 7 */
+ mode = (mode + 6) % 6;
+
+ /* Display the player */
+ display_player(mode);
+
+ /* Prompt */
+ if (mode == 0)
+ {
+ Term_putstr(14, 22, -1, TERM_WHITE,
+ "['t/T' to change tactics, 'e/E' to change movement]");
+ }
+
+ Term_putstr(4, 23, -1, TERM_WHITE,
+ "['c' to change name, 'f' to file, 'p' for previous, 'n' for next, or ESC]");
+
+ /* Query */
+ c = inkey();
+
+ /* Exit */
+ if (c == ESCAPE) break;
+
+ /* Change name */
+ if (c == 'c')
+ {
+ get_name();
+ }
+
+ /* File dump */
+ else if (c == 'f')
+ {
+ strnfmt(tmp, 160, "%s.txt", player_name);
+ if (get_string("Filename(you can post it to http://angband.oook.cz/): ", tmp, 80))
+ {
+ if (tmp[0] && (tmp[0] != ' '))
+ {
+ file_character(tmp, FALSE);
+ }
+ }
+ }
+
+ /* Toggle mode */
+ else if (c == 'n')
+ {
+ mode++;
+ }
+ else if (c == 'p')
+ {
+ mode--;
+ }
+
+ else if (mode == 0)
+ {
+ /* Change tactic */
+ if (c == 't')
+ {
+ (void)do_cmd_change_tactic( -1);
+ }
+ else if (c == 'T')
+ {
+ (void)do_cmd_change_tactic(1);
+ }
+
+ /* Change movement */
+ else if (c == 'e')
+ {
+ do_cmd_change_movement( -1);
+ }
+ else if (c == 'E')
+ {
+ do_cmd_change_movement(1);
+ }
+ else
+ {
+ bell();
+ }
+ }
+ /* Oops */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+
+
+ /* Redraw everything */
+ p_ptr->redraw |= (PR_WIPE | PR_FRAME | 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;
+}
+
+// File-local
+namespace {
+
+ /**
+ * Interaction mode for options
+ */
+ enum class interaction_mode_t {
+ READ_ONLY = 0,
+ READ_WRITE = 1
+ };
+
+}
+
+/**
+ * Interact with given vector of options.
+ */
+static void interact_with_options(std::vector<option_type *> const &options, char const *info, interaction_mode_t interaction_mode)
+{
+ size_t n = options.size();
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact with the player */
+ size_t k = 0; /* Currently selected option index */
+ while (TRUE)
+ {
+ /* Prompt XXX XXX XXX */
+ char buf[80];
+ strnfmt(buf, 80, "%s (RET to advance, y/n to set, ESC to accept) ", info);
+ prt(buf, 0, 0);
+
+ /* Display the options */
+ for (size_t 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)",
+ options[i]->o_desc,
+ (*options[i]->o_var ? "yes" : "no "),
+ options[i]->o_text);
+ c_prt(a, buf, i + 2, 0);
+ }
+
+ /* Hilite current option */
+ move_cursor(k + 2, 50);
+
+ /* Get a key */
+ int ch = inkey();
+
+ /*
+ * Hack -- Try to translate the key into a direction
+ * to allow the use of roguelike keys for navigation
+ */
+ {
+ int 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':
+ {
+ /* Adding n pre-modulo ensures that we don't
+ wrap around to the wrong (post-modulo) index. */
+ 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 (interaction_mode == interaction_mode_t::READ_ONLY)
+ {
+ break;
+ }
+ *(options[k]->o_var) = TRUE;
+ k = (k + 1) % n;
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ case '4':
+ {
+ if (interaction_mode == interaction_mode_t::READ_ONLY)
+ {
+ break;
+ }
+
+ *(options[k]->o_var) = FALSE;
+ k = (k + 1) % n;
+ break;
+ }
+
+ default:
+ {
+ bell();
+
+ break;
+ }
+ }
+ }
+
+
+}
+
+
+
+/*
+ * Cheating options
+ */
+static option_type cheat_info[6] =
+{
+ { &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)
+{
+ // Calculate number of cheat options
+ size_t n = std::distance(std::begin(cheat_info), std::end(cheat_info));
+
+ // Build the vector of options we're going to interact with
+ std::vector<option_type *> options;
+ options.reserve(n);
+ for (auto &option : cheat_info)
+ {
+ options.push_back(&option);
+ }
+
+ // Interact
+ interact_with_options(options, info, interaction_mode_t::READ_WRITE);
+
+ // If user toggled any of the options to TRUE, then we add those cheats
+ // to the player's "noscore" flags. Note that it doesn't matter what the
+ // previous value was -- we don't "unset" noscore flags anyway.
+ for (auto &option: options)
+ {
+ if (*option->o_var)
+ {
+ noscore |= (option->o_page * 256 + option->o_bit);
+ }
+ }
+}
+
+
+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;
+ }
+ }
+ }
+}
+
+/*
+ * Interact with some options
+ */
+void do_cmd_options_aux(int page, cptr info, bool_ read_only)
+{
+ // Scrape together all the options from the relevant page.
+ std::vector<option_type *> options;
+ options.reserve(64); // Seems a reasonable number; anything more would be unusable anyway
+ for (size_t i = 0; option_info[i].o_desc; i++)
+ {
+ if (option_info[i].o_page == page)
+ {
+ options.push_back(&option_info[i]);
+ }
+ }
+
+ // Interact with the options
+ interaction_mode_t interaction_mode = read_only
+ ? interaction_mode_t::READ_ONLY
+ : interaction_mode_t::READ_WRITE;
+ interact_with_options(options, info, interaction_mode);
+}
+
+
+/*
+ * Modify the "window" options
+ */
+static void do_cmd_options_win(void)
+{
+ int i, j, d;
+
+ int y = 0;
+
+ int x = 0;
+
+ char ch;
+
+ bool_ go = TRUE;
+
+ u32b old_flag[8];
+
+
+ /* Memorize old flags */
+ for (j = 0; j < 8; j++)
+ {
+ /* Acquire current flags */
+ old_flag[j] = window_flag[j];
+ }
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Interact */
+ while (go)
+ {
+ /* Prompt XXX XXX XXX */
+ prt("Window Flags (<dir>, t, y, n, ESC) ", 0, 0);
+
+ /* Display the windows */
+ for (j = 0; j < 8; j++)
+ {
+ byte a = TERM_WHITE;
+
+ cptr s = angband_term_name[j];
+
+ /* Use color */
+ if (j == x) a = TERM_L_BLUE;
+
+ /* Window name, staggered, centered */
+ Term_putstr(35 + j * 5 - strlen(s) / 2, 2 + j % 2, -1, a, s);
+ }
+
+ /* Display the options */
+ for (i = 0; i < 16; i++)
+ {
+ byte a = TERM_WHITE;
+
+ cptr str = window_flag_desc[i];
+
+ /* Use color */
+ if (i == y) a = TERM_L_BLUE;
+
+ /* Unused option */
+ if (!str) str = "(Unused option)";
+
+ /* Flag name */
+ Term_putstr(0, i + 5, -1, a, str);
+
+ /* Display the windows */
+ for (j = 0; j < 8; j++)
+ {
+ byte a = TERM_WHITE;
+
+ char c = '.';
+
+ /* Use color */
+ if ((i == y) && (j == x)) a = TERM_L_BLUE;
+
+ /* Active flag */
+ if (window_flag[j] & (1L << i)) c = 'X';
+
+ /* Flag value */
+ Term_putch(35 + j * 5, i + 5, a, c);
+ }
+ }
+
+ /* Place Cursor */
+ Term_gotoxy(35 + x * 5, y + 5);
+
+ /* Get key */
+ ch = inkey();
+
+ /* Analyze */
+ switch (ch)
+ {
+ case ESCAPE:
+ {
+ go = FALSE;
+
+ break;
+ }
+
+ case 'T':
+ case 't':
+ {
+ /* Clear windows */
+ for (j = 0; j < 8; j++)
+ {
+ window_flag[j] &= ~(1L << y);
+ }
+
+ /* Clear flags */
+ for (i = 0; i < 16; i++)
+ {
+ window_flag[x] &= ~(1L << i);
+ }
+
+ /* Fall through */
+ }
+
+ case 'y':
+ case 'Y':
+ {
+ /* Ignore screen */
+ if (x == 0) break;
+
+ /* Set flag */
+ window_flag[x] |= (1L << y);
+
+ break;
+ }
+
+ case 'n':
+ case 'N':
+ {
+ /* Clear flag */
+ window_flag[x] &= ~(1L << y);
+
+ break;
+ }
+
+ default:
+ {
+ d = get_keymap_dir(ch);
+
+ x = (x + ddx[d] + 8) % 8;
+ y = (y + ddy[d] + 16) % 16;
+
+ if (!d) bell();
+
+ break;
+ }
+ }
+ }
+
+ /* Notice changes */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* Dead window */
+ if (!angband_term[j]) continue;
+
+ /* Ignore non-changes */
+ if (window_flag[j] == old_flag[j]) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Erase */
+ Term_clear();
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Write all current options to the given preference file in the
+ * lib/user directory. Modified from KAmband 1.8.
+ */
+static errr option_dump(cptr fname)
+{
+ int i, j;
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, fname);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+
+ /* Skip some lines */
+ fprintf(fff, "\n\n");
+
+ /* Start dumping */
+ fprintf(fff, "# Automatic option dump\n\n");
+
+ /* Dump options (skip cheat, adult, score) */
+ for (i = 0; option_info[i].o_var != NULL; i++)
+ {
+ /* Require a real option */
+ if (!option_info[i].o_text) continue;
+
+ /* No birth options */
+ if (option_info[i].o_page == 6) continue;
+
+ /* Comment */
+ fprintf(fff, "# Option '%s'\n", option_info[i].o_desc);
+
+ /* Dump the option */
+ if ((*option_info[i].o_var))
+ {
+ fprintf(fff, "Y:%s\n", option_info[i].o_text);
+ }
+ else
+ {
+ fprintf(fff, "X:%s\n", option_info[i].o_text);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+ }
+
+ /* Dump window flags */
+ for (i = 1; i < ANGBAND_TERM_MAX; i++)
+ {
+ /* Require a real window */
+ if (!angband_term[i]) continue;
+
+ /* Check each flag */
+ for (j = 0; j < 32; j++)
+ {
+ /* Require a real flag */
+ if (!window_flag_desc[j]) continue;
+
+ /* Comment */
+ fprintf(fff, "# Window '%s', Flag '%s'\n",
+ angband_term_name[i], window_flag_desc[j]);
+
+ /* Dump the flag */
+ if (window_flag[i] & (1L << j))
+ {
+ fprintf(fff, "W:%d:%d:1\n", i, j);
+ }
+ else
+ {
+ fprintf(fff, "W:%d:%d:0\n", i, j);
+ }
+
+ /* Skip a line */
+ fprintf(fff, "\n");
+ }
+ }
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Ask for a "user pref file" and process it.
+ *
+ * This function should only be used by standard interaction commands,
+ * in which a standard "Command:" prompt is present on the given row.
+ *
+ * Allow absolute file names? XXX XXX XXX
+ */
+static void do_cmd_pref_file_hack(int row)
+{
+ char ftmp[80];
+
+
+ /* Prompt */
+ prt("Command: Load a user pref file", row, 0);
+
+ /* Prompt */
+ prt("File: ", row + 2, 0);
+
+ /* Default filename */
+ strnfmt(ftmp, 80, "%s.prf", player_base);
+
+ /* Ask for a file (or cancel) */
+ if (!askfor_aux(ftmp, 80)) return;
+
+ /* Process the given filename */
+ if (process_pref_file(ftmp))
+ {
+ /* Mention failure */
+ msg_format("Failed to load '%s'!", ftmp);
+ }
+ else
+ {
+ /* Mention success */
+ msg_format("Loaded '%s'.", ftmp);
+ }
+}
+
+
+/*
+ * Set or unset various options.
+ *
+ * The user must use the "Ctrl-R" command to "adapt" to changes
+ * in any options which control "visual" aspects of the game.
+ */
+void do_cmd_options(void)
+{
+ int k;
+
+
+ /* Save the screen */
+ screen_save();
+
+ /* Interact */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Why are we here */
+ prt("Options", 2, 0);
+
+ /* Give some choices */
+ prt("(1) User Interface Options", 4, 5);
+ prt("(2) Disturbance Options", 5, 5);
+ prt("(3) Game-Play Options", 6, 5);
+ prt("(4) Efficiency Options", 7, 5);
+ prt("(5) ToME Options", 8, 5);
+ prt("(6) Birth Options(read only)", 9, 5);
+
+ /* Special choices */
+ prt("(D) Base Delay Factor", 10, 5);
+ prt("(H) Hitpoint Warning", 11, 5);
+ prt("(A) Autosave Options", 12, 5);
+
+ /* Automatizer */
+ prt("(T) Automatizer", 14, 5);
+
+
+ /* Window flags */
+ prt("(W) Window Flags", 16, 5);
+
+ /* Cheating */
+ prt("(C) Cheating Options", 18, 5);
+
+ /* Dump */
+ prt("(U) Dump Options setting", 20, 5);
+ prt("(O) Load Options setting", 21, 5);
+
+ /* Prompt */
+ prt("Command: ", 22, 0);
+
+ /* Get command */
+ k = inkey();
+
+ /* Exit */
+ if (k == ESCAPE) break;
+
+ /* Analyze */
+ switch (k)
+ {
+ /* Load a user pref file */
+ case 'o':
+ case 'O':
+ {
+ /* Ask for and load a user pref file */
+ do_cmd_pref_file_hack(21);
+
+ break;
+ }
+
+ /* Append options to a file */
+ case 'u':
+ case 'U':
+ {
+ char ftmp[80];
+
+ /* Prompt */
+ prt("Command: Append options to a file", 21, 0);
+
+ /* Prompt */
+ prt("File: ", 21, 0);
+
+ /* Default filename */
+ strnfmt(ftmp, 80, "%s.prf", player_base);
+
+ /* Ask for a file */
+ if (!askfor_aux(ftmp, 80)) continue;
+
+ /* Dump the options */
+ if (option_dump(ftmp))
+ {
+ /* Failure */
+ msg_print("Failed!");
+ }
+ else
+ {
+ /* Success */
+ msg_print("Done.");
+ }
+
+ break;
+ }
+
+ /* General Options */
+ case '1':
+ {
+ /* Process the general options */
+ do_cmd_options_aux(1, "User Interface Options", FALSE);
+
+ break;
+ }
+
+ /* Disturbance Options */
+ case '2':
+ {
+ /* Spawn */
+ do_cmd_options_aux(2, "Disturbance Options", FALSE);
+
+ break;
+ }
+
+ /* Inventory Options */
+ case '3':
+ {
+ /* Spawn */
+ do_cmd_options_aux(3, "Game-Play Options", FALSE);
+
+ break;
+ }
+
+ /* Efficiency Options */
+ case '4':
+ {
+ /* Spawn */
+ do_cmd_options_aux(4, "Efficiency Options", FALSE);
+
+ break;
+ }
+
+ /* ToME Options */
+ case '5':
+ {
+ do_cmd_options_aux(5, "ToME Options", FALSE);
+
+ break;
+ }
+
+ /* Birth Options - read only */
+ case '6':
+ {
+ do_cmd_options_aux(6, "Birth Options(read only)", TRUE);
+
+ break;
+ }
+ /* Cheating Options */
+ case 'C':
+ {
+ /* Spawn */
+ do_cmd_options_cheat("Cheaters never win");
+
+ break;
+ }
+
+ case 't':
+ case 'T':
+ {
+ do_cmd_automatizer();
+ break;
+ }
+
+ case 'a':
+ case 'A':
+ {
+ do_cmd_options_autosave("Autosave");
+
+ break;
+ }
+
+ /* Window flags */
+ case 'W':
+ case 'w':
+ {
+ /* Spawn */
+ do_cmd_options_win();
+
+ break;
+ }
+
+ /* Hack -- Delay Speed */
+ case 'D':
+ case 'd':
+ {
+ /* Prompt */
+ prt("Command: Base Delay Factor", 21, 0);
+
+ /* Get a new value */
+ while (1)
+ {
+ int msec = delay_factor * delay_factor * delay_factor;
+ prt(format("Current base delay factor: %d (%d msec)",
+ delay_factor, msec), 22, 0);
+ prt("Delay Factor (0-9 or ESC to accept): ", 23, 0);
+ k = inkey();
+ if (k == ESCAPE) break;
+ if (isdigit(k)) delay_factor = D2I(k);
+ else bell();
+ }
+
+ break;
+ }
+
+ /* Hack -- hitpoint warning factor */
+ case 'H':
+ case 'h':
+ {
+ /* Prompt */
+ prt("Command: Hitpoint Warning", 18, 0);
+
+ /* Get a new value */
+ while (1)
+ {
+ prt(format("Current hitpoint warning: %d0%%",
+ hitpoint_warn), 22, 0);
+ prt("Hitpoint Warning (0-9 or ESC to accept): ", 20, 0);
+ k = inkey();
+ if (k == ESCAPE) break;
+ if (isdigit(k)) hitpoint_warn = D2I(k);
+ else bell();
+ }
+
+ break;
+ }
+
+ /* Unknown option */
+ default:
+ {
+ /* Oops */
+ bell();
+
+ break;
+ }
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ screen_load();
+}
+
+
+
+/*
+ * Ask for a "user pref line" and process it
+ *
+ * XXX XXX XXX Allow absolute file names?
+ */
+void do_cmd_pref(void)
+{
+ char buf[80];
+
+
+ /* Default */
+ strcpy(buf, "");
+
+ /* Ask for a "user pref command" */
+ if (!get_string("Pref: ", buf, 80)) return;
+
+ /* Process that pref command */
+ (void)process_pref_file_aux(buf);
+}
+
+
+/*
+ * Hack -- append all current macros to the given file
+ */
+static errr macro_dump(cptr fname)
+{
+ int i;
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, fname);
+
+ /* 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;
+
+ /* Attempt to read a key */
+ i = inkey_scan();
+ }
+
+ /* Terminate */
+ buf[n] = '\0';
+
+ /* Flush */
+ flush();
+
+
+ if (macro_screen)
+ {
+ /* Convert the trigger */
+ ascii_to_text(tmp, buf);
+
+ /* Hack -- display the trigger */
+ Term_addstr( -1, TERM_WHITE, tmp);
+ }
+}
+
+/*
+ * Hack -- ask for a keymap "trigger" (see below)
+ *
+ * Note that both "flush()" calls are extremely important. This may
+ * no longer be true, since "util.c" is much simpler now. XXX XXX XXX
+ */
+static void do_cmd_macro_aux_keymap(char *buf)
+{
+ char tmp[1024];
+
+
+ /* Flush */
+ flush();
+
+
+ /* Get a key */
+ buf[0] = inkey();
+ buf[1] = '\0';
+
+
+ /* Convert to ascii */
+ ascii_to_text(tmp, buf);
+
+ /* Hack -- display the trigger */
+ Term_addstr( -1, TERM_WHITE, tmp);
+
+
+ /* Flush */
+ flush();
+}
+
+
+/*
+ * Hack -- append all keymaps to the given file
+ */
+static errr keymap_dump(cptr fname)
+{
+ int i;
+
+ FILE *fff;
+
+ char key[1024];
+ char buf[1024];
+
+ int mode;
+
+
+ /* Keymap mode */
+ mode = get_keymap_mode();
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, fname);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) return ( -1);
+
+
+ /* Skip space */
+ fprintf(fff, "\n\n");
+
+ /* Start dumping */
+ fprintf(fff, "# Automatic keymap dump\n\n");
+
+ /* Dump them */
+ for (i = 0; i < 256; i++)
+ {
+ cptr act;
+
+ /* Loop up the keymap */
+ act = keymap_act[mode][i];
+
+ /* Skip empty keymaps */
+ if (!act) continue;
+
+ /* Encode the key */
+ buf[0] = i;
+ buf[1] = '\0';
+ ascii_to_text(key, buf);
+
+ /* Encode the action */
+ ascii_to_text(buf, act);
+
+ /* Dump the macro */
+ fprintf(fff, "A:%s\n", buf);
+ fprintf(fff, "C:%d:%s\n", mode, key);
+ }
+
+ /* Start dumping */
+ fprintf(fff, "\n\n\n");
+
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Interact with "macros"
+ *
+ * Note that the macro "action" must be defined before the trigger.
+ *
+ * Could use some helpful instructions on this page. XXX XXX XXX
+ */
+void do_cmd_macros(void)
+{
+ int i;
+
+ char tmp[1024];
+
+ char buf[1024];
+
+ int mode;
+
+
+ /* Keymap mode */
+ mode = get_keymap_mode();
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save screen */
+ Term_save();
+
+
+ /* Process requests until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Describe */
+ prt("Interact with Macros", 2, 0);
+
+
+ /* Describe that action */
+ prt("Current action (if any) shown below:", 20, 0);
+
+ /* Analyze the current action */
+ ascii_to_text(buf, macro__buf);
+
+ /* Display the current action */
+ prt(buf, 22, 0);
+
+
+ /* Selections */
+ prt("(1) Load a user pref file", 4, 5);
+ prt("(2) Append macros to a file", 5, 5);
+ prt("(3) Query a macro", 6, 5);
+ prt("(4) Create a macro", 7, 5);
+ prt("(5) Remove a macro", 8, 5);
+ prt("(6) Append keymaps to a file", 9, 5);
+ prt("(7) Query a keymap", 10, 5);
+ prt("(8) Create a keymap", 11, 5);
+ prt("(9) Remove a keymap", 12, 5);
+ prt("(0) Enter a new action", 13, 5);
+
+ /* Prompt */
+ prt("Command: ", 16, 0);
+
+ /* Get a command */
+ i = inkey();
+
+ /* Leave */
+ if (i == ESCAPE) break;
+
+ /* Load a 'macro' file */
+ else if (i == '1')
+ {
+ /* Prompt */
+ prt("Command: Load a user pref file", 16, 0);
+
+ /* Prompt */
+ prt("File: ", 18, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 1024, "%s.prf", player_name);
+
+ /* Ask for a file */
+ if (!askfor_aux(tmp, 80)) continue;
+
+ /* Process the given filename */
+ if (0 != process_pref_file(tmp))
+ {
+ /* Prompt */
+ msg_print("Could not load file!");
+ }
+ }
+
+ /* Save macros */
+ else if (i == '2')
+ {
+ /* Prompt */
+ prt("Command: Append macros to a file", 16, 0);
+
+ /* Prompt */
+ prt("File: ", 18, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 1024, "%s.prf", player_name);
+
+ /* Ask for a file */
+ if (!askfor_aux(tmp, 80)) continue;
+
+ /* Dump the macros */
+ (void)macro_dump(tmp);
+
+ /* Prompt */
+ msg_print("Appended macros.");
+ }
+
+ /* Query a macro */
+ else if (i == '3')
+ {
+ int k;
+
+ /* Prompt */
+ prt("Command: Query a macro", 16, 0);
+
+ /* Prompt */
+ prt("Trigger: ", 18, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, TRUE);
+
+ /* Acquire action */
+ k = macro_find_exact(buf);
+
+ /* Nothing found */
+ if (k < 0)
+ {
+ /* Prompt */
+ msg_print("Found no macro.");
+ }
+
+ /* Found one */
+ else
+ {
+ /* Obtain the action */
+ strcpy(macro__buf, macro__act[k]);
+
+ /* Analyze the current action */
+ ascii_to_text(buf, macro__buf);
+
+ /* Display the current action */
+ prt(buf, 22, 0);
+
+ /* Prompt */
+ msg_print("Found a macro.");
+ }
+ }
+
+ /* Create a macro */
+ else if (i == '4')
+ {
+ /* Prompt */
+ prt("Command: Create a macro", 16, 0);
+
+ /* Prompt */
+ prt("Trigger: ", 18, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, TRUE);
+
+ /* Clear */
+ clear_from(20);
+
+ /* Prompt */
+ prt("Action: ", 20, 0);
+
+ /* Convert to text */
+ ascii_to_text(tmp, macro__buf);
+
+ /* Get an encoded action */
+ if (askfor_aux(tmp, 80))
+ {
+ /* Convert to ascii */
+ text_to_ascii(macro__buf, tmp);
+
+ /* Link the macro */
+ macro_add(buf, macro__buf);
+
+ /* Prompt */
+ msg_print("Added a macro.");
+ }
+ }
+
+ /* Remove a macro */
+ else if (i == '5')
+ {
+ /* Prompt */
+ prt("Command: Remove a macro", 16, 0);
+
+ /* Prompt */
+ prt("Trigger: ", 18, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, TRUE);
+
+ /* Link the macro */
+ macro_add(buf, buf);
+
+ /* Prompt */
+ msg_print("Removed a macro.");
+ }
+
+ /* Save keymaps */
+ else if (i == '6')
+ {
+ /* Prompt */
+ prt("Command: Append keymaps to a file", 16, 0);
+
+ /* Prompt */
+ prt("File: ", 18, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 1024, "%s.prf", player_name);
+
+ /* Ask for a file */
+ if (!askfor_aux(tmp, 80)) continue;
+
+ /* Dump the macros */
+ (void)keymap_dump(tmp);
+
+ /* Prompt */
+ msg_print("Appended keymaps.");
+ }
+
+ /* Query a keymap */
+ else if (i == '7')
+ {
+ cptr act;
+
+ /* Prompt */
+ prt("Command: Query a keymap", 16, 0);
+
+ /* Prompt */
+ prt("Keypress: ", 18, 0);
+
+ /* Get a keymap trigger */
+ do_cmd_macro_aux_keymap(buf);
+
+ /* Look up the keymap */
+ act = keymap_act[mode][(byte)(buf[0])];
+
+ /* Nothing found */
+ if (!act)
+ {
+ /* Prompt */
+ msg_print("Found no keymap.");
+ }
+
+ /* Found one */
+ else
+ {
+ /* Obtain the action */
+ strcpy(macro__buf, act);
+
+ /* Analyze the current action */
+ ascii_to_text(buf, macro__buf);
+
+ /* Display the current action */
+ prt(buf, 22, 0);
+
+ /* Prompt */
+ msg_print("Found a keymap.");
+ }
+ }
+
+ /* Create a keymap */
+ else if (i == '8')
+ {
+ /* Prompt */
+ prt("Command: Create a keymap", 16, 0);
+
+ /* Prompt */
+ prt("Keypress: ", 18, 0);
+
+ /* Get a keymap trigger */
+ do_cmd_macro_aux_keymap(buf);
+
+ /* Clear */
+ clear_from(20);
+
+ /* Prompt */
+ prt("Action: ", 20, 0);
+
+ /* Convert to text */
+ ascii_to_text(tmp, macro__buf);
+
+ /* Get an encoded action */
+ if (askfor_aux(tmp, 80))
+ {
+ /* Convert to ascii */
+ text_to_ascii(macro__buf, tmp);
+
+ /* Make new keymap */
+ free(keymap_act[mode][(byte)(buf[0])]);
+ keymap_act[mode][(byte)(buf[0])] = strdup(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);
+
+ /* Make new keymap */
+ free(keymap_act[mode][(byte)(buf[0])]);
+ keymap_act[mode][(byte)(buf[0])] = NULL;
+
+ /* Prompt */
+ msg_print("Removed a keymap.");
+ }
+
+ /* Enter a new action */
+ else if (i == '0')
+ {
+ /* Prompt */
+ prt("Command: Enter a new action", 16, 0);
+
+ /* Go to the correct location */
+ Term_gotoxy(0, 22);
+
+ /* Hack -- limit the value */
+ tmp[80] = '\0';
+
+ /* Get an encoded action */
+ if (!askfor_aux(buf, 80)) continue;
+
+ /* Extract an action */
+ text_to_ascii(macro__buf, buf);
+ }
+
+ /* Oops */
+ else
+ {
+ /* Oops */
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+ /* Load screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Interact with "visuals"
+ */
+void do_cmd_visuals(void)
+{
+ int i;
+
+ FILE *fff;
+
+ char tmp[160];
+
+ char buf[1024];
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Interact with Visuals", 2, 0);
+
+ /* Give some choices */
+ prt("(1) Load a user pref file", 4, 5);
+ prt("(2) Dump monster attr/chars", 5, 5);
+ prt("(3) Dump object attr/chars", 6, 5);
+ prt("(4) Dump feature attr/chars", 7, 5);
+ prt("(5) (unused)", 8, 5);
+ prt("(6) Change monster attr/chars", 9, 5);
+ prt("(7) Change object attr/chars", 10, 5);
+ prt("(8) Change feature attr/chars", 11, 5);
+ prt("(9) (unused)", 12, 5);
+ prt("(0) Reset visuals", 13, 5);
+
+ /* Prompt */
+ prt("Command: ", 15, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ /* Load a 'pref' file */
+ else if (i == '1')
+ {
+ /* Prompt */
+ prt("Command: Load a user pref file", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Query */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Process the given filename */
+ (void)process_pref_file(tmp);
+ }
+
+ /* Dump monster attr/chars */
+ else if (i == '2')
+ {
+ /* Prompt */
+ prt("Command: Dump monster attr/chars", 15, 0);
+
+ /* Prompt */
+ prt("File: ", 17, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Monster attr/char definitions\n\n");
+
+ /* Dump monsters */
+ for (i = 0; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Skip non-entries */
+ if (!r_ptr->name) continue;
+
+ /* Dump a comment */
+ fprintf(fff, "# %s\n", r_ptr->name);
+
+ /* Dump the monster attr/char info */
+ fprintf(fff, "R:%d:0x%02X:0x%02X\n\n", i,
+ static_cast<unsigned int>(r_ptr->x_attr),
+ static_cast<unsigned int>(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_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_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_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);
+
+ /* 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);
+
+ /* 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 = (ca + 1);
+ if (i == 'A') r_ptr->x_attr = (ca - 1);
+ if (i == 'c') r_ptr->x_char = (cc + 1);
+ if (i == 'C') r_ptr->x_char = (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 = k_ptr->d_attr;
+ char dc = k_ptr->d_char;
+ byte ca = k_ptr->x_attr;
+ char cc = k_ptr->x_char;
+
+ /* Label the object */
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ format("Object = %d, Name = %-40.40s",
+ k, 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);
+
+ /* 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);
+
+ /* 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 = (ca + 1);
+ if (i == 'A') k_info[k].x_attr = (ca - 1);
+ if (i == 'c') k_info[k].x_char = (cc + 1);
+ if (i == 'C') k_info[k].x_char = (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 = f_ptr->d_attr;
+ char dc = f_ptr->d_char;
+ byte ca = f_ptr->x_attr;
+ char cc = f_ptr->x_char;
+
+ /* Label the object */
+ Term_putstr(5, 17, -1, TERM_WHITE,
+ format("Terrain = %d, Name = %-40.40s",
+ f, 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);
+
+ /* 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);
+
+ /* 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 = (ca + 1);
+ if (i == 'A') f_info[f].x_attr = (ca - 1);
+ if (i == 'c') f_info[f].x_char = (cc + 1);
+ if (i == 'C') f_info[f].x_char = (cc - 1);
+ if (i == 'd')
+ {
+ f_info[f].x_char = f_ptr->d_char;
+ f_info[f].x_attr = f_ptr->d_attr;
+ }
+ }
+ }
+
+ /* Reset visuals */
+ else if (i == '0')
+ {
+ /* Reset */
+ reset_visuals();
+
+ /* Message */
+ msg_print("Visual attr/char tables reset.");
+ }
+
+ /* Unknown option */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Interact with "colors"
+ */
+void do_cmd_colors(void)
+{
+ int i;
+
+ FILE *fff;
+
+ char tmp[160];
+
+ char buf[1024];
+
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Interact until done */
+ while (1)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Ask for a choice */
+ prt("Interact with Colors", 2, 0);
+
+ /* Give some choices */
+ prt("(1) Load a user pref file", 4, 5);
+ prt("(2) Dump colors", 5, 5);
+ prt("(3) Modify colors", 6, 5);
+
+ /* Prompt */
+ prt("Command: ", 8, 0);
+
+ /* Prompt */
+ i = inkey();
+
+ /* Done */
+ if (i == ESCAPE) break;
+
+ /* Load a 'pref' file */
+ if (i == '1')
+ {
+ /* Prompt */
+ prt("Command: Load a user pref file", 8, 0);
+
+ /* Prompt */
+ prt("File: ", 10, 0);
+
+ /* Default file */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Query */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Process the given filename */
+ (void)process_pref_file(tmp);
+
+ /* Mega-Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Mega-Hack -- redraw */
+ Term_redraw();
+ }
+
+ /* Dump colors */
+ else if (i == '2')
+ {
+ /* Prompt */
+ prt("Command: Dump colors", 8, 0);
+
+ /* Prompt */
+ prt("File: ", 10, 0);
+
+ /* Default filename */
+ strnfmt(tmp, 160, "user-%s.prf", ANGBAND_SYS);
+
+ /* Get a filename */
+ if (!askfor_aux(tmp, 70)) continue;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, tmp);
+
+ /* Append to the file */
+ fff = my_fopen(buf, "a");
+
+ /* Failure */
+ if (!fff) continue;
+
+ /* Start dumping */
+ fprintf(fff, "\n\n");
+ fprintf(fff, "# Color redefinitions\n\n");
+
+ /* Dump colors */
+ for (i = 0; i < 256; i++)
+ {
+ int kv = angband_color_table[i][0];
+ int rv = angband_color_table[i][1];
+ int gv = angband_color_table[i][2];
+ int bv = angband_color_table[i][3];
+
+ cptr name = "unknown";
+
+ /* Skip non-entries */
+ if (!kv && !rv && !gv && !bv) continue;
+
+ /* Extract the color name */
+ if (i < 16) name = color_names[i];
+
+ /* Dump a comment */
+ fprintf(fff, "# Color '%s'\n", name);
+
+ /* Dump the monster attr/char info */
+ fprintf(fff, "V:%d:0x%02X:0x%02X:0x%02X:0x%02X\n\n",
+ i, kv, rv, gv, bv);
+ }
+
+ /* All done */
+ fprintf(fff, "\n\n\n\n");
+
+ /* Close */
+ my_fclose(fff);
+
+ /* Message */
+ msg_print("Dumped color redefinitions.");
+ }
+
+ /* Edit colors */
+ else if (i == '3')
+ {
+ static byte a = 0;
+
+ /* Prompt */
+ prt("Command: Modify colors", 8, 0);
+
+ /* Hack -- query until done */
+ while (1)
+ {
+ cptr name;
+
+ /* Clear */
+ clear_from(10);
+
+ /* Exhibit the normal colors */
+ for (i = 0; i < 16; i++)
+ {
+ /* Exhibit this color */
+ Term_putstr(i*4, 20, -1, a, "###");
+
+ /* Exhibit all colors */
+ Term_putstr(i*4, 22, -1, i, format("%3d", i));
+ }
+
+ /* Describe the color */
+ name = ((a < 16) ? color_names[a] : "undefined");
+
+ /* Describe the color */
+ Term_putstr(5, 10, -1, TERM_WHITE,
+ format("Color = %d, Name = %s", a, name));
+
+ /* Label the Current values */
+ Term_putstr(5, 12, -1, TERM_WHITE,
+ format("K = 0x%02x / R,G,B = 0x%02x,0x%02x,0x%02x",
+ angband_color_table[a][0],
+ angband_color_table[a][1],
+ angband_color_table[a][2],
+ angband_color_table[a][3]));
+
+ /* Prompt */
+ Term_putstr(0, 14, -1, TERM_WHITE,
+ "Command (n/N/k/K/r/R/g/G/b/B): ");
+
+ /* Get a command */
+ i = inkey();
+
+ /* All done */
+ if (i == ESCAPE) break;
+
+ /* Analyze */
+ if (i == 'n') a = (a + 1);
+ if (i == 'N') a = (a - 1);
+ if (i == 'k') angband_color_table[a][0] = (angband_color_table[a][0] + 1);
+ if (i == 'K') angband_color_table[a][0] = (angband_color_table[a][0] - 1);
+ if (i == 'r') angband_color_table[a][1] = (angband_color_table[a][1] + 1);
+ if (i == 'R') angband_color_table[a][1] = (angband_color_table[a][1] - 1);
+ if (i == 'g') angband_color_table[a][2] = (angband_color_table[a][2] + 1);
+ if (i == 'G') angband_color_table[a][2] = (angband_color_table[a][2] - 1);
+ if (i == 'b') angband_color_table[a][3] = (angband_color_table[a][3] + 1);
+ if (i == 'B') angband_color_table[a][3] = (angband_color_table[a][3] - 1);
+
+ /* Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Hack -- redraw */
+ Term_redraw();
+ }
+ }
+
+ /* Unknown option */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+/*
+ * Take notes. There are two ways this can happen, either in the message
+ * recall or a file.
+ */
+void do_cmd_note(void)
+{
+ char buf[80];
+
+
+ /* Default */
+ strcpy(buf, "");
+
+ if (!get_string("Note: ", buf, 60)) return;
+
+ /* Ignore empty notes */
+ if (!buf[0] || (buf[0] == ' ')) return;
+
+ /* Add note to file */
+ add_note(buf, ' ');
+}
+
+
+/*
+ * Mention the current version
+ */
+void do_cmd_version(void)
+{
+ /* Silly message */
+ msg_format("You are playing %s made by %s (%s).",
+ get_version_string(),
+ modules[game_module_idx].meta.author.name,
+ modules[game_module_idx].meta.author.email);
+}
+
+
+
+/*
+ * 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_new(HOOK_FEELING, NULL, NULL))
+ {
+ return;
+ }
+
+ /* No useful feeling in special levels */
+ if (dungeon_flags2 & DF2_DESC)
+ {
+ char buf[1024];
+
+ if ((get_dungeon_save(buf)) || (generate_special_feeling) || (dungeon_flags2 & DF2_DESC_ALWAYS))
+ {
+ if (!get_level_desc(buf)) msg_print("Someone forgot to describe this level!");
+ else msg_print(buf);
+ return;
+ }
+ }
+
+ /* No useful feeling in quests */
+ if (p_ptr->inside_quest)
+ {
+ msg_print("Looks like a typical quest level.");
+ return;
+ }
+
+ /* Display feelings in the dungeon, nothing on the surface */
+ if (dun_level)
+ {
+ /* This could be simplified with a correct p_ptr->town_num */
+ int i, town_level = 0;
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+ if (town_level)
+ msg_print("You hear the sound of a market.");
+ else
+ msg_print(do_cmd_feeling_text[feeling]);
+ }
+ return;
+}
+
+
+
+/*
+ * Encode the screen colors
+ */
+static char hack[17] = "dwsorgbuDWvyRGBU";
+
+
+/*
+ * Hack -- load a screen dump from a file
+ */
+void do_cmd_load_screen(void)
+{
+ int i, y, x;
+
+ int wid, hgt;
+ int len;
+
+ byte a = 0;
+ char c = ' ';
+
+ bool_ okay = TRUE;
+
+ FILE *fff;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, "dump.txt");
+
+ /* Append to the file */
+ fff = my_fopen(buf, "r");
+
+ /* Oops */
+ if (!fff) return;
+
+
+ /* Retrieve the current screen size */
+ Term_get_size(&wid, &hgt);
+
+ /* Enter "icky" mode */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Clear the screen */
+ Term_clear();
+
+
+ /* Load the screen */
+ for (y = 0; okay; y++)
+ {
+ /* Get a line of data */
+ if (my_fgets(fff, buf, 1024)) okay = FALSE;
+
+ /* Stop on blank line */
+ if (!buf[0]) break;
+
+ /* Ignore off screen lines */
+ if (y >= hgt) continue;
+
+ /* Get width */
+ len = strlen(buf);
+
+ /* Truncate if it's longer than current screen width */
+ if (len > wid) len = wid;
+
+ /* Show each row */
+ for (x = 0; x < len; x++)
+ {
+ /* Put the attr/char */
+ Term_draw(x, y, TERM_WHITE, buf[x]);
+ }
+ }
+
+ /* Dump the screen */
+ for (y = 0; okay; y++)
+ {
+ /* Get a line of data */
+ if (my_fgets(fff, buf, 1024)) okay = FALSE;
+
+ /* Stop on blank line */
+ if (!buf[0]) break;
+
+ /* Ignore off screen lines */
+ if (y >= hgt) continue;
+
+ /* Get width */
+ len = strlen(buf);
+
+ /* Truncate if it's longer than current screen width */
+ if (len > wid) len = wid;
+
+ /* Dump each row */
+ for (x = 0; x < len; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Look up the attr */
+ for (i = 0; i < 16; i++)
+ {
+ /* Use attr matches */
+ if (hack[i] == buf[x]) a = i;
+ }
+
+ /* Put the attr/char */
+ Term_draw(x, y, a, c);
+ }
+ }
+
+
+ /* Close it */
+ my_fclose(fff);
+
+
+ /* Message */
+ msg_print("Screen dump loaded.");
+ msg_print(NULL);
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
+
+
+
+/*
+ * Hack -- save a screen dump to a file
+ */
+void do_cmd_save_screen(void)
+{
+ 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");
+
+ /* 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];
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Scan the artifacts */
+ std::unique_ptr<bool_[]> okay(new bool_[max_a_idx]);
+ 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;
+ }
+
+ std::unique_ptr<bool_[]> okayk(new bool_[max_k_idx]);
+ 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];
+
+ /* Scan all objects in the grid */
+ for (auto const this_o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type const * o_ptr = &o_list[this_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++)
+ {
+ /* Scan all objects the monster carries */
+ for (auto const this_o_idx: m_list[i].hold_o_idxs)
+ {
+ object_type * o_ptr;
+
+ /* Acquire object */
+ o_ptr = &o_list[this_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);
+}
+
+
+/*
+ * 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_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);
+}
+
+
+static int monster_get_race_level(int r_idx) {
+ /* Hack -- Morgoth is always last */
+ if (r_idx == 862) {
+ return 20000;
+ }
+ /* Otherwise, we'll use the real level. */
+ return r_info[r_idx].level;
+}
+
+static bool compare_monster_level(int r_idx1, int r_idx2) {
+ return monster_get_race_level(r_idx1) < monster_get_race_level(r_idx2);
+}
+
+/*
+ * Display known uniques
+ *
+ * Note that the player ghosts are ignored. XXX XXX XXX
+ */
+static void do_cmd_knowledge_uniques(void)
+{
+ int k;
+
+ 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");
+
+ // Extract the unique race indexes.
+ std::vector<int> unique_r_idxs;
+ 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))
+ {
+ unique_r_idxs.push_back(k);
+ }
+ }
+
+ // Sort races by level.
+ std::sort(std::begin(unique_r_idxs),
+ std::end(unique_r_idxs),
+ compare_monster_level);
+
+ /* Scan the monster races */
+ for (int r_idx : unique_r_idxs)
+ {
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* 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)
+ {
+ fprintf(fff, "[[[[[%c%c] [[[[[R%-68s is dead]\n",
+ conv_color[r_ptr->d_attr],
+ r_ptr->d_char,
+ r_ptr->name);
+ }
+ else
+ {
+ fprintf(fff, "[[[[[%c%c] [[[[[w%-68s is alive]\n",
+ conv_color[r_ptr->d_attr],
+ r_ptr->d_char,
+ r_ptr->name);
+ }
+ }
+ }
+ }
+
+ /* 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);
+}
+
+
+static void plural_aux(char *name)
+{
+ int name_len = strlen(name);
+
+ /* Hack -- Precedent must be pluralised for this one */
+ if (strstr(name, "Disembodied hand"))
+ {
+ strcpy(name, "Disembodied hands that strangled people");
+ }
+
+ /* "someone of something" */
+ else if (strstr(name, " of "))
+ {
+ cptr aider = strstr(name, " of ");
+ char dummy[80];
+ int i = 0;
+ cptr ctr = name;
+
+ while (ctr < aider)
+ {
+ dummy[i] = *ctr;
+ ctr++;
+ i++;
+ }
+
+ if (dummy[i - 1] == 's')
+ {
+ strcpy(&dummy[i], "es");
+ i++;
+ }
+ else
+ {
+ strcpy(&dummy[i], "s");
+ }
+
+ strcpy(&dummy[i + 1], aider);
+ strcpy(name, dummy);
+ }
+
+ /* Creeping coins */
+ else if (strstr(name, "coins"))
+ {
+ char dummy[80];
+ strcpy(dummy, "piles of ");
+ strcat(dummy, name);
+ strcpy(name, dummy);
+ return;
+ }
+
+ /* Manes stay manes */
+ else if (strstr(name, "Manes"))
+ {
+ return;
+ }
+
+ /* Broken plurals are, well, broken */
+ else if (name[name_len - 1] == 'y')
+ {
+ strcpy(&name[name_len - 1], "ies");
+ }
+ else if (streq(&name[name_len - 4], "ouse"))
+ {
+ strcpy(&name[name_len - 4], "ice");
+ }
+ else if (streq(&name[name_len - 6], "kelman"))
+ {
+ strcpy(&name[name_len - 6], "kelmen");
+ }
+ else if (streq(&name[name_len - 2], "ex"))
+ {
+ strcpy(&name[name_len - 2], "ices");
+ }
+ else if (streq(&name[name_len - 3], "olf"))
+ {
+ strcpy(&name[name_len - 3], "olves");
+ }
+
+ /* Now begins sane cases */
+ else if ((streq(&name[name_len - 2], "ch")) || (name[name_len - 1] == 's'))
+ {
+ strcpy(&name[name_len], "es");
+ }
+ else
+ {
+ strcpy(&name[name_len], "s");
+ }
+}
+
+
+/*
+ * Display current pets
+ */
+static void do_cmd_knowledge_pets(void)
+{
+ int i;
+
+ FILE *fff;
+
+ monster_type *m_ptr;
+
+ int t_friends = 0;
+
+ int t_levels = 0;
+
+ int show_upkeep = 0;
+
+ int upkeep_divider = 20;
+
+ char file_name[1024];
+
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ if (has_ability(AB_PERFECT_CASTING)) upkeep_divider = 15;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Calculate "upkeep" for friendly monsters */
+ if (m_ptr->status >= MSTATUS_PET)
+ {
+ auto const r_ptr = m_ptr->race();
+
+ t_friends++;
+ t_levels += m_ptr->level;
+
+ char pet_name[80];
+ 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 " FMTs32b " 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_ptr->name);
+ Total++;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+
+ if (This > 0)
+ {
+ if (This < 2)
+ {
+ if (strstr(r_ptr->name, "coins"))
+ {
+ fprintf(fff, " 1 pile of %s\n", r_ptr->name);
+ }
+ else
+ {
+ fprintf(fff, " 1 %s\n", r_ptr->name);
+ }
+ }
+ else
+ {
+ char to_plural[80];
+ strcpy(to_plural, r_ptr->name);
+ plural_aux(to_plural);
+ fprintf(fff, " %d %s\n", This, to_plural);
+ }
+
+ Total += This;
+ }
+ }
+ }
+
+ fprintf(fff, "----------------------------------------------\n");
+ fprintf(fff, " Total: " FMTs32b " 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_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_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
+ */
+static 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, FALSE);
+ }
+
+ /* 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[MAX_Q_IDX] = { };
+
+ 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");
+
+ 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 descriptions */
+ if (quest[i].gen_desc != NULL)
+ {
+ if (!quest[i].gen_desc(fff))
+ {
+ continue;
+ }
+ }
+
+ /* 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");
+ }
+ }
+ }
+
+ /* 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;
+
+
+ /* 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);
+ 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':
+ {
+ do_cmd_knowledge_notes();
+
+ 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)
+{
+ /* 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 hour = bst(HOUR, turn);
+
+ int min = bst(MINUTE, turn);
+
+ int full = hour * 100 + min;
+
+ 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.");
+
+ /* Display day */
+ u32b days = bst(DAY, turn) + 1;
+ msg_format("This is the %s day of your adventure.",
+ get_day(days));
+
+ /* 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
+ */
+
+std::string *macro_recorder_current = nullptr;
+
+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.");
+ assert (macro_recorder_current == nullptr);
+ macro_recorder_current = new std::string();
+}
+
+void macro_recorder_add(char c)
+{
+ // Gets called unconditionally for all input, so ignore unless
+ // we're actual recording.
+ if (macro_recorder_current) {
+ macro_recorder_current->push_back(c);
+ }
+}
+
+void macro_recorder_stop()
+{
+ assert(macro_recorder_current != nullptr);
+
+ // Remove the last key, because it is the key to stop recording
+ macro_recorder_current->pop_back();
+
+ // Copy out current macro text.
+ std::string macro(*macro_recorder_current);
+
+ // Stop recording.
+ delete macro_recorder_current;
+ macro_recorder_current = nullptr;
+
+ /* Add it */
+ if (get_check("Are you satisfied and want to create the macro? "))
+ {
+ char buf[1024];
+
+ prt("Trigger: ", 0, 0);
+
+ /* Get a macro trigger */
+ do_cmd_macro_aux(buf, FALSE);
+
+ /* Link the macro */
+ macro_add(buf, macro.c_str());
+
+ /* Prompt */
+ std::unique_ptr<char[]> str(new char[(macro.length() + 1) * 3]);
+ str[0] = '\0';
+ ascii_to_text(str.get(), macro.c_str());
+ msg_format("Added a macro '%s'. If you want it to stay permanently, press @ now and dump macros to a file.", str.get());
+ }
+}
+
+void do_cmd_macro_recorder()
+{
+ if (macro_recorder_current == NULL)
+ macro_recorder_start();
+ else
+ macro_recorder_stop();
+}
diff --git a/src/cmd4.hpp b/src/cmd4.hpp
new file mode 100644
index 00000000..4470c94f
--- /dev/null
+++ b/src/cmd4.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "h-basic.h"
+
+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 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);
diff --git a/src/cmd5.cc b/src/cmd5.cc
new file mode 100644
index 00000000..a1dd5cbf
--- /dev/null
+++ b/src/cmd5.cc
@@ -0,0 +1,2214 @@
+/*
+ * 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 "cmd5.hpp"
+
+#include "birth.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "corrupt.hpp"
+#include "lua_bind.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_class.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_type.hpp"
+#include "school_book.hpp"
+#include "skills.hpp"
+#include "spell_type.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "spells4.hpp"
+#include "spells5.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "quark.hpp"
+#include "wizard2.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <boost/noncopyable.hpp>
+#include <boost/optional.hpp>
+#include <cassert>
+
+/* Maximum number of tries for teleporting */
+#define MAX_TRIES 300
+
+static object_filter_t const &is_school_book()
+{
+ using namespace object_filter;
+ static auto instance = Or(
+ TVal(TV_BOOK),
+ TVal(TV_DAEMON_BOOK),
+ TVal(TV_INSTRUMENT));
+ return instance;
+}
+
+/* Does it contains a schooled spell ? */
+static object_filter_t const &hook_school_spellable()
+{
+ using namespace object_filter;
+ static auto has_pval2 =
+ [=](object_type const *o_ptr) -> bool {
+ return (o_ptr->pval2 != -1);
+ };
+ static auto instance = Or(
+ is_school_book(),
+ And(
+ HasFlag5(TR5_SPELL_CONTAIN),
+ has_pval2));
+ return instance;
+}
+
+/* Is it a browsable for spells? */
+static object_filter_t const &item_tester_hook_browsable()
+{
+ using namespace object_filter;
+ static auto instance = Or(
+ hook_school_spellable(),
+ TVal(TV_BOOK));
+ return instance;
+}
+
+/*
+ * 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);
+}
+
+
+static int print_book(s16b sval, s32b spell_idx, object_type *obj)
+{
+ int y = 2;
+ int i;
+
+ random_book_setup(sval, spell_idx);
+
+ school_book *school_book = school_books_at(sval);
+
+ /* Parse all spells */
+ i = 0;
+ for (auto spell_idx : school_book->spell_idxs)
+ {
+ byte color = TERM_L_DARK;
+ bool_ is_ok;
+ char label[8];
+
+ is_ok = is_ok_spell(spell_idx, obj->pval);
+ if (is_ok)
+ {
+ color = (get_mana(spell_idx) > get_power(spell_idx)) ? TERM_ORANGE : TERM_L_GREEN;
+ }
+
+ sprintf(label, "%c) ", 'a' + i);
+
+ y = print_spell(label, color, y, spell_idx);
+ i++;
+ }
+
+ prt(format(" %-20s%-16s Level Cost Fail Info", "Name", "School"), 1, 0);
+ return y;
+}
+
+
+
+static void browse_school_spell(int book, int spell_idx, object_type *o_ptr)
+{
+ int i;
+ int num = 0;
+ int ask;
+ char choice;
+ char out_val[160];
+
+ /* Show choices */
+ window_stuff();
+
+ num = school_book_length(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 */
+ print_book(book, spell_idx, o_ptr);
+
+ /* Get a spell from the user */
+ while (get_com(out_val, &choice))
+ {
+ /* Display a list of spells */
+ print_book(book, spell_idx, o_ptr);
+
+ /* 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 */
+ auto where = print_book(book, spell_idx, o_ptr);
+ print_spell_desc(spell_x(book, spell_idx, i), where);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+ /* Show choices */
+ window_stuff();
+}
+
+
+/*
+ * 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)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Browse which book? ",
+ "You have no books that you can read.",
+ (USE_INVEN | USE_EQUIP | USE_FLOOR),
+ item_tester_hook_browsable()))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ do_cmd_browse_aux(o_ptr);
+}
+
+static void do_poly_wounds()
+{
+ /* 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();
+ }
+
+ /*
+ * 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(*race_info[new_race].title)) ? "n" : ""),
+ race_info[new_race].title);
+ }
+ 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_FRAME);
+
+ 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;
+ gain_random_corruption();
+ }
+
+ 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--;
+ }
+}
+
+/*
+ * Fetch an item (teleport it right underneath the caster)
+ */
+void fetch(int dir, int wgt, bool_ require_los)
+{
+ /* Check to see if an object is already there */
+ if (!cave[p_ptr->py][p_ptr->px].o_idxs.empty())
+ {
+ msg_print("You can't fetch when you're already standing on something.");
+ return;
+ }
+
+ /* Use a target */
+ cave_type *c_ptr = nullptr;
+ if ((dir == 5) && target_okay())
+ {
+ int tx = target_col;
+ int 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_idxs.empty())
+ {
+ 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 */
+ int ty = p_ptr->py; /* Where to drop the item */
+ int tx = p_ptr->px;
+
+ while (1)
+ {
+ ty += ddy[dir];
+ tx += ddx[dir];
+ c_ptr = &cave[ty][tx];
+
+ if ((distance(p_ptr->py, p_ptr->px, ty, tx) > MAX_RANGE) ||
+ !cave_floor_bold(ty, tx)) return;
+
+ if (!c_ptr->o_idxs.empty()) break;
+ }
+ }
+
+ assert(c_ptr != nullptr);
+ assert(!c_ptr->o_idxs.empty());
+
+ /* Pick object from the list */
+ auto o_idx = c_ptr->o_idxs.front();
+
+ object_type *o_ptr = &o_list[o_idx];
+ if (o_ptr->weight > wgt)
+ {
+ /* Too heavy to 'fetch' */
+ msg_print("The object is too heavy.");
+ return;
+ }
+
+ /* Move the object between the lists */
+ c_ptr->o_idxs.erase(c_ptr->o_idxs.begin()); // Remove
+ cave[p_ptr->py][p_ptr->px].o_idxs.push_back(o_idx); // Add
+
+ /* Update object's location */
+ o_ptr->iy = p_ptr->py;
+ o_ptr->ix = p_ptr->px;
+
+ /* Feedback */
+ char o_name[80];
+ 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;
+ }
+ }
+}
+
+/*
+ * 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_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_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;
+
+ 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;
+
+ /* 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, ESC=exit) Use which power of your %s? ",
+ label, (no_cost ? "symbiote" : "body"));
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Get a spell from the user */
+ while (!flag)
+ {
+ /* Show the list */
+ {
+ byte y = 1, x = 0;
+ int ctr = 0;
+ char dummy[80];
+
+ strcpy(dummy, "");
+
+ 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);
+ }
+ }
+
+ if (!get_com(out_val, &choice))
+ {
+ flag = FALSE;
+ break;
+ }
+
+ 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 */
+ 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_FRAME);
+
+ /* 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 s32b hack_force_spell_pval = -1;
+
+boost::optional<int> get_item_hook_find_spell(object_filter_t const &)
+{
+ char buf[80];
+ strcpy(buf, "Manathrust");
+ if (!get_string("Spell name? ", buf, 79))
+ {
+ return boost::none;
+ }
+
+ int const spell = find_spell(buf);
+ if (spell == -1)
+ {
+ return boost::none;
+ }
+
+ for (int i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Extract object flags */
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Must we wield it to cast from it? */
+ 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))
+ {
+ /* Does it contain the appropriate spell? */
+ if ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == spell))
+ {
+ hack_force_spell = spell;
+ hack_force_spell_pval = o_ptr->pval;
+ return i;
+ }
+ }
+ /* A random book ? */
+ else if (school_book_contains_spell(o_ptr->sval, spell))
+ {
+ hack_force_spell = spell;
+ hack_force_spell_pval = o_ptr->pval;
+ return i;
+ }
+ }
+
+ return boost::none;
+}
+
+/*
+ * Is the spell castable?
+ */
+bool_ is_ok_spell(s32b spell_idx, s32b pval)
+{
+ spell_type *spell = spell_at(spell_idx);
+
+ // Calculate availability based on caster's skill level.
+ s32b level;
+ bool_ na;
+ get_level_school(spell, 50, 0, &level, &na);
+ if (na || (level == 0))
+ {
+ return FALSE;
+ }
+ // Are we permitted to cast based on item pval? Only music
+ // spells have non-zero minimum PVAL.
+ if (pval < spell_type_minimum_pval(spell))
+ {
+ return FALSE;
+ }
+ // OK, we're permitted to cast it.
+ return TRUE;
+}
+
+
+/*
+ * Get a spell from a book
+ */
+s32b get_school_spell(cptr do_what, s16b force_book)
+{
+ int i, item;
+ s32b spell = -1;
+ int num = 0;
+ s32b where = 1;
+ int ask;
+ bool_ flag;
+ char out_val[160];
+ object_type *o_ptr, forge;
+ int tmp;
+ int sval, pval;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ hack_force_spell = -1;
+ hack_force_spell_pval = -1;
+
+ /* Ok do we need to ask for a book ? */
+ if (!force_book)
+ {
+ char buf2[40];
+ char buf3[40];
+ 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,
+ hook_school_spellable(),
+ get_item_hook_find_spell))
+ {
+ return -1;
+ }
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* If it can be wielded, it must */
+ if ((wield_slot(o_ptr) != -1) && (item < INVEN_WIELD) && (f5 & TR5_WIELD_CAST))
+ {
+ msg_format("You cannot %s from that object; it must be wielded first.", do_what);
+ return -1;
+ }
+ }
+ else
+ {
+ o_ptr = &forge;
+ o_ptr->tval = TV_BOOK;
+ o_ptr->sval = force_book;
+ o_ptr->pval = 0;
+ }
+
+ if (repeat_pull(&tmp))
+ {
+ return tmp;
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* Show choices */
+ 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;
+ }
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ /* Go */
+ if (hack_force_spell == -1)
+ {
+ num = school_book_length(sval);
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78, "(Spells %c-%c, Descs %c-%c, 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)
+ {
+ char choice;
+
+ /* Restore and save screen; this prevents
+ subprompt from leaving garbage when going
+ around the loop multiple times. */
+ Term_load();
+ Term_save();
+
+ /* Display a list of spells */
+ where = print_book(sval, pval, o_ptr);
+
+ /* Input */
+ if (!get_com(out_val, &choice))
+ {
+ flag = FALSE;
+ break;
+ }
+
+ /* 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)
+ {
+ /* Display a list of spells */
+ where = print_book(sval, pval, o_ptr);
+ print_spell_desc(spell_x(sval, pval, i), where);
+ }
+ else
+ {
+ bool_ ok;
+
+ /* Save the spell index */
+ spell = spell_x(sval, pval, i);
+
+ /* Do we need to do some pre test */
+ ok = is_ok_spell(spell, o_ptr->pval);
+
+ /* 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
+ {
+ bool_ ok;
+
+ /* Require "okay" spells */
+ ok = is_ok_spell(hack_force_spell, hack_force_spell_pval);
+ 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 */
+ Term_load();
+ character_icky = FALSE;
+
+
+ /* Show choices */
+ 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", 0);
+
+ /* Actualy cast the choice */
+ if (spell != -1)
+ {
+ lua_cast_school_spell(spell, FALSE);
+ }
+}
+
+/* Can it contains a schooled spell ? */
+static bool hook_school_can_spellable(object_type const *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ return ((f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 == -1));
+}
+
+/*
+ * Copy a spell from a bok to an object
+ */
+void do_cmd_copy_spell()
+{
+ int spell = get_school_spell("copy", 0);
+ int item;
+
+ if (spell == -1) return;
+
+ /* Spells that cannot be randomly created cannot be copied */
+ if (spell_type_random_type(spell_at(spell)) <= 0)
+ {
+ msg_print("This spell cannot be copied.");
+ return;
+ }
+
+ if (!get_item(&item,
+ "Copy to which object? ",
+ "You have no object to copy to.",
+ (USE_INVEN | USE_EQUIP),
+ hook_school_can_spellable)) return;
+ object_type *o_ptr = get_object(item);
+
+ msg_print("You copy the spell!");
+ o_ptr->pval2 = spell;
+ inven_item_describe(item);
+}
diff --git a/src/cmd5.hpp b/src/cmd5.hpp
new file mode 100644
index 00000000..1b3b062a
--- /dev/null
+++ b/src/cmd5.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type_fwd.hpp"
+
+extern bool_ is_magestaff(void);
+extern void do_cmd_browse_aux(object_type *o_ptr);
+extern void do_cmd_browse(void);
+extern void fetch(int dir, int wgt, bool_ require_los);
+extern void do_poly_self(void);
+extern cptr symbiote_name(bool_ capitalize);
+extern int use_symbiotic_power(int r_idx, bool_ great, bool_ only_number, bool_ no_cost);
+extern bool_ is_ok_spell(s32b spell_idx, s32b pval);
+extern s32b get_school_spell(cptr do_what, s16b force_book);
+extern void do_cmd_copy_spell(void);
+extern void cast_school_spell(void);
diff --git a/src/cmd6.cc b/src/cmd6.cc
new file mode 100644
index 00000000..0a5595fa
--- /dev/null
+++ b/src/cmd6.cc
@@ -0,0 +1,7928 @@
+/*
+ * 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 "cmd6.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd1.hpp"
+#include "cmd7.hpp"
+#include "corrupt.hpp"
+#include "dungeon_info_type.hpp"
+#include "ego_item_type.hpp"
+#include "files.hpp"
+#include "hook_eat_in.hpp"
+#include "hook_eat_out.hpp"
+#include "hooks.hpp"
+#include "lua_bind.hpp"
+#include "mimic.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "randart.hpp"
+#include "skills.hpp"
+#include "spell_type.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "spells5.hpp"
+#include "stats.hpp"
+#include "store.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.h"
+#include "variable.hpp"
+#include "wild.hpp"
+#include "wizard2.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <cassert>
+
+using boost::algorithm::iequals;
+
+/*
+ * Forward declare
+ */
+static bool_ activate_spell(object_type * o_ptr, byte choice);
+
+
+/*
+ * General function to find an item by its name
+ */
+static select_by_name_t select_object_by_name(std::string const &prompt)
+{
+ return [=](object_filter_t const &filter) -> boost::optional<int> {
+ // Ask for the name of the object we want to select
+ char buf[80] = "";
+ if (!get_string(prompt.c_str(), buf, 79))
+ {
+ return boost::none;
+ }
+ // Named objects must be in the inventory
+ for (size_t i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = get_object(i);
+ // Must have an actual item in the slot
+ if (!o_ptr->k_idx)
+ {
+ continue;
+ }
+ // Must pass the filter
+ if (!filter(o_ptr))
+ {
+ continue;
+ }
+ // Check against the name of the object
+ // ignoring case.
+ char buf2[100];
+ object_desc(buf2, o_ptr, -1, 0);
+ if (iequals(buf, buf2))
+ {
+ return i;
+ }
+ }
+ // No match
+ return boost::none;
+ };
+}
+
+
+/*
+ * 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(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 object_filter_t const &item_tester_hook_eatable()
+{
+ using namespace object_filter;
+ static auto instance =
+ Or(
+ TVal(TV_FOOD),
+ TVal(TV_CORPSE));
+ return instance;
+}
+
+
+/*
+ * Eat some food (from the pack or floor)
+ */
+void do_cmd_eat_food(void)
+{
+ int ident, lev, fval = 0;
+
+ object_type *q_ptr, forge;
+
+ monster_race *r_ptr;
+
+ bool_ destroy = TRUE;
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Eat which item? ",
+ "You have nothing to eat.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_eatable(),
+ select_object_by_name("Food full name? ")))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Sound */
+ sound(SOUND_EAT);
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Identity not known yet */
+ ident = FALSE;
+
+ /* Object level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Scripted foods */
+ hook_eat_in in = { o_ptr };
+ hook_eat_out out = { FALSE };
+ if (process_hooks_new(HOOK_EAT, &in, &out))
+ {
+ ident = out.ident;
+ }
+ /* (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(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))
+ {
+ p_ptr->powers_mod[PWR_GROW_MOLD] = TRUE;
+ 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 ((race_flags1_p(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 (race_flags1_p(PR1_NO_FOOD))
+ {
+ if (race_flags1_p(PR1_UNDEAD))
+ {
+ msg_print("The food of mortals is poor sustenance for you.");
+ }
+ else
+ {
+ msg_print("Food is poor sustenance for you.");
+ }
+ set_food(p_ptr->food + ((fval) / 40));
+ }
+
+ /* Those living in fresh */
+ else
+ {
+ (void)set_food(p_ptr->food + fval);
+ }
+
+
+ /* Destroy food? */
+ if (destroy)
+ {
+ inc_stack_size(item, -1);
+ }
+}
+
+
+/*
+ * Cut a corpse up for convenient storage
+ */
+void do_cmd_cut_corpse(void)
+{
+ int item, meat = 0, not_meat = 0;
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Hack up which corpse? ",
+ "You have no corpses.",
+ (USE_INVEN | USE_FLOOR),
+ object_filter::TVal(TV_CORPSE)))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ monster_race *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 */
+ object_type object_type_body;
+ object_type *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 *i_ptr;
+
+ /* Get some meat */
+ if (!get_item(&item,
+ "Cure which meat? ",
+ "You have no meat to cure.",
+ (USE_INVEN | USE_FLOOR),
+ object_filter::And(item_tester_hook_eatable(), object_filter::TVal(TV_CORPSE))))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Get a potion */
+ if (!get_item(&item,
+ "Use which potion? ",
+ "You have no potions to use.",
+ (USE_INVEN | USE_FLOOR),
+ object_filter::TVal(TV_POTION))) return;
+
+ /* Get the item */
+ i_ptr = get_object(item);
+
+ if (i_ptr->number > 1)
+ {
+ /* Get a number */
+ get_count(1, i_ptr->number);
+
+ /* Save it */
+ num = command_arg;
+ }
+ else
+ {
+ num = 1;
+ }
+
+ if (num == 0) return;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ cptr q = "You soak the meat.";
+ cptr s = "You soak the meat.";
+
+ switch (i_ptr->sval)
+ {
+ case SV_POTION_SALT_WATER:
+ {
+ q = "You salt the meat.";
+ cure = 200 * num;
+
+ break;
+ }
+
+ case SV_POTION_POISON:
+ {
+ q = "You poison the meat.";
+ cure = 0;
+ o_ptr->pval /= 2;
+ if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight;
+
+ break;
+ }
+
+ case SV_POTION_CONFUSION:
+ {
+ cure = 80 * num;
+
+ break;
+ }
+
+ case SV_POTION_SLOW_POISON:
+ {
+ cure = 20 * num;
+
+ break;
+ }
+
+ case SV_POTION_CURE_POISON:
+ {
+ cure = 45 * num;
+
+ break;
+ }
+
+ case SV_POTION_DEATH:
+ {
+ q = "You ruin the meat.";
+ cure = 0;
+ o_ptr->pval /= 10;
+ if (o_ptr->pval > o_ptr->weight) o_ptr->pval = o_ptr->weight / 2;
+
+ break;
+ }
+
+ default:
+ {
+ cure = 0;
+
+ break;
+ }
+ }
+
+ /* Message */
+ if (object_known_p(i_ptr))
+ {
+ msg_print(q);
+ }
+ else
+ {
+ msg_print(s);
+ }
+
+ /* The meat is already spoiling */
+ if (((o_ptr->sval == SV_CORPSE_MEAT) && (o_ptr->weight > o_ptr->pval)) ||
+ (o_ptr->weight - o_ptr->pval > 10))
+ {
+ cure = (cure * o_ptr->pval) / (o_ptr->weight * 20);
+ }
+
+ /* Cure the meat */
+ o_ptr->timeout += cure / o_ptr->number;
+
+ if (o_ptr->timeout > o_ptr->pval) o_ptr->timeout = o_ptr->pval;
+
+ /* Use up the potions */
+ inc_stack_size(item, -num);
+}
+
+
+/*
+ * Hook to determine if an object is quaffable
+ */
+static object_filter_t const &item_tester_hook_quaffable()
+{
+ using namespace object_filter;
+ static auto instance = Or(
+ TVal(TV_POTION),
+ TVal(TV_POTION2));
+ return instance;
+}
+
+
+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(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(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_FRAME);
+ 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)
+ {
+ msg_print("You feel more experienced.");
+ gain_exp(100000L);
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_POTION_RESISTANCE:
+ {
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_CURING:
+ {
+ if (hp_player(50)) ident = TRUE;
+ if (set_blind(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+ if (set_image(0)) ident = TRUE;
+ if (heal_insanity(50)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_INVULNERABILITY:
+ {
+ (void)set_invuln(p_ptr->invuln + randint(7) + 7);
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_NEW_LIFE:
+ {
+ do_cmd_rerate();
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_POTION_BLOOD:
+ {
+ msg_print("You feel the blood of life running through your veins!");
+ ident = TRUE;
+ p_ptr->allow_one_death++;
+
+ break;
+ }
+
+ case SV_POTION_MUTATION:
+ {
+ /* In Theme, Melkor likes players who quaff
+ potions of corruption. */
+ if (game_module_idx == MODULE_THEME)
+ {
+ if (p_ptr->pgod == GOD_MELKOR)
+ {
+ msg_print("Your quaffing of this potion pleases Melkor!");
+ set_grace(p_ptr->grace + 2);
+ }
+ }
+
+ msg_print("You feel the dark corruptions of Morgoth coming over you!");
+ gain_random_corruption();
+ 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 = get_mimic_random_duration(pval2);
+
+ set_mimic(time, pval2, (p_ptr->lev * 2) / 3);
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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 ident, lev;
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Quaff which potion? ",
+ "You have no potions to quaff.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_quaffable(),
+ select_object_by_name("Potion full name? ")))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+
+ /* Sound */
+ sound(SOUND_QUAFF);
+
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Object level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Demon Breath corruption can spoil potions. */
+ if (player_has_corruption(CORRUPT_DEMON_BREATH) && magik(9))
+ {
+ msg_print("Your demon breath spoils the potion!");
+ ident = FALSE;
+ }
+ else
+ {
+ /* Normal potion handling */
+ 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);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Potions can feed the player */
+ (void)set_food(p_ptr->food + o_ptr->pval);
+
+
+ /* Destroy potion */
+ inc_stack_size(item, -1);
+}
+
+
+/*
+ * Fill an empty bottle
+ */
+static 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, forge;
+
+ /* 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;
+ }
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Fill which bottle? ",
+ "You have no bottles to fill.",
+ (USE_INVEN),
+ object_filter::TVal(TV_BOTTLE)))
+ {
+ return;
+ }
+
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Find out how many the player wants */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ if (amt > c_ptr->special2) amt = c_ptr->special2;
+
+ /* Destroy bottles */
+ inc_stack_size(item, -amt);
+
+ /* Create the potion */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(tval, sval));
+ q_ptr->number = amt;
+
+ if (c_ptr->info & CAVE_IDNT)
+ {
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ }
+
+ inven_carry(q_ptr, TRUE);
+
+ c_ptr->special2 -= amt;
+
+ if (c_ptr->special2 <= 0)
+ {
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_EMPTY_FOUNTAIN);
+ }
+
+ return;
+}
+
+
+/*
+ * 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;
+ }
+}
+
+
+/*
+ * 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 object_filter_t const &item_tester_hook_readable()
+{
+ using namespace object_filter;
+ static auto instance =
+ Or(
+ TVal(TV_SCROLL),
+ TVal(TV_PARCHMENT));
+ return instance;
+}
+
+
+/*
+ * 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)
+{
+ /* 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;
+ }
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Read which scroll? ",
+ "You have no scrolls to read.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_readable(),
+ select_object_by_name("Scroll full name? ")))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Not identified yet */
+ int ident = FALSE;
+
+ /* Object level */
+ int lev = k_info[o_ptr->k_idx].level;
+
+ /* Assume the scroll will get used up */
+ int used_up = TRUE;
+
+ /* Corruption */
+ if (player_has_corruption(CORRUPT_BALROG_AURA) && magik(5))
+ {
+ msg_print("Your demon aura burns the scroll before you read it!");
+ used_up = TRUE;
+ ident = FALSE;
+ }
+
+ /* Scrolls */
+ else if (o_ptr->tval == TV_SCROLL)
+ {
+ /* Analyze the scroll */
+ switch (o_ptr->sval)
+ {
+ case SV_SCROLL_MASS_RESURECTION:
+ {
+ ident = TRUE;
+ msg_print("You feel the souls of the dead coming back "
+ "from the Halls of Mandos.");
+
+ for (int 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,
+ 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 (int 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 (int 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:
+ {
+ int 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);
+ }
+ 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;
+ }
+
+ case SV_SCROLL_STERILIZATION:
+ {
+ msg_print("A neutralising wave radiates from you!");
+ set_no_breeders(randint(100) + 100);
+
+ 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 */
+ cptr q = format("book-%d.txt", o_ptr->sval);
+
+ /* Peruse the help file */
+ (void)show_file(q, NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+
+ if (o_ptr->sval >= 100)
+ {
+ inscription_info[o_ptr->sval - 100].know = TRUE;
+ }
+
+ used_up = FALSE;
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* The item was tried */
+ object_tried(o_ptr);
+
+ /* An identification was made */
+ if (ident && !object_aware_p(o_ptr))
+ {
+ object_aware(o_ptr);
+ gain_exp((lev + (p_ptr->lev >> 1)) / p_ptr->lev);
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+
+ /* Hack -- allow certain scrolls to be "preserved" */
+ if (!used_up) return;
+
+ sound(SOUND_SCROLL);
+
+ /* Destroy scroll */
+ inc_stack_size(item, -1);
+}
+
+
+
+/* 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;
+ // Ensure that we're not assuming "reentrancy".
+ assert(get_level_use_stick < 0);
+ // Set up the casting mode
+ get_level_use_stick = bonus;
+ get_level_max_stick = max;
+}
+
+/* Remove 'stick mode' */
+void unset_stick_mode()
+{
+ // Ensure that we're not assuming "reentrancy".
+ assert(get_level_use_stick > 0);
+ // Unset the casting mode
+ get_level_use_stick = -1;
+ get_level_max_stick = -1;
+}
+
+
+/*
+ * Activate a device
+ */
+static void activate_stick(object_type *o_ptr, bool_ *obvious, bool_ *use_charge)
+{
+ spell_type *spell = spell_at(o_ptr->pval2);
+ casting_result ret;
+
+ assert(obvious != NULL);
+ assert(use_charge != NULL);
+
+ set_stick_mode(o_ptr);
+ ret = spell_type_produce_effect(spell);
+ unset_stick_mode();
+
+ switch (ret)
+ {
+ case NO_CAST:
+ *use_charge = FALSE;
+ *obvious = FALSE;
+ break;
+ case CAST_HIDDEN:
+ *use_charge = TRUE;
+ *obvious = FALSE;
+ break;
+ case CAST_OBVIOUS:
+ *use_charge = TRUE;
+ *obvious = TRUE;
+ break;
+ default:
+ assert(FALSE);
+ }
+}
+
+
+/*
+ * Use a staff. -RAK-
+ *
+ * One charge of one staff disappears.
+ *
+ * Hack -- staffs of identify can be "cancelled".
+ */
+void do_cmd_use_staff(void)
+{
+ bool_ obvious, use_charge;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Use which staff? ",
+ "You have no staff to use.",
+ (USE_INVEN | USE_FLOOR),
+ object_filter::TVal(TV_STAFF),
+ select_object_by_name("Staff full name? ")))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Mega-Hack -- refuse to use a pile from the ground */
+ if ((item < 0) && (o_ptr->number > 1))
+ {
+ msg_print("You must first pick up the staffs.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Enter device mode */
+ set_stick_mode(o_ptr);
+
+ /* get the chance */
+ int chance;
+ {
+ auto spell = spell_at(o_ptr->pval2);
+ chance = spell_chance_device(spell);
+ }
+
+ /* Leave device mode */
+ unset_stick_mode();
+
+ /* 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);
+ 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);
+ return;
+ }
+
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+ /* Analyze the staff */
+ activate_stick(o_ptr, &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)
+ {
+ 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);
+ }
+}
+
+
+/*
+ * 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)
+{
+ bool_ obvious, use_charge;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Aim which wand? ",
+ "You have no wand to aim.",
+ (USE_INVEN | USE_FLOOR),
+ object_filter::TVal(TV_WAND),
+ select_object_by_name("Wand full name? ")))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Mega-Hack -- refuse to aim a pile from the ground */
+ if ((item < 0) && (o_ptr->number > 1))
+ {
+ msg_print("You must first pick up the wands.");
+ return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Enter device mode */
+ set_stick_mode(o_ptr);
+
+ /* get the chance */
+ int chance;
+ {
+ auto spell = spell_at(o_ptr->pval2);
+ chance = spell_chance_device(spell);
+ }
+
+ /* Leave device mode */
+ unset_stick_mode();
+
+ /* 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);
+ 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);
+ return;
+ }
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+ /* Analyze the wand */
+ activate_stick(o_ptr, &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)
+ {
+ 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);
+ }
+}
+
+
+
+
+
+
+/*
+ * 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 object_filter_t const &item_tester_hook_zapable()
+{
+ using namespace object_filter;
+ static auto instance =
+ Or(
+ TVal(TV_ROD),
+ TVal(TV_ROD_MAIN));
+ return instance;
+}
+
+
+/*
+ * Hook to determine if an object is attachable
+ */
+static bool item_tester_hook_attachable(object_type const *o_ptr)
+{
+ return ((o_ptr->tval == TV_ROD_MAIN) &&
+ (o_ptr->pval == SV_ROD_NOTHING));
+}
+
+
+/*
+ * Combine a rod and a rod tip
+ */
+void zap_combine_rod_tip(object_type *q_ptr, int tip_item)
+{
+ int item;
+
+ 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;
+ }
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Attach the rod tip with which rod? ",
+ "You have no rod to attach to.",
+ (USE_INVEN),
+ item_tester_hook_attachable))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Calculate rod tip's mana cost */
+ cost = q_ptr->pval;
+
+ if (f4 & TR4_CHEAPNESS)
+ {
+ cost /= 2;
+ }
+
+ /*
+ * The rod must have at least the same mana capacity as the
+ * rod tip spell needs
+ */
+ if (o_ptr->pval2 < cost)
+ {
+ msg_print("This rod doesn't have enough mana for the rod tip.");
+ return;
+ }
+
+ /* Attach the tip to the rod */
+ o_ptr->pval = q_ptr->sval;
+
+ /* Destroy rod tip */
+ inc_stack_size(tip_item, -1);
+}
+
+
+/*
+ * Zap a rod, or attack a rod tip to a rod
+ */
+void do_cmd_zap_rod(void)
+{
+ int item, ident, chance, dir, lev;
+
+ int cost;
+
+ bool_ require_dir;
+
+ object_kind *tip_ptr;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* 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;
+ }
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Zap which rod? ",
+ "You have no rod to zap.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_zapable(),
+ select_object_by_name("Rod full name? ")))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+
+ /* "Zapping" a Rod Tip on rod of nothing will attach it */
+ if (o_ptr->tval == TV_ROD)
+ {
+ if (item >= 0)
+ {
+ zap_combine_rod_tip(o_ptr, item);
+ return;
+ }
+ else
+ {
+ msg_print("You can't zap a rod tip that's on the floor.");
+ return;
+ }
+ }
+
+
+ /* Non-directed rods */
+ if (o_ptr->pval < SV_ROD_MIN_DIRECTION)
+ {
+ require_dir = FALSE;
+ }
+
+ /* Some rods always require direction */
+ else
+ {
+ switch (o_ptr->pval)
+ {
+ case SV_ROD_DETECT_TRAP:
+ case SV_ROD_HAVOC:
+ case SV_ROD_HOME:
+ {
+ require_dir = FALSE;
+ break;
+ }
+
+ default:
+ {
+ require_dir = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Get a direction (unless KNOWN not to need it) */
+ if (!object_aware_p(o_ptr) || require_dir)
+ {
+ /* Get a direction, allow cancel */
+ if (!get_aim_dir(&dir)) return;
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_FAST_CAST) energy_use /= 2;
+
+ /* Not identified yet */
+ ident = FALSE;
+
+ /* Extract the item level */
+ tip_ptr = &k_info[lookup_kind(TV_ROD, o_ptr->pval)];
+ lev = k_info[lookup_kind(TV_ROD, o_ptr->pval)].level;
+
+ /* Base chance of success */
+ chance = p_ptr->skill_dev;
+
+ /* Confusion hurts skill */
+ if (p_ptr->confused) chance = chance / 2;
+
+ /* High level objects are harder */
+ chance = chance - ((lev > 50) ? 50 : lev);
+
+ if (chance <= 0)
+ {
+ chance = 1;
+ }
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance *= 10;
+ }
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
+ {
+ /* Flush input if necessary */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("You failed to use the rod properly.");
+
+ sound(SOUND_FAIL);
+
+ return;
+ }
+
+ /* Extract mana cost */
+ cost = tip_ptr->pval;
+
+ /* "Cheapness" ego halven the cost */
+ if (f4 & TR4_CHEAPNESS) cost = cost / 2;
+
+ /* A single rod is still charging */
+ if (o_ptr->timeout < cost)
+ {
+ /* Flush input if necessary */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("The rod does not have enough mana yet.");
+
+ return;
+ }
+
+ /* Increase the timeout by the rod kind's pval. */
+ o_ptr->timeout -= cost;
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+ /* Analyze the rod */
+ switch (o_ptr->pval)
+ {
+ case SV_ROD_HOME:
+ {
+ ident = TRUE;
+
+ do_cmd_home_trump();
+
+ break;
+ }
+
+ case SV_ROD_DETECT_TRAP:
+ {
+ if (detect_traps(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DETECT_DOOR:
+ {
+ if (detect_doors(DEFAULT_RADIUS)) ident = TRUE;
+ if (detect_stairs(DEFAULT_RADIUS)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_IDENTIFY:
+ {
+ ident = TRUE;
+
+ if (!ident_spell()) use_charge = FALSE;
+
+ break;
+ }
+
+ case SV_ROD_RECALL:
+ {
+ if ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? "))
+ {
+ use_charge = FALSE;
+ }
+ else
+ {
+ recall_player(21, 15);
+
+ ident = TRUE;
+ }
+
+ break;
+ }
+
+ case SV_ROD_ILLUMINATION:
+ {
+ if (lite_area(damroll(2, 8), 2)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_MAPPING:
+ {
+ map_area();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DETECTION:
+ {
+ detect_all(DEFAULT_RADIUS);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_PROBING:
+ {
+ probing();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_CURING:
+ {
+ if (set_blind(0)) ident = TRUE;
+ if (set_poisoned(0)) ident = TRUE;
+ if (set_confused(0)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+ if (set_image(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_HEALING:
+ {
+ if (hp_player(500)) ident = TRUE;
+ if (set_stun(0)) ident = TRUE;
+ if (set_cut(0)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_RESTORATION:
+ {
+ if (restore_level()) ident = TRUE;
+ if (do_res_stat(A_STR, TRUE)) ident = TRUE;
+ if (do_res_stat(A_INT, TRUE)) ident = TRUE;
+ if (do_res_stat(A_WIS, TRUE)) ident = TRUE;
+ if (do_res_stat(A_DEX, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CON, TRUE)) ident = TRUE;
+ if (do_res_stat(A_CHR, TRUE)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_SPEED:
+ {
+ if (!p_ptr->fast)
+ {
+ if (set_fast(randint(30) + 15, 10)) ident = TRUE;
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+
+ break;
+ }
+
+ case SV_ROD_TELEPORT_AWAY:
+ {
+ if (teleport_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DISARMING:
+ {
+ if (disarm_trap(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_LITE:
+ {
+ msg_print("A line of blue shimmering light appears.");
+ lite_line(dir);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_SLEEP_MONSTER:
+ {
+ if (sleep_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_SLOW_MONSTER:
+ {
+ if (slow_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_DRAIN_LIFE:
+ {
+ if (drain_life(dir, 75)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_POLYMORPH:
+ {
+ if (poly_monster(dir)) ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ACID_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_ACID, dir, damroll(6, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ELEC_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_ELEC, dir, damroll(3, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_FIRE_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_FIRE, dir, damroll(8, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_COLD_BOLT:
+ {
+ fire_bolt_or_beam(10, GF_COLD, dir, damroll(5, 8));
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ACID_BALL:
+ {
+ fire_ball(GF_ACID, dir, 60, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_ELEC_BALL:
+ {
+ fire_ball(GF_ELEC, dir, 32, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_FIRE_BALL:
+ {
+ fire_ball(GF_FIRE, dir, 72, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_COLD_BALL:
+ {
+ fire_ball(GF_COLD, dir, 48, 2);
+
+ ident = TRUE;
+
+ break;
+ }
+
+ case SV_ROD_HAVOC:
+ {
+ call_chaos();
+
+ ident = TRUE;
+
+ break;
+ }
+
+ default:
+ 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 object_filter_t const &item_tester_hook_activate()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ IsKnown(),
+ HasFlag3(TR3_ACTIVATE));
+ return instance;
+}
+
+
+/*
+ * 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),
+ (((plev > 24) && (randint(3) == 1)) ? TRUE : FALSE)))
+ {
+ msg_print("Cold winds begin to blow around you, "
+ "carrying with them the stench of decay...");
+ msg_print("Ancient, long-dead forms arise from the ground "
+ "to serve you!");
+ }
+ timeout = 200 + rand_int(200);
+ }
+
+ /* Rule the World -- only if we can really do so */
+ else if (p == 2)
+ {
+ msg_print("The power of the ring destroys the world!");
+ msg_print("The world changes!");
+
+ autosave_checkpoint();
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ timeout = 250 + rand_int(250);
+ }
+
+ /* Cast a powerful spell */
+ else if (p == 3)
+ {
+ int dir;
+
+ if (!get_aim_dir(&dir)) return (0);
+
+ if (rand_int(3) == 0)
+ {
+ msg_print("You call the fire of Mount Doom!");
+ fire_ball(GF_METEOR, dir, 600, 4);
+ }
+ else
+ {
+ msg_print("Your ring tries to take possession of your enemy's mind!");
+ fire_bolt(GF_CHARM, dir, 600);
+ }
+ timeout = 300 + rand_int(300);
+ }
+
+ return (timeout);
+}
+
+
+
+
+/*
+ * Enchant some bolts
+ */
+bool_ brand_bolts(void)
+{
+ int i;
+
+
+ /* Use the first acceptable bolts */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-bolts */
+ if (o_ptr->tval != TV_BOLT) continue;
+
+ /* Skip artifacts and ego-items */
+ if (o_ptr->art_name || artifact_p(o_ptr) || ego_item_p(o_ptr)) continue;
+
+ /* Skip cursed/broken items */
+ if (cursed_p(o_ptr)) continue;
+
+ /* Randomize */
+ if (rand_int(100) < 75) continue;
+
+ /* Message */
+ msg_print("Your bolts are covered in a fiery aura!");
+
+ /* Ego-item */
+ o_ptr->name2 = EGO_FLAME;
+
+ /* Apply the ego */
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Enchant */
+ enchant(o_ptr, rand_int(3) + 4, ENCH_TOHIT | ENCH_TODAM);
+
+ /* Notice */
+ return (TRUE);
+ }
+
+ /* Flush */
+ if (flush_failure) flush();
+
+ /* Fail */
+ msg_print("The fiery enchantment failed.");
+
+ /* Notice */
+ return (TRUE);
+}
+
+
+/*
+ * Eternal flame activation
+ */
+
+static int get_eternal_artifact_idx(object_type const *o_ptr)
+{
+ if ((o_ptr->tval == TV_SWORD) && (o_ptr->sval == SV_LONG_SWORD)) {
+ return 147;
+ } else if ((o_ptr->tval == TV_MSTAFF) && (o_ptr->sval == SV_MSTAFF)) {
+ return 127;
+ } else if ((o_ptr->tval == TV_BOW) && (o_ptr->sval == SV_HEAVY_XBOW)) {
+ return 152;
+ } else if ((o_ptr->tval == TV_DRAG_ARMOR) && (o_ptr->sval == SV_DRAGON_POWER)) {
+ return 17;
+ }
+
+ if (game_module_idx == MODULE_THEME)
+ {
+ if ((o_ptr->tval == TV_HAFTED) && (o_ptr->sval == SV_LUCERN_HAMMER)) {
+ return 241;
+ } else if ((o_ptr->tval == TV_POLEARM) && (o_ptr->sval == SV_TRIDENT)) {
+ return 242;
+ } else if ((o_ptr->tval == TV_AXE) && (o_ptr->sval == SV_BROAD_AXE)) {
+ return 243;
+ } else if ((o_ptr->tval == TV_BOW) && (o_ptr->sval == SV_LONG_BOW)) {
+ return 245;
+ } else if ((o_ptr->tval == TV_BOOMERANG) && (o_ptr->sval == SV_BOOM_METAL)) {
+ return 247;
+ } else if ((o_ptr->tval == TV_BOW) && (o_ptr->sval == SV_SLING)) {
+ return 246;
+ } else if ((o_ptr->tval == TV_SWORD) && (o_ptr->sval == SV_RAPIER)) {
+ return 244;
+ } else if ((o_ptr->tval == TV_AMULET) && (o_ptr->sval == SV_AMULET_SPELL)) {
+ return 248;
+ }
+ }
+
+ /* Not usable */
+ return -1;
+}
+
+static bool eternal_flame_item_tester_hook(object_type const *o_ptr)
+{
+ if ((o_ptr->name1 > 0) ||
+ (o_ptr->name2 > 0))
+ {
+ return FALSE;
+ }
+
+ return (get_eternal_artifact_idx(o_ptr) >= 0);
+}
+
+static bool activate_eternal_flame(int flame_item)
+{
+ int item;
+ int artifact_idx = -1;
+
+ if (!get_item(&item,
+ "Which object do you want to imbue?",
+ "You have no objects to imbue.",
+ USE_INVEN,
+ eternal_flame_item_tester_hook))
+ {
+ return false;
+ }
+
+ /* Get the artifact idx */
+ artifact_idx = get_eternal_artifact_idx(get_object(item));
+ assert(artifact_idx >= 0);
+
+ /* Forge the item */
+ object_type *o_ptr = get_object(item);
+ o_ptr->name1 = artifact_idx;
+
+ apply_magic(o_ptr, -1, TRUE, TRUE, TRUE);
+
+ o_ptr->found = OBJ_FOUND_SELFMADE;
+
+ inven_item_increase(flame_item, -1);
+ inven_item_describe(flame_item);
+ inven_item_optimize(flame_item);
+ return true;
+}
+
+
+/**
+ * Farmer Maggot's sling activation.
+ */
+static bool activate_maggot()
+{
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return false;
+ }
+
+ fire_ball(GF_TURN_ALL, dir, 40, 2);
+ return true;
+}
+
+
+/**
+ * 'Radagast' (Theme)
+ */
+static void activate_radagast()
+{
+ cmsg_print(TERM_GREEN, "The staff's power cleanses you completely!");
+ remove_all_curse();
+ do_res_stat(A_STR, TRUE);
+ do_res_stat(A_CON, TRUE);
+ do_res_stat(A_DEX, TRUE);
+ do_res_stat(A_WIS, TRUE);
+ do_res_stat(A_INT, TRUE);
+ do_res_stat(A_CHR, TRUE);
+ restore_level();
+ // clean_corruptions(); TODO: Do we want to implement this?
+ hp_player(5000);
+ heal_insanity(5000);
+ set_poisoned(0);
+ set_blind(0);
+ set_confused(0);
+ set_image(0);
+ set_stun(0);
+ set_cut(0);
+ set_parasite(0, 0);
+
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ }
+ p_ptr->black_breath = FALSE;
+
+ p_ptr->update |= PU_BONUS;
+ p_ptr->window |= PW_PLAYER;
+}
+
+
+/**
+ * 'Valaroma' (Theme)
+ */
+static void activate_valaroma()
+{
+ int power = 5 * p_ptr->lev;
+ banish_evil(power);
+}
+
+
+/*
+ * 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;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Get an item */
+ command_wrk = USE_EQUIP;
+ if (!get_item(&item,
+ "Activate which item? ",
+ "You have nothing to activate.",
+ (USE_EQUIP | USE_INVEN),
+ item_tester_hook_activate()))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Extract object flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Wearable items have to be worn */
+ if (!(f5 & TR5_ACTIVATE_NO_WIELD))
+ {
+ if (item < INVEN_WIELD)
+ {
+ msg_print("You must wear it to activate it.");
+ return;
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Extract the item level */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Hack -- Use artifact level instead */
+ if (artifact_p(o_ptr))
+ {
+ if (o_ptr->tval == TV_RANDART)
+ {
+ lev = random_artifacts[o_ptr->sval].level;
+ }
+ else
+ {
+ lev = a_info[o_ptr->name1].level;
+ }
+ }
+
+ /* Base chance of success */
+ chance = p_ptr->skill_dev;
+
+ /* Confusion hurts skill */
+ if (p_ptr->confused) chance = chance / 2;
+
+ /* Hight level objects are harder */
+ chance = chance - ((lev > 50) ? 50 : lev);
+
+ if (chance <= 0)
+ {
+ chance = 1;
+ }
+
+ /* Is it simple to use ? */
+ if (f4 & TR4_EASY_USE)
+ {
+ chance *= 10;
+ }
+
+ /* Give everyone a (slight) chance */
+ if ((chance < USE_DEVICE) && (rand_int(USE_DEVICE - chance + 1) == 0))
+ {
+ chance = USE_DEVICE;
+ }
+
+ /* Roll for usage */
+ if ((chance < USE_DEVICE) || (randint(chance) < USE_DEVICE))
+ {
+ if (flush_failure) flush();
+ msg_print("You failed to activate it properly.");
+ sound(SOUND_FAIL);
+ return;
+ }
+
+ /* Check the recharge */
+ if (o_ptr->timeout)
+ {
+ /* Mage Staff of Spells -- Have another timeout in xtra2 */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && o_ptr->xtra2)
+ {
+ msg_print("It whines, glows and fades...");
+ return;
+ }
+
+ /* Monster eggs */
+ else if (o_ptr->tval == TV_EGG)
+ {
+ msg_print("You resume the development of the egg.");
+ o_ptr->timeout = 0;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Success */
+ return;
+ }
+
+ /* Normal activatable items */
+ else
+ {
+ msg_print("It whines, glows and fades...");
+ return;
+ }
+ }
+
+
+ /* Activate the item */
+ msg_print("You activate it...");
+
+ /* Sound */
+ sound(SOUND_ZAP);
+
+ /* 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 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!";
+
+ /* Activations always have positive numbers */
+ assert(spell > 0);
+
+ /* 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);
+
+ 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) every 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) every 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(randint(5 * oops + 1));
+
+ /* Confusing. */
+ (void)set_confused(p_ptr->confused +
+ randint(5 * oops + 1));
+ }
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_FRAME);
+ }
+
+ take_hit(damroll(1, 12), "perilous secrets");
+
+ /* Confusing. */
+ if (rand_int(5) == 0)
+ {
+ (void)set_confused(p_ptr->confused + randint(10));
+ }
+
+ /* Exercise a little care... */
+ if (rand_int(20) == 0)
+ {
+ take_hit(damroll(4, 10), "perilous secrets");
+ }
+
+ o_ptr->timeout = 1;
+
+ break;
+ }
+
+ case ACT_RAZORBACK:
+ {
+ if (!doit) return "star ball (150) every 1000 turns";
+ msg_print("Your armor is surrounded by lightning...");
+ for (i = 0; i < 8; i++) fire_ball(GF_ELEC, ddd[i], 150, 3);
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_BLADETURNER:
+ {
+ if (!doit) return "invulnerability (4+d8) every 800 turns";
+ set_invuln(p_ptr->invuln + randint(8) + 4);
+
+ o_ptr->timeout = 800;
+
+ break;
+ }
+
+ case ACT_MEDIATOR:
+ {
+ if (!doit) return "breathe elements (300), berserk rage, bless, and resistance every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, 300, 4);
+ msg_print("Your armor glows many colours...");
+ (void)set_afraid(0);
+ (void)set_shero(p_ptr->shero + randint(50) + 50);
+ (void)hp_player(30);
+ (void)set_blessed(p_ptr->blessed + randint(50) + 50);
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(50) + 50);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(50) + 50);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(50) + 50);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(50) + 50);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(50) + 50);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_BELEGENNON:
+ {
+ if (!doit) return ("heal (777), curing and heroism every 300 turns");
+ msg_print("A heavenly choir sings...");
+ (void)set_poisoned(0);
+ (void)set_cut(0);
+ (void)set_stun(0);
+ (void)set_confused(0);
+ (void)set_blind(0);
+ (void)set_hero(p_ptr->hero + randint(25) + 25);
+ (void)hp_player(777);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_GORLIM:
+ {
+ if (!doit) return "rays of fear in every direction";
+ turn_monsters(40 + p_ptr->lev);
+
+ o_ptr->timeout = 3 * (p_ptr->lev + 10);
+
+ break;
+ }
+
+ case ACT_COLLUIN:
+ {
+ if (!doit) return "resistance (20+d20 turns) every 111 turns";
+ msg_print("Your cloak glows many colours...");
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
+ (void)set_oppose_pois(p_ptr->oppose_pois + randint(20) + 20);
+
+ o_ptr->timeout = 111;
+
+ break;
+ }
+
+
+ case ACT_BELANGIL:
+ {
+ if (!doit) return "frost ball (48) every 5+d5 turns";
+ msg_print("Your dagger is covered in frost...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 48, 2);
+
+ o_ptr->timeout = rand_int(5) + 5;
+
+ break;
+ }
+
+ case ACT_ANGUIREL:
+ {
+ if (!doit) return "a getaway every 35 turns";
+ switch (randint(13))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ teleport_player(10);
+
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ {
+ teleport_player(222);
+
+ break;
+ }
+
+ case 11:
+ case 12:
+ {
+ (void)stair_creation();
+
+ break;
+ }
+
+ default:
+ {
+ if (get_check("Leave this level? "))
+ {
+ autosave_checkpoint();
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+
+ break;
+ }
+ }
+
+ o_ptr->timeout = 35;
+
+ break;
+ }
+
+ case ACT_ERU:
+ {
+ if (!doit) return "healing(7000), curing every 500 turns";
+ msg_print("Your sword glows an intense white...");
+ hp_player(7000);
+ heal_insanity(50);
+ set_blind(0);
+ set_poisoned(0);
+ set_confused(0);
+ set_stun(0);
+ set_cut(0);
+ set_image(0);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_DAWN:
+ {
+ if (!doit) return "summon the Legion of the Dawn every 500+d500 turns";
+ msg_print("You summon the Legion of the Dawn.");
+ (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_DAWN, TRUE);
+
+ o_ptr->timeout = 500 + randint(500);
+
+ break;
+ }
+
+ case ACT_FIRESTAR:
+ {
+ if (!doit) return "large fire ball (72) every 100 turns";
+ msg_print("Your morning star rages in fire...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 72, 3);
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_TURMIL:
+ {
+ if (!doit) return "drain life (90) every 70 turns";
+ msg_print("Your hammer glows white...");
+ if (!get_aim_dir(&dir)) break;
+ drain_life(dir, 90);
+
+ o_ptr->timeout = 70;
+
+ break;
+ }
+
+ case ACT_CUBRAGOL:
+ {
+ if (!doit) return "fire branding of bolts every 999 turns";
+ msg_print("Your crossbow glows deep red...");
+ (void)brand_bolts();
+
+ o_ptr->timeout = 999;
+
+ break;
+ }
+
+ case ACT_ELESSAR:
+ {
+ if (!doit) return "heal and cure black breath every 200 turns";
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ }
+ p_ptr->black_breath = FALSE;
+ hp_player(100);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_GANDALF:
+ {
+ if (!doit) return "restore mana every 666 turns";
+ msg_print("Your mage staff glows deep blue...");
+ if (p_ptr->csp < p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+ msg_print("Your feel your head clear.");
+ p_ptr->redraw |= (PR_FRAME);
+ 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, (plev == 50 ? TRUE : FALSE)))
+ {
+ msg_print("A Thunderlord comes from thin air!");
+ msg_print("'I will help you in your difficult task.'");
+ }
+ }
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_PALANTIR:
+ {
+ if (!doit) return "clairvoyance every 100+d100 turns";
+ msg_print("The stone glows a deep green...");
+ wiz_lite_extra();
+ (void)detect_traps(DEFAULT_RADIUS);
+ (void)detect_doors(DEFAULT_RADIUS);
+ (void)detect_stairs(DEFAULT_RADIUS);
+
+ o_ptr->timeout = rand_int(100) + 100;
+
+ break;
+ }
+
+ case ACT_EREBOR:
+ {
+ if (!doit) return "open a secret passage every 75 turns";
+ msg_print("Your pick twists in your hands.");
+
+ if (!get_aim_dir(&dir)) break;
+ if (passwall(dir, TRUE))
+ {
+ msg_print("A passage opens, and you step through.");
+ }
+ else
+ {
+ msg_print("There is no wall there!");
+ }
+
+ o_ptr->timeout = 75;
+
+ break;
+ }
+
+ case ACT_DRUEDAIN:
+ {
+ if (!doit) return "detection every 99 turns";
+ msg_print("Your drum shows you the world.");
+ detect_all(DEFAULT_RADIUS);
+
+ o_ptr->timeout = 99;
+
+ break;
+ }
+
+ case ACT_ROHAN:
+ {
+ if (!doit) return "heroism, berserker, and haste every 250 turns";
+ msg_print("Your horn glows deep red.");
+ set_afraid(0);
+ set_shero(p_ptr->shero + damroll(5, 10) + 30);
+ set_afraid(0);
+ set_hero(p_ptr->hero + damroll(5, 10) + 30);
+ set_fast(p_ptr->fast + damroll(5, 10) + 30, 10);
+ hp_player(30);
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_HELM:
+ {
+ if (!doit) return "sound ball (300) every 300 turns";
+ msg_print("Your horn emits a loud sound.");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_SOUND, dir, 300, 6);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_BOROMIR:
+ {
+ if (!doit) return "mass human summoning every 1000 turns";
+ msg_print("Your horn calls for help.");
+ for (i = 0; i < 15; i++)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_HUMAN, TRUE);
+ }
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ case ACT_HURIN:
+ {
+ if (!doit) return "berserker and +10 to speed (50) every 100+d200 turns";
+ if (!p_ptr->fast)
+ {
+ (void)set_fast(randint(50) + 50, 10);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + 5, 10);
+ }
+ hp_player(30);
+ set_afraid(0);
+ set_shero(p_ptr->shero + randint(50) + 50);
+
+ o_ptr->timeout = rand_int(200) + 100;
+
+ break;
+ }
+
+ case ACT_AXE_GOTHMOG:
+ {
+ if (!doit) return "fire ball (300) every 200+d200 turns";
+ msg_print("Your lochaber axe erupts in fire...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 300, 4);
+
+ o_ptr->timeout = 200 + rand_int(200);
+
+ break;
+ }
+
+ case ACT_MELKOR:
+ {
+ if (!doit) return "darkness ball (150) every 100 turns";
+ msg_print("Your spear is covered of darkness...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_DARK, dir, 150, 3);
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_GROND:
+ {
+ if (!doit) return "alter reality every 100 turns";
+ msg_print("Your hammer hits the floor...");
+ alter_reality();
+
+ o_ptr->timeout = 100;
+
+ break;
+ }
+
+ case ACT_NATUREBANE:
+ {
+ if (!doit) return "dispel monsters (300) every 200+d200 turns";
+ msg_print("Your axe glows blood red...");
+ dispel_monsters(300);
+
+ o_ptr->timeout = 200 + randint(200);
+
+ break;
+ }
+
+ case ACT_NIGHT:
+ {
+ if (!doit) return "vampiric drain (3*100) every 250 turns";
+ msg_print("Your axe emits a black aura...");
+ if (!get_aim_dir(&dir)) break;
+ for (i = 0; i < 3; i++)
+ {
+ if (drain_life(dir, 100)) hp_player(100);
+ }
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_ORCHAST:
+ {
+ if (!doit) return "detect orcs every 10 turns";
+ msg_print("Your weapon glows brightly...");
+ (void)detect_monsters_xxx(RF3_ORC, DEFAULT_RADIUS);
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+ case ACT_SUNLIGHT:
+ {
+ if (!doit) return "beam of sunlight every 10 turns";
+
+ if (!get_aim_dir(&dir)) break;
+ msg_print("A line of sunlight appears.");
+ lite_line(dir);
+
+ o_ptr->timeout = 10;
+
+ break;
+ }
+
+ case ACT_BO_MISS_1:
+ {
+ if (!doit) return "magic missile (2d6) every 2 turns";
+ msg_print("It glows extremely brightly...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_MISSILE, dir, damroll(2, 6));
+
+ o_ptr->timeout = 2;
+
+ break;
+ }
+
+ case ACT_BA_POIS_1:
+ {
+ if (!doit) return "stinking cloud (12), rad. 3, every 4+d4 turns";
+ msg_print("It throbs deep green...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_POIS, dir, 12, 3);
+
+ o_ptr->timeout = rand_int(4) + 4;
+
+ break;
+ }
+
+ case ACT_BO_ELEC_1:
+ {
+ if (!doit) return "lightning bolt (4d8) every 6+d6 turns";
+ msg_print("It is covered in sparks...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_ELEC, dir, damroll(4, 8));
+
+ o_ptr->timeout = rand_int(6) + 6;
+
+ break;
+ }
+
+ case ACT_BO_ACID_1:
+ {
+ if (!doit) return "acid bolt (5d8) every 5+d5 turns";
+ msg_print("It is covered in acid...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_ACID, dir, damroll(5, 8));
+
+ o_ptr->timeout = rand_int(5) + 5;
+
+ break;
+ }
+
+ case ACT_BO_COLD_1:
+ {
+ if (!doit) return "frost bolt (6d8) every 7+d7 turns";
+ msg_print("It is covered in frost...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_COLD, dir, damroll(6, 8));
+
+ o_ptr->timeout = rand_int(7) + 7;
+
+ break;
+ }
+
+ case ACT_BO_FIRE_1:
+ {
+ if (!doit) return "fire bolt (9d8) every 8+d8 turns";
+ msg_print("It is covered in fire...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_FIRE, dir, damroll(9, 8));
+
+ o_ptr->timeout = rand_int(8) + 8;
+
+ break;
+ }
+
+ case ACT_BA_COLD_1:
+ {
+ if (!doit) return "ball of cold (48) every 400 turns";
+ msg_print("It is covered in frost...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 48, 2);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_BA_FIRE_1:
+ {
+ if (!doit) return "ball of fire (72) every 400 turns";
+ msg_print("It glows an intense red...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 72, 2);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_DRAIN_1:
+ {
+ if (!doit) return "drain life (100) every 100+d100 turns";
+ msg_print("It glows black...");
+ if (!get_aim_dir(&dir)) break;
+ if (drain_life(dir, 100))
+
+ o_ptr->timeout = rand_int(100) + 100;
+
+ break;
+ }
+
+ case ACT_BA_COLD_2:
+ {
+ if (!doit) return "ball of cold (100) every 300 turns";
+ msg_print("It glows an intense blue...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 100, 2);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_BA_ELEC_2:
+ {
+ if (!doit) return "ball of lightning (100) every 500 turns";
+ msg_print("It crackles with electricity...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_ELEC, dir, 100, 3);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_DRAIN_2:
+ {
+ if (!doit) return "drain life (120) every 400 turns";
+ msg_print("It glows black...");
+ if (!get_aim_dir(&dir)) break;
+ drain_life(dir, 120);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_VAMPIRE_1:
+ {
+ if (!doit) return "vampiric drain (3*50) every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ for (dummy = 0; dummy < 3; dummy++)
+ {
+ if (drain_life(dir, 50))
+ hp_player(50);
+ }
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_BO_MISS_2:
+ {
+ if (!doit) return "arrows (150) every 90+d90 turns";
+ msg_print("It grows magical spikes...");
+ if (!get_aim_dir(&dir)) break;
+ fire_bolt(GF_ARROW, dir, 150);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BA_FIRE_2:
+ {
+ if (!doit) return "fire ball (120) every 225+d225 turns";
+ msg_print("It glows deep red...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_FIRE, dir, 120, 3);
+
+ o_ptr->timeout = rand_int(225) + 225;
+
+ break;
+ }
+
+ case ACT_BA_COLD_3:
+ {
+ if (!doit) return "ball of cold (200) every 325+d325 turns";
+ msg_print("It glows bright white...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_COLD, dir, 200, 3);
+
+ o_ptr->timeout = rand_int(325) + 325;
+
+ break;
+ }
+
+ case ACT_BA_ELEC_3:
+ {
+ if (!doit) return "Lightning Ball (250) every 425+d425 turns";
+ msg_print("It glows deep blue...");
+ if (!get_aim_dir(&dir)) break;
+ fire_ball(GF_ELEC, dir, 250, 3);
+
+ o_ptr->timeout = rand_int(425) + 425;
+
+ break;
+ }
+
+ case ACT_WHIRLWIND:
+ {
+ int y = 0, x = 0;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ if (!doit) return "whirlwind attack every 250 turns";
+
+ for (dir = 0; dir <= 9; dir++)
+ {
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ /* Get the monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Hack -- attack monsters */
+ if (c_ptr->m_idx && (m_ptr->ml || cave_floor_bold(y, x)))
+ {
+ py_attack(y, x, -1);
+ }
+ }
+
+ o_ptr->timeout = 250;
+
+ break;
+ }
+
+ case ACT_VAMPIRE_2:
+ {
+ if (!doit) return "vampiric drain (3*100) every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ for (dummy = 0; dummy < 3; dummy++)
+ {
+ if (drain_life(dir, 100))
+ hp_player(100);
+ }
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+
+ case ACT_CALL_CHAOS:
+ {
+ if (!doit) return "call chaos every 350 turns";
+ msg_print("It glows in scintillating colours...");
+ call_chaos();
+
+ o_ptr->timeout = 350;
+
+ break;
+ }
+
+ case ACT_ROCKET:
+ {
+ if (!doit) return "launch rocket (120+level) every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You launch a rocket!");
+ fire_ball(GF_ROCKET, dir, 120 + (plev), 2);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_DISP_EVIL:
+ {
+ if (!doit) return "dispel evil (level*5) every 300+d300 turns";
+ msg_print("It floods the area with goodness...");
+ dispel_evil(p_ptr->lev * 5);
+
+ o_ptr->timeout = rand_int(300) + 300;
+
+ break;
+ }
+
+ case ACT_DISP_GOOD:
+ {
+ if (!doit) return "dispel good (level*5) every 300+d300 turns";
+ msg_print("It floods the area with evil...");
+ dispel_good(p_ptr->lev * 5);
+
+ o_ptr->timeout = rand_int(300) + 300;
+
+ break;
+ }
+
+ case ACT_BA_MISS_3:
+ {
+ if (!doit) return "elemental breath (300) every 500 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, 300, 4);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ /* Activate for other offensive action */
+
+ case ACT_CONFUSE:
+ {
+ if (!doit) return "confuse monster every 15 turns";
+ msg_print("It glows in scintillating colours...");
+ if (!get_aim_dir(&dir)) break;
+ confuse_monster(dir, 20);
+
+ o_ptr->timeout = 15;
+
+ break;
+ }
+
+ case ACT_SLEEP:
+ {
+ if (!doit) return "sleep nearby monsters every 55 turns";
+ msg_print("It glows deep blue...");
+ sleep_monsters_touch();
+
+ o_ptr->timeout = 55;
+
+ break;
+ }
+
+ case ACT_QUAKE:
+ {
+ if (!doit) return "earthquake (rad 10) every 50 turns";
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ {
+ earthquake(p_ptr->py, p_ptr->px, 10);
+ o_ptr->timeout = 50;
+ }
+
+ break;
+ }
+
+ case ACT_TERROR:
+ {
+ if (!doit) return "terror every 3 * (level+10) turns";
+ turn_monsters(40 + p_ptr->lev);
+
+ o_ptr->timeout = 3 * (p_ptr->lev + 10);
+
+ break;
+ }
+
+ case ACT_TELE_AWAY:
+ {
+ if (!doit) return "teleport away every 200 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void)fire_beam(GF_AWAY_ALL, dir, plev);
+
+ o_ptr->timeout = 200;
+
+ break;
+ }
+
+ case ACT_BANISH_EVIL:
+ {
+ if (!doit) return "banish evil every 250+d250 turns";
+ if (banish_evil(100))
+ {
+ msg_print("The power of the artifact banishes evil!");
+ }
+
+ o_ptr->timeout = 250 + randint(250);
+
+ break;
+ }
+
+ case ACT_GENOCIDE:
+ {
+ if (!doit) return "genocide every 500 turns";
+ msg_print("It glows deep blue...");
+ (void)genocide(TRUE);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_MASS_GENO:
+ {
+ if (!doit) return "mass genocide every 1000 turns";
+ msg_print("It lets out a long, shrill note...");
+ (void)mass_genocide(TRUE);
+
+ o_ptr->timeout = 1000;
+
+ break;
+ }
+
+ /* Activate for summoning / charming */
+
+ case ACT_CHARM_ANIMAL:
+ {
+ if (!doit) return "charm animal every 300 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void) charm_animal(dir, plev);
+
+ o_ptr->timeout = 300;
+
+ break;
+ }
+
+ case ACT_CHARM_UNDEAD:
+ {
+ if (!doit) return "enslave undead every 333 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void)control_one_undead(dir, plev);
+
+ o_ptr->timeout = 333;
+
+ break;
+ }
+
+ case ACT_CHARM_OTHER:
+ {
+ if (!doit) return "charm monster every 400 turns";
+ if (!get_aim_dir(&dir)) break;
+ (void) charm_monster(dir, plev);
+
+ o_ptr->timeout = 400;
+
+ break;
+ }
+
+ case ACT_CHARM_ANIMALS:
+ {
+ if (!doit) return "animal friendship every 500 turns";
+ (void) charm_animals(plev * 2);
+
+ o_ptr->timeout = 500;
+
+ break;
+ }
+
+ case ACT_CHARM_OTHERS:
+ {
+ if (!doit) return "mass charm every 750 turns";
+ charm_monsters(plev * 2);
+
+ o_ptr->timeout = 750;
+
+ break;
+ }
+
+ case ACT_SUMMON_ANIMAL:
+ {
+ if (!doit) return "summon animal every 200+d300 turns";
+ (void)summon_specific_friendly(p_ptr->py, p_ptr->px, plev, SUMMON_ANIMAL_RANGER, TRUE);
+
+ o_ptr->timeout = 200 + randint(300);
+
+ break;
+ }
+
+ case ACT_SUMMON_PHANTOM:
+ {
+ if (!doit) return "summon phantasmal servant every 200+d200 turns";
+ msg_print("You summon a phantasmal servant.");
+ (void)summon_specific_friendly(p_ptr->py, p_ptr->px, dun_level, SUMMON_PHANTOM, TRUE);
+
+ o_ptr->timeout = 200 + randint(200);
+
+ break;
+ }
+
+ case ACT_SUMMON_ELEMENTAL:
+ {
+ if (!doit) return "summon elemental every 750 turns";
+ if (randint(3) == 1)
+ {
+ if (summon_specific(p_ptr->py, p_ptr->px, ((plev * 3) / 2), SUMMON_ELEMENTAL))
+ {
+ msg_print("An elemental materialises...");
+ msg_print("You fail to control it!");
+ }
+ }
+ else
+ {
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, ((plev * 3) / 2),
+ SUMMON_ELEMENTAL, (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, (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),
+ (((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(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();
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_INSANITY:
+ {
+ if (!doit) return "cure insanity every 200 turns";
+ heal_insanity(damroll(10, 10));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_CURE_MUT:
+ {
+ msg_print("Ahah, you wish.");
+ /* Timeout is set before return */
+
+ break;
+ }
+
+ case ACT_LIGHT_ABSORBTION:
+ {
+ int y, x, light = 0, dir;
+ cave_type *c_ptr;
+
+ if (!doit) return "light absorption every 80 turns";
+
+ for (y = p_ptr->py - 6; y <= p_ptr->py + 6; y++)
+ {
+ for (x = p_ptr->px - 6; x <= p_ptr->px + 6; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ c_ptr = &cave[y][x];
+
+ if (distance(y, x, p_ptr->py, p_ptr->px) > 6) continue;
+
+ if (c_ptr->info & CAVE_GLOW)
+ {
+ light++;
+
+ /* No longer in the array */
+ c_ptr->info &= ~(CAVE_TEMP);
+
+ /* Darken the grid */
+ c_ptr->info &= ~(CAVE_GLOW);
+
+ /* Hack -- Forget "boring" grids */
+ if (cave_plain_floor_grid(c_ptr) &&
+ !(c_ptr->info & (CAVE_TRDT)))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ note_spot(y, x);
+ }
+
+ /* Process affected monsters */
+ if (c_ptr->m_idx)
+ {
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, FALSE);
+ }
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+ }
+ }
+
+ if (!get_aim_dir(&dir)) return (FALSE);
+
+ msg_print("The light around you is absorbed... "
+ "and released in a powerful bolt!");
+ fire_bolt(GF_LITE, dir, damroll(light, p_ptr->lev));
+
+ /* Timeout is set before return */
+
+ break;
+ }
+ /* Horns of DragonKind (Note that these are new egos)*/
+ case ACT_BA_FIRE_H:
+ {
+ if (!doit) return "large fire ball (300) every 100 turns";
+ fire_ball(GF_FIRE, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+ case ACT_BA_COLD_H:
+ {
+ if (!doit) return "large cold ball (300) every 100 turns";
+ fire_ball(GF_COLD, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+ case ACT_BA_ELEC_H:
+ {
+ if (!doit) return "large lightning ball (300) every 100 turns";
+ fire_ball(GF_ELEC, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+ case ACT_BA_ACID_H:
+ {
+ if (!doit) return "large acid ball (300) every 100 turns";
+ fire_ball(GF_ACID, 5, 300, 7);
+
+ o_ptr->timeout = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ break;
+ }
+
+ case ACT_SPIN:
+ {
+ if (!doit) return "spinning around every 50+d25 turns";
+ do_spin();
+
+ o_ptr->timeout = 50 + randint(25);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ break;
+ }
+ case ACT_NOLDOR:
+ {
+ if (!doit) return "detect treasure every 10+d20 turns";
+ detect_treasure(DEFAULT_RADIUS);
+
+ o_ptr->timeout = 10 + randint(20);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ break;
+ }
+ case ACT_SPECTRAL:
+ {
+ if (!doit) return "wraith-form every 50+d50 turns";
+ if (!p_ptr->wraith_form)
+ {
+ set_shadow(20 + randint(20));
+ }
+ else
+ {
+ set_shadow(p_ptr->tim_wraith + randint(20));
+ }
+
+ o_ptr->timeout = 50 + randint(50);
+
+ /* Window stuff */
+ p_ptr->window |= PW_INVEN | PW_EQUIP;
+
+ /* Done */
+ break;
+ }
+ case ACT_JUMP:
+ {
+ if (!doit) return "phasing every 10+d10 turns";
+ teleport_player(10);
+ o_ptr->timeout = 10 + randint(10);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Done */
+ break;
+ }
+
+ case ACT_DEST_TELE:
+ {
+ if (!doit) return "teleportation and destruction of the ring";
+ if (!item)
+ {
+ msg_print("You can't activate this when it's there!");
+ }
+ if (get_check("This will destroy the ring. Do you wish to continue? "))
+ {
+ msg_print("The ring explodes into a space distortion.");
+ teleport_player(200);
+
+ /* It explodes, doesn't it ? */
+ take_hit(damroll(2, 10), "an exploding ring");
+
+ inc_stack_size_ex(item, -255, OPTIMIZE, NO_DESCRIBE);
+ }
+
+ break;
+ }
+ /*amulet of serpents dam 100, rad 2 timeout 40+d60 */
+ case ACT_BA_POIS_4:
+ {
+ if (!doit) return "venom breathing every 40+d60 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ msg_print("You breathe venom...");
+ fire_ball(GF_POIS, dir, 100, 2);
+
+ o_ptr->timeout = rand_int(60) + 40;
+
+ /* Window stuff */
+ p_ptr->window |= PW_INVEN | PW_EQUIP;
+
+ /* Done */
+ break;
+ }
+ /*rings of X 50,50+d50 dur 20+d20 */
+ case ACT_BA_COLD_4:
+ {
+ if (!doit) return "ball of cold and resist cold every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_COLD, dir, 50, 2);
+ (void)set_oppose_cold(p_ptr->oppose_cold + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_BA_FIRE_4:
+ {
+ if (!doit) return "ball of fire and resist fire every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_FIRE, dir, 50, 2);
+ (void)set_oppose_fire(p_ptr->oppose_fire + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+ case ACT_BA_ACID_4:
+ {
+ if (!doit) return "ball of acid and resist acid every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ACID, dir, 50, 2);
+ (void)set_oppose_acid(p_ptr->oppose_acid + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_BA_ELEC_4:
+ {
+ if (!doit) return "ball of lightning and resist lightning every 50+d50 turns";
+ /* Get a direction for breathing (or abort) */
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(GF_ELEC, dir, 50, 2);
+ (void)set_oppose_elec(p_ptr->oppose_elec + randint(20) + 20);
+
+ o_ptr->timeout = rand_int(50) + 50;
+
+ break;
+ }
+
+ case ACT_BR_ELEC:
+ {
+ if (!doit) return "breathe lightning (100) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe lightning.");
+ fire_ball(GF_ELEC, dir, 100, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_COLD:
+ {
+ if (!doit) return "breathe frost (110) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe frost.");
+ fire_ball(GF_COLD, dir, 110, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_FIRE:
+ {
+ if (!doit) return "breathe fire (200) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe fire.");
+ fire_ball(GF_FIRE, dir, 200, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_ACID:
+ {
+ if (!doit) return "breathe acid (130) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe acid.");
+ fire_ball(GF_ACID, dir, 130, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_POIS:
+ {
+ if (!doit) return "breathe poison gas (150) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe poison gas.");
+ fire_ball(GF_POIS, dir, 150, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_MANY:
+ {
+ if (!doit) return "breathe multi-hued (250) every 60+d60 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(5);
+ msg_format("You breathe %s.",
+ ((chance == 1) ? "lightning" :
+ ((chance == 2) ? "frost" :
+ ((chance == 3) ? "acid" :
+ ((chance == 4) ? "poison gas" : "fire")))));
+ fire_ball(((chance == 1) ? GF_ELEC :
+ ((chance == 2) ? GF_COLD :
+ ((chance == 3) ? GF_ACID :
+ ((chance == 4) ? GF_POIS : GF_FIRE)))),
+ dir, 250, 2);
+
+ o_ptr->timeout = rand_int(60) + 60;
+
+ break;
+ }
+
+ case ACT_BR_CONF:
+ {
+ if (!doit) return "breathe confusion (120) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe confusion.");
+ fire_ball(GF_CONFUSION, dir, 120, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_SOUND:
+ {
+ if (!doit) return "breathe sound (130) every 90+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe sound.");
+ fire_ball(GF_SOUND, dir, 130, 2);
+
+ o_ptr->timeout = rand_int(90) + 90;
+
+ break;
+ }
+
+ case ACT_BR_CHAOS:
+ {
+ if (!doit) return "breathe chaos/disenchant (220) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(2);
+ msg_format("You breathe %s.",
+ ((chance == 1 ? "chaos" : "disenchantment")));
+ fire_ball((chance == 1 ? GF_CHAOS : GF_DISENCHANT),
+ dir, 220, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+
+ case ACT_BR_SHARD:
+ {
+ if (!doit) return "breathe sound/shards (230) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(2);
+ msg_format("You breathe %s.",
+ ((chance == 1 ? "sound" : "shards")));
+ fire_ball((chance == 1 ? GF_SOUND : GF_SHARDS),
+ dir, 230, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+
+ case ACT_BR_BALANCE:
+ {
+ if (!doit) return "breathe balance (250) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(4);
+ msg_format("You breathe %s.",
+ ((chance == 1) ? "chaos" :
+ ((chance == 2) ? "disenchantment" :
+ ((chance == 3) ? "sound" : "shards"))));
+ fire_ball(((chance == 1) ? GF_CHAOS :
+ ((chance == 2) ? GF_DISENCHANT :
+ ((chance == 3) ? GF_SOUND : GF_SHARDS))),
+ dir, 250, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+
+ case ACT_BR_LIGHT:
+ {
+ if (!doit) return "breathe light/darkness (200) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ chance = rand_int(2);
+ msg_format("You breathe %s.",
+ ((chance == 0 ? "light" : "darkness")));
+ fire_ball((chance == 0 ? GF_LITE : GF_DARK), dir, 200, 2);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+ case ACT_BR_POWER:
+ {
+ if (!doit) return "breathe the elements (300) every 60+d90 turns";
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, 300, 3);
+
+ o_ptr->timeout = rand_int(90) + 60;
+
+ break;
+ }
+ case ACT_GROW_MOLD:
+ {
+ if (!doit) return "grow mushrooms every 50+d50 turns";
+ msg_print("You twirl and spores fly everywhere!");
+ for (i = 0; i < 8; i++)
+ summon_specific_friendly(p_ptr->py, p_ptr->px, p_ptr->lev, SUMMON_BIZARRE1, FALSE);
+
+ o_ptr->timeout = randint(50) + 50;
+
+ break;
+ }
+ case ACT_MUSIC:
+ /* Should be handled specially by caller, so if we get here something's wrong. */
+ abort();
+ case ACT_ETERNAL_FLAME:
+ {
+ if (!doit) return "imbuing an object with the eternal fire";
+
+ if (!activate_eternal_flame(item))
+ {
+ // Eternal Flame object was NOT destroyed, so let's
+ // set the timeout.
+ o_ptr->timeout = 0;
+ }
+ break;
+ }
+ case ACT_MAGGOT:
+ {
+ if (!doit) return "terrify every 10+d50 turns";
+
+ if (activate_maggot())
+ {
+ o_ptr->timeout = 10 + randint(50);
+ }
+ break;
+ }
+ case ACT_LEBOHAUM:
+ {
+ if (!doit) return "sing a cheerful song every turn";
+
+ 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!'");
+
+ o_ptr->timeout = 3;
+
+ break;
+ }
+ case ACT_DURANDIL:
+ {
+ if (!doit) return "sing a cheerful song every turn";
+
+ 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!'");
+
+ o_ptr->timeout = 3;
+
+ break;
+ }
+ case ACT_RADAGAST:
+ {
+ if (!doit) return "purity and health every 15000 turns";
+
+ activate_radagast();
+ o_ptr->timeout = 15000;
+
+ break;
+ }
+ case ACT_VALAROMA:
+ {
+ if (!doit) return "banish evil (level x5) every 250 turns";
+
+ activate_valaroma();
+ o_ptr->timeout = 250;
+
+ break;
+ }
+ 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/cmd6.hpp b/src/cmd6.hpp
new file mode 100644
index 00000000..ad6619f6
--- /dev/null
+++ b/src/cmd6.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type_fwd.hpp"
+
+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_cut_corpse(void);
+extern void do_cmd_cure_meat(void);
+extern void do_cmd_drink_fountain(void);
diff --git a/src/cmd7.cc b/src/cmd7.cc
new file mode 100644
index 00000000..2317f8b9
--- /dev/null
+++ b/src/cmd7.cc
@@ -0,0 +1,4466 @@
+/*
+ * 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 "cmd7.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd1.hpp"
+#include "cmd5.hpp"
+#include "cmd6.hpp"
+#include "ego_item_type.hpp"
+#include "files.hpp"
+#include "hooks.hpp"
+#include "mimic.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "skills.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+/*
+ * Describe class powers of Mindcrafters
+ *
+ * 'p' points to a 80 byte long buffer
+ */
+void mindcraft_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_MINDCRAFT);
+
+
+ /* Clear buffer */
+ strcpy(p, "");
+
+ /* Fill the buffer with requested power description */
+ switch (power)
+ {
+ case 0:
+ strnfmt(p, 80, " rad %d", DEFAULT_RADIUS);
+ break;
+ case 1:
+ strnfmt(p, 80, " dam %dd%d", 3 + ((plev - 1) / 4), 3 + plev / 15);
+ break;
+ case 2:
+ strnfmt(p, 80, " range %d", (plev < 25 ? 10 : plev + 2 + p_ptr->to_s * 3));
+ break;
+ case 3:
+ strnfmt(p, 80, " range %d", plev * 5);
+ break;
+ case 4:
+ strnfmt(p, 80, " power %d", plev * (plev < 30 ? 1 : 2));
+ break;
+ case 5:
+ if (plev > 20)
+ strnfmt(p, 80, " dam %dd8 rad %d", 8 + ((plev - 5) / 4), (plev - 20)/8 + 1);
+ else
+ strnfmt(p, 80, " dam %dd8", 8 + ((plev - 5) / 4));
+ break;
+ case 6:
+ strnfmt(p, 80, " dur %d", plev);
+ break;
+ case 7:
+ break;
+ case 8:
+ if (plev < 25)
+ strnfmt(p, 80, " dam %d rad %d", (3 * plev) / 2, 2 + (plev / 10));
+ else
+ strnfmt(p, 80, " dam %d", plev * ((plev - 5) / 10 + 1));
+ break;
+ case 9:
+ strnfmt(p, 80, " dur 11-%d", 10 + plev + plev / 2);
+ break;
+ case 10:
+ strnfmt(p, 80, " dam %dd6 rad %d", plev / 2, 0 + (plev - 25) / 10);
+ break;
+ case 11:
+ strnfmt(p, 80, " dam %d rad %d", plev * (plev > 39 ? 4 : 3), 3 + plev / 10);
+ break;
+ }
+}
+
+
+/*
+ * Describe class powers of Mimics
+ *
+ * 'p' points to a 80 byte long buffer
+ */
+void mimic_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_MIMICRY);
+ object_type *o_ptr = &p_ptr->inventory[INVEN_OUTER];
+
+ /* Clear the buffer */
+ strcpy(p, "");
+
+ /* Fill the buffer with requested power description */
+ switch (power)
+ {
+ case 0:
+ strnfmt(p, 80, " dur %d", k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000));
+ break;
+ case 1:
+ strnfmt(p, 80, " dur %d+d20", 10 + plev);
+ break;
+ case 2:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ case 3:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ case 4:
+ strnfmt(p, 80, " dur 50+d%d", 50 + (2 * plev));
+ break;
+ }
+}
+
+/**
+ * Show magic powers that user can choose from
+ */
+static void display_magic_powers(
+ magic_power *powers,
+ int max_powers,
+ void (*power_info)(char *p, int power),
+ int plev,
+ int cast_stat,
+ int y,
+ int x)
+{
+ char psi_desc[80];
+ magic_power spell;
+ int i;
+ int chance = 0;
+ int minfail = 0;
+ char comment[80];
+
+ /* Display a list of spells */
+ prt("", 1, x);
+ prt("", y, x);
+ put_str("Name", y, x + 5);
+ put_str("Lv Mana Fail Info", y, x + 35);
+
+ /* Dump the spells */
+ for (i = 0; i < max_powers; i++)
+ {
+ /* Access the spell */
+ spell = powers[i];
+ if (spell.min_lev > plev)
+ {
+ break;
+ }
+
+ chance = spell.fail;
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[cast_stat]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[cast_stat]];
+
+ /* Failure rate */
+ chance = clamp_failure_chance(chance, minfail);
+
+ /* Get info */
+ power_info(comment, i);
+
+ /* Dump the spell --(-- */
+ strnfmt(psi_desc, 80, " %c) %-30s%2d %4d %3d%%%s",
+ I2A(i), spell.name,
+ spell.min_lev, spell.mana_cost, chance, comment);
+ prt(psi_desc, y + i + 1, x);
+ }
+
+ /* Clear the bottom line */
+ prt("", y + i + 1, x);
+}
+
+/*
+ * 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.
+ */
+static 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 info;
+
+ char choice;
+
+ char out_val[160];
+
+ cptr p = "power";
+
+ magic_power spell;
+
+ bool_ flag;
+
+
+ /* Assume cancelled */
+ *sn = ( -1);
+
+ /* Get the spell, if available */
+ if (repeat_pull(sn))
+ {
+ /* Verify the spell */
+ if (powers[*sn].min_lev <= plev)
+ {
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* 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, 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();
+
+ /* Show the list */
+ display_magic_powers(powers, max_powers, power_info, plev, cast_stat, y, x);
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* 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;
+
+ /* Redisplay choices */
+ display_magic_powers(powers, max_powers, power_info, plev, cast_stat, y, x);
+ continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+
+ /* Abort if needed */
+ if (!flag) return (FALSE);
+
+ /* Save the choice */
+ (*sn) = i;
+
+
+ repeat_push(*sn);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * do_cmd_cast calls this function if the player's class
+ * is 'mindcrafter'.
+ */
+void do_cmd_mindcraft(void)
+{
+ int n = 0, b = 0;
+
+ int chance;
+
+ int dir;
+
+ int minfail = 0;
+
+ int plev = get_skill(SKILL_MINDCRAFT);
+
+ magic_power spell;
+
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, mindcraft_powers, MAX_MINDCRAFT_POWERS,
+ mindcraft_info, plev, A_WIS)) return;
+
+ spell = mindcraft_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (get_skill(SKILL_MINDCRAFT) - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_WIS]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_WIS]];
+
+ /* Failure rate */
+ chance = clamp_failure_chance(chance, minfail);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+
+ msg_format("You failed to concentrate hard enough!");
+
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (chance / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+ if (b < 5)
+ {
+ msg_print("Oh, no! Your mind has gone blank!");
+ lose_all_info();
+ }
+ else if (b < 15)
+ {
+ msg_print("Weird visions seem to dance before your eyes...");
+ set_image(p_ptr->image + 5 + randint(10));
+ }
+ else if (b < 45)
+ {
+ msg_print("Your brain is addled!");
+ set_confused(p_ptr->confused + randint(8));
+ }
+ else if (b < 90)
+ {
+ set_stun(p_ptr->stun + randint(8));
+ }
+ else
+ {
+ /* Mana storm */
+ msg_print("Your mind unleashes its power in an uncontrollable storm!");
+ project(1, 2 + plev / 10, p_ptr->py, p_ptr->px, plev * 2,
+ GF_MANA, PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM);
+ p_ptr->csp = MAX(0, p_ptr->csp - plev * MAX(1, plev / 10));
+ }
+ }
+ }
+
+ /* Successful spells */
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ /* Precog */
+ case 0:
+ {
+ /* Magic mapping */
+ if (plev > 44)
+ {
+ wiz_lite();
+ }
+ else if (plev > 19)
+ {
+ map_area();
+ }
+
+ /* Detection */
+ if (plev < 30)
+ {
+ b = detect_monsters_normal(DEFAULT_RADIUS);
+ if (plev > 14) b |= detect_monsters_invis(DEFAULT_RADIUS);
+ if (plev > 4) b |= detect_traps(DEFAULT_RADIUS);
+ }
+ else
+ {
+ b = detect_all(DEFAULT_RADIUS);
+ }
+
+ /* Telepathy */
+ if (plev > 24)
+ {
+ set_tim_esp(p_ptr->tim_esp + plev);
+
+ /* If plvl >= 40, we should have permanent ESP */
+ }
+
+ if (!b) msg_print("You feel safe.");
+
+ break;
+ }
+
+ /* Mindblast */
+ case 1:
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ if (randint(100) < plev * 2)
+ {
+ fire_beam(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)));
+ }
+ else
+ {
+ fire_ball(GF_PSI, dir, damroll(3 + ((plev - 1) / 4), (3 + plev / 15)), 0);
+ }
+
+ break;
+ }
+
+ /* Minor displace */
+ case 2:
+ {
+ if (plev < 25)
+ {
+ teleport_player(10);
+ }
+ else
+ {
+ int ii, ij;
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("Not on special levels!");
+ break;
+ }
+
+ msg_print("You open a Void Jumpgate. Choose a destination.");
+
+ if (!tgt_pt(&ii, &ij)) return;
+ p_ptr->energy -= 60 - plev;
+
+ if (!cave_empty_bold(ij, ii) ||
+ (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev + 2 + (p_ptr->to_s*3)) ||
+ (rand_int(plev * plev / 2) == 0))
+ {
+ msg_print("You fail to exit the void correctly!");
+ p_ptr->energy -= 100;
+ get_pos_player(10 + p_ptr->to_s / 2, &ij, &ii);
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_BETWEEN);
+ cave_set_feat(ij, ii, FEAT_BETWEEN);
+ cave[p_ptr->py][p_ptr->px].special = ii + (ij << 8);
+ cave[ij][ii].special = p_ptr->px + (p_ptr->py << 8);
+ }
+
+ break;
+ }
+
+ /* Major displace */
+ case 3:
+ {
+ if (plev > 29) banish_monsters(plev);
+ teleport_player(plev * 5);
+
+ break;
+ }
+
+ /* Domination */
+ case 4:
+ {
+ if (plev < 30)
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_DOMINATION, dir, plev, 0);
+ }
+ else
+ {
+ charm_monsters(plev * 2);
+ }
+
+ break;
+ }
+
+ /* Fist of Force --- not 'true' TK */
+ case 5:
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_ball(GF_SOUND, dir, damroll(8 + ((plev - 5) / 4), 8),
+ (plev > 20 ? (plev - 20) / 8 + 1 : 0));
+
+ break;
+ }
+
+ /* Character Armour */
+ case 6:
+ {
+ set_shield(p_ptr->shield + plev, plev, 0, 0, 0);
+ if (plev > 14) set_oppose_acid(p_ptr->oppose_acid + plev);
+ if (plev > 19) set_oppose_fire(p_ptr->oppose_fire + plev);
+ if (plev > 24) set_oppose_cold(p_ptr->oppose_cold + plev);
+ if (plev > 29) set_oppose_elec(p_ptr->oppose_elec + plev);
+ if (plev > 34) set_oppose_pois(p_ptr->oppose_pois + plev);
+
+ break;
+ }
+
+ /* Psychometry */
+ case 7:
+ {
+ ident_spell();
+ break;
+ }
+
+ /* Mindwave */
+ case 8:
+ {
+ msg_print("Mind-warping forces emanate from your brain!");
+ if (plev < 25)
+ {
+ project(0, 2 + plev / 10, p_ptr->py, p_ptr->px,
+ (plev*3) / 2, GF_PSI, PROJECT_KILL);
+ }
+ else
+ {
+ (void)mindblast_monsters(plev * ((plev - 5) / 10 + 1));
+ }
+
+ break;
+ }
+
+ /* Adrenaline */
+ case 9:
+ {
+ set_afraid(0);
+ set_stun(0);
+ hp_player(plev);
+
+ b = 10 + randint((plev * 3) / 2);
+
+ if (plev < 35)
+ {
+ set_hero(p_ptr->hero + b);
+ }
+ else
+ {
+ set_shero(p_ptr->shero + b);
+ }
+
+ if (!p_ptr->fast)
+ {
+ /* Haste */
+ (void)set_fast(b, plev / 5);
+ }
+ else
+ {
+ (void)set_fast(p_ptr->fast + b, plev / 5);
+ }
+
+ break;
+ }
+
+ /* Psychic Drain */
+ case 10:
+ {
+ if (!get_aim_dir(&dir)) return;
+
+ b = damroll(plev / 2, 6);
+
+ if (fire_ball(GF_PSI_DRAIN, dir, b, 0 + (plev - 25) / 10))
+ {
+ p_ptr->energy -= randint(150);
+ }
+
+ break;
+ }
+
+ /* Telekinesis */
+ case 11:
+ {
+ msg_print("A wave of pure physical force radiates out from your body!");
+ project(0, 3 + plev / 10, p_ptr->py, p_ptr->px,
+ plev * (plev > 39 ? 4 : 3), GF_TELEKINESIS,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID);
+
+ break;
+ }
+
+ default:
+ {
+ msg_print("Zap?");
+
+ break;
+ }
+ }
+ }
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Sufficient mana */
+ if (spell.mana_cost <= p_ptr->csp)
+ {
+ /* Use some mana */
+ p_ptr->csp -= spell.mana_cost;
+ }
+
+ /* Over-exert the player */
+ else
+ {
+ int oops = spell.mana_cost - p_ptr->csp;
+
+ /* No mana left */
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+
+ /* Message */
+ msg_print("You faint from the effort!");
+
+ /* Hack -- Bypass free action */
+ (void)set_paralyzed(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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+
+static int get_mimic_chance(int mimic)
+{
+ s32b chance;
+
+ chance = get_mimic_level(mimic);
+ chance *= 3;
+
+ chance -= get_skill_scale(SKILL_MIMICRY, 150);
+ chance -= 3 * adj_mag_stat[p_ptr->stat_ind[A_DEX]];
+
+ /* Return the chance */
+ return clamp_failure_chance(chance, 2);
+}
+
+
+void do_cmd_mimic_lore()
+{
+ int fail;
+
+ object_type *o_ptr;
+
+
+ /* Player has to be able to see */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* No transformations when confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+
+ /* Already in a mimic form -- Allow cancelling */
+ if (p_ptr->mimic_form)
+ {
+ msg_print("You morph back to your natural form!");
+
+ set_mimic(0, 0, 0);
+ }
+
+ /* Not in mimic forms -- Allow transformations */
+ else
+ {
+ o_ptr = &p_ptr->inventory[INVEN_OUTER];
+
+ if ((o_ptr->tval != TV_CLOAK) || (o_ptr->sval != SV_MIMIC_CLOAK))
+ {
+ msg_print("You are not wearing any cloaks of mimicry.");
+ return;
+ }
+
+ /* Calculate failure rate */
+ fail = get_mimic_chance(o_ptr->pval2);
+
+ if (fail > 75)
+ {
+ msg_print("You feel uneasy with this shape-change.");
+
+ if (!get_check("Try it anyway? ")) return;
+ }
+
+ /* Fumble */
+ if (randint(100) < fail)
+ {
+ msg_print("Your shape-change goes horribly wrong!");
+
+ if (randint(100) < p_ptr->skill_sav)
+ {
+ msg_print("You manage to wrest your body back under control.");
+ return;
+ }
+
+ set_mimic(30, resolve_mimic_name("Abomination"), get_skill(SKILL_MIMICRY));
+ }
+
+ /* Success */
+ else
+ {
+ set_mimic(k_info[o_ptr->k_idx].pval2 + get_skill_scale(SKILL_MIMICRY, 1000), o_ptr->pval2, get_skill(SKILL_MIMICRY));
+ }
+ }
+
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+}
+
+static bool_ mimic_forbid_travel(void *, void *, void *)
+{
+ 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_new(HOOK_FORBID_TRAVEL, mimic_forbid_travel, "mimic_forbid_travel", NULL);
+ 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(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_FRAME);
+
+ /* 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);
+}
+
+
+/*
+ * 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_WIPE | PR_FRAME | PR_MAP;
+ energy_use = 100;
+ }
+}
+
+
+/*
+ * Return percentage chance of spell failure.
+ */
+int spell_chance_random(random_spell* rspell)
+{
+ int chance, minfail;
+
+
+ /* Extract the base spell failure rate */
+ chance = rspell->level + 10;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (get_skill(SKILL_THAUMATURGY) - rspell->level);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1);
+
+ /* Not enough mana to cast */
+ if (rspell->mana > p_ptr->csp)
+ {
+ chance += 5 * (rspell->mana - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]];
+
+ /* Failure rate */
+ return clamp_failure_chance(chance, minfail);
+}
+
+
+
+
+/*
+ * Print a batch of spells.
+ */
+static void print_spell_batch(int batch, int max)
+{
+ char buff[80];
+
+ random_spell* rspell;
+
+ int i;
+
+
+ prt(format(" %-30s Lev Fail Mana Damage ", "Name"), 1, 20);
+
+ for (i = 0; i < max; i++)
+ {
+ rspell = &random_spells[batch * 10 + i];
+
+ if (rspell->untried)
+ {
+ strnfmt(buff, 80, " %c) %-30s (Spell untried) ",
+ I2A(i), rspell->name);
+
+ }
+ else
+ {
+ strnfmt(buff, 80, " %c) %-30s %3d %4d%% %3d %3dd%d ",
+ I2A(i), rspell->name,
+ rspell->level, spell_chance_random(rspell), rspell->mana,
+ rspell->dam_dice, rspell->dam_sides);
+ }
+
+ prt(buff, 2 + i, 20);
+ }
+
+ prt("", 2 + i, 20);
+}
+
+
+
+/*
+ * List ten random spells and ask to pick one.
+ */
+static random_spell* select_spell_from_batch(int batch)
+{
+ 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, A-%c to browse, / to rename, - to comment) Select a power: ",
+ I2A(mut_max - 1), I2A(mut_max - 1) - 'a' + 'A');
+
+ prt(tmp, 0, 0);
+
+ while (1)
+ {
+ /* Print power list */
+ print_spell_batch(batch, mut_max);
+
+ /* Get a command */
+ which = inkey();
+
+ /* Abort */
+ if (which == ESCAPE)
+ {
+ /* No selection */
+ ret = NULL;
+
+ /* Leave the command loop */
+ break;
+
+ }
+
+ /* 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
+ */
+static random_spell* select_spell()
+{
+ 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)
+ {
+ Term_load();
+
+ ret = NULL;
+
+ break;
+ }
+
+ if (which == '\r')
+ {
+ if (batch_max == 0)
+ {
+ Term_load();
+
+ ret = select_spell_from_batch(0);
+
+ break;
+ }
+
+ continue;
+ }
+
+ which = tolower(which);
+ if (isalpha(which) && (A2I(which) <= batch_max))
+ {
+ Term_load();
+
+ ret = select_spell_from_batch(A2I(which));
+
+ break;
+ }
+ else
+ {
+ bell();
+ }
+ }
+
+ /* 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();
+
+ 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_FRAME);
+
+ 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_FRAME);
+}
+
+
+/*
+ * Brand some ammunition. Used by Cubragol and a mage spell. The spell was
+ * moved here from cmd6.c where it used to be for Cubragol only. I've also
+ * expanded it to do either frost, fire or venom, at random. -GJW -KMW-
+ */
+void brand_ammo(int brand_type, int bolts_only)
+{
+ int a;
+
+ for (a = 0; a < INVEN_PACK; a++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[a];
+
+ if (bolts_only && (o_ptr->tval != TV_BOLT)) continue;
+
+ if (!bolts_only && (o_ptr->tval != TV_BOLT) &&
+ (o_ptr->tval != TV_ARROW) && (o_ptr->tval != TV_SHOT))
+ continue;
+
+ if (!artifact_p(o_ptr) && !ego_item_p(o_ptr) &&
+ !cursed_p(o_ptr))
+ break;
+ }
+
+ /* Enchant the ammo (or fail) */
+ if ((a < INVEN_PACK) && (rand_int(100) < 50))
+ {
+ object_type *o_ptr = &p_ptr->inventory[a];
+ const char *ammo_name;
+ const 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 (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_FRAME);
+ }
+ }
+ }
+ 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 object_filter_t const &item_tester_hook_convertible()
+{
+ using namespace object_filter;
+ static auto instance =
+ Or(
+ TVal(TV_JUNK),
+ TVal(TV_SKELETON));
+ return instance;
+}
+
+
+/*
+ * 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;
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Convert which item? ",
+ "You have no item to convert.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_convertible())) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some arrows */
+ object_prep(q_ptr, lookup_kind(TV_ARROW, m_bonus(1, dun_level) + 1));
+ q_ptr->number = (byte)rand_range(15, 25);
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ msg_print("You make some ammo.");
+
+ inc_stack_size(item, -1);
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+
+ /**********Create bolts*********/
+ else if (ext == 3)
+ {
+ int item;
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Convert which item? ",
+ "You have no item to convert.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_convertible())) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some bolts */
+ object_prep(q_ptr, lookup_kind(TV_BOLT, m_bonus(1, dun_level) + 1));
+ q_ptr->number = (byte)rand_range(15, 25);
+ if (!artifact_p(q_ptr))
+ q_ptr->number = (byte)rand_range(15, 30);
+ else
+ q_ptr->number = 1;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ apply_magic(q_ptr, dun_level, TRUE, TRUE, (magik(20)) ? TRUE : FALSE);
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ msg_print("You make some ammo.");
+
+ inc_stack_size(item, -1);
+
+ (void)inven_carry(q_ptr, FALSE);
+ }
+}
+
+/*
+ * Control whether shots are allowed to pierce
+ */
+void do_cmd_set_piercing(void)
+{
+ char ch;
+ char com[80];
+
+ if ((get_skill(SKILL_BOW) <= 25) && (get_skill(SKILL_XBOW) <= 25) &&
+ (get_skill(SKILL_SLING) <= 25))
+ {
+ msg_print("You can't fire piercing shots yet.");
+ return;
+ }
+
+ strnfmt(com, 80, "Allow shots to pierce? ");
+
+ while (TRUE)
+ {
+ if (!get_com(com, &ch))
+ {
+ break;
+ }
+ if ((ch == 'Y') || (ch == 'y'))
+ {
+ p_ptr->use_piercing_shots = 1;
+ msg_print("Piercing shots activated.");
+ break;
+ }
+ if ((ch == 'N') || (ch == 'n'))
+ {
+ p_ptr->use_piercing_shots = 0;
+ msg_print("Piercing shots deactivated.");
+ break;
+ }
+ }
+}
+/*
+ * Helper function to describe necro powers
+ */
+void necro_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_NECROMANCY);
+
+ strcpy(p, "");
+
+ switch (power)
+ {
+ case 0:
+ {
+ if (p_ptr->to_s)
+ strnfmt(p, 80, " power %dd%d+%d", 2 + (plev * 2 / 3), 4, (p_ptr->to_s * 2));
+ else
+ strnfmt(p, 80, " power %dd%d", 2 + (plev * 2 / 3), 4);
+ break;
+ }
+ case 2:
+ {
+ strnfmt(p, 80, " dur d%d+%d", 100 + (plev * 4), 200 + (plev * 3));
+ break;
+ }
+ case 3:
+ {
+ strnfmt(p, 80, " dur d%d+%d", 30 + (plev * 2), 50 + plev);
+ break;
+ }
+ }
+}
+
+
+/*
+ * Cast a Necromancy spell
+ */
+void do_cmd_necromancer(void)
+{
+ int n = 0, b = 0;
+ int chance;
+ int dir;
+ int minfail = 0;
+ int plev = get_skill(SKILL_NECROMANCY);
+ magic_power spell;
+ int to_s2 = p_ptr->to_s / 2;
+ int mto_s2 = p_ptr->to_s / 2;
+
+
+ if (mto_s2 == 0) mto_s2 = 1;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, necro_powers, MAX_NECRO_POWERS, necro_info,
+ get_skill(SKILL_NECROMANCY), A_CON)) return;
+
+ spell = necro_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_CON]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_CON]];
+
+ /* Failure rate */
+ chance = clamp_failure_chance(chance, minfail);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+
+ if (randint(100) < (chance / 2))
+ {
+ /* Backfire */
+ b = randint(100);
+ if (b < 10)
+ {
+ msg_print("Oh, no! You become undead!");
+
+ p_ptr->necro_extra |= CLASS_UNDEAD;
+ p_ptr->necro_extra2 = 2 * plev;
+ msg_format("You have to kill %d monster%s to be brought back to life.",
+ p_ptr->necro_extra2,
+ (p_ptr->necro_extra2 == 1) ? "" : "s");
+
+ /* MEGA-HACK !!! */
+ calc_hitpoints();
+
+ /* Enforce maximum */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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_FRAME);
+
+ /* 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(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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/*
+ * Hook to determine if an object is "runestone"
+ */
+static bool item_tester_hook_runestone(object_type const *o_ptr)
+{
+ return ((o_ptr->tval == TV_RUNE2) &&
+ (o_ptr->sval == RUNE_STONE) &&
+ (o_ptr->pval == 0));
+}
+
+static bool item_tester_hook_runestone_full(object_type const *o_ptr)
+{
+ return ((o_ptr->tval == TV_RUNE2) &&
+ (o_ptr->sval == RUNE_STONE) &&
+ (o_ptr->pval != 0));
+}
+
+/*
+ * math.h(sqrt) is banned of angband so ... :)
+ */
+s32b sroot(s32b n)
+{
+ s32b i = n / 2;
+
+ if (n < 2) return (n);
+
+ while (1)
+ {
+ s32b err = (i - n / (i + 1)) / 2;
+
+ if (!err) break;
+
+ i -= err;
+ }
+
+ return ((n / i < i) ? (i - 1) : i);
+}
+
+
+/*
+ * Damage formula, for runes
+ */
+void rune_calc_power(s32b *power, s32b *powerdiv)
+{
+ /* Not too weak power(paranoia) */
+ *power = (*power < 1) ? 1 : *power;
+ *power += 3;
+
+ *power = 37 * sroot(*power) / 10;
+
+ /* To reduce the high level power, while increasing the low levels */
+ *powerdiv = *power / 3;
+ if (*powerdiv < 1) *powerdiv = 1;
+
+ /* Use the spell multiplicator */
+ *power *= (p_ptr->to_s / 2) ? (p_ptr->to_s / 2) : 1;
+}
+
+
+/*
+ * Return percentage chance of runespell failure.
+ */
+int spell_chance_rune(rune_spell* spell)
+{
+ int chance, minfail;
+
+ s32b power = spell->mana, power_rune = 0, powerdiv = 0;
+
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ power_rune += 4;
+ }
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ power_rune += 3;
+ }
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ power_rune += 2;
+ }
+ if (spell->rune2 & RUNE_RAY)
+ {
+ power_rune += 1;
+ }
+
+ rune_calc_power(&power, &powerdiv);
+
+ chance = (5 * power_rune) + (power);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_DEX]] - 1);
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_DEX]];
+
+ /* Return the chance */
+ return clamp_failure_chance(chance, minfail);
+}
+
+
+/*
+ * Combine the Runes
+ */
+int rune_exec(rune_spell *spell, int cost)
+{
+ int dir, power_rune = 0, mana_used, plev = get_skill(SKILL_RUNECRAFT);
+
+ int chance;
+
+ s32b power, powerdiv;
+
+ int rad = 0, ty = -1, tx = -1, dam = 0, flg = 0;
+
+
+ if (spell->rune2 & RUNE_POWER_SURGE)
+ {
+ power_rune += 4;
+ }
+ if (spell->rune2 & RUNE_ARMAGEDDON)
+ {
+ power_rune += 3;
+ }
+ if (spell->rune2 & RUNE_SPHERE)
+ {
+ power_rune += 2;
+ }
+ if (spell->rune2 & RUNE_RAY)
+ {
+ power_rune += 1;
+ }
+
+
+ power = spell->mana;
+
+ if (cost && ((power * cost / 100) > p_ptr->csp - (power_rune * (plev / 5))))
+ {
+ power = p_ptr->csp - (power_rune * (plev / 5));
+ mana_used = power + (power_rune * (plev / 5));
+ }
+ else
+ {
+ mana_used = (power * cost / 100) + (power_rune * (plev / 5));
+ }
+
+ rune_calc_power(&power, &powerdiv);
+
+ dam = damroll(powerdiv, power);
+
+ if (wizard) msg_format("Rune %dd%d = dam %d", powerdiv, power, dam);
+
+ /* Extract the base spell failure rate */
+ chance = spell_chance_rune(spell);
+
+ /* Failure ? */
+ if (rand_int(100) < chance)
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+ char sfail[80];
+
+ /* Flush input if told so */
+ if (flush_failure) flush();
+
+ /* Insane players can see something strange */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("sfail.txt", sfail);
+ msg_format("A cloud of %s appears above you.", sfail);
+ }
+
+ /* Normal failure messages */
+ else
+ {
+ msg_print("You failed to get the spell off!");
+ }
+
+ sound(SOUND_FAIL);
+
+ if (is_magestaff()) energy_use = 80;
+ else energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_FRAME);
+ 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_FRAME);
+
+ 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)
+{
+ s32b rune_combine = 0;
+
+ /* Lambda to use for selecting the secondary rune(s) */
+ auto rune2_filter = [&](object_type const *o_ptr) -> bool {
+ return ((o_ptr->tval == TV_RUNE2) &&
+ (o_ptr->sval != RUNE_STONE) &&
+ (!(rune_combine & BIT(o_ptr->sval))));
+ };
+
+ /* Prompt */
+ const char *const q = "Use which rune? ";
+ const char *const s = "You have no rune to use.";
+
+ /* Extract first rune for the base effect */
+ int type = 0;
+ {
+ int item;
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR), object_filter::TVal(TV_RUNE1)))
+ {
+ return FALSE;
+ }
+
+ object_type *o_ptr = get_object(item);
+ type = o_ptr->sval;
+ }
+
+ /* Choose secondary rune(s) */
+ int rune2 = 0;
+ while (1)
+ {
+ int item;
+ if (!get_item(&item, q, nullptr, (USE_INVEN | USE_FLOOR), rune2_filter))
+ {
+ break;
+ }
+
+ object_type *o_ptr = get_object(item);
+
+ rune_combine |= 1 << o_ptr->sval;
+ rune2 |= 1 << o_ptr->sval;
+ }
+
+ if (!rune2)
+ {
+ msg_print("You have not selected a second rune!");
+ return (FALSE);
+ }
+
+ int power_rune = 0;
+ int plev = get_skill(SKILL_RUNECRAFT);
+ s32b 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_FRAME);
+}
+
+
+/*
+ * 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, int *s_idx)
+{
+ char tmp[160];
+
+ char out_val[30];
+
+ char which;
+
+ int mut_max = 10;
+
+ rune_spell* ret;
+
+
+ character_icky = TRUE;
+
+ 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);
+
+ while (1)
+ {
+ Term_save();
+
+ print_runespell_batch(batch, mut_max);
+
+ which = inkey();
+
+ Term_load();
+
+ 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();
+ }
+ }
+ }
+
+ character_icky = FALSE;
+
+ return (ret);
+}
+
+
+/*
+ * Pick a random spell from a menu
+ */
+
+rune_spell* select_runespell(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, 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), 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(&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_FRAME);
+}
+
+
+/*
+ * Cast a runespell from a carved runestone
+ */
+void do_cmd_runestone()
+{
+ rune_spell s_ptr;
+
+ 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;
+ }
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Cast from which runestone? ",
+ "You have no runestone to cast from.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_runestone_full))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ s_ptr.type = o_ptr->pval;
+ s_ptr.rune2 = o_ptr->pval2;
+ s_ptr.mana = o_ptr->pval3;
+
+ /* Execute less mana */
+ p_ptr->csp -= rune_exec(&s_ptr, 75);
+
+ /* Safety :) */
+ if (p_ptr->csp < 0) p_ptr->csp = 0;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_FRAME);
+}
+
+
+/*
+ * 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 learned the maximum 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_FRAME);
+}
+
+
+/*
+ * Carve a runespell onto a Runestone
+ */
+void do_cmd_rune_carve()
+{
+ rune_spell s_ptr;
+
+ 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;
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Use which runestone? ",
+ "You have no runestone to use.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_runestone))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ o_ptr->pval = s_ptr.type;
+ o_ptr->pval2 = s_ptr.rune2;
+ o_ptr->pval3 = s_ptr.mana;
+
+ /* Start with nothing */
+ strcpy(out_val, "");
+
+ /* Use old inscription */
+ if (o_ptr->note)
+ {
+ /* Start with the old inscription */
+ strcpy(out_val, quark_str(o_ptr->note));
+ }
+
+ /* Get a new inscription (possibly empty) */
+ if (get_string("Name this runestone: ", out_val, 80))
+ {
+ /* Save the inscription */
+ o_ptr->note = quark_add(out_val);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+
+ /* Delete the runes */
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ if (o_ptr->k_idx)
+ {
+ bool_ do_del = FALSE;
+
+ if ((o_ptr->tval == TV_RUNE1) && (o_ptr->sval == s_ptr.type)) do_del = TRUE;
+ if ((o_ptr->tval == TV_RUNE2) && (BIT(o_ptr->sval) & s_ptr.rune2)) do_del = TRUE;
+
+ if (do_del)
+ {
+ inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE);
+ }
+ }
+ }
+
+ /* Take a turn -- Carving takes a LONG time */
+ energy_use = 400;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ p_ptr->redraw |= (PR_FRAME);
+}
+
+
+/*
+ * 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(&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_FRAME);
+}
+
+
+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 object_filter_t const &item_tester_hook_totemable()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ TVal(TV_CORPSE),
+ Or(
+ SVal(SV_CORPSE_CORPSE),
+ SVal(SV_CORPSE_SKELETON)));
+ return instance;
+}
+
+
+/*
+ * Summoners
+ */
+void do_cmd_summoner_extract()
+{
+ object_type forge, *q_ptr;
+
+ /* 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;
+ }
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Use which corpse? ",
+ "You have no corpse to use.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_totemable()))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ bool_ partial;
+ if (r_info[o_ptr->pval2].flags1 & RF1_UNIQUE)
+ {
+ partial = FALSE;
+ }
+ else
+ {
+ partial = get_check("Do you want to create a partial totem?");
+ }
+
+ int r = o_ptr->pval2;
+
+ inc_stack_size(item, -1);
+
+ if (magik(r_info[o_ptr->pval2].level - get_skill(SKILL_SUMMON)))
+ {
+ msg_print("You failed to extract a totem.");
+ energy_use += 100;
+ return;
+ }
+
+ /* Prepare for object creation */
+ q_ptr = &forge;
+
+ /* Create the object */
+ object_prep(q_ptr, lookup_kind(TV_TOTEM, partial ? 1 : 2));
+ q_ptr->pval = r;
+ q_ptr->pval2 = 0;
+ q_ptr->number = 1;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You extract a totem from the dead corpse.");
+ energy_use += 100;
+}
+
+
+void summon_true(int r_idx, int item)
+{
+ int i, status, x = 1, y = 1, rx, ry = 0, chance;
+
+ bool_ used;
+
+ monster_race *r_ptr = &r_info[r_idx];
+
+
+ /* Uniques are less likely to be nice */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ /* Because it's unique, it will always be destroyed */
+ used = TRUE;
+
+ /* About twice as hard as non-uniques */
+ chance = (get_skill(SKILL_SUMMON) * 70 / (r_ptr->level + 1));
+
+ if (magik(chance))
+ {
+ status = MSTATUS_PET;
+ }
+ else
+ {
+ status = MSTATUS_ENEMY;
+ }
+ }
+
+ /* Non-uniques are easier to handle */
+ else
+ {
+ if (get_skill(SKILL_SUMMON) == 0)
+ {
+ used = TRUE;
+ }
+ else
+ {
+ /* It can be used multiple times */
+ used = FALSE;
+
+ /* But it is not 100% sure (note: skill > 0) */
+ chance = (r_ptr->level * 25 / get_skill(SKILL_SUMMON));
+ if (magik(chance)) used = TRUE;
+ }
+
+ chance = (get_skill(SKILL_SUMMON) * 130 / (r_ptr->level + 1));
+
+ if (magik(chance))
+ {
+ status = MSTATUS_PET;
+ }
+ else
+ {
+ status = MSTATUS_ENEMY;
+ }
+ }
+
+ /* Find a grid where the monster is summoned */
+ for (i = 0; i < 40; i++)
+ {
+ rx = (rand_int(8) - 4) + p_ptr->px;
+ ry = (rand_int(8) - 4) + p_ptr->py;
+ if (in_bounds(ry, rx) && cave_empty_bold(ry, rx))
+ {
+ x = rx;
+ y = ry;
+ break;
+ }
+ }
+
+ /* No room found */
+ if (i == 40)
+ {
+ msg_print("The summoning fails due to lack of room.");
+ return;
+ }
+
+ /* Summon the monster */
+ bypass_r_ptr_max_num = TRUE;
+ if (!(i = place_monster_one (y, x, r_idx, 0, 0, status)))
+ {
+ msg_print("The summoning fails.");
+ }
+ else
+ {
+ m_list[i].status = status;
+ m_list[i].mflag |= MFLAG_NO_DROP;
+ }
+ bypass_r_ptr_max_num = FALSE;
+
+ /* Destroy the totem if the used flag is set */
+ if (used)
+ {
+ /* Eliminate the totem */
+ inc_stack_size(item, -1);
+ }
+
+ /* Done */
+ return;
+}
+
+
+void do_cmd_summoner_summon()
+{
+ int item, x = 1, y = 1, rx, ry, m_idx = 0, i;
+
+ cptr q, s;
+
+ monster_type *m_ptr;
+
+
+ /* Which Totem? */
+ q = "Summon from which Totem?";
+ s = "There are no totems to summon from!";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR), object_filter::TVal(TV_TOTEM))) return;
+
+ /* Access the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* True Totems have their own function. */
+ if (o_ptr->sval == 2)
+ {
+ summon_true(o_ptr->pval, item);
+ return;
+ }
+
+ /* Handle partial totems */
+
+ /* Find a grid where the monster is summoned */
+ for (i = 0; i < 40; i++)
+ {
+ rx = (rand_int(8) - 4) + p_ptr->px;
+ ry = (rand_int(8) - 4) + p_ptr->py;
+ if (in_bounds(ry, rx) && cave_empty_bold(ry, rx))
+ {
+ x = rx;
+ y = ry;
+ break;
+ }
+ }
+
+ /* No room found */
+ if (i == 40)
+ {
+ msg_print("The summoning fails due to lack of room.");
+ return;
+ }
+
+ /* Summon the monster */
+ bypass_r_ptr_max_num = TRUE;
+ place_monster_one_no_drop = TRUE;
+ m_idx = place_monster_one(y, x, o_ptr->pval, 0, 0, MSTATUS_PET);
+ bypass_r_ptr_max_num = FALSE;
+
+ /* Failure. */
+ if (!m_idx)
+ {
+ msg_print("The summoning fails.");
+ }
+
+ /* Mark the monster as a "partial" ally */
+ m_ptr = &m_list[m_idx];
+ m_ptr->mflag |= MFLAG_PARTIAL | MFLAG_NO_DROP;
+}
+
+
+void do_cmd_summoner(void)
+{
+ int ext = 0;
+
+ char ch;
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* not if blind */
+ if (p_ptr->blind || no_lite())
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Select what to do */
+ while (TRUE)
+ {
+ if (!get_com("[E]xtract a totem, [S]ummon", &ch))
+ {
+ ext = 0;
+ break;
+ }
+ if ((ch == 'E') || (ch == 'e'))
+ {
+ ext = 1;
+ break;
+ }
+ if ((ch == 's') || (ch == 'S'))
+ {
+ ext = 2;
+ break;
+ }
+ }
+
+ switch (ext)
+ {
+ case 1:
+ {
+ do_cmd_summoner_extract();
+ break;
+ }
+
+ case 2:
+ {
+ do_cmd_summoner_summon();
+ break;
+ }
+ }
+}
+
+
+/*
+ * Dodge Chance Feedback.
+ */
+void use_ability_blade(void)
+{
+ int chance = p_ptr->dodge_chance - ((dun_level * 5) / 6);
+
+ if (chance < 0) chance = 0;
+ if (wizard)
+ {
+ msg_format("You have exactly %d chances of dodging a level %d monster.", chance, dun_level);
+ }
+
+ if (chance < 5)
+ {
+ msg_format("You have almost no chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 10)
+ {
+ msg_format("You have a slight chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 20)
+ {
+ msg_format("You have a significant chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 40)
+ {
+ msg_format("You have a large chance of dodging a level %d monster.", dun_level);
+ }
+ else if (chance < 70)
+ {
+ msg_format("You have a high chance of dodging a level %d monster.", dun_level);
+ }
+ else
+ {
+ msg_format("You will usually dodge successfully a level %d monster.", dun_level);
+ }
+
+ return;
+}
+
+/*
+ * Helper function to describe symbiotic powers
+ */
+void symbiotic_info(char *p, int power)
+{
+ int plev = get_skill(SKILL_SYMBIOTIC);
+
+ strcpy(p, "");
+
+ switch (power)
+ {
+ case 2:
+ {
+ strnfmt(p, 80, " power %d", plev * 3);
+ break;
+ }
+ case 5:
+ {
+ strnfmt(p, 80, " heal %d%%", 15 + get_skill_scale(SKILL_SYMBIOTIC, 35));
+ break;
+ }
+ }
+}
+
+
+/*
+ * Cast a symbiotic spell
+ */
+void do_cmd_symbiotic(void)
+{
+ int n = 0;
+ int chance;
+ int minfail = 0;
+ int plev = get_skill(SKILL_SYMBIOTIC);
+ magic_power spell;
+
+ /* Get the carried monster */
+ object_type *o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ /* No magic */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ return;
+ }
+
+ /* No magic */
+ if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ return;
+ }
+
+ /* not if confused */
+ if (p_ptr->confused)
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* get power */
+ if (!get_magic_power(&n, symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info,
+ get_skill(SKILL_SYMBIOTIC), A_INT)) return;
+
+ spell = symbiotic_powers[n];
+
+ /* Verify "dangerous" spells */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ /* Warning */
+ msg_print("You do not have enough mana to use this power.");
+
+ /* Verify */
+ if (!get_check("Attempt it anyway? ")) return;
+ }
+
+ /* Spell failure chance */
+ chance = spell.fail;
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= 3 * (plev - spell.min_lev);
+
+ /* Reduce failure rate by INT/WIS adjustment */
+ chance -= 3 * (adj_mag_stat[p_ptr->stat_ind[A_INT]] - 1);
+
+ /* Not enough mana to cast */
+ if (spell.mana_cost > p_ptr->csp)
+ {
+ chance += 5 * (spell.mana_cost - p_ptr->csp);
+ }
+
+ /* Extract the minimum failure rate */
+ minfail = adj_mag_fail[p_ptr->stat_ind[A_INT]];
+
+ /* Failure rate */
+ chance = clamp_failure_chance(chance, minfail);
+
+ /* Failed spell */
+ if (rand_int(100) < chance)
+ {
+ if (flush_failure) flush();
+ msg_format("You failed to concentrate hard enough!");
+ sound(SOUND_FAIL);
+ }
+ else
+ {
+ sound(SOUND_ZAP);
+
+ /* spell code */
+ switch (n)
+ {
+ case 0:
+ {
+ int dir, x, y;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ 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];
+ auto const r_ptr = m_ptr->race();
+
+ 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;
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Awaken which monster? ",
+ "You have no monster to awaken.",
+ (USE_FLOOR),
+ object_filter::TVal(TV_HYPNOS)))
+ {
+ return;
+ }
+
+ object_type *o_ptr = &o_list[0 - item];
+
+ d = 2;
+ while (d < 100)
+ {
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ if (cave_floor_bold(y, x) && (!cave[y][x].m_idx)) break;
+
+ d++;
+ }
+
+ if (d >= 100) return;
+
+ if ((m_idx = place_monster_one(y, x, o_ptr->pval, 0, FALSE, MSTATUS_PET)) == 0) return;
+
+ /* TODO fix this hack hack hack hackity hack with ToME 3 flags */
+ /* Have to be careful here; releasing the symbiote into a
+ * dungeon with leveled monsters will level the symbiote
+ * before we can get hold of it. We'll be nice and use the
+ * larger of the saved exp and the exp that the newly-generated
+ * monster starts with. */
+ m_ptr = &m_list[m_idx];
+ if (m_ptr->exp < o_ptr->exp)
+ {
+ m_ptr->exp = o_ptr->exp;
+ monster_check_experience(m_idx, TRUE);
+ if (m_ptr->level != o_ptr->elevel)
+ cmsg_format(TERM_VIOLET, "ERROR: level-%d HYPNOS becomes level-%d symbiote", o_ptr->elevel, m_ptr->level);
+ }
+ m_ptr->hp = o_ptr->pval2;
+ m_ptr->maxhp = o_ptr->pval3;
+
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ break;
+ }
+
+ /* Charm */
+ case 2:
+ {
+ int dir;
+
+ if (!get_aim_dir(&dir)) return;
+
+ fire_bolt(GF_CHARM_UNMOVING, dir, plev * 3);
+
+ break;
+ }
+
+ /* Life Share */
+ case 3:
+ {
+ s32b percent1, percent2;
+
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ percent1 = p_ptr->chp;
+ percent1 = (percent1 * 100) / p_ptr->mhp;
+
+ percent2 = o_ptr->pval2;
+ percent2 = (percent2 * 100) / o_ptr->pval3;
+
+ /* Now get the average */
+ percent1 = (percent1 + percent2) / 2;
+
+ /* And set the hp of monster & player to it */
+ p_ptr->chp = (percent1 * p_ptr->mhp) / 100;
+ o_ptr->pval2 = (percent1 * o_ptr->pval3) / 100;
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_FRAME);
+
+ break;
+ }
+
+ /* Minor Symbiotic Powers */
+ case 4:
+ {
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ if (0 > use_symbiotic_power(o_ptr->pval, FALSE, FALSE, TRUE))
+ return;
+
+ break;
+ }
+
+ /* Heal Symbiote */
+ case 5:
+ {
+ int hp;
+
+ if (!o_ptr->k_idx)
+ {
+ msg_print("You are not in symbiosis.");
+ break;
+ }
+
+ hp = o_ptr->pval3 * (15 + get_skill_scale(SKILL_SYMBIOTIC, 35)) / 100;
+ o_ptr->pval2 += hp;
+ if (o_ptr->pval2 > o_ptr->pval3) o_ptr->pval2 = o_ptr->pval3;
+
+ msg_format("%s is healed.", symbiote_name(TRUE));
+
+ /* Display the monster hitpoints */
+ p_ptr->redraw |= (PR_FRAME);
+
+ 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(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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/*
+ * Boulder creation .. sorry :)
+ */
+void do_cmd_create_boulder()
+{
+ int x, y, dir;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ /* Granite -- How about other wall types? */
+ if (((c_ptr->feat >= FEAT_WALL_EXTRA) && (c_ptr->feat <= FEAT_WALL_SOLID)) ||
+ ((c_ptr->feat >= FEAT_MAGMA_H) && (c_ptr->feat <= FEAT_QUARTZ_K)) ||
+ ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_QUARTZ)))
+ {
+ object_type forge;
+ object_type *q_ptr;
+
+ (void)wall_to_mud(dir);
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Hack -- Give the player some shots */
+ object_prep(q_ptr, lookup_kind(TV_JUNK, SV_BOULDER));
+ q_ptr->number = (byte)rand_range(2, 5);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+ q_ptr->discount = 90;
+ q_ptr->found = OBJ_FOUND_SELFMADE;
+
+ (void)inven_carry(q_ptr, FALSE);
+
+ msg_print("You make some boulders.");
+
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Take a turn */
+ energy_use = 100;
+ }
+}
+
+/*
+ * Clamp failure chance
+ */
+extern int clamp_failure_chance(int chance, int minfail)
+{
+ if (minfail < 0) minfail = 0;
+
+ /* Minimum failure rate */
+ if (chance < minfail) chance = minfail;
+
+ /* Stunning makes spells harder */
+ if (p_ptr->stun > 50) chance += 25;
+ else if (p_ptr->stun) chance += 15;
+
+ /* Always a 5 percent chance of working */
+ if (chance > 95) chance = 95;
+
+ return chance;
+}
diff --git a/src/cmd7.hpp b/src/cmd7.hpp
new file mode 100644
index 00000000..162e5461
--- /dev/null
+++ b/src/cmd7.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "h-basic.h"
+#include "rune_spell_fwd.hpp"
+#include "object_type_fwd.hpp"
+
+extern void do_cmd_pray(void);
+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 void do_cmd_summoner(void);
+extern void do_cmd_mindcraft(void);
+extern void do_cmd_mimic(void);
+extern void use_ability_blade(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 do_cmd_runecrafter(void);
+extern void do_cmd_symbiotic(void);
+extern s32b sroot(s32b n);
+extern int clamp_failure_chance(int chance, int minfail);
diff --git a/src/config.h b/src/config.h
new file mode 100644
index 00000000..290ec624
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,131 @@
+/* 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"),
+ * and "USE_X11" (allow basic use with Unix X11).
+ *
+ * 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".
+ */
+
+
+/*
+ * 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: Include "ncurses.h" instead of "curses.h" in "main-gcu.c"
+ */
+/* #define USE_NCURSES */
+
+
+
+/*
+ * OPTION: Maximum flow depth
+ */
+#define MONSTER_FLOW_DEPTH 32
+
+
+
+
+/*
+ * 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
+
+
+/*
+ * Where to put the user's files.
+ */
+#define PRIVATE_USER_PATH "~/.tome"
+
+
+/*
+ * 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
+
+
+
+/*
+ * Allow makefiles to override the default file mode
+ */
+#ifndef FILE_MODE
+#define FILE_MODE 0644
+#endif
diff --git a/src/corrupt.cc b/src/corrupt.cc
new file mode 100644
index 00000000..f182a9e2
--- /dev/null
+++ b/src/corrupt.cc
@@ -0,0 +1,1003 @@
+#include "corrupt.hpp"
+
+#include "init1.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_type.hpp"
+#include "stats.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+/**
+ * Corruptions
+ */
+typedef struct corruption_type corruption_type;
+struct corruption_type
+{
+ int modules[3]; /* Modules where this corruption is available; terminated with -1 entry */
+ byte color;
+ cptr group;
+ cptr name;
+ cptr get_text;
+ cptr lose_text; /* If NULL, the corruption is NOT removable by any means */
+ cptr desc;
+ s16b depends[5]; /* terminated by a -1 entry */
+ s16b opposes[5]; /* terminated by a -1 entry */
+ void (*gain_callback)(); /* callback to invoke when gained */
+ s16b power; /* index of granted power if >= 0, ignored otherwise */
+};
+
+/**
+ * Vampire corruption helpers
+ */
+
+static void subrace_add_power(player_race_mod *rmp_ptr, int power)
+{
+ int i;
+
+ for (i=0; i<4; i++)
+ {
+ if (rmp_ptr->powers[i] == -1)
+ {
+ rmp_ptr->powers[i] = power;
+ return;
+ }
+ }
+}
+
+static void player_gain_vampire_teeth()
+{
+ player_race_mod *rmp_ptr = NULL;
+
+ switch_subrace(SUBRACE_SAVE, TRUE);
+
+ rmp_ptr = &race_mod_info[SUBRACE_SAVE];
+ subrace_add_power(rmp_ptr, PWR_VAMPIRISM);
+ rmp_ptr->flags1 = rmp_ptr->flags1
+ | PR1_VAMPIRE
+ | PR1_UNDEAD
+ | PR1_NO_SUBRACE_CHANGE;
+}
+
+static void player_gain_vampire_strength()
+{
+ player_race_mod *rmp_ptr = &race_mod_info[SUBRACE_SAVE];
+ /* Apply the bonuses/penalities */
+ rmp_ptr->r_mhp = rmp_ptr->r_mhp + 1;
+ rmp_ptr->r_exp = rmp_ptr->r_exp + 100;
+
+ rmp_ptr->r_adj[A_STR] = rmp_ptr->r_adj[A_STR] + 3;
+ rmp_ptr->r_adj[A_INT] = rmp_ptr->r_adj[A_INT] + 2;
+ rmp_ptr->r_adj[A_WIS] = rmp_ptr->r_adj[A_WIS] - 3;
+ rmp_ptr->r_adj[A_DEX] = rmp_ptr->r_adj[A_DEX] - 2;
+ rmp_ptr->r_adj[A_CON] = rmp_ptr->r_adj[A_CON] + 1;
+ rmp_ptr->r_adj[A_CHR] = rmp_ptr->r_adj[A_CHR] - 4;
+
+ /* be reborn! */
+ do_rebirth();
+ cmsg_print(TERM_L_DARK, "You feel death slipping inside.");
+}
+
+static void player_gain_vampire()
+{
+ player_race_mod *rmp_ptr = &race_mod_info[SUBRACE_SAVE];
+
+ /* Be a Vampire and be proud of it */
+ cptr title = rmp_ptr->title;
+ if (streq(title, "Vampire"))
+ {
+ title = "Vampire";
+ rmp_ptr->place = FALSE;
+ set_subrace_title(rmp_ptr, title);
+ }
+ else
+ {
+ char buf[512];
+ sprintf(buf, "Vampire %s", title);
+ set_subrace_title(rmp_ptr, buf);
+ }
+
+ /* Bonus/and .. not bonus :) */
+ rmp_ptr->flags1 = rmp_ptr->flags1 | PR1_HURT_LITE;
+ rmp_ptr->oflags2[2] = rmp_ptr->oflags2[2]
+ | TR2_RES_POIS
+ | TR2_RES_NETHER
+ | TR2_RES_COLD
+ | TR2_RES_DARK
+ | TR2_HOLD_LIFE;
+ rmp_ptr->oflags3[2] = rmp_ptr->oflags3[2]
+ | TR3_LITE1;
+}
+
+/**
+ * Corruptions
+ */
+corruption_type corruptions[CORRUPTIONS_MAX] =
+{
+ /*
+ * BALROG corruptions
+ */
+
+ { /* 0 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_ORANGE,
+ NULL /* no group */,
+ "Balrog Aura",
+ "A corrupted wall of flames surrounds you.",
+ "The wall of corrupted flames abandons you.",
+ " Surrounds you with a fiery aura\n"
+ " But it can burn scrolls when you read them",
+ { -1 },
+ { -1 },
+ NULL,
+ -1,
+ },
+
+ { /* 1 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_ORANGE,
+ NULL /* no group */,
+ "Balrog Wings",
+ "Wings of shadow grow in your back.",
+ "The wings in your back fall apart.",
+ " Creates ugly, but working, wings allowing you to fly\n"
+ " But it reduces charisma by 4 and dexterity by 2",
+ { -1 },
+ { -1 },
+ NULL,
+ -1,
+ },
+
+ { /* 2 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_ORANGE,
+ NULL /* no group */,
+ "Balrog Strength",
+ "Your muscles get unnatural strength.",
+ "Your muscles get weaker again.",
+ " Provides 3 strength and 1 constitution\n"
+ " But it reduces charisma by 1 and dexterity by 3",
+ { -1 },
+ { -1 },
+ NULL,
+ -1,
+ },
+
+ { /* 3 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_YELLOW,
+ NULL /* no group */,
+ "Balrog Form",
+ "You feel the might of a Balrog inside you.",
+ "The presence of the Balrog seems to abandon you.",
+ " Allows you to turn into a Balrog at will\n"
+ " You need Balrog Wings, Balrog Aura and Balrog Strength to activate it",
+ { CORRUPT_BALROG_AURA, CORRUPT_BALROG_WINGS, CORRUPT_BALROG_STRENGTH },
+ { -1 },
+ NULL,
+ PWR_BALROG,
+ },
+
+ /*
+ * DEMON corruptions
+ */
+ { /* 4 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Demon Spirit",
+ "Your spirit opens to corrupted thoughts.",
+ "Your spirit closes again to the corrupted thoughts.",
+ " Increases your intelligence by 1\n"
+ " But reduce your charisma by 2",
+ { -1 },
+ { -1 },
+ NULL,
+ -1,
+ },
+
+ { /* 5 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Demon Hide",
+ "Your skin grows into a thick hide.",
+ "Your skin returns to a natural state.",
+ " Increases your armour class by your level\n"
+ " Provides immunity to fire at level 40\n"
+ " But reduces speed by your level / 7",
+ { -1 },
+ { -1 },
+ NULL,
+ -1,
+ },
+
+ { /* 6 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Demon Breath",
+ "Your breath becomes mephitic.",
+ "Your breath is once again normal.",
+ " Provides fire breath\n"
+ " But gives a small chance to spoil potions when you quaff them",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_BR_FIRE,
+ },
+
+ { /* 7 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_L_RED,
+ NULL /* no group */,
+ "Demon Realm",
+ "You feel more attuned to the demon realm.",
+ "You lose your attunement to the demon realm.",
+ " Provides access to the demon school skill and the use of demonic equipment\n"
+ " You need Demon Spirit, Demon Hide and Demon Breath to activate it",
+ { CORRUPT_DEMON_SPIRIT, CORRUPT_DEMON_HIDE, CORRUPT_DEMON_BREATH },
+ { -1 },
+ NULL,
+ -1,
+ },
+
+ /*
+ * Teleportation corruptions
+ */
+
+ { /* 8 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_GREEN,
+ NULL /* no group */,
+ "Random teleportation",
+ "Space seems to fizzle around you.",
+ "Space solidify again around you.",
+ " Randomly teleports you around",
+ { -1 },
+ { CORRUPT_ANTI_TELEPORT, -1 },
+ NULL,
+ -1,
+ },
+
+ { /* 9 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_GREEN,
+ NULL /* no group */,
+ "Anti-teleportation",
+ "Space continuum freezes around you.",
+ "Space continuum can once more be altered around you.",
+ " Prevents all teleportations, be it of you or monsters",
+ { -1 },
+ { CORRUPT_RANDOM_TELEPORT, -1 },
+ NULL,
+ POWER_COR_SPACE_TIME,
+ },
+
+ /*
+ * Troll blood
+ */
+
+ { /* 10 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_GREEN,
+ NULL /* no group */,
+ "Troll Blood",
+ "Your blood thickens, you sense corruption in it.",
+ "Your blood returns to a normal state.",
+ " Troll blood flows in your veins, granting increased regeneration\n"
+ " It also enables you to feel the presence of other troll beings\n"
+ " But it will make your presence more noticeable and aggravating",
+ { -1 },
+ { -1 },
+ NULL,
+ -1,
+ },
+
+ /*
+ * The vampire corruption set
+ */
+
+ { /* 11 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_L_DARK,
+ "Vampire",
+ "Vampiric Teeth",
+ "You grow vampiric teeth!",
+ NULL, /* cannot lose */
+ " Your teeth allow you to drain blood to feed yourself\n"
+ " However your stomach now only accepts blood.",
+ { -1 },
+ { -1 },
+ player_gain_vampire_teeth,
+ -1,
+ },
+
+ { /* 12 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_L_DARK,
+ "Vampire",
+ "Vampiric Strength",
+ "Your body seems more dead than alive.",
+ NULL, /* cannot lose */
+ " Your body seems somewhat dead\n"
+ " In this near undead state it has improved strength, constitution and intelligence\n"
+ " But reduced dexterity, wisdom and charisma.",
+ { CORRUPT_VAMPIRE_TEETH, -1 },
+ { -1 },
+ player_gain_vampire_strength,
+ -1,
+ },
+
+ { /* 13 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ TERM_L_DARK,
+ "Vampire",
+ "Vampire",
+ "You die to be reborn in a Vampire form.",
+ NULL, /* cannot lose */
+ " You are a Vampire. As such you resist cold, poison, darkness and nether.\n"
+ " Your life is sustained, but you cannot stand the light of the sun.",
+ { CORRUPT_VAMPIRE_STRENGTH, -1 },
+ { -1 },
+ player_gain_vampire,
+ -1,
+ },
+
+ /*
+ * Activatable corruptions (mutations)
+ */
+
+ { /* 14 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Ancalagon's Breath",
+ "You gain the ability to spit acid.",
+ "You lose the ability to spit acid.",
+ " Fires an acid ball.\n"
+ " Damage=level Radius 1+(level/30)\n"
+ " Level=9, Cost=9, Stat=DEX, Difficulty=15",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_SPIT_ACID,
+ },
+
+ { /* 15 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Smaug's Breath",
+ "You gain the ability to breathe fire.",
+ "You lose the ability to breathe fire.",
+ " Fires a fire ball.\n"
+ " Damage=2*level Radius 1+(level/20)\n"
+ " Level=20, Cost=10, Stat=CON, Difficulty=18",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_BR_FIRE,
+ },
+
+ { /* 16 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Glaurung's Gaze",
+ "Your eyes look mesmerizing...",
+ "Your eyes look uninteresting.",
+ " Tries to make a monster your pet.\n"
+ " Power=level\n"
+ " Level=12, Cost=12, Stat=CHR, Difficulty=18",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_HYPN_GAZE,
+ },
+
+ { /* 17 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Saruman's Power",
+ "You gain the ability to move objects telekinetically.",
+ "You lose the ability to move objects telekinetically.",
+ " Move an object in line of sight to you.\n"
+ " Max weight equal to (level) pounds\n"
+ " Level=9, Cost=9, Stat=WIS, Difficulty=14",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_TELEKINES,
+ },
+
+ { /* 18 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Teleport",
+ "You gain the power of teleportation at will.",
+ "You lose the power of teleportation at will.",
+ " Teleports the player at will.\n"
+ " Distance 10+4*level squares\n"
+ " Level=7, Cost=7, Stat=WIS, Difficulty=15",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_VTELEPORT,
+ },
+
+ { /* 19 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Glaurung's Spell",
+ "You gain the power of Mind Blast.",
+ "You lose the power of Mind Blast.",
+ " Fires a mind blasting bolt (psi damage).\n"
+ " Psi Damage (3+(level-1)/5)d3\n"
+ " Level=5, Cost=3, Stat=WIS, Difficulty=15",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_MIND_BLST,
+ },
+
+ { /* 20 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Vampiric Drain",
+ "You become vampiric.",
+ "You are no longer vampiric.",
+ " You can drain life from a foe like a vampire.\n"
+ " Drains (level+1d(level))*(level/10) hitpoints,\n"
+ " heals you and satiates you. Doesn't work on all monsters\n"
+ " Level=4, Cost=5, Stat=CON, Difficulty=9",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_VAMPIRISM,
+ },
+
+ { /* 21 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Carcharoth's Nose",
+ "You smell a metallic odour.",
+ "You no longer smell a metallic odour.",
+ " You can detect nearby precious metal (treasure).\n"
+ " Radius 25\n"
+ " Level=3, Cost=2, Stat=INT, Difficulty=12",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_SMELL_MET,
+ },
+
+ { /* 22 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Huan's Nose",
+ "You smell filthy monsters.",
+ "You no longer smell filthy monsters.",
+ " You can detect nearby monsters.\n"
+ " Radius 25\n"
+ " Level=5, Cost=4, Stat=INT, Difficulty=15",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_SMELL_MON,
+ },
+
+ { /* 23 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Blink",
+ "You gain the power of minor teleportation.",
+ "You lose the power of minor teleportation.",
+ " You can teleport yourself short distances (10 squares).\n"
+ " Level=3, Cost=3, Stat=WIS, Difficulty=12",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_BLINK,
+ },
+
+ { /* 24 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Eat Rock",
+ "The walls look delicious.",
+ "The walls look unappetizing.",
+ " You can consume solid rock with food benefit,\n"
+ " leaving an empty space behind.\n"
+ " Level=8, Cost=12, Stat=CON, Difficulty=18",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_EAT_ROCK,
+ },
+
+ { /* 25 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Swap Position",
+ "You feel like walking a mile in someone else's shoes.",
+ "You feel like staying in your own shoes.",
+ " You can switch locations with another being,\n"
+ " unless it resists teleportation.\n"
+ " Level=15, Cost=12, Stat=DEX, Difficulty=16",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_SWAP_POS,
+ },
+
+ { /* 26 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Shriek",
+ "Your vocal cords get much tougher.",
+ "Your vocal cords get much weaker.",
+ " Fires a sound ball and aggravates monsters.\n"
+ " Damage=level*4, Radius=8, centered on player\n"
+ " Level=4, Cost=4, Stat=CON, Difficulty=6",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_SHRIEK,
+ },
+
+ { /* 27 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Illuminate",
+ "You can light up rooms with your presence.",
+ "You can no longer light up rooms with your presence.",
+ " You can emit bright light that illuminates an area.\n"
+ " Damage=2d(level/2) Radius=(level/10)+1\n"
+ " Level=3, Cost=2, Stat=INT, Difficulty=10",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_ILLUMINE,
+ },
+
+ { /* 28 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Detect Curses",
+ "You can feel evil magics.",
+ "You can no longer feel evil magics.",
+ " You can feel the danger of evil magic.\n"
+ " It detects cursed items in the inventory\n"
+ " Level=7, Cost=14, Stat=WIS, Difficulty=14",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_DET_CURSE,
+ },
+
+ { /* 29 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Berserk",
+ "You feel a controlled rage.",
+ "You no longer feel a controlled rage.",
+ " You can drive yourself into a berserk frenzy.\n"
+ " It grants super-heroism. Duration=10+1d(level)\n"
+ " Level=8, Cost=8, Stat=STR, Difficulty=14",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_BERSERK,
+ },
+
+ { /* 30 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Midas touch",
+ "You gain the Midas touch.",
+ "You lose the Midas touch.",
+ " You can turn ordinary items to gold.\n"
+ " Turns a non-artifact object into 1/3 its value in gold\n"
+ " Level=10, Cost=5, Stat=INT, Difficulty=12",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_MIDAS_TCH,
+ },
+
+ { /* 31 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Grow Mold",
+ "You feel a sudden affinity for mold.",
+ "You feel a sudden dislike for mold.",
+ " You can cause mold to grow near you.\n"
+ " Summons up to 8 molds around the player\n"
+ " Level=1, Cost=6, Stat=CON, Difficulty=14",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_GROW_MOLD,
+ },
+
+ { /* 32 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Resist Elements",
+ "You feel like you can protect yourself.",
+ "You feel like you might be vulnerable.",
+ " You can harden yourself to the ravages of the elements.\n"
+ " Level-dependent chance of gaining resistances to the four \n"
+ " elements and poison. Duration=20 + d20\n"
+ " Level=10, Cost=12, Stat=CON, Difficulty=12",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_RESIST,
+ },
+
+ { /* 33 */
+ { MODULE_THEME, -1 },
+ TERM_RED,
+ NULL /* no group */,
+ "Earthquake",
+ "You gain the ability to wreck the dungeon.",
+ "You lose the ability to wreck the dungeon.",
+ " You can bring down the dungeon around your ears.\n"
+ " Radius=10, center on the player\n"
+ " Level=12, Cost=12, Stat=STR, Difficulty=16",
+ { -1 },
+ { -1 },
+ NULL,
+ PWR_EARTHQUAKE,
+ },
+
+};
+
+/**
+ * Initialize corruptions
+ */
+void init_corruptions()
+{
+ /* Nothing needed currently */
+}
+
+/*
+ * Corruptions
+ */
+bool_ player_has_corruption(int corruption_idx)
+{
+ if (corruption_idx < 0)
+ {
+ return FALSE;
+ }
+
+ return (p_ptr->corruptions[corruption_idx]);
+}
+
+static bool_ player_can_gain_corruption(int corruption_idx)
+{
+ bool_ allowed = TRUE; /* Allowed by default */
+
+ assert(corruption_idx >= 0);
+
+ if (corruption_idx == CORRUPT_TROLL_BLOOD)
+ {
+ /* Ok trolls should not get this one. never. */
+ if (streq(rp_ptr->title, "Troll"))
+ {
+ allowed = FALSE;
+ }
+ }
+
+ /* Theme module adds additional restrictions for Maiar */
+
+ if (game_module_idx == MODULE_THEME)
+ {
+ if (streq(rp_ptr->title, "Maia"))
+ {
+ /* We use a whitelist of corruptions for Maiar */
+ bool_ allow = FALSE;
+ if ((corruption_idx == CORRUPT_BALROG_AURA) ||
+ (corruption_idx == CORRUPT_BALROG_WINGS) ||
+ (corruption_idx == CORRUPT_BALROG_STRENGTH) ||
+ (corruption_idx == CORRUPT_BALROG_FORM) ||
+ (corruption_idx == CORRUPT_DEMON_BREATH))
+ {
+ allow = TRUE;
+ };
+
+ /* Mix result into 'allowed' flag */
+ allowed = allowed & allow;
+ }
+ }
+
+ /* Result */
+ return allowed;
+}
+
+static bool_ player_allow_corruption(int corruption_idx)
+{
+ int i;
+ bool_ found = FALSE;
+ corruption_type *c_ptr = NULL;
+
+ assert(corruption_idx < CORRUPTIONS_MAX);
+ c_ptr = &corruptions[corruption_idx];
+
+ /* Must be allowed by module */
+ for (i = 0; c_ptr->modules[i] >= 0; i++)
+ {
+ if (c_ptr->modules[i] == game_module_idx)
+ {
+ found = TRUE;
+ }
+ }
+
+ if (!found)
+ {
+ return FALSE;
+ }
+
+ /* Vampire teeth is special */
+ if (corruption_idx == CORRUPT_VAMPIRE_TEETH)
+ {
+ if (race_flags1_p(PR1_NO_SUBRACE_CHANGE))
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void player_set_corruption(int c, bool_ set)
+{
+ p_ptr->corruptions[c] = set;
+ p_ptr->redraw = p_ptr->redraw | PR_FRAME;
+ p_ptr->update = p_ptr->update | PU_BONUS | PU_TORCH | PU_BODY | PU_POWERS;
+
+}
+
+void player_gain_corruption(int corruption_idx)
+{
+ corruption_type *c_ptr = NULL;
+ assert(corruption_idx >= 0);
+ assert(corruption_idx < CORRUPTIONS_MAX);
+ c_ptr = &corruptions[corruption_idx];
+
+ /* Set the player's corruption flag */
+ player_set_corruption(corruption_idx, TRUE);
+
+ /* Invoke callback if necessary */
+ if (c_ptr->gain_callback)
+ {
+ c_ptr->gain_callback();
+ }
+}
+
+static void player_lose_corruption(int corruption_idx)
+{
+ assert(corruption_idx >= 0);
+ assert(corruption_idx < CORRUPTIONS_MAX);
+
+ player_set_corruption(corruption_idx, FALSE);
+
+ /* Currently no corruptions need any special handling when lost */
+}
+
+/*
+ * 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
+ */
+static bool_ test_depend_corrupt(s16b corrupt_idx, bool_ can_gain)
+{
+ s16b i;
+ corruption_type *c_ptr = NULL;
+
+ assert(corrupt_idx >= 0);
+ assert(corrupt_idx < CORRUPTIONS_MAX);
+
+ c_ptr = &corruptions[corrupt_idx];
+
+ if (can_gain)
+ {
+ if (p_ptr->corruptions[corrupt_idx])
+ {
+ return FALSE;
+ }
+ } else {
+ if (!p_ptr->corruptions[corrupt_idx])
+ {
+ return FALSE;
+ }
+ }
+
+ /* Go through all dependencies */
+ for (i=0; c_ptr->depends[i] >= 0; i++)
+ {
+ if (!test_depend_corrupt(c_ptr->depends[i], FALSE))
+ {
+ return FALSE;
+ }
+ }
+
+ /* Go through all opposers */
+ for (i=0; c_ptr->opposes[i] >= 0; i++)
+ {
+ if (test_depend_corrupt(c_ptr->opposes[i], FALSE))
+ {
+ return FALSE;
+ }
+ }
+
+ /* are we even allowed to get it? */
+ return player_can_gain_corruption(corrupt_idx);
+}
+
+void gain_random_corruption()
+{
+ s16b i, max;
+ s16b pos[CORRUPTIONS_MAX];
+
+ /* Get the list of all possible ones */
+ max = 0;
+ for (i=0; i < CORRUPTIONS_MAX; i++)
+ {
+ if (test_depend_corrupt(i, TRUE) &&
+ player_allow_corruption(i))
+ {
+ pos[max] = i;
+ max = max + 1;
+ }
+ }
+
+ /* Ok now get one of them */
+ if (max > 0)
+ {
+ s16b ret = rand_int(max);
+ int c_idx = pos[ret];
+ assert(c_idx < CORRUPTIONS_MAX);
+
+ player_gain_corruption(c_idx);
+ cmsg_print(TERM_L_RED, corruptions[c_idx].get_text);
+ }
+}
+
+static void remove_corruption(int c_idx)
+{
+ assert(c_idx >= 0 && c_idx < CORRUPTIONS_MAX);
+ assert(corruptions[c_idx].lose_text);
+
+ player_lose_corruption(c_idx);
+ cmsg_print(TERM_L_RED, corruptions[c_idx].lose_text);
+}
+
+void lose_corruption()
+{
+ s16b i, max;
+ s16b pos[CORRUPTIONS_MAX];
+
+ /* Get the list of all possible ones */
+ max = 0;
+ for (i = 0; i < CORRUPTIONS_MAX; i++)
+ {
+ bool_ is_removable = (corruptions[i].lose_text != NULL);
+ if (test_depend_corrupt(i, FALSE) && is_removable)
+ {
+ pos[max] = i;
+ max = max + 1;
+ }
+ }
+
+ /* Ok now get one of them */
+ if (max > 0)
+ {
+ s16b ret = rand_int(max);
+ int c_idx = pos[ret];
+
+ /* Remove the corruption */
+ remove_corruption(c_idx);
+
+ /* Ok now lets see if it broke some dependencies */
+ for (i = 0; i < max - 1; i++)
+ {
+ if (p_ptr->corruptions[pos[i]] != test_depend_corrupt(pos[i], FALSE))
+ {
+ remove_corruption(pos[i]);
+ }
+ }
+ }
+}
+
+
+/*
+ * Dump the corruption list
+ */
+void dump_corruptions(FILE *fff, bool_ color, bool_ header)
+{
+ int i;
+
+ assert(fff != NULL);
+
+ for (i = 0; i < CORRUPTIONS_MAX; i++)
+ {
+ corruption_type *c_ptr = &corruptions[i];
+
+ if (header)
+ {
+ fprintf(fff, "\n Corruption list:\n");
+ header = FALSE;
+ }
+
+ if (p_ptr->corruptions[i])
+ {
+ byte c = c_ptr->color;
+
+ if (color)
+ {
+ fprintf(fff, "#####%c%s:\n", conv_color[c], c_ptr->name);
+ }
+ else
+ {
+ fprintf(fff, "%s:\n", c_ptr->name);
+ }
+
+ fprintf(fff, "%s\n", c_ptr->desc);
+ }
+ }
+}
+
+/*
+ * Get the power granted by a corruption. Returns -1
+ * if the given corruption does not grant a power.
+ */
+s16b get_corruption_power(int corruption_idx)
+{
+ corruption_type *c_ptr = NULL;
+
+ assert(corruption_idx >= 0);
+ assert(corruption_idx < CORRUPTIONS_MAX);
+
+ c_ptr = &corruptions[corruption_idx];
+
+ if ((c_ptr->power >= 0) && (c_ptr->power < POWER_MAX))
+ {
+ return c_ptr->power;
+ }
+ else
+ {
+ assert(c_ptr->power == -1); /* Sanity check: Should always be the case. */
+ return -1;
+ }
+}
diff --git a/src/corrupt.hpp b/src/corrupt.hpp
new file mode 100644
index 00000000..c200762e
--- /dev/null
+++ b/src/corrupt.hpp
@@ -0,0 +1,47 @@
+#include "h-basic.h"
+
+extern void gain_random_corruption();
+extern void dump_corruptions(FILE *OutFile, bool_ color, bool_ header);
+extern void lose_corruption();
+extern bool_ player_has_corruption(int corruption_idx);
+extern void player_gain_corruption(int corruption_idx);
+extern s16b get_corruption_power(int corruption_idx);
+
+/*
+ * Corruptions
+ */
+#define CORRUPT_BALROG_AURA 0
+#define CORRUPT_BALROG_WINGS 1
+#define CORRUPT_BALROG_STRENGTH 2
+#define CORRUPT_BALROG_FORM 3
+#define CORRUPT_DEMON_SPIRIT 4
+#define CORRUPT_DEMON_HIDE 5
+#define CORRUPT_DEMON_BREATH 6
+#define CORRUPT_DEMON_REALM 7
+#define CORRUPT_RANDOM_TELEPORT 8
+#define CORRUPT_ANTI_TELEPORT 9
+#define CORRUPT_TROLL_BLOOD 10
+#define CORRUPT_VAMPIRE_TEETH 11
+#define CORRUPT_VAMPIRE_STRENGTH 12
+#define CORRUPT_VAMPIRE_VAMPIRE 13
+#define MUT1_SPIT_ACID 14
+#define MUT1_BR_FIRE 15
+#define MUT1_HYPN_GAZE 16
+#define MUT1_TELEKINES 17
+#define MUT1_VTELEPORT 18
+#define MUT1_MIND_BLST 19
+#define MUT1_VAMPIRISM 20
+#define MUT1_SMELL_MET 21
+#define MUT1_SMELL_MON 22
+#define MUT1_BLINK 23
+#define MUT1_EAT_ROCK 24
+#define MUT1_SWAP_POS 25
+#define MUT1_SHRIEK 26
+#define MUT1_ILLUMINE 27
+#define MUT1_DET_CURSE 28
+#define MUT1_BERSERK 29
+#define MUT1_MIDAS_TCH 30
+#define MUT1_GROW_MOLD 31
+#define MUT1_RESIST 32
+#define MUT1_EARTHQUAKE 33
+#define CORRUPTIONS_MAX 34
diff --git a/src/defines.h b/src/defines.h
new file mode 100644
index 00000000..fc1f61f5
--- /dev/null
+++ b/src/defines.h
@@ -0,0 +1,3879 @@
+/* File: defines.h */
+
+/* Purpose: global constants and macro definitions */
+
+
+/*
+ * Do not edit this file unless you know *exactly* what you are doing.
+ *
+ * Some of the values in this file were chosen to preserve game balance,
+ * while others are hard-coded based on the format of old save-files, the
+ * definition of arrays in various places, mathematical properties, fast
+ * computation, storage limits, or the format of external text files.
+ *
+ * Changing some of these values will induce crashes or memory errors or
+ * savefile mis-reads. Most of the comments in this file are meant as
+ * reminders, not complete descriptions, and even a complete knowledge
+ * of the source may not be sufficient to fully understand the effects
+ * of changing certain definitions.
+ *
+ * Lastly, note that the code does not always use the symbolic constants
+ * below, and sometimes uses various hard-coded values that may not even
+ * be defined in this file, but which may be related to definitions here.
+ * This is of course bad programming practice, but nobody is perfect...
+ *
+ * For example, there are MANY things that depend on the screen being
+ * 80x24, with the top line used for messages, the bottom line being
+ * used for status, and exactly 22 lines used to show the dungeon.
+ * Just because your screen can hold 46 lines does not mean that the
+ * game will work if you try to use 44 lines to show the dungeon.
+ *
+ * You have been warned.
+ */
+
+
+/*
+ * Release state, CVS or not, remember to switch it when making releases
+ */
+
+#ifndef IS_CVS
+/* #define IS_CVS " (ah)" */
+#define IS_CVS " (ah, git)"
+#endif
+
+#define USER_PATH_VERSION "/2.4"
+
+#define SAVEFILE_VERSION 105
+
+/*
+ * 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_SPELLS 100
+#define MAX_RUNES 100
+
+/*
+ * Total number of stores (see "store.c", etc)
+ */
+#define STORE_GENERAL 0
+#define STORE_HOME 7
+
+/*
+ * Maximum number of player "sex" types (see "table.c", etc)
+ */
+#define MAX_SEXES 3
+
+/* Number of Random Artifacts */
+#define MAX_RANDARTS 84
+#define MAX_T_ACT 51
+
+
+/* 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
+#define MIMIC_FORMS_MAX 14
+
+/* Symbiosis */
+#define MAX_SYMBIOTIC_POWERS 9
+
+
+/* A hack for cave.c */
+#define BMP_FIRST_PC_CLASS 164
+#define BMP_FIRST_PC_RACE 128
+
+
+/*
+ * 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
+
+/*
+ * 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_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 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 */
+
+/*
+ * 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 */
+
+/*
+ * A "stack" of items is limited to less than 100 items (hard-coded).
+ */
+#define MAX_STACK_SIZE 100
+
+
+/*
+ * 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 */
+
+/* XXX */
+#define MKEY_MINDCRAFT 2
+#define MKEY_ANTIMAGIC 3
+#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_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
+#define MKEY_DEATH_TOUCH 100
+#define MKEY_GEOMANCY 101
+#define MKEY_REACH_ATTACK 102
+
+
+/*** 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" */
+
+
+
+/*** 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
+
+/* 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 3
+
+/*
+ * Maximum number of school spells
+ */
+#define SCHOOL_SPELLS_MAX 200
+
+/*
+ * Maximum number of spell schools
+ */
+#define SCHOOLS_MAX 50
+
+/*
+ * 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 */
+
+/*** 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
+#define ACT_ETERNAL_FLAME 201
+#define ACT_MAGGOT 202
+#define ACT_LEBOHAUM 203
+#define ACT_DURANDIL 204
+#define ACT_RADAGAST 205 /* Theme */
+#define ACT_VALAROMA 206 /* Theme */
+
+/*** 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 ('!') */
+#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
+
+/* 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
+#define SV_SCROLL_STERILIZATION 54
+
+/* 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_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_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_FRAME 0x02000000L /* Display Basic Info */
+#define PR_MAP 0x04000000L /* Display Map */
+#define PR_WIPE 0x08000000L /* Hack -- Total Redraw */
+
+/*
+ * Bit flags for the "p_ptr->window" variable (etc)
+ */
+#define PW_INVEN 0x00000001L /* Display inven/equip */
+#define PW_EQUIP 0x00000002L /* Display equip/inven */
+/* xxx */
+#define PW_PLAYER 0x00000008L /* Display character */
+#define PW_M_LIST 0x00000010L /* Show monster list */
+/* xxx */
+#define PW_MESSAGE 0x00000040L /* Display messages */
+#define PW_OVERHEAD 0x00000080L /* Display overhead view */
+#define PW_MONSTER 0x00000100L /* Display monster recall */
+#define PW_OBJECT 0x00000200L /* Display object recall */
+/* xxx */
+#define PW_SNAPSHOT 0x00000800L /* Display snap-shot */
+/* xxx */
+/* xxx */
+#define PW_BORG_1 0x00004000L /* Display borg messages */
+#define PW_BORG_2 0x00008000L /* Display borg status */
+
+
+/* jk */
+#define FTRAP_CHEST 0x000000001 /* may appear on chests */
+#define FTRAP_DOOR 0x000000002 /* may appear on doors/floors */
+#define FTRAP_FLOOR 0x000000004 /* may appear on floor */
+#define FTRAP_CHANGE 0x000000008 /* Color changing */
+#define FTRAP_XXX5 0x000000010
+#define FTRAP_XXX6 0x000000020
+#define FTRAP_XXX7 0x000000040
+#define FTRAP_XXX8 0x000000080
+#define FTRAP_XXX9 0x000000100
+#define FTRAP_XXX10 0x000000200
+#define FTRAP_XXX11 0x000000400
+#define FTRAP_XXX12 0x000000800
+#define FTRAP_XXX13 0x000001000
+#define FTRAP_XXX14 0x000002000
+#define FTRAP_XXX15 0x000004000
+#define FTRAP_XXX16 0x000008000
+#define FTRAP_LEVEL1 0x000010000 /* low level ball/bolt trap */
+#define FTRAP_LEVEL2 0x000020000 /* medium level ball/bolt trap */
+#define FTRAP_LEVEL3 0x000040000 /* high level ball/bolt trap */
+#define FTRAP_LEVEL4 0x000080000 /* oops level ball/bolt trap */
+#define FTRAP_XXX21 0x000100000
+#define FTRAP_XXX22 0x000200000
+#define FTRAP_XXX23 0x000400000
+#define FTRAP_XXX24 0x000800000
+#define FTRAP_XXX25 0x001000000
+#define FTRAP_XXX26 0x002000000
+#define FTRAP_XXX27 0x004000000
+#define FTRAP_XXX28 0x008000000
+#define FTRAP_XXX29 0x010000000
+#define FTRAP_XXX30 0x020000000
+#define FTRAP_XXX31 0x040000000
+#define FTRAP_XXX32 0x080000000
+
+/* jk */
+#define STAT_DEC_TEMPORARY 1
+#define STAT_DEC_NORMAL 2
+#define STAT_DEC_PERMANENT 3
+
+/* jk - which trap is which number */
+#define TRAP_OF_WEAKNESS_I 1
+#define TRAP_OF_WEAKNESS_II 2
+#define TRAP_OF_WEAKNESS_III 3
+#define TRAP_OF_INTELLIGENCE_I 4
+#define TRAP_OF_INTELLIGENCE_II 5
+#define TRAP_OF_INTELLIGENCE_III 6
+#define TRAP_OF_WISDOM_I 7
+#define TRAP_OF_WISDOM_II 8
+#define TRAP_OF_WISDOM_III 9
+#define TRAP_OF_FUMBLING_I 10
+#define TRAP_OF_FUMBLING_II 11
+#define TRAP_OF_FUMBLING_III 12
+#define TRAP_OF_WASTING_I 13
+#define TRAP_OF_WASTING_II 14
+#define TRAP_OF_WASTING_III 15
+#define TRAP_OF_BEAUTY_I 16
+#define TRAP_OF_BEAUTY_II 17
+#define TRAP_OF_BEAUTY_III 18
+
+#define TRAP_OF_CURSE_WEAPON 20
+#define TRAP_OF_CURSE_ARMOR 21
+#define TRAP_OF_EARTHQUAKE 22
+#define TRAP_OF_POISON_NEEDLE 23
+#define TRAP_OF_SUMMON_MONSTER 24
+#define TRAP_OF_SUMMON_UNDEAD 25
+#define TRAP_OF_SUMMON_GREATER_UNDEAD 26
+#define TRAP_OF_TELEPORT 27
+#define TRAP_OF_PARALYZING 28
+#define TRAP_OF_EXPLOSIVE_DEVICE 29
+#define TRAP_OF_TELEPORT_AWAY 30
+#define TRAP_OF_LOSE_MEMORY 31
+#define TRAP_OF_BITTER_REGRET 32
+#define TRAP_OF_BOWEL_CRAMPS 33
+#define TRAP_OF_BLINDNESS_CONFUSION 34
+#define TRAP_OF_AGGRAVATION 35
+#define TRAP_OF_MULTIPLICATION 36
+#define TRAP_OF_STEAL_ITEM 37
+#define TRAP_OF_SUMMON_FAST_QUYLTHULGS 38
+#define TRAP_OF_SINKING 39
+#define TRAP_OF_MANA_DRAIN 40
+#define TRAP_OF_MISSING_MONEY 41
+#define TRAP_OF_NO_RETURN 42
+#define TRAP_OF_SILENT_SWITCHING 43
+#define TRAP_OF_WALLS 44
+#define TRAP_OF_CALLING_OUT 45
+#define TRAP_OF_SLIDING 46
+#define TRAP_OF_CHARGES_DRAIN 47
+#define TRAP_OF_STAIR_MOVEMENT 48
+#define TRAP_OF_NEW 49
+#define TRAP_OF_SCATTER_ITEMS 50
+#define TRAP_OF_DECAY 51
+#define TRAP_OF_WASTING_WANDS 52
+#define TRAP_OF_FILLING 53
+#define TRAP_OF_DRAIN_SPEED 54
+
+#define TRAP_OF_ELEC_BOLT 60
+#define TRAP_OF_POIS_BOLT 61
+#define TRAP_OF_ACID_BOLT 62
+#define TRAP_OF_COLD_BOLT 63
+#define TRAP_OF_FIRE_BOLT 64
+#define TRAP_OF_PLASMA_BOLT 65
+#define TRAP_OF_WATER_BOLT 66
+#define TRAP_OF_LITE_BOLT 67
+#define TRAP_OF_DARK_BOLT 68
+#define TRAP_OF_SHARDS_BOLT 69
+#define TRAP_OF_SOUND_BOLT 70
+#define TRAP_OF_CONFUSION_BOLT 71
+#define TRAP_OF_FORCE_BOLT 72
+#define TRAP_OF_INERTIA_BOLT 73
+#define TRAP_OF_MANA_BOLT 74
+#define TRAP_OF_ICE_BOLT 75
+#define TRAP_OF_CHAOS_BOLT 76
+#define TRAP_OF_NETHER_BOLT 77
+#define TRAP_OF_DISENCHANT_BOLT 78
+#define TRAP_OF_NEXUS_BOLT 79
+#define TRAP_OF_TIME_BOLT 80
+#define TRAP_OF_GRAVITY_BOLT 81
+
+#define TRAP_OF_ELEC_BALL 82
+#define TRAP_OF_POIS_BALL 83
+#define TRAP_OF_ACID_BALL 84
+#define TRAP_OF_COLD_BALL 85
+#define TRAP_OF_FIRE_BALL 86
+#define TRAP_OF_PLASMA_BALL 87
+#define TRAP_OF_WATER_BALL 88
+#define TRAP_OF_LITE_BALL 89
+#define TRAP_OF_DARK_BALL 90
+#define TRAP_OF_SHARDS_BALL 91
+#define TRAP_OF_SOUND_BALL 92
+#define TRAP_OF_CONFUSION_BALL 93
+#define TRAP_OF_FORCE_BALL 94
+#define TRAP_OF_INERTIA_BALL 95
+#define TRAP_OF_MANA_BALL 96
+#define TRAP_OF_ICE_BALL 97
+#define TRAP_OF_CHAOS_BALL 98
+#define TRAP_OF_NETHER_BALL 99
+#define TRAP_OF_DISENCHANT_BALL 100
+#define TRAP_OF_NEXUS_BALL 101
+#define TRAP_OF_TIME_BALL 102
+#define TRAP_OF_GRAVITY_BALL 103
+
+#define TRAP_OF_ARROW_I 110
+#define TRAP_OF_ARROW_II 111
+#define TRAP_OF_ARROW_III 112
+#define TRAP_OF_ARROW_IV 113
+#define TRAP_OF_POISON_ARROW_I 114
+#define TRAP_OF_POISON_ARROW_II 115
+#define TRAP_OF_POISON_ARROW_III 116
+#define TRAP_OF_POISON_ARROW_IV 117
+#define TRAP_OF_DAGGER_I 118
+#define TRAP_OF_DAGGER_II 119
+#define TRAP_OF_POISON_DAGGER_I 120
+#define TRAP_OF_POISON_DAGGER_II 121
+#define TRAP_OF_ARROWS_I 122
+#define TRAP_OF_ARROWS_II 123
+#define TRAP_OF_ARROWS_III 124
+#define TRAP_OF_ARROWS_IV 125
+#define TRAP_OF_POISON_ARROWS_I 126
+#define TRAP_OF_POISON_ARROWS_II 127
+#define TRAP_OF_POISON_ARROWS_III 128
+#define TRAP_OF_POISON_ARROWS_IV 129
+#define TRAP_OF_DAGGERS_I 130
+#define TRAP_OF_DAGGERS_II 131
+#define TRAP_OF_POISON_DAGGERS_I 132
+#define TRAP_OF_POISON_DAGGERS_II 133
+
+#define TRAP_OF_DROP_ITEMS 140
+#define TRAP_OF_DROP_ALL_ITEMS 141
+#define TRAP_OF_DROP_EVERYTHING 142
+
+/* -SC- */
+#define TRAP_OF_FEMINITY 150
+#define TRAP_OF_MASCULINITY 151
+#define TRAP_OF_NEUTRALITY 152
+#define TRAP_OF_AGING 153
+#define TRAP_OF_GROWING 154
+#define TRAP_OF_SHRINKING 155
+#define TRAP_OF_ELDRITCH_HORROR 156
+/* XXX */
+#define TRAP_OF_DIVINE_ANGER 158
+#define TRAP_OF_DIVINE_WRATH 159
+#define TRAP_OF_HALLUCINATION 160
+
+#define TRAP_OF_ROCKET 161
+#define TRAP_OF_NUKE_BOLT 162
+#define TRAP_OF_DEATH_RAY 163
+#define TRAP_OF_HOLY_FIRE 164
+#define TRAP_OF_HELL_FIRE 165
+#define TRAP_OF_PSI_BOLT 166
+#define TRAP_OF_PSI_DRAIN 167
+#define TRAP_OF_NUKE_BALL 168
+#define TRAP_OF_PSI_BALL 169
+
+/* DG */
+#define TRAP_OF_ACQUIREMENT 170
+
+/* Runescrye */
+#define TRAP_G_ELEC_BOLT 171
+#define TRAP_G_POIS_BOLT 172
+#define TRAP_G_ACID_BOLT 173
+#define TRAP_G_COLD_BOLT 174
+#define TRAP_G_FIRE_BOLT 175
+
+/*** General index values ***/
+
+
+/*
+ * Legal restrictions for "summon_specific()"
+ */
+#define SUMMON_ANT 11
+#define SUMMON_SPIDER 12
+#define SUMMON_HOUND 13
+#define SUMMON_HYDRA 14
+#define SUMMON_ANGEL 15
+#define SUMMON_DEMON 16
+#define SUMMON_UNDEAD 17
+#define SUMMON_DRAGON 18
+#define SUMMON_HI_UNDEAD 21
+#define SUMMON_HI_DRAGON 22
+#define SUMMON_WRAITH 31
+#define SUMMON_UNIQUE 32
+#define SUMMON_BIZARRE1 33
+#define SUMMON_BIZARRE2 34
+#define SUMMON_BIZARRE3 35
+#define SUMMON_BIZARRE4 36
+#define SUMMON_BIZARRE5 37
+#define SUMMON_BIZARRE6 38
+#define SUMMON_HI_DEMON 39
+#define SUMMON_KIN 40
+#define SUMMON_DAWN 41
+#define SUMMON_ANIMAL 42
+#define SUMMON_ANIMAL_RANGER 43
+#define SUMMON_HI_UNDEAD_NO_UNIQUES 44
+#define SUMMON_HI_DRAGON_NO_UNIQUES 45
+#define SUMMON_NO_UNIQUES 46
+#define SUMMON_PHANTOM 47
+#define SUMMON_ELEMENTAL 48
+#define SUMMON_THUNDERLORD 49
+#define SUMMON_BLUE_HORROR 50
+#define SUMMON_BUG 51
+#define SUMMON_RNG 52
+#define SUMMON_MINE 53
+#define SUMMON_HUMAN 54
+#define SUMMON_SHADOWS 55
+#define SUMMON_GHOST 56
+#define SUMMON_QUYLTHULG 57
+
+
+/*
+ * 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 GF_INSTA_DEATH 111
+#define GF_ELEMENTAL_WALL 112
+#define GF_ELEMENTAL_GROWTH 113
+#define MAX_GF 114
+
+/*
+ * 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_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_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_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 */
+
+
+
+/*
+ * 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
+
+/*
+ * 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 */
+
+
+/*
+ * 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])
+
+
+
+/*** Sound constants ***/
+
+
+#define SOUND_HIT 1
+#define SOUND_MISS 2
+#define SOUND_FLEE 3
+#define SOUND_DROP 4
+#define SOUND_KILL 5
+#define SOUND_LEVEL 6
+#define SOUND_DEATH 7
+#define SOUND_STUDY 8
+#define SOUND_TELEPORT 9
+#define SOUND_SHOOT 10
+#define SOUND_QUAFF 11
+#define SOUND_ZAP 12
+#define SOUND_WALK 13
+#define SOUND_TPOTHER 14
+#define SOUND_HITWALL 15
+#define SOUND_EAT 16
+#define SOUND_STORE1 17
+#define SOUND_STORE2 18
+#define SOUND_STORE3 19
+#define SOUND_STORE4 20
+#define SOUND_DIG 21
+#define SOUND_OPENDOOR 22
+#define SOUND_SHUTDOOR 23
+#define SOUND_TPLEVEL 24
+#define SOUND_SCROLL 25
+#define SOUND_BUY 26
+#define SOUND_SELL 27
+#define SOUND_WARN 28
+#define SOUND_ROCKET 29 /* Somebody's shooting rockets */
+#define SOUND_N_KILL 30 /* The player kills a non-living/undead monster */
+#define SOUND_U_KILL 31 /* The player kills a unique */
+#define SOUND_QUEST 32 /* The player has just completed a quest */
+#define SOUND_HEAL 33 /* The player was healed a little bit */
+#define SOUND_X_HEAL 34 /* The player was healed full health */
+#define SOUND_BITE 35 /* A monster bites you */
+#define SOUND_CLAW 36 /* A monster claws you */
+#define SOUND_M_SPELL 37 /* A monster casts a miscellaneous spell */
+#define SOUND_SUMMON 38 /* A monster casts a summoning spell */
+#define SOUND_BREATH 39 /* A monster breathes */
+#define SOUND_BALL 40 /* A monster casts a ball / bolt spell */
+#define SOUND_M_HEAL 41 /* A monster heals itself somehow */
+#define SOUND_ATK_SPELL 42 /* A monster casts a misc. offensive spell */
+#define SOUND_EVIL 43 /* Something nasty has just happened! */
+#define SOUND_TOUCH 44 /* A monster touches you */
+#define SOUND_STING 45 /* A monster stings you */
+#define SOUND_CRUSH 46 /* A monster crushes / envelopes you */
+#define SOUND_SLIME 47 /* A monster drools/spits/etc on you */
+#define SOUND_WAIL 48 /* A monster wails */
+#define SOUND_WINNER 49 /* Just won the game! */
+#define SOUND_FIRE 50 /* An item was burned */
+#define SOUND_ACID 51 /* An item was destroyed by acid */
+#define SOUND_ELEC 52 /* An item was destroyed by electricity */
+#define SOUND_COLD 53 /* An item was shattered */
+#define SOUND_ILLEGAL 54 /* Illegal command attempted */
+#define SOUND_FAIL 55 /* Fail to get a spell off / activate an item */
+#define SOUND_WAKEUP 56 /* A monster wakes up */
+#define SOUND_INVULN 57 /* Invulnerability! */
+#define SOUND_FALL 58 /* Falling through a trapdoor... */
+#define SOUND_PAIN 59 /* A monster is in pain! */
+#define SOUND_DESTITEM 60 /* An item was destroyed by misc. means */
+#define SOUND_MOAN 61 /* A monster makes a moan/beg/insult attack */
+#define SOUND_SHOW 62 /* A monster makes a "show" attack */
+#define SOUND_UNUSED 63 /* (no sound for gaze attacks) */
+#define SOUND_EXPLODE 64 /* Something (or somebody) explodes */
+
+/*
+ * Mega-Hack -- maximum known sounds
+ */
+#define SOUND_MAX 65
+
+
+
+/*** Hack ***/
+
+
+/*
+ * Road flags
+ */
+#define ROAD_NORTH 1
+#define ROAD_SOUTH 2
+#define ROAD_EAST 4
+#define ROAD_WEST 8
+
+
+/*
+ * Buildings actions
+ */
+#define BACT_RESEARCH_ITEM 1
+#define BACT_TOWN_HISTORY 2
+#define BACT_RACE_LEGENDS 3
+#define BACT_KING_LEGENDS 5
+#define BACT_QUEST1 6
+#define BACT_IN_BETWEEN 12
+#define BACT_GAMBLE_RULES 13
+#define BACT_CRAPS 14
+#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_ENCHANT_WEAPON 23
+#define BACT_ENCHANT_ARMOR 24
+#define BACT_RECHARGE 25
+#define BACT_IDENTS 26
+#define BACT_HEALING 28
+#define BACT_RESTORE 29
+#define BACT_ENCHANT_ARROWS 30
+#define BACT_ENCHANT_BOW 31
+#define BACT_RECALL 33
+#define BACT_TELEPORT_LEVEL 34
+#define BACT_MIMIC_NORMAL 37
+#define BACT_DIVINATION 42
+#define BACT_SELL 43
+#define BACT_BUY 44
+#define BACT_EXAMINE 45
+#define BACT_STEAL 46
+#define BACT_STAR_HEAL 50
+#define BACT_DROP_ITEM 54
+#define BACT_GET_ITEM 55
+#define BACT_FIREPROOF_QUEST 56
+#define BACT_LIBRARY_QUEST 61
+#define BACT_EREBOR_KEY 66
+/* 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
+
+/*
+ * 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_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
+
+/* 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 */
+
+/* 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_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
+
+/*
+ * 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 QUEST_BOUNTY 26
+#define QUEST_FIREPROOF 27
+#define QUEST_LIBRARY 28
+#define QUEST_GOD 29
+#define MAX_Q_IDX 30
+
+#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
+
+/*
+ * Modules
+ */
+#define MODULE_TOME 0
+#define MODULE_THEME 1
+#define MAX_MODULES 2
+
+/*
+ * Races
+ */
+#define RACE_HUMAN 0
+#define RACE_HALF_ELF 1
+#define RACE_ELF 2
+#define RACE_HOBBIT 3
+#define RACE_GNOME 4
+#define RACE_DWARF 5
+#define RACE_ORC 6
+#define RACE_TROLL 7
+#define RACE_DUNADAN 8
+#define RACE_HIGH_ELF 9
+#define RACE_HALF_OGRE 10
+#define RACE_BEORNING 11
+#define RACE_KOBOLD 12 /* ToME */
+#define RACE_DRUADAN 12 /* Theme */
+#define RACE_PETTY_DWARF 13
+#define RACE_DARK_ELF 14
+#define RACE_ENT 15
+#define RACE_ROHANKNIGHT 16
+#define RACE_THUNDERLORD 17 /* ToME */
+#define RACE_EAGLE 17 /* Theme */
+#define RACE_DEATHMOLD 18 /* ToME */
+#define RACE_DRAGON 18 /* Theme */
+#define RACE_YEEK 19
+#define RACE_WOOD_ELF 20
+#define RACE_MAIA 21
+#define RACE_EASTERLING 22 /* Theme */
+#define RACE_DEMON 23 /* Theme */
+
+/*
+ * Hooks
+ */
+#define HOOK_MONSTER_DEATH 0
+#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_NEW_MONSTER_END 22
+#define HOOK_AIM 24
+#define HOOK_USE 25
+#define HOOK_CHAT 32
+#define HOOK_MON_SPEAK 33
+#define HOOK_BIRTH_OBJECTS 35
+#define HOOK_SAVE_GAME 40
+#define HOOK_LOAD_GAME 41
+#define HOOK_LEVEL_REGEN 42
+#define HOOK_LEVEL_END_GEN 43
+#define HOOK_GEN_LEVEL_BEGIN 49
+#define HOOK_GET 50
+#define HOOK_RECALC_SKILLS 52
+#define HOOK_ENTER_DUNGEON 53
+#define HOOK_EAT 55
+#define HOOK_DIE 56
+#define HOOK_CALC_HP 57
+#define HOOK_CALC_MANA 60
+#define HOOK_RECALL 62
+#define HOOK_BODY_PARTS 65
+#define HOOK_MON_ASK_HELP 69
+#define HOOK_GAME_START 72
+#define HOOK_FORBID_TRAVEL 75
+
+/*
+ * 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 */
+
+/* Number of skill choices for Lost Sword quests. */
+#define LOST_SWORD_NSKILLS 4
+
+/* SKill flags */
+#define SKF1_HIDDEN 0x00000001 /* Starts hidden */
+#define SKF1_AUTO_HIDE 0x00000002 /* Tries to rehide at calc_bonus */
+#define SKF1_RANDOM_GAIN 0x00000004 /* Can be randomly gained by certain quests & such */
+
+#define MAX_MELEE 3
+
+
+/*
+ * 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 GOD_AULE 6
+#define GOD_VARDA 7
+#define GOD_ULMO 8
+#define GOD_MANDOS 9
+#define MAX_GODS 10
+
+/*
+ * Command numbers for do_cmd_cli().
+ *
+ * As the user is not intended to have a way to enter these codes directly
+ * (doing so isn't harmful, but these codes are not intended as mnemonics),
+ * only codes in the range 0xE000 - 0xF8FF (the private area of Unicode 3.0)
+ * should be used.
+ *
+ * In addition, values at the lower end of this range are preferred as the upper
+ * end may have a system-specific encoding
+ */
+#define CMD_CLI_HELP -8192
+#define CMD_SHOW_TIME -8188
+#define CMD_SHOW_SKILL -8187
+#define CMD_DUMP_HTML -8186
+#define CMD_MACRO -8185
+#define CMD_QUEST -8184
+#define CMD_BLUNDER -8183
+#define CMD_SHOW_ABILITY -8182
+
+#define CLI_MAX 128
+
+
+/*
+ * The various winner state
+ */
+#define WINNER_NORMAL 1
+#define WINNER_ULTRA 2
+
+/*
+ * The abilities
+ */
+#define AB_SPREAD_BLOWS 0
+#define AB_TREE_WALK 1
+#define AB_PERFECT_CASTING 2
+#define AB_MAX_BLOW1 3
+#define AB_MAX_BLOW2 4
+#define AB_AMMO_CREATION 5
+#define AB_DEATH_TOUCH 6
+#define AB_FAR_REACHING 8
+#define AB_TRAPPING 9
+#define AB_UNDEAD_FORM 10
+
+/**
+ * Spell school books/tomes
+ */
+#define TOME_MANA 0
+#define TOME_FIRE 1
+#define TOME_WINDS 2
+#define TOME_EARTH 3
+#define TOME_WATER 4
+#define TOME_TRANSLOCATION 5
+#define TOME_NATURE 6
+#define TOME_KNOWLEDGE 7
+#define TOME_TIME 8
+#define TOME_META 9
+#define TOME_MIND 10
+#define TOME_HELLFLAME 11
+#define TOME_ERU 20
+#define TOME_MANWE 21
+#define TOME_TULKAS 22
+#define TOME_MELKOR 23
+#define TOME_YAVANNA 24
+#define BOOK_CANTRIPS 50
+#define BOOK_TELEPORTATION 51
+#define BOOK_SUMMONING 52
+#define BOOK_DEMON_SWORD 55
+#define BOOK_DEMON_SHIELD 56
+#define BOOK_DEMON_HELM 57
+#define BOOK_DRUMS 58
+#define BOOK_HARPS 59
+#define BOOK_HORNS 60
+#define BOOK_PLAYER 61
+#define BOOK_GEOMANCY 62
+#define BOOK_AULE 63
+#define BOOK_VARDA 64
+#define BOOK_ULMO 65
+#define BOOK_MANDOS 66
+#define BOOK_RANDOM 255
+
+#define SCHOOL_BOOKS_SIZE 256
+
+/**
+ * Macro to generate a memoizing named monster lookup function.
+ *
+ * This is meant as a stopgap measure until a better method
+ * can be implemented.
+ */
+#define GENERATE_MONSTER_LOOKUP_FN(fn, name) \
+ static int fn()\
+ {\
+ static int r_idx = -1;\
+ if (r_idx < 0)\
+ {\
+ r_idx = test_monster_name(name);\
+ assert(r_idx);\
+ }\
+ return r_idx;\
+ }
diff --git a/src/deity_type.hpp b/src/deity_type.hpp
new file mode 100644
index 00000000..0dcbb818
--- /dev/null
+++ b/src/deity_type.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+/**
+ * A structure for deity information.
+ */
+struct deity_type
+{
+ int modules[3]; /* terminated with -1 */
+ char const *name;
+ char desc[10][80];
+};
diff --git a/src/deity_type_fwd.hpp b/src/deity_type_fwd.hpp
new file mode 100644
index 00000000..492bf1fa
--- /dev/null
+++ b/src/deity_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct deity_type;
diff --git a/src/device_allocation.cc b/src/device_allocation.cc
new file mode 100644
index 00000000..ec2d208c
--- /dev/null
+++ b/src/device_allocation.cc
@@ -0,0 +1,20 @@
+#include "device_allocation.hpp"
+
+#include <cassert>
+
+static void device_allocation_init(device_allocation *device_allocation, byte tval)
+{
+ assert(device_allocation != NULL);
+
+ device_allocation->tval = tval;
+ device_allocation->rarity = 0;
+ range_init(&device_allocation->base_level, 0, 0);
+ range_init(&device_allocation->max_level, 0, 0);
+}
+
+device_allocation *device_allocation_new(byte tval)
+{
+ device_allocation *d = new device_allocation;
+ device_allocation_init(d, tval);
+ return d;
+}
diff --git a/src/device_allocation.hpp b/src/device_allocation.hpp
new file mode 100644
index 00000000..28854eac
--- /dev/null
+++ b/src/device_allocation.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "device_allocation_fwd.hpp"
+#include "range.hpp"
+
+/*
+ * Device allocation for skill
+ */
+struct device_allocation
+{
+ byte tval;
+ s32b rarity;
+ range_type base_level;
+ range_type max_level;
+};
+
+struct device_allocation *device_allocation_new(byte tval);
diff --git a/src/device_allocation_fwd.hpp b/src/device_allocation_fwd.hpp
new file mode 100644
index 00000000..70e53fca
--- /dev/null
+++ b/src/device_allocation_fwd.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "h-basic.h"
+
+typedef struct device_allocation device_allocation;
+struct device_allocation;
+
+struct device_allocation *device_allocation_new(byte tval);
diff --git a/src/dice.cc b/src/dice.cc
new file mode 100644
index 00000000..187bae68
--- /dev/null
+++ b/src/dice.cc
@@ -0,0 +1,98 @@
+#include "dice.hpp"
+
+#include "z-rand.hpp"
+
+#include <cassert>
+
+void dice_init(dice_type *dice, long base, long num, long sides)
+{
+ assert(dice != NULL);
+
+ dice->base = base;
+ dice->num = num;
+ dice->sides = sides;
+}
+
+bool_ dice_parse(dice_type *dice, cptr s)
+{
+ long base, num, sides;
+
+ if (sscanf(s, "%ld+%ldd%ld", &base, &num, &sides) == 3)
+ {
+ dice_init(dice, base, num, sides);
+ return TRUE;
+ }
+
+ if (sscanf(s, "%ld+d%ld", &base, &sides) == 2)
+ {
+ dice_init(dice, base, 1, sides);
+ return TRUE;
+ }
+
+ if (sscanf(s, "d%ld", &sides) == 1)
+ {
+ dice_init(dice, 0, 1, sides);
+ return TRUE;
+ }
+
+ if (sscanf(s, "%ldd%ld", &num, &sides) == 2)
+ {
+ dice_init(dice, 0, num, sides);
+ return TRUE;
+ }
+
+ if (sscanf(s, "%ld", &base) == 1)
+ {
+ dice_init(dice, base, 0, 0);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void dice_parse_checked(dice_type *dice, cptr s)
+{
+ bool_ result = dice_parse(dice, s);
+ if (!result)
+ {
+ abort();
+ }
+}
+
+long dice_roll(dice_type *dice)
+{
+ assert(dice != NULL);
+ return dice->base + damroll(dice->num, dice->sides);
+}
+
+void dice_print(dice_type *dice, char *output)
+{
+ char buf[16];
+
+ output[0] = '\0';
+
+ if (dice->base > 0)
+ {
+ sprintf(buf, "%ld", dice->base);
+ strcat(output, buf);
+ }
+
+ if ((dice->num > 0) || (dice->sides > 0))
+ {
+ if (dice->base > 0)
+ {
+ strcat(output, "+");
+ }
+
+ if (dice->num > 1)
+ {
+ sprintf(buf, "%ld", dice->num);
+ strcat(output, buf);
+ }
+
+ strcat(output, "d");
+
+ sprintf(buf, "%ld", dice->sides);
+ strcat(output, buf);
+ }
+}
diff --git a/src/dice.hpp b/src/dice.hpp
new file mode 100644
index 00000000..40a49e37
--- /dev/null
+++ b/src/dice.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "dice_fwd.hpp"
+
+/**
+ * Dice
+ */
+struct dice_type
+{
+ long base; /* Base value to which roll is added. */
+ long num; /* Number of dice */
+ long sides; /* Sides per dice */
+};
diff --git a/src/dice_fwd.hpp b/src/dice_fwd.hpp
new file mode 100644
index 00000000..72b3b5ca
--- /dev/null
+++ b/src/dice_fwd.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "h-basic.h"
+
+typedef struct dice_type dice_type;
+struct dice_type;
+
+void dice_init(dice_type *dice, long base, long num, long sides);
+bool_ dice_parse(dice_type *dice, cptr s);
+void dice_parse_checked(dice_type *dice, cptr s);
+long dice_roll(dice_type *dice);
+void dice_print(dice_type *dice, char *buf);
diff --git a/src/dungeon.cc b/src/dungeon.cc
new file mode 100644
index 00000000..3ec79068
--- /dev/null
+++ b/src/dungeon.cc
@@ -0,0 +1,5565 @@
+/*
+ * 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 "dungeon.hpp"
+#include "dungeon.h"
+
+#include "birth.hpp"
+#include "birth.h"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd1.hpp"
+#include "cmd2.hpp"
+#include "cmd3.hpp"
+#include "cmd4.hpp"
+#include "cmd5.hpp"
+#include "cmd6.hpp"
+#include "cmd7.hpp"
+#include "corrupt.hpp"
+#include "dungeon_info_type.hpp"
+#include "feature_type.hpp"
+#include "files.h"
+#include "files.hpp"
+#include "generate.hpp"
+#include "gen_evol.hpp"
+#include "gods.hpp"
+#include "help.hpp"
+#include "hooks.hpp"
+#include "init2.hpp"
+#include "levels.hpp"
+#include "loadsave.h"
+#include "loadsave.hpp"
+#include "lua_bind.hpp"
+#include "melee1.hpp"
+#include "melee2.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "modules.hpp"
+#include "notes.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "options.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_spec.hpp"
+#include "player_type.hpp"
+#include "powers.hpp"
+#include "quest.hpp"
+#include "quark.hpp"
+#include "skills.hpp"
+#include "spell_type.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "spells5.hpp"
+#include "squeltch.hpp"
+#include "stats.hpp"
+#include "store.hpp"
+#include "tables.hpp"
+#include "timer_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "wild.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+#include "wizard2.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <boost/filesystem.hpp>
+#include <cassert>
+
+#define TY_CURSE_CHANCE 100
+#define DG_CURSE_CHANCE 50
+#define AUTO_CURSE_CHANCE 15
+#define CHAINSWORD_NOISE 100
+
+/**
+ * Type of a "sense" function
+ */
+typedef byte (*sense_function_t)(object_type const *o_ptr);
+
+/*
+ * Return a "feeling" (or NULL) about an item. Method 1 (Heavy).
+ */
+static byte value_check_aux1(object_type const *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);
+}
+
+static byte value_check_aux1_magic(object_type const *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).
+ */
+static byte value_check_aux2(object_type const *o_ptr)
+{
+ /* Cursed items (all of them) */
+ if (cursed_p(o_ptr)) return (SENSE_CURSED);
+
+ /* Artifacts -- except cursed/broken ones */
+ if (artifact_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Ego-Items -- except cursed/broken ones */
+ if (ego_item_p(o_ptr)) return (SENSE_GOOD_LIGHT);
+
+ /* Good armor bonus */
+ if (o_ptr->to_a > 0) return (SENSE_GOOD_LIGHT);
+
+ /* Good weapon bonuses */
+ if (o_ptr->to_h + o_ptr->to_d > 0) return (SENSE_GOOD_LIGHT);
+
+ /* Default to "average" */
+ return (SENSE_AVERAGE);
+}
+
+
+static byte value_check_aux2_magic(object_type const *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)
+{
+ if (praying_to(GOD_ERU))
+ {
+ if (p_ptr->grace > 100000)
+ {
+ if (magik(70)) return (TRUE);
+ else return (FALSE);
+ }
+ }
+ return (FALSE);
+}
+
+static sense_function_t select_sense(object_type *o_ptr, sense_function_t combat, sense_function_t magic)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOW:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_MSTAFF:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_BOOMERANG:
+ case TV_TRAPKIT:
+ {
+ return combat;
+ }
+
+ case TV_POTION:
+ case TV_POTION2:
+ case TV_SCROLL:
+ case TV_WAND:
+ case TV_STAFF:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ {
+ return magic;
+ }
+
+ /* Dual use? */
+ case TV_DAEMON_BOOK:
+ {
+ return combat;
+ }
+ }
+
+ return nullptr;
+}
+
+/*
+ * Sense quality of specific list of objects.
+ *
+ * Combat items (weapons and armour) - Fast, weak if combat skill < 10, strong
+ * otherwise.
+ *
+ * Magic items (scrolls, staffs, wands, potions etc) - Slow, weak if
+ * magic skill < 10, strong otherwise.
+ *
+ * It shouldn't matter a lot to discriminate against magic users, because
+ * they learn one form of ID or another, and because most magic items are
+ * easy_know.
+ */
+void sense_objects(std::vector<int> const &object_idxs)
+{
+ /* No sensing when confused */
+ if (p_ptr->confused) return;
+
+ /*
+ * In Angband, the chance of pseudo-id uses two different formulae:
+ *
+ * (1) Fast. 0 == rand_int(BASE / (plev * plev + 40)
+ * (2) Slow. 0 == rand_int(BASE / (plev + 5)
+ *
+ * Warriors: Fase with BASE == 9000
+ * Magi: Slow with BASE == 240000
+ * Priests: Fast with BASE == 10000
+ * Rogues: Fase with BASE == 20000
+ * Rangers: Slow with BASE == 120000
+ * Paladins: Fast with BASE == 80000
+ *
+ * In other words, those who have identify spells are penalised.
+ * The problems with Pern/Tome since it externalised player classes
+ * is that it uses the same and slow formula for spellcasters and
+ * fighters.
+ *
+ * In the following code, combat item pseudo-ID improves exponentially,
+ * (fast with BASE 9000) and magic one linear (slow with base 60000 --
+ * twice faster than V rangers).
+ *
+ * I hope this makes it closer to the original model -- pelpel
+ */
+
+ /* The combat skill affects weapon/armour pseudo-ID */
+ int combat_lev = get_skill(SKILL_COMBAT);
+
+ /* The magic skill affects magic item pseudo-ID */
+ int magic_lev = get_skill(SKILL_MAGIC);
+
+ /* Higher skill levels give the player better sense of items */
+ auto feel_combat = (combat_lev > 10) ? value_check_aux1 : value_check_aux2;
+ auto feel_magic = (magic_lev > 10) ? value_check_aux1_magic : value_check_aux2_magic;
+
+ /*** Sense everything ***/
+
+ /* Check everything */
+ for (auto i : object_idxs)
+ {
+ object_type *o_ptr = get_object(i);
+
+ /* Skip empty slots */
+ if (!o_ptr->k_idx) continue;
+
+ /* We know about it already, do not tell us again */
+ if (o_ptr->ident & (IDENT_SENSE)) continue;
+
+ /* It is fully known, no information needed */
+ if (object_known_p(o_ptr)) continue;
+
+ /* Select appropriate sensing function, if any */
+ sense_function_t sense = select_sense(o_ptr, feel_combat, feel_magic);
+
+ /* Skip non-sensed items */
+ if (!sense)
+ {
+ continue;
+ }
+
+ /* Check for a feeling */
+ byte feel = sense(o_ptr);
+
+ /* Skip non-feelings */
+ if (feel == SENSE_NONE)
+ {
+ continue;
+ }
+
+ /* Get an object description */
+ char o_name[80];
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Messages */
+ if (i < 0) {
+ // We get a message from the floor handling code, so
+ // it would be overkill to have a message here too.
+ }
+ else 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]);
+ }
+ 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();
+}
+
+void sense_inventory(void)
+{
+ static std::vector<int> idxs;
+ // Initialize static vector if necessary
+ if (idxs.empty())
+ {
+ idxs.reserve(INVEN_TOTAL);
+ for (int i = 0; i < INVEN_TOTAL; i++)
+ {
+ idxs.push_back(i);
+ }
+ }
+ sense_objects(idxs);
+}
+
+/*
+ * Go to any level (ripped off from wiz_jump)
+ */
+static void pattern_teleport(void)
+{
+ /* Ask for level */
+ if (get_check("Teleport level? "))
+ {
+ char ppp[80];
+
+ char tmp_val[160];
+
+ /* Prompt */
+ sprintf(ppp, "Teleport to level (0-%d): ", 99);
+
+ /* Default */
+ sprintf(tmp_val, "%d", dun_level);
+
+ /* Ask for a level */
+ if (!get_string(ppp, tmp_val, 10)) return;
+
+ /* Extract request */
+ command_arg = atoi(tmp_val);
+ }
+ else if (get_check("Normal teleport? "))
+ {
+ teleport_player(200);
+ return;
+ }
+ else
+ {
+ return;
+ }
+
+ /* Paranoia */
+ if (command_arg < 0) command_arg = 0;
+
+ /* Paranoia */
+ if (command_arg > 99) command_arg = 99;
+
+ /* Accept request */
+ msg_format("You teleport to dungeon level %d.", command_arg);
+
+ autosave_checkpoint();
+
+ /* Change level */
+ dun_level = command_arg;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Returns TRUE if we are on the Straight Road...
+ */
+static bool_ pattern_effect(void)
+{
+ if ((cave[p_ptr->py][p_ptr->px].feat < FEAT_PATTERN_START) ||
+ (cave[p_ptr->py][p_ptr->px].feat > FEAT_PATTERN_XTRA2)) return (FALSE);
+
+ if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_END)
+ {
+ (void)set_poisoned(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ (void)set_blind(0);
+ (void)set_afraid(0);
+ (void)do_res_stat(A_STR, TRUE);
+ (void)do_res_stat(A_INT, TRUE);
+ (void)do_res_stat(A_WIS, TRUE);
+ (void)do_res_stat(A_DEX, TRUE);
+ (void)do_res_stat(A_CON, TRUE);
+ (void)do_res_stat(A_CHR, TRUE);
+ (void)restore_level();
+ (void)hp_player(1000);
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_PATTERN_OLD);
+ msg_print("This section of the Straight Road looks less powerful.");
+ }
+
+
+ /*
+ * We could make the healing effect of the
+ * Pattern center one-time only to avoid various kinds
+ * of abuse, like luring the win monster into fighting you
+ * in the middle of the pattern...
+ */
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_OLD)
+ {
+ /* No effect */
+ }
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA1)
+ {
+ pattern_teleport();
+ }
+ else if (cave[p_ptr->py][p_ptr->px].feat == FEAT_PATTERN_XTRA2)
+ {
+ if (!(p_ptr->invuln))
+ take_hit(200, "walking the corrupted Straight Road");
+ }
+
+ else
+ {
+ if (!(p_ptr->invuln))
+ take_hit(damroll(1, 3), "walking the Straight Road");
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * If player has inscribed the object with "!!", let him know when it's
+ * recharged. -LM-
+ */
+static void recharged_notice(object_type *o_ptr)
+{
+ char o_name[80];
+
+ cptr s;
+
+
+ /* No inscription */
+ if (!o_ptr->note) return;
+
+ /* Find a '!' */
+ s = strchr(quark_str(o_ptr->note), '!');
+
+ /* Process notification request. */
+ while (s)
+ {
+ /* Find another '!' */
+ if (s[1] == '!')
+ {
+ /* Describe (briefly) */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Notify the player */
+ if (o_ptr->number > 1)
+ {
+ msg_format("Your %s are recharged.", o_name);
+ }
+ else
+ {
+ msg_format("Your %s is recharged.", o_name);
+ }
+
+ /* Done. */
+ return;
+ }
+
+ /* Keep looking for '!'s */
+ s = strchr(s + 1, '!');
+ }
+}
+
+
+
+/*
+ * Regenerate hit points -RAK-
+ */
+static void regenhp(int percent)
+{
+ s32b new_chp, new_chp_frac;
+
+ int old_chp;
+
+
+ /* Only if alive */
+ if (!(p_ptr->necro_extra & CLASS_UNDEAD))
+ {
+ /* Save the old hitpoints */
+ old_chp = p_ptr->chp;
+
+ /* Extract the new hitpoints */
+ new_chp = ((long)p_ptr->mhp) * percent + PY_REGEN_HPBASE;
+
+ /* div 65536 */
+ p_ptr->chp += new_chp >> 16;
+
+ /* check for overflow */
+ if ((p_ptr->chp < 0) && (old_chp > 0)) p_ptr->chp = MAX_SHORT;
+
+ /* mod 65536 */
+ new_chp_frac = (new_chp & 0xFFFF) + p_ptr->chp_frac;
+
+ if (new_chp_frac >= 0x10000L)
+ {
+ p_ptr->chp_frac = new_chp_frac - 0x10000L;
+ p_ptr->chp++;
+ }
+ else
+ {
+ p_ptr->chp_frac = new_chp_frac;
+ }
+
+ /* Fully healed */
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+ }
+
+ /* Notice changes */
+ if (old_chp != p_ptr->chp)
+ {
+ /* Redraw */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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_FRAME);
+
+ /* 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_FRAME);
+ }
+ }
+
+ /* Regenerate everyone */
+ for (i = 1; i < m_max; i++)
+ {
+ /* Check the i'th monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* 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 */
+ auto const r_ptr = m_ptr->race();
+ 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_FRAME);
+ }
+ }
+}
+
+
+/*
+ * Does an object decay?
+ *
+ * Should belong to object1.c, renamed to object_decays() -- pelpel
+ */
+static 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)
+{
+ spell_type *spell = spell_at(-music);
+ return spell_type_produce_effect_lasting(spell);
+}
+
+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 = 0;
+ }
+ else
+ {
+ p_ptr->csp -= use_mana;
+
+ /* Redraw mana */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+/*
+ * Generate the feature effect
+ */
+static void apply_effect(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ feature_type *f_ptr = &f_info[c_ptr->feat];
+
+
+ if (f_ptr->d_frequency[0] != 0)
+ {
+ int i;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* Check the frequency */
+ if (f_ptr->d_frequency[i] == 0) continue;
+
+ if (((turn % f_ptr->d_frequency[i]) == 0) &&
+ ((f_ptr->d_side[i] != 0) || (f_ptr->d_dice[i] != 0)))
+ {
+ int l, dam = 0;
+ int d = f_ptr->d_dice[i], s = f_ptr->d_side[i];
+
+ if (d == -1) d = p_ptr->lev;
+ if (s == -1) s = p_ptr->lev;
+
+ /* Roll damage */
+ for (l = 0; l < d; l++)
+ {
+ dam += randint(s);
+ }
+
+ /* Apply damage */
+ project( -100, 0, y, x, dam, f_ptr->d_type[i],
+ PROJECT_KILL | PROJECT_HIDE);
+
+ /* Hack -- notice death */
+ if (!alive || death) return;
+ }
+ }
+ }
+}
+
+
+
+/* XXX XXX XXX */
+static bool_ is_recall = FALSE;
+
+
+/*
+ * Hook for corruptions
+ */
+static void process_world_corruptions()
+{
+ if (player_has_corruption(CORRUPT_RANDOM_TELEPORT))
+ {
+ if (rand_int(300) == 1)
+ {
+ if (magik(70))
+ {
+ if (get_check("Teleport?"))
+ {
+ teleport_player(50);
+ }
+ else
+ {
+ disturb(0);
+ msg_print("Your corruption takes over you, you teleport!");
+ teleport_player(50);
+ }
+ }
+ }
+ }
+
+ if (player_has_corruption(CORRUPT_ANTI_TELEPORT))
+ {
+ if (p_ptr->corrupt_anti_teleport_stopped)
+ {
+ int amt = p_ptr->msp + p_ptr->csp;
+ amt = amt / 100;
+ if (amt < 1) {
+ amt = 1;
+ }
+ increase_mana(-amt);
+ if (p_ptr->csp == 0)
+ {
+ p_ptr->corrupt_anti_teleport_stopped = FALSE;
+ msg_print("You stop controlling your corruption.");
+ p_ptr->update = p_ptr->update | PU_BONUS;
+ }
+ }
+ }
+}
+
+
+/*
+ * Shim for accessing Lua variable.
+ */
+static bool_ grace_delay_trigger()
+{
+ p_ptr->grace_delay++;
+
+ if (p_ptr->grace_delay >= 15)
+ {
+ /* reset */
+ p_ptr->grace_delay = 0;
+ /* triggered */
+ return TRUE;
+ }
+ else
+ {
+ /* not triggered */
+ return FALSE;
+ }
+}
+
+/*
+ * Hook for gods
+ */
+static void process_world_gods()
+{
+ const char *race_name = rp_ptr->title;
+ const char *subrace_name = rmp_ptr->title;
+
+ if (p_ptr->pgod == GOD_VARDA)
+ {
+ if (grace_delay_trigger())
+ {
+ /* Piety increases if in light. */
+ if (cave[p_ptr->py][p_ptr->px].info & CAVE_GLOW)
+ {
+ inc_piety(GOD_ALL, 2);
+ }
+
+ if (streq(race_name, "Orc") ||
+ streq(race_name, "Troll") ||
+ streq(race_name, "Dragon") ||
+ streq(race_name, "Demon"))
+ {
+ /* Varda hates evil races */
+ inc_piety(GOD_ALL, -2);
+ } else {
+ /* ... and everyone slightly less */
+ inc_piety(GOD_ALL, -1);
+ }
+
+ /* Prayer uses piety */
+ if (p_ptr->praying)
+ {
+ inc_piety(GOD_ALL, -1);
+ }
+ }
+ }
+
+ if (p_ptr->pgod == GOD_ULMO)
+ {
+ if (grace_delay_trigger())
+ {
+ int i;
+ /* Ulmo likes the Edain (except Easterlings) */
+ if (streq(race_name, "Human") ||
+ streq(race_name, "Dunadan") ||
+ streq(race_name, "Druadan") ||
+ streq(race_name, "RohanKnight"))
+ {
+ inc_piety(GOD_ALL, 2);
+ }
+ else if (streq(race_name, "Easterling") ||
+ streq(race_name, "Demon") ||
+ streq(race_name, "Orc"))
+ {
+ /* hated races */
+ inc_piety(GOD_ALL, -2);
+ }
+ else
+ {
+ inc_piety(GOD_ALL, 1);
+ }
+
+ if (p_ptr->praying)
+ {
+ inc_piety(GOD_ALL, -1);
+ }
+
+ /* Gain 1 point for each trident in inventory */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ if ((p_ptr->inventory[i].tval == TV_POLEARM) &&
+ (p_ptr->inventory[i].sval == SV_TRIDENT))
+ {
+ inc_piety(GOD_ALL, 1);
+ }
+ }
+ }
+ }
+
+ if (p_ptr->pgod == GOD_AULE)
+ {
+ if (grace_delay_trigger())
+ {
+ int i;
+
+ /* Aule likes Dwarves and Dark Elves (Eol's
+ * influence here) */
+ if (!(streq(race_name, "Dwarf") ||
+ streq(race_name, "Petty-dwarf") ||
+ streq(race_name, "Gnome") ||
+ streq(race_name, "Dark-Elf")))
+ {
+ inc_piety(GOD_ALL, -1);
+ }
+
+ /* Search inventory for axe or hammer - Gain 1
+ * point of grace for each hammer or axe */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ int tval = p_ptr->inventory[i].tval;
+ int sval = p_ptr->inventory[i].sval;
+
+ switch (tval)
+ {
+ case TV_AXE:
+ inc_piety(GOD_ALL, 1);
+ break;
+
+ case TV_HAFTED:
+ if ((sval == SV_WAR_HAMMER) ||
+ (sval == SV_LUCERN_HAMMER) ||
+ (sval == SV_GREAT_HAMMER))
+ {
+ inc_piety(GOD_ALL, 1);
+ }
+ break;
+ }
+ }
+
+ /* Praying may grant you a free stone skin
+ * once in a while */
+ if (p_ptr->praying)
+ {
+ int chance;
+ s32b grace;
+
+ inc_piety(GOD_ALL, -2);
+ grace = p_ptr->grace; /* shorthand */
+
+ chance = 1;
+ if (grace >= 50000)
+ {
+ chance = 50000;
+ }
+ else
+ {
+ chance = 50000 - grace;
+ }
+
+ if (randint(100000) <= 100000 / chance)
+ {
+ s16b type = 0;
+
+ if (grace >= 10000)
+ {
+ type = SHIELD_COUNTER;
+ }
+
+ set_shield(
+ randint(10) + 10 + (grace / 100),
+ 10 + (grace / 100),
+ type,
+ 2 + (grace / 200),
+ 3 + (grace / 400));
+
+ msg_print("Aule casts Stone Skin on you.");
+ }
+ }
+ }
+ }
+
+ if (p_ptr->pgod == GOD_MANDOS)
+ {
+ if (grace_delay_trigger())
+ {
+ /* He loves astral beings */
+ if (streq(subrace_name, "LostSoul"))
+ {
+ inc_piety(GOD_ALL, 1);
+ }
+
+ /* He likes High Elves only, though, as races */
+ if (!streq(race_name, "High-Elf"))
+ {
+ inc_piety(GOD_ALL, -1);
+ }
+
+ /* Really hates vampires and demons */
+ if (streq(subrace_name, "Vampire") ||
+ streq(race_name, "Demon"))
+ {
+ inc_piety(GOD_ALL, -10);
+ }
+ else
+ {
+ inc_piety(GOD_ALL, 2);
+ }
+ /* he really doesn't like to be disturbed */
+ if (p_ptr->praying)
+ {
+ inc_piety(GOD_ALL, -5);
+ }
+ }
+ }
+
+}
+
+/*
+ * Handle certain things once every 10 game turns
+ *
+ * Note that a single movement in the overhead wilderness mode
+ * consumes 132 times as much energy as a normal one...
+ */
+static void process_world(void)
+{
+ timer_type *t_ptr;
+
+ int x, y, i, j;
+
+ int regen_amount;
+ bool_ cave_no_regen = FALSE;
+ int upkeep_factor = 0;
+
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ cave_type *c_ptr;
+
+ object_type *o_ptr;
+ u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0;
+
+
+ /*
+ * Every 10 game turns -- which means this section is invoked once
+ * in a player turn under the normal speed, and 132 times in a move
+ * in the reduced map mode.
+ */
+ if (turn % 10) return;
+
+ /*
+ * I don't know if this is the right thing to do because I'm totally
+ * ignorant (yes, I must admit that...) about the scripting part of
+ * the game, but since there have been complaints telling us it
+ * runs terribly slow in the reduced map mode... -- pelpel
+ *
+ * Note to coders: if it is desirable to make this active in the
+ * reduced map mode, remove the if condition surrounding the line
+ * and move the code inside into every 1000 game turns section.
+ */
+ if (dun_level || (!p_ptr->wild_mode))
+ {
+ /* Handle corruptions */
+ process_world_corruptions();
+
+ /* Handle gods */
+ process_world_gods();
+
+ /* 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;
+ assert(t_ptr->callback != NULL);
+ t_ptr->callback();
+ }
+ }
+
+ /* Check the fate */
+ if (fate_option && (p_ptr->lev > 10))
+ {
+ /*
+ * WAS: == 666 against randint(50000).
+ * Since CPU's don't know Judeo-Christian / Cabalistic traditions,
+ * and since comparisons with zero is more efficient in many
+ * architectures...
+ */
+ if (rand_int(50000) == 0) gain_fate(0);
+ }
+
+ /*** Is the wielded monsters still hypnotised ***/
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (o_ptr->k_idx)
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval];
+
+ if ((randint(1000) < r_ptr->level - ((p_ptr->lev * 2) + get_skill(SKILL_SYMBIOTIC))))
+ {
+ msg_format("%s breaks free from hypnosis!",
+ symbiote_name(TRUE));
+ carried_make_attack_normal(o_ptr->pval);
+ }
+ }
+
+ /*** Attempt timed autosave ***/
+ if (autosave_t && autosave_freq)
+ {
+ if ((turn % ((s32b)autosave_freq * 10)) == 0)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+ }
+
+
+ /*** Handle the wilderness/town (sunshine) ***/
+
+ /* While in town/wilderness and not in the overworld mode */
+ if (!dun_level && !p_ptr->wild_mode)
+ {
+ /* Hack -- Daybreak/Nighfall in town */
+ if ((turn % ((10L * DAY) / 2)) == 0)
+ {
+ bool_ dawn;
+
+ /* Check for dawn */
+ dawn = ((turn % (10L * DAY)) == 0);
+
+ /* Day breaks */
+ if (dawn)
+ {
+ /* Message */
+ msg_print("The sun has risen.");
+
+ /* Hack -- Scan the town */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Assume lit */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Hack -- Memorize lit grids if allowed */
+ if (view_perma_grids) c_ptr->info |= (CAVE_MARK);
+
+ /* Hack -- Notice spot */
+ note_spot(y, x);
+ }
+ }
+ }
+
+ /* Night falls */
+ else
+ {
+ /* Message */
+ msg_print("The sun has set.");
+
+ /* Hack -- Scan the town */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* Get the cave grid */
+ c_ptr = &cave[y][x];
+
+ /* Darken "boring" features */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Forget the grid */
+ c_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Hack -- Notice spot */
+ note_spot(y, x);
+ }
+ }
+ }
+ }
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ }
+
+ /*** Process the monsters ***/
+
+ /* Check for creature generation. */
+ if (!p_ptr->wild_mode &&
+ !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->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 (race_flags1_p(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);
+
+ /* Hack -- faint (bypass free action) */
+ (void)set_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;
+
+ 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;
+ }
+ }
+
+ /* 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);
+ }
+ else
+ {
+ regenmana(regen_amount);
+ }
+ }
+
+ /* Eru piety incraese with time */
+ if (((turn % 100) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode))
+ {
+ if ((p_ptr->pgod == GOD_ERU) && !p_ptr->praying)
+ {
+ 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))
+ {
+ if (p_ptr->pgod == GOD_MANWE)
+ {
+ int dec = 4 - wisdom_scale(3);
+
+ if (p_ptr->praying)
+ {
+ dec++;
+ }
+
+ if (race_flags1_p(PR1_ELF))
+ {
+ dec -= wisdom_scale(2);
+ }
+
+ dec = std::max(1, dec);
+
+ inc_piety(GOD_MANWE, -dec);
+ }
+
+ if (p_ptr->pgod == GOD_MELKOR)
+ {
+ int dec = 8 - wisdom_scale(6);
+
+ if (p_ptr->praying)
+ {
+ dec++;
+ }
+
+ if (race_flags1_p(PR1_ELF))
+ {
+ dec += 5 - wisdom_scale(4);
+ }
+
+ dec = std::max(1, dec);
+
+ inc_piety(GOD_MELKOR, -dec);
+ }
+
+ if (praying_to(GOD_TULKAS))
+ {
+ int dec = std::max(1, 4 - wisdom_scale(3));
+
+ inc_piety(GOD_TULKAS, -dec);
+ }
+ }
+ /* Yavanna piety decrease with time */
+ if (((turn % 400) == 0) && (!p_ptr->did_nothing) && (!p_ptr->wild_mode) && (dun_level))
+ {
+ if (p_ptr->pgod == GOD_YAVANNA)
+ {
+ int dec = 5 - wisdom_scale(3);
+
+ /* Blech what an hideous hack */
+ if (!strcmp(rp_ptr->title, "Ent"))
+ {
+ dec -= wisdom_scale(2);
+ }
+
+ dec = std::max(1, dec);
+ 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 */
+ if (praying_to(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_FRAME);
+
+ /* 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);
+ }
+ }
+
+ /* True Strike */
+ if (p_ptr->strike)
+ {
+ (void)set_strike(p_ptr->strike - 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 precognition */
+ if (p_ptr->tim_precognition > 0)
+ {
+ set_tim_precognition(p_ptr->tim_precognition - 1);
+ }
+
+ /* 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 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);
+ }
+
+ /* 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);
+ }
+
+ /* 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)
+ {
+ dec_paralyzed();
+ }
+
+ /* 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);
+ }
+
+ /* 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);
+ }
+
+ 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_idxs.empty()) &&
+ ((j != p_ptr->py) && (i != p_ptr->px))) continue;
+ }
+
+ /* Let's not hurt poor monsters */
+ if (cave[j][k].m_idx) continue;
+
+ /* Roll damage */
+ for (l = 0; l < d_ptr->d_dice[i]; l++)
+ {
+ dam += randint(d_ptr->d_side[i]);
+ }
+
+ /* Apply damage */
+ project( -100, 0, j, k, dam, d_ptr->d_type[i],
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ }
+ }
+ }
+ }
+
+ /* handle spell effects */
+ if (!p_ptr->wild_mode)
+ {
+ /*
+ * I noticed significant performance degrade after the introduction
+ * of staying spell effects. I believe serious optimisation effort
+ * is required before another release.
+ *
+ * More important is to fix that display weirdness...
+ *
+ * It seems that the game never expects that monster deaths and
+ * terrain feature changes should happen here... Moving these
+ * to process_player() [before resting code, with "every 10 game turn"
+ * 'if'] may or may not fix the problem... -- pelpel to DG
+ */
+ for (j = 0; j < cur_hgt - 1; j++)
+ {
+ for (i = 0; i < cur_wid - 1; i++)
+ {
+ int e = cave[j][i].effect;
+
+ if (e)
+ {
+ effect_type *e_ptr = &effects[e];
+
+ if (e_ptr->time)
+ {
+ /* Apply damage */
+ project(0, 0, j, i, e_ptr->dam, e_ptr->type,
+ PROJECT_KILL | PROJECT_ITEM | PROJECT_HIDE);
+ }
+ else
+ {
+ cave[j][i].effect = 0;
+ }
+
+ if ((e_ptr->flags & EFF_WAVE) && !(e_ptr->flags & EFF_LAST))
+ {
+ if (distance(e_ptr->cy, e_ptr->cx, j, i) < e_ptr->rad - 1)
+ cave[j][i].effect = 0;
+ }
+ else if ((e_ptr->flags & EFF_STORM) && !(e_ptr->flags & EFF_LAST))
+ {
+ cave[j][i].effect = 0;
+ }
+
+ lite_spot(j, i);
+ }
+ }
+ }
+
+ /* Reduce & handle effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ {
+ /* Skip empty slots */
+ if (effects[i].time == 0) continue;
+
+ /* Reduce duration */
+ effects[i].time--;
+
+ /* Creates a "wave" effect*/
+ if (effects[i].flags & EFF_WAVE)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y, z;
+
+ e_ptr->rad++;
+
+ /* What a frelling ugly line of ifs ... */
+ if (effects[i].flags & EFF_DIR8)
+ for (y = e_ptr->cy - e_ptr->rad, z = 0; y <= e_ptr->cy; y++, z++)
+ {
+ for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR2)
+ for (y = e_ptr->cy, z = e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++, z--)
+ {
+ for (x = e_ptr->cx - (e_ptr->rad - z); x <= e_ptr->cx + (e_ptr->rad - z); x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR6)
+ for (x = e_ptr->cx, z = e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++, z--)
+ {
+ for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR4)
+ for (x = e_ptr->cx - e_ptr->rad, z = 0; x <= e_ptr->cx; x++, z++)
+ {
+ for (y = e_ptr->cy - (e_ptr->rad - z); y <= e_ptr->cy + (e_ptr->rad - z); y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR9)
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++)
+ {
+ for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR1)
+ for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR7)
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else if (effects[i].flags & EFF_DIR3)
+ for (y = e_ptr->cy; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ else
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ /* This is *slow* -- pelpel */
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) == e_ptr->rad))
+ cave[y][x].effect = i;
+ }
+ }
+ }
+ /* Creates a "storm" effect*/
+ else if (effects[i].flags & EFF_STORM)
+ {
+ effect_type *e_ptr = &effects[i];
+ int x, y;
+
+ e_ptr->cy = p_ptr->py;
+ e_ptr->cx = p_ptr->px;
+ for (y = e_ptr->cy - e_ptr->rad; y <= e_ptr->cy + e_ptr->rad; y++)
+ {
+ for (x = e_ptr->cx - e_ptr->rad; x <= e_ptr->cx + e_ptr->rad; x++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (los(e_ptr->cy, e_ptr->cx, y, x) &&
+ (distance(e_ptr->cy, e_ptr->cx, y, x) <= e_ptr->rad))
+ {
+ cave[y][x].effect = i;
+ lite_spot(y, x);
+ }
+ }
+ }
+ }
+ }
+
+ apply_effect(p_ptr->py, p_ptr->px);
+ }
+
+ /* Arg cannot breath? */
+ if ((dungeon_flags2 & DF2_WATER_BREATH) && (!p_ptr->water_breath))
+ {
+ cmsg_print(TERM_L_RED, "You cannot breathe water! You suffocate!");
+ take_hit(damroll(3, p_ptr->lev), "suffocating");
+ }
+ if ((dungeon_flags2 & DF2_NO_BREATH) && (!p_ptr->magical_breath))
+ {
+ cmsg_print(TERM_L_RED, "There is no air there! You suffocate!");
+ take_hit(damroll(3, p_ptr->lev), "suffocating");
+ }
+
+ /*
+ * Every 1500 turns, warn about any Black Breath not gotten from
+ * an equipped object, and stop any resting. -LM-
+ *
+ * It's apparent that someone has halved the frequency... -- pelpel
+ */
+ if (((turn % 3000) == 0) && p_ptr->black_breath)
+ {
+ u32b f1, f2, f3, f4, f5;
+
+ bool_ be_silent = FALSE;
+
+ /* check all equipment for the Black Breath flag. */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* No messages if object has the flag, to avoid annoyance. */
+ if (f4 & (TR4_BLACK_BREATH)) be_silent = TRUE;
+
+ }
+ /* If we are allowed to speak, warn and disturb. */
+
+ if (!be_silent)
+ {
+ cmsg_print(TERM_L_DARK, "The Black Breath saps your soul!");
+ disturb(0);
+ }
+ }
+
+
+ /*** 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);
+ cmsg_print(TERM_YELLOW, "Your light has gone out!");
+ }
+
+ /* The light is getting dim */
+ else if ((o_ptr->timeout < 100) && (o_ptr->timeout % 10 == 0))
+ {
+ if (disturb_minor) disturb(0);
+ cmsg_print(TERM_YELLOW, "Your light is growing faint.");
+ }
+ }
+ }
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+
+ /*** Process Inventory ***/
+
+ /*
+ * Handle experience draining. In Oangband, the effect is worse,
+ * especially for high-level characters. As per Tolkien, hobbits
+ * are resistant.
+ */
+ if (p_ptr->black_breath)
+ {
+ byte chance = 0;
+ int plev = p_ptr->lev;
+
+ if (race_flags1_p(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);
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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);
+
+ 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_FRAME);
+
+ /* 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);
+ }
+
+ /* Redraw */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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);
+
+ /* 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);
+ teleport_player(50);
+ }
+ }
+ }
+
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Hack: Skip wielded lights that need fuel (already handled above) */
+ if ((i == INVEN_LITE) && (o_ptr->tval == TV_LITE) && (f4 & TR4_FUEL_LITE)) continue;
+
+ /* Recharge activatable objects */
+ if (o_ptr->timeout > 0)
+ {
+ /* Recharge */
+ o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->timeout == 0)
+ {
+ recharged_notice(o_ptr);
+ j++;
+ }
+ }
+
+ /* Recharge second spell in Mage Staffs of Spells */
+ if (is_ego_p(o_ptr, EGO_MSTAFF_SPELL) && (o_ptr->xtra2 > 0))
+ {
+ /* Recharge */
+ o_ptr->xtra2--;
+
+ /* Notice changes */
+ if (o_ptr->xtra2 == 0) j++;
+ }
+ }
+
+ /* Notice changes */
+ if (j)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Recharge rods */
+ for (j = 0, i = 0; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Temporary items are destroyed */
+ if (f5 & TR5_TEMPORARY)
+ {
+ o_ptr->timeout--;
+
+ if (o_ptr->timeout <= 0)
+ {
+ inc_stack_size(i, -99);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+
+ /* Examine all charging rods or stacks of charging rods. */
+ if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2))
+ {
+ /* Increase the rod's mana. */
+ o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1;
+
+ /* Always notice */
+ j++;
+
+ /* Notice changes, provide message if object is inscribed. */
+ if (o_ptr->timeout >= o_ptr->pval2)
+ {
+ o_ptr->timeout = o_ptr->pval2;
+ recharged_notice(o_ptr);
+ }
+ }
+
+ /* Examine all charging random artifacts */
+ if ((f5 & TR5_ACTIVATE_NO_WIELD) && (o_ptr->timeout > 0))
+ {
+ /* Charge it */
+ o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->timeout == 0)
+ {
+ j++;
+ recharged_notice(o_ptr);
+ }
+ }
+
+ /* Decay objects in pack */
+ if (decays(o_ptr))
+ {
+ /* Decay it */
+ if (o_ptr->pval != 0)
+ {
+ if (o_ptr->timeout > 0)
+ {
+ if (dungeon_flags1 & DF1_HOT)
+ {
+ o_ptr->pval -= 2;
+ }
+ else if ((dungeon_flags1 & DF1_COLD) && rand_int(2))
+ {
+ if (magik(50)) o_ptr->pval--;
+ }
+ else
+ {
+ o_ptr->pval--;
+ }
+ }
+
+ if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ pack_decay(i);
+ j++;
+ }
+ }
+ }
+
+ /* Hatch eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ if (o_ptr->timeout == 0)
+ {
+ o_ptr->pval--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ int mx = p_ptr->px;
+ int 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);
+
+ monster_type *m_ptr = &m_list[cave[my][mx].m_idx];
+ auto const r_ptr = m_ptr->race();
+
+ if ((r_ptr->flags9 & RF9_IMPRESED) && can_create_companion())
+ {
+ msg_format("And you have given the imprint to your %s!", r_ptr->name);
+ m_ptr->status = MSTATUS_COMPANION;
+ }
+
+ inc_stack_size(i, -1);
+
+ j++;
+ }
+ }
+ }
+ }
+
+ /* Notice changes */
+ if (j)
+ {
+ /* Combine pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /*** Process Objects ***/
+
+ /* Process objects */
+ for (i = 1; i < o_max; i++)
+ {
+ /* Access object */
+ o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the rod */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Temporary items are destroyed */
+ if (f5 & TR5_TEMPORARY)
+ {
+ o_ptr->timeout--;
+
+ if (o_ptr->timeout <= 0)
+ {
+ floor_item_increase(i, -99);
+ floor_item_optimize(i);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+
+ /* Recharge rods on the ground. No messages. */
+ if ((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->timeout < o_ptr->pval2))
+ {
+ /* Increase the rod's mana. */
+ o_ptr->timeout += (f4 & TR4_CHARGING) ? 2 : 1;
+
+ /* Do not overflow */
+ if (o_ptr->timeout >= o_ptr->pval2)
+ {
+ o_ptr->timeout = o_ptr->pval2;
+ }
+ }
+
+ /* Decay objects on the ground*/
+ if (decays(o_ptr))
+ {
+ /* Decay it */
+ if (o_ptr->pval != 0)
+ {
+ if (o_ptr->timeout > 0)
+ {
+ if (dungeon_flags1 & DF1_HOT)
+ {
+ o_ptr->pval -= 2;
+ }
+ else if ((dungeon_flags1 & DF1_COLD) && rand_int(2))
+ {
+ if (magik(50)) o_ptr->pval--;
+ }
+ else
+ {
+ o_ptr->pval--;
+ }
+ }
+
+ if ((o_ptr->timeout > 0) && o_ptr->timeout < o_ptr->weight) o_ptr->timeout--;
+
+ /* Turn it into a skeleton */
+ if (o_ptr->pval <= 0)
+ {
+ floor_decay(i);
+ }
+ }
+ }
+
+ /* Hatch eggs */
+ if (o_ptr->tval == TV_EGG)
+ {
+ int mx, my;
+ if (o_ptr->timeout > 0) o_ptr->pval--;
+
+ /* Notice changes */
+ if (o_ptr->pval <= 0)
+ {
+ mx = o_ptr->ix;
+ my = o_ptr->iy;
+ get_pos_player(5, &my, &mx);
+ msg_print("An egg hatches!");
+ place_monster_one(my, mx, o_ptr->pval2, 0, FALSE, MSTATUS_ENEMY);
+ floor_item_increase(i, -1);
+ floor_item_describe(i);
+ floor_item_optimize(i);
+ }
+ }
+ }
+
+
+ /*** Involuntary Movement ***/
+
+ /* Delayed Word-of-Recall */
+ if (p_ptr->word_recall)
+ {
+ /* Can we ? */
+ if (process_hooks_new(HOOK_RECALL, NULL, NULL))
+ {
+ p_ptr->word_recall = 0;
+ }
+
+ /* No recall. sorry */
+ else if (dungeon_flags2 & DF2_NO_RECALL_OUT)
+ {
+ cmsg_print(TERM_L_DARK, "You cannot recall from here.");
+ p_ptr->word_recall = 0;
+ }
+
+ /* Cannot WoR out of death fate levels */
+ else if (dungeon_type == DUNGEON_DEATH)
+ {
+ cmsg_print(TERM_L_DARK, "You are fated to die here. FIGHT for your life!");
+ p_ptr->word_recall = 0;
+ }
+
+ /* I think the 'inside_quest' code belongs here -- pelpel */
+
+ /* They cannot use word of recall until reaching surface */
+ else if (p_ptr->astral)
+ {
+ msg_print("As an astral being you can't recall.");
+ p_ptr->word_recall = 0;
+ }
+
+ /* Normal WoR */
+ else
+ {
+ /*
+ * HACK: Autosave BEFORE resetting the recall counter (rr9)
+ * The player is yanked up/down as soon as
+ * he loads the autosaved game.
+ */
+ if (p_ptr->word_recall == 1)
+ {
+ autosave_checkpoint();
+ }
+
+ /* Make SURE that persistent levels are saved
+ * I don't know if this is needed, but I'm getting reports,
+ * so I'm adding this extra save -- Neil
+ */
+ save_dungeon();
+
+ /* Count down towards recall */
+ p_ptr->word_recall--;
+
+ /* Activate the recall */
+ if (p_ptr->word_recall == 0)
+ {
+ /* Disturbing! */
+ disturb(0);
+
+ /* Determine the level */
+ if (p_ptr->inside_quest)
+ {
+ msg_print("The recall is cancelled by a powerful magic force!");
+ }
+ else if (dun_level)
+ {
+ msg_print("You feel yourself yanked upwards!");
+
+ p_ptr->recall_dungeon = dungeon_type;
+ dungeon_type = DUNGEON_WILDERNESS;
+ dun_level = 0;
+
+ is_recall = TRUE;
+
+ p_ptr->inside_quest = 0;
+ p_ptr->leaving = TRUE;
+ }
+ else
+ {
+ msg_print("You feel yourself yanked downwards!");
+
+ /* New depth */
+ dungeon_type = p_ptr->recall_dungeon;
+ dun_level = max_dlv[dungeon_type];
+ if (dun_level < 1) dun_level = 1;
+
+ /* Reset player position */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+
+ /* Leaving */
+ is_recall = TRUE;
+
+ p_ptr->leaving = TRUE;
+ p_ptr->wild_mode = FALSE;
+ }
+
+ /* Sound */
+ sound(SOUND_TPLEVEL);
+ }
+ }
+ }
+}
+
+
+/*
+ * Verify use of "wizard" mode
+ */
+static bool_ enter_wizard_mode(void)
+{
+ /* Ask first time, but not while loading a dead char with the -w option */
+ if (!noscore && !(p_ptr->chp < 0))
+ {
+ /* Mention effects */
+ msg_print("Wizard mode is for debugging and experimenting.");
+ msg_print("The game will not be scored if you enter wizard mode.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to enter wizard mode? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0002;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Verify use of "debug" commands
+ */
+static bool_ enter_debug_mode(void)
+{
+ /* Ask first time */
+ if (!noscore && !wizard)
+ {
+ /* Mention effects */
+ msg_print("The debug commands are for debugging and experimenting.");
+ msg_print("The game will not be scored if you use debug commands.");
+ msg_print(NULL);
+
+ /* Verify request */
+ if (!get_check("Are you sure you want to use debug commands? "))
+ {
+ return (FALSE);
+ }
+
+ /* Mark savefile */
+ noscore |= 0x0008;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Parse and execute the current command
+ * Give "Warning" on illegal commands.
+ *
+ * XXX XXX XXX Make some "blocks"
+ */
+static void process_command(void)
+{
+ char error_m[80];
+
+ /* Handle repeating the last command */
+ repeat_check();
+
+ /* Parse the command */
+ switch (command_cmd)
+ {
+ /* Ignore */
+ case ESCAPE:
+ case ' ':
+ case 0:
+ {
+ break;
+ }
+
+ /* Ignore return */
+ case '\r':
+ {
+ break;
+ }
+
+
+
+ /*** 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_FRAME);
+
+ break;
+ }
+
+ /* Special "debug" commands */
+ case KTRL('A'):
+ {
+ /* Enter debug mode */
+ if (enter_debug_mode())
+ {
+ do_cmd_debug();
+ }
+ break;
+ }
+
+
+ /*** Inventory Commands ***/
+
+ /* Wear/wield equipment */
+ case 'w':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_wield();
+ break;
+ }
+
+ /* Take off equipment */
+ case 't':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_takeoff();
+ p_ptr->redraw |= (PR_FRAME);
+ break;
+ }
+
+ /* Drop an item */
+ case 'd':
+ {
+ if (do_control_drop()) break;
+ if (!p_ptr->wild_mode) do_cmd_drop();
+ break;
+ }
+
+ /* Destroy an item */
+ case 'k':
+ {
+ if (p_ptr->control) break;
+ do_cmd_destroy();
+ break;
+ }
+
+ /* Equipment list */
+ case 'e':
+ {
+ if (p_ptr->control) break;
+ do_cmd_equip();
+ break;
+ }
+
+ /* Inventory list */
+ case 'i':
+ {
+ if (do_control_inven()) break;
+ do_cmd_inven();
+ break;
+ }
+
+
+ /*** Various commands ***/
+
+ /* Identify an object */
+ case 'I':
+ {
+ do_cmd_observe();
+ break;
+ }
+
+ /* Hack -- toggle windows */
+ case KTRL('I'):
+ {
+ toggle_inven_equip();
+ break;
+ }
+
+
+ /*** Standard "Movement" Commands ***/
+
+ /* Alter a grid */
+ case '+':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_alter();
+ break;
+ }
+
+ /* Dig a tunnel */
+ case 'T':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_tunnel();
+ break;
+ }
+
+ /* Move (usually pick up things) */
+ case ';':
+ {
+ if (do_control_walk()) break;
+
+ do_cmd_walk(always_pickup, TRUE);
+
+ break;
+ }
+
+ /* Move (usually do not pick up) */
+ case '-':
+ {
+ if (do_control_walk()) break;
+
+ do_cmd_walk(!always_pickup, TRUE);
+
+ break;
+ }
+
+
+ /*** Running, Resting, Searching, Staying */
+
+ /* Begin Running -- Arg is Max Distance */
+ case '.':
+ {
+ if (p_ptr->control || p_ptr->wild_mode) break;
+ do_cmd_run();
+ break;
+ }
+
+ /* Stay still (usually pick things up) */
+ case ',':
+ {
+ if (do_control_pickup()) break;
+ do_cmd_stay(always_pickup);
+ break;
+ }
+
+ /* Stay still (usually do not pick up) */
+ case 'g':
+ {
+ if (p_ptr->control) break;
+ do_cmd_stay(!always_pickup);
+ break;
+ }
+
+ /* Rest -- Arg is time */
+ case 'R':
+ {
+ if (p_ptr->control) break;
+ do_cmd_rest();
+ break;
+ }
+
+ /* Search for traps/doors */
+ case 's':
+ {
+ if (p_ptr->control) break;
+ do_cmd_search();
+ break;
+ }
+
+ /* Toggle search mode */
+ case 'S':
+ {
+ if (p_ptr->control) break;
+ do_cmd_toggle_search();
+ break;
+ }
+
+
+ /*** Stairs and Doors and Chests and Traps ***/
+
+ /* Enter store */
+ case '_':
+ {
+ if (p_ptr->control) break;
+ if (!p_ptr->wild_mode) do_cmd_store();
+ break;
+ }
+
+ /* Go up staircase */
+ case '<':
+ {
+ object_type *o_ptr;
+ u32b f1 = 0 , f2 = 0 , f3 = 0, f4 = 0, f5 = 0, esp = 0;
+
+
+ /* Check for light being wielded */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+ /* Burn some fuel in the current lite */
+ if (o_ptr->tval == TV_LITE)
+ /* Extract the item flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Cannot move if rooted in place */
+ if (p_ptr->tim_roots) break;
+
+ if (p_ptr->control) break;
+ /* Normal cases */
+ if (p_ptr->wild_mode || dun_level || is_quest(dun_level))
+ {
+ do_cmd_go_up();
+ }
+ /* Don't let the player < when he'd just drop right back down */
+ else if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ msg_print("You are too hungry to travel.");
+ }
+ else if (p_ptr->sensible_lite &&
+ (((turn / ((10L * DAY) / 2)) % 2) == 0))
+ {
+ /* Burn vampires! burn! */
+ msg_print("You can't travel during the day!");
+ }
+ else if (p_ptr->sensible_lite &&
+ (o_ptr->tval != 0) &&
+ (o_ptr->sval >= SV_LITE_GALADRIEL) &&
+ (o_ptr->sval <= SV_STONE_LORE) &&
+ (o_ptr->sval != SV_LITE_UNDEATH))
+ {
+ msg_print("Travel with your present light would be unsafe.");
+ }
+ else if (p_ptr->cut || p_ptr->poisoned)
+ {
+ /* I actually died this way once -- neil */
+ msg_print("You are too injured to travel.");
+ }
+ else if (ambush_flag)
+ {
+ msg_print("To flee the ambush you have to reach the edge of the map.");
+ }
+ /* TODO: make the above stuff use this hook */
+ else if (!process_hooks_new(HOOK_FORBID_TRAVEL, NULL, NULL))
+ {
+ 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;
+
+ 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;
+
+ 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;
+
+ j_ptr = &p_ptr->inventory[INVEN_BOW];
+
+ 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;
+
+ do_cmd_throw();
+ break;
+ }
+
+ /* Aim a wand */
+ case 'a':
+ {
+ if (p_ptr->control) break;
+ if (p_ptr->wild_mode) 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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;
+
+ 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 (race_flags1_p(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 ***/
+
+ /* Single line from a pref file */
+ case '"':
+ {
+ do_cmd_pref();
+ break;
+ }
+
+ /* Interact with macros */
+ case '@':
+ {
+ do_cmd_macros();
+ break;
+ }
+
+ /* Interact with visuals */
+ case '%':
+ {
+ do_cmd_visuals();
+ break;
+ }
+
+ /* Interact with colors */
+ case '&':
+ {
+ do_cmd_colors();
+ break;
+ }
+
+ /* Interact with options */
+ case '=':
+ {
+ do_cmd_options();
+ break;
+ }
+
+
+ /*** Misc Commands ***/
+
+ /* Take notes */
+ case ':':
+ {
+ do_cmd_note();
+ break;
+ }
+
+ /* Version info */
+ case 'V':
+ {
+ do_cmd_version();
+ break;
+ }
+
+ /* Repeat level feeling */
+ case KTRL('F'):
+ {
+ if (!p_ptr->wild_mode)
+ do_cmd_feeling();
+ break;
+ }
+
+ /* Show previous message */
+ case KTRL('O'):
+ {
+ do_cmd_message_one();
+ break;
+ }
+
+ /* Show previous messages */
+ case KTRL('P'):
+ {
+ do_cmd_messages();
+ break;
+ }
+
+ /* Show quest status -KMW- */
+ case KTRL('Q'):
+ case CMD_QUEST:
+{
+ do_cmd_checkquest();
+ break;
+ }
+
+ /* Redraw the screen */
+ case KTRL('R'):
+ {
+ do_cmd_redraw();
+ break;
+ }
+
+ /* Hack -- Save and don't quit */
+ case KTRL('S'):
+ {
+ is_autosave = FALSE;
+ do_cmd_save_game();
+ break;
+ }
+
+ case KTRL('T'):
+ {
+ do_cmd_time();
+ }
+ break;
+
+ /* Save and quit */
+ case KTRL('X'):
+ {
+ alive = FALSE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ break;
+ }
+
+ /* Quit (commit suicide) */
+ case 'Q':
+ {
+ do_cmd_suicide();
+ break;
+ }
+
+ /* Extended command */
+ case '#':
+ {
+ do_cmd_cli();
+ break;
+ }
+
+ /* Check artifacts, uniques, objects */
+ case '~':
+ {
+ do_cmd_knowledge();
+ break;
+ }
+
+ /* Commands only available as extended commands: */
+
+ /* Extended command help. */
+ case CMD_CLI_HELP:
+ {
+ do_cmd_cli_help();
+ break;
+ }
+
+ /* Game time. */
+ case CMD_SHOW_TIME:
+ {
+ do_cmd_time();
+ break;
+ }
+
+ /* Check skills. */
+ case CMD_SHOW_SKILL:
+ {
+ do_cmd_skill();
+ break;
+ }
+
+ /* Check abilities. */
+ case CMD_SHOW_ABILITY:
+ {
+ do_cmd_ability();
+ break;
+ }
+
+ /* Save a html screenshot. */
+ case CMD_DUMP_HTML:
+ {
+ do_cmd_html_dump();
+ break;
+ }
+
+ /* Record a macro. */
+ case '$':
+ case CMD_MACRO:
+ {
+ do_cmd_macro_recorder();
+ break;
+ }
+ case CMD_BLUNDER:
+ {
+ if (do_control_walk()) break;
+ do_cmd_walk(always_pickup, FALSE);
+ break;
+ }
+ /* Hack -- Unknown command */
+ default:
+ {
+ int insanity = (p_ptr->msane - p_ptr->csane) * 100 / p_ptr->msane;
+
+ /* Would like to have an option disabling this -- pelpel */
+ if (rand_int(100) < insanity)
+ {
+ get_rnd_line("error.txt", error_m);
+ sound(SOUND_ILLEGAL);
+ msg_print(error_m);
+ }
+ else
+ {
+ prt("Type '?' for help.", 0, 0);
+ }
+
+ break;
+ }
+ }
+}
+
+
+
+
+/*
+ * Process the player
+ *
+ * Notice the annoying code to handle "pack overflow", which
+ * must come first just in case somebody manages to corrupt
+ * the savefiles by clever use of menu commands or something.
+ */
+static void process_player(void)
+{
+ int i, j;
+
+ int speed_use;
+
+
+ /*** Apply energy ***/
+
+ /* 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);
+ }
+ }
+
+ /* Complete resting */
+ else if (resting == -2)
+ {
+ bool_ stop = TRUE;
+ object_type *o_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ /* Stop resting */
+ if ((!p_ptr->drain_life) && (p_ptr->chp != p_ptr->mhp)) stop = FALSE;
+ if ((!p_ptr->drain_mana) && (p_ptr->csp != p_ptr->msp)) stop = FALSE;
+ if (o_ptr->pval2 < o_ptr->pval3) stop = FALSE;
+ if (p_ptr->blind || p_ptr->confused) stop = FALSE;
+ if (p_ptr->poisoned || p_ptr->afraid) stop = FALSE;
+ if (p_ptr->stun || p_ptr->cut) stop = FALSE;
+ if (p_ptr->slow || p_ptr->paralyzed) stop = FALSE;
+ if (p_ptr->image || p_ptr->word_recall) stop = FALSE;
+ if (p_ptr->immov_cntr != 0) stop = FALSE;
+
+ for (i = 0; i < 6; i++)
+ {
+ if (p_ptr->stat_cnt[i] > 0) stop = FALSE;
+ }
+
+ if (stop)
+ {
+ disturb(0);
+ }
+ p_ptr->redraw |= (PR_FRAME);
+ }
+ }
+
+ /* Handle "abort" */
+ if (!avoid_abort)
+ {
+ /* Check for "player abort" (semi-efficiently for resting) */
+ if (running || command_rep || (resting && !(resting & 0x0F)))
+ {
+ /* Check for a key */
+ if (inkey_scan())
+ {
+ /* Flush input */
+ flush();
+
+ /* Disturb */
+ disturb(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);
+
+ /* Warning */
+ msg_print("Your pack overflows!");
+
+ /* Describe */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Drop it (carefully) near the player */
+ drop_near(o_ptr, 0, p_ptr->py, p_ptr->px);
+
+ /* Modify, Describe, Optimize */
+ inc_stack_size(item, -255);
+
+ /* Notice stuff (if needed) */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff (if needed) */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff (if needed) */
+ if (p_ptr->window) window_stuff();
+ }
+
+
+ /* Assume free turn */
+ energy_use = 0;
+
+
+ /* Paralyzed or Knocked Out */
+ if ((p_ptr->paralyzed) || (p_ptr->stun >= 100))
+ {
+ /* Take a turn */
+ energy_use = 100;
+ }
+
+ /* Resting */
+ else if (resting)
+ {
+ /* Timed rest */
+ if (resting > 0)
+ {
+ /* Reduce rest count */
+ resting--;
+
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_FRAME);
+ }
+
+ 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_FRAME);
+
+ /* 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 && shimmer_monsters)
+ {
+ /* Clear the flag */
+ shimmer_monsters = FALSE;
+
+ /* Shimmer multi-hued monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ /* Access monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Access the monster race */
+ auto const r_ptr = m_ptr->race();
+
+ /* 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 && 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 &&
+ !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;
+
+
+ /* Disturb */
+ disturb(1);
+
+ /* 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_FRAME);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_SANITY | PU_BODY);
+
+ /* Calculate torch radius */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Redraw stuff */
+ window_stuff();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_DISTANCE | PU_MON_LITE);
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Leave "xtra" mode */
+ character_xtra = FALSE;
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS | PU_BODY);
+
+ /* Combine / Reorder the pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Update stuff */
+ update_stuff();
+
+ /* Redraw stuff */
+ redraw_stuff();
+
+ /* Window stuff */
+ window_stuff();
+
+ /* Refresh */
+ Term_fresh();
+
+
+ /* Announce (or repeat) the feeling */
+ if (dun_level) do_cmd_feeling();
+
+
+ /* Hack -- notice death or departure */
+ if (!alive || death) return;
+
+ /*** Process this dungeon level ***/
+
+ /* Reset the monster generation level */
+ monster_level = dun_level;
+
+ /* Reset the object generation level */
+ object_level = dun_level;
+
+ hack_mind = TRUE;
+
+ /* Mega Hack, if needed wipe all stairs */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ int i, j;
+
+ for (i = 0; i < cur_wid; i++)
+ {
+ for (j = 0; j < cur_hgt; j++)
+ {
+ cave_type *c_ptr = &cave[j][i];
+
+ switch (c_ptr->feat)
+ {
+ case FEAT_MORE:
+ case FEAT_LESS:
+ case FEAT_SHAFT_UP:
+ case FEAT_SHAFT_DOWN:
+ {
+ cave_set_feat(j, i, FEAT_FLOOR);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Reset the monster generation level */
+ monster_level = 127;
+
+ /* Reset the object generation level */
+ object_level = 0;
+ }
+
+ /* Main loop */
+ while (TRUE)
+ {
+ /* Hack -- Compact the monster list occasionally */
+ if (m_cnt + 32 > max_m_idx) compact_monsters(64);
+
+ /* Hack -- Compress the monster list occasionally */
+ if (m_cnt + 32 < m_max) compact_monsters(0);
+
+
+ /* Hack -- Compact the object list occasionally */
+ if (o_cnt + 32 > max_o_idx) compact_objects(64);
+
+ /* Hack -- Compress the object list occasionally */
+ if (o_cnt + 32 < o_max) compact_objects(0);
+
+
+
+ /* Process the player */
+ process_player();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+
+ total_friends = 0;
+ total_friend_levels = 0;
+
+ /* Process all of the monsters */
+ process_monsters();
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Hack -- Hilite the player */
+ move_cursor_relative(p_ptr->py, p_ptr->px);
+
+ /* Optional fresh */
+ if (fresh_after) Term_fresh();
+
+ /* Hack -- Notice death or departure */
+ if (!alive || death) break;
+
+
+ /* Process the world */
+ process_world();
+
+ /* Process the appropriate hooks */
+ process_hooks_new(HOOK_END_TURN, NULL, NULL);
+
+ /* 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);
+
+ /* Process that file */
+ process_pref_file(buf);
+
+ /* Access the "class" pref file */
+ sprintf(buf, "%s.prf", spp_ptr->title);
+
+ /* 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);
+
+ /* Load automatizer settings. Character-specific automatizer
+ * file gets priority over the "template" file. We do not try
+ * to merge the two files since that would require tracking
+ * the providence of rules and such to avoid the same
+ * duplication problems as caused when saving macros/keymaps. */
+ boost::filesystem::path userDirectory(ANGBAND_DIR_USER);
+ if (automatizer_load(userDirectory / (std::string(player_name) + ".atm")))
+ {
+ // Done
+ }
+ else if (automatizer_load(userDirectory / "automat.atm"))
+ {
+ // Done
+ }
+}
+
+/*
+ * 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;
+
+ /* Initialize player */
+ p_ptr = new player_type();
+
+ /* Hack -- Character is "icky" */
+ character_icky = TRUE;
+
+
+ /* Make sure main term is active */
+ Term_activate(angband_term[0]);
+
+ /* Initialise the resize hooks for all the terminals */
+ angband_term[0]->resize_hook = resize_map;
+ for (i = 1; i < ANGBAND_TERM_MAX; 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);
+ }
+ }
+
+ /* Process old character */
+ if (!new_game)
+ {
+ /* Process the player name */
+ process_player_name(FALSE);
+ }
+
+ /* 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)
+ {
+ /* Show intro */
+ modules[game_module_idx].intro();
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+
+ /* Hack -- seed for flavors */
+ seed_flavor = rand_int(0x10000000);
+
+ /* Roll up a new character */
+ player_birth();
+
+ /* Start in town, or not */
+ if (p_ptr->astral) dun_level = 98;
+ else dun_level = 0;
+ p_ptr->inside_quest = 0;
+
+ /* Hack -- enter the world */
+ /* Mega-hack Vampires and Spectres start at midnight */
+ if (race_flags1_p(PR1_UNDEAD))
+ {
+ turn = (10L * DAY / 2) + 1;
+ }
+ else
+ {
+ turn = 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_quests();
+ init_hooks_help();
+ init_hooks_module();
+
+ /* 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)
+ {
+ 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_new(HOOK_GAME_START, NULL, NULL);
+
+ /* 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;
+
+ /* Process */
+ while (TRUE)
+ {
+ /* Save the level */
+ old_dun_level = dun_level;
+ p_ptr->old_wild_mode = p_ptr->wild_mode;
+
+ /* We reached surface ? good, lets go down again !! */
+ if (p_ptr->astral && !dun_level)
+ {
+ p_ptr->astral = FALSE;
+ cmsg_print(TERM_L_GREEN,
+ "Well done ! You reached the town ! "
+ "You can now go down again.");
+ }
+
+ /* Update monster list window */
+ p_ptr->window |= (PW_M_LIST);
+
+ /* Process the level */
+ dungeon();
+
+ /* Save the current level if in a persistent level */
+ tmp_dun = dun_level;
+ dun_level = old_dun_level;
+ save_dungeon();
+ dun_level = tmp_dun;
+
+ /* A death fate affects level generation */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ /* Ignore empty slots */
+ if (!fates[i].fate) continue;
+
+ /* Ignore non-applicable fates */
+ if (fates[i].level != dun_level) continue;
+
+ /* Non-serious fate fails to fire 50% of time */
+ if (!fates[i].serious && (rand_int(2) == 0)) continue;
+
+ /* Analyse fate */
+ switch (fates[i].fate)
+ {
+ /* You are doomed */
+ case FATE_DIE:
+ {
+ cmsg_print(TERM_L_DARK, "You were fated to die here. DIE!");
+
+ /* You shall perish there */
+ dungeon_type = DUNGEON_DEATH;
+ dun_level = d_info[dungeon_type].mindepth; /* was 1 */
+
+ fates[i].fate = FATE_NONE;
+ break;
+ }
+ }
+ }
+
+ /* Notice stuff */
+ if (p_ptr->notice) notice_stuff();
+
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+
+ /* Cancel the target */
+ target_who = 0;
+
+ /* Cancel the health bar */
+ health_track(0);
+
+
+ /* Forget the lite */
+ forget_mon_lite();
+
+ /* Forget the view */
+ forget_view();
+
+ /* Handle "quit and save" */
+ if (!alive && !death) break;
+
+
+ /* Erase the old cave */
+ wipe_o_list();
+
+
+ /* XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Accidental Death */
+ if (alive && death)
+ {
+ cheat_death = FALSE;
+
+ /* Can we die ? please let us die ! */
+ if (process_hooks_new(HOOK_DIE, NULL, NULL))
+ {
+ 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; */
+ 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_new(HOOK_NEW_LEVEL, NULL, NULL);
+ generate_cave();
+ }
+
+ /* Close stuff */
+ close_game();
+
+ /* Quit */
+ quit(NULL);
+}
+
diff --git a/src/dungeon.h b/src/dungeon.h
new file mode 100644
index 00000000..1ce166d1
--- /dev/null
+++ b/src/dungeon.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "h-basic.h"
+
+// C linkage required for these functions since main-* code uses them.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void play_game(bool_ new_game);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/dungeon.hpp b/src/dungeon.hpp
new file mode 100644
index 00000000..bbe6da87
--- /dev/null
+++ b/src/dungeon.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <vector>
+
+extern void sense_inventory();
+extern void sense_objects(std::vector<int> const &object_idxs);
diff --git a/src/dungeon_info_type.hpp b/src/dungeon_info_type.hpp
new file mode 100644
index 00000000..5f6fc9d0
--- /dev/null
+++ b/src/dungeon_info_type.hpp
@@ -0,0 +1,72 @@
+#pragma once
+
+#include "h-basic.h"
+#include "rule_type.hpp"
+#include "obj_theme.hpp"
+
+/**
+ * Maximum number of towns per dungeon
+ */
+constexpr int TOWN_DUNGEON = 4;
+
+/* A structure for the != dungeon types */
+struct dungeon_info_type
+{
+ const char *name; /* Name */
+ char *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 */
+};
+
diff --git a/src/dungeon_info_type_fwd.hpp b/src/dungeon_info_type_fwd.hpp
new file mode 100644
index 00000000..26ed0289
--- /dev/null
+++ b/src/dungeon_info_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct dungeon_info_type;
diff --git a/src/effect_type.hpp b/src/effect_type.hpp
new file mode 100644
index 00000000..8cd638c6
--- /dev/null
+++ b/src/effect_type.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Lasting spell effects. (Clouds, etc.)
+ */
+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 */
+};
diff --git a/src/ego_item_type.hpp b/src/ego_item_type.hpp
new file mode 100644
index 00000000..f9b6970a
--- /dev/null
+++ b/src/ego_item_type.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "h-basic.h"
+
+/*
+ * Size of flag rarity tables
+ */
+constexpr int FLAG_RARITY_MAX = 6;
+
+/**
+ * Ego item descriptors.
+ */
+struct ego_item_type
+{
+ const char *name; /* Name (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[FLAG_RARITY_MAX];
+ u32b flags1[FLAG_RARITY_MAX]; /* Ego-Item Flags, set 1 */
+ u32b flags2[FLAG_RARITY_MAX]; /* Ego-Item Flags, set 2 */
+ u32b flags3[FLAG_RARITY_MAX]; /* Ego-Item Flags, set 3 */
+ u32b flags4[FLAG_RARITY_MAX]; /* Ego-Item Flags, set 4 */
+ u32b flags5[FLAG_RARITY_MAX]; /* Ego-Item Flags, set 5 */
+ u32b esp[FLAG_RARITY_MAX]; /* ESP flags */
+ u32b oflags1[FLAG_RARITY_MAX]; /* Ego-Item Obvious Flags, set 1 */
+ u32b oflags2[FLAG_RARITY_MAX]; /* Ego-Item Obvious Flags, set 2 */
+ u32b oflags3[FLAG_RARITY_MAX]; /* Ego-Item Obvious Flags, set 3 */
+ u32b oflags4[FLAG_RARITY_MAX]; /* Ego-Item Obvious Flags, set 4 */
+ u32b oflags5[FLAG_RARITY_MAX]; /* Ego-Item Obvious Flags, set 5 */
+ u32b oesp[FLAG_RARITY_MAX]; /* Obvious ESP flags */
+ u32b fego[FLAG_RARITY_MAX]; /* 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) */
+};
diff --git a/src/ego_item_type_fwd.hpp b/src/ego_item_type_fwd.hpp
new file mode 100644
index 00000000..795f4403
--- /dev/null
+++ b/src/ego_item_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct ego_item_type;
diff --git a/src/fate.hpp b/src/fate.hpp
new file mode 100644
index 00000000..906bc99d
--- /dev/null
+++ b/src/fate.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Fate descritpor.
+ */
+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 */
+};
diff --git a/src/feature_type.hpp b/src/feature_type.hpp
new file mode 100644
index 00000000..1a79aeb3
--- /dev/null
+++ b/src/feature_type.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Terrain feature descriptor.
+ */
+struct feature_type
+{
+ char *name; /* Name */
+
+ const char *text; /* Text. May point to shared read-only memory, DO NOT FREE! */
+ const char *tunnel; /* Text for tunneling. May point to shared read-only memory, DO NOT FREE! */
+ const char *block; /* Text for blocking. May point to shared read-only memory, DO NOT FREE! */
+
+ 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 */
+};
diff --git a/src/feature_type_fwd.hpp b/src/feature_type_fwd.hpp
new file mode 100644
index 00000000..168ec6c7
--- /dev/null
+++ b/src/feature_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct feature_type;
diff --git a/src/files.cc b/src/files.cc
new file mode 100644
index 00000000..cc168ba5
--- /dev/null
+++ b/src/files.cc
@@ -0,0 +1,5788 @@
+/*
+ * 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 "files.hpp"
+#include "files.h"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "corrupt.hpp"
+#include "cmd3.hpp"
+#include "dungeon_info_type.hpp"
+#include "feature_type.hpp"
+#include "hiscore.hpp"
+#include "hook_chardump_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "levels.hpp"
+#include "loadsave.h"
+#include "loadsave.hpp"
+#include "mimic.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_ego.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "notes.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_class.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_spec.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "spells2.hpp"
+#include "store_info_type.hpp"
+#include "store_type.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+#include "xtra1.hpp"
+#include "z-rand.hpp"
+
+#include <memory>
+#include <unordered_set>
+
+/*
+ * 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]);
+
+ free(keymap_act[mode][i]);
+
+ keymap_act[mode][i] = strdup(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)
+ {
+ delete[] macro_template;
+ macro_template = NULL;
+
+ for (i = 0; i < max_macrotrigger; i++)
+ {
+ delete[] macro_trigger_name[i];
+ macro_trigger_name[i] = nullptr;
+ }
+ 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 = new char[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] = new char[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;
+ }
+
+ /* Race */
+ else if (streq(b + 1, "RACE"))
+ {
+ v = rp_ptr->title;
+ }
+
+ /* Race */
+ else if (streq(b + 1, "RACEMOD"))
+ {
+ v = rmp_ptr->title;
+ }
+
+ /* Class */
+ else if (streq(b + 1, "CLASS"))
+ {
+ v = spp_ptr->title;
+ }
+
+ /* Player */
+ else if (streq(b + 1, "PLAYER"))
+ {
+ v = player_base;
+ }
+ }
+
+ /* Constant */
+ else
+ {
+ v = b;
+ }
+ }
+
+ /* Save */
+ (*fp) = f;
+
+ /* Save */
+ (*sp) = s;
+
+ /* Result */
+ return (v);
+}
+
+
+
+
+/*
+ * Process the "user pref file" with the given name
+ *
+ * See the function above for a list of legal "commands".
+ *
+ * We also accept the special "?" and "%" directives, which
+ * allow conditional evaluation and filename inclusion.
+ */
+errr process_pref_file(cptr name)
+{
+ FILE *fp;
+
+ char buf[1024];
+
+ int num = -1;
+
+ errr err = 0;
+
+ bool_ bypass = FALSE;
+
+ /* Build the filename -- Allow users to override system pref files */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* No such file -- Try system pref file */
+ if (!fp)
+ {
+ /* Build the pathname, this time using the system pref directory */
+ path_build(buf, 1024, ANGBAND_DIR_PREF, name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Failed again */
+ if (!fp) return ( -1);
+ }
+
+
+ /* Process the file */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Count lines */
+ num++;
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) continue;
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) continue;
+
+ /* Skip comments */
+ if (buf[0] == '#') continue;
+
+
+ /* Process "?:<expr>" */
+ if ((buf[0] == '?') && (buf[1] == ':'))
+ {
+ char f;
+ cptr v;
+ char *s;
+
+ /* Start */
+ s = buf + 2;
+
+ /* Parse the expr */
+ v = process_pref_file_expr(&s, &f);
+
+ /* Set flag */
+ bypass = (streq(v, "0") ? TRUE : FALSE);
+
+ /* Continue */
+ continue;
+ }
+
+ /* Apply conditionals */
+ if (bypass) continue;
+
+
+ /* Process "%:<file>" */
+ if (buf[0] == '%')
+ {
+ /* Process that file if allowed */
+ (void)process_pref_file(buf + 2);
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Process the line */
+ err = process_pref_file_aux(buf);
+
+ /* Oops */
+ if (err) break;
+ }
+
+
+ /* Error */
+ if (err)
+ {
+ /* Useful error message */
+ msg_format("Error %d in line %d of file '%s'.", err, num, name);
+ msg_format("Parsing '%s'", buf);
+ }
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Result */
+ return (err);
+}
+
+
+
+
+/*
+ * Print long number with header at given row, column
+ * Use the color for the number, not the header
+ */
+static void prt_lnum(cptr header, s32b num, int row, int col, byte color)
+{
+ int len = strlen(header);
+ char out_val[32];
+
+ put_str(header, row, col);
+ (void)sprintf(out_val, "%9ld", (long)num);
+ c_put_str(color, out_val, row, col + len);
+}
+
+
+/*
+ * Print number with header at given row, column
+ */
+static void prt_num(cptr header, int num, int row, int col, byte color,
+ cptr 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 */
+ if (p_ptr->pgod == GOD_ERU)
+ {
+ if ((p_ptr->grace >= 100) || (p_ptr->grace <= -100)) (*f1) |= TR1_MANA;
+ if (p_ptr->grace > 10000) (*f1) |= TR1_WIS;
+ }
+
+ if (p_ptr->pgod == 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);
+ if (p_ptr->praying)
+ {
+ if (p_ptr->grace > 5000) (*f2) |= TR2_INVIS;
+ if (p_ptr->grace > 15000) (*f2) |= TR2_IM_FIRE;
+ }
+ }
+
+ if (p_ptr->pgod == GOD_MANWE)
+ {
+ if (p_ptr->grace >= 2000) (*f3) |= TR3_FEATHER;
+ if (p_ptr->praying)
+ {
+ 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;
+ }
+ }
+
+ if (p_ptr->pgod == GOD_TULKAS)
+ {
+ if (p_ptr->grace > 5000) (*f1) |= TR1_CON;
+ if (p_ptr->grace > 10000) (*f1) |= TR1_STR;
+ }
+
+ if (p_ptr->pgod == GOD_AULE)
+ {
+ if (p_ptr->grace > 5000)
+ {
+ (*f2) |= TR2_RES_FIRE;
+ }
+ }
+
+ if (p_ptr->pgod == GOD_MANDOS)
+ {
+ (*f2) |= TR2_RES_NETHER;
+
+ if ((p_ptr->grace > 10000) &&
+ (p_ptr->praying == TRUE))
+ {
+ (*f3) |= TR3_NO_TELE;
+ }
+
+ if ((p_ptr->grace > 20000) &&
+ (p_ptr->praying == TRUE))
+ {
+ (*f4) |= TR4_IM_NETHER;
+ }
+ }
+
+ if (p_ptr->pgod == GOD_ULMO)
+ {
+ (*f5) |= TR5_WATER_BREATH;
+
+ if ((p_ptr->grace > 1000) &&
+ (p_ptr->praying == TRUE))
+ {
+ (*f2) |= TR2_RES_POIS;
+ }
+
+ if ((p_ptr->grace > 15000) &&
+ (p_ptr->praying == TRUE))
+ {
+ (*f5) |= TR5_MAGIC_BREATH;
+ }
+ }
+
+ /* Classes */
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ (*f1) |= cp_ptr->oflags1[i];
+ (*f2) |= cp_ptr->oflags2[i];
+ (*f3) |= cp_ptr->oflags3[i];
+ (*f4) |= cp_ptr->oflags4[i];
+ (*f5) |= cp_ptr->oflags5[i];
+ (*esp) |= cp_ptr->oesp[i];
+ }
+
+ /* Races */
+ if ((!p_ptr->mimic_form) && (!p_ptr->body_monster))
+ {
+ for (i = 1; i <= p_ptr->lev; i++)
+ {
+ (*f1) |= rp_ptr->oflags1[i];
+ (*f2) |= rp_ptr->oflags2[i];
+ (*f3) |= rp_ptr->oflags3[i];
+ (*f4) |= rp_ptr->oflags4[i];
+ (*f5) |= rp_ptr->oflags5[i];
+ (*esp) |= rp_ptr->oesp[i];
+
+ (*f1) |= rmp_ptr->oflags1[i];
+ (*f2) |= rmp_ptr->oflags2[i];
+ (*f3) |= rmp_ptr->oflags3[i];
+ (*f4) |= rmp_ptr->oflags4[i];
+ (*f5) |= rmp_ptr->oflags5[i];
+ (*esp) |= rmp_ptr->oesp[i];
+ }
+ }
+ else
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ if (r_ptr->flags2 & RF2_REFLECTING) (*f2) |= TR2_REFLECT;
+ if (r_ptr->flags2 & RF2_REGENERATE) (*f3) |= TR3_REGEN;
+ if (r_ptr->flags2 & RF2_AURA_FIRE) (*f3) |= TR3_SH_FIRE;
+ if (r_ptr->flags2 & RF2_AURA_ELEC) (*f3) |= TR3_SH_ELEC;
+ if (r_ptr->flags2 & RF2_PASS_WALL) (*f3) |= TR3_WRAITH;
+ if (r_ptr->flags3 & RF3_SUSCEP_FIRE) (*f2) |= TR2_SENS_FIRE;
+ if (r_ptr->flags3 & RF3_IM_ACID) (*f2) |= TR2_RES_ACID;
+ if (r_ptr->flags3 & RF3_IM_ELEC) (*f2) |= TR2_RES_ELEC;
+ if (r_ptr->flags3 & RF3_IM_FIRE) (*f2) |= TR2_RES_FIRE;
+ if (r_ptr->flags3 & RF3_IM_POIS) (*f2) |= TR2_RES_POIS;
+ if (r_ptr->flags3 & RF3_IM_COLD) (*f2) |= TR2_RES_COLD;
+ if (r_ptr->flags3 & RF3_RES_NETH) (*f2) |= TR2_RES_NETHER;
+ if (r_ptr->flags3 & RF3_RES_NEXU) (*f2) |= TR2_RES_NEXUS;
+ if (r_ptr->flags3 & RF3_RES_DISE) (*f2) |= TR2_RES_DISEN;
+ if (r_ptr->flags3 & RF3_NO_FEAR) (*f2) |= TR2_RES_FEAR;
+ if (r_ptr->flags3 & RF3_NO_SLEEP) (*f2) |= TR2_FREE_ACT;
+ if (r_ptr->flags3 & RF3_NO_CONF) (*f2) |= TR2_RES_CONF;
+ if (r_ptr->flags7 & RF7_CAN_FLY) (*f3) |= TR3_FEATHER;
+ }
+
+ (*f1) |= p_ptr->xtra_f1;
+ (*f2) |= p_ptr->xtra_f2;
+ (*f3) |= p_ptr->xtra_f3;
+ (*f4) |= p_ptr->xtra_f4;
+ (*f5) |= p_ptr->xtra_f5;
+ (*esp) |= p_ptr->xtra_esp;
+
+ if (p_ptr->black_breath)
+ {
+ (*f4) |= TR4_BLACK_BREATH;
+ }
+
+ if (p_ptr->hp_mod != 0)
+ {
+ (*f2) |= TR2_LIFE;
+ }
+}
+
+/*
+ * Object flag names
+ */
+static cptr object_flag_names[192] =
+{
+ "Add Str",
+ "Add Int",
+ "Add Wis",
+ "Add Dex",
+ "Add Con",
+ "Add Chr",
+ "Mul Mana",
+ "Mul SPower",
+ "Add Stea.",
+ "Add Sear.",
+ "Add Infra",
+ "Add Tun..",
+ "Add Speed",
+ "Add Blows",
+ "Chaotic",
+ "Vampiric",
+ "Slay Anim.",
+ "Slay Evil",
+ "Slay Und.",
+ "Slay Demon",
+ "Slay Orc",
+ "Slay Troll",
+ "Slay Giant",
+ "Slay Drag.",
+ "Kill Drag.",
+ "Sharpness",
+ "Impact",
+ "Poison Brd",
+ "Acid Brand",
+ "Elec Brand",
+ "Fire Brand",
+ "Cold Brand",
+
+ "Sust Str",
+ "Sust Int",
+ "Sust Wis",
+ "Sust Dex",
+ "Sust Con",
+ "Sust Chr",
+ "Invisible",
+ "Mul life",
+ "Imm Acid",
+ "Imm Elec",
+ "Imm Fire",
+ "Imm Cold",
+ "Sens Fire",
+ "Reflect",
+ "Free Act",
+ "Hold Life",
+ "Res Acid",
+ "Res Elec",
+ "Res Fire",
+ "Res Cold",
+ "Res Pois",
+ "Res Fear",
+ "Res Light",
+ "Res Dark",
+ "Res Blind",
+ "Res Conf",
+ "Res Sound",
+ "Res Shard",
+ "Res Neth",
+ "Res Nexus",
+ "Res Chaos",
+ "Res Disen",
+
+
+
+ "Aura Fire",
+ "Aura Elec",
+ "Auto Curse",
+ NULL,
+ "NoTeleport",
+ "AntiMagic",
+ "WraithForm",
+ "EvilCurse",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Levitate",
+ "Lite",
+ "See Invis",
+ NULL,
+ "Digestion",
+ "Regen",
+ "Xtra Might",
+ "Xtra Shots",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Activate",
+ "Drain Exp",
+ "Teleport",
+ "Aggravate",
+ "Blessed",
+ "Cursed",
+ "Hvy Curse",
+ "Prm Curse",
+
+ "No blows",
+ "Precogn.",
+ "B.Breath",
+ "Recharge",
+ "Fly",
+ "Mrg.Curse",
+ NULL,
+ NULL,
+ "Sentient",
+ "Clone",
+ NULL,
+ "Climb",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Imm Neth",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+
+ "Orc.ESP",
+ "Troll.ESP",
+ "Dragon.ESP",
+ "Giant.ESP",
+ "Demon.ESP",
+ "Undead.ESP",
+ "Evil.ESP",
+ "Animal.ESP",
+ "TLord.ESP",
+ "Good.ESP",
+ "Nlive.ESP",
+ "Unique.ESP",
+ "Spider ESP",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Full ESP",
+};
+
+/*
+ * Summarize resistances
+ */
+static void display_player_ben_one(int mode)
+{
+ int i, n, x, y, z, dispx, modetemp, xtemp;
+
+ object_type *o_ptr;
+
+ char dummy[80], c;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ u16b b[INVEN_TOTAL - INVEN_WIELD + 1][10];
+
+ int d[INVEN_TOTAL - INVEN_WIELD + 1];
+
+ bool_ got;
+
+ byte a;
+
+ cptr name;
+
+ /* Scan equipment */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ /* Index */
+ n = (i - INVEN_WIELD);
+
+ /* Object */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Known object flags */
+ object_flags_known(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Incorporate */
+ b[n][0] = (u16b)(f1 & 0xFFFF);
+ b[n][1] = (u16b)(f1 >> 16);
+ b[n][2] = (u16b)(f2 & 0xFFFF);
+ b[n][3] = (u16b)(f2 >> 16);
+ b[n][4] = (u16b)(f3 & 0xFFFF);
+ b[n][5] = (u16b)(f3 >> 16);
+ b[n][6] = (u16b)(f4 & 0xFFFF);
+ b[n][7] = (u16b)(f4 >> 16);
+ b[n][8] = (u16b)(esp & 0xFFFF);
+ b[n][9] = (u16b)(esp >> 16);
+ d[n] = o_ptr->pval;
+ }
+
+ /* Carried symbiote */
+ n = INVEN_CARRY - INVEN_WIELD;
+
+ /* Player flags */
+ wield_monster_flags(&f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Incorporate */
+ b[n][0] = (u16b)(f1 & 0xFFFF);
+ b[n][1] = (u16b)(f1 >> 16);
+ b[n][2] = (u16b)(f2 & 0xFFFF);
+ b[n][3] = (u16b)(f2 >> 16);
+ b[n][4] = (u16b)(f3 & 0xFFFF);
+ b[n][5] = (u16b)(f3 >> 16);
+ b[n][6] = (u16b)(f4 & 0xFFFF);
+ b[n][7] = (u16b)(f4 >> 16);
+ b[n][8] = (u16b)(esp & 0xFFFF);
+ b[n][9] = (u16b)(esp >> 16);
+
+ /* Index */
+ n = INVEN_TOTAL - INVEN_WIELD;
+
+ /* Player flags */
+ player_flags(&f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Incorporate */
+ b[n][0] = (u16b)(f1 & 0xFFFF);
+ b[n][1] = (u16b)(f1 >> 16);
+ b[n][2] = (u16b)(f2 & 0xFFFF);
+ b[n][3] = (u16b)(f2 >> 16);
+ b[n][4] = (u16b)(f3 & 0xFFFF);
+ b[n][5] = (u16b)(f3 >> 16);
+ b[n][6] = (u16b)(f4 & 0xFFFF);
+ b[n][7] = (u16b)(f4 >> 16);
+ b[n][8] = (u16b)(esp & 0xFFFF);
+ b[n][9] = (u16b)(esp >> 16);
+
+ /* Generate the equip chars */
+ sprintf(dummy, " ");
+ for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++)
+ {
+ /* If you have that body part then show it */
+ if (p_ptr->body_parts[i])
+ {
+ strcat(dummy, format("%c", i + 'a'));
+ }
+ }
+ strcat(dummy, "@");
+
+ /* Scan cols */
+ for (x = 1; x > -1; x--)
+ {
+ /* Label */
+ Term_putstr(x * 40 + 11, 3, -1, TERM_WHITE, dummy);
+
+ /* Scan rows */
+ for (y = 0; y < 16; y++)
+ {
+ if (mode == 3 && x == 1)
+ {
+ modetemp = 4;
+ xtemp = 0;
+ }
+ else
+ {
+ modetemp = mode;
+ xtemp = x;
+ }
+
+ for (z = mode; z <= modetemp; z++)
+ {
+ if (mode == 3 && x == 1 && z == modetemp) xtemp = 1;
+ name = object_flag_names[32 * modetemp + 16 * xtemp + y];
+ got = FALSE;
+
+ /* No name */
+ if (!name) continue;
+
+ /* Dump colon */
+ if (!(modetemp == 1 && x == 0 && y > 7 && y < 12))
+ {
+ Term_putch(x * 40 + 10, y + 4, TERM_WHITE, ':');
+ }
+
+ /* Check flags */
+ dispx = 0;
+ for (n = 0; n < INVEN_TOTAL - INVEN_WIELD + 1; n++)
+ {
+ /* Change colour every two columns */
+ bool_ is_green = (dispx & 0x02);
+ a = (is_green ? TERM_GREEN : TERM_SLATE);
+ c = '.';
+
+ /* If the body part doesn't exists then skip it :) */
+ if ((n < INVEN_TOTAL - INVEN_WIELD) && (!p_ptr->body_parts[n])) continue;
+
+ /* Increment the drawing coordinates */
+ dispx++;
+
+ /* Check flag */
+ if (b[n][2 * modetemp + xtemp] & (1 << y))
+ {
+ a = (is_green ? TERM_L_GREEN : TERM_WHITE);
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ c = '*';
+ }
+ else if (modetemp == 0 && x == 0 && y < 14 && (y < 6 || y > 7))
+ {
+ if (n == INVEN_TOTAL - INVEN_WIELD)
+ {
+ c = '+';
+ }
+ else
+ {
+ c = d[n];
+ if (c < 0)
+ {
+ c = -c;
+ a = TERM_RED;
+ }
+ c = (c > 9 ? '*' : I2D(c));
+ }
+ }
+ else
+ {
+ c = '+';
+ }
+ got = TRUE;
+ }
+
+ /* HACK - Check for nether immunity and
+ apply to Res Neth line */
+ if (modetemp == 1 && x == 1 && y == 12)
+ {
+ if (b[n][7] & (1 << 6))
+ {
+ a = (is_green ? TERM_L_GREEN : TERM_WHITE);
+ c = '*';
+ got = TRUE;
+ }
+ }
+
+ /* Dump flag */
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ if (c == '*') Term_putch(40 + 11 + dispx, y - 4, a, c);
+ }
+ else
+ {
+ Term_putch(x * 40 + 11 + dispx, y + 4, a, c);
+ }
+ }
+
+ a = TERM_WHITE;
+ if (got)
+ {
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ a = TERM_L_GREEN;
+ }
+ else if (modetemp != 0)
+ {
+ a = TERM_GREEN;
+ }
+ }
+
+ /* HACK - Check for nether immunity and change "Res Neth" */
+ if (modetemp == 1 && x == 1 && y == 12 && p_ptr->immune_neth)
+ {
+ name = "Imm Neth";
+ a = TERM_L_GREEN;
+ }
+
+ /* Dump name */
+ if (modetemp == 1 && x == 0 && y > 7 && y < 12)
+ {
+ if (got) Term_putstr(40, y - 4, -1, a, name);
+ }
+ else
+ {
+ Term_putstr(x * 40, y + 4, -1, a, name);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Display the character on the screen (various modes)
+ *
+ * The top two and bottom two lines are left blank.
+ *
+ * Mode 0 = standard display with skills
+ * Mode 1 = standard display with history
+ * Mode 2 = current flags (part 1)
+ * Mode 3 = current flags (part 2)
+ * Mode 4 = current flags (part 3)
+ * Mode 5 = current flags (part 4)
+ * Mode 6 = current flags (part 5 -- esp)
+ */
+void display_player(int mode)
+{
+ int i;
+
+ char buf[80];
+
+
+ /* Erase screen */
+ clear_from(0);
+
+ /* Standard */
+ if ((mode == 0) || (mode == 1))
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ /* Name, Sex, Race, Class */
+ put_str("Name :", 2, 1);
+ put_str("Sex :", 3, 1);
+ put_str("Race :", 4, 1);
+ put_str("Class :", 5, 1);
+ put_str("Body :", 6, 1);
+ put_str("God :", 7, 1);
+ c_put_str(TERM_L_BLUE, player_name, 2, 9);
+ if (p_ptr->body_monster != 0)
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ char tmp[12];
+
+ if ((r_ptr->flags1 & RF1_MALE) != 0)
+ strcpy(tmp, "Male");
+ else if ((r_ptr->flags1 & RF1_FEMALE) != 0)
+ strcpy(tmp, "Female");
+ else
+ strcpy(tmp, "Neuter");
+ c_put_str(TERM_L_BLUE, tmp, 3, 9);
+ }
+ else
+ c_put_str(TERM_L_BLUE, sp_ptr->title, 3, 9);
+ sprintf(buf, "%s", get_player_race_name(p_ptr->prace, p_ptr->pracem));
+ c_put_str(TERM_L_BLUE, buf, 4, 9);
+ c_put_str(TERM_L_BLUE, spp_ptr->title, 5, 9);
+ c_put_str(TERM_L_BLUE, 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), 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);
+ }
+ else if (wf_info[feat].terrain_idx == TERRAIN_TOWN)
+ {
+ sprintf(desc, "in the town of %s", wf_info[feat].name);
+ }
+ else if (wf_info[feat].entrance)
+ {
+ sprintf(desc, "near %s", wf_info[feat].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);
+ }
+ else if (pwx == lwx && pwy == lwy)
+ {
+ /* Paranoia; this should have been caught above */
+ sprintf(desc, "near %s", wf_info[feat].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,
+ ns,
+ ew,
+ wf_info[landmark].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_info[store].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.
+ */
+static bool_ file_character_check_stores(std::unordered_set<store_type *> *seen_stores, wilderness_type_info *place, int store)
+{
+ town_type *town = &town_info[place->entrance];
+ store_type *st_ptr = &town->store[store];
+
+ // Already seen store?
+ if (seen_stores->find(st_ptr) != seen_stores->end())
+ {
+ return FALSE;
+ }
+
+ // Add
+ seen_stores->insert(st_ptr);
+ 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];
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ /* Check for existing file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Existing file */
+ if (fd >= 0)
+ {
+ char out_val[160];
+
+ /* Close the file */
+ (void)fd_close(fd);
+
+ /* Build query */
+ (void)sprintf(out_val, "Replace existing file %s? ", buf);
+
+ /* Ask */
+ if (get_check(out_val)) fd = -1;
+ }
+
+ /* Open the non-existing file */
+ if (fd < 0) fff = my_fopen(buf, "w");
+
+ /* Invalid file */
+ if (!fff)
+ {
+ /* Message */
+ msg_format("Character sheet creation failed!");
+ msg_print(NULL);
+
+ /* Error */
+ return ( -1);
+ }
+
+
+ /* Begin dump */
+ fprintf(fff, " [%s Character Sheet]\n\n", get_version_string());
+
+
+ /* Display player */
+ display_player(0);
+
+ /* Dump part of the screen */
+ for (y = 2; y < 22; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < 79; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = c;
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* Display history */
+ display_player(1);
+
+ /* Dump part of the screen */
+ for (y = 15; y < 20; y++)
+ {
+ /* Dump each row */
+ for (x = 0; x < 79; x++)
+ {
+ /* Get the attr/char */
+ (void)(Term_what(x, y, &a, &c));
+
+ /* Dump it */
+ buf[x] = c;
+ }
+
+ /* Terminate */
+ buf[x] = '\0';
+
+ /* End the row */
+ fprintf(fff, "%s\n", buf);
+ }
+
+ /* List the patches */
+ 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->preserve)
+ fprintf(fff, "\n Preserve Mode: ON");
+ else
+ fprintf(fff, "\n Preserve Mode: OFF");
+
+ if (auto_scum)
+ fprintf(fff, "\n Autoscum: ON");
+ else
+ fprintf(fff, "\n Autoscum: OFF");
+
+ if (always_small_level)
+ fprintf(fff, "\n Small Levels: ALWAYS");
+ else if (small_levels)
+ fprintf(fff, "\n Small Levels: ON");
+ else
+ fprintf(fff, "\n Small Levels: OFF");
+
+ if (empty_levels)
+ fprintf(fff, "\n Arena Levels: ON");
+ else
+ fprintf(fff, "\n Arena Levels: OFF");
+
+ if (ironman_rooms)
+ fprintf(fff, "\n Always unusual rooms: ON");
+ else
+ fprintf(fff, "\n Always unusual rooms: OFF");
+
+ fprintf(fff, "\n\n Recall Depth:");
+ for (y = 1; y < max_d_idx; y++)
+ {
+ if (max_dlv[y])
+ fprintf(fff, "\n %s: Level %d (%d')",
+ d_info[y].name,
+ max_dlv[y], 50 * (max_dlv[y]));
+ }
+ fprintf(fff, "\n");
+
+ if (noscore)
+ fprintf(fff, "\n You have done something illegal.");
+
+ if (race_flags1_p(PR1_EXPERIMENTAL))
+ fprintf(fff, "\n You have done something experimental.");
+
+ {
+ 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)
+ {
+ mimic = get_mimic_name(p_ptr->mimic_form);
+ fprintf(fff, "\n You %s disguised as a %s.", (death ? "were" : "are"), mimic);
+ }
+ }
+
+ /* Where we are, if we're alive */
+ if (!death) fprintf(fff, "\n You are currently %s.", describe_player_location());
+
+ /* Monsters slain */
+ {
+ int k;
+ s32b Total = 0;
+
+ for (k = 1; k < max_r_idx; k++)
+ {
+ monster_race *r_ptr = &r_info[k];
+
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ bool_ dead = (r_ptr->max_num == 0);
+ if (dead)
+ {
+ Total++;
+ }
+ }
+ else
+ {
+ s16b This = r_ptr->r_pkills;
+ if (This > 0)
+ {
+ Total += This;
+ }
+ }
+ }
+
+ if (Total < 1)
+ fprintf(fff, "\n You have defeated no enemies yet.");
+ else if (Total == 1)
+ fprintf(fff, "\n You have defeated one enemy.");
+ else
+ fprintf(fff, "\n You have defeated %ld enemies.", (long int) Total);
+ }
+
+ struct hook_chardump_in in = { fff };
+ process_hooks_new(HOOK_CHAR_DUMP, &in, NULL);
+
+ /* Date */
+ {
+ u32b days = bst(DAY, turn);
+ fprintf(fff,
+ (death ? "\n Your adventure lasted %ld day%s." : "\n You have been adventuring for %ld day%s."),
+ (long int) days, (days == 1) ? "" : "s");
+ }
+
+ fprintf (fff, "\n\n");
+
+ /* Emit the self-knowledge lines, even though they duplicate the
+ information in the grids (below), because they contain information
+ that's not in the grids (racial abilities, luck, etc.). */
+ if (full)
+ {
+ self_knowledge(fff);
+ fprintf(fff, "\n\n");
+ }
+
+ /* adds and slays */
+ display_player (2);
+ file_character_print_grid(fff, FALSE, TRUE);
+
+ /* sustains and resistances */
+ display_player (3);
+ file_character_print_grid(fff, TRUE, FALSE);
+
+ /* stuff */
+ display_player (4);
+ file_character_print_grid(fff, FALSE, FALSE);
+
+ /* a little bit of stuff */
+ display_player (5);
+ file_character_print_grid(fff, FALSE, FALSE);
+
+ /* Dump corruptions */
+ dump_corruptions(fff, FALSE, TRUE);
+
+ /* Dump skills */
+ dump_skills(fff);
+ dump_abilities(fff);
+
+ /* Dump companions. */
+ dump_companions(fff);
+
+ if (p_ptr->companion_killed)
+ {
+ if (p_ptr->companion_killed == 1)
+ fprintf(fff, "\n One of your companion(s) has been killed.");
+ else
+ fprintf(fff, "\n %d of your companions have been killed.", p_ptr->companion_killed);
+ }
+
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if ((fates[i].fate) && (fates[i].know))
+ {
+ fprintf(fff, "\n\n [Fates]\n\n");
+ dump_fates(fff);
+ break;
+ }
+ }
+
+ /* Skip some lines */
+ fprintf(fff, "\n\n");
+
+
+ /* Dump the equipment */
+ text_out_indent = 4;
+ if (equip_cnt)
+ {
+ fprintf(fff, " [Character Equipment]\n\n");
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (!p_ptr->body_parts[i - INVEN_WIELD]) continue;
+
+ file_character_print_item(fff, index_to_label(i), &p_ptr->inventory[i], full);
+ }
+ fprintf(fff, "\n\n");
+ }
+
+ /* Dump the inventory */
+ fprintf(fff, " [Character Inventory]\n\n");
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ file_character_print_item(fff, index_to_label(i), &p_ptr->inventory[i], full);
+ }
+ fprintf(fff, "\n\n");
+
+ /* Print all homes in the different towns */
+ {
+ std::unordered_set<store_type *> seen_stores;
+ for (j = 0; j < max_wf_idx; j++)
+ {
+ if (wf_info[j].feat == FEAT_TOWN &&
+ file_character_check_stores(&seen_stores, &wf_info[j], 7))
+ {
+ file_character_print_store(fff, &wf_info[j], 7, full);
+ }
+ }
+ }
+
+ /* Print all Mathom-houses in the different towns */
+ {
+ std::unordered_set<store_type *> seen_stores;
+ for (j = 0; j < max_wf_idx; j++)
+ {
+ if (wf_info[j].feat == FEAT_TOWN &&
+ file_character_check_stores(&seen_stores, &wf_info[j], 57))
+ {
+ file_character_print_store(fff, &wf_info[j], 57, full);
+ }
+ }
+ }
+
+ 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;
+
+ /* 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 */
+ std::unique_ptr<hyperlink_type> h_ptr(new hyperlink_type);
+ memset(h_ptr.get(), 0, sizeof(hyperlink_type));
+
+ /* Setup buffer pointer */
+ buf = h_ptr->rbuf;
+
+ /* Wipe the links */
+ for (i = 0; i < MAX_LINKS; i++)
+ {
+ h_ptr->link_x[i] = -1;
+ }
+
+ /* Hack XXX XXX XXX */
+ if (what)
+ {
+ /* h_ptr->caption */
+ strcpy(h_ptr->caption, what);
+
+ /* Access the "file" */
+ strcpy(h_ptr->path, name);
+
+ /* Open */
+ fff = my_fopen(h_ptr->path, "r");
+ }
+
+ /* Look in "help" */
+ if (!fff)
+ {
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "Help file '%s'", name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, name);
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+ }
+
+ /* Look in "info" */
+ if (!fff)
+ {
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "Info file '%s'", name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_INFO, name);
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+ }
+
+ /* Look in "file" */
+ if (!fff)
+ {
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "File '%s'", name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_FILE, name);
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+ }
+
+ /* Oops */
+ if (!fff)
+ {
+ /* Message */
+ msg_format("Cannot open '%s'.", name);
+ msg_print(NULL);
+
+ /* Oops */
+ return (TRUE);
+ }
+
+
+ /* Pre-Parse the file */
+ while (TRUE)
+ {
+ /* Read a line or stop */
+ if (my_fgets(fff, h_ptr->rbuf, 1024)) break;
+
+ /* Get a color */
+ if (prefix(h_ptr->rbuf, "#####"))
+ {
+ buf = &h_ptr->rbuf[6];
+ }
+ else buf = h_ptr->rbuf;
+
+ /* Get the link colors */
+ if (prefix(buf, "|||||"))
+ {
+ link_color = color_char_to_attr(buf[5]);
+ link_color_sel = color_char_to_attr(buf[6]);
+ }
+
+ /* Tag ? */
+ if (prefix(buf, "~~~~~"))
+ {
+ if (line < 0)
+ {
+ int i;
+ char old_c;
+
+ for (i = 5; (buf[i] >= '0') && (buf[i] <= '9'); i++)
+ ;
+ old_c = buf[i];
+ buf[i] = '\0';
+
+ if (atoi(buf + 5) == -line)
+ {
+ line = next + 1;
+ }
+ buf[i] = old_c;
+ }
+ }
+
+ x = 0;
+ while (buf[x])
+ {
+ /* Hyperlink ? */
+ if (prefix(buf + x, "*****"))
+ {
+ int xx = x + 5, stmp, xdeb = x + 5, z;
+ char tmp[20];
+
+ for (z = 0; z < 20; z++) tmp[z] = '\0';
+
+ h_ptr->link_x[max_link] = x;
+ h_ptr->link_y[max_link] = next;
+
+ if (buf[xx] == '/')
+ {
+ xx++;
+ h_ptr->link_key[max_link] = buf[xx];
+ xx++;
+ xdeb += 2;
+ }
+ else
+ {
+ h_ptr->link_key[max_link] = 0;
+ }
+
+ /* Zap the link info */
+ while (buf[xx] != '*')
+ {
+ h_ptr->link[max_link][xx - xdeb] = buf[xx];
+ xx++;
+ }
+ h_ptr->link[max_link][xx - xdeb] = '\0';
+ xx++;
+ stmp = xx;
+ while (buf[xx] != '[')
+ {
+ tmp[xx - stmp] = buf[xx];
+ xx++;
+ }
+ xx++;
+ tmp[xx - stmp] = '\0';
+ h_ptr->link_line[max_link] = -atoi(tmp);
+ max_link++;
+ }
+ x++;
+ }
+
+ /* Count the "real" lines */
+ next++;
+ }
+
+ /* Save the number of "real" lines */
+ size = next;
+
+
+
+ /* Display the file */
+ while (TRUE)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ Term_get_size(&wid, &hgt);
+
+ /* Restart when necessary */
+ if (line >= size) line = 0;
+
+
+ /* Re-open the file if needed */
+ if (next > line)
+ {
+ /* Close it */
+ my_fclose(fff);
+
+ /* Hack -- Re-Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Oops */
+ if (!fff)
+ {
+ return (FALSE);
+ }
+
+ /* File has been restarted */
+ next = 0;
+ }
+
+ /* Skip lines if needed */
+ for (; next < line; next++)
+ {
+ /* Skip a line */
+ if (my_fgets(fff, buf, 1024)) break;
+ }
+
+
+ /* Dump the next 20 (or more in bigscreen) lines of the file */
+ for (i = 0; i < (hgt - 4); )
+ {
+ int print_x;
+
+ /* Hack -- track the "first" line */
+ if (!i) line = next;
+
+ /* Get a line of the file or stop */
+ if (my_fgets(fff, h_ptr->rbuf, 1024)) break;
+
+ /* Get a color */
+ if (prefix(h_ptr->rbuf, "#####"))
+ {
+ color = color_char_to_attr(h_ptr->rbuf[5]);
+ buf = &h_ptr->rbuf[6];
+ }
+ else buf = h_ptr->rbuf;
+
+ /* Count the "real" lines */
+ next++;
+
+ /* Skip link colors */
+ if (prefix(buf, "|||||")) continue;
+
+ /* Skip tags */
+ if (prefix(buf, "~~~~~"))
+ {
+ i++;
+ continue;
+ }
+
+ /* Hack -- keep searching */
+ if (find && !i && !strstr(buf, find)) continue;
+
+ /* Hack -- stop searching */
+ find = NULL;
+
+ /* Be sure to get a correct cur_link */
+ if (h_ptr->link_y[cur_link] >= line + (hgt - 4))
+ {
+ while ((cur_link > 0) && (h_ptr->link_y[cur_link] >= line + (hgt - 4)))
+ {
+ cur_link--;
+ }
+ }
+ if (h_ptr->link_y[cur_link] < line)
+ {
+ while ((cur_link < max_link) && (h_ptr->link_y[cur_link] < line))
+ {
+ cur_link++;
+ }
+ }
+
+ /* Dump the line */
+ print_x = 0;
+ if (!prefix(buf, "&&&&&"))
+ {
+ x = 0;
+ while (buf[x])
+ {
+ /* Hyperlink ? */
+ if (prefix(buf + x, "*****"))
+ {
+ int xx = x + 5;
+
+ /* Zap the link info */
+ while (buf[xx] != '[')
+ {
+ xx++;
+ }
+ xx++;
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ byte color = link_color;
+
+ if ((h_ptr->link_x[cur_link] == x) && (h_ptr->link_y[cur_link] == line + i))
+ color = link_color_sel;
+
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+
+ Term_putch(print_x, i + 2, color, buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x = xx;
+ }
+ /* Color ? */
+ else if (prefix(buf + x, "[[[[["))
+ {
+ int xx = x + 6;
+
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+ Term_putch(print_x, i + 2, color_char_to_attr(buf[x + 5]), buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x = xx;
+ }
+ /* Remove HTML ? */
+ else if (prefix(buf + x, "{{{{{"))
+ {
+ int xx = x + 6;
+
+ /* Ok remove this section */
+ while (buf[xx] != '}')
+ {
+ xx++;
+ }
+ x = xx;
+ }
+ else
+ {
+ Term_putch(print_x, i + 2, color, buf[x]);
+ print_x++;
+ }
+
+ x++;
+ }
+ }
+ /* Verbatim mode: i.e: acacacac */
+ else
+ {
+ x = 5;
+ while (buf[x])
+ {
+ Term_putch(print_x, i + 2, color_char_to_attr(buf[x]), buf[x + 1]);
+ print_x++;
+ x += 2;
+ }
+ }
+ color = TERM_WHITE;
+
+ /* Hilite "h_ptr->shower" */
+ if (h_ptr->shower[0])
+ {
+ cptr str = buf;
+
+ /* Display matches */
+ while ((str = strstr(str, h_ptr->shower)) != NULL)
+ {
+ int len = strlen(h_ptr->shower);
+
+ /* Display the match */
+ Term_putstr(str - buf, i + 2, len, TERM_YELLOW, h_ptr->shower);
+
+ /* Advance */
+ str += len;
+ }
+ }
+
+ /* Count the printed lines */
+ i++;
+ }
+
+ /* Hack -- failed search */
+ if (find)
+ {
+ bell();
+ line = back;
+ find = NULL;
+ continue;
+ }
+
+
+ /* Show a general "title" */
+ prt(format("[%s, %s, Line %d/%d]", get_version_string(),
+ h_ptr->caption, line, size), 0, 0);
+
+ /* Prompt -- menu screen */
+ if (menu)
+ {
+ /* Wait for it */
+ prt("[Press a Number, or ESC to exit.]", hgt - 1, 0);
+ }
+
+ /* Prompt -- small files */
+ else if (size <= (hgt - 4))
+ {
+ /* Wait for it */
+ prt("[Press ESC to exit.]", hgt - 1, 0);
+ }
+
+ /* Prompt -- large files */
+ else
+ {
+ /* Wait for it */
+ prt("[Press 2, 8, 4, 6, /, =, #, %, backspace, or ESC to exit.]", hgt - 1, 0);
+ }
+
+ /* Get a keypress */
+ k = inkey();
+
+ /* Hack -- return to last screen */
+ if ((k == '?') || (k == 0x7F) || (k == '\010')) break;
+
+ /* Hack -- try showing */
+ if (k == '=')
+ {
+ /* Get "h_ptr->shower" */
+ prt("Show: ", hgt - 1, 0);
+ (void)askfor_aux(h_ptr->shower, 80);
+ }
+
+ /* Hack -- try finding */
+ if (k == '/')
+ {
+ /* Get "h_ptr->finder" */
+ prt("Find: ", hgt - 1, 0);
+ if (askfor_aux(h_ptr->finder, 80))
+ {
+ /* Find it */
+ find = h_ptr->finder;
+ back = line;
+ line = line + 1;
+
+ /* Show it */
+ strcpy(h_ptr->shower, h_ptr->finder);
+ }
+ }
+
+ /* Hack -- go to a specific line */
+ if (k == '#')
+ {
+ char tmp[81];
+ prt("Goto Line: ", hgt - 1, 0);
+ strcpy(tmp, "0");
+ if (askfor_aux(tmp, 80))
+ {
+ line = atoi(tmp);
+ }
+ }
+
+ /* Hack -- go to a specific file */
+ if (k == '%')
+ {
+ char tmp[81];
+ prt("Goto File: ", hgt - 1, 0);
+ strcpy(tmp, "help.hlp");
+ if (askfor_aux(tmp, 80))
+ {
+ if (!show_file(tmp, NULL, 0, mode)) k = ESCAPE;
+ }
+ }
+
+ /* Hack -- Allow backing up */
+ if (k == '-')
+ {
+ line = line - (hgt - 4);
+ if (line < 0) line = 0;
+ }
+
+ if (k == '8')
+ {
+ line--;
+ if (line < 0) line = 0;
+ }
+
+ /* Hack -- Advance a single line */
+ if (k == '2')
+ {
+ line = line + 1;
+ }
+
+ /* Advance one page */
+ if (k == ' ')
+ {
+ line = line + (hgt - 4);
+ }
+
+ /* Advance one link */
+ if ((k == '6') || (k == '\t'))
+ {
+ cur_link++;
+ if (cur_link >= max_link) cur_link = max_link - 1;
+
+ if (h_ptr->link_y[cur_link] < line) line = h_ptr->link_y[cur_link];
+ if (h_ptr->link_y[cur_link] >= line + (hgt - 4)) line = h_ptr->link_y[cur_link] - (hgt - 4);
+ }
+ /* Return one link */
+ if (k == '4')
+ {
+ cur_link--;
+ if (cur_link < 0) cur_link = 0;
+
+ if (h_ptr->link_y[cur_link] < line) line = h_ptr->link_y[cur_link];
+ if (h_ptr->link_y[cur_link] >= line + (hgt - 4)) line = h_ptr->link_y[cur_link] - (hgt - 4);
+ }
+
+ /* Recurse on numbers */
+ if (k == '\r')
+ {
+ if (h_ptr->link_x[cur_link] != -1)
+ {
+ /* Recurse on that file */
+ if (!show_file(h_ptr->link[cur_link], NULL, h_ptr->link_line[cur_link], mode)) k = ESCAPE;
+ }
+ }
+
+ /* Exit on escape */
+ if (k == ESCAPE) break;
+
+ /* No other key ? lets look for a shortcut */
+ for (i = 0; i < max_link; i++)
+ {
+ if (h_ptr->link_key[i] == k)
+ {
+ /* Recurse on that file */
+ if (!show_file(h_ptr->link[i], NULL, h_ptr->link_line[i], mode)) k = ESCAPE;
+ break;
+ }
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Escape */
+ if (k == ESCAPE) return (FALSE);
+
+ /* Normal return */
+ return (TRUE);
+}
+
+bool_ txt_to_html(cptr head, cptr foot, cptr base, cptr ext, bool_ force, bool_ recur)
+{
+ int i, x;
+
+ /* Number of "real" lines passed by */
+ int next = 0;
+
+ char buf_name[80];
+
+ /* Color of the next line */
+ byte color = TERM_WHITE;
+
+ /* Current help file */
+ FILE *fff = NULL;
+
+ /* Current aux file */
+ FILE *aux = NULL;
+
+ /* Current html file */
+ FILE *htm = NULL;
+
+ cptr file_ext = "html";
+ cptr link_prefix = "";
+ cptr link_suffix = "";
+
+ /* Pointer to general buffer in the above */
+ char *buf;
+
+ /* Allocate hyperlink data */
+ std::unique_ptr<hyperlink_type> h_ptr(new hyperlink_type);
+ memset(h_ptr.get(), 0, sizeof(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;
+ }
+
+ sprintf(buf_name, "%s.%s", base, file_ext);
+
+ if ((!force) && file_exist(buf_name)) return FALSE;
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, buf_name);
+
+ /* Open the file */
+ htm = my_fopen(h_ptr->path, "w");
+
+ sprintf(buf_name, "%s.%s", base, ext);
+
+ /* h_ptr->caption */
+ sprintf(h_ptr->caption, "Help file '%s'", buf_name);
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, buf_name);
+
+ /* Open the file */
+ fff = my_fopen(h_ptr->path, "r");
+
+ /* Oops */
+ if (!fff || !htm)
+ {
+ my_fclose(fff);
+ my_fclose(htm);
+
+ /* Oops */
+ return (TRUE);
+ }
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, head);
+
+ /* Open the file */
+ aux = my_fopen(h_ptr->path, "r");
+
+ /* Copy the header */
+ if (aux)
+ {
+ while (TRUE)
+ {
+ char *find;
+
+ if (my_fgets(aux, h_ptr->rbuf, 1024)) break;
+ find = strstr(h_ptr->rbuf, "%t");
+ if (find != NULL)
+ {
+ *find = '\0';
+ find += 2;
+ fprintf(htm, "%s", h_ptr->rbuf);
+ fprintf(htm, "%s", base);
+ fprintf(htm, "%s\n", find);
+ }
+ else
+ fprintf(htm, "%s\n", h_ptr->rbuf);
+ }
+ my_fclose(aux);
+ }
+
+ /* Display the file */
+ while (TRUE)
+ {
+ bool_ do_color = FALSE;
+
+ /* Skip a line */
+ if (my_fgets(fff, h_ptr->rbuf, 1024)) break;
+
+ color = TERM_WHITE;
+
+ {
+ int print_x;
+
+ /* Get a color */
+ if (prefix(h_ptr->rbuf, "#####"))
+ {
+ color = color_char_to_attr(h_ptr->rbuf[5]);
+ do_color = TRUE;
+ fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+ buf = &h_ptr->rbuf[6];
+ }
+ else buf = h_ptr->rbuf;
+
+ /* Count the "real" lines */
+ next++;
+
+ /* Skip link colors */
+ if (prefix(buf, "|||||")) continue;
+
+ /* Skip tags */
+ if (prefix(buf, "~~~~~"))
+ {
+ int i;
+
+ for (i = 5; (buf[i] >= '0') && (buf[i] <= '9'); i++)
+ ;
+ buf[i] = '\0';
+ fprintf(htm, "<A NAME=\"%s\"></A>", buf + 5);
+ continue;
+ }
+
+ /* Dump the line */
+ print_x = 0;
+ if (!prefix(buf, "&&&&&"))
+ {
+ x = 0;
+ while (buf[x])
+ {
+ /* Hyperlink ? */
+ if (prefix(buf + x, "*****"))
+ {
+ int xx = x + 5, z = 0;
+ char buff[80];
+ char link_line[80], *s;
+
+ if (buf[xx] == '/') xx += 2;
+
+ /* Zap the link info */
+ while (buf[xx] != '*')
+ {
+ buff[z++] = buf[xx];
+ xx++;
+ }
+ xx++;
+ buff[z] = '\0';
+
+ /* Zap the link info */
+ z = 0;
+ while (buf[xx] != '[')
+ {
+ link_line[z++] = buf[xx];
+ xx++;
+ }
+ xx++;
+ link_line[z] = '\0';
+
+ /* parse it */
+ s = buff;
+ while (*s != '.') s++;
+ *s = '\0';
+ s++;
+ if (recur) txt_to_html(head, foot, buff, s, FALSE, recur);
+
+ if (atoi(link_line)) fprintf(htm, "<A HREF=\"%s%s.%s%s#%d\">", link_prefix, buff, file_ext, link_suffix, atoi(link_line));
+ else fprintf(htm, "<A HREF=\"%s%s.%s%s\">", link_prefix, buff, file_ext, link_suffix);
+
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+ fprintf(htm, "%c", buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x = xx;
+
+ fprintf(htm, "</A>");
+ }
+ /* Color ? */
+ else if (prefix(buf + x, "[[[[["))
+ {
+ int xx = x + 6;
+
+ color = color_char_to_attr(buf[x + 5]);
+ fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+
+ /* Ok print the link name */
+ while (buf[xx] != ']')
+ {
+ /* Now we treat the next char as printable */
+ if (buf[xx] == '\\')
+ xx++;
+ fprintf(htm, "%c", buf[xx]);
+ xx++;
+ print_x++;
+ }
+ x++;
+ x = xx;
+
+ fprintf(htm, "</FONT>");
+ }
+ /* Hidden HTML tag? */
+ else if (prefix(buf + x, "{{{{{"))
+ {
+ int xx = x + 5;
+
+ /* Ok output the tag inside */
+ while (buf[xx] != '}')
+ {
+ fprintf(htm, "%c", buf[xx]);
+ xx++;
+ }
+ x++;
+ x = xx;
+ }
+ else
+ {
+ fprintf(htm, "%c", buf[x]);
+ print_x++;
+ }
+
+ x++;
+ }
+ }
+ /* Verbatim mode: i.e: acacacac */
+ else
+ {
+ byte old_color;
+
+ x = 5;
+ old_color = color_char_to_attr(buf[x]);
+ fprintf(htm, "<FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+ while (buf[x])
+ {
+ color = color_char_to_attr(buf[x]);
+ if (color != old_color)
+ fprintf(htm, "</FONT><FONT COLOR=\"#%02X%02X%02X\">",
+ angband_color_table[color][1],
+ angband_color_table[color][2],
+ angband_color_table[color][3]);
+
+ fprintf(htm, "%c", buf[x + 1]);
+ print_x++;
+ x += 2;
+ }
+ fprintf(htm, "</FONT>");
+ }
+ }
+ if (do_color)
+ {
+ fprintf(htm, "</FONT>");
+ }
+ fprintf(htm, "\n");
+ }
+
+ /* Build the filename */
+ path_build(h_ptr->path, 1024, ANGBAND_DIR_HELP, foot);
+
+ /* Open the file */
+ aux = my_fopen(h_ptr->path, "r");
+
+ /* Copy the footer */
+ if (aux)
+ {
+ while (TRUE)
+ {
+ if (my_fgets(aux, h_ptr->rbuf, 1024)) break;
+ fprintf(htm, "%s\n", h_ptr->rbuf);
+ }
+ my_fclose(aux);
+ }
+
+ /* Close the file */
+ my_fclose(htm);
+ my_fclose(fff);
+
+ /* Normal return */
+ return (TRUE);
+}
+
+static 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';
+}
+
+/* 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);
+
+ /* 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);
+
+ /* Append to the file */
+ htm = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!htm) return;
+
+ /* Retrieve current screen size */
+ Term_get_size(&wid, &hgt);
+
+ fprintf(htm, "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
+ "<head>\n");
+ fprintf(htm, "<meta name=\"GENERATOR\" content=\"%s\"/>\n",
+ get_version_string());
+ fprintf(htm, "<title>%s</title>\n", name);
+ fprintf(htm, "</head>\n"
+ "<body>\n"
+ "<pre style=\"color: #ffffff; background-color: #000000; font-family: monospace\">\n");
+ fprintf(htm, "<span style=\"color: #%02X%02X%02X\">\n",
+ angband_color_table[TERM_WHITE][1],
+ angband_color_table[TERM_WHITE][2],
+ angband_color_table[TERM_WHITE][3]);
+
+ /* Dump the screen */
+ for (y = 0; y < hgt; y++)
+ {
+ cmovie_clean_line(y, abuf, cbuf);
+
+ /* Dump each row */
+ for (x = 0; x < wid; x++)
+ {
+ a = color_char_to_attr(abuf[x]);
+ c = cbuf[x];
+
+ if (oa != a)
+ {
+ fprintf(htm, "</span><span style=\"color: #%02X%02X%02X\">", angband_color_table[a][1], angband_color_table[a][2], angband_color_table[a][3]);
+ oa = a;
+ }
+ if (c == '<')
+ fprintf(htm, "&lt;");
+ else if (c == '>')
+ fprintf(htm, "&gt;");
+ else if (c == '&')
+ fprintf(htm, "&amp;");
+ else
+ fprintf(htm, "%c", c);
+ }
+
+ /* End the row */
+ fprintf(htm, "\n");
+ }
+ fprintf(htm, "</span>\n"
+ "</pre>\n"
+ "</body>\n"
+ "</html>\n");
+
+ /* Close it */
+ my_fclose(htm);
+}
+
+
+/*
+ * Peruse the On-Line-Help
+ */
+void do_cmd_help(void)
+{
+ /* Save screen */
+ screen_save();
+
+ /* Peruse the main help file */
+ (void)show_file("help.hlp", NULL, 0, 0);
+
+ /* Load screen */
+ screen_load();
+}
+
+
+
+
+/*
+ * Process the player name.
+ * Extract a clean "base name".
+ * Build the savefile name if needed.
+ */
+void process_player_base()
+{
+ char temp[128];
+
+ /* Rename the savefile, using the player_base */
+ (void)sprintf(temp, "%s", player_base);
+
+ /* Build the filename */
+ path_build(savefile, 1024, ANGBAND_DIR_SAVE, temp);
+}
+
+void process_player_name(bool_ sf)
+{
+ int i, k = 0;
+ char tmp[50];
+
+ /* Cannot be too long */
+ if (strlen(player_base) > 15)
+ {
+ /* Name too long */
+ quit_fmt("The name '%s' is too long!", player_base);
+ }
+
+ /* Cannot contain "icky" characters */
+ for (i = 0; player_base[i]; i++)
+ {
+ /* No control characters */
+ if (iscntrl(player_base[i]))
+ {
+ /* Illegal characters */
+ quit_fmt("The name '%s' contains control chars!", player_base);
+ }
+ }
+
+ /* 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++] = '_';
+ }
+
+
+#if defined(WINDOWS)
+
+ /* Hack -- max length */
+ if (k > 8) k = 8;
+
+#endif
+
+ /* Terminate */
+ tmp[k] = '\0';
+ sprintf(player_base, "%s", tmp);
+
+ /* Require a "base" name */
+ if (!player_base[0]) strcpy(player_base, "PLAYER");
+
+
+ /* Change the savefile name */
+ if (sf)
+ {
+ process_player_base();
+ }
+}
+
+
+/*
+ * Gets a name for the character, reacting to name changes.
+ *
+ * Assumes that "display_player(0)" has just been called
+ *
+ * Perhaps we should NOT ask for a name (at "birth()") on
+ * Unix machines? XXX XXX
+ *
+ * What a horrible name for a global function. XXX XXX XXX
+ */
+void get_name(void)
+{
+ char tmp[32];
+
+ /* Clear last line */
+ clear_from(22);
+
+ /* Prompt and ask */
+ prt("[Enter your player's name above, or hit ESCAPE]", 23, 2);
+
+ /* Ask until happy */
+ while (1)
+ {
+ /* Go to the "name" field */
+ move_cursor(2, 9);
+
+ /* Save the player name */
+ strcpy(tmp, player_name);
+
+ /* Get an input, ignore "Escape" */
+ if (askfor_aux(tmp, 31)) strcpy(player_name, tmp);
+
+ /* Process the player name */
+ process_player_name(FALSE);
+
+ /* All done */
+ break;
+ }
+
+ /* Pad the name (to clear junk) */
+ sprintf(tmp, "%-31.31s", player_name);
+
+ /* Re-Draw the name (in light blue) */
+ c_put_str(TERM_L_BLUE, tmp, 2, 9);
+
+ /* Erase the prompt, etc */
+ clear_from(22);
+}
+
+
+
+/*
+ * Hack -- commit suicide
+ */
+void do_cmd_suicide(void)
+{
+ int i;
+
+ /* Flush input */
+ flush();
+
+ /* Verify Retirement */
+ if (total_winner)
+ {
+ /* Verify */
+ if (!get_check("Do you want to retire? ")) return;
+ }
+
+ /* Verify Suicide */
+ else
+ {
+ /* Verify */
+ if (!get_check("Do you really want to quit? ")) return;
+
+ if (!noscore)
+ {
+ /* Special Verification for suicide */
+ prt("Please verify QUITTING by typing the '@' sign: ", 0, 0);
+ flush();
+ i = inkey();
+ prt("", 0, 0);
+ if (i != '@') return;
+ }
+ }
+
+ /* Stop playing */
+ alive = FALSE;
+
+ /* Kill the player */
+ death = TRUE;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Cause of death */
+ (void)strcpy(died_from, "Quitting");
+}
+
+
+ /* HACK - Remove / set the CAVE_VIEW flag, since view_x / view_y
+ * is not saved, and the visible locations are not lighted correctly
+ * when the game is loaded again
+ * Alternatively forget_view() and update_view() can be used
+ */
+void remove_cave_view(bool_ remove)
+{
+ int i;
+ cave_type *c_ptr;
+
+ if (view_n)
+ {
+ /* Clear them all */
+ for (i = 0; i < view_n; i++)
+ {
+ int y = view_y[i];
+ int x = view_x[i];
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ if (remove)
+ c_ptr->info &= ~(CAVE_VIEW);
+ else
+ c_ptr->info |= (CAVE_VIEW);
+ }
+ }
+}
+
+/*
+ * Save the game
+ */
+void do_cmd_save_game(void)
+{
+ remove_cave_view(TRUE);
+
+ /* Save the current level if in a persistent level */
+ save_dungeon();
+
+ /* Autosaves do not disturb */
+ if (!is_autosave)
+ {
+ /* Disturb the player */
+ disturb(1);
+ }
+
+ /* 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)");
+
+ /* 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);
+
+ /* Refresh */
+ Term_fresh();
+
+ /* Note that the player is not dead */
+ (void)strcpy(died_from, "(alive and well)");
+}
+
+/*
+ * Auto-save depending on whether the auto save flag is set.
+ */
+void autosave_checkpoint()
+{
+ if (autosave_l)
+ {
+ is_autosave = TRUE;
+ msg_print("Autosaving the game...");
+ do_cmd_save_game();
+ is_autosave = FALSE;
+ }
+}
+
+/*
+ * Hack -- Calculates the total number of points earned -JWT-
+ */
+static long total_points(void)
+{
+ s16b max_dl = 0, i, k;
+ long temp, Total = 0;
+ long mult = 20; /* was 100. Divided values by 5 because of an overflow error */
+ long comp_death = (p_ptr->companion_killed * 2 / 5);
+
+ if (!comp_death) comp_death = 1;
+
+ if (p_ptr->preserve) mult -= 1; /* Penalize preserve, maximize modes */
+ mult -= 1; /* maximize pentalty, always on */
+ if (auto_scum) mult -= 4;
+ if (small_levels) mult += ((always_small_level) ? 4 : 10);
+ if (empty_levels) mult += 2;
+ if (smart_learn) 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;
+
+ if (total_winner) temp += 1000000;
+
+
+
+ return (temp);
+}
+
+
+
+/*
+ * Centers a string within a 31 character string -JWT-
+ */
+static void center_string(char *buf, cptr str)
+{
+ int i, j;
+
+ /* Total length */
+ i = strlen(str);
+
+ /* Necessary border */
+ j = 15 - i / 2;
+
+ /* Mega-Hack */
+ (void)sprintf(buf, "%*s%s%*s", j, "", str, 31 - i - j, "");
+}
+
+
+/*
+ * Display a "tomb-stone"
+ */
+static void print_tomb(void)
+{
+ cptr p;
+
+ char tmp[160];
+
+ char buf[1024];
+ char dummy[80];
+
+ FILE *fp;
+
+ time_t ct = time(nullptr);
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, "dead.txt");
+
+ /* Open the News file */
+ fp = my_fopen(buf, "r");
+
+ /* Dump */
+ if (fp)
+ {
+ int i = 0;
+
+ /* Dump the file to the screen */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Display and advance */
+ display_message(0, i++, strlen(buf), TERM_WHITE, buf);
+ }
+
+ /* Close */
+ my_fclose(fp);
+ }
+
+
+ /* King or Queen */
+ if (total_winner || (p_ptr->lev > PY_MAX_LEVEL))
+ {
+ p = "Magnificent";
+ }
+
+ /* Normal */
+ else
+ {
+ p = cp_ptr->titles[(p_ptr->lev - 1) / 5];
+ }
+
+ 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);
+ 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();
+ show_equip_full();
+ prt("You are using: -more-", 0, 0);
+ if (inkey() == ESCAPE) return;
+ }
+
+ /* Inventory -- if any */
+ if (inven_cnt)
+ {
+ Term_clear();
+ show_inven_full();
+ prt("You are carrying: -more-", 0, 0);
+ if (inkey() == ESCAPE) return;
+ }
+
+ /* Homes in the different towns */
+ for (k = 1; k < max_towns; k++)
+ {
+ st_ptr = &town_info[k].store[7];
+
+ /* Home -- if anything there */
+ if (st_ptr->stock_num)
+ {
+ /* Display contents of the home */
+ for (k = 0, i = 0; i < st_ptr->stock_num; k++)
+ {
+ /* Clear screen */
+ Term_clear();
+
+ /* Show 12 items */
+ for (j = 0; (j < 12) && (i < st_ptr->stock_num); j++, i++)
+ {
+ char o_name[80];
+ char tmp_val[80];
+
+ /* Acquire item */
+ o_ptr = &st_ptr->stock[i];
+
+ /* Print header, clear line */
+ sprintf(tmp_val, "%c) ", I2A(j));
+ prt(tmp_val, j + 2, 4);
+
+ /* Display object description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+ c_put_str(tval_to_attr[o_ptr->tval], o_name, j + 2, 7);
+ }
+
+ /* h_ptr->caption */
+ prt(format("Your home contains (page %d): -more-", k + 1), 0, 0);
+
+ /* Wait for it */
+ if (inkey() == ESCAPE) return;
+ }
+ }
+ }
+}
+
+
+
+
+
+/*
+ * 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 highscore_fd, 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(highscore_fd, 0)) return;
+
+ /* Hack -- Count the high scores */
+ for (i = 0; i < MAX_HISCORES; i++)
+ {
+ if (highscore_read(highscore_fd, &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 gold, when, aged;
+
+ int 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(highscore_fd, j)) break;
+ if (highscore_read(highscore_fd, &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_quest = atoi(the_score.inside_quest);
+
+ /* Hack -- extract the gold and such */
+ 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,
+ 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_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,
+ " (Date %s, Gold %s, Turn %s).",
+ 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;
+ }
+}
+
+
+/*
+ * show_highclass - selectively list highscores based on class
+ * -KMW-
+ */
+void show_highclass(int building)
+{
+
+ int i = 0, j, m = 0;
+ int pr, pc, clev;
+ high_score the_score;
+ char buf[1024], out_val[256];
+ int highscore_fd;
+
+ 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_USER, "scores.raw");
+
+ /* Open file */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return;
+ }
+
+ if (highscore_seek(highscore_fd, 0)) return;
+
+ for (i = 0; i < MAX_HISCORES; i++)
+ if (highscore_read(highscore_fd, &the_score)) break;
+
+ m = 0;
+ j = 0;
+ clev = 0;
+
+ while ((m < 9) || (j < MAX_HISCORES))
+ {
+ if (highscore_seek(highscore_fd, j)) break;
+ if (highscore_read(highscore_fd, &the_score)) break;
+ pr = atoi(the_score.p_r);
+ pc = atoi(the_score.p_c);
+ clev = atoi(the_score.cur_lev);
+ if (((pc == (building - 10)) && (building != 1)) ||
+ ((building == 1) && (clev >= PY_MAX_LEVEL)))
+ {
+ sprintf(out_val, "%3d) %s the %s (Level %2d)",
+ (m + 1), the_score.who, 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, race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+ else if ((building != 1))
+ {
+ if ((p_ptr->lev > clev) && (p_ptr->pclass == (building - 10)))
+ {
+ sprintf(out_val, "You) %s the %s (Level %2d)",
+ player_name, race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+ }
+
+ fd_close(highscore_fd);
+
+ 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)
+{
+ int i = 0, j, m = 0;
+ int pr, clev, lastlev;
+ high_score the_score;
+ char buf[1024], out_val[256], tmp_str[80];
+ int highscore_fd;
+
+ lastlev = 0;
+
+ /* rr9: TODO - pluralize the race */
+ sprintf(tmp_str, "The Greatest of all the %s", race_info[race_num].title);
+ prt(tmp_str, 5, 3);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, "scores.raw");
+
+ /* Open the highscore file */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ return;
+ }
+
+ if (highscore_seek(highscore_fd, 0)) return;
+
+ for (i = 0; i < MAX_HISCORES; i++)
+ {
+ if (highscore_read(highscore_fd, &the_score)) break;
+ }
+
+ m = 0;
+ j = 0;
+
+ while ((m < 10) && (j < i))
+ {
+ if (highscore_seek(highscore_fd, j)) break;
+ if (highscore_read(highscore_fd, &the_score)) break;
+ pr = atoi(the_score.p_r);
+ clev = atoi(the_score.cur_lev);
+ if (pr == race_num)
+ {
+ sprintf(out_val, "%3d) %s the %s (Level %3d)",
+ (m + 1), the_score.who,
+ 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, race_info[p_ptr->prace].title, p_ptr->lev);
+ prt(out_val, (m + 8), 0);
+ }
+
+ fd_close(highscore_fd);
+}
+
+
+/*
+ * 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.
+ */
+static errr top_twenty(void)
+{
+ int j;
+
+ high_score the_score;
+
+ time_t ct = time((time_t*)0);
+
+ char buf[1024];
+
+ int highscore_fd = 0;
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "scores.raw");
+
+ /* Open the highscore file, for reading/writing */
+ highscore_fd = fd_open(buf, O_RDWR);
+
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ goto out;
+ }
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Wizard-mode pre-empts scoring */
+ if (noscore & 0x000F)
+ {
+ msg_print("Score not registered for wizards.");
+ msg_print(NULL);
+ display_scores_aux(highscore_fd, 0, 10, -1, NULL);
+ goto out;
+ }
+
+ /* Borg-mode pre-empts scoring */
+ if (noscore & 0x00F0)
+ {
+ msg_print("Score not registered for borgs.");
+ msg_print(NULL);
+ display_scores_aux(highscore_fd, 0, 10, -1, NULL);
+ goto out;
+ }
+
+ /* Cheaters are not scored */
+ if (noscore & 0xFF00)
+ {
+ msg_print("Score not registered for cheaters.");
+ msg_print(NULL);
+ display_scores_aux(highscore_fd, 0, 10, -1, NULL);
+ goto out;
+ }
+
+ /* Interupted */
+ if (!total_winner && streq(died_from, "Interrupting"))
+ {
+ msg_print("Score not registered due to interruption.");
+ msg_print(NULL);
+ display_scores_aux(highscore_fd, 0, 10, -1, NULL);
+ goto out;
+ }
+
+ /* Quitter */
+ if (!total_winner && streq(died_from, "Quitting"))
+ {
+ msg_print("Score not registered due to quitting.");
+ msg_print(NULL);
+ display_scores_aux(highscore_fd, 0, 10, -1, NULL);
+ goto out;
+ }
+
+
+ /* Clear the record */
+ static_assert(std::is_pod<high_score>::value,
+ "Cannot memset a non-POD type");
+ memset(&the_score, 0, sizeof(high_score));
+
+ /* Save the version */
+ sprintf(the_score.what, "%ld.%ld.%ld",
+ (long int) VERSION_MAJOR, (long int) VERSION_MINOR, (long int) VERSION_PATCH);
+
+ /* Calculate and save the points */
+ sprintf(the_score.pts, "%9lu", (long)total_points());
+ the_score.pts[9] = '\0';
+
+ /* Save the current gold */
+ sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
+ the_score.gold[9] = '\0';
+
+ /* Save the current turn */
+ sprintf(the_score.turns, "%9lu", (long)turn);
+ the_score.turns[9] = '\0';
+
+ /* Save the date in standard form (8 chars) */
+ strftime(the_score.day, 9, "%m/%d/%y", localtime(&ct));
+
+ /* Save the player name (15 chars) */
+ sprintf(the_score.who, "%-.15s", player_name);
+
+ /* Save the player info XXX XXX XXX */
+ 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.inside_quest, "%3d", p_ptr->inside_quest);
+
+ /* Save the cause of death (31 chars) */
+ sprintf(the_score.how, "%-.31s", died_from);
+
+
+ /* Add a new entry to the score list, see where it went */
+ j = highscore_add(highscore_fd, &the_score);
+
+
+ /* Hack -- Display the top fifteen scores */
+ if (j < 10)
+ {
+ display_scores_aux(highscore_fd, 0, 15, j, NULL);
+ }
+
+ /* Display the scores surrounding the player */
+ else
+ {
+ display_scores_aux(highscore_fd, 0, 5, j, NULL);
+ display_scores_aux(highscore_fd, j - 2, j + 7, j, NULL);
+ }
+
+
+out:
+ if (highscore_fd >= 0)
+ {
+ fd_close(highscore_fd);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Predict the players location, and display it.
+ */
+static errr predict_score(void)
+{
+ int j;
+
+ high_score the_score;
+
+ char buf[1024];
+
+ int highscore_fd = 0;
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "scores.raw");
+
+ /* Open the highscore file */
+ highscore_fd = fd_open(buf, O_RDONLY);
+
+ if (highscore_fd < 0)
+ {
+ msg_print("Score file unavailable.");
+ msg_print(NULL);
+ goto out;
+ }
+
+ /* Clear the record */
+ static_assert(std::is_pod<high_score>::value,
+ "Cannot memset a non-POD type");
+ memset(&the_score, 0, sizeof(high_score));
+
+ /* Save the version */
+ sprintf(the_score.what, "%ld.%ld.%ld",
+ (long int) VERSION_MAJOR, (long int) VERSION_MINOR, (long int) VERSION_PATCH);
+
+ /* Calculate and save the points */
+ sprintf(the_score.pts, "%9lu", (long)total_points());
+ the_score.pts[9] = '\0';
+
+ /* Save the current gold */
+ sprintf(the_score.gold, "%9lu", (long)p_ptr->au);
+ the_score.gold[9] = '\0';
+
+ /* Save the current turn */
+ sprintf(the_score.turns, "%9lu", (long)turn);
+ 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.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.inside_quest, "%3d", p_ptr->inside_quest);
+
+ /* Hack -- no cause of death */
+ strcpy(the_score.how, "nobody (yet!)");
+
+
+ /* See where the entry would be placed */
+ j = highscore_where(highscore_fd, &the_score);
+
+
+ /* Hack -- Display the top fifteen scores */
+ if (j < 10)
+ {
+ display_scores_aux(highscore_fd, 0, 15, j, &the_score);
+ }
+
+ /* Display some "useful" scores */
+ else
+ {
+ display_scores_aux(highscore_fd, 0, 5, -1, NULL);
+ display_scores_aux(highscore_fd, j - 2, j + 7, j, &the_score);
+ }
+
+out:
+ if (highscore_fd >= 0)
+ {
+ fd_close(highscore_fd);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+void predict_score_gui(bool_ *initialized_p, bool_ *game_in_progress_p)
+{
+ char buf[1024];
+ int highscore_fd;
+
+ /* Paranoia */
+ if (!(*initialized_p) || character_icky ||
+ !(*game_in_progress_p) || !character_generated)
+ {
+ /* Can't happen but just in case */
+ plog("You may not do that right now.");
+ return;
+ }
+
+ /* Build the pathname of the score file */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "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.");
+ return;
+ }
+
+ /* Mega-Hack - prevent various functions XXX XXX XXX */
+ *initialized_p = FALSE;
+
+ /* Save screen */
+ screen_save();
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Prepare scores */
+ if ((*game_in_progress_p) && character_generated)
+ {
+ predict_score();
+ }
+
+ /* 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_p = TRUE;
+}
+
+
+/*
+ * Change the player into a King! -RAK-
+ */
+static void kingly(void)
+{
+ /* Hack -- retire in town */
+ dun_level = 0;
+
+ /* Fake death */
+ (void)strcpy(died_from, "Ripe Old Age");
+
+ /* Restore the experience */
+ p_ptr->exp = p_ptr->max_exp;
+
+ /* Restore the level */
+ p_ptr->lev = p_ptr->max_plv;
+
+ /* Hack -- Instant Gold */
+ p_ptr->au += 10000000L;
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Would like to see something more Tolkienian here... */
+
+ /* Display a crown */
+ put_str("#", 1, 34);
+ put_str("#####", 2, 32);
+ put_str("#", 3, 34);
+ put_str(",,, $$$ ,,,", 4, 28);
+ put_str(",,=$ \"$$$$$\" $=,,", 5, 24);
+ put_str(",$$ $$$ $$,", 6, 22);
+ put_str("*> <*> <*", 7, 22);
+ put_str("$$ $$$ $$", 8, 22);
+ put_str("\"$$ $$$ $$\"", 9, 22);
+ put_str("\"$$ $$$ $$\"", 10, 23);
+ put_str("*#########*#########*", 11, 24);
+ put_str("*#########*#########*", 12, 24);
+
+ /* Display a message */
+ put_str("Veni, Vidi, Vici!", 15, 26);
+ put_str("I came, I saw, I conquered!", 16, 21);
+ put_str(format("All Hail the Mighty %s!", sp_ptr->winner), 17, 22);
+
+ /* Flush input */
+ flush();
+
+ /* Wait for response */
+ pause_line(23);
+}
+
+
+/*
+ * Wipe the saved levels
+ */
+void wipe_saved()
+{
+ int d, l, od = dungeon_type, ol = dun_level;
+
+ for (d = 0; d < max_d_idx; d++)
+ {
+ dungeon_info_type *d_ptr = &d_info[d];
+
+ for (l = d_ptr->mindepth; l <= d_ptr->maxdepth; l++)
+ {
+ char buf[10];
+
+ dun_level = l;
+ dungeon_type = d;
+ if (get_dungeon_save(buf))
+ {
+ char tmp[80], name[1024];
+
+ sprintf(tmp, "%s.%s", player_base, buf);
+ path_build(name, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Remove the dungeon save file */
+ fd_kill(name);
+ }
+ }
+ }
+
+ dungeon_type = od;
+ dun_level = ol;
+}
+
+
+/*
+ * Close up the current game (player may or may not be dead)
+ *
+ * This function is called only from "main.c" and "signals.c".
+ */
+void close_game(void)
+{
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Flush the messages */
+ msg_print(NULL);
+
+ /* Flush the input */
+ flush();
+
+
+ /* Hack -- Character is now "icky" */
+ character_icky = TRUE;
+
+
+ /* Handle death */
+ if (death)
+ {
+ /* Handle retirement */
+ if (total_winner)
+ {
+ /* Make a note */
+ add_note_type(NOTE_WINNER);
+
+ kingly();
+ }
+
+ /* Wipe the saved levels */
+ wipe_saved();
+
+ /* Save memories */
+ if (!save_player()) msg_print("Death save failed!");
+
+ /* You are dead */
+ print_tomb();
+
+ /* Show more info */
+ show_info();
+
+ /* Make a note */
+ {
+ char long_day[30];
+ char buf[80];
+ time_t ct = time((time_t*)NULL);
+
+ /* Get the date */
+ strftime(long_day, 30,
+ "%Y-%m-%d at %H:%M:%S", localtime(&ct));
+
+ /* Create string */
+ sprintf(buf, "\n%s was killed by %s on %s\n", player_name,
+ died_from, long_day);
+
+ /* Output to the notes file */
+ output_note(buf);
+ }
+
+ /* Handle score, show Top scores */
+ top_twenty();
+ }
+
+ /* Still alive */
+ else
+ {
+ is_autosave = FALSE;
+
+ /* Save the game */
+ do_cmd_save_game();
+
+ /* Make a note pf session end */
+ add_note_type(NOTE_SAVE_GAME);
+
+ /* Prompt for scores XXX XXX XXX */
+ prt("Press Return (or Escape).", 0, 40);
+
+ /* Predict score (or ESCAPE) */
+ if (inkey() != ESCAPE) predict_score();
+ }
+}
+
+
+/*
+ * Grab a randomly selected line in lib/file/file_name
+ */
+errr get_rnd_line(const char *file_name, char *output)
+{
+ FILE *fp;
+
+ char buf[1024];
+
+ int lines = 0;
+
+ int line;
+
+ int i;
+
+
+ /* Clear the output buffer */
+ strcpy(output, "");
+
+ /* test hack */
+ if (wizard && cheat_xtra) msg_print(file_name);
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, file_name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Failed */
+ if (!fp) return ( -1);
+
+ /* Read the first line */
+ if (0 != my_fgets(fp, buf, 80))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Retrieve number of valid lines in the file */
+ lines = atoi(buf);
+
+ /* Pick a line in the file */
+ line = randint(lines);
+
+ /*
+ * Scan through the file XXX XXX XXX
+ * Seemingly wrong use of the counter is justified by the
+ * stupid 'buffer' lines in the random text files.
+ */
+ for (i = 0; i <= line; i++)
+ {
+ if (0 != my_fgets(fp, buf, 80))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Found the line */
+ if (i == line) break;
+ }
+
+ /* Copy the line to the output buffer */
+ strcpy(output, buf);
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Read line'th line file the file
+ * and return pointer to it, or NULL if it fails.
+ *
+ * Nuked the static buffer. Caller should provide one. -- pelpel
+ *
+ * Caution: 'linbuf' should be at least 80 byte long.
+ */
+char *get_line(const char* fname, cptr fdir, char *linbuf, int line)
+{
+ FILE* fp;
+ int i;
+ char buf[1024];
+
+
+ /* Don't count the first line in the file, which is a comment line */
+ line++;
+
+ /* Build the filename */
+ path_build(buf, 1024, fdir, fname);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Failed */
+ if (!fp) return (NULL);
+
+ /* Read past specified number of lines */
+ for (i = 0; i <= line; i++)
+ {
+ /* Oops */
+ if (my_fgets(fp, linbuf, 80) != 0)
+ {
+ my_fclose(fp);
+ return (NULL);
+ }
+ }
+
+ my_fclose(fp);
+
+ return (linbuf);
+}
+
+
+/*
+ * Return a line for a speaking unique, by Matt G.
+ *
+ * XXX XXX XXX Opening a file and scanning it through whenever a unique
+ * tries to say something? Something like DELAY_LOAD_?_TEXT would be
+ * much better -- pelpel
+ *
+ * XXX XXX XXX I must say the original is an extremely poor and unreliable
+ * implementation... I removed noxious flag -- I'm too stupid to
+ * understand such complexities -- and added extra error checkings
+ * and made sure fd is always closed -- pelpel
+ */
+errr get_xtra_line(const char *file_name, monster_type *m_ptr, char *output)
+{
+ FILE *fp;
+ char buf[1024];
+ int line;
+ int num_entries;
+ int i;
+ int mnum;
+
+
+ /* Clear the message buffer */
+ strcpy(output, "");
+
+ /* test and DEBUG hack */
+ if (wizard && cheat_xtra)
+ {
+ msg_print(file_name);
+ }
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_FILE, file_name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* Failed */
+ if (!fp) return ( -1);
+
+ /* Monster number we are looking for */
+ mnum = m_ptr->r_idx;
+
+ /* Find matching N: line */
+ while (1)
+ {
+ int n;
+
+ /* Read a line */
+ if (my_fgets(fp, buf, 90) != 0)
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Not a N: line */
+ if (buf[0] != 'N') continue;
+
+ /* Skip "N:" and parse off a number */
+ sscanf(buf + 2, "%d", &n);
+
+ /* Match found */
+ if (n == mnum) break;
+ }
+
+ /* Retrieve number of normal messages */
+ while (1)
+ {
+ /* Read next line */
+ if (my_fgets(fp, buf, 90) != 0)
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* The first line not beginning with 'N:' holds number of lines */
+ if (buf[0] != 'N')
+ {
+ num_entries = atoi(buf);
+ break;
+ }
+ }
+
+ /* The monster is afraid */
+ if (m_ptr->monfear)
+ {
+ /* Read past normal lines */
+ for (line = 0; line < num_entries + 1; line++)
+ {
+ if (my_fgets(fp, buf, 90))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+ }
+
+ /* Retrieve number of 'afraid' lines */
+ num_entries = atoi(buf);
+ }
+
+
+ /* Pick a random line */
+ line = rand_int(num_entries);
+
+ /* test and DEBUG hack */
+ if (wizard && cheat_xtra)
+ {
+ sprintf(buf, "Line number %d", line);
+ msg_print(buf);
+ }
+
+ /* Find the selected line */
+ for (i = 0; i <= line; i++)
+ {
+ /* Oops */
+ if (0 != my_fgets(fp, buf, 90))
+ {
+ my_fclose(fp);
+ return ( -1);
+ }
+
+ /* Found it */
+ if (i == line) break;
+ }
+
+ /* Copy it to the output buffer */
+ strcpy(output, buf);
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Success */
+ return (0);
+}
diff --git a/src/files.h b/src/files.h
new file mode 100644
index 00000000..e3df5636
--- /dev/null
+++ b/src/files.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "h-basic.h"
+
+// C linkage required for these functions since main-* code uses them.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern bool_ txt_to_html(cptr head, cptr food, cptr base, cptr ext, bool_ force, bool_ recur);
+extern void process_player_name(bool_ sf);
+extern void do_cmd_save_game(void);
+extern void predict_score_gui(bool_ *initialized, bool_ *game_in_progress);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/files.hpp b/src/files.hpp
new file mode 100644
index 00000000..52206d12
--- /dev/null
+++ b/src/files.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "h-basic.h"
+#include "monster_type_fwd.hpp"
+
+extern void html_screenshot(cptr name);
+extern void help_file_screenshot(cptr name);
+extern void player_flags(u32b* f1, u32b* f2, u32b* f3, u32b* f4, u32b* f5, u32b* esp);
+extern void wipe_saved(void);
+extern s16b tokenize(char *buf, s16b num, char **tokens, char delim1, char delim2);
+extern void display_player(int mode);
+extern cptr describe_player_location(void);
+extern errr file_character(cptr name, bool_ full);
+extern errr process_pref_file_aux(char *buf);
+extern errr process_pref_file(cptr name);
+extern 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 get_name(void);
+extern void do_cmd_suicide(void);
+extern void autosave_checkpoint();
+extern void close_game(void);
+extern errr get_rnd_line(const char * file_name, char * output);
+extern char *get_line(const char* fname, cptr fdir, char *linbuf, int line);
+extern void race_legends(void);
+extern void show_highclass(int building);
+extern errr get_xtra_line(const char * file_name, monster_type *m_ptr, char * output);
diff --git a/src/flags_group.hpp b/src/flags_group.hpp
new file mode 100644
index 00000000..bdf5216b
--- /dev/null
+++ b/src/flags_group.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * For level gaining artifacts
+ */
+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 */
+};
diff --git a/src/gen_evol.cc b/src/gen_evol.cc
new file mode 100644
index 00000000..6f3fbcba
--- /dev/null
+++ b/src/gen_evol.cc
@@ -0,0 +1,160 @@
+/*
+ * 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 "gen_evol.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "feature_type.hpp"
+#include "generate.hpp"
+#include "levels.hpp"
+#include "player_type.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+/*
+ * 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_idxs.empty()) || 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_idxs.empty()) || c_ptr->m_idx) continue;
+
+ /* Avoid evolving player grid */
+ if ((j == p_ptr->py) && (i == p_ptr->px)) continue;
+
+
+ /* Reset tally */
+ c = 0;
+
+ /* Count number of surrounding walls */
+ for (x = i - 1; x <= i + 1; x++)
+ {
+ for (y = j - 1; y <= j + 1; y++)
+ {
+ if ((x == i) && (y == j)) continue;
+ if (f_info[cave[y][x].feat].flags1 & FF1_WALL) c++;
+ }
+ }
+
+ /*
+ * Changed these parameters a bit, so that it doesn't produce
+ * too open or too narrow levels -- pelpel
+ */
+ /* Starved or suffocated */
+ if ((c < 4) || (c >= 7))
+ {
+ if (f_info[c_ptr->feat].flags1 & FF1_WALL)
+ {
+ place_floor(j, i);
+ }
+ }
+
+ /* Spawned */
+ else if ((c == 4) || (c == 5))
+ {
+ if (!(f_info[c_ptr->feat].flags1 & FF1_WALL))
+ {
+ place_filler(j, i);
+ }
+ }
+ }
+ }
+
+ /* Notice changes */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_FLOW | PU_MON_LITE);
+}
+
+
+bool_ level_generate_life()
+{
+ int i, j;
+
+ for (i = 1; i < cur_wid - 1; i++)
+ {
+ for (j = 1; j < cur_hgt - 1; j++)
+ {
+ cave[j][i].info = (CAVE_ROOM | CAVE_GLOW | CAVE_MARK);
+ if (magik(45)) place_floor(j, i);
+ else place_filler(j, i);
+ }
+ }
+
+ evolve_level(FALSE);
+ evolve_level(FALSE);
+ evolve_level(FALSE);
+
+ /* Determine the character location */
+ if (!new_player_spot(get_branch()))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/gen_evol.hpp b/src/gen_evol.hpp
new file mode 100644
index 00000000..65b1320d
--- /dev/null
+++ b/src/gen_evol.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern bool_ level_generate_life();
+extern void evolve_level(bool_ noise);
diff --git a/src/gen_maze.cc b/src/gen_maze.cc
new file mode 100644
index 00000000..22722e0a
--- /dev/null
+++ b/src/gen_maze.cc
@@ -0,0 +1,294 @@
+/*
+ * 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 "gen_evol.hpp"
+
+#include "cave.hpp"
+#include "generate.hpp"
+#include "levels.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <memory>
+
+/*
+ * 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];
+
+static void dig(maze_row *maze, int y, int x, int d)
+{
+ int k;
+ int dy = 0, dx = 0;
+
+ /*
+ * first, open the wall of the new cell
+ * in the direction we come from.
+ */
+ switch (d)
+ {
+ case 0:
+ {
+ maze[y][x] |= 4;
+ break;
+ }
+
+ case 1:
+ {
+ maze[y][x] |= 8;
+ break;
+ }
+
+ case 2:
+ {
+ maze[y][x] |= 1;
+ break;
+ }
+
+ case 3:
+ {
+ maze[y][x] |= 2;
+ break;
+ }
+ }
+
+ /*
+ * try to chage direction, here 50% times.
+ * with smaller values (say 25%) the maze
+ * is made of long straight corridors. with
+ * greaters values (say 75%) the maze is
+ * very "turny".
+ */
+ if (rand_range(1, 100) < 50) d = rand_range(0, 3);
+
+ for (k = 1; k <= 4; k++)
+ {
+ switch (d)
+ {
+ case 0:
+ {
+ dy = 0;
+ dx = 1;
+ break;
+ }
+
+ case 1:
+ {
+ dy = -1;
+ dx = 0;
+ break;
+ }
+
+ case 2:
+ {
+ dy = 0;
+ dx = -1;
+ break;
+ }
+
+ case 3:
+ {
+ dy = 1;
+ dx = 0;
+ break;
+ }
+ }
+
+ if (maze[y + dy][x + dx] == 0)
+ {
+ /*
+ * now, open the wall of the new cell
+ * in the direction we go to.
+ */
+ switch (d)
+ {
+ case 0:
+ {
+ maze[y][x] |= 1;
+ break;
+ }
+
+ case 1:
+ {
+ maze[y][x] |= 2;
+ break;
+ }
+
+ case 2:
+ {
+ maze[y][x] |= 4;
+ break;
+ }
+
+ case 3:
+ {
+ maze[y][x] |= 8;
+ break;
+ }
+ }
+
+ dig(maze, y + dy, x + dx, d);
+ }
+
+ d = (d + 1) % 4;
+ }
+}
+
+
+bool_ level_generate_maze()
+{
+ int i, j, d;
+ int y, dy = 0;
+ int x, dx = 0;
+ int m_1 = 0, m_2 = 0;
+
+ /* Allocate temporary memory */
+ std::unique_ptr<maze_row[]> maze(new maze_row[(MAX_HGT / 2) + 2]);
+
+ /*
+ * 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.get(), 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);
+ }
+ }
+ }
+
+ /* Determine the character location */
+ if (!new_player_spot(get_branch()))
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/src/gen_maze.hpp b/src/gen_maze.hpp
new file mode 100644
index 00000000..28c092e3
--- /dev/null
+++ b/src/gen_maze.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern bool_ level_generate_maze();
diff --git a/src/generate.cc b/src/generate.cc
new file mode 100644
index 00000000..b5733682
--- /dev/null
+++ b/src/generate.cc
@@ -0,0 +1,8698 @@
+/*
+ * 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 "generate.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "dungeon_info_type.hpp"
+#include "feature_type.hpp"
+#include "hook_build_room1_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "levels.hpp"
+#include "loadsave.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "randart.hpp"
+#include "spells1.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "vault_type.hpp"
+#include "wild.hpp"
+#include "wilderness_map.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+#include <memory>
+#include <vector>
+
+#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)();
+
+ struct level_generator_type *next;
+};
+
+static level_generator_type *level_generators = NULL;
+
+/*
+ * Add a new generator
+ */
+void add_level_generator(cptr name, bool_ (*generator)())
+{
+ assert(name != nullptr);
+
+ level_generator_type *g = new level_generator_type;
+
+ g->name = strdup(name);
+ g->generator = generator;
+
+ 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))
+ {
+ /* 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;
+
+ /* 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 + rand_int(8));
+ }
+}
+
+
+
+/*
+ * Places some staircases near walls
+ */
+static void alloc_stairs(int feat, int num, int walls, int branch)
+{
+ int y, x, i, j, cnt;
+
+ /* Place "num" stairs */
+ for (cnt = 0, i = 0; i < num || (cnt < 1 && num > 1); i++)
+ {
+ /* Try several times, then decrease "walls" */
+ for (j = 0; j <= SAFE_MAX_ATTEMPTS; j++)
+ {
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ place_new_way(&y, &x);
+ }
+ else
+ {
+ /* Pick a random grid */
+ y = rand_int(cur_hgt);
+ x = rand_int(cur_wid);
+
+ /* Require "naked" floor grid */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* Require a certain number of adjacent walls */
+ if (next_to_walls(y, x) < walls) continue;
+ }
+
+ /* Town -- must go down */
+ if (!dun_level)
+ {
+ /* Clear previous contents, add down stairs */
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(y, x, FEAT_WAY_MORE);
+ }
+ else if ((rand_int(3) == 0) && (!(dungeon_flags2 & DF2_NO_SHAFT)))
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_DOWN);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_MORE);
+ }
+ }
+
+ /* Quest -- must go up */
+ else if ((is_quest(dun_level) && (dun_level >= 1)) ||
+ ((dun_level >= d_info[dungeon_type].maxdepth) &&
+ (!(dungeon_flags1 & DF1_FORCE_DOWN))))
+ {
+ /* Clear previous contents, add up stairs */
+ if (dungeon_flags1 & DF1_FLAT)
+ {
+ cave_set_feat(y, x, FEAT_WAY_LESS);
+ }
+ else if ((rand_int(3) == 0) && (!(dungeon_flags2 & DF2_NO_SHAFT)))
+ {
+ cave_set_feat(y, x, FEAT_SHAFT_UP);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_LESS);
+ }
+ }
+
+ /* Requested type */
+ else
+ {
+ /* Clear previous contents, add stairs */
+ cave_set_feat(y, x, feat);
+ }
+
+ cave[y][x].special = branch;
+
+ /* Count the number of stairs we've actually managed to place. */
+ cnt++;
+
+ /* All done */
+ break;
+ }
+
+ /* Require fewer walls */
+ if (walls) walls--;
+ }
+}
+
+
+
+
+/*
+ * Allocates some objects (using "place" and "type")
+ */
+static void alloc_object(int set, int typ, int num)
+{
+ int y = 1, x = 1, k;
+ int dummy = 0;
+
+ /* Place some objects */
+ for (k = 0; k < num; k++)
+ {
+ /* Pick a "legal" spot */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ bool_ room;
+
+ dummy++;
+
+ /* Location */
+ y = rand_int(cur_hgt);
+ x = rand_int(cur_wid);
+
+ /* Require "naked" floor grid */
+ if (!cave_naked_bold(y, x)) continue;
+
+ /* Check for "room" */
+ room = (cave[y][x].info & (CAVE_ROOM)) ? TRUE : FALSE;
+
+ /* Require corridor? */
+ if ((set == ALLOC_SET_CORR) && room) continue;
+
+ /* Require room? */
+ if ((set == ALLOC_SET_ROOM) && !room) continue;
+
+ /* Accept it */
+ break;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_format("Warning! Could not place object, type : %d!", typ);
+ }
+
+ return;
+ }
+
+
+ /* Place something */
+ switch (typ)
+ {
+ case ALLOC_TYP_RUBBLE:
+ {
+ place_rubble(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_TRAP:
+ {
+ place_trap(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_GOLD:
+ {
+ place_gold(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_OBJECT:
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ break;
+ }
+
+ case ALLOC_TYP_ALTAR:
+ {
+ place_altar(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_BETWEEN:
+ {
+ place_between(y, x);
+ break;
+ }
+
+ case ALLOC_TYP_FOUNTAIN:
+ {
+ place_fountain(y, x);
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * The following functions create a rectangle (e.g. outer wall of rooms)
+ */
+void build_rectangle(int y1, int x1, int y2, int x2, int feat, int info)
+{
+ int y, x;
+
+ /* Top and bottom boundaries */
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y1, x, feat);
+ cave[y1][x].info |= (info);
+
+ cave_set_feat(y2, x, feat);
+ cave[y2][x].info |= (info);
+ }
+
+ /* Top and bottom boundaries */
+ for (y = y1; y <= y2; y++)
+ {
+ cave_set_feat(y, x1, feat);
+ cave[y][x1].info |= (info);
+
+ cave_set_feat(y, x2, feat);
+ cave[y][x2].info |= (info);
+ }
+}
+
+
+/*
+ * Place water through the dungeon using recursive fractal algorithm
+ *
+ * Why do those good at math and/or algorithms tend *not* to
+ * place any spaces around binary operators? I've been always
+ * wondering. This seems almost a unversal phenomenon...
+ * Tried to make those conform to the rule, but there may still
+ * some left untouched...
+ */
+static void recursive_river(int x1, int y1, int x2, int y2,
+ int feat1, int feat2, int width)
+{
+ int dx, dy, length, l, x, y;
+ int changex, changey;
+ int ty, tx;
+
+
+ length = distance(x1, y1, x2, y2);
+
+ if (length > 4)
+ {
+ /*
+ * Divide path in half and call routine twice.
+ * There is a small chance of splitting the river
+ */
+ dx = (x2 - x1) / 2;
+ dy = (y2 - y1) / 2;
+
+ if (dy != 0)
+ {
+ /* perturbation perpendicular to path */
+ changex = randint(abs(dy)) * 2 - abs(dy);
+ }
+ else
+ {
+ changex = 0;
+ }
+
+ if (dx != 0)
+ {
+ /* perturbation perpendicular to path */
+ changey = randint(abs(dx)) * 2 - abs(dx);
+ }
+ else
+ {
+ changey = 0;
+ }
+
+
+
+ /* construct river out of two smaller ones */
+ recursive_river(x1, y1, x1 + dx + changex, y1 + dy + changey,
+ feat1, feat2, width);
+ recursive_river(x1 + dx + changex, y1 + dy + changey, x2, y2,
+ feat1, feat2, width);
+
+ /* Split the river some of the time -junctions look cool */
+ if ((width > 0) && (rand_int(DUN_WAT_CHG) == 0))
+ {
+ recursive_river(x1 + dx + changex, y1 + dy + changey,
+ x1 + 8 * (dx + changex), y1 + 8 * (dy + changey),
+ feat1, feat2, width - 1);
+ }
+ }
+
+ /* Actually build the river */
+ else
+ {
+ for (l = 0; l < length; l++)
+ {
+ x = x1 + l * (x2 - x1) / length;
+ y = y1 + l * (y2 - y1) / length;
+
+ for (ty = y - width - 1; ty <= y + width + 1; ty++)
+ {
+ for (tx = x - width - 1; tx <= x + width + 1; tx++)
+ {
+ if (!in_bounds(ty, tx)) continue;
+
+ if (cave[ty][tx].feat == feat1) continue;
+ if (cave[ty][tx].feat == feat2) continue;
+
+ if (distance(ty, tx, y, x) > rand_spread(width, 1)) continue;
+
+ /* Do not convert permanent features */
+ if (cave_perma_bold(ty, tx)) continue;
+
+ /*
+ * Clear previous contents, add feature
+ * The border mainly gets feat2, while the center
+ * gets feat1
+ */
+ if (distance(ty, tx, y, x) > width)
+ {
+ cave_set_feat(ty, tx, feat2);
+ }
+ else
+ {
+ cave_set_feat(ty, tx, feat1);
+ }
+
+ /* Lava terrain glows */
+ if ((feat1 == FEAT_DEEP_LAVA) ||
+ (feat1 == FEAT_SHAL_LAVA))
+ {
+ cave[ty][tx].info |= CAVE_GLOW;
+ }
+
+ /* Hack -- don't teleport here */
+ cave[ty][tx].info |= CAVE_ICKY;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Places water through dungeon.
+ */
+static void add_river(int feat1, int feat2)
+{
+ int y2, x2;
+ int y1 = 0, x1 = 0, wid;
+
+
+ /* Hack -- Choose starting point */
+ y2 = randint(cur_hgt / 2 - 2) + cur_hgt / 2;
+ x2 = randint(cur_wid / 2 - 2) + cur_wid / 2;
+
+ /* Hack -- Choose ending point somewhere on boundary */
+ switch (randint(4))
+ {
+ case 1:
+ {
+ /* top boundary */
+ x1 = randint(cur_wid - 2) + 1;
+ y1 = 1;
+ break;
+ }
+ case 2:
+ {
+ /* left boundary */
+ x1 = 1;
+ y1 = randint(cur_hgt - 2) + 1;
+ break;
+ }
+ case 3:
+ {
+ /* right boundary */
+ x1 = cur_wid - 1;
+ y1 = randint(cur_hgt - 2) + 1;
+ break;
+ }
+ case 4:
+ {
+ /* bottom boundary */
+ x1 = randint(cur_wid - 2) + 1;
+ y1 = cur_hgt - 1;
+ break;
+ }
+ }
+ wid = randint(DUN_WAT_RNG);
+ recursive_river(x1, y1, x2, y2, feat1, feat2, wid);
+}
+
+
+/*
+ * Places "streamers" of rock through dungeon
+ *
+ * Note that their are actually six different terrain features used
+ * to represent streamers. Three each of magma and quartz, one for
+ * basic vein, one with hidden gold, and one with known gold. The
+ * hidden gold types are currently unused.
+ */
+static void build_streamer(int feat, int chance)
+{
+ int i, tx, ty;
+ int y, x, dir;
+ int dummy = 0;
+ cave_type *c_ptr;
+
+
+ /* Hack -- Choose starting point */
+ y = rand_spread(cur_hgt / 2, 10);
+ x = rand_spread(cur_wid / 2, 15);
+
+ /* Choose a random compass direction */
+ dir = ddd[rand_int(8)];
+
+ /* Place streamer into dungeon */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ dummy++;
+
+ /* One grid per density */
+ for (i = 0; i < DUN_STR_DEN; i++)
+ {
+ int d = DUN_STR_RNG;
+
+ /* Pick a nearby grid */
+ while (1)
+ {
+ ty = rand_spread(y, d);
+ tx = rand_spread(x, d);
+ if (!in_bounds2(ty, tx)) continue;
+ break;
+ }
+
+ /* Access the grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Only convert "granite" walls */
+ if ((c_ptr->feat != feat_wall_inner) &&
+ (c_ptr->feat != feat_wall_outer) &&
+ (c_ptr->feat != d_info[dungeon_type].fill_type1) &&
+ (c_ptr->feat != d_info[dungeon_type].fill_type2) &&
+ (c_ptr->feat != d_info[dungeon_type].fill_type3)) continue;
+
+ /* Clear mimic feature to avoid nasty consequences */
+ c_ptr->mimic = 0;
+
+ /* Clear previous contents, add proper vein type */
+ cave_set_feat(ty, tx, feat);
+
+ /* Hack -- Add some (known) treasure */
+ if (rand_int(chance) == 0)
+ {
+ cave_set_feat(ty, tx, c_ptr->feat + 0x04);
+ }
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place streamer!");
+ }
+ return;
+ }
+
+
+ /* Advance the streamer */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Quit before leaving the dungeon */
+ if (!in_bounds(y, x)) break;
+ }
+}
+
+
+
+/*
+ * Place streams of water, lava, & trees -KMW-
+ * This routine varies the placement based on dungeon level
+ * otherwise is similar to build_streamer
+ */
+static void build_streamer2(int feat, int killwall)
+{
+ int i, j, mid, tx, ty;
+ int y, x, dir;
+ int poolchance;
+ int poolsize;
+ cave_type *c_ptr;
+
+ poolchance = randint(10);
+
+ /* Hack -- Choose starting point */
+ y = rand_spread(cur_hgt / 2, 10);
+ x = rand_spread(cur_wid / 2, 15);
+
+ /* Choose a random compass direction */
+ dir = ddd[rand_int(8)];
+
+ /* Place streamer into dungeon */
+ if (poolchance > 2)
+ {
+ while (TRUE)
+ {
+ /* One grid per density */
+ for (i = 0; i < (DUN_STR_DWLW + 1); i++)
+ {
+ int d = DUN_STR_WLW;
+
+ /* Pick a nearby grid */
+ while (1)
+ {
+ ty = rand_spread(y, d);
+ tx = rand_spread(x, d);
+ if (in_bounds(ty, tx)) break;
+ }
+
+ /* Access grid */
+ c_ptr = &cave[ty][tx];
+
+ /* Never convert vaults */
+ if (c_ptr->info & (CAVE_ICKY)) continue;
+
+ /* Reject permanent features */
+ if ((f_info[c_ptr->feat].flags1 & (FF1_PERMANENT)) &&
+ (f_info[c_ptr->feat].flags1 & (FF1_FLOOR))) continue;
+
+ /* Avoid converting walls when told so */
+ if (killwall == 0)
+ {
+ if (f_info[c_ptr->feat].flags1 & FF1_WALL) continue;
+ }
+
+ /* Clear mimic feature to avoid nasty consequences */
+ c_ptr->mimic = 0;
+
+ /* Clear previous contents, add proper vein type */
+ cave_set_feat(ty, tx, feat);
+ }
+
+ /* Advance the streamer */
+ y += ddy[dir];
+ x += ddx[dir];
+
+ /* Change direction */
+ if (rand_int(20) == 0) dir = ddd[rand_int(8)];
+
+ /* Stop at dungeon edge */
+ if (!in_bounds(y, x)) break;
+ }
+ }
+
+ /* Create pool */
+ else if ((feat == FEAT_DEEP_WATER) || (feat == FEAT_DEEP_LAVA))
+ {
+ poolsize = 5 + randint(10);
+ mid = poolsize / 2;
+
+ /* One grid per density */
+ for (i = 0; i < poolsize; i++)
+ {
+ for (j = 0; j < poolsize; j++)
+ {
+ tx = x + j;
+ ty = y + i;
+
+ if (!in_bounds(ty, tx)) continue;
+
+ if (i < mid)
+ {
+ if (j < mid)
+ {
+ if ((i + j + 1) < mid)
+ continue;
+ }
+ else if (j > (mid + i))
+ continue;
+ }
+ else if (j < mid)
+ {
+ if (i > (mid + j))
+ continue;
+ }
+ else if ((i + j) > ((mid * 3)-1))
+ continue;
+
+ /* Only convert non-permanent features */
+ if (f_info[cave[ty][tx].feat].flags1 & FF1_PERMANENT) continue;
+
+ /* Clear mimic feature to avoid nasty consequences */
+ cave[ty][tx].mimic = 0;
+
+ /* Clear previous contents, add proper vein type */
+ cave_set_feat(ty, tx, feat);
+ }
+ }
+ }
+}
+
+
+
+/*
+ * Build a destroyed level
+ */
+static void destroy_level(void)
+{
+ int y1, x1, y, x, k, t, n;
+
+ cave_type *c_ptr;
+
+ /* Note destroyed levels */
+ if ((cheat_room) || (p_ptr->precognition)) msg_print("Destroyed Level");
+
+ /* Drop a few epi-centers (usually about two) */
+ for (n = 0; n < randint(5); n++)
+ {
+ /* Pick an epi-center */
+ x1 = rand_range(5, cur_wid - 1 - 5);
+ y1 = rand_range(5, cur_hgt - 1 - 5);
+
+ /* Big area of affect */
+ for (y = (y1 - 15); y <= (y1 + 15); y++)
+ {
+ for (x = (x1 - 15); x <= (x1 + 15); x++)
+ {
+ /* Skip illegal grids */
+ if (!in_bounds(y, x)) continue;
+
+ /* Extract the distance */
+ k = distance(y1, x1, y, x);
+
+ /* Stay in the circle of death */
+ if (k >= 16) continue;
+
+ /* Delete the monster (if any) */
+ delete_monster(y, x);
+
+ /* Destroy valid grids */
+ if (cave_valid_bold(y, x))
+ {
+ /* Delete objects */
+ delete_object(y, x);
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Wall (or floor) type */
+ t = rand_int(200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 60)
+ {
+ /* Create quartz vein */
+ cave_set_feat(y, x, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 90)
+ {
+ /* Create magma vein */
+ cave_set_feat(y, x, FEAT_MAGMA);
+ }
+
+ /* Sand */
+ else if (t < 110)
+ {
+ /* Create sand vein */
+ cave_set_feat(y, x, FEAT_SANDWALL);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ place_floor(y, x);
+ }
+
+ /* No longer part of a room or vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* No longer illuminated or known */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Function that sees if a square is a floor (Includes range checking)
+ */
+static bool_ get_is_floor(int x, int y)
+{
+ /* Out of bounds */
+ if (!in_bounds(y, x)) return (FALSE);
+
+ /* Do the real check: */
+ if (f_info[cave[y][x].feat].flags1 & FF1_FLOOR) return (TRUE);
+
+ return (FALSE);
+}
+
+
+/*
+ * Tunnel around a room if it will cut off part of a cave system
+ */
+static void check_room_boundary(int x1, int y1, int x2, int y2)
+{
+ int count, x, y;
+ bool_ old_is_floor, new_is_floor;
+
+ /* Avoid doing this in irrelevant places -- pelpel */
+ if (!(dungeon_flags1 & DF1_CAVERN)) return;
+
+ /* Initialize */
+ count = 0;
+
+ old_is_floor = get_is_floor(x1 - 1, y1);
+
+ /*
+ * Count the number of floor-wall boundaries around the room
+ * Note: diagonal squares are ignored since the player can move diagonally
+ * to bypass these if needed.
+ */
+
+ /* Above the top boundary */
+ for (x = x1; x <= x2; x++)
+ {
+ new_is_floor = get_is_floor(x, y1 - 1);
+
+ /* increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+ /* Right boundary */
+ for (y = y1; y <= y2; y++)
+ {
+ new_is_floor = get_is_floor(x2 + 1, y);
+
+ /* increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+ /* Bottom boundary*/
+ for (x = x2; x >= x1; x--)
+ {
+ new_is_floor = get_is_floor(x, y2 + 1);
+
+ /* Increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+ /* Left boundary */
+ for (y = y2; y >= y1; y--)
+ {
+ new_is_floor = get_is_floor(x1 - 1, y);
+
+ /* Increment counter if they are different */
+ if (new_is_floor != old_is_floor) count++;
+
+ old_is_floor = new_is_floor;
+ }
+
+
+ /* If all the same, or only one connection exit */
+ if ((count == 0) || (count == 2)) return;
+
+
+ /* Tunnel around the room so to prevent problems with caves */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ if (in_bounds(y, x)) place_floor(y, x);
+ }
+ }
+}
+
+
+/*
+ * Create up to "num" objects near the given coordinates
+ * Only really called by some of the "vault" routines.
+ */
+static void vault_objects(int y, int x, int num)
+{
+ int dummy = 0;
+ int i = 0, j = y, k = x;
+
+
+ /* Attempt to place 'num' objects */
+ for (; num > 0; --num)
+ {
+ /* Try up to 11 spots looking for empty space */
+ for (i = 0; i < 11; ++i)
+ {
+ /* Pick a random location */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ j = rand_spread(y, 2);
+ k = rand_spread(x, 3);
+ dummy++;
+ if (in_bounds(j, k)) break;
+ }
+
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place vault object!");
+ }
+ }
+
+
+ /* Require "clean" floor space */
+ if (!cave_clean_bold(j, k)) continue;
+
+ /* Place an item */
+ if (rand_int(100) < 75)
+ {
+ place_object(j, k, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+
+ /* Place gold */
+ else
+ {
+ place_gold(j, k);
+ }
+
+ /* Placement accomplished */
+ break;
+ }
+ }
+}
+
+
+/*
+ * Place a trap with a given displacement of point
+ */
+static void vault_trap_aux(int y, int x, int yd, int xd)
+{
+ int count = 0, y1 = y, x1 = x;
+ int dummy = 0;
+
+ /* Place traps */
+ for (count = 0; count <= 5; count++)
+ {
+ /* Get a location */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ y1 = rand_spread(y, yd);
+ x1 = rand_spread(x, xd);
+ dummy++;
+ if (in_bounds(y1, x1)) break;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place vault trap!");
+ }
+ }
+
+
+ /* Require "naked" floor grids */
+ if (!cave_naked_bold(y1, x1)) continue;
+
+ /* Place the trap */
+ place_trap(y1, x1);
+
+ /* Done */
+ break;
+ }
+}
+
+
+/*
+ * Place some traps with a given displacement of given location
+ */
+static void vault_traps(int y, int x, int yd, int xd, int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ {
+ vault_trap_aux(y, x, yd, xd);
+ }
+}
+
+
+/*
+ * Hack -- Place some sleeping monsters near the given location
+ */
+static void vault_monsters(int y1, int x1, int num)
+{
+ int k, i, y, x;
+
+ /* Try to summon "num" monsters "near" the given location */
+ for (k = 0; k < num; k++)
+ {
+ /* Try nine locations */
+ for (i = 0; i < 9; i++)
+ {
+ int d = 1;
+
+ /* Pick a nearby location */
+ scatter(&y, &x, y1, x1, d);
+
+ /* Require "empty" floor grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Place the monster (allow groups) */
+ monster_level = dun_level + 2;
+ (void)place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ }
+}
+
+/*
+ * Allocate the space needed by a room in the room_map array.
+ *
+ * width, height represent the size of the room (0...x-1) by (0...y-1).
+ * crowded is used to denote a monset nest.
+ * by0, bx0 are the positions in the room_map array given to the build_type'x'
+ * function.
+ * cx, cy are the returned center of the allocated room in coordinates for
+ * cave.feat and cave.info etc.
+ */
+bool_ room_alloc(int width, int height, bool_ crowded, int by0, int bx0, int *cx, int *cy)
+{
+ int temp, eby, ebx, by, bx;
+
+ /* Calculate number of room_map squares to allocate */
+
+ /* Total number along width */
+ temp = ((width - 1) / BLOCK_WID) + 1;
+
+ for (ebx = bx0 + temp; bx0 > 0 && ebx > dun->col_rooms; bx0--, ebx--);
+
+ if (ebx > dun->col_rooms) return (FALSE);
+
+ /* Total number along height */
+ temp = ((height - 1) / BLOCK_HGT) + 1;
+
+ for (eby = by0 + temp; by0 > 0 && eby > dun->row_rooms; by0--, eby--);
+
+ /* Never run off the screen */
+ if (eby > dun->row_rooms) return (FALSE);
+
+ /* Verify open space */
+ for (by = by0; by < eby; by++)
+ {
+ for (bx = bx0; bx < ebx; bx++)
+ {
+ if (dun->room_map[by][bx]) return (FALSE);
+ }
+ }
+
+ /*
+ * It is *extremely* important that the following calculation
+ * be *exactly* correct to prevent memory errors XXX XXX XXX
+ */
+
+ /* Acquire the location of the room */
+ *cy = ((by0 + eby) * BLOCK_HGT) / 2;
+ *cx = ((bx0 + ebx) * BLOCK_WID) / 2;
+
+ /* Save the room location */
+ if (dun->cent_n < CENT_MAX)
+ {
+ dun->cent[dun->cent_n].y = *cy;
+ dun->cent[dun->cent_n].x = *cx;
+ dun->cent_n++;
+ }
+
+ /* Reserve some blocks */
+ for (by = by0; by < eby; by++)
+ {
+ for (bx = bx0; bx < ebx; bx++)
+ {
+ dun->room_map[by][bx] = TRUE;
+ }
+ }
+
+ /* Count "crowded" rooms */
+ if (crowded) dun->crowded = TRUE;
+
+ /*
+ * Hack -- See if room will cut off a cavern.
+ * If so, fix by tunneling outside the room in such a way as
+ * to conect the caves.
+ */
+ check_room_boundary(*cx - width / 2 - 1, *cy - height / 2 - 1,
+ *cx + width / 2 + 1, *cy + height / 2 + 1);
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * Room building routines.
+ *
+ * Room types:
+ * 1 -- normal
+ * 2 -- overlapping
+ * 3 -- cross shaped
+ * 4 -- large room with features
+ * 5 -- monster nests
+ * 6 -- monster pits
+ * 7 -- simple vaults
+ * 8 -- greater vaults
+ * 9 -- circular rooms
+ */
+
+/*
+ * Type 1 -- normal rectangular rooms
+ */
+static void build_type1(int by0, int bx0)
+{
+ u16b info;
+ int y, x = 1, y2, x2, yval, xval;
+ int y1, x1, xsize, ysize;
+
+ /* Pick a room size */
+ y1 = rand_range(1, 4);
+ x1 = rand_range(1, 10);
+ y2 = rand_range(1, 3);
+ x2 = rand_range(1, 9);
+
+ xsize = x1 + x2;
+ ysize = y1 + y2;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* Get corner values */
+ y1 = yval - ysize / 2;
+ x1 = xval - xsize / 2;
+ y2 = y1 + ysize - 1;
+ x2 = x1 + xsize - 1;
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place a full floor under the room */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Walls around the room */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, info);
+
+ /* Hack -- Occasional pillar room */
+ if (ysize > 2 && xsize > 2)
+ {
+ if (rand_int(20) == 0)
+ {
+ for (y = y1; y <= y2; y += 2)
+ {
+ for (x = x1; x <= x2; x += 2)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Hack -- Occasional ragged-edge room */
+ else if (rand_int(50) == 0)
+ {
+ for (y = y1 + 2; y <= y2 - 2; y += 2)
+ {
+ cave_set_feat(y, x1, feat_wall_inner);
+ cave_set_feat(y, x2, feat_wall_inner);
+ }
+ for (x = x1 + 2; x <= x2 - 2; x += 2)
+ {
+ cave_set_feat(y1, x, feat_wall_inner);
+ cave_set_feat(y2, x, feat_wall_inner);
+ }
+ }
+ }
+}
+
+/*
+ * Type 2 -- Overlapping rectangular rooms
+ */
+static void build_type2(int by0, int bx0)
+{
+ u16b info;
+ int y, x, yval, xval;
+ int y1a, x1a, y2a, x2a;
+ int y1b, x1b, y2b, x2b;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* Determine extents of the first room */
+ y1a = yval - randint(4);
+ y2a = yval + randint(3);
+ x1a = xval - randint(14);
+ x2a = xval + randint(6);
+
+ /* Determine extents of the second room */
+ y1b = yval - randint(3);
+ y2b = yval + randint(4);
+ x1b = xval - randint(6);
+ x2b = xval + randint(14);
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1a - 1, x1a - 1, y2a + 1, x2a + 1, feat_wall_outer, info);
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1b - 1, x1b - 1, y2b + 1, x2b + 1, feat_wall_outer, info);
+
+ /* Replace the floor for room "a" */
+ for (y = y1a; y <= y2a; y++)
+ {
+ for (x = x1a; x <= x2a; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Replace the floor for room "b" */
+ for (y = y1b; y <= y2b; y++)
+ {
+ for (x = x1b; x <= x2b; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+}
+
+/*
+ * Type 3 -- Cross shaped rooms
+ *
+ * Builds a room at a row, column coordinate
+ *
+ * Room "a" runs north/south, and Room "b" runs east/east
+ * So the "central pillar" runs from x1a,y1b to x2a,y2b.
+ *
+ * Note that currently, the "center" is always 3x3, but I think that
+ * the code below will work (with "bounds checking") for 5x5, or even
+ * for unsymetric values like 4x3 or 5x3 or 3x4 or 3x5, or even larger.
+ */
+static void build_type3(int by0, int bx0)
+{
+ u16b info;
+ int y, x, dy, dx, wy, wx;
+ int y1a, x1a, y2a, x2a;
+ int y1b, x1b, y2b, x2b;
+ int yval, xval;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* For now, always 3x3 */
+ wx = wy = 1;
+
+ /* Pick max vertical size (at most 4) */
+ dy = rand_range(3, 4);
+
+ /* Pick max horizontal size (at most 11) */
+ dx = rand_range(3, 11);
+
+ /* Determine extents of the north/south room */
+ y1a = yval - dy;
+ y2a = yval + dy;
+ x1a = xval - wx;
+ x2a = xval + wx;
+
+ /* Determine extents of the east/west room */
+ y1b = yval - wy;
+ y2b = yval + wy;
+ x1b = xval - dx;
+ x2b = xval + dx;
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1a - 1, x1a - 1, y2a + 1, x2a + 1, feat_wall_outer, info);
+
+ /* Place the walls around room "a" */
+ build_rectangle(y1b - 1, x1b - 1, y2b + 1, x2b + 1, feat_wall_outer, info);
+
+ /* Replace the floor for room "a" */
+ for (y = y1a; y <= y2a; y++)
+ {
+ for (x = x1a; x <= x2a; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Replace the floor for room "b" */
+ for (y = y1b; y <= y2b; y++)
+ {
+ for (x = x1b; x <= x2b; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Special features (3/4) */
+ switch (rand_int(4))
+ {
+ /* Large solid middle pillar */
+ case 1:
+ {
+ for (y = y1b; y <= y2b; y++)
+ {
+ for (x = x1a; x <= x2a; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ break;
+ }
+
+ /* Inner treasure vault */
+ case 2:
+ {
+ /* Build the vault */
+ build_rectangle(y1b, x1a, y2b, x2a, feat_wall_inner, info);
+
+ /* Place a secret door on the inner room */
+ switch (rand_int(4))
+ {
+ case 0:
+ place_secret_door(y1b, xval);
+ break;
+ case 1:
+ place_secret_door(y2b, xval);
+ break;
+ case 2:
+ place_secret_door(yval, x1a);
+ break;
+ case 3:
+ place_secret_door(yval, x2a);
+ break;
+ }
+
+ /* Place a treasure in the vault */
+ place_object(yval, xval, FALSE, FALSE, OBJ_FOUND_FLOOR);
+
+ /* Let's guard the treasure well */
+ vault_monsters(yval, xval, rand_int(2) + 3);
+
+ /* Traps naturally */
+ vault_traps(yval, xval, 4, 4, rand_int(3) + 2);
+
+ break;
+ }
+
+ /* Something else */
+ case 3:
+ {
+ /* Occasionally pinch the center shut */
+ if (rand_int(3) == 0)
+ {
+ /* Pinch the east/west sides */
+ for (y = y1b; y <= y2b; y++)
+ {
+ if (y == yval) continue;
+ cave_set_feat(y, x1a - 1, feat_wall_inner);
+ cave_set_feat(y, x2a + 1, feat_wall_inner);
+ }
+
+ /* Pinch the north/south sides */
+ for (x = x1a; x <= x2a; x++)
+ {
+ if (x == xval) continue;
+ cave_set_feat(y1b - 1, x, feat_wall_inner);
+ cave_set_feat(y2b + 1, x, feat_wall_inner);
+ }
+
+ /* Sometimes shut using secret doors */
+ if (rand_int(3) == 0)
+ {
+ place_secret_door(yval, x1a - 1);
+ place_secret_door(yval, x2a + 1);
+ place_secret_door(y1b - 1, xval);
+ place_secret_door(y2b + 1, xval);
+ }
+ }
+
+ /* Occasionally put a "plus" in the center */
+ else if (rand_int(3) == 0)
+ {
+ cave_set_feat(yval, xval, feat_wall_inner);
+ cave_set_feat(y1b, xval, feat_wall_inner);
+ cave_set_feat(y2b, xval, feat_wall_inner);
+ cave_set_feat(yval, x1a, feat_wall_inner);
+ cave_set_feat(yval, x2a, feat_wall_inner);
+ }
+
+ /* Occasionally put a pillar in the center */
+ else if (rand_int(3) == 0)
+ {
+ cave_set_feat(yval, xval, feat_wall_inner);
+ }
+
+ break;
+ }
+ }
+}
+
+/*
+ * Type 4 -- Large room with inner features
+ *
+ * Possible sub-types:
+ * 1 - Just an inner room with one door
+ * 2 - An inner room within an inner room
+ * 3 - An inner room with pillar(s)
+ * 4 - Inner room has a maze
+ * 5 - A set of four inner rooms
+ */
+static void build_type4(int by0, int bx0)
+{
+ u16b info;
+ int y, x, y1, x1;
+ int y2, x2, tmp, yval, xval;
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, FALSE, by0, bx0, &xval, &yval)) return;
+
+ /* Large room */
+ y1 = yval - 4;
+ y2 = yval + 4;
+ x1 = xval - 11;
+ x2 = xval + 11;
+
+ info = (dun_level <= randint(25)) ? (CAVE_ROOM|CAVE_GLOW) : CAVE_ROOM;
+
+ /* Place a full floor under the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= info;
+ }
+ }
+
+ /* Outer Walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, info);
+
+ /* The inner room */
+ y1 = y1 + 2;
+ y2 = y2 - 2;
+ x1 = x1 + 2;
+ x2 = x2 - 2;
+
+ /* The inner walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_inner, info);
+
+ /* Inner room variations */
+ switch (randint(5))
+ {
+ /* Just an inner room with a monster */
+ case 1:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Place a monster in the room */
+ vault_monsters(yval, xval, 1);
+
+ break;
+ }
+
+ /* Treasure Vault (with a door) */
+ case 2:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Place another inner room */
+ build_rectangle(yval - 1, xval - 1, yval + 1, xval + 1,
+ feat_wall_inner, info);
+
+ /* Place a locked door on the inner room */
+ switch (randint(4))
+ {
+ case 1:
+ place_locked_door(yval - 1, xval);
+ break;
+ case 2:
+ place_locked_door(yval + 1, xval);
+ break;
+ case 3:
+ place_locked_door(yval, xval - 1);
+ break;
+ case 4:
+ place_locked_door(yval, xval + 1);
+ break;
+ }
+
+ /* Monsters to guard the "treasure" */
+ vault_monsters(yval, xval, randint(3) + 2);
+
+ /* Object (80%) */
+ if (rand_int(100) < 80)
+ {
+ place_object(yval, xval, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+
+ /* Stairs (20%) */
+ else
+ {
+ place_random_stairs(yval, xval);
+ }
+
+ /* Traps to protect the treasure */
+ vault_traps(yval, xval, 4, 10, 2 + randint(3));
+
+ break;
+ }
+
+ /* Inner pillar(s). */
+ case 3:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Large Inner Pillar */
+ for (y = yval - 1; y <= yval + 1; y++)
+ {
+ for (x = xval - 1; x <= xval + 1; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+
+ /* Occasionally, two more Large Inner Pillars */
+ if (rand_int(2) == 0)
+ {
+ tmp = randint(2);
+ for (y = yval - 1; y <= yval + 1; y++)
+ {
+ for (x = xval - 5 - tmp; x <= xval - 3 - tmp; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ for (x = xval + 3 + tmp; x <= xval + 5 + tmp; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Occasionally, some Inner rooms */
+ if (rand_int(3) == 0)
+ {
+ /* Long horizontal walls */
+ for (x = xval - 5; x <= xval + 5; x++)
+ {
+ cave_set_feat(yval - 1, x, feat_wall_inner);
+ cave_set_feat(yval + 1, x, feat_wall_inner);
+ }
+
+ /* Close off the left/right edges */
+ cave_set_feat(yval, xval - 5, feat_wall_inner);
+ cave_set_feat(yval, xval + 5, feat_wall_inner);
+
+ /* Secret doors (random top/bottom) */
+ place_secret_door(yval - 3 + (randint(2) * 2), xval - 3);
+ place_secret_door(yval - 3 + (randint(2) * 2), xval + 3);
+
+ /* Monsters */
+ vault_monsters(yval, xval - 2, randint(2));
+ vault_monsters(yval, xval + 2, randint(2));
+
+ /* Objects */
+ if (rand_int(3) == 0) place_object(yval, xval - 2, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ if (rand_int(3) == 0) place_object(yval, xval + 2, FALSE, FALSE, OBJ_FOUND_FLOOR);
+ }
+
+ break;
+ }
+
+ /* Maze inside. */
+ case 4:
+ {
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Maze (really a checkerboard) */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ if (0x1 & (x + y))
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Monsters just love mazes. */
+ vault_monsters(yval, xval - 5, randint(3));
+ vault_monsters(yval, xval + 5, randint(3));
+
+ /* Traps make them entertaining. */
+ vault_traps(yval, xval - 3, 2, 8, randint(3));
+ vault_traps(yval, xval + 3, 2, 8, randint(3));
+
+ /* Mazes should have some treasure too. */
+ vault_objects(yval, xval, 3);
+
+ break;
+ }
+
+ /* Four small rooms. */
+ case 5:
+ {
+ /* Inner "cross" */
+ for (y = y1; y <= y2; y++)
+ {
+ cave_set_feat(y, xval, feat_wall_inner);
+ }
+
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(yval, x, feat_wall_inner);
+ }
+
+ /* Doors into the rooms */
+ if (rand_int(100) < 50)
+ {
+ int i = randint(10);
+ place_secret_door(y1 - 1, xval - i);
+ place_secret_door(y1 - 1, xval + i);
+ place_secret_door(y2 + 1, xval - i);
+ place_secret_door(y2 + 1, xval + i);
+ }
+ else
+ {
+ int i = randint(3);
+ place_secret_door(yval + i, x1 - 1);
+ place_secret_door(yval - i, x1 - 1);
+ place_secret_door(yval + i, x2 + 1);
+ place_secret_door(yval - i, x2 + 1);
+ }
+
+ /* Treasure, centered at the center of the cross */
+ vault_objects(yval, xval, 2 + randint(2));
+
+ /* Gotta have some monsters. */
+ vault_monsters(yval + 1, xval - 4, randint(4));
+ vault_monsters(yval + 1, xval + 4, randint(4));
+ vault_monsters(yval - 1, xval - 4, randint(4));
+ vault_monsters(yval - 1, xval + 4, randint(4));
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Determine if the given monster is appropriate for inclusion in
+ * a monster nest or monster pit or the given type.
+ *
+ * None of the pits/nests are allowed to include "unique" monsters,
+ * or monsters which can "multiply".
+ *
+ * Some of the pits/nests are asked to avoid monsters which can blink
+ * away or which are invisible. This is probably a hack.
+ *
+ * The old method made direct use of monster "names", which is bad.
+ *
+ * Note the use of Angband 2.7.9 monster race pictures in various places.
+ */
+
+
+/*
+ * Helper function for "monster nest (jelly)"
+ */
+static bool_ vault_aux_jelly(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Also decline evil jellies (like death molds and shoggoths) */
+ if (r_ptr->flags3 & (RF3_EVIL)) return (FALSE);
+
+ /* Require icky thing, jelly, mold, or mushroom */
+ if (!strchr("ijm,", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (animal)"
+ */
+static bool_ vault_aux_animal(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require "animal" flag */
+ if (!(r_ptr->flags3 & (RF3_ANIMAL))) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (undead)"
+ */
+static bool_ vault_aux_undead(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require Undead */
+ if (!(r_ptr->flags3 & (RF3_UNDEAD))) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (chapel)"
+ */
+static bool_ vault_aux_chapel(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require "priest" or Angel */
+ if (!((r_ptr->d_char == 'A') || strstr(r_ptr->name, "riest")))
+ {
+ return (FALSE);
+ }
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (kennel)"
+ */
+static bool_ vault_aux_kennel(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require a Zephyr Hound or a dog */
+ return ((r_ptr->d_char == 'Z') || (r_ptr->d_char == 'C'));
+
+}
+
+
+/*
+ * Helper function for "monster nest (treasure)"
+ */
+static bool_ vault_aux_treasure(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Require "priest" or Angel */
+ if (!((r_ptr->d_char == '!') || (r_ptr->d_char == '|') ||
+ (r_ptr->d_char == '$') || (r_ptr->d_char == '?') ||
+ (r_ptr->d_char == '=')))
+ {
+ return (FALSE);
+ }
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster nest (clone)"
+ */
+static bool_ vault_aux_clone(int r_idx)
+{
+ return (r_idx == template_race);
+}
+
+
+/*
+ * Helper function for "monster nest (symbol clone)"
+ */
+static bool_ vault_aux_symbol(int r_idx)
+{
+ return ((r_info[r_idx].d_char == (r_info[template_race].d_char))
+ && !(r_info[r_idx].flags1 & RF1_UNIQUE));
+}
+
+
+/*
+ * Helper function for "monster pit (orc)"
+ */
+static bool_ vault_aux_orc(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "o" monsters */
+ if (!strchr("o", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+
+/*
+ * Helper function for "monster pit (troll)"
+ */
+static bool_ vault_aux_troll(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "T" monsters */
+ if (!strchr("T", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster pit (giant)"
+ */
+static bool_ vault_aux_giant(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "P" monsters */
+ if (!strchr("P", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Hack -- breath type for "vault_aux_dragon()"
+ */
+static u32b vault_aux_dragon_mask4;
+
+
+/*
+ * Helper function for "monster pit (dragon)"
+ */
+static bool_ vault_aux_dragon(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "d" or "D" monsters */
+ if (!strchr("Dd", r_ptr->d_char)) return (FALSE);
+
+ /* Hack -- Require correct "breath attack" */
+ if (r_ptr->flags4 != vault_aux_dragon_mask4) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Helper function for "monster pit (demon)"
+ */
+static bool_ vault_aux_demon(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Decline unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) return (FALSE);
+
+ /* Hack -- Require "U" monsters */
+ if (!strchr("U", r_ptr->d_char)) return (FALSE);
+
+ /* Okay */
+ return (TRUE);
+}
+
+
+/*
+ * Type 5 -- Monster nests
+ *
+ * A monster nest is a "big" room, with an "inner" room, containing
+ * a "collection" of monsters of a given type strewn about the room.
+ *
+ * The monsters are chosen from a set of 64 randomly selected monster
+ * races, to allow the nest creation to fail instead of having "holes".
+ *
+ * Note the use of the "get_mon_num_prep()" function, and the special
+ * "get_mon_num_hook()" restriction function, to prepare the "monster
+ * allocation table" in such a way as to optimize the selection of
+ * "appropriate" non-unique monsters for the nest.
+ *
+ * Currently, a monster nest is one of
+ * a nest of "jelly" monsters (Dungeon level 5 and deeper)
+ * a nest of "animal" monsters (Dungeon level 30 and deeper)
+ * a nest of "undead" monsters (Dungeon level 50 and deeper)
+ *
+ * Note that the "get_mon_num()" function may (rarely) fail, in which
+ * case the nest will be empty, and will not affect the level rating.
+ *
+ * Note that "monster nests" will never contain "unique" monsters.
+ */
+static void build_type5(int by0, int bx0)
+{
+ int y, x, y1, x1, y2, x2, xval, yval;
+ int tmp, i;
+ cptr name;
+ bool_ empty = FALSE;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+ s16b what[64];
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, TRUE, by0, bx0, &xval, &yval)) return;
+
+ /* Large room */
+ y1 = yval - 4;
+ y2 = yval + 4;
+ x1 = xval - 11;
+ x2 = xval + 11;
+
+ /* Place the floor area */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= (CAVE_ROOM);
+ }
+ }
+
+ /* Place the outer walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM);
+
+ /* Advance to the center room */
+ y1 = y1 + 2;
+ y2 = y2 - 2;
+ x1 = x1 + 2;
+ x2 = x2 - 2;
+
+ /* The inner walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_inner, CAVE_ROOM);
+
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Hack -- Choose a nest type */
+ tmp = randint(dun_level);
+
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ if ((tmp < 25) && (rand_int(2) != 0))
+ {
+ while (1)
+ {
+ template_race = randint(max_r_idx - 2);
+
+ /* Reject uniques */
+ if (r_info[template_race].flags1 & RF1_UNIQUE) continue;
+
+ /* Reject OoD monsters in a loose fashion */
+ if (((r_info[template_race].level) + randint(5)) >
+ (dun_level + randint(5))) continue;
+
+ /* Don't like 'break's like this, but this cannot be made better */
+ break;
+ }
+
+ if ((dun_level >= (25 + randint(15))) && (rand_int(2) != 0))
+ {
+ name = "symbol clone";
+ get_mon_num_hook = vault_aux_symbol;
+ }
+ else
+ {
+ name = "clone";
+ get_mon_num_hook = vault_aux_clone;
+ }
+ }
+ else if (tmp < 25)
+ /* Monster nest (jelly) */
+ {
+ /* Describe */
+ name = "jelly";
+
+ /* Restrict to jelly */
+ get_mon_num_hook = vault_aux_jelly;
+ }
+
+ else if (tmp < 50)
+ {
+ name = "treasure";
+ get_mon_num_hook = vault_aux_treasure;
+ }
+
+ /* Monster nest (animal) */
+ else if (tmp < 65)
+ {
+ if (rand_int(3) == 0)
+ {
+ name = "kennel";
+ get_mon_num_hook = vault_aux_kennel;
+ }
+ else
+ {
+ /* Describe */
+ name = "animal";
+
+ /* Restrict to animal */
+ get_mon_num_hook = vault_aux_animal;
+ }
+ }
+
+ /* Monster nest (undead) */
+ else
+ {
+ if (rand_int(3) == 0)
+ {
+ name = "chapel";
+ get_mon_num_hook = vault_aux_chapel;
+ }
+ else
+ {
+ /* Describe */
+ name = "undead";
+
+ /* Restrict to undead */
+ get_mon_num_hook = vault_aux_undead;
+ }
+ }
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick some monster types */
+ for (i = 0; i < 64; i++)
+ {
+ /* Get a (hard) monster type */
+ what[i] = get_mon_num(dun_level + 10);
+
+ /* Notice failure */
+ if (!what[i]) empty = TRUE;
+ }
+
+ /* Remove restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Oops */
+ if (empty) return;
+
+ /* Describe */
+ if (cheat_room || p_ptr->precognition)
+ {
+ /* Room type */
+ msg_format("Monster nest (%s)", name);
+ }
+
+ /* Increase the level rating */
+ rating += 10;
+
+ /* (Sometimes) Cause a "special feeling" (for "Monster Nests") */
+ if ((dun_level <= 40) && (randint(dun_level * dun_level + 50) < 300))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Place some monsters */
+ for (y = yval - 2; y <= yval + 2; y++)
+ {
+ for (x = xval - 9; x <= xval + 9; x++)
+ {
+ int r_idx = what[rand_int(64)];
+
+ /* Place that "random" monster (no groups) */
+ (void)place_monster_aux(y, x, r_idx, FALSE, FALSE, MSTATUS_ENEMY);
+ }
+ }
+}
+
+
+
+/*
+ * Type 6 -- Monster pits
+ *
+ * A monster pit is a "big" room, with an "inner" room, containing
+ * a "collection" of monsters of a given type organized in the room.
+ *
+ * Monster types in the pit (list out of date...)
+ * orc pit (Dungeon Level 5 and deeper)
+ * troll pit (Dungeon Level 20 and deeper)
+ * giant pit (Dungeon Level 40 and deeper)
+ * dragon pit (Dungeon Level 60 and deeper)
+ * demon pit (Dungeon Level 80 and deeper)
+ *
+ * The inside room in a monster pit appears as shown below, where the
+ * actual monsters in each location depend on the type of the pit
+ *
+ * #####################
+ * #0000000000000000000#
+ * #0112233455543322110#
+ * #0112233467643322110#
+ * #0112233455543322110#
+ * #0000000000000000000#
+ * #####################
+ *
+ * Note that the monsters in the pit are now chosen by using "get_mon_num()"
+ * to request 16 "appropriate" monsters, sorting them by level, and using
+ * the "even" entries in this sorted list for the contents of the pit.
+ *
+ * Hack -- all of the "dragons" in a "dragon" pit must be the same "color",
+ * which is handled by requiring a specific "breath" attack for all of the
+ * dragons. This may include "multi-hued" breath. Note that "wyrms" may
+ * be present in many of the dragon pits, if they have the proper breath.
+ *
+ * Note the use of the "get_mon_num_prep()" function, and the special
+ * "get_mon_num_hook()" restriction function, to prepare the "monster
+ * allocation table" in such a way as to optimize the selection of
+ * "appropriate" non-unique monsters for the pit.
+ *
+ * Note that the "get_mon_num()" function may (rarely) fail, in which case
+ * the pit will be empty, and will not effect the level rating.
+ *
+ * Note that "monster pits" will never contain "unique" monsters.
+ */
+static void build_type6(int by0, int bx0)
+{
+ int tmp, what[16];
+ int i, j, y, x, y1, x1, y2, x2, xval, yval;
+ bool_ empty = FALSE;
+ cptr name;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(25, 11, TRUE, by0, bx0, &xval, &yval)) return;
+
+ /* Large room */
+ y1 = yval - 4;
+ y2 = yval + 4;
+ x1 = xval - 11;
+ x2 = xval + 11;
+
+ /* Place the floor area */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ place_floor(y, x);
+ cave[y][x].info |= (CAVE_ROOM);
+ }
+ }
+
+ /* Place the outer walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM);
+
+ /* Advance to the center room */
+ y1 = y1 + 2;
+ y2 = y2 - 2;
+ x1 = x1 + 2;
+ x2 = x2 - 2;
+
+ /* The inner walls */
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM);
+
+ /* Place a secret door */
+ switch (randint(4))
+ {
+ case 1:
+ place_secret_door(y1 - 1, xval);
+ break;
+ case 2:
+ place_secret_door(y2 + 1, xval);
+ break;
+ case 3:
+ place_secret_door(yval, x1 - 1);
+ break;
+ case 4:
+ place_secret_door(yval, x2 + 1);
+ break;
+ }
+
+ /* Choose a pit type */
+ tmp = randint(dun_level);
+
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Orc pit */
+ if (tmp < 20)
+ {
+ /* Message */
+ name = "orc";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_orc;
+ }
+
+ /* Troll pit */
+ else if (tmp < 40)
+ {
+ /* Message */
+ name = "troll";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_troll;
+ }
+
+ /* Giant pit */
+ else if (tmp < 55)
+ {
+ /* Message */
+ name = "giant";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_giant;
+ }
+
+ else if (tmp < 70)
+ {
+ if (randint(4) != 1)
+ {
+ /* Message */
+ name = "ordered clones";
+
+ do
+ {
+ template_race = randint(max_r_idx - 2);
+ }
+ while ((r_info[template_race].flags1 & RF1_UNIQUE)
+ || (((r_info[template_race].level) + randint(5)) >
+ (dun_level + randint(5))));
+
+ /* Restrict selection */
+ get_mon_num_hook = vault_aux_symbol;
+ }
+ else
+ {
+ name = "ordered chapel";
+ get_mon_num_hook = vault_aux_chapel;
+ }
+
+ }
+
+ /* Dragon pit */
+ else if (tmp < 80)
+ {
+ /* Pick dragon type */
+ switch (rand_int(6))
+ {
+ /* Black */
+ case 0:
+ {
+ /* Message */
+ name = "acid dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_ACID;
+
+ /* Done */
+ break;
+ }
+
+ /* Blue */
+ case 1:
+ {
+ /* Message */
+ name = "electric dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_ELEC;
+
+ /* Done */
+ break;
+ }
+
+ /* Red */
+ case 2:
+ {
+ /* Message */
+ name = "fire dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_FIRE;
+
+ /* Done */
+ break;
+ }
+
+ /* White */
+ case 3:
+ {
+ /* Message */
+ name = "cold dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_COLD;
+
+ /* Done */
+ break;
+ }
+
+ /* Green */
+ case 4:
+ {
+ /* Message */
+ name = "poison dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = RF4_BR_POIS;
+
+ /* Done */
+ break;
+ }
+
+ /* Multi-hued */
+ default:
+ {
+ /* Message */
+ name = "multi-hued dragon";
+
+ /* Restrict dragon breath type */
+ vault_aux_dragon_mask4 = (RF4_BR_ACID | RF4_BR_ELEC |
+ RF4_BR_FIRE | RF4_BR_COLD |
+ RF4_BR_POIS);
+
+ /* Done */
+ break;
+ }
+
+ }
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_dragon;
+ }
+
+ /* Demon pit */
+ else
+ {
+ /* Message */
+ name = "demon";
+
+ /* Restrict monster selection */
+ get_mon_num_hook = vault_aux_demon;
+ }
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick some monster types */
+ for (i = 0; i < 16; i++)
+ {
+ /* Get a (hard) monster type */
+ what[i] = get_mon_num(dun_level + 10);
+
+ /* Notice failure */
+ if (!what[i]) empty = TRUE;
+ }
+
+ /* Remove restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Oops */
+ if (empty) return;
+
+ /* XXX XXX XXX */
+ /* Sort the entries */
+ for (i = 0; i < 16 - 1; i++)
+ {
+ /* Sort the entries */
+ for (j = 0; j < 16 - 1; j++)
+ {
+ int i1 = j;
+ int i2 = j + 1;
+
+ int p1 = r_info[what[i1]].level;
+ int p2 = r_info[what[i2]].level;
+
+ /* Bubble */
+ if (p1 > p2)
+ {
+ int tmp = what[i1];
+ what[i1] = what[i2];
+ what[i2] = tmp;
+ }
+ }
+ }
+
+ /* Select the entries */
+ for (i = 0; i < 8; i++)
+ {
+ /* Every other entry */
+ what[i] = what[i * 2];
+ }
+
+ /* Message */
+ if (cheat_room || p_ptr->precognition)
+ {
+ /* Room type */
+ msg_format("Monster pit (%s)", name);
+
+ if (cheat_hear || p_ptr->precognition)
+ {
+ /* Contents */
+ for (i = 0; i < 8; i++)
+ {
+ /* Message */
+ msg_print(r_info[what[i]].name);
+ }
+ }
+ }
+
+ /* Increase the level rating */
+ rating += 10;
+
+ /* (Sometimes) Cause a "special feeling" (for "Monster Pits") */
+ if ((dun_level <= 40) && (randint(dun_level * dun_level + 50) < 300))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Top and bottom rows */
+ for (x = xval - 9; x <= xval + 9; x++)
+ {
+ place_monster_aux(yval - 2, x, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(yval + 2, x, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+ }
+
+ /* Middle columns */
+ for (y = yval - 1; y <= yval + 1; y++)
+ {
+ place_monster_aux(y, xval - 9, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 9, what[0], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 8, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 8, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 7, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 7, what[1], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 6, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 6, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 5, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 5, what[2], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 4, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 4, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 3, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 3, what[3], FALSE, FALSE, MSTATUS_ENEMY);
+
+ place_monster_aux(y, xval - 2, what[4], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(y, xval + 2, what[4], FALSE, FALSE, MSTATUS_ENEMY);
+ }
+
+ /* Above/Below the center monster */
+ for (x = xval - 1; x <= xval + 1; x++)
+ {
+ place_monster_aux(yval + 1, x, what[5], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(yval - 1, x, what[5], FALSE, FALSE, MSTATUS_ENEMY);
+ }
+
+ /* Next to the center monster */
+ place_monster_aux(yval, xval + 1, what[6], FALSE, FALSE, MSTATUS_ENEMY);
+ place_monster_aux(yval, xval - 1, what[6], FALSE, FALSE, MSTATUS_ENEMY);
+
+ /* Center monster */
+ place_monster_aux(yval, xval, what[7], FALSE, FALSE, MSTATUS_ENEMY);
+}
+
+/*
+ * Hack -- fill in "vault" rooms
+ */
+static void build_vault(int yval, int xval, int ymax, int xmax, cptr data)
+{
+ int dx, dy, x, y, bwy[8], bwx[8], i;
+
+ cptr t;
+
+ cave_type *c_ptr;
+
+ /* Clean the between gates arrays */
+ for (i = 0; i < 8; i++)
+ {
+ bwy[i] = bwx[i] = 9999;
+ }
+
+ /* Place dungeon features and objects */
+ for (t = data, dy = 0; dy < ymax; dy++)
+ {
+ for (dx = 0; dx < xmax; dx++, t++)
+ {
+ /* Extract the location */
+ x = xval - (xmax / 2) + dx;
+ y = yval - (ymax / 2) + dy;
+
+ /* Hack -- skip "non-grids" */
+ if (*t == ' ') continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Lay down a floor */
+ place_floor(y, x);
+
+ /* Part of a vault */
+ c_ptr->info |= (CAVE_ROOM | CAVE_ICKY);
+
+ /* Analyze the grid */
+ switch (*t)
+ {
+ /* Granite wall (outer) */
+ case '%':
+ {
+ cave_set_feat(y, x, FEAT_WALL_OUTER);
+ break;
+ }
+
+ /* Granite wall (inner) */
+ case '#':
+ {
+ cave_set_feat(y, x, FEAT_WALL_INNER);
+ break;
+ }
+
+ /* Permanent wall (inner) */
+ case 'X':
+ {
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+ break;
+ }
+
+ /* Treasure/trap */
+ case '*':
+ {
+ if (rand_int(100) < 75)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_VAULT);
+ }
+ else
+ {
+ place_trap(y, x);
+ }
+ break;
+ }
+
+ /* Secret doors */
+ case '+':
+ {
+ place_secret_door(y, x);
+ break;
+ }
+
+ /* Trap */
+ case '^':
+ {
+ place_trap(y, x);
+ break;
+ }
+
+ /* Glass wall */
+ case 'G':
+ {
+ cave_set_feat(y, x, FEAT_GLASS_WALL);
+ break;
+ }
+
+ /* Illusion wall */
+ case 'I':
+ {
+ cave_set_feat(y, x, FEAT_ILLUS_WALL);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Place dungeon monsters and objects */
+ for (t = data, dy = 0; dy < ymax; dy++)
+ {
+ for (dx = 0; dx < xmax; dx++, t++)
+ {
+ /* Extract the grid */
+ x = xval - (xmax / 2) + dx;
+ y = yval - (ymax / 2) + dy;
+
+ /* Hack -- skip "non-grids" */
+ if (*t == ' ') continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Analyze the symbol */
+ switch (*t)
+ {
+ /* Monster */
+ case '&':
+ {
+ monster_level = dun_level + 5;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ break;
+ }
+
+ /* Meaner monster */
+ case '@':
+ {
+ monster_level = dun_level + 11;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ break;
+ }
+
+ /* Meaner monster, plus treasure */
+ case '9':
+ {
+ monster_level = dun_level + 9;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 7;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ break;
+ }
+
+ /* Nasty monster and treasure */
+ case '8':
+ {
+ monster_level = dun_level + 40;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ object_level = dun_level + 20;
+ place_object(y, x, TRUE, TRUE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ break;
+ }
+
+ /* Monster and/or object */
+ case ',':
+ {
+ if (rand_int(100) < 50)
+ {
+ monster_level = dun_level + 3;
+ place_monster(y, x, TRUE, TRUE);
+ monster_level = dun_level;
+ }
+ if (rand_int(100) < 50)
+ {
+ object_level = dun_level + 7;
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ }
+ break;
+ }
+
+ case 'p':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_START);
+ break;
+ }
+
+ case 'a':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_1);
+ break;
+ }
+
+ case 'b':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_2);
+ break;
+ }
+
+ case 'c':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_3);
+ break;
+ }
+
+ case 'd':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_4);
+ break;
+ }
+
+ case 'P':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_END);
+ break;
+ }
+
+ case 'B':
+ {
+ cave_set_feat(y, x, FEAT_PATTERN_XTRA1);
+ break;
+ }
+
+ case 'A':
+ {
+ object_level = dun_level + 12;
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_VAULT);
+ object_level = dun_level;
+ break;
+ }
+
+
+ /* Between gates */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ /* Not found before */
+ if (bwy[*t - '0'] == 9999)
+ {
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ bwy[*t - '0'] = y;
+ bwx[*t - '0'] = x;
+ }
+ /* The second time */
+ else
+ {
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ c_ptr->special = bwx[*t - '0'] + (bwy[*t - '0'] << 8);
+ cave[bwy[*t - '0']][bwx[*t - '0']].special = x + (y << 8);
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Type 7 -- simple vaults (see "v_info.txt")
+ */
+static void build_type7(int by0, int bx0)
+{
+ vault_type *v_ptr = NULL;
+ int dummy = 0, xval, yval;
+
+ /* Pick a lesser vault */
+ while (dummy < SAFE_MAX_ATTEMPTS)
+ {
+ dummy++;
+
+ /* Access a random vault record */
+ v_ptr = &v_info[rand_int(max_v_idx)];
+
+ /* Accept the first lesser vault */
+ if (v_ptr->typ == 7) break;
+ }
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(v_ptr->wid, v_ptr->hgt, FALSE, by0, bx0, &xval, &yval))
+ {
+ if (cheat_room) msg_print("Could not allocate this vault here");
+ return;
+ }
+
+ if (dummy >= SAFE_MAX_ATTEMPTS)
+ {
+ if (cheat_room)
+ {
+ msg_print("Warning! Could not place lesser vault!");
+ }
+ return;
+ }
+
+
+ /* 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_ptr->data);
+}
+
+
+
+/*
+ * 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;
+ }
+
+
+ /* 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_ptr->data);
+}
+
+/*
+ * 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 = ((static_cast<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 m, n, num_vertices;
+ 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 */
+ std::vector<int> visited(num_vertices, 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.data());
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x1, x2, y1, y2, randint(5));
+}
+
+
+/*
+ * 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 m, n, num_vertices;
+
+ 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 */
+ std::vector<int> visited(num_vertices, 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.data());
+
+ /* 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);
+}
+
+
+/*
+ * Build a town/ castle by using a recursive algorithm.
+ * Basically divide each region in a probalistic way to create
+ * smaller regions. When the regions get too small stop.
+ *
+ * The power variable is a measure of how well defended a region is.
+ * This alters the possible choices.
+ */
+static void build_recursive_room(int x1, int y1, int x2, int y2, int power)
+{
+ int xsize, ysize;
+ int x, y;
+ int choice;
+
+ /* Temp variables */
+ int t1, t2, t3, t4;
+
+ xsize = x2 - x1;
+ ysize = y2 - y1;
+
+ if ((power < 3) && (xsize > 12) && (ysize > 12))
+ {
+ /* Need outside wall +keep */
+ choice = 1;
+ }
+ else
+ {
+ if (power < 10)
+ {
+ /* Make rooms + subdivide */
+ if ((randint(10) > 2) && (xsize < 8) && (ysize < 8))
+ {
+ choice = 4;
+ }
+ else
+ {
+ choice = randint(2) + 1;
+ }
+ }
+ else
+ {
+ /* Mostly subdivide */
+ choice = randint(3) + 1;
+ }
+ }
+
+ /* Based on the choice made above, do something */
+ switch (choice)
+ {
+ /* Outer walls */
+ case 1:
+ {
+ /* Top and bottom */
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y1, x, feat_wall_outer);
+ cave_set_feat(y2, x, feat_wall_outer);
+ }
+
+ /* Left and right */
+ for (y = y1 + 1; y < y2; y++)
+ {
+ cave_set_feat(y, x1, feat_wall_outer);
+ cave_set_feat(y, x2, feat_wall_outer);
+ }
+
+ /* Make a couple of entrances */
+ if (rand_int(2) == 0)
+ {
+ /* Left and right */
+ y = randint(ysize) + y1;
+ place_floor(y, x1);
+ place_floor(y, x2);
+ }
+ else
+ {
+ /* Top and bottom */
+ x = randint(xsize) + x1;
+ place_floor(y1, x);
+ place_floor(y2, x);
+ }
+
+ /* Select size of keep */
+ t1 = randint(ysize / 3) + y1;
+ t2 = y2 - randint(ysize / 3);
+ t3 = randint(xsize / 3) + x1;
+ t4 = x2 - randint(xsize / 3);
+
+ /* Do outside areas */
+
+ /* Above and below keep */
+ build_recursive_room(x1 + 1, y1 + 1, x2 - 1, t1, power + 1);
+ build_recursive_room(x1 + 1, t2, x2 - 1, y2, power + 1);
+
+ /* Left and right of keep */
+ build_recursive_room(x1 + 1, t1 + 1, t3, t2 - 1, power + 3);
+ build_recursive_room(t4, t1 + 1, x2 - 1, t2 - 1, power + 3);
+
+ /* Make the keep itself: */
+ x1 = t3;
+ x2 = t4;
+ y1 = t1;
+ y2 = t2;
+ xsize = x2 - x1;
+ ysize = y2 - y1;
+ power += 2;
+
+ /* Fall through */
+ }
+
+ /* Try to build a room */
+ case 4:
+ {
+ if ((xsize < 3) || (ysize < 3))
+ {
+ for (y = y1; y < y2; y++)
+ {
+ for (x = x1; x < x2; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+
+ /* Too small */
+ return;
+ }
+
+ /* Make outside walls */
+
+ /* Top and bottom */
+ for (x = x1 + 1; x <= x2 - 1; x++)
+ {
+ cave_set_feat(y1 + 1, x, feat_wall_inner);
+ cave_set_feat(y2 - 1, x, feat_wall_inner);
+ }
+
+ /* Left and right */
+ for (y = y1 + 1; y <= y2 - 1; y++)
+ {
+ cave_set_feat(y, x1 + 1, feat_wall_inner);
+ cave_set_feat(y, x2 - 1, feat_wall_inner);
+ }
+
+ /* Make a door */
+ y = randint(ysize - 3) + y1 + 1;
+
+ if (rand_int(2) == 0)
+ {
+ /* Left */
+ place_floor(y, x1 + 1);
+ }
+ else
+ {
+ /* Right */
+ place_floor(y, x2 - 1);
+ }
+
+ /* Build the room */
+ build_recursive_room(x1 + 2, y1 + 2, x2 - 2, y2 - 2, power + 3);
+
+ break;
+ }
+
+ /* Try and divide vertically */
+ case 2:
+ {
+ if (xsize < 3)
+ {
+ /* Too small */
+ for (y = y1; y < y2; y++)
+ {
+ for (x = x1; x < x2; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ return;
+ }
+
+ t1 = randint(xsize - 2) + x1 + 1;
+ build_recursive_room(x1, y1, t1, y2, power - 2);
+ build_recursive_room(t1 + 1, y1, x2, y2, power - 2);
+
+ break;
+ }
+
+ /* Try and divide horizontally */
+ case 3:
+ {
+ if (ysize < 3)
+ {
+ /* Too small */
+ for (y = y1; y < y2; y++)
+ {
+ for (x = x1; x < x2; x++)
+ {
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ return;
+ }
+
+ t1 = randint(ysize - 2) + y1 + 1;
+ build_recursive_room(x1, y1, x2, t1, power - 2);
+ build_recursive_room(x1, t1 + 1, x2, y2, power - 2);
+
+ break;
+ }
+ }
+}
+
+
+/*
+ * Build a castle
+ *
+ * Clear the region and call the recursive room routine.
+ *
+ * This makes a vault that looks like a castle or city in the dungeon.
+ */
+static void build_castle_vault(int x0, int y0, int xsize, int ysize)
+{
+ int dy, dx;
+ int y1, x1, y2, x2;
+ int y, x;
+
+ /* Pick a random room size */
+ dy = ysize / 2 - 1;
+ dx = xsize / 2 - 1;
+
+ y1 = y0 - dy;
+ x1 = x0 - dx;
+ y2 = y0 + dy;
+ x2 = x0 + dx;
+
+ if (cheat_room) msg_print("Castle Vault");
+
+ /* Generate the room */
+ for (y = y1 - 1; y <= y2 + 1; y++)
+ {
+ for (x = x1 - 1; x <= x2 + 1; x++)
+ {
+ cave[y][x].info |= (CAVE_ROOM | CAVE_ICKY);
+
+ /* Make everything a floor */
+ place_floor(y, x);
+ }
+ }
+
+ /* Make the castle */
+ build_recursive_room(x1, y1, x2, y2, randint(5));
+
+ /* Fill with monsters and treasure, low difficulty */
+ fill_treasure(x1, x2, y1, y2, randint(3));
+}
+
+
+/*
+ * Add outer wall to a floored region
+ *
+ * Note: no range checking is done so must be inside dungeon
+ * This routine also stomps on doors
+ */
+static void add_outer_wall(int x, int y, int light, int x1, int y1,
+ int x2, int y2)
+{
+ int i, j;
+
+ if (!in_bounds(y, x)) return;
+
+ /*
+ * Hack -- Check to see if square has been visited before
+ * if so, then exit (use room flag to do this)
+ */
+ if (cave[y][x].info & CAVE_ROOM) return;
+
+ /* Set room flag */
+ cave[y][x].info |= (CAVE_ROOM);
+
+ if (get_is_floor(x, y))
+ {
+ for (i = -1; i <= 1; i++)
+ {
+ for (j = -1; j <= 1; j++)
+ {
+ if ((x + i >= x1) && (x + i <= x2) &&
+ (y + j >= y1) && (y + j <= y2))
+ {
+ add_outer_wall(x + i, y + j, light, x1, y1, x2, y2);
+ if (light) cave[y][x].info |= CAVE_GLOW;
+ }
+ }
+ }
+ }
+
+ /* Set bounding walls */
+ else if (cave[y][x].feat == FEAT_WALL_EXTRA)
+ {
+ cave[y][x].feat = feat_wall_outer;
+ if (light == TRUE) cave[y][x].info |= CAVE_GLOW;
+ }
+
+ /* Set bounding walls */
+ else if (cave[y][x].feat == FEAT_PERM_OUTER)
+ {
+ if (light == TRUE) cave[y][x].info |= CAVE_GLOW;
+ }
+}
+
+
+/*
+ * Hacked distance formula - gives the 'wrong' answer
+ *
+ * Used to build crypts
+ */
+static int dist2(int x1, int y1, int x2, int y2,
+ int h1, int h2, int h3, int h4)
+{
+ int dx, dy;
+ dx = abs(x2 - x1);
+ dy = abs(y2 - y1);
+
+ /*
+ * Basically this works by taking the normal pythagorean formula
+ * and using an expansion to express this in a way without the
+ * square root. This approximate formula is then perturbed to give
+ * the distorted results. (I found this by making a mistake when I was
+ * trying to fix the circular rooms.)
+ */
+
+ /* h1-h4 are constants that describe the metric */
+ if (dx >= 2 * dy) return (dx + (dy * h1) / h2);
+ if (dy >= 2 * dx) return (dy + (dx * h1) / h2);
+
+ /* 128/181 is approx. 1/sqrt(2) */
+ return (((dx + dy) * 128) / 181 +
+ (dx * dx / (dy * h3) + dy * dy / (dx * h3)) * h4);
+}
+
+
+/*
+ * Build target vault
+ *
+ * This is made by two concentric "crypts" with perpendicular
+ * walls creating the cross-hairs.
+ */
+static void build_target_vault(int x0, int y0, int xsize, int ysize)
+{
+ int rad, x, y;
+
+ int h1, h2, h3, h4;
+
+
+ /* Make a random metric */
+ h1 = randint(32) - 16;
+ h2 = randint(16);
+ h3 = randint(32);
+ h4 = randint(32) - 16;
+
+ if (cheat_room) msg_print("Target Vault");
+
+ /* Work out outer radius */
+ if (xsize > ysize)
+ {
+ rad = ysize / 2;
+ }
+ else
+ {
+ rad = xsize / 2;
+ }
+
+ /* Make floor */
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ cave_type *c_ptr;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear room flag */
+ c_ptr->info &= ~(CAVE_ROOM);
+
+ /* Grids in vaults are required to be "icky" */
+ c_ptr->info |= (CAVE_ICKY);
+
+ /* Inside -- floor */
+ if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1)
+ {
+ place_floor(y, x);
+ }
+
+ /* Outside -- make it granite so that arena works */
+ else
+ {
+ c_ptr->feat = FEAT_WALL_EXTRA;
+ }
+
+ /* Proper boundary for arena */
+ if (((y + rad) == y0) || ((y - rad) == y0) ||
+ ((x + rad) == x0) || ((x - rad) == x0))
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ }
+ }
+
+ /* Find visible outer walls and set to be FEAT_OUTER */
+ add_outer_wall(x0, y0, FALSE, x0 - rad - 1, y0 - rad - 1,
+ x0 + rad + 1, y0 + rad + 1);
+
+ /* Add inner wall */
+ for (x = x0 - rad / 2; x <= x0 + rad / 2; x++)
+ {
+ for (y = y0 - rad / 2; y <= y0 + rad / 2; y++)
+ {
+ if (dist2(y0, x0, y, x, h1, h2, h3, h4) == rad / 2)
+ {
+ /* Make an internal wall */
+ cave_set_feat(y, x, feat_wall_inner);
+ }
+ }
+ }
+
+ /* Add perpendicular walls */
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ cave_set_feat(y0, x, feat_wall_inner);
+ }
+
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ cave_set_feat(y, x0, feat_wall_inner);
+ }
+
+ /* Make inner vault */
+ for (y = y0 - 1; y <= y0 + 1; y++)
+ {
+ cave_set_feat(y, x0 - 1, feat_wall_inner);
+ cave_set_feat(y, x0 + 1, feat_wall_inner);
+ }
+ for (x = x0 - 1; x <= x0 + 1; x++)
+ {
+ cave_set_feat(y0 - 1, x, feat_wall_inner);
+ cave_set_feat(y0 + 1, x, feat_wall_inner);
+ }
+
+ place_floor(y0, x0);
+
+
+ /*
+ * Add doors to vault
+ *
+ * Get two distances so can place doors relative to centre
+ */
+ x = (rad - 2) / 4 + 1;
+ y = rad / 2 + x;
+
+ add_door(x0 + x, y0);
+ add_door(x0 + y, y0);
+ add_door(x0 - x, y0);
+ add_door(x0 - y, y0);
+ add_door(x0, y0 + x);
+ add_door(x0, y0 + y);
+ add_door(x0, y0 - x);
+ add_door(x0, y0 - y);
+
+ /* Fill with stuff - medium difficulty */
+ fill_treasure(x0 - rad, x0 + rad, y0 - rad, y0 + rad, randint(3) + 3);
+}
+
+
+/*
+ * Random vaults
+ */
+static void build_type11(int by0, int bx0)
+{
+ int y0, x0, xsize, ysize, vtype;
+
+ /* Get size -- gig enough to look good, small enough to be fairly common */
+ xsize = randint(22) + 22;
+ ysize = randint(11) + 11;
+
+ /* Allocate in room_map. If will not fit, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &x0, &y0)) return;
+
+ /*
+ * Boost the rating -- Higher than lesser vaults and lower than
+ * greater vaults
+ */
+ rating += 10;
+
+ /* (Sometimes) Cause a special feeling */
+ if ((dun_level <= 50) ||
+ (randint((dun_level - 40) * (dun_level - 40) + 1) < 400))
+ {
+ good_item_flag = TRUE;
+ }
+
+ /* Select type of vault */
+ vtype = randint(8);
+
+ switch (vtype)
+ {
+ /* Build an appropriate room */
+ case 1:
+ {
+ build_bubble_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 2:
+ {
+ build_room_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 3:
+ {
+ build_cave_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 4:
+ {
+ build_maze_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 5:
+ {
+ build_mini_c_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 6:
+ {
+ build_castle_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ case 7:
+ {
+ build_target_vault(x0, y0, xsize, ysize);
+ break;
+ }
+
+ /* I know how to add a few more... give me some time. */
+
+ /* Paranoia */
+ default:
+ {
+ return;
+ }
+ }
+}
+
+/*
+ * Crypt room generation from Z 2.5.1
+ */
+
+/*
+ * Build crypt room.
+ * For every grid in the possible square, check the (fake) distance.
+ * If it's less than the radius, make it a room square.
+ *
+ * When done fill from the inside to find the walls,
+ */
+static void build_type12(int by0, int bx0)
+{
+ int light, rad, x, y, x0, y0;
+ bool_ emptyflag = TRUE;
+ int h1, h2, h3, h4;
+
+ /* Make a random metric */
+ h1 = randint(32) - 16;
+ h2 = randint(16);
+ h3 = randint(32);
+ h4 = randint(32) - 16;
+
+ /* Occasional light */
+ light = (randint(dun_level) <= 5) ? TRUE : FALSE;
+
+ rad = randint(9);
+
+ /* Allocate in room_map. If will not fit, exit */
+ if (!room_alloc(rad * 2 + 3, rad * 2 + 3, FALSE, by0, bx0, &x0, &y0)) return;
+
+ /* Make floor */
+ for (x = x0 - rad; x <= x0 + rad; x++)
+ {
+ for (y = y0 - rad; y <= y0 + rad; y++)
+ {
+ /* Clear room flag */
+ cave[y][x].info &= ~(CAVE_ROOM);
+
+ /* Inside -- floor */
+ if (dist2(y0, x0, y, x, h1, h2, h3, h4) <= rad - 1)
+ {
+ place_floor(y, x);
+ }
+ else if (distance(y0, x0, y, x) < 3)
+ {
+ place_floor(y, x);
+ }
+
+ /* Outside -- make it granite so that arena works */
+ else
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+
+ /* Proper boundary for arena */
+ if (((y + rad) == y0) || ((y - rad) == y0) ||
+ ((x + rad) == x0) || ((x - rad) == x0))
+ {
+ cave_set_feat(y, x, feat_wall_outer);
+ }
+ }
+ }
+
+ /* Find visible outer walls and set to be FEAT_OUTER */
+ add_outer_wall(x0, y0, light, x0 - rad - 1, y0 - rad - 1,
+ x0 + rad + 1, y0 + rad + 1);
+
+ /* Check to see if there is room for an inner vault */
+ for (x = x0 - 2; x <= x0 + 2; x++)
+ {
+ for (y = y0 - 2; y <= y0 + 2; y++)
+ {
+ if (!get_is_floor(x, y))
+ {
+ /* Wall in the way */
+ emptyflag = FALSE;
+ }
+ }
+ }
+
+ if (emptyflag && (rand_int(2) == 0))
+ {
+ /* Build the vault */
+ build_small_room(x0, y0);
+
+ /* Place a treasure in the vault */
+ place_object(y0, x0, FALSE, FALSE, OBJ_FOUND_FLOOR);
+
+ /* Let's guard the treasure well */
+ vault_monsters(y0, x0, rand_int(2) + 3);
+
+ /* Traps naturally */
+ vault_traps(y0, x0, 4, 4, rand_int(3) + 2);
+ }
+}
+
+
+/*
+ * Constructs a tunnel between two points
+ *
+ * This function must be called BEFORE any streamers are created,
+ * since we use the special "granite wall" sub-types to keep track
+ * of legal places for corridors to pierce rooms.
+ *
+ * We use "door_flag" to prevent excessive construction of doors
+ * along overlapping corridors.
+ *
+ * We queue the tunnel grids to prevent door creation along a corridor
+ * which intersects itself.
+ *
+ * We queue the wall piercing grids to prevent a corridor from leaving
+ * a room and then coming back in through the same entrance.
+ *
+ * We "pierce" grids which are "outer" walls of rooms, and when we
+ * do so, we change all adjacent "outer" walls of rooms into "solid"
+ * walls so that no two corridors may use adjacent grids for exits.
+ *
+ * The "solid" wall check prevents corridors from "chopping" the
+ * corners of rooms off, as well as "silly" door placement, and
+ * "excessively wide" room entrances.
+ *
+ * Useful "feat" values:
+ * FEAT_WALL_EXTRA -- granite walls
+ * FEAT_WALL_INNER -- inner room walls
+ * FEAT_WALL_OUTER -- outer room walls
+ * FEAT_WALL_SOLID -- solid room walls
+ * FEAT_PERM_EXTRA -- shop walls (perma)
+ * FEAT_PERM_INNER -- inner room walls (perma)
+ * FEAT_PERM_OUTER -- outer room walls (perma)
+ * FEAT_PERM_SOLID -- dungeon border (perma)
+ */
+static void build_tunnel(int row1, int col1, int row2, int col2, bool_ water)
+{
+ int i, y, x;
+ int tmp_row, tmp_col;
+ int row_dir, col_dir;
+ int start_row, start_col;
+ int main_loop_count = 0;
+
+ bool_ door_flag = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Reset the arrays */
+ dun->tunn_n = 0;
+ dun->wall_n = 0;
+
+ /* Save the starting location */
+ start_row = row1;
+ start_col = col1;
+
+ /* Start out in the correct direction */
+ correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
+
+ /* Keep going until done (or bored) */
+ while ((row1 != row2) || (col1 != col2))
+ {
+ /* Mega-Hack -- Paranoia -- prevent infinite loops */
+ if (main_loop_count++ > 2000) break;
+
+ /* Allow bends in the tunnel */
+ if (rand_int(100) < DUN_TUN_CHG)
+ {
+ /* Acquire the correct direction */
+ correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
+
+ /* Random direction */
+ if (rand_int(100) < DUN_TUN_RND)
+ {
+ rand_dir(&row_dir, &col_dir);
+ }
+ }
+
+ /* Get the next location */
+ tmp_row = row1 + row_dir;
+ tmp_col = col1 + col_dir;
+
+
+ /* Extremely Important -- do not leave the dungeon */
+ while (!in_bounds(tmp_row, tmp_col))
+ {
+ /* Acquire the correct direction */
+ correct_dir(&row_dir, &col_dir, row1, col1, row2, col2);
+
+ /* Random direction */
+ if (rand_int(100) < DUN_TUN_RND)
+ {
+ rand_dir(&row_dir, &col_dir);
+ }
+
+ /* Get the next location */
+ tmp_row = row1 + row_dir;
+ tmp_col = col1 + col_dir;
+ }
+
+
+ /* Access the location */
+ c_ptr = &cave[tmp_row][tmp_col];
+
+
+ /* Avoid the edge of the dungeon */
+ if (c_ptr->feat == FEAT_PERM_SOLID) continue;
+
+ /* Avoid the edge of vaults */
+ if (c_ptr->feat == FEAT_PERM_OUTER) continue;
+
+ /* Avoid "solid" granite walls */
+ if (c_ptr->feat == FEAT_WALL_SOLID) continue;
+
+ /*
+ * Pierce "outer" walls of rooms
+ * Cannot trust feat code any longer...
+ */
+ if ((c_ptr->feat == feat_wall_outer) &&
+ (c_ptr->info & CAVE_ROOM))
+ {
+ /* Acquire the "next" location */
+ y = tmp_row + row_dir;
+ x = tmp_col + col_dir;
+
+ /* Hack -- Avoid outer/solid permanent walls */
+ if (cave[y][x].feat == FEAT_PERM_SOLID) continue;
+ if (cave[y][x].feat == FEAT_PERM_OUTER) continue;
+
+ /* Hack -- Avoid outer/solid granite walls */
+ if ((cave[y][x].feat == feat_wall_outer) &&
+ (cave[y][x].info & CAVE_ROOM)) continue;
+ if (cave[y][x].feat == FEAT_WALL_SOLID) continue;
+
+ /* Accept this location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+
+ /* Save the wall location */
+ if (dun->wall_n < WALL_MAX)
+ {
+ dun->wall[dun->wall_n].y = row1;
+ dun->wall[dun->wall_n].x = col1;
+ dun->wall_n++;
+ }
+
+ /* Forbid re-entry near this piercing */
+ for (y = row1 - 1; y <= row1 + 1; y++)
+ {
+ for (x = col1 - 1; x <= col1 + 1; x++)
+ {
+ /* Convert adjacent "outer" walls as "solid" walls */
+ if ((cave[y][x].feat == feat_wall_outer) &&
+ (cave[y][x].info & CAVE_ROOM))
+ {
+ /* Change the wall to a "solid" wall */
+ /* Mega-Hack -- to be brought back later... */
+ cave_set_feat(y, x, FEAT_WALL_SOLID);
+ }
+ }
+ }
+ }
+
+ /* Travel quickly through rooms */
+ else if (c_ptr->info & (CAVE_ROOM))
+ {
+ /* Accept the location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+ }
+
+ /* Tunnel through all other walls */
+ else if ((c_ptr->feat == d_info[dungeon_type].fill_type1) ||
+ (c_ptr->feat == d_info[dungeon_type].fill_type2) ||
+ (c_ptr->feat == d_info[dungeon_type].fill_type3))
+ {
+ /* Accept this location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+
+ /* Save the tunnel location */
+ if (dun->tunn_n < TUNN_MAX)
+ {
+ dun->tunn[dun->tunn_n].y = row1;
+ dun->tunn[dun->tunn_n].x = col1;
+ dun->tunn_n++;
+ }
+
+ /* Allow door in next grid */
+ door_flag = FALSE;
+ }
+
+ /* Handle corridor intersections or overlaps */
+ else
+ {
+ /* Accept the location */
+ row1 = tmp_row;
+ col1 = tmp_col;
+
+ /* Collect legal door locations */
+ if (!door_flag)
+ {
+ /* Save the door location */
+ if (dun->door_n < DOOR_MAX)
+ {
+ dun->door[dun->door_n].y = row1;
+ dun->door[dun->door_n].x = col1;
+ dun->door_n++;
+ }
+
+ /* No door in next grid */
+ door_flag = TRUE;
+ }
+
+ /* Hack -- allow pre-emptive tunnel termination */
+ if (rand_int(100) >= DUN_TUN_CON)
+ {
+ /* Distance between row1 and start_row */
+ tmp_row = row1 - start_row;
+ if (tmp_row < 0) tmp_row = ( -tmp_row);
+
+ /* Distance between col1 and start_col */
+ tmp_col = col1 - start_col;
+ if (tmp_col < 0) tmp_col = ( -tmp_col);
+
+ /* Terminate the tunnel */
+ if ((tmp_row > 10) || (tmp_col > 10)) break;
+ }
+ }
+ }
+
+
+ /* Turn the tunnel into corridor */
+ for (i = 0; i < dun->tunn_n; i++)
+ {
+ /* Access the grid */
+ y = dun->tunn[i].y;
+ x = dun->tunn[i].x;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear previous contents, add a floor */
+ if (!water)
+ {
+ place_floor(y, x);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_SHAL_WATER);
+ }
+ }
+
+
+ /* Apply the piercings that we found */
+ for (i = 0; i < dun->wall_n; i++)
+ {
+ /* Access the grid */
+ y = dun->wall[i].y;
+ x = dun->wall[i].x;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Clear previous contents, add up floor */
+ place_floor(y, x);
+
+ /* Occasional doorway */
+ if (!(dungeon_flags1 & DF1_NO_DOORS) &&
+ (rand_int(100) < DUN_TUN_PEN))
+ {
+ /* Place a random door */
+ place_random_door(y, x);
+ }
+ }
+}
+
+
+
+
+/*
+ * Count the number of "corridor" grids adjacent to the given grid.
+ *
+ * Note -- Assumes "in_bounds(y1, x1)"
+ *
+ * XXX XXX This routine currently only counts actual "empty floor"
+ * grids which are not in rooms. We might want to also count stairs,
+ * open doors, closed doors, etc.
+ */
+static int next_to_corr(int y1, int x1)
+{
+ int i, y, x, k = 0;
+
+ cave_type *c_ptr;
+
+ /* Scan adjacent grids */
+ for (i = 0; i < 4; i++)
+ {
+ /* Extract the location */
+ y = y1 + ddy_ddd[i];
+ x = x1 + ddx_ddd[i];
+
+ /* Skip non floors */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Access the grid */
+ c_ptr = &cave[y][x];
+
+ /* Skip non "empty floor" grids */
+ if ((c_ptr->feat != d_info[dungeon_type].floor1) &&
+ (c_ptr->feat != d_info[dungeon_type].floor2) &&
+ (c_ptr->feat != d_info[dungeon_type].floor3))
+ {
+ continue;
+ }
+
+ /* Skip grids inside rooms */
+ if (c_ptr->info & (CAVE_ROOM)) continue;
+
+ /* Count these grids */
+ k++;
+ }
+
+ /* Return the number of corridors */
+ return (k);
+}
+
+
+/*
+ * Determine if the given location is "between" two walls,
+ * and "next to" two corridor spaces. XXX XXX XXX
+ *
+ * Assumes "in_bounds(y,x)"
+ */
+static bool_ possible_doorway(int y, int x)
+{
+ /* Count the adjacent corridors */
+ if (next_to_corr(y, x) >= 2)
+ {
+ /* Check Vertical */
+ if ((f_info[cave[y - 1][x].feat].flags1 & FF1_WALL) &&
+ (f_info[cave[y + 1][x].feat].flags1 & FF1_WALL))
+ {
+ return (TRUE);
+ }
+
+ /* Check Horizontal */
+ if ((f_info[cave[y][x - 1].feat].flags1 & FF1_WALL) &&
+ (f_info[cave[y][x + 1].feat].flags1 & FF1_WALL))
+ {
+ return (TRUE);
+ }
+ }
+
+ /* No doorway */
+ return (FALSE);
+}
+
+
+/*
+ * Places doors around y, x position
+ */
+static void try_doors(int y, int x)
+{
+ bool_ dir_ok[4];
+ int i, k, n;
+ int yy, xx;
+
+ /* Paranoia */
+ /* if (!in_bounds(y, x)) return; */
+
+ /* Some dungeons don't have doors at all */
+ if (dungeon_flags1 & (DF1_NO_DOORS)) return;
+
+ /* Reset tally */
+ n = 0;
+
+ /* Look four cardinal directions */
+ for (i = 0; i < 4; i++)
+ {
+ /* Assume NG */
+ dir_ok[i] = FALSE;
+
+ /* Access location */
+ yy = y + ddy_ddd[i];
+ xx = x + ddx_ddd[i];
+
+ /* Out of level boundary */
+ if (!in_bounds(yy, xx)) continue;
+
+ /* Ignore walls */
+ if (f_info[cave[yy][xx].feat].flags1 & (FF1_WALL)) continue;
+
+ /* Ignore room grids */
+ if (cave[yy][xx].info & (CAVE_ROOM)) continue;
+
+ /* Not a doorway */
+ if (!possible_doorway(yy, xx)) continue;
+
+ /* Accept the direction */
+ dir_ok[i] = TRUE;
+
+ /* Count good spots */
+ n++;
+ }
+
+ /* Use the traditional method 75% of time */
+ if (rand_int(100) < 75)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ /* Bad locations */
+ if (!dir_ok[i]) continue;
+
+ /* Place one of various kinds of doors */
+ if (rand_int(100) < DUN_TUN_JCT)
+ {
+ /* Access location */
+ yy = y + ddy_ddd[i];
+ xx = x + ddx_ddd[i];
+
+ /* Place a door */
+ place_random_door(yy, xx);
+ }
+ }
+ }
+
+ /* Use alternative method */
+ else
+ {
+ /* A crossroad */
+ if (n == 4)
+ {
+ /* Clear OK flags XXX */
+ for (i = 0; i < 4; i++) dir_ok[i] = FALSE;
+
+ /* Put one or two secret doors */
+ dir_ok[rand_int(4)] = TRUE;
+ dir_ok[rand_int(4)] = TRUE;
+ }
+
+ /* A T-shaped intersection or two possible doorways */
+ else if ((n == 3) || (n == 2))
+ {
+ /* Pick one random location from the list */
+ k = rand_int(n);
+
+ for (i = 0; i < 4; i++)
+ {
+ /* Reject all but k'th OK direction */
+ if (dir_ok[i] && (k-- != 0)) dir_ok[i] = FALSE;
+ }
+ }
+
+ /* Place secret door(s) */
+ for (i = 0; i < 4; i++)
+ {
+ /* Bad location */
+ if (!dir_ok[i]) continue;
+
+ /* Access location */
+ yy = y + ddy_ddd[i];
+ xx = x + ddx_ddd[i];
+
+ /* Place a secret door */
+ place_secret_door(yy, xx);
+ }
+ }
+}
+
+
+/*
+ * Attempt to build a room of the given type at the given block
+ *
+ * Note that we restrict the number of "crowded" rooms to reduce
+ * the chance of overflowing the monster list during level creation.
+ */
+static bool_ room_build(int y, int x, int typ)
+{
+ /* Restrict level */
+ if ((dun_level < roomdep[typ]) && !ironman_rooms) return (FALSE);
+
+ /* Restrict "crowded" rooms */
+ if (dun->crowded && ((typ == 5) || (typ == 6))) return (FALSE);
+
+ /* Build a room */
+ switch (typ)
+ {
+ /* Build an appropriate room */
+ case 12:
+ build_type12(y, x);
+ break;
+ case 11:
+ build_type11(y, x);
+ break;
+ case 10:
+ build_type10(y, x);
+ break;
+ case 9:
+ build_type9 (y, x);
+ break;
+ case 8:
+ build_type8 (y, x);
+ break;
+ case 7:
+ build_type7 (y, x);
+ break;
+ case 6:
+ build_type6 (y, x);
+ break;
+ case 5:
+ build_type5 (y, x);
+ break;
+ case 4:
+ build_type4 (y, x);
+ break;
+ case 3:
+ build_type3 (y, x);
+ break;
+ case 2:
+ build_type2 (y, x);
+ break;
+ case 1:
+ build_type1 (y, x);
+ break;
+
+ /* Paranoia */
+ default:
+ return (FALSE);
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * Set level boundaries
+ */
+static void set_bounders(bool_ empty_level)
+{
+ int y, x;
+
+ /* Special boundary walls -- Top */
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[0][x].mimic = fill_type[rand_int(100)];
+ else cave[0][x].mimic = cave[0][x].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(0, x, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- Bottom */
+ for (x = 0; x < cur_wid; x++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[cur_hgt - 1][x].mimic = fill_type[rand_int(100)];
+ else cave[cur_hgt - 1][x].mimic = cave[cur_hgt - 1][x].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(cur_hgt - 1, x, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- Left */
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[y][0].mimic = fill_type[rand_int(100)];
+ else cave[y][0].mimic = cave[y][0].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(y, 0, FEAT_PERM_SOLID);
+ }
+
+ /* Special boundary walls -- Right */
+ for (y = 1; y < cur_hgt - 1; y++)
+ {
+ /* XXX XXX */
+ if (empty_level) cave[y][cur_wid - 1].mimic = fill_type[rand_int(100)];
+ else cave[y][cur_wid - 1].mimic = cave[y][cur_wid - 1].feat;
+
+ /* Clear previous contents, add "solid" perma-wall */
+ cave_set_feat(y, cur_wid - 1, FEAT_PERM_SOLID);
+ }
+}
+
+/* Needed to refill empty levels */
+static void fill_level(bool_ use_floor, byte smooth);
+
+/*
+ * Generate a normal dungeon level
+ */
+bool_ level_generate_dungeon()
+{
+ int i, k, y, x, y1, x1, branch = get_branch();
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ int max_vault_ok = 2;
+
+ bool_ destroyed = FALSE;
+ bool_ empty_level = FALSE;
+ bool_ cavern = FALSE;
+ s16b town_level = 0;
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+ /* Check for arena level */
+ if ((dungeon_flags1 & (DF1_EMPTY)) ||
+ (empty_levels && (rand_int(EMPTY_LEVEL) == 0)))
+ {
+ empty_level = TRUE;
+
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print("Arena level.");
+ }
+
+ /* Refill the level with floor tiles */
+ fill_level(empty_level, d_ptr->fill_method);
+ }
+
+ /* Possible cavern */
+ if ((dungeon_flags1 & DF1_CAVERN) && (rand_int(dun_level / 2) > DUN_CAVERN))
+ {
+ cavern = TRUE;
+
+ /* Make a large fractal cave in the middle of the dungeon */
+ if (cheat_room)
+ {
+ msg_print("Cavern on level.");
+ }
+
+ build_cavern();
+ }
+
+ /* Possible "destroyed" level */
+ if ((dun_level > 10) && (rand_int(DUN_DEST) == 0))
+ {
+ destroyed = TRUE;
+ }
+
+ /* Hack -- No destroyed "quest" levels */
+ if (is_quest(dun_level)) destroyed = FALSE;
+
+ /* Hack -- No destroyed "small" levels */
+ if ((cur_wid != MAX_WID) || (cur_hgt != MAX_HGT)) destroyed = FALSE;
+
+ /* Hack -- No destroyed levels */
+ if (dungeon_flags1 & DF1_NO_DESTROY) destroyed = FALSE;
+
+ /* Actual maximum number of rooms on this level */
+ dun->row_rooms = cur_hgt / BLOCK_HGT;
+ dun->col_rooms = cur_wid / BLOCK_WID;
+
+
+ /* Initialize the room table */
+ for (y = 0; y < dun->row_rooms; y++)
+ {
+ for (x = 0; x < dun->col_rooms; x++)
+ {
+ dun->room_map[y][x] = FALSE;
+ }
+ }
+
+ /* No "crowded" rooms yet */
+ dun->crowded = FALSE;
+
+ /* No rooms yet */
+ dun->cent_n = 0;
+
+ /* Pick a block for the room */
+ y = rand_int(dun->row_rooms);
+ x = rand_int(dun->col_rooms);
+
+ /* Align dungeon rooms */
+ if (dungeon_align)
+ {
+ /* Slide some rooms right */
+ if ((x % 3) == 0) x++;
+
+ /* Slide some rooms left */
+ if ((x % 3) == 2) x--;
+ }
+
+ /* Ugly */
+ {
+ struct hook_build_room1_in in = { y, x };
+ process_hooks_new(HOOK_BUILD_ROOM1, &in, NULL);
+ }
+
+ /* 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))
+ {
+ /* 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;
+ }
+
+ /* 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
+ */
+static void replace_all_friends()
+{
+ if (p_ptr->wild_mode)
+ {
+ return;
+ }
+
+ /* Scan every saved pet */
+ for (int i = 0; i < max_m_idx; i++)
+ {
+ if ((km_list[i].r_idx) && (km_list[i].status == MSTATUS_COMPANION))
+ {
+ int y = p_ptr->py;
+ int x = p_ptr->px;
+
+ /* Find a suitable location */
+ get_pos_player(5, &y, &x);
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Get a m_idx to use */
+ c_ptr->m_idx = m_pop();
+ monster_type *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_idxs.clear(); // Objects have been removed previously by caller
+ }
+ }
+}
+
+/*
+ * Save the imprinted pets from the old level
+ */
+static void save_all_friends()
+{
+ if (p_ptr->old_wild_mode) return;
+
+ for (int i = 0; i < max_m_idx; i++) {
+ km_list[i] = m_list[i];
+ }
+}
+
+
+
+/*
+ * Build probability tables for walls and floors and set feat_wall_outer
+ * and feat_wall_inner according to the current information in d_info.txt
+ *
+ * *hint* *hint* with this made extern, and we no longer have to
+ * store fill_type and floor_type in the savefile...
+ */
+static void init_feat_info(void)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ int i;
+ int cur_depth, max_depth;
+ int p1, p2;
+ int floor_lim1, floor_lim2;
+ int fill_lim1, fill_lim2;
+
+
+ /* Retrieve dungeon depth info (base 1, to avoid zero divide errors) */
+ cur_depth = (dun_level - d_ptr->mindepth) + 1;
+ max_depth = (d_ptr->maxdepth - d_ptr->mindepth) + 1;
+
+
+ /* Set room wall types */
+ feat_wall_outer = d_ptr->outer_wall;
+ feat_wall_inner = d_ptr->inner_wall;
+
+
+ /* Setup probability info -- Floors */
+ p1 = d_ptr->floor_percent1[0];
+ p2 = d_ptr->floor_percent1[1];
+ floor_lim1 = p1 + (p2 - p1) * cur_depth / max_depth;
+
+ p1 = d_ptr->floor_percent2[0];
+ p2 = d_ptr->floor_percent2[1];
+ floor_lim2 = floor_lim1 + p1 + (p2 - p1) * cur_depth / max_depth;
+
+ /* Setup probability info -- Fillers */
+ p1 = d_ptr->fill_percent1[0];
+ p2 = d_ptr->fill_percent1[1];
+ fill_lim1 = p1 + (p2 - p1) * cur_depth / max_depth;
+
+ p1 = d_ptr->fill_percent2[0];
+ p2 = d_ptr->fill_percent2[1];
+ fill_lim2 = fill_lim1 + p1 + (p2 - p1) * cur_depth / max_depth;
+
+
+ /* Fill the arrays of floors and walls in the good proportions */
+ for (i = 0; i < 100; i++)
+ {
+ if (i < floor_lim1)
+ {
+ floor_type[i] = d_ptr->floor1;
+ }
+ else if (i < floor_lim2)
+ {
+ floor_type[i] = d_ptr->floor2;
+ }
+ else
+ {
+ floor_type[i] = d_ptr->floor3;
+ }
+
+ if (i < fill_lim1)
+ {
+ fill_type[i] = d_ptr->fill_type1;
+ }
+ else if (i < fill_lim2)
+ {
+ fill_type[i] = d_ptr->fill_type2;
+ }
+ else
+ {
+ fill_type[i] = d_ptr->fill_type3;
+ }
+ }
+}
+
+
+/*
+ * Fill a level with wall type specified in A: or L: line of d_info.txt
+ *
+ * 'use_floor', when it is TRUE, tells the function to use floor type
+ * terrains (L:) instead of walls (A:).
+ *
+ * Filling behaviour can be controlled by the second parameter 'smooth',
+ * with the following options available:
+ *
+ * smooth behaviour
+ * ------ ------------------------------------------------------------
+ * 0 Fill the entire level with fill_type1 / floor1
+ * 1 All the grids are randomly selected (== --P5.1.2)
+ * 2 Slightly smoothed -- look like scattered patches
+ * 3 More smoothed -- tend to look like caverns / small scale map
+ * 4-- Max smoothing -- tend to look like landscape/island/
+ * continent etc.
+ *
+ * I put it here, because there's another filler generator in
+ * wild.c, but it works better there, in fact...
+ *
+ * CAVEAT: smoothness of 3 or greater doesn't work well with the
+ * current secret door implementation. Outer walls also need some
+ * rethinking.
+ *
+ * -- pelpel
+ */
+
+/*
+ * Thou shalt not invoke the name of thy RNG in vain.
+ * The Angband RNG generates 28 bit pseudo-random number, hence
+ * 28 / 2 = 14
+ */
+#define MAX_SHIFTS 14
+
+static void fill_level(bool_ use_floor, byte smooth)
+{
+ int y, x;
+ int step;
+ int shift;
+
+
+ /* Convert smoothness to initial step */
+ if (smooth == 0) step = 0;
+ else if (smooth == 1) step = 1;
+ else if (smooth == 2) step = 2;
+ else if (smooth == 3) step = 4;
+ else step = 8;
+
+ /*
+ * Paranoia -- step must be less than or equal to a half of
+ * width or height, whichever shorter
+ */
+ if ((cur_hgt < 16) && (step > 4)) step = 4;
+ if ((cur_wid < 16) && (step > 4)) step = 4;
+
+
+ /* Special case -- simple fill */
+ if (step == 0)
+ {
+ byte filler;
+
+ /* Pick a filler XXX XXX XXX */
+ if (use_floor) filler = d_info[dungeon_type].floor1;
+ else filler = d_info[dungeon_type].fill_type1;
+
+ /* Fill the level with the filler without calling RNG */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, filler);
+ }
+ }
+
+ /* Done */
+ return;
+ }
+
+
+ /*
+ * Fill starting positions -- every 'step' grids horizontally and
+ * vertically
+ */
+ for (y = 0; y < cur_hgt; y += step)
+ {
+ for (x = 0; x < cur_wid; x += step)
+ {
+ /*
+ * Place randomly selected terrain feature using the prebuilt
+ * probability table
+ *
+ * By slightly modifying this, you can build streamers as
+ * well as normal fillers all at once, but this calls for
+ * modifications to the other part of the dungeon generator.
+ */
+ if (use_floor) place_floor(y, x);
+ else place_filler(y, x);
+ }
+ }
+
+
+ /*
+ * Fill spaces between, randomly picking one of their neighbours
+ *
+ * This simple yet powerful algorithm was described by Mike Anderson:
+ *
+ * A B A | B A a B
+ * -> --+-- -> d e b
+ * D C D | C D c C
+ *
+ * a can be either A or B, b B or C, c C or D and d D or A.
+ * e is chosen from A, B, C and D.
+ * Subdivide and repeat the process as many times as you like.
+ *
+ * All the nasty tricks that obscure this simplicity are mine (^ ^;)
+ */
+
+ /* Initialise bit shift counter */
+ shift = MAX_SHIFTS;
+
+ /* Repeat subdivision until all the grids are filled in */
+ while ((step = step >> 1) > 0)
+ {
+ bool_ y_even, x_even;
+ s16b y_wrap, x_wrap;
+ s16b y_sel, x_sel;
+ u32b selector = 0;
+
+ /* Hacklette -- Calculate wrap-around locations */
+ y_wrap = ((cur_hgt - 1) / (step * 2)) * (step * 2);
+ x_wrap = ((cur_wid - 1) / (step * 2)) * (step * 2);
+
+ /* Initialise vertical phase */
+ y_even = 0;
+
+ for (y = 0; y < cur_hgt; y += step)
+ {
+ /* Flip vertical phase */
+ y_even = !y_even;
+
+ /* Initialise horizontal phase */
+ x_even = 0;
+
+ for (x = 0; x < cur_wid; x += step)
+ {
+ /* Flip horizontal phase */
+ x_even = !x_even;
+
+ /* Already filled in by previous iterations */
+ if (y_even && x_even) continue;
+
+ /*
+ * Retrieve next two bits from pseudo-random bit sequence
+ *
+ * You can do well not caring so much about their randomness.
+ *
+ * This is not really necessary, but I don't like to invoke
+ * relatively expensive RNG when we can do with much smaller
+ * number of calls.
+ */
+ if (shift >= MAX_SHIFTS)
+ {
+ selector = rand_int(0x10000000L);
+ shift = 0;
+ }
+ else
+ {
+ selector >>= 2;
+ shift++;
+ }
+
+ /* Vertically in sync */
+ if (y_even) y_sel = y;
+
+ /* Bit 1 selects neighbouring y */
+ else y_sel = (selector & 2) ? y + step : y - step;
+
+ /* Horizontally in sync */
+ if (x_even) x_sel = x;
+
+ /* Bit 0 selects neighbouring x */
+ else x_sel = (selector & 1) ? x + step : x - step;
+
+ /* Hacklette -- Fix out of range indices by wrapping around */
+ if (y_sel >= cur_hgt) y_sel = 0;
+ else if (y_sel < 0) y_sel = y_wrap;
+ if (x_sel >= cur_wid) x_sel = 0;
+ else if (x_sel < 0) x_sel = x_wrap;
+
+ /*
+ * Fill the grid with terrain feature of the randomly
+ * picked up neighbour
+ */
+ cave_set_feat(y, x, cave[y_sel][x_sel].feat);
+ }
+ }
+ }
+}
+
+
+/**
+ * @brief double a grid tile. Used for the double-size dungeons
+ */
+static void supersize_grid_tile(int sy, int sx, int ty, int tx)
+{
+ /* Displacements for copied grid tiles */
+ constexpr std::size_t n_disp = 4;
+ int disp[n_disp][2] = {
+ { 0, 0 },
+ { 0, +1 },
+ { +1, 0 },
+ { +1, +1 }
+ };
+
+ /* Acquire the grid tile and monster */
+ cave_type *cc_ptr = &cave[sy][sx];
+ monster_type *m_ptr = &m_list[cc_ptr->m_idx];
+
+ /* Save the list of objects */
+ auto const object_idxs(cc_ptr->o_idxs);
+
+ /* Save the monster */
+ auto m_idx = cc_ptr->m_idx;
+
+ /* Create pointers to each of the target grid tiles */
+ cave_type *c_ptr[n_disp];
+ for (std::size_t i = 0; i < n_disp; i++)
+ {
+ c_ptr[i] = &cave[ty + disp[i][0]][tx + disp[i][1]];
+ }
+
+ /* Now we copy around the grid tiles. Objects and
+ monsters are "removed" for now. */
+ for (std::size_t i = 0; i < 4; i++)
+ {
+ c_ptr[i] = &cave[ty + disp[i][0]][tx + disp[i][1]];
+
+ /* Copy grid */
+ *c_ptr[i] = *cc_ptr;
+ c_ptr[i]->o_idxs.clear(); // ... except objects in the tile
+ c_ptr[i]->m_idx = 0; // ... except monsters in the tile
+
+ /* Void gates need special attention */
+ 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);
+ }
+ }
+
+ /* Scatter objects randomly into the destination grid tiles */
+ for (auto const o_idx: object_idxs)
+ {
+ std::size_t i = static_cast<std::size_t>(rand_int(4));
+ /* Put object into grid tile */
+ c_ptr[i]->o_idxs.push_back(o_idx);
+ /* Give object its location */
+ object_type *o_ptr = &o_list[o_idx];
+ o_ptr->iy = ty + disp[i][0];
+ o_ptr->ix = tx + disp[i][1];
+ }
+
+ /* Scatter move monster randomly into one of the destination grid tiles */
+ if (m_idx != 0)
+ {
+ std::size_t i = static_cast<std::size_t>(rand_int(4));
+ /* Place monster into grid tile */
+ c_ptr[i]->m_idx = cc_ptr->m_idx;
+ /* Give the monster its location */
+ m_ptr->fy = ty + disp[i][0];
+ m_ptr->fx = tx + disp[i][1];
+ }
+}
+
+
+/*
+ * Generate a new dungeon level
+ *
+ * Note that "dun_body" adds about 4000 bytes of memory to the stack.
+ */
+static bool_ cave_gen(void)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ int max_vault_ok = 2;
+
+ bool_ empty_level = FALSE;
+
+ level_generator_type *generator;
+
+ dun_data dun_body;
+
+ char generator_name[100];
+
+ if (!get_dungeon_generator(generator_name))
+ strnfmt(generator_name, 99, "%s", d_ptr->generator);
+
+ /*
+ * We generate a double dungeon. First we should halve the desired
+ * width/height, generate the dungeon normally, then double it
+ * in both directions
+ */
+ if (dungeon_flags1 & DF1_DOUBLE)
+ {
+ cur_wid /= 2;
+ cur_hgt /= 2;
+ }
+
+ /* Fill the arrays of floors and walls in the good proportions */
+ init_feat_info();
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Global data */
+ dun = &dun_body;
+
+ if (!(max_panel_rows)) max_vault_ok--;
+ if (!(max_panel_cols)) max_vault_ok--;
+
+ /*
+ * Hack -- Start with fill_type's
+ *
+ * Need a way to know appropriate smoothing factor for the current
+ * dungeon. Maybe we need another d_info flag/value.
+ */
+ fill_level(empty_level, d_ptr->fill_method);
+
+ set_bounders(empty_level);
+
+ /*
+ * Call the good level generator
+ */
+ generator = level_generators;
+ while (generator)
+ {
+ if (!strcmp(generator->name, generator_name))
+ {
+ if (!generator->generator())
+ return FALSE;
+ break;
+ }
+
+ generator = generator->next;
+ }
+
+ /* Generate stairs */
+ {
+ /* Is there a dungeon branch ? */
+ if (int branch = get_branch())
+ {
+ /* Place 5 down stair some walls */
+ alloc_stairs(FEAT_MORE, 5, 3, branch);
+ }
+
+ /* Is there a father dungeon branch ? */
+ if (int 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_new(HOOK_GEN_LEVEL, NULL, NULL);
+
+ /* Basic "amount" */
+ int k = (dun_level / 3);
+ if (k > 10) k = 10;
+ if (k < 2) k = 2;
+
+ /* Place monsters */
+ {
+
+ /*
+ * Pick a base number of monsters
+ */
+ int 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 (std::size_t 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 (std::size_t 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;
+ }
+ }
+ }
+
+ /* Place traps and rubble */
+ {
+ /* 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));
+ }
+
+ /* Place objects and treasure */
+ {
+ /* 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));
+ }
+
+ /* Place random features such as altars and void gates, etc. */
+ {
+ /* Put some altars */
+ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_ALTAR, randnor(DUN_AMT_ALTAR, 3));
+
+ /* Put some between gates */
+ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_BETWEEN, randnor(DUN_AMT_BETWEEN, 3));
+
+ /* Put some fountains */
+ alloc_object(ALLOC_SET_ROOM, ALLOC_TYP_FOUNTAIN, randnor(DUN_AMT_FOUNTAIN, 3));
+ }
+
+ /* Put an Artifact and Artifact Guardian is requested */
+ if (d_ptr->final_guardian && (d_ptr->maxdepth == dun_level))
+ {
+ int oy;
+ int ox;
+ int m_idx, tries = 10000;
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ oy = randint(cur_hgt - 4) + 2;
+ ox = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(oy, ox)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place the guardian */
+ m_allow_special[d_ptr->final_guardian] = TRUE;
+ place_monster_one(oy, ox, d_ptr->final_guardian, 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[d_ptr->final_guardian] = FALSE;
+
+
+ m_idx = cave[oy][ox].m_idx;
+
+ if (!m_idx && wizard) cmsg_print(TERM_L_RED, "WARNING: Could not place guardian.");
+
+ /*
+ * If guardian is successfully created and his/her/its
+ * treasure hasn't been found, let him/her/it own that
+ */
+ if (m_idx && d_ptr->final_artifact &&
+ (a_info[d_ptr->final_artifact].cur_num == 0))
+ {
+ artifact_type *a_ptr = &a_info[d_ptr->final_artifact];
+ object_type *q_ptr, forge, *o_ptr;
+ int I_kind, o_idx;
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ /* Proceed only if there's an object slot available */
+ if (o_idx)
+ {
+ a_allow_special[d_ptr->final_artifact] = TRUE;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = d_ptr->final_artifact;
+
+ /* Actually create it */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Where it is found ? */
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = d_ptr->final_guardian;
+ q_ptr->found_aux2 = 0;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ a_allow_special[d_ptr->final_artifact] = FALSE;
+
+ /* Get the item */
+ o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idxs.push_back(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->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idxs.push_back(o_idx);
+ }
+ }
+ }
+
+ if ((empty_level) && (randint(DARK_EMPTY) != 1 || (randint(100) > dun_level)))
+ wiz_lite();
+
+ /* Now double the generated dungeon */
+ if (dungeon_flags1 & DF1_DOUBLE)
+ {
+ /*
+ * We begin at the bottom-right corner and move upwards
+ * to the left. This avoids the need for an extra copy of
+ * the cave array.
+ *
+ * We double the border permanent walls, too.
+ */
+ int y = cur_hgt - 1;
+ int y1 = y * 2;
+ for (; y >= 0; y--, y1 -= 2)
+ {
+ int x = cur_wid - 1;
+ int x1 = x * 2;
+ for (; x >= 0; x--, x1 -= 2)
+ {
+ supersize_grid_tile(y, x, y1, x1);
+ }
+ }
+
+ /* 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;
+}
+
+
+
+/*
+ * Creates a special level
+ */
+
+/* Mega-Hack */
+#define REGEN_HACK 0x02
+
+bool_ build_special_level(void)
+{
+ char buf[80];
+ int y, x, ystart = 2, xstart = 2;
+ s16b level;
+
+ /* No special levels on the surface */
+ if (!dun_level) return FALSE;
+
+ level = dun_level - d_info[dungeon_type].mindepth;
+ if ((!get_dungeon_save(buf)) && (special_lvl[level][dungeon_type])) return FALSE;
+ if (!get_dungeon_special(buf)) return FALSE;
+
+ /* Big town */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON | INIT_POSITION;
+ process_dungeon_file(buf, &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ special_lvl[level][dungeon_type] = REGEN_HACK;
+ generate_special_feeling = TRUE;
+
+ /* Special feeling because it's special */
+ good_item_flag = TRUE;
+
+ /*
+ * Hack -- It's better/more dangerous than a greater vault.
+ * Better to have a rating field in special level description.
+ */
+ rating += 40;
+
+ return TRUE;
+}
+
+/*
+ * Prepare regeneration of a special level, which should not happen,
+ * but just in case...
+ */
+static void wipe_special_level(void)
+{
+ s16b level;
+ char buf[80];
+
+ /* No special levels on the surface */
+ if (!dun_level) return;
+
+ process_hooks_new(HOOK_LEVEL_REGEN, NULL, NULL);
+
+ /* 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_new(HOOK_LEVEL_END_GEN, NULL, NULL);
+
+ /* 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
+ */
+static void generate_grid_mana()
+{
+ int y, x, mana, mult;
+ bool_ xtra_magic = FALSE;
+
+ if (randint(XTRA_MAGIC) == 1)
+ {
+ xtra_magic = TRUE;
+
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print("Magical level");
+ }
+ }
+
+ mult = ((xtra_magic) ? 3 : 2);
+
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Calculate the amount of mana in each grid */
+ mana = mult * m_bonus(255, dun_level) / 2;
+ if (xtra_magic) mana += 10 + rand_int(10);
+
+ /* Never more than 255 or less than 0(paranoia) */
+ if (mana < 0) mana = 0;
+ if (mana > 255) mana = 255;
+
+ c_ptr->mana = mana;
+ }
+ }
+}
+
+
+/*
+ * Generates a random dungeon level -RAK-
+ *
+ * Hack -- regenerate any "overflow" levels
+ *
+ * Hack -- allow auto-scumming via a gameplay option.
+ */
+void generate_cave(void)
+{
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+ int tester_1, tester_2;
+ int y, x, num, i;
+ bool_ loaded = FALSE;
+ char buf[80];
+ s16b town_level = 0;
+
+ /* The dungeon is not ready */
+ character_dungeon = FALSE;
+ generate_special_feeling = FALSE;
+
+ /* Initialize the flags with the basic dungeon flags */
+ if (!dun_level)
+ {
+ dungeon_flags1 = d_info[DUNGEON_WILDERNESS].flags1;
+ dungeon_flags2 = d_info[DUNGEON_WILDERNESS].flags2;
+ }
+ else
+ {
+ dungeon_flags1 = d_ptr->flags1;
+ dungeon_flags2 = d_ptr->flags2;
+ }
+
+ /* Is it a town level ? */
+ for (i = 0; i < TOWN_DUNGEON; i++)
+ {
+ if (d_ptr->t_level[i] == dun_level) town_level = d_ptr->t_idx[i];
+ }
+
+ /* Save the imprinted monsters */
+ save_all_friends();
+ wipe_m_list();
+
+ /* Seed the RNG if appropriate */
+ if (town_level)
+ {
+ Rand_quick = TRUE;
+ Rand_value = town_info[town_level].seed;
+ }
+
+ process_hooks_new(HOOK_GEN_LEVEL_BEGIN, NULL, NULL);
+
+ /* 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++)
+ {
+ /* Wipe */
+ cave[y][x].wipe();
+
+ /* No features */
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+ }
+ }
+
+ 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++)
+ {
+ /* Wipe */
+ cave[y][x].wipe();
+
+ /* No features */
+ cave_set_feat(y, x, FEAT_PERM_INNER);
+ }
+ }
+
+
+ /* 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;
+
+ /* Quest levels -KMW- */
+ if (p_ptr->inside_quest)
+ {
+ process_hooks_new(HOOK_GEN_QUEST, NULL, NULL);
+ }
+
+ /* Special levels */
+ else if (build_special_level())
+ {
+ /* nothing */
+ }
+
+ /* Build the town */
+ else if (!dun_level)
+ {
+ /* Big town */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ /* Big wilderness mode */
+ if (!p_ptr->wild_mode)
+ {
+ /* Make the wilderness */
+ wilderness_gen();
+ }
+
+ /* Small wilderness mode */
+ else
+ {
+ /* Make the wilderness */
+ wilderness_gen_small();
+ }
+
+
+ okay = TRUE;
+ }
+
+ /* Build a dungeon level */
+ else
+ {
+ /* Requested size level */
+ if (d_ptr->size_x != -1)
+ {
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print ("A 'size' dungeon level.");
+ }
+
+ cur_hgt = d_ptr->size_y * SCREEN_HGT;
+ cur_wid = d_ptr->size_x * SCREEN_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ if (cheat_room)
+ {
+ msg_format("X:%d, Y:%d.", max_panel_cols, max_panel_rows);
+ }
+ }
+ /* Very small (1 x 1 panel) level */
+ else if (!(dungeon_flags1 & DF1_BIG) &&
+ (dungeon_flags1 & DF1_SMALLEST))
+ {
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print ("A 'small' dungeon level.");
+ }
+
+ cur_hgt = SCREEN_HGT;
+ cur_wid = SCREEN_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = 1;
+ max_panel_cols = 1;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ if (cheat_room)
+ {
+ msg_format("X:1, Y:1.");
+ }
+ }
+
+ /* Small level */
+ else if (!(dungeon_flags1 & DF1_BIG) &&
+ (always_small_level ||
+ (dungeon_flags1 & DF1_SMALL) ||
+ (small_levels && rand_int(SMALL_LEVEL) == 0)))
+ {
+ if (cheat_room || p_ptr->precognition)
+ {
+ msg_print ("A 'small' dungeon level.");
+ }
+
+ tester_1 = rand_range(1, (MAX_HGT / SCREEN_HGT));
+ tester_2 = rand_range(1, (MAX_WID / SCREEN_WID) - 1);
+
+ cur_hgt = tester_1 * SCREEN_HGT;
+ cur_wid = tester_2 * SCREEN_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+
+ if (cheat_room)
+ {
+ msg_format("X:%d, Y:%d.", max_panel_cols, max_panel_rows);
+ }
+ }
+
+ /* Normal level */
+ else
+ {
+ /* Use full panels */
+ cur_hgt = MAX_HGT;
+ cur_wid = MAX_WID;
+
+ /* Determine number of panels */
+ max_panel_rows = (cur_hgt / SCREEN_HGT) * 2 - 2;
+ max_panel_cols = (cur_wid / SCREEN_WID) * 2 - 2;
+
+ /* Assume illegal panel */
+ panel_row_min = max_panel_rows * (SCREEN_HGT / 2);
+ panel_col_min = max_panel_cols * (SCREEN_WID / 2);
+ }
+
+ /* Generate a level */
+ if (!cave_gen())
+ {
+ why = "could not place player";
+ okay = FALSE;
+ }
+ }
+
+ /* Extract the feeling */
+ if (rating > 100) feeling = 2;
+ else if (rating > 80) feeling = 3;
+ else if (rating > 60) feeling = 4;
+ else if (rating > 40) feeling = 5;
+ else if (rating > 30) feeling = 6;
+ else if (rating > 20) feeling = 7;
+ else if (rating > 10) feeling = 8;
+ else if (rating > 0) feeling = 9;
+ else feeling = 10;
+
+ /* Hack -- Have a special feeling sometimes */
+ if (good_item_flag && !p_ptr->preserve) feeling = 1;
+
+ /* It takes 1000 game turns for "feelings" to recharge */
+ if ((turn - old_turn) < 1000) feeling = 0;
+
+ /* Hack -- no feeling in the town */
+ if (!dun_level) feeling = 0;
+
+
+ /* Prevent object over-flow */
+ if (o_max >= max_o_idx)
+ {
+ /* Message */
+ why = "too many objects";
+
+ /* Message */
+ okay = FALSE;
+ }
+
+ /* Prevent monster over-flow */
+ if (m_max >= max_m_idx)
+ {
+ /* Message */
+ why = "too many monsters";
+
+ /* Message */
+ okay = FALSE;
+ }
+
+ /* Mega-Hack -- "auto-scum" */
+ if (auto_scum && (num < 100) && !p_ptr->inside_quest && dun_level)
+ {
+ /* Require "goodness" */
+ if ((feeling > 9) ||
+ ((dun_level >= 5) && (feeling > 8)) ||
+ ((dun_level >= 10) && (feeling > 7)) ||
+ ((dun_level >= 20) && (feeling > 6)) ||
+ ((dun_level >= 40) && (feeling > 5)))
+ {
+ /* Give message to cheaters */
+ if (cheat_room || cheat_hear ||
+ cheat_peek || cheat_xtra || p_ptr->precognition)
+ {
+ /* Message */
+ why = "boring level";
+ }
+
+ /* Try again */
+ okay = FALSE;
+ }
+ }
+
+ /* Accept */
+ if (okay || town_level) break;
+
+ /* Message */
+ if (why) msg_format("Generation restarted (%s)", why);
+
+ /* Wipe the objects */
+ wipe_o_list();
+
+ /* Wipe the monsters */
+ wipe_m_list();
+
+ /* Clear the fate icky flags */
+ for (i = 0; i < MAX_FATES; i++) fates[i].icky = FALSE;
+
+ /*
+ * Mega-Hack -- Reset special level flag if necessary
+ * XXX XXX XXX
+ */
+ wipe_special_level();
+ }
+
+ /* Give some mana to each grid -- DG */
+ generate_grid_mana();
+ }
+
+ /* Put the kept monsters -- DG */
+ if (!p_ptr->wild_mode)
+ {
+ replace_all_friends();
+ }
+
+ /* Hack -- Clear used up fates */
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if (fates[i].icky)
+ {
+ /* Mark the artefact as generated */
+ if ((fates[i].fate == FATE_FIND_A) && fates[i].a_idx)
+ {
+ a_info[fates[i].a_idx].cur_num = 1;
+ }
+ fates[i].fate = FATE_NONE;
+ fates[i].icky = FALSE;
+ }
+ }
+
+ /* Set special level generated flag if applicable */
+ finalise_special_level();
+
+ /* No teleporatations yet */
+ last_teleportation_y = -1;
+ last_teleportation_x = -1;
+
+ /* Mark the dungeon town as found */
+ if (town_level)
+ {
+ /* Set the known flag */
+ town_info[town_level].flags |= (TOWN_KNOWN);
+ }
+
+ /* The dungeon is ready */
+ character_dungeon = TRUE;
+
+ /* Remember when this level was "created" */
+ old_turn = turn;
+
+ /* Provide astral chars with the full map */
+ if (p_ptr->astral && dun_level)
+ {
+ wiz_lite_extra();
+ }
+
+ /* Player should get the first move upon entering the dungeon */
+ p_ptr->energy = 100;
+}
diff --git a/src/generate.hpp b/src/generate.hpp
new file mode 100644
index 00000000..d09907b5
--- /dev/null
+++ b/src/generate.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern bool_ new_player_spot(int branch);
+extern void add_level_generator(cptr name, bool_ (*generator)());
+extern bool_ level_generate_dungeon();
+extern bool_ generate_fracave(int y0, int x0,int xsize,int ysize,int cutoff,bool_ light,bool_ room);
+extern void generate_hmap(int y0, int x0,int xsiz,int ysiz,int grd,int roug,int cutoff);
+extern bool_ room_alloc(int x,int y,bool_ crowded,int by0,int bx0,int *xx,int *yy);
+extern void generate_cave(void);
+extern void build_rectangle(int y1, int x1, int y2, int x2, int feat, int info);
diff --git a/src/gf_name_type.hpp b/src/gf_name_type.hpp
new file mode 100644
index 00000000..0b17bdbf
--- /dev/null
+++ b/src/gf_name_type.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+/**
+ * GF_XXX descriptor entry.
+ */
+struct gf_name_type
+{
+ int gf;
+ char const *name;
+};
diff --git a/src/gods.cc b/src/gods.cc
new file mode 100644
index 00000000..1163e9b6
--- /dev/null
+++ b/src/gods.cc
@@ -0,0 +1,212 @@
+/*
+ * 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 "gods.hpp"
+
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "skill_type.hpp"
+#include "stats.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra2.hpp"
+
+#include <cassert>
+
+/*
+ * 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);
+ }
+}
+
+/*
+ * Check if god may be followed by player
+ */
+static bool_ may_follow_god(int god)
+{
+ if (god == GOD_MELKOR)
+ {
+ int i;
+
+ /* Check if player has wielded The One Ring */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (p_ptr->inventory[i].name1 == ART_POWER)
+ {
+ msg_print("The One Ring has corrupted "
+ "you, and you are rejected.");
+ return FALSE;
+ }
+ }
+ }
+ /* Default is to allow */
+ return TRUE;
+}
+
+/*
+ * 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 (!may_follow_god(god))
+ return;
+
+ if (p_ptr->pgod == GOD_NONE)
+ {
+ p_ptr->pgod = god;
+
+ /* Melkor offer Udun magic */
+ if (p_ptr->pgod == 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.");
+ }
+ }
+}
+
+/*
+ * Show religious info.
+ */
+bool_ show_god_info()
+{
+ int pgod = p_ptr->pgod;
+
+ deity_type *d_ptr;
+
+ if (pgod < 0)
+ {
+ msg_print("You don't worship anyone.");
+ msg_print(NULL);
+ return FALSE;
+ }
+ else
+ {
+ int i;
+
+ d_ptr = &deity_info[pgod];
+
+ msg_print(NULL);
+
+ character_icky = TRUE;
+ Term_save();
+
+ text_out(format("You worship %s. ", d_ptr->name));
+ for (i = 0; (i < 10) && (strcmp(d_ptr->desc[i], "")); i++)
+ text_out(d_ptr->desc[i]);
+ text_out("\n");
+
+ inkey();
+
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ return TRUE;
+}
+
+/*
+ * Rescale the wisdom value to a 0 <-> max range
+ */
+int wisdom_scale(int max)
+{
+ int i = p_ptr->stat_ind[A_WIS];
+
+ return (i * max) / 37;
+}
+
+/*
+ * Get deity info for a given god index.
+ * Returns NULL for the "atheist" god.
+ */
+deity_type *god_at(byte god_idx)
+{
+ assert(god_idx >= 0);
+ assert(god_idx < MAX_GODS);
+
+ if (god_idx == 0)
+ {
+ return NULL;
+ }
+
+ return &deity_info[god_idx];
+}
+
+/*
+ * Check if god is enabled for the current module
+ */
+bool_ god_enabled(struct deity_type *deity)
+{
+ int i;
+
+ for (i = 0; deity->modules[i] != -1; i++)
+ {
+ if (deity->modules[i] == game_module_idx)
+ {
+ return TRUE;
+ }
+ }
+ /* Not enabled */
+ return FALSE;
+}
+
+/* Find a god by name */
+int find_god(cptr name)
+{
+ int i;
+
+ for (i = 0; i < MAX_GODS; i++)
+ {
+ /* The name matches and god is "enabled" for the
+ current module. */
+ if (god_enabled(&deity_info[i]) &&
+ streq(deity_info[i].name, name))
+ {
+ return (i);
+ }
+ }
+ return -1;
+}
+
+bool praying_to(int god)
+{
+ return (p_ptr->pgod == god) && p_ptr->praying;
+}
diff --git a/src/gods.hpp b/src/gods.hpp
new file mode 100644
index 00000000..7035dd14
--- /dev/null
+++ b/src/gods.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "h-basic.h"
+
+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);
+extern bool_ god_enabled(struct deity_type *deity);
+extern deity_type *god_at(byte god_idx);
+extern bool_ show_god_info();
+extern bool praying_to(int god);
diff --git a/src/h-basic.h b/src/h-basic.h
new file mode 100644
index 00000000..a65780a5
--- /dev/null
+++ b/src/h-basic.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * 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"
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/h-config.h b/src/h-config.h
new file mode 100644
index 00000000..09e9bac8
--- /dev/null
+++ b/src/h-config.h
@@ -0,0 +1,84 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * 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 Windows (automatic)
+ */
+#ifndef WINDOWS
+/* #define WINDOWS */
+#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(__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.
+ * Basically, SET_UID should *only* be set for "Unix" machines.
+ */
+#if !defined(WINDOWS)
+# define SET_UID
+#endif
+
+
+/*
+ * Every system seems to use its own symbol as a path separator.
+ * Default to the standard Unix slash, but attempt to change this
+ * for various other systems. Note that any system that uses the
+ * "period" as a separator (i.e. ACORN) will have to pretend that
+ * it uses the slash, and do its own mapping of period <-> slash.
+ */
+#undef PATH_SEP
+#define PATH_SEP "/"
+#if defined(WINDOWS) || defined(WINNT)
+# undef PATH_SEP
+# define PATH_SEP "\\"
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/h-define.h b/src/h-define.h
new file mode 100644
index 00000000..76e7e339
--- /dev/null
+++ b/src/h-define.h
@@ -0,0 +1,92 @@
+/* File: h-define.h */
+
+#ifndef INCLUDED_H_DEFINE_H
+#define INCLUDED_H_DEFINE_H
+
+/*
+ * Define some simple constants
+ */
+
+
+/*
+ * Hack -- Define NULL
+ */
+#ifndef NULL
+# ifdef __STDC__
+# define NULL ((void*)0)
+# else
+# define NULL ((char*)0)
+# endif /* __STDC__ */
+#endif /* NULL */
+
+
+/*
+ * Hack -- force definitions -- see fd_seek()
+ */
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+#ifndef SEEK_CUR
+# define SEEK_CUR 1
+#endif
+#ifndef SEEK_END
+# define SEEK_END 2
+#endif
+
+
+/*
+ * 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))
+
+
+/*
+ * Note that all "index" values must be "lowercase letters", while
+ * all "digits" must be "digits". Control characters can be made
+ * from any legal characters. XXX XXX XXX
+ */
+#define A2I(X) ((X) - 'a')
+#define I2A(X) ((X) + 'a')
+#define D2I(X) ((X) - '0')
+#define I2D(X) ((X) + '0')
+#define KTRL(X) ((X) & 0x1F)
+#define ESCAPE '\033'
+
+
+#endif
+
diff --git a/src/h-system.h b/src/h-system.h
new file mode 100644
index 00000000..92041742
--- /dev/null
+++ b/src/h-system.h
@@ -0,0 +1,90 @@
+/* File: h-system.h */
+
+#ifndef INCLUDED_H_SYSTEM_H
+#define INCLUDED_H_SYSTEM_H
+
+/*
+ * Include the basic "system" files.
+ *
+ * Make sure all "system" constants/macros are defined.
+ * Make sure all "system" functions have "extern" declarations.
+ *
+ * This file is a big hack to make other files less of a hack.
+ * This file has been rebuilt -- it may need a little more work.
+ */
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <stdlib.h>
+
+
+#ifdef SET_UID
+
+# include <sys/types.h>
+
+# if defined(linux)
+# include <sys/time.h>
+# endif
+
+# include <sys/timeb.h>
+
+#endif
+
+
+#include <time.h>
+
+
+
+#if defined(WINDOWS)
+# include <io.h>
+#endif
+
+#include <memory.h>
+
+
+# include <fcntl.h>
+
+
+#ifdef SET_UID
+
+# include <sys/param.h>
+# include <sys/file.h>
+
+# ifdef linux
+# include <sys/file.h>
+# endif
+
+# include <pwd.h>
+
+# include <unistd.h>
+
+# include <sys/stat.h>
+
+
+#endif
+
+#ifdef SET_UID
+
+# include <strings.h>
+
+#else
+
+# include <string.h>
+
+#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..bcf013bb
--- /dev/null
+++ b/src/h-type.h
@@ -0,0 +1,187 @@
+/* File: h-type.h */
+
+#ifndef INCLUDED_H_TYPE_H
+#define INCLUDED_H_TYPE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * 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 WINDOWS
+ */
+#undef huge
+#define huge huge_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;
+#define FMTs16b "%hd"
+#define FMTu16b "%hu"
+
+/* Signed/Unsigned 32 bit value */
+#ifdef L64 /* 64 bit longs */
+typedef signed int s32b;
+typedef unsigned int u32b;
+#define FMTs32b "%d"
+#define FMTu32b "%u"
+#else
+typedef signed long s32b;
+typedef unsigned long u32b;
+#define FMTs32b "%ld"
+#define FMTu32b "%lu"
+#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);
+
+
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
+
diff --git a/src/help.cc b/src/help.cc
new file mode 100644
index 00000000..a6d83079
--- /dev/null
+++ b/src/help.cc
@@ -0,0 +1,742 @@
+/*
+ * Copyright (c) 2001 DarkGod
+ * Copyright (c) 2012 Bardur Arantsson
+ *
+ * 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 "help.hpp"
+
+#include "cave_type.hpp"
+#include "files.hpp"
+#include "hook_get_in.hpp"
+#include "hook_identify_in.hpp"
+#include "hook_move_in.hpp"
+#include "hooks.hpp"
+#include "object1.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#define DESC_MAX 14
+#define TRIGGERED_HELP_MAX 18
+
+#define HELP_VOID_JUMPGATE 0
+#define HELP_FOUNTAIN 1
+#define HELP_FOUND_OBJECT 2
+#define HELP_FOUND_ALTAR 3
+#define HELP_FOUND_STAIR 4
+#define HELP_GET_RUNE 5
+#define HELP_GET_ROD 6
+#define HELP_GET_ROD_TIP 7
+#define HELP_GET_TRAP_KIT 8
+#define HELP_GET_DEVICE 9
+#define HELP_WILDERNESS 10
+#define HELP_GAME_TOME 11
+#define HELP_GAME_THEME 12
+#define HELP_1ST_LEVEL 13
+#define HELP_20TH_LEVEL 14
+#define HELP_ID_SPELL_ITM 15
+#define HELP_MELEE_SKILLS 16
+#define HELP_MON_ASK_HELP 17
+
+/**
+ * Game started?
+ */
+static bool_ game_started = FALSE;
+
+/**
+ * Struct for help triggered by a boolean condition
+ */
+typedef struct triggered_help_type triggered_help_type;
+struct triggered_help_type
+{
+ /* Help item index; see HELP_* constants above */
+ int help_index;
+ /* Hook type */
+ int hook_type;
+ /* Trigger function */
+ bool_ (*trigger_func)(void *in, void *out);
+ /* Description; NULL terminated */
+ cptr desc[DESC_MAX];
+};
+
+/**
+ * Struct for context-sensitive help
+ */
+typedef struct context_help_type context_help_type;
+struct context_help_type
+{
+ cptr key; /* Lookup key */
+ cptr file_name; /* Name of help file */
+ int anchor; /* Anchor in file */
+};
+
+/**
+ * Race help files.
+ */
+context_help_type race_table[] =
+{
+ /* ToME */
+ { "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 },
+ /* Theme */
+ { "Dragon", "r_dragon.txt", 0 },
+ { "Druadan", "r_druadan.txt", 0 },
+ { "Eagle", "r_eagle.txt", 0 },
+ { "Easterling", "r_easterl.txt", 0 },
+ { "Demon", "r_demon.txt", 0 },
+ /* End of list */
+ { NULL, NULL, 0 },
+};
+
+/**
+ * Subrace help files.
+ */
+context_help_type subrace_table[] =
+{
+ /* ToME */
+ { "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 },
+ /* Theme */
+ { "Red", "rm_red.txt", 0 },
+ { "Black", "rm_black.txt", 0 },
+ { "Green", "rm_green.txt", 0 },
+ { "Blue", "rm_blue.txt", 0 },
+ { "White", "rm_white.txt", 0 },
+ { "Ethereal", "rm_ether.txt", 0 },
+ { "(Narrog)", "rm_narrog.txt", 0 },
+ { "(Aewrog)", "rm_aewrog.txt", 0 },
+ { "(Hurog)", "rm_hurog.txt", 0 },
+ { "(Sarnrog)", "rm_sarnrog.txt", 0 },
+ { "(Caborrog)", "rm_cabrog.txt", 0 },
+ { "(Draugrog)", "rm_drarog.txt", 0 },
+ { "(Lygrog)", "rm_lygrog.txt", 0 },
+ { "(Limrog)", "rm_limrog.txt", 0 },
+ { "(Rawrog)", "rm_rawrog.txt", 0 },
+ { "(Adanrog)", "rm_adanrog.txt", 0 },
+ /* End of list */
+ { NULL, NULL, 0 },
+};
+
+/**
+ * Class help files
+ */
+context_help_type class_table[] =
+{
+ /* ToME */
+ { "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 },
+ /* Theme */
+ { "Ascetic", "c_ascet.txt", 0 },
+ { "Clairvoyant", "c_clairv.txt", 0 },
+ { "Mercenary", "c_mercen.txt", 0 },
+ { "Pacifist", "c_pacif.txt", 0 },
+ { "Peace-mage", "c_peacemag.txt", 0 },
+ { "Priest(Mandos)", "c_pr_mand.txt", 0 },
+ { "Priest(Ulmo)", "c_pr_ulmo.txt", 0 },
+ { "Priest(Varda)", "c_pr_varda.txt", 0 },
+ { "Sniper", "c_sniper.txt", 0 },
+ { "Stonewright", "c_stonewr.txt", 0 },
+ { "Trapper", "c_trapper.txt", 0 },
+ { "Wainrider", "c_wainrid.txt", 0 },
+ { "War-mage", "c_warmage.txt", 0 },
+ /* End of list */
+ { NULL, NULL, 0 },
+};
+
+/**
+ * God help files
+ */
+context_help_type god_table[] =
+{
+ /* ToME */
+ { "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 },
+ /* Theme */
+ { "Aule the Smith", "g_aule.txt", 0 },
+ { "Mandos", "g_mandos.txt", 0 },
+ { "Ulmo", "g_ulmo.txt", 0 },
+ { "Varda Elentari", "g_varda.txt", 0 },
+ /* End of list */
+ { NULL, NULL, 0 },
+};
+
+/**
+ * Skill help files
+ */
+context_help_type skill_table[] =
+{
+ { "Air", "skills.txt", 27 },
+ { "Antimagic", "skills.txt", 50 },
+ { "Archery", "skills.txt", 8 },
+ { "Axe-mastery", "skills.txt", 5 },
+ { "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", 1 },
+ { "Conveyance", "skills.txt", 30 },
+ { "Corpse-preservation", "skills.txt", 44 },
+ { "Critical-hits", "skills.txt", 4 },
+ { "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", 6 },
+ { "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", 7 },
+ { "Possession", "skills.txt", 45 },
+ { "Prayer", "skills.txt", 39 },
+ { "Runecraft", "skills.txt", 36 },
+ { "Sling-mastery", "skills.txt", 9 },
+ { "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", 3 },
+ { "Symbiosis", "skills.txt", 46 },
+ { "Temporal", "skills.txt", 32 },
+ { "Thaumaturgy", "skills.txt", 37 },
+ { "Udun", "skills.txt", 48 },
+ { "Weaponmastery", "skills.txt", 2 },
+ { "Water", "skills.txt", 26 },
+ { NULL, NULL, 0 },
+};
+
+/**
+ * Ability help files
+ */
+context_help_type ability_table[] =
+{
+ { "Spread blows", "ability.txt", 2 },
+ { "Tree walking", "ability.txt", 3 },
+ { "Perfect casting", "ability.txt", 4 },
+ { "Extra Max Blow(1)", "ability.txt", 5 },
+ { "Extra Max Blow(2)", "ability.txt", 6 },
+ { "Ammo creation", "ability.txt", 7 },
+ { "Touch of death", "ability.txt", 8 },
+ { "Far reaching attack", "ability.txt", 10 },
+ { "Trapping", "ability.txt", 11 },
+ { "Undead Form", "ability.txt", 12 },
+ { NULL, NULL, 0 },
+};
+
+/**
+ * Trigger functions
+ */
+static bool_ trigger_void_jumpgate(void *in, void *out) {
+ hook_move_in *p = (hook_move_in *) in;
+ return cave[p->y][p->x].feat == FEAT_BETWEEN;
+}
+
+static bool_ trigger_fountain(void *in, void *out) {
+ hook_move_in *p = (hook_move_in *) in;
+ return cave[p->y][p->x].feat == FEAT_FOUNTAIN;
+}
+
+static bool_ trigger_found_object(void *in, void *out) {
+ hook_move_in *p = (hook_move_in *) in;
+ return !cave[p->y][p->x].o_idxs.empty();
+}
+
+static bool_ trigger_found_altar(void *in, void *out) {
+ hook_move_in *p = (hook_move_in *) in;
+ return ((cave[p->y][p->x].feat >= FEAT_ALTAR_HEAD) &&
+ (cave[p->y][p->x].feat <= FEAT_ALTAR_TAIL));
+}
+
+static bool_ trigger_found_stairs(void *in, void *out) {
+ hook_move_in *p = (hook_move_in *) in;
+ return (cave[p->y][p->x].feat == FEAT_MORE);
+}
+
+static bool_ trigger_get_rune(void *in, void *out) {
+ hook_get_in *g = (hook_get_in *) in;
+ return ((g->o_ptr->tval == TV_RUNE1) ||
+ (g->o_ptr->tval == TV_RUNE2));
+}
+
+static bool_ trigger_get_rod(void *in, void *out) {
+ hook_get_in *g = (hook_get_in *) in;
+ return (g->o_ptr->tval == TV_ROD_MAIN);
+}
+
+static bool_ trigger_get_rod_tip(void *in, void *out) {
+ hook_get_in *g = (hook_get_in *) in;
+ return (g->o_ptr->tval == TV_ROD);
+}
+
+static bool_ trigger_get_trap_kit(void *in, void *out) {
+ hook_get_in *g = (hook_get_in *) in;
+ return (g->o_ptr->tval == TV_TRAPKIT);
+}
+
+static bool_ trigger_get_magic_device(void *in, void *out) {
+ hook_get_in *g = (hook_get_in *) in;
+ return ((g->o_ptr->tval == TV_WAND) ||
+ (g->o_ptr->tval == TV_STAFF));
+}
+
+static bool_ trigger_end_turn_wilderness(void *in, void *out) {
+ return (((p_ptr->wilderness_x != 34) ||
+ (p_ptr->wilderness_y != 21)) &&
+ (!p_ptr->astral));
+}
+
+static bool_ trigger_game_theme(void *in, void *out) {
+ return (game_module_idx == MODULE_THEME);
+}
+
+static bool_ trigger_game_tome(void *in, void *out) {
+ return (game_module_idx == MODULE_TOME);
+}
+
+static bool_ trigger_1st_level(void *in, void *out) {
+ return (p_ptr->lev > 1);
+}
+
+static bool_ trigger_20th_level(void *in, void *out) {
+ return (p_ptr->lev >= 20);
+}
+
+static bool_ trigger_identify_spell_item(void *in_, void *out) {
+ hook_identify_in *in = (hook_identify_in *) in_;
+
+ if (in->mode == IDENT_FULL)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(in->o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f5 & TR5_SPELL_CONTAIN)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static bool_ trigger_melee_skills(void *in, void *out) {
+ return (game_started && (get_melee_skills() > 1));
+}
+
+static bool_ trigger_always(void *in, void *out) {
+ return TRUE;
+}
+
+/**
+ * Trigger-based help items
+ */
+static triggered_help_type triggered_help[TRIGGERED_HELP_MAX] =
+{
+ { HELP_VOID_JUMPGATE,
+ HOOK_MOVE,
+ trigger_void_jumpgate,
+ { "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.",
+ NULL }
+ },
+ { HELP_FOUNTAIN,
+ HOOK_MOVE,
+ trigger_fountain,
+ { "Fountains are always magical. You can quaff from them by pressing H.",
+ "Beware that unlike potions they cannot be identified.",
+ NULL }
+ },
+ { HELP_FOUND_OBJECT,
+ HOOK_MOVE,
+ trigger_found_object,
+ { "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.",
+ NULL }
+ },
+ { HELP_FOUND_ALTAR,
+ HOOK_MOVE,
+ trigger_found_altar,
+ { "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.",
+ NULL }
+ },
+ { HELP_FOUND_STAIR,
+ HOOK_MOVE,
+ trigger_found_stairs,
+ { "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.",
+ NULL }
+ },
+ { HELP_GET_RUNE,
+ HOOK_GET,
+ trigger_get_rune,
+ { "Ah, a rune! Runes are used with the Runecraft skill to allow you to",
+ "create spells on your own.",
+ NULL
+ }
+ },
+ { HELP_GET_ROD,
+ HOOK_GET,
+ trigger_get_rod,
+ { "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.",
+ NULL
+ }
+ },
+ { HELP_GET_ROD_TIP,
+ HOOK_GET,
+ trigger_get_rod_tip,
+ { "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.",
+ NULL
+ }
+ },
+ { HELP_GET_TRAP_KIT,
+ HOOK_GET,
+ trigger_get_trap_kit,
+ { "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.",
+ NULL
+ }
+ },
+ { HELP_GET_DEVICE,
+ HOOK_GET,
+ trigger_get_magic_device,
+ { "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.",
+ NULL
+ }
+ },
+ { HELP_WILDERNESS,
+ HOOK_END_TURN,
+ trigger_end_turn_wilderness,
+ { "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.",
+ NULL
+ }
+ },
+ { HELP_GAME_TOME,
+ HOOK_END_TURN,
+ trigger_game_tome,
+ { "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!",
+ NULL
+ }
+ },
+ { HELP_GAME_THEME,
+ HOOK_END_TURN,
+ trigger_game_theme,
+ { "Welcome to Theme! 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!",
+ NULL
+ }
+ },
+ { HELP_1ST_LEVEL,
+ HOOK_PLAYER_LEVEL,
+ trigger_1st_level,
+ { "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.",
+ NULL
+ }
+ },
+ { HELP_20TH_LEVEL,
+ HOOK_PLAYER_LEVEL,
+ trigger_20th_level,
+ { "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.",
+ NULL
+ }
+ },
+ { HELP_ID_SPELL_ITM,
+ HOOK_IDENTIFY,
+ trigger_identify_spell_item,
+ { "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.",
+ NULL
+ }
+ },
+ { HELP_MELEE_SKILLS,
+ HOOK_RECALC_SKILLS,
+ trigger_melee_skills,
+ { "Ah, you now possess more than one melee type. To switch between them press m",
+ "and select the switch melee type option.",
+ NULL
+ }
+ },
+ { HELP_MON_ASK_HELP,
+ HOOK_MON_ASK_HELP,
+ trigger_always,
+ { "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.",
+ NULL
+ }
+ }
+};
+
+static bool_ triggered_help_hook(void *data, void *in, void *out)
+{
+ triggered_help_type *triggered_help = (triggered_help_type *) data;
+ /* Not triggered before and trigger now? */
+ if ((option_ingame_help) &&
+ (!p_ptr->help.activated[triggered_help->help_index]) &&
+ triggered_help->trigger_func(in,out))
+ {
+ int i;
+
+ /* Triggered */
+ p_ptr->help.activated[triggered_help->help_index] = TRUE;
+
+ /* Show the description */
+ for (i = 0; (i < DESC_MAX) && (triggered_help->desc[i] != NULL); i++)
+ {
+ cmsg_print(TERM_YELLOW, triggered_help->desc[i]);
+ }
+ }
+ /* Don't stop processing */
+ return FALSE;
+}
+
+static bool_ hook_game_start(void *data, void *in, void *out)
+{
+ game_started = TRUE;
+ return FALSE;
+}
+
+static void setup_triggered_help_hook(int i)
+{
+ static int counter = 0;
+ char name[40];
+ triggered_help_type *h = &triggered_help[i];
+
+ /* Build name */
+ sprintf(name, "help_trigger_%d", counter);
+ counter++;
+
+ /* Add the hook */
+ add_hook_new(h->hook_type,
+ triggered_help_hook,
+ name,
+ h);
+}
+
+static void setup_triggered_help_hooks()
+{
+ int i;
+
+ for (i = 0; i < TRIGGERED_HELP_MAX; i++)
+ {
+ setup_triggered_help_hook(i);
+ }
+
+ add_hook_new(HOOK_GAME_START,
+ hook_game_start,
+ "help_game_start",
+ NULL);
+}
+
+/*
+ * Driver for the context-sensitive help system
+ */
+void init_hooks_help()
+{
+ setup_triggered_help_hooks();
+}
+
+/*
+ * Show help file
+ */
+static void show_context_help(context_help_type *context_help)
+{
+ if (context_help == NULL)
+ {
+ return;
+ }
+
+ screen_save();
+
+ show_file(context_help->file_name, 0, -context_help->anchor, 0);
+
+ screen_load();
+}
+
+/*
+ * Find context help
+ */
+static context_help_type *find_context_help(context_help_type table[], cptr key)
+{
+ int i;
+
+ for (i = 0; ; i++)
+ {
+ context_help_type *context_help = &table[i];
+
+ if (context_help->key == NULL)
+ {
+ return NULL; /* End of list */
+ }
+
+ if (streq(key, context_help->key))
+ {
+ return context_help;
+ }
+ }
+}
+
+/*
+ * Racial help
+ */
+void help_race(cptr race)
+{
+ show_context_help(find_context_help(race_table, race));
+}
+
+void help_subrace(cptr subrace)
+{
+ show_context_help(find_context_help(subrace_table, subrace));
+}
+
+void help_class(cptr klass)
+{
+ show_context_help(find_context_help(class_table, klass));
+}
+
+void help_god(cptr god)
+{
+ context_help_type *context_help =
+ find_context_help(god_table, god);
+
+ if (context_help != NULL)
+ {
+ show_context_help(context_help);
+ }
+}
+
+void help_skill(cptr skill)
+{
+ show_context_help(find_context_help(skill_table, skill));
+}
+
+void help_ability(cptr ability)
+{
+ show_context_help(find_context_help(ability_table, ability));
+}
diff --git a/src/help.hpp b/src/help.hpp
new file mode 100644
index 00000000..7c057a01
--- /dev/null
+++ b/src/help.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern void init_hooks_help();
+extern void help_race(cptr race);
+extern void help_subrace(cptr subrace);
+extern void help_class(cptr klass);
+extern void help_god(cptr god);
+extern void help_skill(cptr skill);
+extern void help_ability(cptr ability);
diff --git a/src/help_info.hpp b/src/help_info.hpp
new file mode 100644
index 00000000..ea440bb9
--- /dev/null
+++ b/src/help_info.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Maximum number of help items.
+ */
+constexpr int HELP_MAX = 64;
+
+/**
+ * Context help runtime data.
+ */
+struct help_info
+{
+ bool_ enabled; /* ingame help enabled */
+ bool_ activated[HELP_MAX]; /* help item #i activated? */
+};
diff --git a/src/hiscore.cc b/src/hiscore.cc
new file mode 100644
index 00000000..971b84cd
--- /dev/null
+++ b/src/hiscore.cc
@@ -0,0 +1,85 @@
+#include "hiscore.hpp"
+
+#include "util.hpp"
+
+#include <cassert>
+
+int highscore_seek(int highscore_fd, int i)
+{
+ /* Seek for the requested record */
+ return (fd_seek(highscore_fd, (huge)(i) * sizeof(high_score)));
+}
+
+errr highscore_read(int highscore_fd, high_score *score)
+{
+ /* Read the record, note failure */
+ return (fd_read(highscore_fd, (char*)(score), sizeof(high_score)));
+}
+
+int highscore_write(int highscore_fd, high_score *score)
+{
+ /* Write the record, note failure */
+ return (fd_write(highscore_fd, (char*)(score), sizeof(high_score)));
+}
+
+int highscore_where(int highscore_fd, 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(highscore_fd, 0)) return ( -1);
+
+ /* Read until we get to a higher score */
+ for (i = 0; i < MAX_HISCORES; i++)
+ {
+ if (highscore_read(highscore_fd, &the_score)) return (i);
+ if (strcmp(the_score.pts, score->pts) < 0) return (i);
+ }
+
+ /* The "last" entry is always usable */
+ return (MAX_HISCORES - 1);
+}
+
+int highscore_add(int highscore_fd, 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(highscore_fd, 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(highscore_fd, i)) return ( -1);
+ if (highscore_read(highscore_fd, &tmpscore)) done = TRUE;
+
+ /* Back up and dump the score we were holding */
+ if (highscore_seek(highscore_fd, i)) return ( -1);
+ if (highscore_write(highscore_fd, &the_score)) return ( -1);
+
+ /* Hack -- Save the old score, for the next pass */
+ the_score = tmpscore;
+ }
+
+ /* Return location used */
+ return (slot);
+}
diff --git a/src/hiscore.hpp b/src/hiscore.hpp
new file mode 100644
index 00000000..0b1b713d
--- /dev/null
+++ b/src/hiscore.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Maximum number of high scores in the high score file
+ */
+constexpr int MAX_HISCORES = 100;
+
+/*
+ * 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 unused_1[8]; /* Kept for compatibility only */
+
+ 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 unused_2[4]; /* Kept for compatibility only */
+ char unused_3[4]; /* Kept for compatibility only */
+ char inside_quest[4]; /* Did the player die in a quest? */
+ char unused_4[4]; /* Kept for compatibility only */
+
+ char how[32]; /* Method of death (string) */
+};
+
+/*
+ * Seek score 'i' in the highscore file
+ */
+int highscore_seek(int highscore_fd, int i);
+
+/*
+ * Read one score from the highscore file
+ */
+errr highscore_read(int highscore_fd, high_score *score);
+
+/*
+ * Write one score to the highscore file
+ */
+int highscore_write(int highscore_fd, high_score *score);
+
+/*
+ * Determine where a new score *would* be placed
+ * Return the location (0 is best) or -1 on failure
+ */
+int highscore_where(int highscore_fd, high_score *score);
+
+/*
+ * Place an entry into the high score file. Return the location (0 is
+ * best) or -1 on "failure"
+ */
+int highscore_add(int highscore_fd, high_score *score);
diff --git a/src/hist_type.hpp b/src/hist_type.hpp
new file mode 100644
index 00000000..2da47b7c
--- /dev/null
+++ b/src/hist_type.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Player background descriptor.
+ */
+struct hist_type
+{
+ char *info; /* Textual History */
+
+ byte roll; /* Frequency of this entry */
+ s16b chart; /* Chart index */
+ s16b next; /* Next chart index */
+ byte bonus; /* Social Class Bonus + 50 */
+};
diff --git a/src/hist_type_fwd.hpp b/src/hist_type_fwd.hpp
new file mode 100644
index 00000000..d1cbce91
--- /dev/null
+++ b/src/hist_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct hist_type;
diff --git a/src/hook_build_room1_in.hpp b/src/hook_build_room1_in.hpp
new file mode 100644
index 00000000..e9a5d367
--- /dev/null
+++ b/src/hook_build_room1_in.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_build_room1_in {
+ s32b y;
+ s32b x;
+};
diff --git a/src/hook_calculate_hp_in.hpp b/src/hook_calculate_hp_in.hpp
new file mode 100644
index 00000000..924add45
--- /dev/null
+++ b/src/hook_calculate_hp_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_calculate_hp_in {
+ s32b mhp;
+};
diff --git a/src/hook_calculate_hp_out.hpp b/src/hook_calculate_hp_out.hpp
new file mode 100644
index 00000000..7923997f
--- /dev/null
+++ b/src/hook_calculate_hp_out.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_calculate_hp_out {
+ s32b mhp;
+};
diff --git a/src/hook_chardump_in.hpp b/src/hook_chardump_in.hpp
new file mode 100644
index 00000000..c8edea62
--- /dev/null
+++ b/src/hook_chardump_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_chardump_in {
+ FILE *file;
+};
diff --git a/src/hook_chat_in.hpp b/src/hook_chat_in.hpp
new file mode 100644
index 00000000..5062b0e6
--- /dev/null
+++ b/src/hook_chat_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_chat_in {
+ s32b m_idx;
+};
diff --git a/src/hook_drop_in.hpp b/src/hook_drop_in.hpp
new file mode 100644
index 00000000..acaa10ff
--- /dev/null
+++ b/src/hook_drop_in.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+struct hook_drop_in {
+ int o_idx;
+};
diff --git a/src/hook_eat_in.hpp b/src/hook_eat_in.hpp
new file mode 100644
index 00000000..075bb2bf
--- /dev/null
+++ b/src/hook_eat_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "object_type_fwd.hpp"
+
+struct hook_eat_in {
+ object_type *o_ptr;
+};
diff --git a/src/hook_eat_out.hpp b/src/hook_eat_out.hpp
new file mode 100644
index 00000000..70bb92a4
--- /dev/null
+++ b/src/hook_eat_out.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_eat_out {
+ bool_ ident;
+};
diff --git a/src/hook_enter_dungeon_in.hpp b/src/hook_enter_dungeon_in.hpp
new file mode 100644
index 00000000..2f667c78
--- /dev/null
+++ b/src/hook_enter_dungeon_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_enter_dungeon_in {
+ s32b d_idx;
+};
diff --git a/src/hook_get_in.hpp b/src/hook_get_in.hpp
new file mode 100644
index 00000000..9bc4b795
--- /dev/null
+++ b/src/hook_get_in.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "object_type_fwd.hpp"
+
+struct hook_get_in {
+ object_type *o_ptr;
+ int o_idx;
+};
diff --git a/src/hook_give_in.hpp b/src/hook_give_in.hpp
new file mode 100644
index 00000000..0ef5a11d
--- /dev/null
+++ b/src/hook_give_in.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+struct hook_give_in {
+ int m_idx;
+ int item;
+};
diff --git a/src/hook_identify_in.hpp b/src/hook_identify_in.hpp
new file mode 100644
index 00000000..40a8fc79
--- /dev/null
+++ b/src/hook_identify_in.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "object_type_fwd.hpp"
+#include "identify_mode.hpp"
+
+struct hook_identify_in {
+ object_type *o_ptr;
+ identify_mode mode;
+};
diff --git a/src/hook_init_quest_in.hpp b/src/hook_init_quest_in.hpp
new file mode 100644
index 00000000..5d4b274a
--- /dev/null
+++ b/src/hook_init_quest_in.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+struct hook_init_quest_in {
+ int q_idx;
+};
diff --git a/src/hook_mon_speak_in.hpp b/src/hook_mon_speak_in.hpp
new file mode 100644
index 00000000..f3a14338
--- /dev/null
+++ b/src/hook_mon_speak_in.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_mon_speak_in {
+ s32b m_idx;
+ cptr m_name;
+};
diff --git a/src/hook_monster_ai_in.hpp b/src/hook_monster_ai_in.hpp
new file mode 100644
index 00000000..492006aa
--- /dev/null
+++ b/src/hook_monster_ai_in.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "h-basic.h"
+#include "monster_type_fwd.hpp"
+
+struct hook_monster_ai_in {
+ s32b m_idx;
+ monster_type *m_ptr;
+};
diff --git a/src/hook_monster_ai_out.hpp b/src/hook_monster_ai_out.hpp
new file mode 100644
index 00000000..4c5ef28f
--- /dev/null
+++ b/src/hook_monster_ai_out.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_monster_ai_out {
+ s32b y;
+ s32b x;
+};
diff --git a/src/hook_monster_death_in.hpp b/src/hook_monster_death_in.hpp
new file mode 100644
index 00000000..354c37d2
--- /dev/null
+++ b/src/hook_monster_death_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_monster_death_in {
+ s32b m_idx;
+};
diff --git a/src/hook_move_in.hpp b/src/hook_move_in.hpp
new file mode 100644
index 00000000..6e299e5b
--- /dev/null
+++ b/src/hook_move_in.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+struct hook_move_in {
+ int y;
+ int x;
+};
diff --git a/src/hook_new_monster_end_in.hpp b/src/hook_new_monster_end_in.hpp
new file mode 100644
index 00000000..3c5f835b
--- /dev/null
+++ b/src/hook_new_monster_end_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "monster_type_fwd.hpp"
+
+struct hook_new_monster_end_in {
+ monster_type *m_ptr;
+};
diff --git a/src/hook_new_monster_in.hpp b/src/hook_new_monster_in.hpp
new file mode 100644
index 00000000..10b0420c
--- /dev/null
+++ b/src/hook_new_monster_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_new_monster_in {
+ s32b r_idx;
+};
diff --git a/src/hook_player_level_in.hpp b/src/hook_player_level_in.hpp
new file mode 100644
index 00000000..90804e27
--- /dev/null
+++ b/src/hook_player_level_in.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+struct hook_player_level_in {
+ int gained_levels;
+};
diff --git a/src/hook_quest_fail_in.hpp b/src/hook_quest_fail_in.hpp
new file mode 100644
index 00000000..2edf86a3
--- /dev/null
+++ b/src/hook_quest_fail_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_quest_fail_in {
+ s16b q_idx;
+};
diff --git a/src/hook_quest_finish_in.hpp b/src/hook_quest_finish_in.hpp
new file mode 100644
index 00000000..55490be4
--- /dev/null
+++ b/src/hook_quest_finish_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_quest_finish_in {
+ s32b q_idx;
+};
diff --git a/src/hook_stair_in.hpp b/src/hook_stair_in.hpp
new file mode 100644
index 00000000..884a187c
--- /dev/null
+++ b/src/hook_stair_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "stairs_direction.hpp"
+
+struct hook_stair_in {
+ stairs_direction direction;
+};
diff --git a/src/hook_stair_out.hpp b/src/hook_stair_out.hpp
new file mode 100644
index 00000000..5e3d515a
--- /dev/null
+++ b/src/hook_stair_out.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_stair_out {
+ bool_ allow;
+};
diff --git a/src/hook_wield_in.hpp b/src/hook_wield_in.hpp
new file mode 100644
index 00000000..036e62da
--- /dev/null
+++ b/src/hook_wield_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "object_type_fwd.hpp"
+
+struct hook_wield_in {
+ object_type *o_ptr;
+};
diff --git a/src/hook_wild_gen_in.hpp b/src/hook_wild_gen_in.hpp
new file mode 100644
index 00000000..147a74db
--- /dev/null
+++ b/src/hook_wild_gen_in.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct hook_wild_gen_in {
+ bool_ small;
+};
diff --git a/src/hooks.cc b/src/hooks.cc
new file mode 100644
index 00000000..4fcc39d3
--- /dev/null
+++ b/src/hooks.cc
@@ -0,0 +1,113 @@
+/*
+ * 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 "hooks.hpp"
+
+#include <algorithm>
+#include <assert.h>
+#include <unordered_map>
+#include <vector>
+
+/******** Hooks stuff *********/
+
+struct hook_data
+{
+private:
+ hook_func_t m_hook_func;
+ void *m_hook_data;
+public:
+ hook_data(hook_func_t hook_func, void *hook_data)
+ : m_hook_func(hook_func)
+ , m_hook_data(hook_data) {
+ }
+
+ hook_data() = delete;
+
+ /**
+ * Check if the given hook points to the given function.
+ */
+ bool is(hook_func_t hook_func) const {
+ return m_hook_func == hook_func;
+ }
+
+ /**
+ * Invoke the hook with the given input and output pointers.
+ */
+ bool_ invoke(void *in, void *out) const {
+ return m_hook_func(m_hook_data, in, out);
+ }
+};
+
+std::unordered_map<size_t, std::vector<hook_data>> &hooks_instance()
+{
+ static auto instance = new std::unordered_map<size_t, std::vector<hook_data>>();
+ return *instance;
+}
+
+
+int process_hooks_restart = FALSE;
+
+static std::vector<hook_data>::iterator find_hook(std::vector<hook_data> &hooks, hook_func_t hook_func)
+{
+ return std::find_if(hooks.begin(),
+ hooks.end(),
+ [&](const hook_data &hook_data) {
+ return hook_data.is(hook_func);
+ });
+}
+
+void add_hook_new(int h_idx, hook_func_t hook_func, cptr name, void *data)
+{
+ auto &hooks = hooks_instance()[h_idx];
+ // Only insert if not already present.
+ if (find_hook(hooks, hook_func) == hooks.end()) {
+ hooks.emplace_back(hook_func, data);
+ }
+}
+
+void del_hook_new(int h_idx, hook_func_t hook_func)
+{
+ auto &hooks = hooks_instance()[h_idx];
+
+ /* Find it */
+ auto found_it = find_hook(hooks, hook_func);
+ if (found_it != hooks.end())
+ {
+ hooks.erase(found_it);
+ }
+}
+
+bool_ process_hooks_new(int h_idx, void *in, void *out)
+{
+ auto const &hooks = hooks_instance()[h_idx];
+
+ auto hooks_it = hooks.begin();
+ while (hooks_it != hooks.end())
+ {
+ auto &hook_data = *hooks_it;
+
+ /* Invoke hook function; stop processing if the hook
+ returns TRUE */
+ if (hook_data.invoke(in, out))
+ {
+ return TRUE;
+ }
+
+ /* Should we restart processing at the beginning? */
+ if (process_hooks_restart)
+ {
+ hooks_it = hooks.begin();
+ process_hooks_restart = FALSE;
+ }
+ else
+ {
+ hooks_it++;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/src/hooks.hpp b/src/hooks.hpp
new file mode 100644
index 00000000..b6124e6a
--- /dev/null
+++ b/src/hooks.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "h-basic.h"
+
+typedef bool_ (*hook_func_t)(void *, void *, void *);
+
+extern void add_hook_new(int h_idx, hook_func_t hook_func, cptr name, void *data);
+extern void del_hook_new(int h_idx, hook_func_t hook_func);
+extern int process_hooks_restart;
+extern bool_ process_hooks_new(int h_idx, void *in, void *out);
diff --git a/src/identify_mode.hpp b/src/identify_mode.hpp
new file mode 100644
index 00000000..687a1fae
--- /dev/null
+++ b/src/identify_mode.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+typedef enum { IDENT_NORMAL, IDENT_FULL } identify_mode;
diff --git a/src/include/tome/enum_string_map.hpp b/src/include/tome/enum_string_map.hpp
new file mode 100644
index 00000000..8ae1e115
--- /dev/null
+++ b/src/include/tome/enum_string_map.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <boost/bimap.hpp>
+#include <boost/noncopyable.hpp>
+#include <string>
+#include <cassert>
+
+/**
+ * Bidirectional mapping between enumerated values
+ * and strings.
+ */
+template <class E>
+class EnumStringMap : boost::noncopyable {
+
+private:
+ typedef boost::bimap< E, std::string > bimap_type;
+ typedef typename bimap_type::value_type value_type;
+
+ bimap_type bimap;
+
+public:
+ explicit EnumStringMap(std::initializer_list< std::pair<E, const char *> > in) {
+ for (auto es : in)
+ {
+ bimap.insert(value_type(es.first, es.second));
+ }
+ // Sanity check that there were no
+ // duplicate mappings.
+ assert(bimap.size() == in.size());
+ }
+
+ const char *stringify(E e) {
+ auto i = bimap.left.find(e);
+ assert(i != bimap.left.end() && "Missing mapping for enumerated value");
+ return i->second.c_str();
+ }
+
+ E parse(const char *s) {
+ E e;
+ bool result = parse(s, &e);
+ assert(result && "Missing string->enum mapping");
+ return e;
+ }
+
+ bool parse(const char *s, E *e) {
+ auto i = bimap.right.find(s);
+ if (i == bimap.right.end())
+ {
+ return false;
+ }
+
+ *e = i->second;
+ return true;
+ }
+};
diff --git a/src/include/tome/make_array.hpp b/src/include/tome/make_array.hpp
new file mode 100644
index 00000000..23cb8ac0
--- /dev/null
+++ b/src/include/tome/make_array.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <type_traits>
+
+/*
+ * Make an array of a POD type.
+ */
+template <typename T> T *make_array(std::size_t n) {
+ static_assert(std::is_pod<T>::value, "Type parameter must be POD type");
+ T *array = new T[n];
+ memset(array, 0, n*sizeof(T));
+ return array;
+}
diff --git a/src/include/tome/squelch/automatizer.hpp b/src/include/tome/squelch/automatizer.hpp
new file mode 100644
index 00000000..786ca1ae
--- /dev/null
+++ b/src/include/tome/squelch/automatizer.hpp
@@ -0,0 +1,156 @@
+#pragma once
+
+#include <boost/noncopyable.hpp>
+#include <memory>
+#include <vector>
+#include <jansson.h>
+
+#include "tome/squelch/rule_fwd.hpp"
+#include "tome/squelch/cursor_fwd.hpp"
+#include "tome/squelch/tree_printer_fwd.hpp"
+#include "tome/squelch/condition_fwd.hpp"
+#include "../object_type_fwd.hpp"
+
+namespace squelch {
+
+/**
+ * Automatizer
+ */
+class Automatizer : public boost::noncopyable
+{
+public:
+ Automatizer(std::shared_ptr<TreePrinter> tree_printer,
+ std::shared_ptr<Cursor> cursor)
+ : m_selected_rule(0)
+ , m_begin(0)
+ , m_tree_printer(tree_printer)
+ , m_cursor(cursor)
+ , m_rules() {
+ }
+
+ /**
+ * Append a rule
+ */
+ int append_rule(std::shared_ptr<Rule> rule);
+
+ /**
+ * Swap two rules
+ */
+ void swap_rules(int i, int j);
+
+ /**
+ * Apply all rules to the given object
+ */
+ bool apply_rules(object_type *o_ptr, int item_idx) const;
+
+ /**
+ * Build a JSON data structure to represent
+ * all the rules.
+ */
+ std::shared_ptr<json_t> to_json() const;
+
+ /**
+ * Load rules from a JSON data structure.
+ */
+ void load_json(json_t *json);
+
+ /**
+ * Remove currently selected condition or rule.
+ */
+ int remove_current_selection();
+
+ /**
+ * Reset view.
+ */
+ void reset_view();
+
+ /**
+ * Show current rule
+ */
+ void show_current() const;
+
+ /**
+ * Scroll view up
+ */
+ void scroll_up();
+
+ /**
+ * Scroll view down
+ */
+ void scroll_down();
+
+ /**
+ * Scroll view left
+ */
+ void scroll_left();
+
+ /**
+ * Scroll view right
+ */
+ void scroll_right();
+
+ /**
+ * Move selection up
+ */
+ void move_up();
+
+ /**
+ * Move selection down
+ */
+ void move_down();
+
+ /**
+ * Move selection left
+ */
+ void move_left();
+
+ /**
+ * Move selection right
+ */
+ void move_right();
+
+ /**
+ * Add new condition to selected rule
+ */
+ void add_new_condition(std::function<std::shared_ptr<Condition> ()> factory);
+
+ /**
+ * Get rule names. The names are not stable across multiple
+ * calls to methods on this class.
+ */
+ void get_rule_names(std::vector<const char *> *names) const;
+
+ /**
+ * Get current number of rules.
+ */
+ int rules_count() const;
+
+ /**
+ * Get the "beginning" rule.
+ */
+ int rules_begin() const;
+
+ /**
+ * Select a new rule.
+ */
+ void select_rule(int selected_rule);
+
+ /**
+ * Return selected rule index
+ */
+ int selected_rule() const;
+
+ /**
+ * Return selected rule
+ */
+ std::shared_ptr<Rule> current_rule();
+
+private:
+ int m_selected_rule;
+ int m_begin;
+ std::shared_ptr<TreePrinter> m_tree_printer;
+ std::shared_ptr<Cursor> m_cursor;
+ std::vector < std::shared_ptr < Rule > > m_rules;
+};
+
+} // namespace
diff --git a/src/include/tome/squelch/automatizer_fwd.hpp b/src/include/tome/squelch/automatizer_fwd.hpp
new file mode 100644
index 00000000..068f297a
--- /dev/null
+++ b/src/include/tome/squelch/automatizer_fwd.hpp
@@ -0,0 +1,10 @@
+#ifndef H_cbf79126_fd24_4f80_8f2d_9dd69cb7010f
+#define H_cbf79126_fd24_4f80_8f2d_9dd69cb7010f
+
+namespace squelch {
+
+class Automatizer;
+
+} // namespace
+
+#endif
diff --git a/src/include/tome/squelch/condition.hpp b/src/include/tome/squelch/condition.hpp
new file mode 100644
index 00000000..5d1240f5
--- /dev/null
+++ b/src/include/tome/squelch/condition.hpp
@@ -0,0 +1,632 @@
+#pragma once
+
+#include "tome/squelch/condition_fwd.hpp"
+
+#include <boost/noncopyable.hpp>
+#include <functional>
+#include <memory>
+#include <cstdint>
+#include <jansson.h>
+
+#include "tome/squelch/cursor_fwd.hpp"
+#include "tome/squelch/tree_printer_fwd.hpp"
+#include "tome/squelch/object_status_fwd.hpp"
+#include "tome/enum_string_map.hpp"
+#include "../object_type_fwd.hpp"
+
+namespace squelch {
+
+/**
+ * Types of matches used for conditions.
+ */
+enum class match_type {
+ AND , OR , NOT , NAME , CONTAIN ,
+ INSCRIBED, DISCOUNT, SYMBOL , STATE , STATUS ,
+ TVAL , SVAL , RACE , SUBRACE , CLASS ,
+ LEVEL , SKILL , ABILITY, INVENTORY, EQUIPMENT };
+
+/**
+ * Bidirectional map between enumeration values and strings.
+ */
+EnumStringMap<match_type> &match_mapping();
+
+/**
+ * Identification states an object can have: identified or not
+ * identified.
+ */
+enum class identification_state {
+ IDENTIFIED, NOT_IDENTIFIED };
+
+/**
+ * Biredectional map between identification_state values and strings.
+ */
+EnumStringMap<identification_state> &identification_state_mapping();
+
+/**
+ * Condition represents a tree of checks which
+ * can be applied to objects, the player, etc.
+ */
+class Condition : public boost::noncopyable
+{
+public:
+ Condition(match_type match_) : match(match_) {
+ }
+
+ void display(TreePrinter *, Cursor *) const;
+
+ virtual bool is_match(object_type *) const = 0;
+
+ virtual ~Condition() {
+ }
+
+public:
+ json_t *to_json() const;
+
+ virtual void add_child(ConditionFactory const &factory) {
+ // Default is to not support children.
+ };
+
+ virtual void remove_child(Condition *c) {
+ // Nothing to do by default.
+ }
+
+ virtual std::shared_ptr<Condition> first_child() {
+ // No children.
+ return nullptr;
+ }
+
+ virtual std::shared_ptr<Condition> previous_child(Condition *) {
+ // Default no children, so no predecessor.
+ return nullptr;
+ }
+
+ virtual std::shared_ptr<Condition> next_child(Condition *) {
+ // Default no children, so no successor.
+ return nullptr;
+ }
+
+ /**
+ * Parse condition from JSON
+ */
+ static std::shared_ptr<Condition> parse_condition(json_t *);
+
+ /**
+ * Convert an (optional) condition to JSON.
+ */
+ static json_t *optional_to_json(std::shared_ptr<Condition> condition);
+
+protected:
+ virtual void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const = 0;
+ virtual void to_json(json_t *) const = 0;
+
+ // What do we want to match?
+ match_type match;
+};
+
+/**
+ * Check for a specific TVAL
+ */
+class TvalCondition : public Condition
+{
+public:
+ TvalCondition(uint8_t tval)
+ : Condition(match_type::TVAL)
+ , m_tval(tval) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ uint8_t m_tval;
+};
+
+/**
+ * Check for object name
+ */
+class NameCondition : public Condition
+{
+public:
+ NameCondition(std::string name) :
+ Condition(match_type::NAME),
+ m_name(name) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ std::string m_name;
+};
+
+/**
+ * Check for infix of object name
+ */
+class ContainCondition : public Condition
+{
+public:
+ ContainCondition(std::string contain) :
+ Condition(match_type::CONTAIN),
+ m_contain(contain) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ std::string m_contain;
+};
+
+/**
+ * Check for specific SVAL
+ */
+class SvalCondition : public Condition
+{
+public:
+ SvalCondition(uint8_t min, uint8_t max)
+ : Condition(match_type::SVAL)
+ , m_min(min)
+ , m_max(max) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ uint8_t m_min;
+ uint8_t m_max;
+};
+
+/**
+ * Groupings of subconditions
+ */
+class GroupingCondition : public Condition
+{
+public:
+ GroupingCondition(match_type match,
+ std::vector< std::shared_ptr<Condition> > conditions = std::vector< std::shared_ptr<Condition> >())
+ : Condition(match)
+ , m_conditions(conditions) {
+ }
+
+ virtual void add_condition(std::shared_ptr<Condition> condition) {
+ if (condition)
+ {
+ m_conditions.push_back(condition);
+ }
+ }
+
+ // Child manipulation
+ virtual void add_child(ConditionFactory const &factory) override;
+ virtual void remove_child(Condition *condition) override;
+ virtual std::shared_ptr<Condition> first_child() override;
+ virtual std::shared_ptr<Condition> previous_child(Condition *) override;
+ virtual std::shared_ptr<Condition> next_child(Condition *current) override;
+
+ // Parse a list of conditions from JSON property
+ static std::vector< std::shared_ptr<Condition> > parse_conditions(json_t *);
+
+protected:
+ void to_json(json_t *) const override;
+
+protected:
+ std::vector< std::shared_ptr<Condition> > m_conditions;
+};
+
+/**
+ * Conditions that are AND'ed together
+ */
+class AndCondition : public GroupingCondition
+{
+public:
+ AndCondition() : GroupingCondition(match_type::AND) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+};
+
+/**
+ * Conditions that are OR'ed together
+ */
+class OrCondition : public GroupingCondition
+{
+public:
+ OrCondition() : GroupingCondition(match_type::OR) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+};
+
+/**
+ * Check for object status
+ */
+class StatusCondition : public Condition
+{
+public:
+ StatusCondition(status_type status)
+ : Condition(match_type::STATUS)
+ , m_status(status) {
+ }
+
+public:
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ status_type m_status;
+};
+
+/**
+ * Check for player race
+ */
+class RaceCondition : public Condition
+{
+public:
+ RaceCondition(std::string race)
+ : Condition(match_type::RACE)
+ , m_race(race) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ std::string m_race;
+};
+
+/**
+ * Check player subrace
+ */
+class SubraceCondition : public Condition
+{
+public:
+ SubraceCondition(std::string subrace)
+ : Condition(match_type::SUBRACE)
+ , m_subrace(subrace) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ std::string m_subrace;
+};
+
+/**
+ * Check player class
+ */
+class ClassCondition : public Condition
+{
+public:
+ ClassCondition(std::string klass)
+ : Condition(match_type::CLASS)
+ , m_class(klass) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ std::string m_class;
+};
+
+/**
+ * Check object inscription
+ */
+class InscriptionCondition : public Condition
+{
+public:
+ InscriptionCondition(std::string inscription)
+ : Condition(match_type::INSCRIBED)
+ , m_inscription(inscription) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ std::string m_inscription;
+};
+
+/**
+ * Check object discount
+ */
+class DiscountCondition : public Condition
+{
+public:
+ DiscountCondition(int min, int max)
+ : Condition(match_type::DISCOUNT)
+ , m_min(min)
+ , m_max(max) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ int m_min;
+ int m_max;
+};
+
+/**
+ * Check player level
+ */
+class LevelCondition : public Condition
+{
+public:
+ LevelCondition(int min, int max)
+ : Condition(match_type::LEVEL)
+ , m_min(min)
+ , m_max(max) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ int m_min;
+ int m_max;
+};
+
+/**
+ * Check player's skill level
+ */
+class SkillCondition : public Condition
+{
+public:
+ SkillCondition(uint16_t skill_idx, uint16_t min, uint16_t max)
+ : Condition(match_type::SKILL)
+ , m_skill_idx(skill_idx)
+ , m_min(min)
+ , m_max(max) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ uint16_t m_skill_idx;
+ uint16_t m_min;
+ uint16_t m_max;
+};
+
+/**
+ * Check identification state
+ */
+class StateCondition : public Condition
+{
+public:
+ StateCondition(identification_state state)
+ : Condition(match_type::STATE)
+ , m_state(state) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ identification_state m_state;
+};
+
+/**
+ * Check object symbol
+ */
+class SymbolCondition : public Condition
+{
+public:
+ SymbolCondition(char symbol)
+ : Condition(match_type::SYMBOL)
+ , m_symbol(symbol) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ char m_symbol;
+};
+
+/**
+ * Check if player has a particular ability
+ */
+class AbilityCondition : public Condition
+{
+public:
+ AbilityCondition(uint16_t ability_idx)
+ : Condition(match_type::ABILITY)
+ , m_ability_idx(ability_idx) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+
+ void to_json(json_t *) const override;
+
+private:
+ uint16_t m_ability_idx;
+};
+
+/**
+ * Condition with a single subcondition
+ */
+class SingleSubconditionCondition : public Condition
+{
+public:
+ SingleSubconditionCondition(match_type match,
+ std::shared_ptr<Condition> subcondition)
+ : Condition(match)
+ , m_subcondition(subcondition) {
+ }
+
+ virtual void add_child(std::function< std::shared_ptr<Condition> () > const &factory) override;
+
+ virtual void remove_child(Condition *c) override;
+
+ virtual std::shared_ptr<Condition> first_child() override;
+
+protected:
+ void to_json(json_t *) const override;
+
+ static std::shared_ptr<Condition> parse_single_subcondition(
+ json_t *condition_json);
+
+protected:
+ std::shared_ptr<Condition> m_subcondition;
+};
+
+/**
+ * Condition which negates another condition
+ */
+class NotCondition : public SingleSubconditionCondition
+{
+public:
+ NotCondition(std::shared_ptr<Condition> subcondition = nullptr)
+ : SingleSubconditionCondition(match_type::NOT, subcondition) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+};
+
+/**
+ * Condition which checks if player inventory contains object(s)
+ * satisfying another condition.
+ */
+class InventoryCondition : public SingleSubconditionCondition
+{
+public:
+ InventoryCondition(std::shared_ptr<Condition> subcondition = nullptr)
+ : SingleSubconditionCondition(match_type::INVENTORY, subcondition) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+};
+
+/**
+ * Condition which checks if player equipment contains object(s)
+ * satisfying another condition.
+ */
+class EquipmentCondition : public SingleSubconditionCondition
+{
+public:
+ EquipmentCondition(std::shared_ptr<Condition> subcondition = nullptr)
+ : SingleSubconditionCondition(match_type::EQUIPMENT, subcondition) {
+ }
+
+ bool is_match(object_type *) const override;
+
+ static std::shared_ptr<Condition> from_json(json_t *);
+
+protected:
+ void write_tree(TreePrinter *, Cursor *, uint8_t, uint8_t) const override;
+};
+
+} // namespace
diff --git a/src/include/tome/squelch/condition_fwd.hpp b/src/include/tome/squelch/condition_fwd.hpp
new file mode 100644
index 00000000..744e7884
--- /dev/null
+++ b/src/include/tome/squelch/condition_fwd.hpp
@@ -0,0 +1,15 @@
+#ifndef H_1122f873_e83d_4af8_b6a8_a9e8db195473
+#define H_1122f873_e83d_4af8_b6a8_a9e8db195473
+
+#include <functional>
+#include <memory>
+
+namespace squelch {
+
+enum class match_type : int;
+class Condition;
+typedef std::function< std::shared_ptr<Condition> () > ConditionFactory;
+
+} // namespace
+
+#endif
diff --git a/src/include/tome/squelch/condition_metadata.hpp b/src/include/tome/squelch/condition_metadata.hpp
new file mode 100644
index 00000000..fbb26bc3
--- /dev/null
+++ b/src/include/tome/squelch/condition_metadata.hpp
@@ -0,0 +1,12 @@
+#ifndef H_787198a1_aa3e_45c9_bc9f_fd7f490db78c
+#define H_787198a1_aa3e_45c9_bc9f_fd7f490db78c
+
+#include "tome/squelch/condition_metadata_fwd.hpp"
+
+namespace squelch {
+
+std::shared_ptr<Condition> new_condition_interactive();
+
+} // namespace
+
+#endif
diff --git a/src/include/tome/squelch/condition_metadata_fwd.hpp b/src/include/tome/squelch/condition_metadata_fwd.hpp
new file mode 100644
index 00000000..1f57481b
--- /dev/null
+++ b/src/include/tome/squelch/condition_metadata_fwd.hpp
@@ -0,0 +1,14 @@
+#ifndef H_7922f591_bdfd_491d_8561_b11225285fea
+#define H_7922f591_bdfd_491d_8561_b11225285fea
+
+#include "tome/squelch/condition_fwd.hpp"
+
+#include <memory>
+
+namespace squelch {
+
+std::shared_ptr<Condition> new_condition_interactive();
+
+} // namespace
+
+#endif
diff --git a/src/include/tome/squelch/cursor.hpp b/src/include/tome/squelch/cursor.hpp
new file mode 100644
index 00000000..8dbfc6bf
--- /dev/null
+++ b/src/include/tome/squelch/cursor.hpp
@@ -0,0 +1,50 @@
+#ifndef H_8a10111d_64a1_4af9_a85b_24ec8922d3fa
+#define H_8a10111d_64a1_4af9_a85b_24ec8922d3fa
+
+#include <boost/noncopyable.hpp>
+#include <deque>
+
+#include "tome/squelch/condition_fwd.hpp"
+
+namespace squelch {
+
+/**
+ * Cursor which maintains selected condition(s)
+ */
+class Cursor : public boost::noncopyable
+{
+public:
+ bool is_selected(Condition const *condition) const;
+
+ void push(Condition *condition) {
+ m_conditions.push_back(condition);
+ }
+
+ Condition *pop();
+
+ Condition *current();
+
+ void clear() {
+ m_conditions.clear();
+ }
+
+ bool empty() const {
+ return m_conditions.empty();
+ }
+
+ std::size_t size() const {
+ return m_conditions.size();
+ }
+
+ void move_left();
+ void move_right();
+ void move_up();
+ void move_down();
+
+private:
+ std::deque<Condition *> m_conditions;
+};
+
+} // namespace
+
+#endif
diff --git a/src/include/tome/squelch/cursor_fwd.hpp b/src/include/tome/squelch/cursor_fwd.hpp
new file mode 100644
index 00000000..f07e9aa9
--- /dev/null
+++ b/src/include/tome/squelch/cursor_fwd.hpp
@@ -0,0 +1,10 @@
+#ifndef H_a4caa98a_1044_4192_b1af_27c2e8790cae
+#define H_a4caa98a_1044_4192_b1af_27c2e8790cae
+
+namespace squelch {
+
+class Cursor;
+
+} // namespace
+
+#endif
diff --git a/src/include/tome/squelch/object_status.hpp b/src/include/tome/squelch/object_status.hpp
new file mode 100644
index 00000000..c52a35ea
--- /dev/null
+++ b/src/include/tome/squelch/object_status.hpp
@@ -0,0 +1,28 @@
+#ifndef H_e3f9ebbe_ff9a_4687_a847_6101f094b483
+#define H_e3f9ebbe_ff9a_4687_a847_6101f094b483
+
+#include "tome/enum_string_map.hpp"
+
+namespace squelch {
+
+/**
+ * Types of statuses for objects, e.g. "special" for artifacts and
+ * "average" for plain objects with no plusses.
+ */
+enum class status_type {
+ BAD , VERY_BAD, AVERAGE, GOOD, VERY_GOOD,
+ SPECIAL, TERRIBLE, NONE, CHEST_EMPTY, CHEST_DISARMED };
+
+/**
+ * Bidirectional map between status_type values and strings.
+ */
+EnumStringMap<status_type> &status_mapping();
+
+/**
+ * Find the status of an object
+ */
+status_type object_status(object_type *o_ptr);
+
+} // namespace
+
+#endif
diff --git a/src/include/tome/squelch/object_status_fwd.hpp b/src/include/tome/squelch/object_status_fwd.hpp
new file mode 100644
index 00000000..361ea2fe
--- /dev/null
+++ b/src/include/tome/squelch/object_status_fwd.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "../object_type_fwd.hpp"
+#include "tome/enum_string_map.hpp"
+
+namespace squelch {
+
+enum class status_type;
+EnumStringMap<status_type> &status_mapping();
+status_type object_status(object_type *o_ptr);
+
+} // namespace
diff --git a/src/include/tome/squelch/rule.hpp b/src/include/tome/squelch/rule.hpp
new file mode 100644
index 00000000..63f1b6c0
--- /dev/null
+++ b/src/include/tome/squelch/rule.hpp
@@ -0,0 +1,162 @@
+#pragma once
+
+#include <jansson.h>
+#include <memory>
+
+#include "tome/squelch/condition_fwd.hpp"
+#include "tome/squelch/cursor_fwd.hpp"
+#include "tome/squelch/tree_printer_fwd.hpp"
+#include "tome/enum_string_map.hpp"
+#include "../object_type_fwd.hpp"
+
+namespace squelch {
+
+/**
+ * Types of automatizer actions: destroy, pick up, and inscribe.
+ */
+enum class action_type { AUTO_DESTROY, AUTO_PICKUP, AUTO_INSCRIBE };
+
+/**
+ * Bidirectional map between rule actions and strings.
+ */
+EnumStringMap<action_type> &action_mapping();
+
+/**
+ * Rules are the representation of "when condition X is true, do Y".
+ */
+class Rule : public boost::noncopyable
+{
+public:
+ Rule(std::string name,
+ action_type action,
+ int module_idx,
+ std::shared_ptr<Condition> condition)
+ : m_name(name)
+ , m_action(action)
+ , m_module_idx(module_idx)
+ , m_condition(condition) {
+ }
+
+ /**
+ * Set the name of the rule
+ */
+ void set_name(const char *new_name);
+
+ /**
+ * Get the name of the rule
+ */
+ const char *get_name() const;
+
+ /**
+ * Get condition
+ */
+ std::shared_ptr<Condition> get_condition() const;
+
+ /**
+ * Add new condition using a factory to instantiate the
+ * condition only if the rule permits adding a condition.
+ */
+ void add_new_condition(Cursor *cursor,
+ ConditionFactory const &factory);
+
+ /**
+ * Remove currently selected condition
+ */
+ void delete_selected_condition(Cursor *cursor);
+
+ /**
+ * Write out tree representing rule
+ */
+ void write_tree(TreePrinter *p, Cursor *cursor) const;
+
+ /**
+ * Apply rule to object
+ */
+ bool apply_rule(object_type *o_ptr, int item_idx) const;
+
+ /**
+ * Convert rule to JSON
+ */
+ virtual json_t *to_json() const;
+
+ /**
+ * Parse rule from JSON
+ */
+ static std::shared_ptr<Rule> parse_rule(json_t *);
+
+protected:
+ virtual bool do_apply_rule(object_type *, int) const = 0;
+ virtual void do_write_tree(TreePrinter *p) const = 0;
+
+protected:
+ // Rule name
+ std::string m_name;
+ // What does the rule do?
+ action_type m_action;
+ // Which module does this rule apply to?
+ int m_module_idx;
+ // Condition to check
+ std::shared_ptr<Condition> m_condition;
+};
+
+/**
+ * Rule for destroying matching objects
+ */
+class DestroyRule : public Rule
+{
+public:
+ DestroyRule(std::string name,
+ int module_idx,
+ std::shared_ptr<Condition> condition)
+ : Rule(name, action_type::AUTO_DESTROY, module_idx, condition) {
+ }
+
+protected:
+ virtual bool do_apply_rule(object_type *o_ptr, int item_idx) const override;
+ virtual void do_write_tree(TreePrinter *p) const override;
+};
+
+/**
+ * Rule for picking up matching objects
+ */
+class PickUpRule : public Rule
+{
+public:
+
+ PickUpRule(std::string name,
+ int module_idx,
+ std::shared_ptr<Condition> condition)
+ : Rule(name, action_type::AUTO_PICKUP, module_idx, condition) {
+ }
+
+protected:
+ virtual void do_write_tree(TreePrinter *p) const override;
+ virtual bool do_apply_rule(object_type *o_ptr, int item_idx) const override;
+};
+
+/**
+ * Rule for inscribing matching objects
+ */
+class InscribeRule : public Rule
+{
+public:
+ InscribeRule(std::string name,
+ int module_idx,
+ std::shared_ptr<Condition> condition,
+ std::string inscription)
+ : Rule(name, action_type::AUTO_INSCRIBE, module_idx, condition)
+ , m_inscription(inscription) {
+ }
+
+ json_t *to_json() const override;
+
+protected:
+ virtual void do_write_tree(TreePrinter *p) const override;
+ virtual bool do_apply_rule(object_type *o_ptr, int) const override;
+
+private:
+ // Inscription to use for inscription rules.
+ std::string m_inscription;
+};
+
+} // namespace
diff --git a/src/include/tome/squelch/rule_fwd.hpp b/src/include/tome/squelch/rule_fwd.hpp
new file mode 100644
index 00000000..091e77ef
--- /dev/null
+++ b/src/include/tome/squelch/rule_fwd.hpp
@@ -0,0 +1,16 @@
+#ifndef H_4a8d2cfb_182c_4138_983d_606a9ac70784
+#define H_4a8d2cfb_182c_4138_983d_606a9ac70784
+
+#include "tome/enum_string_map.hpp"
+
+namespace squelch {
+
+enum class action_type;
+
+EnumStringMap<action_type> &action_mapping();
+
+class Rule;
+
+} // namespace
+
+#endif
diff --git a/src/include/tome/squelch/tree_printer.hpp b/src/include/tome/squelch/tree_printer.hpp
new file mode 100644
index 00000000..e8ee1e56
--- /dev/null
+++ b/src/include/tome/squelch/tree_printer.hpp
@@ -0,0 +1,49 @@
+#ifndef H_3d6cc652_c674_4a84_911d_e8ec35cc992a
+#define H_3d6cc652_c674_4a84_911d_e8ec35cc992a
+
+#include <boost/noncopyable.hpp>
+#include <cstdint>
+
+namespace squelch {
+
+/**
+ * Printing trees.
+ */
+class TreePrinter : boost::noncopyable
+{
+public:
+ TreePrinter();
+
+ void indent();
+
+ void dedent();
+
+ void reset();
+
+ void reset_scroll();
+
+ void scroll_up();
+
+ void scroll_down();
+
+ void scroll_left();
+
+ void scroll_right();
+
+ void write(uint8_t color, const char *line);
+
+private:
+ int m_indent;
+ int m_write_out_y;
+ int m_write_out_x;
+ int m_write_out_h;
+ int m_write_out_w;
+ int m_write_y;
+ int m_write_x;
+ int m_write_off_x;
+ int m_write_off_y;
+};
+
+} // namespace
+
+#endif
diff --git a/src/include/tome/squelch/tree_printer_fwd.hpp b/src/include/tome/squelch/tree_printer_fwd.hpp
new file mode 100644
index 00000000..d7d75453
--- /dev/null
+++ b/src/include/tome/squelch/tree_printer_fwd.hpp
@@ -0,0 +1,10 @@
+#ifndef H_4ce68eb3_c475_4fc4_8a70_0590c16dc684
+#define H_4ce68eb3_c475_4fc4_8a70_0590c16dc684
+
+namespace squelch {
+
+class TreePrinter;
+
+} // namespace
+
+#endif
diff --git a/src/init1.cc b/src/init1.cc
new file mode 100644
index 00000000..7083a5f3
--- /dev/null
+++ b/src/init1.cc
@@ -0,0 +1,10225 @@
+#include "init1.hpp"
+
+#include "ability_type.hpp"
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "dungeon_info_type.hpp"
+#include "ego_item_type.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "gods.hpp"
+#include "hist_type.hpp"
+#include "init2.hpp"
+#include "meta_class_type.hpp"
+#include "monster2.hpp"
+#include "monster_ego.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "owner_type.hpp"
+#include "player_class.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_type.hpp"
+#include "randart_gen_type.hpp"
+#include "randart_part_type.hpp"
+#include "set_type.hpp"
+#include "skill_type.hpp"
+#include "skills.hpp"
+#include "spells5.hpp"
+#include "store_action_type.hpp"
+#include "store_info_type.hpp"
+#include "store_type.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "trap_type.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "vault_type.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+#include "z-rand.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+
+using boost::algorithm::iequals;
+using boost::algorithm::ends_with;
+
+
+/*
+ * This file is used to initialize various variables and arrays for the
+ * Angband game. Note the use of "fd_read()" and "fd_write()" to bypass
+ * the common limitation of "read()" and "write()" to only 32767 bytes
+ * at a time.
+ *
+ * Several of the arrays for Angband are built from "template" files in
+ * the "lib/file" directory, from which quick-load binary "image" files
+ * are constructed whenever they are not present in the "lib/data"
+ * directory, or if those files become obsolete, if we are allowed.
+ *
+ * Warning -- the "ascii" file parsers use a minor hack to collect the
+ * name and text information in a single pass. Thus, the game will not
+ * be able to load any template file with more than 20K of names or 60K
+ * of text, even though technically, up to 64K should be legal.
+ */
+
+
+/*** Helper arrays for parsing ascii template files ***/
+
+/*
+ * Monster Blow Methods
+ */
+static cptr r_info_blow_method[] =
+{
+ "*",
+ "HIT",
+ "TOUCH",
+ "PUNCH",
+ "KICK",
+ "CLAW",
+ "BITE",
+ "STING",
+ "XXX1",
+ "BUTT",
+ "CRUSH",
+ "ENGULF",
+ "CHARGE",
+ "CRAWL",
+ "DROOL",
+ "SPIT",
+ "EXPLODE",
+ "GAZE",
+ "WAIL",
+ "SPORE",
+ "XXX4",
+ "BEG",
+ "INSULT",
+ "MOAN",
+ "SHOW",
+ NULL
+};
+
+
+/*
+ * Monster Blow Effects
+ */
+static cptr r_info_blow_effect[] =
+{
+ "*",
+ "HURT",
+ "POISON",
+ "UN_BONUS",
+ "UN_POWER",
+ "EAT_GOLD",
+ "EAT_ITEM",
+ "EAT_FOOD",
+ "EAT_LITE",
+ "ACID",
+ "ELEC",
+ "FIRE",
+ "COLD",
+ "BLIND",
+ "CONFUSE",
+ "TERRIFY",
+ "PARALYZE",
+ "LOSE_STR",
+ "LOSE_INT",
+ "LOSE_WIS",
+ "LOSE_DEX",
+ "LOSE_CON",
+ "LOSE_CHR",
+ "LOSE_ALL",
+ "SHATTER",
+ "EXP_10",
+ "EXP_20",
+ "EXP_40",
+ "EXP_80",
+ "DISEASE",
+ "TIME",
+ "INSANITY",
+ "HALLU",
+ "PARASITE",
+ "ABOMINATION",
+ NULL
+};
+
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags1[] =
+{
+ "UNIQUE",
+ "QUESTOR",
+ "MALE",
+ "FEMALE",
+ "CHAR_CLEAR",
+ "CHAR_MULTI",
+ "ATTR_CLEAR",
+ "ATTR_MULTI",
+ "FORCE_DEPTH",
+ "FORCE_MAXHP",
+ "FORCE_SLEEP",
+ "FORCE_EXTRA",
+ "FRIEND",
+ "FRIENDS",
+ "ESCORT",
+ "ESCORTS",
+ "NEVER_BLOW",
+ "NEVER_MOVE",
+ "RAND_25",
+ "RAND_50",
+ "ONLY_GOLD",
+ "ONLY_ITEM",
+ "DROP_60",
+ "DROP_90",
+ "DROP_1D2",
+ "DROP_2D2",
+ "DROP_3D2",
+ "DROP_4D2",
+ "DROP_GOOD",
+ "DROP_GREAT",
+ "DROP_USEFUL",
+ "DROP_CHOSEN"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags2[] =
+{
+ "STUPID",
+ "SMART",
+ "CAN_SPEAK",
+ "REFLECTING",
+ "INVISIBLE",
+ "COLD_BLOOD",
+ "EMPTY_MIND",
+ "WEIRD_MIND",
+ "DEATH_ORB",
+ "REGENERATE",
+ "SHAPECHANGER",
+ "ATTR_ANY",
+ "POWERFUL",
+ "ELDRITCH_HORROR",
+ "AURA_FIRE",
+ "AURA_ELEC",
+ "OPEN_DOOR",
+ "BASH_DOOR",
+ "PASS_WALL",
+ "KILL_WALL",
+ "MOVE_BODY",
+ "KILL_BODY",
+ "TAKE_ITEM",
+ "KILL_ITEM",
+ "BRAIN_1",
+ "BRAIN_2",
+ "BRAIN_3",
+ "BRAIN_4",
+ "BRAIN_5",
+ "BRAIN_6",
+ "BRAIN_7",
+ "BRAIN_8"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags3[] =
+{
+ "ORC",
+ "TROLL",
+ "GIANT",
+ "DRAGON",
+ "DEMON",
+ "UNDEAD",
+ "EVIL",
+ "ANIMAL",
+ "THUNDERLORD",
+ "GOOD",
+ "AURA_COLD", /* TODO: Implement aura_cold */
+ "NONLIVING",
+ "HURT_LITE",
+ "HURT_ROCK",
+ "SUSCEP_FIRE",
+ "SUSCEP_COLD",
+ "IM_ACID",
+ "IM_ELEC",
+ "IM_FIRE",
+ "IM_COLD",
+ "IM_POIS",
+ "RES_TELE",
+ "RES_NETH",
+ "RES_WATE",
+ "RES_PLAS",
+ "RES_NEXU",
+ "RES_DISE",
+ "UNIQUE_4",
+ "NO_FEAR",
+ "NO_STUN",
+ "NO_CONF",
+ "NO_SLEEP"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags4[] =
+{
+ "SHRIEK",
+ "MULTIPLY",
+ "S_ANIMAL",
+ "ROCKET",
+ "ARROW_1",
+ "ARROW_2",
+ "ARROW_3",
+ "ARROW_4",
+ "BR_ACID",
+ "BR_ELEC",
+ "BR_FIRE",
+ "BR_COLD",
+ "BR_POIS",
+ "BR_NETH",
+ "BR_LITE",
+ "BR_DARK",
+ "BR_CONF",
+ "BR_SOUN",
+ "BR_CHAO",
+ "BR_DISE",
+ "BR_NEXU",
+ "BR_TIME",
+ "BR_INER",
+ "BR_GRAV",
+ "BR_SHAR",
+ "BR_PLAS",
+ "BR_WALL",
+ "BR_MANA",
+ "BA_NUKE",
+ "BR_NUKE",
+ "BA_CHAO",
+ "BR_DISI",
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags5[] =
+{
+ "BA_ACID",
+ "BA_ELEC",
+ "BA_FIRE",
+ "BA_COLD",
+ "BA_POIS",
+ "BA_NETH",
+ "BA_WATE",
+ "BA_MANA",
+ "BA_DARK",
+ "DRAIN_MANA",
+ "MIND_BLAST",
+ "BRAIN_SMASH",
+ "CAUSE_1",
+ "CAUSE_2",
+ "CAUSE_3",
+ "CAUSE_4",
+ "BO_ACID",
+ "BO_ELEC",
+ "BO_FIRE",
+ "BO_COLD",
+ "BO_POIS",
+ "BO_NETH",
+ "BO_WATE",
+ "BO_MANA",
+ "BO_PLAS",
+ "BO_ICEE",
+ "MISSILE",
+ "SCARE",
+ "BLIND",
+ "CONF",
+ "SLOW",
+ "HOLD"
+};
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags6[] =
+{
+ "HASTE",
+ "HAND_DOOM",
+ "HEAL",
+ "S_ANIMALS",
+ "BLINK",
+ "TPORT",
+ "TELE_TO",
+ "TELE_AWAY",
+ "TELE_LEVEL",
+ "DARKNESS",
+ "TRAPS",
+ "FORGET",
+ "ANIM_DEAD", /* ToDo: Implement ANIM_DEAD */
+ "S_BUG",
+ "S_RNG",
+ "S_THUNDERLORD", /* DG : Summon Thunderlord */
+ "S_KIN",
+ "S_HI_DEMON",
+ "S_MONSTER",
+ "S_MONSTERS",
+ "S_ANT",
+ "S_SPIDER",
+ "S_HOUND",
+ "S_HYDRA",
+ "S_ANGEL",
+ "S_DEMON",
+ "S_UNDEAD",
+ "S_DRAGON",
+ "S_HI_UNDEAD",
+ "S_HI_DRAGON",
+ "S_WRAITH",
+ "S_UNIQUE"
+};
+
+
+/*
+ * Monster race flags
+ */
+static cptr r_info_flags7[] =
+{
+ "AQUATIC",
+ "CAN_SWIM",
+ "CAN_FLY",
+ "FRIENDLY",
+ "PET",
+ "MORTAL",
+ "SPIDER",
+ "NAZGUL",
+ "DG_CURSE",
+ "POSSESSOR",
+ "NO_DEATH",
+ "NO_TARGET",
+ "AI_ANNOY",
+ "AI_SPECIAL",
+ "NEUTRAL",
+ "DROP_ART",
+ "DROP_RANDART",
+ "AI_PLAYER",
+ "NO_THEFT",
+ "SPIRIT",
+ "XXX7X20",
+ "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",
+ "XXX5",
+ "XXX5",
+ "XXX5",
+ "EASY_USE",
+ "IM_NETHER",
+ "RECHARGED",
+ "ULTIMATE",
+ "AUTO_ID",
+ "LITE2",
+ "LITE3",
+ "FUEL_LITE",
+ "XXX5",
+ "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"
+};
+
+/*
+ * 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}
+};
+
+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*/
+ "ETERNAL_FLAME", /* 201 */
+ "MAGGOT", /* 202 */
+ "LEBOHAUM", /* 203 */
+ "DURANDIL", /* 204 */
+ "RADAGAST", /* 205, Theme */
+ "VALAROMA", /* 206, Theme */
+ ""
+};
+
+/*
+ * 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;
+ }
+ }
+}
+
+/**
+ * Version of strdup() which just aborts if an allocation
+ * error occurs.
+ */
+static char *my_strdup(const char *s)
+{
+ char *p = strdup(s);
+ if (!p)
+ {
+ abort();
+ }
+ return p;
+}
+
+
+/**
+ * Append one string to the end of another, reallocating if
+ * necessary.
+ */
+static void strappend(char **s, const char *t)
+{
+ // Do we need to initialize the destination string?
+ if (*s == nullptr)
+ {
+ // Costs an extra allocation which could be avoided
+ // but this leads to simpler code.
+ *s = my_strdup("");
+ }
+ // We should really be preserving the original pointer and
+ // do something else in case of failure to realloc(), but
+ // instead we just do the lazy thing and call abort() if
+ // reallocation fails. In practice it won't.
+ *s = static_cast<char *>(realloc(*s, strlen(*s) + strlen(t) + 1));
+ if (*s == nullptr)
+ {
+ abort(); // Cannot handle failure to reallocate
+ }
+
+ /* Append 't' to the destination string */
+ strcat(*s, t);
+}
+
+/*** 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); 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); 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) */
+static int get_activation(char *activation)
+{
+ int i;
+ for ( i = 0 ; activation_names[i][0] ; i++)
+ {
+ if (!strncmp(activation_names[i], activation, 19))
+ {
+ return i;
+ }
+ }
+
+ msg_format("Unknown activation '%s'.", activation);
+ 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)
+{
+ int i = 0, z;
+ int powers = 0;
+ int lev = 1;
+ int tit_idx = 0;
+ int spec_idx = 0;
+ int cur_ab = -1;
+ char buf[1024];
+ char *s, *t;
+
+ /* 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;
+
+ /* 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 */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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]);
+
+ /* Copy text */
+ assert(!bg[idx].info);
+ bg[idx].info = my_strdup(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 >= max_rp_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ rp_ptr = &race_info[i];
+
+ /* Copy title */
+ assert(!rp_ptr->title);
+ rp_ptr->title = my_strdup(s);
+
+ /* Initialize */
+ 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;
+
+ if (!rp_ptr->desc)
+ {
+ rp_ptr->desc = my_strdup(s);
+ }
+ else
+ {
+ strappend(&rp_ptr->desc, format("\n%s", s));
+ }
+
+ /* 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 (iequals(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 >= max_rmp_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ rmp_ptr = &race_mod_info[i];
+
+ /* Copy title */
+ assert(!rmp_ptr->title);
+ rmp_ptr->title = my_strdup(s);
+
+ /* Initialize */
+ 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;
+
+ /* Place */
+ if (buf[4] == 'A')
+ {
+ rmp_ptr->place = TRUE;
+ }
+ else
+ {
+ rmp_ptr->place = FALSE;
+ }
+
+ /* Description */
+ if (!rmp_ptr->desc)
+ {
+ rmp_ptr->desc = my_strdup(s);
+ }
+ else
+ {
+ /* Append chars to the name */
+ strappend(&rmp_ptr->desc, format("\n%s", s));
+ }
+
+ /* 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 (iequals(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 >= max_c_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ c_ptr = &class_info[i];
+
+ /* Copy name */
+ assert(!c_ptr->title);
+ c_ptr->title = my_strdup(s);
+
+ /* Initialize */
+ 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;
+
+ switch (buf[4])
+ {
+ case '0': /* Class description */
+ if (!c_ptr->desc)
+ {
+
+ c_ptr->desc = my_strdup(s);
+ }
+ else
+ {
+ strappend(&c_ptr->desc, format("\n%s", s));
+ }
+ break;
+
+ case '1': /* Class title */
+ /* Copy */
+ assert(!c_ptr->titles[tit_idx]);
+ c_ptr->titles[tit_idx] = my_strdup(s);
+
+ /* Go to next title in array */
+ tit_idx++;
+
+ break;
+
+ default: /* Unknown */
+ 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 (iequals(s, powers_type[i].name)) break;
+ }
+
+ if (i == POWER_MAX) return (6);
+
+ c_ptr->powers[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'K' for "sKills" */
+ if ((buf[0] == 'C') && (buf[2] == 'K'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ c_ptr->c_dis = s[0];
+ c_ptr->c_dev = s[1];
+ c_ptr->c_sav = s[2];
+ c_ptr->c_stl = s[3];
+ c_ptr->c_srh = s[4];
+ c_ptr->c_fos = s[5];
+ c_ptr->c_thn = s[6];
+ c_ptr->c_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'x' for "Xtra skills" */
+ if ((buf[0] == 'C') && (buf[2] == 'X'))
+ {
+ int s[8];
+
+ /* Scan for the values */
+ if (8 != sscanf(buf + 4, "%d:%d:%d:%d:%d:%d:%d:%d",
+ &s[0], &s[1], &s[2], &s[3], &s[4], &s[5], &s[6], &s[7])) return (1);
+
+ c_ptr->x_dis = s[0];
+ c_ptr->x_dev = s[1];
+ c_ptr->x_sav = s[2];
+ c_ptr->x_stl = s[3];
+ c_ptr->x_srh = s[4];
+ c_ptr->x_fos = s[5];
+ c_ptr->x_thn = s[6];
+ c_ptr->x_thb = s[7];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'P' for "xtra" */
+ if ((buf[0] == 'C') && (buf[2] == 'P'))
+ {
+ int s[2];
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 4, "%d:%d",
+ &s[0], &s[1])) return (1);
+
+ c_ptr->c_mhp = s[0];
+ c_ptr->c_exp = s[1];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'C' for "sensing" */
+ if ((buf[0] == 'C') && (buf[2] == 'C'))
+ {
+ long int s[3];
+ char h, m;
+
+ /* Scan for the values */
+ if (5 != sscanf(buf + 4, "%c:%c:%ld:%ld:%ld",
+ &h, &m, &s[0], &s[1], &s[2])) return (1);
+
+ c_ptr->sense_heavy = (h == 'H') ? TRUE : FALSE;
+ c_ptr->sense_heavy_magic = (m == 'H') ? TRUE : FALSE;
+ c_ptr->sense_base = s[0];
+ c_ptr->sense_pl = s[1];
+ c_ptr->sense_plus = s[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'B' for "blows" */
+ if ((buf[0] == 'C') && (buf[2] == 'B'))
+ {
+ int s[3];
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 4, "%d:%d:%d",
+ &s[0], &s[1], &s[2])) return (1);
+
+ c_ptr->blow_num = s[0];
+ c_ptr->blow_wgt = s[1];
+ c_ptr->blow_mul = s[2];
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'G' for "Player flags" (multiple lines) */
+ if ((buf[0] == 'C') && (buf[2] == 'G'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_player_race_flag(&c_ptr->flags1, &c_ptr->flags2, s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'F' for "level Flags" (multiple lines) */
+ if ((buf[0] == 'C') && (buf[2] == 'F'))
+ {
+ /* Parse every entry */
+ for (s = buf + 4; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_race_kind_flag(&c_ptr->oflags1[lev], &c_ptr->oflags2[lev], &c_ptr->oflags3[lev], &c_ptr->oflags4[lev], &c_ptr->oflags5[lev], &c_ptr->oesp[lev], s)) return (5);
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Specialities */
+ if ((buf[0] == 'C') && (buf[2] == 'a'))
+ {
+ /* Process 'N' for "New/Number/Name" */
+ if (buf[4] == 'N')
+ {
+ /* Find the colon before the name */
+ s = buf + 6;
+
+ /* Paranoia -- require a name */
+ if (!*s) return (1);
+ /* Get the index */
+ spec_idx++;
+
+ /* Verify information */
+ if (spec_idx >= MAX_SPEC) return (2);
+
+ /* Point at the "info" */
+ s_ptr = &c_ptr->spec[spec_idx];
+
+ /* Copy title */
+ assert(!s_ptr->title);
+ s_ptr->title = my_strdup(s);
+
+ /* Initialize */
+ 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;
+
+ if (!s_ptr->desc)
+ {
+ s_ptr->desc = my_strdup(s);
+ }
+ else
+ {
+ strappend(&s_ptr->desc, format("\n%s", s));
+ }
+
+ /* 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 (class_info[i].title && iequals(s, class_info[i].title))
+ {
+ break;
+ }
+ }
+
+ if (i == max_c_idx) return (6);
+
+ mc_ptr->classes[powers++] = i;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize the "v_info" array, by parsing an ascii "template" file
+ */
+errr init_v_info_txt(FILE *fp)
+{
+ int i;
+ char buf[1024];
+ char *s;
+
+ /* Current entry */
+ vault_type *v_ptr = NULL;
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_v_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ v_ptr = &v_info[i];
+
+ /* Initialize data -- we ignore the name, it's not
+ * used for anything */
+ v_ptr->data = my_strdup("");
+
+ /* 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;
+
+ /* Append data */
+ strappend(&v_ptr->data, 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);
+ }
+
+
+ /* 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)
+{
+ int i;
+ char buf[1024];
+ char *s, *t;
+
+ /* Current entry */
+ feature_type *f_ptr = NULL;
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_f_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ f_ptr = &f_info[i];
+
+ /* Copy name */
+ assert(!f_ptr->name);
+ f_ptr->name = my_strdup(s);
+
+ /* Initialize */
+ f_ptr->mimic = i;
+ f_ptr->text = DEFAULT_FEAT_TEXT;
+ f_ptr->tunnel = DEFAULT_FEAT_TUNNEL;
+ f_ptr->block = DEFAULT_FEAT_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;
+
+ switch (buf[2])
+ {
+ case '0':
+ assert(f_ptr->text == DEFAULT_FEAT_TEXT);
+ f_ptr->text = my_strdup(s);
+ break;
+ case '1':
+ assert(f_ptr->tunnel == DEFAULT_FEAT_TUNNEL);
+ f_ptr->tunnel = my_strdup(s);
+ break;
+ case '2':
+ assert(f_ptr->block == DEFAULT_FEAT_BLOCK);
+ f_ptr->block = my_strdup(s);
+ break;
+ default:
+ return (6);
+ }
+
+ /* 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);
+ }
+
+ /* 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)
+{
+ int i;
+ char buf[1024];
+ char *s, *t;
+
+ /* Current entry */
+ object_kind *k_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+
+ /* 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 >= max_k_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ k_ptr = &k_info[i];
+
+ /* Advance and Save the name index */
+ assert(!k_ptr->name);
+ k_ptr->name = my_strdup(s);
+
+ /* Ensure empty description */
+ k_ptr->text = my_strdup("");
+
+ /* 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;
+
+ /* Append description */
+ strappend(&k_ptr->text, 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, unused, wgt;
+ long cost;
+
+ /* Scan for the values */
+ if (4 != sscanf(buf + 2, "%d:%d:%d:%ld",
+ &level, &unused, &wgt, &cost)) return (1);
+
+ /* Save the values */
+ k_ptr->level = level;
+ 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 (iequals(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')
+ {
+ k_ptr->activate = get_activation(buf + 2);
+ 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)
+ {
+ if (i >= ALLOCATION_MAX) {
+ msg_print("Too many allocation entries.");
+ return 1;
+ }
+
+ /* 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);
+ }
+
+ /* 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)
+{
+ int i;
+ char buf[1024];
+ char *s, *t;
+
+ /* Current entry */
+ artifact_type *a_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_a_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ a_ptr = &a_info[i];
+
+ /* Copy name */
+ assert(!a_ptr->name);
+ a_ptr->name = my_strdup(s);
+
+ /* Ensure empty description */
+ a_ptr->text = my_strdup("");
+
+ /* 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;
+
+ /* Add separator if necessary */
+ if (*a_ptr->text != '\0' && !ends_with(a_ptr->text, " ")) {
+ strappend(&a_ptr->text, " ");
+ }
+
+ /* Append to description */
+ strappend(&a_ptr->text, 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 (iequals(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')
+ {
+ a_ptr->activate = get_activation(buf + 2);
+ if (a_ptr->activate == -1)
+ {
+ return 1;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+
+ /* Oops */
+ return (6);
+ }
+
+ /* Success */
+ return (0);
+}
+
+/*
+* Initialize the "set_info" array, by parsing an ascii "template" file
+*/
+errr init_set_info_txt(FILE *fp)
+{
+ int i;
+ int cur_art = 0, cur_num = 0;
+ char buf[1024];
+
+ char *s, *t;
+
+ /* Current entry */
+ set_type *set_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_set_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ set_ptr = &set_info[i];
+
+ /* Copy name */
+ assert(!set_ptr->name);
+ set_ptr->name = my_strdup(s);
+
+ /* Initialize */
+ 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;
+
+ /* Append chars to the description */
+ strappend(&set_ptr->desc, 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);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Initialize the "s_info" array, by parsing an ascii "template" file
+ */
+errr init_s_info_txt(FILE *fp)
+{
+ int i, z, order = 1;
+ char buf[1024];
+ char *s;
+
+ /* Current entry */
+ skill_type *s_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_s_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ s_ptr = &s_info[i];
+
+ /* Copy name */
+ assert(!s_ptr->name);
+ s_ptr->name = my_strdup(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;
+
+ /* Description */
+ if (!s_ptr->desc)
+ {
+ s_ptr->desc = my_strdup(s);
+ }
+ else
+ {
+ strappend(&s_ptr->desc, format("\n%s", s));
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'A' for "Activation Description" (one line only) */
+ if (buf[0] == 'A')
+ {
+ char *txt;
+
+ /* Acquire the text */
+ s = buf + 2;
+
+ if (NULL == (txt = strchr(s, ':'))) return (1);
+ *txt = '\0';
+ txt++;
+
+ /* Copy action description */
+ assert(!s_ptr->action_desc);
+ s_ptr->action_desc = my_strdup(txt);
+
+ /* Copy mkey index */
+ s_ptr->action_mkey = atoi(s);
+
+ /* 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);
+ }
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "ab_info" array, by parsing an ascii "template" file
+ */
+errr init_ab_info_txt(FILE *fp)
+{
+ int i, z;
+ char buf[1024];
+ char *s;
+
+ /* Current entry */
+ ability_type *ab_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_ab_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ab_ptr = &ab_info[i];
+
+ /* Copy name */
+ assert(!ab_ptr->name);
+ ab_ptr->name = my_strdup(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;
+
+ /* Append description */
+ if (!ab_ptr->desc)
+ {
+ ab_ptr->desc = my_strdup(s);
+ }
+ else
+ {
+ strappend(&ab_ptr->desc, format("\n%s", s));
+ }
+
+ /* 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++;
+
+ /* Copy name */
+ assert(!ab_ptr->action_desc);
+ ab_ptr->action_desc = my_strdup(txt);
+
+ /* Set mkey */
+ ab_ptr->action_mkey = atoi(s);
+
+ /* 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);
+ }
+
+ /* 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;
+ assert(n < FLAG_RARITY_MAX);
+
+ /* 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)
+{
+ int i, cur_r = -1, cur_t = 0, j;
+ char buf[1024];
+ char *s, *t;
+
+ /* 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 */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_e_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ e_ptr = &e_info[i];
+
+ /* Copy name */
+ assert(!e_ptr->name);
+ e_ptr->name = my_strdup(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 < FLAG_RARITY_MAX; 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;
+ e_ptr->oflags1[j] = 0;
+ e_ptr->oflags2[j] = 0;
+ e_ptr->oflags3[j] = 0;
+ e_ptr->oflags4[j] = 0;
+ e_ptr->oflags5[j] = 0;
+ e_ptr->oesp[j] = 0;
+ e_ptr->fego[j] = 0;
+ }
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current e_ptr */
+ if (!e_ptr) return (3);
+
+
+ /* Process 'T' for "Tval/Sval" (up to 5 lines) */
+ if (buf[0] == 'T')
+ {
+ int tv, minsv, maxsv;
+
+ if (cur_t == 10) return 1;
+
+ /* Scan for the values */
+ if (3 != sscanf(buf + 2, "%d:%d:%d",
+ &tv, &minsv, &maxsv)) return (1);
+
+ /* Save the values */
+ e_ptr->tval[cur_t] = tv;
+ e_ptr->min_sval[cur_t] = minsv;
+ e_ptr->max_sval[cur_t] = maxsv;
+
+ cur_t++;
+
+ /* Next... */
+ continue;
+ }
+
+ /* Process 'R' for "flags rarity" (up to 5 lines) */
+ if (buf[0] == 'R')
+ {
+ int rar;
+
+ cur_r++;
+
+ if (cur_r >= FLAG_RARITY_MAX) {
+ return 1;
+ }
+
+ /* Scan for the values */
+ if (1 != sscanf(buf + 2, "%d",
+ &rar)) return (1);
+
+ /* 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 (iequals(s, powers_type[i].name)) break;
+ }
+
+ if (i == POWER_MAX) return (6);
+
+ e_ptr->power = i;
+
+ /* Next... */
+ continue;
+ }
+
+ if (buf[0] == 'a')
+ {
+ e_ptr->activate = get_activation(buf + 2);
+ 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);
+ }
+
+ /* 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)
+{
+ int i, cur_t = 0, j, cur_g = 0;
+ char buf[1024];
+ char *s, *t;
+
+ /* 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 */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_ra_idx) 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 (iequals(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);
+ }
+
+ /* 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)
+{
+ int i;
+ char buf[1024];
+ char *s, *t;
+
+ /* Current entry */
+ monster_race *r_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_r_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ r_ptr = &r_info[i];
+
+ /* Allocate name string. */
+ assert(!r_ptr->name); // Sanity check that we aren't overwriting anything
+ r_ptr->name = my_strdup(s);
+
+ /* Ensure empty description */
+ r_ptr->text = my_strdup("");
+
+ /* 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;
+
+ /* Append to description */
+ strappend(&r_ptr->text, 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);
+ }
+
+ /* Postprocessing */
+ 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;
+ }
+
+ /* 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)
+{
+ int i, j;
+ char buf[1024];
+ byte blow_num = 0;
+ int r_char_number = 0, nr_char_number = 0;
+
+ char *s, *t;
+
+ /* Current entry */
+ monster_ego *re_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_re_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ re_ptr = &re_info[i];
+
+ /* Copy name */
+ assert(!re_ptr->name);
+ re_ptr->name = my_strdup(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);
+ }
+
+ /* 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)
+{
+ int i;
+ char buf[1024];
+ char *s, *t;
+
+ /* Current entry */
+ trap_type *t_ptr = NULL;
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_t_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ t_ptr = &t_info[i];
+
+ /* Copy name */
+ t_ptr->name = my_strdup(s);
+
+ /* Initialize */
+ t_ptr->text = my_strdup("");
+
+ /* 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;
+
+ /* Append chars to the name */
+ strappend(&t_ptr->text, 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);
+ }
+
+ /* 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)
+{
+ int i, j;
+ char buf[1024];
+
+ s16b rule_num = 0;
+
+ byte r_char_number = 0;
+
+ char *s, *t;
+
+ /* Current entry */
+ dungeon_info_type *d_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_d_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ d_ptr = &d_info[i];
+
+ /* Copy name */
+ assert(!d_ptr->name);
+ d_ptr->name = my_strdup(s);
+
+ /* Initialize description */
+ d_ptr->text = my_strdup("");
+
+ /* 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;
+
+ /* Append to description */
+ strappend(&d_ptr->text, 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);
+ }
+
+ /* 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)
+{
+ int i = 0, item_idx = 0;
+ char buf[1024];
+ char *s, *t;
+
+ /* Current entry */
+ store_info_type *st_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_st_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ st_ptr = &st_info[i];
+
+ /* Copy name */
+ assert(!st_ptr->name);
+ st_ptr->name = my_strdup(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;
+ assert(st_ptr->table_num <= STORE_CHOICES);
+
+ /* 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);
+ }
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "ba_info" array, by parsing an ascii "template" file
+ */
+errr init_ba_info_txt(FILE *fp)
+{
+ int i = 0;
+ char buf[1024];
+ char *s;
+
+ /* Current entry */
+ store_action_type *ba_ptr = NULL;
+
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_ba_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ba_ptr = &ba_info[i];
+
+ /* Copy name */
+ assert(!ba_ptr->name);
+ ba_ptr->name = my_strdup(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);
+ }
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "ow_info" array, by parsing an ascii "template" file
+ */
+errr init_ow_info_txt(FILE *fp)
+{
+ int i;
+ char buf[1024];
+ char *s, *t;
+
+ /* Current entry */
+ owner_type *ow_ptr = NULL;
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_ow_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ ow_ptr = &ow_info[i];
+
+ /* Copy name */
+ assert(!ow_ptr->name);
+ ow_ptr->name = my_strdup(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, inf;
+
+ /* Scan for the values */
+ if (2 != sscanf(buf + 2, "%d:%d",
+ &cost, &inf)) return (1);
+
+ /* Save the values */
+ ow_ptr->max_cost = cost;
+ ow_ptr->inflation = inf;
+
+ /* 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);
+ }
+
+ /* Success */
+ return (0);
+}
+
+/*
+ * Initialize the "wf_info" array, by parsing an ascii "template" file
+ */
+errr init_wf_info_txt(FILE *fp)
+{
+ int i;
+ char buf[1024];
+ char *s;
+
+ /* Current entry */
+ wilderness_type_info *wf_ptr = NULL;
+
+ /* Just before the first record */
+ error_idx = -1;
+
+ /* Just before the first line */
+ error_line = -1;
+
+ /* Parse */
+ while (0 == my_fgets(fp, 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);
+
+ /* 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 >= max_wf_idx) return (2);
+
+ /* Save the index */
+ error_idx = i;
+
+ /* Point at the "info" */
+ wf_ptr = &wf_info[i];
+
+ /* Copy the name */
+ assert(!wf_ptr->name);
+ wf_ptr->name = my_strdup(s);
+
+ /* Next... */
+ continue;
+ }
+
+ /* There better be a current wf_ptr */
+ if (!wf_ptr) return (3);
+
+ /* Process 'D' for "Description (one line only) */
+ if (buf[0] == 'D')
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ /* Copy description */
+ assert(!wf_ptr->text);
+ wf_ptr->text = my_strdup(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;
+ }
+
+ /* Oops */
+ return (6);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/* Random dungeon grid effects */
+#define RANDOM_NONE 0x00
+#define RANDOM_FEATURE 0x01
+#define RANDOM_MONSTER 0x02
+#define RANDOM_OBJECT 0x04
+#define RANDOM_EGO 0x08
+#define RANDOM_ARTIFACT 0x10
+#define RANDOM_TRAP 0x20
+
+
+typedef struct dungeon_grid dungeon_grid;
+
+struct dungeon_grid
+{
+ int feature; /* Terrain feature */
+ int monster; /* Monster */
+ int object; /* Object */
+ int ego; /* Ego-Item */
+ int artifact; /* Artifact */
+ int trap; /* Trap */
+ int cave_info; /* Flags for CAVE_MARK, CAVE_GLOW, CAVE_ICKY, CAVE_ROOM */
+ int special; /* Reserved for special terrain info */
+ int random; /* Number of the random effect */
+ int bx, by; /* For between gates */
+ int mimic; /* Mimiced features */
+ s32b mflag; /* monster's mflag */
+ bool_ ok;
+ bool_ defined;
+};
+static bool_ meta_sleep = TRUE;
+
+static dungeon_grid letter[255];
+
+/*
+ * Parse a sub-file of the "extra info"
+ */
+static errr process_dungeon_file_aux(char *buf, int *yval, int *xval, int xvalstart, int ymax, int xmax, bool_ full)
+{
+ int i;
+
+ char *zz[33];
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) return (0);
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) return (0);
+
+ /* Skip comments */
+ if (buf[0] == '#') return (0);
+
+ /* Require "?:*" format */
+ if (buf[1] != ':') return (1);
+
+
+ /* Process "%:<fname>" */
+ if (buf[0] == '%')
+ {
+ /* Attempt to Process the given file */
+ return (process_dungeon_file(buf + 2, yval, xval, ymax, xmax, FALSE, full));
+ }
+
+ /* Process "N:<sleep>" */
+ if (buf[0] == 'N')
+ {
+ int num;
+
+ if ((num = tokenize(buf + 2, 1, zz, ':', '/')) > 0)
+ {
+ meta_sleep = atoi(zz[0]);
+ }
+
+ return (0);
+ }
+
+ /* Process "F:<letter>:<terrain>:<cave_info>:<monster>:<object>:<ego>:<artifact>:<trap>:<special>:<mimic>:<mflag>" -- info for dungeon grid */
+ if (buf[0] == 'F')
+ {
+ int num;
+
+ if ((num = tokenize(buf + 2, 11, zz, ':', '/')) > 1)
+ {
+ int index = zz[0][0];
+
+ /* Reset the feature */
+ letter[index].feature = 0;
+ letter[index].monster = 0;
+ letter[index].object = 0;
+ letter[index].ego = 0;
+ letter[index].artifact = 0;
+ letter[index].trap = 0;
+ letter[index].cave_info = 0;
+ letter[index].special = 0;
+ letter[index].random = 0;
+ letter[index].mimic = 0;
+ letter[index].mflag = 0;
+ letter[index].ok = TRUE;
+ letter[index].defined = TRUE;
+
+ if (num > 1)
+ {
+ if (zz[1][0] == '*')
+ {
+ letter[index].random |= RANDOM_FEATURE;
+ if (zz[1][1])
+ {
+ zz[1]++;
+ letter[index].feature = atoi(zz[1]);
+ }
+ }
+ else
+ {
+ letter[index].feature = atoi(zz[1]);
+ }
+ }
+
+ if (num > 2)
+ letter[index].cave_info = atoi(zz[2]);
+
+ /* Monster */
+ if (num > 3)
+ {
+ if (zz[3][0] == '*')
+ {
+ letter[index].random |= RANDOM_MONSTER;
+ if (zz[3][1])
+ {
+ zz[3]++;
+ letter[index].monster = atoi(zz[3]);
+ }
+ }
+ else
+ {
+ letter[index].monster = atoi(zz[3]);
+ }
+ }
+
+ /* Object */
+ if (num > 4)
+ {
+ if (zz[4][0] == '*')
+ {
+ letter[index].random |= RANDOM_OBJECT;
+
+ if (zz[4][1])
+ {
+ zz[4]++;
+ letter[index].object = atoi(zz[4]);
+ }
+ }
+ else
+ {
+ letter[index].object = atoi(zz[4]);
+ }
+ }
+
+ /* Ego-Item */
+ if (num > 5)
+ {
+ if (zz[5][0] == '*')
+ {
+ letter[index].random |= RANDOM_EGO;
+
+ if (zz[5][1])
+ {
+ zz[5]++;
+ letter[index].ego = atoi(zz[5]);
+ }
+ }
+ else
+ {
+ letter[index].ego = atoi(zz[5]);
+ }
+ }
+
+ /* Artifact */
+ if (num > 6)
+ {
+ if (zz[6][0] == '*')
+ {
+ letter[index].random |= RANDOM_ARTIFACT;
+
+ if (zz[6][1])
+ {
+ zz[6]++;
+ letter[index].artifact = atoi(zz[6]);
+ }
+ }
+ else
+ {
+ letter[index].artifact = atoi(zz[6]);
+ }
+ }
+
+ if (num > 7)
+ {
+ if (zz[7][0] == '*')
+ {
+ letter[index].random |= RANDOM_TRAP;
+
+ if (zz[7][1])
+ {
+ zz[7]++;
+ letter[index].trap = atoi(zz[7]);
+ }
+ }
+ else
+ letter[index].trap = atoi(zz[7]);
+ }
+
+ if (num > 8)
+ {
+ /* Quests can be defined by name only */
+ if (zz[8][0] == '"')
+ {
+ int i;
+
+ /* Hunt & shoot the ending " */
+ i = strlen(zz[8]) - 1;
+ if (zz[8][i] == '"') zz[8][i] = '\0';
+ letter[index].special = 0;
+ for (i = 0; i < MAX_Q_IDX; i++)
+ {
+ if (!strcmp(&zz[8][1], quest[i].name))
+ {
+ letter[index].special = i;
+ break;
+ }
+ }
+ }
+ else
+ letter[index].special = atoi(zz[8]);
+ }
+
+ if (num > 9)
+ {
+ letter[index].mimic = atoi(zz[9]);
+ }
+
+ if (num > 10)
+ {
+ letter[index].mflag = atoi(zz[10]);
+ }
+
+ return (0);
+ }
+ }
+
+ /* Process "f:flags" -- level flags */
+ else if (buf[0] == 'f')
+ {
+ char *s, *t;
+
+ /* Parse every entry textually */
+ for (s = buf + 2; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_dungeon_flag(&dungeon_flags1, &dungeon_flags2, s)) return 1;
+
+ /* Start the next entry */
+ s = t;
+ }
+
+ return 0;
+ }
+
+ /* Process "D:<dungeon>" -- info for the cave grids */
+ else if (buf[0] == 'D')
+ {
+ int x, m_idx = 0;
+
+ object_type object_type_body;
+
+ /* Acquire the text */
+ char *s = buf + 2;
+
+ /* Length of the text */
+ int len = strlen(s);
+
+ int y = *yval;
+ *xval = xvalstart;
+ for (x = *xval, i = 0; ((x < xmax) && (i < len)); x++, s++, i++)
+ {
+ /* Access the grid */
+ cave_type *c_ptr = &cave[y][x];
+
+ int idx = s[0];
+
+ int object_index = letter[idx].object;
+ int monster_index = letter[idx].monster;
+ int random = letter[idx].random;
+ int artifact_index = letter[idx].artifact;
+
+ if (!letter[idx].ok) msg_format("Warning '%c' not defined but used.", idx);
+
+ if (init_flags & INIT_GET_SIZE) continue;
+
+ /* use the plasma generator wilderness */
+ if (((!dun_level) || (!letter[idx].defined)) && (idx == ' ')) continue;
+
+ /* Clear some info */
+ c_ptr->info = 0;
+
+ /* Lay down a floor */
+ c_ptr->mimic = letter[idx].mimic;
+ cave_set_feat(y, x, letter[idx].feature);
+
+ /* Cave info */
+ c_ptr->info |= letter[idx].cave_info;
+
+ /* Create a monster */
+ if (random & RANDOM_MONSTER)
+ {
+ int level = monster_level;
+
+ monster_level = quest[p_ptr->inside_quest].level + monster_index;
+
+ m_idx = place_monster(y, x, meta_sleep, FALSE);
+
+ monster_level = level;
+ }
+ else if (monster_index)
+ {
+ /* Place it */
+ m_allow_special[monster_index] = TRUE;
+ m_idx = place_monster_aux(y, x, monster_index, meta_sleep, FALSE, MSTATUS_ENEMY);
+ m_allow_special[monster_index] = FALSE;
+ }
+
+ /* Set the mflag of the monster */
+ if (m_idx) m_list[m_idx].mflag |= letter[idx].mflag;
+
+ /* Object (and possible trap) */
+ if ((random & RANDOM_OBJECT) && (random & RANDOM_TRAP))
+ {
+ int level = object_level;
+
+ object_level = quest[p_ptr->inside_quest].level;
+
+ /*
+ * Random trap and random treasure defined
+ * 25% chance for trap and 75% chance for object
+ */
+ if (rand_int(100) < 75)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_SPECIAL);
+ }
+ else
+ {
+ place_trap(y, x);
+ }
+
+ object_level = level;
+ }
+ else if (random & RANDOM_OBJECT)
+ {
+ /* Create an out of deep object */
+ if (object_index)
+ {
+ int level = object_level;
+
+ object_level = quest[p_ptr->inside_quest].level + object_index;
+ if (rand_int(100) < 75)
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_SPECIAL);
+ else if (rand_int(100) < 80)
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_SPECIAL);
+ else
+ place_object(y, x, TRUE, TRUE, OBJ_FOUND_SPECIAL);
+
+ object_level = level;
+ }
+ else if (rand_int(100) < 75)
+ {
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_SPECIAL);
+ }
+ else if (rand_int(100) < 80)
+ {
+ place_object(y, x, TRUE, FALSE, OBJ_FOUND_SPECIAL);
+ }
+ else
+ {
+ place_object(y, x, TRUE, TRUE, OBJ_FOUND_SPECIAL);
+ }
+ }
+ /* Random trap */
+ else if (random & RANDOM_TRAP)
+ {
+ place_trap(y, x);
+ }
+ else if (object_index)
+ {
+ /* Get local object */
+ object_type *o_ptr = &object_type_body;
+
+ k_allow_special[object_index] = TRUE;
+
+ /* Create the item */
+ object_prep(o_ptr, object_index);
+
+ /* Apply magic (no messages, no artifacts) */
+ apply_magic(o_ptr, dun_level, FALSE, TRUE, FALSE);
+
+ o_ptr->found = OBJ_FOUND_SPECIAL;
+
+ k_allow_special[object_index] = FALSE;
+
+ drop_near(o_ptr, -1, y, x);
+ }
+
+ /* Artifact */
+ if (artifact_index)
+ {
+ int I_kind = 0;
+
+ artifact_type *a_ptr = &a_info[artifact_index];
+
+ object_type forge;
+
+ /* Get local object */
+ object_type *q_ptr = &forge;
+
+ a_allow_special[artifact_index] = TRUE;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = artifact_index;
+
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+ q_ptr->found = OBJ_FOUND_SPECIAL;
+
+ random_artifact_resistance(q_ptr);
+
+ a_info[artifact_index].cur_num = 1;
+
+ a_allow_special[artifact_index] = FALSE;
+
+ /* It's amazing that this "creating objects anywhere"
+ junk ever worked.
+ Let's just HACK around one observed bug: Shadow Cloak
+ of Luthien [Globe of Light] */
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(q_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f5 & TR5_SPELL_CONTAIN)
+ q_ptr->pval2 = -1;
+ }
+
+ /* Drop the artifact */
+ drop_near(q_ptr, -1, y, x);
+
+ }
+
+ /* Terrain special */
+ if (letter[idx].special == -1)
+ {
+ if (!letter[idx].bx)
+ {
+ letter[idx].bx = x;
+ letter[idx].by = y;
+ }
+ else
+ {
+ c_ptr->special = (letter[idx].by << 8) + letter[idx].bx;
+ cave[letter[idx].by][letter[idx].bx].special = (y << 8) + x;
+ }
+ }
+ else
+ {
+ c_ptr->special = letter[idx].special;
+ }
+ }
+ if (full && (*xval < x)) *xval = x;
+ (*yval)++;
+
+ return (0);
+ }
+
+ /* Process "W:<command>: ..." -- info for the wilderness */
+ else if (buf[0] == 'W')
+ {
+ /* Process "W:D:<layout> */
+ /* Layout of the wilderness */
+ if (buf[2] == 'D')
+ {
+ int x;
+ char i;
+
+ /* Acquire the text */
+ char *s = buf + 4;
+
+ int y = *yval;
+
+ for (x = 0; x < max_wild_x; x++)
+ {
+ if (1 != sscanf(s + x, "%c", &i)) return (1);
+ wild_map[y][x].feat = wildc2i[(int)i];
+
+ /*
+ * If this is a town/dungeon entrance, note
+ * its coordinates. (Have to check for
+ * duplicate Morias...)
+ */
+ if (wf_info[wildc2i[(int)i]].entrance &&
+ wf_info[wildc2i[(int)i]].wild_x == 0)
+ {
+ wf_info[wildc2i[(int)i]].wild_x = x;
+ wf_info[wildc2i[(int)i]].wild_y = y;
+ }
+ }
+
+ (*yval)++;
+
+ return (0);
+ }
+ /* Process "M:<plus>:<line>" -- move line lines */
+ else if (buf[2] == 'M')
+ {
+ if (tokenize(buf + 4, 2, zz, ':', '/') == 2)
+ {
+ if (atoi(zz[0]))
+ {
+ (*yval) += atoi(zz[1]);
+ }
+ else
+ {
+ (*yval) -= atoi(zz[1]);
+ }
+ }
+ else
+ {
+ return (1);
+ }
+ return (0);
+ }
+ /* Process "W:P:<x>:<y> - starting position in the wilderness */
+ else if (buf[2] == 'P')
+ {
+ if ((p_ptr->wilderness_x == 0) &&
+ (p_ptr->wilderness_y == 0))
+ {
+ if (tokenize(buf + 4, 2, zz, ':', '/') == 2)
+ {
+ p_ptr->wilderness_x = atoi(zz[0]);
+ p_ptr->wilderness_y = atoi(zz[1]);
+ }
+ else
+ {
+ return (1);
+ }
+ }
+
+ return (0);
+ }
+ /* Process "W:E:<dungeon>:<y>:<x> - entrance to the dungeon <dungeon> */
+ else if (buf[2] == 'E')
+ {
+ if (tokenize(buf + 4, 3, zz, ':', '/') == 3)
+ {
+ wild_map[atoi(zz[1])][atoi(zz[2])].entrance = 1000 + atoi(zz[0]);
+ }
+ else
+ {
+ return (1);
+ }
+
+ return (0);
+ }
+ }
+
+ /* Process "P:<y>:<x>" -- player position */
+ else if (buf[0] == 'P')
+ {
+ if (init_flags & INIT_CREATE_DUNGEON)
+ {
+ if (tokenize(buf + 2, 2, zz, ':', '/') == 2)
+ {
+ /* Place player in a quest level */
+ if (p_ptr->inside_quest || (init_flags & INIT_POSITION))
+ {
+ p_ptr->py = atoi(zz[0]);
+ p_ptr->px = atoi(zz[1]);
+ }
+ /* Place player in the town */
+ else if ((p_ptr->oldpx == 0) && (p_ptr->oldpy == 0))
+ {
+ p_ptr->oldpy = atoi(zz[0]);
+ p_ptr->oldpx = atoi(zz[1]);
+ }
+ }
+ }
+
+ return (0);
+ }
+
+ /* Process "M:<type>:<maximum>" -- set maximum values */
+ else if (buf[0] == 'M')
+ {
+ if (tokenize(buf + 2, 3, zz, ':', '/') >= 2)
+ {
+ /* Maximum towns */
+ if (zz[0][0] == 'T')
+ {
+ max_towns = atoi(zz[1]);
+ }
+
+ /* Maximum real towns */
+ if (zz[0][0] == 't')
+ {
+ max_real_towns = atoi(zz[1]);
+ }
+
+ /* Maximum r_idx */
+ else if (zz[0][0] == 'R')
+ {
+ max_r_idx = atoi(zz[1]);
+ }
+
+ /* Maximum re_idx */
+ else if (zz[0][0] == 'r')
+ {
+ max_re_idx = atoi(zz[1]);
+ }
+
+ /* Maximum s_idx */
+ else if (zz[0][0] == 'k')
+ {
+ max_s_idx = atoi(zz[1]);
+ if (max_s_idx > MAX_SKILLS) return (1);
+ }
+
+ /* Maximum ab_idx */
+ else if (zz[0][0] == 'b')
+ {
+ max_ab_idx = atoi(zz[1]);
+ }
+
+ /* Maximum k_idx */
+ else if (zz[0][0] == 'K')
+ {
+ max_k_idx = atoi(zz[1]);
+ }
+
+ /* Maximum v_idx */
+ else if (zz[0][0] == 'V')
+ {
+ max_v_idx = atoi(zz[1]);
+ }
+
+ /* Maximum f_idx */
+ else if (zz[0][0] == 'F')
+ {
+ max_f_idx = atoi(zz[1]);
+ }
+
+ /* Maximum a_idx */
+ else if (zz[0][0] == 'A')
+ {
+ max_a_idx = atoi(zz[1]);
+ }
+
+ /* Maximum 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)
+{
+ static char pref_tmp_value[8];
+ 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;
+ }
+
+ /* Race */
+ else if (streq(b + 1, "RACE"))
+ {
+ v = rp_ptr->title;
+ }
+
+ /* Race Mod */
+ else if (streq(b + 1, "RACEMOD"))
+ {
+ v = rmp_ptr->title;
+ }
+
+ /* Class */
+ else if (streq(b + 1, "CLASS"))
+ {
+ v = cp_ptr->title;
+ }
+
+ /* Player */
+ else if (streq(b + 1, "PLAYER"))
+ {
+ v = player_base;
+ }
+
+ /* Town */
+ else if (streq(b + 1, "TOWN"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", p_ptr->town_num);
+ v = pref_tmp_value;
+ }
+
+ /* Town destroyed */
+ else if (prefix(b + 1, "TOWN_DESTROY"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", town_info[atoi(b + 13)].destroyed);
+ v = pref_tmp_value;
+ }
+
+ /* Current quest number */
+ else if (streq(b + 1, "QUEST_NUMBER"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", p_ptr->inside_quest);
+ v = pref_tmp_value;
+ }
+
+ /* Number of last quest */
+ else if (streq(b + 1, "LEAVING_QUEST"))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", leaving_quest);
+ v = pref_tmp_value;
+ }
+
+ /* DAYTIME status */
+ else if (prefix(b + 1, "DAYTIME"))
+ {
+ if ((bst(HOUR, turn) >= 6) && (bst(HOUR, turn) < 18))
+ v = "1";
+ else
+ v = "0";
+ }
+
+ /* Quest status */
+ else if (prefix(b + 1, "QUEST"))
+ {
+ /* "QUEST" uses a special parameter to determine the number of the quest */
+ if (*(b + 6) != '"')
+ strnfmt(pref_tmp_value, 8, "%d", quest[atoi(b + 6)].status);
+ else
+ {
+ char c[80];
+ int i;
+
+ /* Copy it to temp array, so that we can modify it safely */
+ strcpy(c, b + 7);
+
+ /* Hunt & shoot the ending " */
+ for (i = 0; (c[i] != '"') && (c[i] != '\0'); i++);
+ if (c[i] == '"') c[i] = '\0';
+ strcpy(pref_tmp_value, "-1");
+ for (i = 0; i < MAX_Q_IDX; i++)
+ {
+ if (streq(c, quest[i].name))
+ {
+ strnfmt(pref_tmp_value, 8, "%d", quest[i].status);
+ break;
+ }
+ }
+ }
+ v = pref_tmp_value;
+ }
+
+ /* Variant name */
+ else if (streq(b + 1, "VARIANT"))
+ {
+ v = "ToME";
+ }
+
+ /* Wilderness */
+ else if (streq(b + 1, "WILDERNESS"))
+ {
+ v = "NORMAL";
+ }
+ }
+
+ /* Constant */
+ else
+ {
+ v = b;
+ }
+ }
+
+ /* Save */
+ (*fp) = f;
+
+ /* Save */
+ (*sp) = s;
+
+ /* Result */
+ return (v);
+}
+
+
+errr process_dungeon_file(cptr name, int *yval, int *xval, int ymax, int xmax, bool_ init, bool_ full)
+{
+ FILE *fp = 0;
+
+ char buf[1024];
+
+ int num = -1, i;
+
+ errr err = 0;
+
+ bool_ bypass = FALSE;
+
+ /* Save the start since it ought to be modified */
+ int xmin = *xval;
+
+ if (init)
+ {
+ meta_sleep = TRUE;
+ for (i = 0; i < 255; i++)
+ {
+ letter[i].defined = FALSE;
+ if (i == ' ') letter[i].ok = TRUE;
+ else letter[i].ok = FALSE;
+ letter[i].bx = 0;
+ letter[i].by = 0;
+ }
+ }
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_EDIT, name);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* No such file */
+ if (!fp)
+ {
+ msg_format("Cannot find file %s at %s", name, buf);
+ return ( -1);
+ }
+
+ /* Process the file */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Count lines */
+ num++;
+
+
+ /* Skip "empty" lines */
+ if (!buf[0]) continue;
+
+ /* Skip "blank" lines */
+ if (isspace(buf[0])) continue;
+
+ /* Skip comments */
+ if (buf[0] == '#') continue;
+
+
+ /* Process "?:<expr>" */
+ if ((buf[0] == '?') && (buf[1] == ':'))
+ {
+ char f;
+ cptr v;
+ char *s;
+
+ /* Start */
+ s = buf + 2;
+
+ /* Parse the expr */
+ v = process_dungeon_file_expr(&s, &f);
+
+ /* Set flag */
+ bypass = (streq(v, "0") ? TRUE : FALSE);
+
+ /* Continue */
+ continue;
+ }
+
+ /* Apply conditionals */
+ if (bypass) continue;
+
+
+ /* Process "%:<file>" */
+ if (buf[0] == '%')
+ {
+ /* Process that file if allowed */
+ (void)process_dungeon_file(buf + 2, yval, xval, ymax, xmax, FALSE, full);
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Process the line */
+ err = process_dungeon_file_aux(buf, yval, xval, xmin, ymax, xmax, full);
+
+ /* Oops */
+ if (err) break;
+ }
+
+
+ /* Error */
+ if (err)
+ {
+ /* Useful error message */
+ msg_format("Error %d in line %d of file '%s'.", err, num, name);
+ msg_format("Parsing '%s'", buf);
+ }
+
+ /* Close the file */
+ my_fclose(fp);
+
+ /* Result */
+ return (err);
+}
diff --git a/src/init1.hpp b/src/init1.hpp
new file mode 100644
index 00000000..766e467d
--- /dev/null
+++ b/src/init1.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern int color_char_to_attr(char c);
+extern byte conv_color[16];
+extern errr init_player_info_txt(FILE *fp);
+extern errr init_ab_info_txt(FILE *fp);
+extern errr init_s_info_txt(FILE *fp);
+extern errr init_set_info_txt(FILE *fp);
+extern errr init_v_info_txt(FILE *fp);
+extern errr init_f_info_txt(FILE *fp);
+extern errr init_k_info_txt(FILE *fp);
+extern errr init_a_info_txt(FILE *fp);
+extern errr init_ra_info_txt(FILE *fp);
+extern errr init_e_info_txt(FILE *fp);
+extern errr init_r_info_txt(FILE *fp);
+extern errr init_re_info_txt(FILE *fp);
+extern errr init_d_info_txt(FILE *fp);
+extern errr init_t_info_txt(FILE *fp);
+extern errr init_ba_info_txt(FILE *fp);
+extern errr init_st_info_txt(FILE *fp);
+extern errr init_ow_info_txt(FILE *fp);
+extern errr init_wf_info_txt(FILE *fp);
+extern errr grab_one_dungeon_flag(u32b *flags1, u32b *flags2, cptr what);
+extern errr process_dungeon_file(cptr name, int *yval, int *xval, int ymax, int xmax, bool_ init, bool_ full);
diff --git a/src/init2.cc b/src/init2.cc
new file mode 100644
index 00000000..338ebf10
--- /dev/null
+++ b/src/init2.cc
@@ -0,0 +1,1494 @@
+#include "init2.hpp"
+#include "init2.h"
+
+#include "ability_type.hpp"
+#include "alloc_entry.hpp"
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cli_comm.hpp"
+#include "dungeon_info_type.hpp"
+#include "ego_item_type.hpp"
+#include "files.hpp"
+#include "feature_type.hpp"
+#include "generate.hpp"
+#include "gen_evol.hpp"
+#include "gen_maze.hpp"
+#include "hist_type.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "lua_bind.hpp"
+#include "messages.hpp"
+#include "meta_class_type.hpp"
+#include "modules.hpp"
+#include "monster_ego.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object_kind.hpp"
+#include "owner_type.hpp"
+#include "player_class.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "quark.hpp"
+#include "randart.hpp"
+#include "randart_part_type.hpp"
+#include "script.h"
+#include "set_type.hpp"
+#include "skill_type.hpp"
+#include "spells3.hpp"
+#include "squeltch.hpp"
+#include "store_action_type.hpp"
+#include "store_info_type.hpp"
+#include "store_type.hpp"
+#include "tables.hpp"
+#include "trap_type.hpp"
+#include "tome/make_array.hpp"
+#include "town_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "vault_type.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+
+#include <cassert>
+#include <type_traits>
+
+/*
+ * This file is used to initialise various variables and arrays for the
+ * Angband game. Note the use of "fd_read()" and "fd_write()" to bypass
+ * the common limitation of "read()" and "write()" to only 32767 bytes
+ * at a time.
+ *
+ * Several of the arrays for Angband are built from "template" files in
+ * the "lib/file" directory, from which quick-load binary "image" files
+ * are constructed whenever they are not present in the "lib/data"
+ * directory, or if those files become obsolete, if we are allowed.
+ *
+ * Warning -- the "ascii" file parsers use a minor hack to collect the
+ * name and text information in a single pass. Thus, the game will not
+ * be able to load any template file with more than 20K of names or 60K
+ * of text, even though technically, up to 64K should be legal.
+ *
+ * The "init1.c" file is used only to parse the ascii template files.
+ */
+
+
+
+/*
+ * Find the default paths to all of our important sub-directories.
+ *
+ * The purpose of each sub-directory is described in "variable.c".
+ *
+ * All of the sub-directories should, by default, be located inside
+ * the main "lib" directory, whose location is very system dependant.
+ *
+ * This function takes a writable buffer, initially containing the
+ * "path" to the "lib" directory, for example, "/pkg/lib/angband/",
+ * or a system dependant string, for example, ":lib:". The buffer
+ * must be large enough to contain at least 32 more characters.
+ *
+ * Various command line options may allow some of the important
+ * directories to be changed to user-specified directories, most
+ * importantly, the "info" and "user" and "save" directories,
+ * but this is done after this function, see "main.c".
+ *
+ * In general, the initial path should end in the appropriate "PATH_SEP"
+ * string. All of the "sub-directory" paths (created below or supplied
+ * by the user) will NOT end in the "PATH_SEP" string, see the special
+ * "path_build()" function in "util.c" for more information.
+ *
+ * Mega-Hack -- support fat raw files under NEXTSTEP, using special
+ * "suffixed" directories for the "ANGBAND_DIR_DATA" directory, but
+ * requiring the directories to be created by hand by the user.
+ *
+ * Hack -- first we free all the strings, since this is known
+ * to succeed even if the strings have not been allocated yet,
+ * as long as the variables start out as "NULL". This allows
+ * this function to be called multiple times, for example, to
+ * try several base "path" values until a good one is found.
+ */
+void init_file_paths(char *path)
+{
+ char *tail;
+ int pathlen;
+
+ assert(path != nullptr);
+
+ /*** Free everything ***/
+
+ /* Free the main path */
+ free(ANGBAND_DIR);
+
+ /* Free the sub-paths */
+ free(ANGBAND_DIR_CORE);
+ free(ANGBAND_DIR_DNGN);
+ free(ANGBAND_DIR_DATA);
+ free(ANGBAND_DIR_EDIT);
+ free(ANGBAND_DIR_FILE);
+ free(ANGBAND_DIR_HELP);
+ free(ANGBAND_DIR_INFO);
+ free(ANGBAND_DIR_MODULES);
+ free(ANGBAND_DIR_NOTE);
+ free(ANGBAND_DIR_SAVE);
+ free(ANGBAND_DIR_PREF);
+ free(ANGBAND_DIR_USER);
+ free(ANGBAND_DIR_XTRA);
+
+
+ /*** 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 = strdup(path);
+ path[pathlen - seplen] = *PATH_SEP;
+ }
+ else
+ {
+ ANGBAND_DIR = strdup(path);
+ }
+ }
+ else
+ {
+ ANGBAND_DIR = strdup(path);
+ }
+
+ /* Prepare to append to the Base Path */
+ tail = path + pathlen;
+
+
+
+ /*** Build the sub-directory names ***/
+
+ /* Build a path name */
+ strcpy(tail, "core");
+ ANGBAND_DIR_CORE = strdup(path);
+
+ /* Build a path name */
+ strcpy(tail, "dngn");
+ ANGBAND_DIR_DNGN = strdup(path);
+
+ /* Build a path name */
+ strcpy(tail, "data");
+ ANGBAND_DIR_DATA = strdup(path);
+
+ /* Build a path name */
+ strcpy(tail, "edit");
+ ANGBAND_DIR_EDIT = strdup(path);
+
+ /* Build a path name */
+ strcpy(tail, "file");
+ ANGBAND_DIR_FILE = strdup(path);
+
+ /* Build a path name */
+ strcpy(tail, "help");
+ ANGBAND_DIR_HELP = strdup(path);
+
+ /* Build a path name */
+ strcpy(tail, "info");
+ ANGBAND_DIR_INFO = strdup(path);
+
+ /* Build a path name */
+ strcpy(tail, "mods");
+ ANGBAND_DIR_MODULES = strdup(path);
+
+ /* Build a path name */
+ strcpy(tail, "pref");
+ ANGBAND_DIR_PREF = strdup(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 = strdup(user_path);
+ ANGBAND_DIR_NOTE = strdup(user_path);
+
+ /* Savefiles are in user directory */
+ strcat(user_path, "/save");
+ ANGBAND_DIR_SAVE = strdup(user_path);
+ }
+
+ /* Build a path name */
+ strcpy(tail, "xtra");
+ ANGBAND_DIR_XTRA = strdup(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.
+ */
+void init_file_paths_with_env()
+{
+ char path[1024];
+
+ cptr tail;
+
+ /* Get the environment variable */
+ tail = getenv("TOME_PATH");
+
+ /* Use the angband_path, or a default */
+ strcpy(path, tail ? tail : DEFAULT_PATH);
+
+ /* Hack -- Add a path separator (only if needed) */
+ if (!suffix(path, PATH_SEP)) strcat(path, PATH_SEP);
+
+ /* Initialize */
+ init_file_paths(path);
+}
+
+
+/*
+ * Hack -- help give useful error messages
+ */
+s16b error_idx;
+s16b error_line;
+
+
+/*
+ * Standard error message text
+ */
+static cptr err_str[9] =
+{
+ NULL,
+ "parse error",
+ "obsolete file",
+ "missing record header",
+ "non-sequential records",
+ "invalid flag specification",
+ "undefined directive",
+ "out of memory",
+ "invalid skill chart"
+};
+
+
+/*
+ * Hack -- take notes on line 23
+ */
+static void note(cptr str)
+{
+ Term_erase(0, 23, 255);
+ Term_putstr(20, 23, -1, TERM_WHITE, str);
+ Term_fresh();
+}
+
+
+/*
+ * Traits for data arrays
+ */
+namespace {
+
+ struct f_info_traits {
+
+ static constexpr char const *name = "f_info.txt";
+
+ static void allocate()
+ {
+ f_info = make_array<feature_type>(max_f_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_f_info_txt(fp);
+ }
+
+ };
+
+ struct k_info_traits {
+
+ static constexpr char const *name = "k_info.txt";
+
+ static void allocate()
+ {
+ k_info = make_array<object_kind>(max_k_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_k_info_txt(fp);
+ };
+
+ };
+
+ struct set_info_traits {
+
+ static constexpr char const *name = "set_info.txt";
+
+ static void allocate()
+ {
+ set_info = make_array<set_type>(max_set_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_set_info_txt(fp);
+ }
+
+ };
+
+ struct a_info_traits {
+
+ static constexpr char const *name = "a_info.txt";
+
+ static void allocate()
+ {
+ a_info = make_array<artifact_type>(max_a_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_a_info_txt(fp);
+ }
+
+ };
+
+ struct s_info_traits {
+
+ static constexpr char const *name = "s_info.txt";
+
+ static void allocate()
+ {
+ s_info = make_array<skill_type>(max_s_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_s_info_txt(fp);
+ }
+
+ };
+
+ struct ab_info_traits {
+
+ static constexpr char const *name = "ab_info.txt";
+
+ static void allocate()
+ {
+ ab_info = make_array<ability_type>(max_ab_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_ab_info_txt(fp);
+ }
+
+ };
+
+ struct e_info_traits {
+
+ static constexpr char const *name = "e_info.txt";
+
+ static void allocate()
+ {
+ e_info = make_array<ego_item_type>(max_e_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_e_info_txt(fp);
+ }
+
+ };
+
+ struct ra_info_traits {
+
+ static constexpr char const *name = "ra_info.txt";
+
+ static void allocate()
+ {
+ ra_info = make_array<randart_part_type>(max_ra_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_ra_info_txt(fp);
+ }
+
+ };
+
+ struct r_info_traits {
+
+ static constexpr char const *name = "r_info.txt";
+
+ static void allocate()
+ {
+ r_info = make_array<monster_race>(max_r_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_r_info_txt(fp);
+ }
+
+ };
+
+ struct re_info_traits {
+
+ static constexpr char const *name = "re_info.txt";
+
+ static void allocate()
+ {
+ re_info = make_array<monster_ego>(max_re_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_re_info_txt(fp);
+ }
+
+ };
+
+ struct d_info_traits {
+
+ static constexpr char const *name = "d_info.txt";
+
+ static void allocate()
+ {
+ d_info = make_array<dungeon_info_type>(max_d_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_d_info_txt(fp);
+ }
+
+ };
+
+ struct st_info_traits {
+
+ static constexpr char const *name = "st_info.txt";
+
+ static void allocate()
+ {
+ st_info = make_array<store_info_type>(max_st_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_st_info_txt(fp);
+ }
+
+ };
+
+ struct ow_info_traits {
+
+ static constexpr char const *name = "ow_info.txt";
+
+ static void allocate()
+ {
+ ow_info = make_array<owner_type>(max_ow_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_ow_info_txt(fp);
+ }
+
+ };
+
+ struct ba_info_traits {
+
+ static constexpr char const *name = "ba_info.txt";
+
+ static void allocate()
+ {
+ ba_info = make_array<store_action_type>(max_ba_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_ba_info_txt(fp);
+ }
+
+ };
+
+ struct wf_info_traits {
+
+ static constexpr char const *name = "wf_info.txt";
+
+ static void allocate()
+ {
+ wf_info = make_array<wilderness_type_info>(max_wf_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_wf_info_txt(fp);
+ }
+
+ };
+
+ struct tr_info_traits {
+
+ static constexpr char const *name = "tr_info.txt";
+
+ static void allocate()
+ {
+ t_info = make_array<trap_type>(max_t_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_t_info_txt(fp);
+ }
+
+ };
+
+ struct v_info_traits {
+
+ static constexpr char const *name = "v_info.txt";
+
+ static void allocate()
+ {
+ v_info = make_array<vault_type>(max_v_idx);
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_v_info_txt(fp);
+ }
+
+ };
+
+ struct p_info_traits {
+
+ static constexpr char const *name = "p_info.txt";
+
+ static void allocate()
+ {
+ race_info = make_array<player_race>(max_rp_idx);
+ race_mod_info = make_array<player_race_mod>(max_rmp_idx);
+ class_info = make_array<player_class>(max_c_idx);
+ bg = make_array<hist_type>(max_bg_idx);
+ meta_class_info = make_array<meta_class_type>(max_mc_idx);
+ for (std::size_t i = 0; i < max_mc_idx; i++)
+ {
+ meta_class_info[i].classes = make_array<s16b>(max_c_idx);
+ }
+ }
+
+ static errr parse(FILE *fp)
+ {
+ return init_player_info_txt(fp);
+ }
+
+ };
+
+}
+
+template<typename T> static errr init_x_info() {
+
+ /* Allocate the data array */
+ T::allocate();
+
+ /* Build the filename */
+ boost::filesystem::path path(ANGBAND_DIR_EDIT);
+ path /= T::name;
+
+ /* Open the file */
+ FILE *fp = my_fopen(path.c_str(), "r");
+
+ /* Parse it */
+ if (!fp)
+ {
+ quit_fmt("Cannot open '%s' file.", T::name);
+ }
+
+ /* Parse the file */
+ errr err = T::parse(fp);
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Errors */
+ if (err)
+ {
+ /* Error string */
+ cptr oops = (((err > 0) && (err < 8)) ? err_str[err] : "unknown");
+
+ /* Oops */
+ msg_format("Error %d at line %d of '%s'.", err, error_line, T::name);
+ msg_format("Record %d contains a '%s' error.", error_idx, oops);
+ msg_print(NULL);
+
+ /* Quit */
+ quit_fmt("Error in '%s' file.", T::name);
+ }
+
+ /* Success */
+ return (0);
+}
+
+errr init_v_info()
+{
+ return init_x_info<v_info_traits>();
+}
+
+/*
+ * Initialize the very basic arrays
+ */
+static void init_basic()
+{
+ /* Macro variables */
+ macro__pat = make_array<char *>(MACRO_MAX);
+ macro__act = make_array<char *>(MACRO_MAX);
+ macro__cmd = make_array<bool_>(MACRO_MAX);
+
+ /* Macro action buffer */
+ macro__buf = make_array<char>(1024);
+
+ /* Extended trigger macros */
+ cli_info = make_array<cli_comm>(CLI_MAX);
+}
+
+
+/*
+ * Initialise misc. values
+ */
+static errr init_misc(void)
+{
+ int xstart = 0;
+ int ystart = 0;
+ int i;
+
+ /*** Prepare the various "bizarre" arrays ***/
+
+ /* Initialize quark subsystem */
+ quark_init();
+
+ /* Initialize messages subsystem */
+ message_init();
+
+ /* Initialise the values */
+ process_dungeon_file("misc.txt", &ystart, &xstart, 0, 0, TRUE, FALSE);
+
+ /* Init the spell effects */
+ for (i = 0; i < MAX_EFFECTS; i++)
+ effects[i].time = 0;
+
+ /* Initialize timers */
+ TIMER_INERTIA_CONTROL =
+ new_timer(meta_inertia_control_timer_callback,
+ 10);
+ TIMER_AGGRAVATE_EVIL =
+ new_timer(timer_aggravate_evil_callback,
+ 10);
+
+ return 0;
+}
+
+
+/*
+ * Initialise town array
+ */
+static errr init_towns(void)
+{
+ int i = 0, j = 0;
+
+ /*** Prepare the Towns ***/
+
+ /* Allocate the towns */
+ town_info = make_array<town_type>(max_towns);
+
+ for (i = 1; i < max_towns; i++)
+ {
+ if (i <= max_real_towns) town_info[i].flags |= (TOWN_REAL);
+
+ /* Allocate the stores */
+ town_info[i].store = make_array<store_type>(max_st_idx);
+
+ /* 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 */
+ st_ptr->stock = make_array<object_type>(st_ptr->stock_size);
+ }
+ t_ptr->stocked = TRUE;
+}
+
+/*
+ * Initialise wilderness map array
+ */
+static errr init_wilderness(void)
+{
+ int i;
+
+ /* Allocate the wilderness (two-dimension array) */
+ wild_map = make_array<wilderness_map *>(max_wild_y);
+
+ /* Init the other pointers */
+ for (i = 0; i < max_wild_y; i++)
+ {
+ wild_map[i] = make_array<wilderness_map>(max_wild_x);
+ }
+
+ /* No encounter right now */
+ generate_encounter = FALSE;
+
+ return 0;
+}
+
+/*
+ * Initialise some other arrays
+ */
+static errr init_other(void)
+{
+ int i, n;
+
+ /*** Prepare the "dungeon" information ***/
+
+ /* Allocate and Wipe the special gene flags */
+ m_allow_special = make_array<bool_>(max_r_idx);
+ k_allow_special = make_array<bool_>(max_k_idx);
+ a_allow_special = make_array<bool_>(max_a_idx);
+
+
+ /*** Prepare "vinfo" array ***/
+
+ /* Used by "update_view()" */
+ (void)vinfo_init();
+
+
+ /* Allocate and Wipe the object list */
+ o_list = make_array<object_type>(max_o_idx);
+
+ /* Allocate and Wipe the monster list */
+ m_list = new monster_type[max_m_idx];
+
+ /* Allocate and Wipe the to keep monster list */
+ km_list = new monster_type[max_m_idx];
+
+ /* Allocate and Wipe the max dungeon level */
+ max_dlv = make_array<s16b>(max_d_idx);
+
+ /* Allocate and Wipe the special levels */
+ for (i = 0; i < MAX_DUNGEON_DEPTH; i++)
+ {
+ special_lvl[i] = make_array<bool_>(max_d_idx);
+ }
+
+ /* Allocate and wipe each line of the cave */
+ cave = new cave_type *[MAX_HGT];
+ for (i = 0; i < MAX_HGT; i++)
+ {
+ /* Allocate one row of the cave */
+ cave[i] = new cave_type[MAX_WID];
+ }
+
+ /*** 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);
+ add_level_generator("maze", level_generate_maze);
+ add_level_generator("life", level_generate_life);
+
+ /*** Pre-allocate space for the "format()" buffer ***/
+
+ /* Hack -- Just call the "format()" function */
+ (void)format("%s (%s).", "Dark God <darkgod@t-o-m-e.net>", MAINTAINER);
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Initialise some other arrays
+ */
+static errr init_alloc(void)
+{
+ int i, j;
+
+ object_kind *k_ptr;
+
+ monster_race *r_ptr;
+
+ alloc_entry *table;
+
+ s16b num[MAX_DEPTH_MONSTER];
+
+ s16b aux[MAX_DEPTH_MONSTER];
+
+ /*** Analyze object allocation info ***/
+
+ /* Clear the "aux" array */
+ memset(aux, 0, MAX_DEPTH_MONSTER * sizeof(s16b));
+
+ /* Clear the "num" array */
+ memset(num, 0, MAX_DEPTH_MONSTER * sizeof(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 < ALLOCATION_MAX; 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 */
+ alloc_kind_table = make_array<alloc_entry>(alloc_kind_size);
+
+ /* 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 < ALLOCATION_MAX; 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 */
+ memset(aux, 0, MAX_DEPTH_MONSTER * sizeof(s16b));
+
+ /* Clear the "num" array */
+ memset(num, 0, MAX_DEPTH_MONSTER * sizeof(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 */
+ alloc_race_table = make_array<alloc_entry>(alloc_race_size);
+
+ /* 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 */
+static 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;
+
+ const char *news_file;
+
+ char buf[1024];
+
+ /* Init some VERY basic stuff, like macro arrays */
+ init_basic();
+
+ /* Select & init a module if needed */
+ select_module();
+
+ /*** Choose which news.txt file to use ***/
+
+ /* Choose the news file */
+ switch (time(NULL) % 2)
+ {
+ default:
+ {
+ news_file = "news.txt";
+ break;
+ }
+
+ case 0:
+ {
+ news_file = "news2.txt";
+ break;
+ }
+ }
+
+ /*** Verify the "news" file ***/
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, news_file);
+
+ /* Attempt to open the file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Failure */
+ if (fd < 0)
+ {
+ char why[1024];
+
+ /* Message */
+ sprintf(why, "Cannot access the '%s' file!", buf);
+
+ /* Crash and burn */
+ init_angband_aux(why);
+ }
+
+ /* Close it */
+ (void)fd_close(fd);
+
+
+ /*** Display the "news" file ***/
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_FILE, news_file);
+
+ /* Open the News file */
+ fp = my_fopen(buf, "r");
+
+ /* Dump */
+ if (fp)
+ {
+ int i = 0;
+
+ /* Dump the file to the screen */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Display and advance - we use display_message to parse colour codes XXX */
+ display_message(0, i++, strlen(buf), TERM_WHITE, buf);
+ }
+
+ /* Close */
+ my_fclose(fp);
+ }
+
+ /* Flush it */
+ Term_fresh();
+
+
+ /*** Verify (or create) the "high score" file ***/
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_USER, "scores.raw");
+
+ /* Attempt to open the high score file */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* Failure */
+ if (fd < 0)
+ {
+ /* Create a new high score file */
+ fd = fd_make(buf, mode);
+
+ /* Failure */
+ if (fd < 0)
+ {
+ char why[1024];
+
+ /* Message */
+ sprintf(why, "Cannot create the '%s' file!", buf);
+
+ /* Crash and burn */
+ init_angband_aux(why);
+ }
+ }
+
+ /* Close it */
+ (void)fd_close(fd);
+
+
+ /*** Initialise some arrays ***/
+
+ /* Initialise misc. values */
+ note("[Initialising values... (misc)]");
+ if (init_misc()) quit("Cannot initialise misc. values");
+
+ /* Initialise some other arrays */
+ note("[Initialising scripting... (script)]");
+ init_lua_init();
+
+ /* Initialise skills info */
+ note("[Initialising arrays... (skills)]");
+ if (init_x_info<s_info_traits>()) quit("Cannot initialise skills");
+
+ /* Initialise abilities info */
+ note("[Initialising arrays... (abilities)]");
+ if (init_x_info<ab_info_traits>()) quit("Cannot initialise abilities");
+
+ /* Initialise player info */
+ note("[Initialising arrays... (players)]");
+ if (init_x_info<p_info_traits>()) quit("Cannot initialise players");
+
+ /* Initialise feature info */
+ note("[Initialising arrays... (features)]");
+ if (init_x_info<f_info_traits>()) quit("Cannot initialise features");
+
+ /* Initialise object info */
+ note("[Initialising arrays... (objects)]");
+ if (init_x_info<k_info_traits>()) quit("Cannot initialise objects");
+
+ /* Initialise artifact info */
+ note("[Initialising arrays... (artifacts)]");
+ if (init_x_info<a_info_traits>()) quit("Cannot initialise artifacts");
+
+ /* Initialise set info */
+ note("[Initialising item sets... (sets)]");
+ if (init_x_info<set_info_traits>()) quit("Cannot initialise item sets");
+ init_sets_aux();
+
+ /* Initialise ego-item info */
+ note("[Initialising arrays... (ego-items)]");
+ if (init_x_info<e_info_traits>()) quit("Cannot initialise ego-items");
+
+ /* Initialise randart parts info */
+ note("[Initialising arrays... (randarts)]");
+ if (init_x_info<ra_info_traits>()) quit("Cannot initialise randarts");
+
+ /* Initialise monster info */
+ note("[Initialising arrays... (monsters)]");
+ if (init_x_info<r_info_traits>()) quit("Cannot initialise monsters");
+
+ /* Initialise ego monster info */
+ note("[Initialising arrays... (ego monsters)]");
+ if (init_x_info<re_info_traits>()) quit("Cannot initialise ego monsters");
+
+ /* Initialise dungeon type info */
+ note("[Initialising arrays... (dungeon types)]");
+ if (init_x_info<d_info_traits>()) quit("Cannot initialise dungeon types");
+ init_guardians();
+
+ /* Initialise actions type info */
+ note("[Initialising arrays... (action types)]");
+ if (init_x_info<ba_info_traits>()) quit("Cannot initialise action types");
+
+ /* Initialise owners type info */
+ note("[Initialising arrays... (owners types)]");
+ if (init_x_info<ow_info_traits>()) quit("Cannot initialise owners types");
+
+ /* Initialise stores type info */
+ note("[Initialising arrays... (stores types)]");
+ if (init_x_info<st_info_traits>()) quit("Cannot initialise stores types");
+
+ /* Initialise wilderness features array */
+ note("[Initialising arrays... (wilderness features)]");
+ if (init_x_info<wf_info_traits>()) 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_x_info<tr_info_traits>()) 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);
+
+ /* Initialize the automatizer */
+ automatizer_init();
+
+ /*** 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);
+
+ /* Done */
+ note("[Initialisation complete]");
+}
diff --git a/src/init2.h b/src/init2.h
new file mode 100644
index 00000000..5697e4ef
--- /dev/null
+++ b/src/init2.h
@@ -0,0 +1,14 @@
+#pragma once
+
+// C linkage required for these functions since main-* code uses them.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void init_file_paths(char *path);
+extern void init_file_paths_with_env();
+extern void init_angband(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/init2.hpp b/src/init2.hpp
new file mode 100644
index 00000000..707a2706
--- /dev/null
+++ b/src/init2.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern void init_corruptions();
+extern void create_stores_stock(int t);
+extern errr init_v_info(void);
+extern s16b error_idx;
+extern s16b error_line;
diff --git a/src/inscription_info_type.hpp b/src/inscription_info_type.hpp
new file mode 100644
index 00000000..6dbb67f1
--- /dev/null
+++ b/src/inscription_info_type.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Inscriptions
+ */
+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 */
+};
diff --git a/src/inventory.hpp b/src/inventory.hpp
new file mode 100644
index 00000000..775f7a7e
--- /dev/null
+++ b/src/inventory.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+/*
+ * 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
+
+/*
+ * 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)
diff --git a/src/joke.cc b/src/joke.cc
new file mode 100644
index 00000000..be272115
--- /dev/null
+++ b/src/joke.cc
@@ -0,0 +1,40 @@
+#include "joke.hpp"
+
+#include "monster2.hpp"
+#include "options.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+static void gen_joke_place_monster(int r_idx)
+{
+ int try_;
+
+ for (try_ = 0; try_ < 1000; try_++)
+ {
+ int x = randint(cur_hgt - 4) + 2;
+ int y = randint(cur_wid - 4) + 2;
+
+ if (place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY))
+ {
+ return;
+ }
+ }
+}
+
+bool_ gen_joke_monsters(void *data, void *in, void *out)
+{
+ if (joke_monsters)
+ {
+ if ((dungeon_type == 20) &&
+ (dun_level == 72))
+ {
+ int r_idx = test_monster_name("Neil, the Sorceror");
+ m_allow_special[r_idx] = TRUE;
+ gen_joke_place_monster(r_idx);
+ m_allow_special[r_idx] = FALSE;
+ }
+ }
+
+ return FALSE;
+}
diff --git a/src/joke.hpp b/src/joke.hpp
new file mode 100644
index 00000000..05ac1843
--- /dev/null
+++ b/src/joke.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern bool_ gen_joke_monsters(void *data, void *in, void *out);
diff --git a/src/levels.cc b/src/levels.cc
new file mode 100644
index 00000000..ac3aa3d3
--- /dev/null
+++ b/src/levels.cc
@@ -0,0 +1,237 @@
+/*
+ * 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 "levels.hpp"
+
+#include "dungeon_info_type.hpp"
+#include "init1.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+
+
+/*
+ * Return the parameter of the given command in the given file
+ */
+static int start_line = 0;
+static bool_ get_command(const char *file, char comm, char *param)
+{
+ char buf[1024];
+ int i = -1;
+ FILE *fp;
+ char *s;
+
+ /* Build the filename */
+ path_build(buf, 1024, ANGBAND_DIR_DNGN, file);
+
+ /* Open the file */
+ fp = my_fopen(buf, "r");
+
+ /* The file exists ? */
+ /* no ? then command not found */
+ if (!fp) return FALSE;
+
+ /* Parse to the end of the file or when the command is found */
+ while (0 == my_fgets(fp, buf, 1024))
+ {
+ /* Advance the line number */
+ i++;
+
+ /* Skip comments and blank lines */
+ if (!buf[0] || (buf[0] == '#')) continue;
+
+ /* Is it the command we are looking for ? */
+ if ((i > start_line) && (buf[0] == comm))
+ {
+ /* Acquire the text */
+ s = buf + 2;
+
+ start_line = i;
+
+ /* Get the parameter */
+ strcpy(param, s);
+
+ /* Close it */
+ my_fclose(fp);
+
+ return TRUE;
+ }
+
+ }
+
+ /* Close it */
+ my_fclose(fp);
+
+ /* Assume command not found */
+ return FALSE;
+}
+
+
+/*
+ * Return the dungeon branch starting form the current dungeon/level
+ */
+int get_branch()
+{
+ char file[20], buf[5];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the branch */
+ start_line = -1;
+ if (get_command(file, 'B', buf)) return (atoi(buf));
+
+ /* No branch ? return 0 */
+ else return 0;
+}
+
+/*
+ * Return the father dungeon branch
+ */
+int get_fbranch()
+{
+ char file[20], buf[5];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the branch */
+ start_line = -1;
+ if (get_command(file, 'A', buf)) return (atoi(buf));
+
+ /* No branch ? return 0 */
+ else return 0;
+}
+
+/*
+ * Return the father dungeon level
+ */
+int get_flevel()
+{
+ char file[20], buf[5];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'L', buf)) return (atoi(buf));
+
+ /* No level ? return 0 */
+ else return 0;
+}
+
+/*
+ * Return the extension of the savefile for the level
+ */
+bool_ get_dungeon_save(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'S', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the level generator
+ */
+bool_ get_dungeon_generator(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'G', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the special level
+ */
+bool_ get_dungeon_special(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'U', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the special level name
+ */
+bool_ get_dungeon_name(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'N', buf)) return (TRUE);
+ else return FALSE;
+}
+
+/*
+ * Return the special level name
+ */
+void get_level_flags()
+{
+ char file[20];
+ char buf[1024], *s, *t;
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ start_line = -1;
+
+ /* Parse until done */
+ while (get_command(file, 'F', buf))
+ {
+ /* Parse every entry textually */
+ for (s = buf; *s; )
+ {
+ /* Find the end of this entry */
+ for (t = s; *t && (*t != ' ') && (*t != '|'); ++t) /* loop */;
+
+ /* Nuke and skip any dividers */
+ if (*t)
+ {
+ *t++ = '\0';
+ while (*t == ' ' || *t == '|') t++;
+ }
+
+ /* Parse this entry */
+ if (0 != grab_one_dungeon_flag(&dungeon_flags1, &dungeon_flags2, s)) return;
+
+ /* Start the next entry */
+ s = t;
+ }
+ }
+}
+
+/*
+ * Return the special level desc
+ */
+bool_ get_level_desc(char *buf)
+{
+ char file[20];
+
+ sprintf(file, "dun%d.%d", dungeon_type, dun_level - d_info[dungeon_type].mindepth);
+
+ /* Get and return the level */
+ start_line = -1;
+ if (get_command(file, 'D', buf)) return (TRUE);
+ else return FALSE;
+}
diff --git a/src/levels.hpp b/src/levels.hpp
new file mode 100644
index 00000000..187092b1
--- /dev/null
+++ b/src/levels.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "h-basic.h"
+
+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 int get_branch(void);
+extern int get_fbranch(void);
+extern int get_flevel(void);
+extern bool_ get_dungeon_save(char *buf);
diff --git a/src/loadsave.cc b/src/loadsave.cc
new file mode 100644
index 00000000..1806d7c8
--- /dev/null
+++ b/src/loadsave.cc
@@ -0,0 +1,2975 @@
+#include "loadsave.hpp"
+#include "loadsave.h"
+
+#include "ability_type.hpp"
+#include "artifact_type.hpp"
+#include "birth.hpp"
+#include "cave_type.hpp"
+#include "dungeon_info_type.hpp"
+#include "ego_item_type.hpp"
+#include "init1.hpp"
+#include "init2.hpp"
+#include "levels.hpp"
+#include "messages.hpp"
+#include "modules.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "player_class.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "hooks.hpp"
+#include "skill_type.hpp"
+#include "store_type.hpp"
+#include "tables.hpp"
+#include "timer_type.hpp"
+#include "town_type.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "wilderness_map.hpp"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+#include <memory>
+
+static u32b vernum; /* Version flag */
+static FILE *fff; /* Local savefile ptr */
+
+/**
+ * Load/save flag
+ */
+enum class ls_flag_t {
+ LOAD = 3,
+ SAVE = 7
+};
+
+/*
+ * Basic byte-level reading from savefile. This provides a single point
+ * of interface to the pseudoencryption that ToME (and Angband)
+ * uses. I'm thinking about if it might be faster/better to modify all
+ * the do_* functions to directly do this stuff -- it'd make the code
+ * somewhat uglier to maintain, but concievably might be much faster. Or
+ * is it better maybe to scrap the pseudoencryption entirely and adopt
+ * some other means of obfuscation, should it still prove useful in any
+ * way? -- Improv
+ *
+ * What's the point of encryption on savefiles anyway? If I wanted to
+ * make a cheater savefile, I'd activate debug mode, and hack the game
+ * not to save it. There's no point. -- takkaria
+ */
+
+static byte sf_get(void)
+{
+ byte c;
+
+ /* Get a character, decode the value */
+ c = getc(fff) & 0xFF;
+
+ /* Return the value */
+ return (c);
+}
+
+
+static void sf_put(byte v)
+{
+ (void)putc((int)v, fff);
+}
+
+/*
+ * Size-aware read/write routines for the savefile, do all their
+ * work through sf_get and sf_put.
+ */
+static void do_byte(byte *v, ls_flag_t flag)
+{
+ switch (flag)
+ {
+ case ls_flag_t::LOAD:
+ {
+ *v = sf_get();
+ return;
+ }
+ case ls_flag_t::SAVE:
+ {
+ byte val = *v;
+ sf_put(val);
+ return;
+ }
+ }
+}
+
+static void do_bool(bool_ *f, ls_flag_t flag)
+{
+ byte b = *f;
+ do_byte(&b, flag);
+ if (flag == ls_flag_t::LOAD)
+ {
+ *f = b;
+ }
+}
+
+static void do_u16b(u16b *v, ls_flag_t flag)
+{
+ switch (flag)
+ {
+ case ls_flag_t::LOAD:
+ {
+ (*v) = sf_get();
+ (*v) |= ((u16b)(sf_get()) << 8);
+ return;
+ }
+ case ls_flag_t::SAVE:
+ {
+ u16b val;
+ val = *v;
+ sf_put((byte)(val & 0xFF));
+ sf_put((byte)((val >> 8) & 0xFF));
+ return;
+ }
+ }
+}
+
+static void do_s16b(s16b *ip, ls_flag_t flag)
+{
+ do_u16b((u16b *)ip, flag);
+}
+
+static void do_u32b(u32b *ip, ls_flag_t flag)
+{
+ switch(flag)
+ {
+ case ls_flag_t::LOAD:
+ {
+ (*ip) = sf_get();
+ (*ip) |= ((u32b)(sf_get()) << 8);
+ (*ip) |= ((u32b)(sf_get()) << 16);
+ (*ip) |= ((u32b)(sf_get()) << 24);
+ return;
+ }
+ case ls_flag_t::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;
+ }
+ }
+}
+
+static void do_s32b(s32b *ip, ls_flag_t flag)
+{
+ do_u32b((u32b *)ip, flag);
+}
+
+/*
+ * Do object memory and similar stuff
+ */
+static void do_xtra(int k_idx, ls_flag_t flag)
+{
+ byte tmp8u = 0;
+ object_kind *k_ptr = &k_info[k_idx];
+
+ switch(flag)
+ {
+ case ls_flag_t::SAVE:
+ {
+ if (k_ptr->aware) tmp8u |= 0x01;
+ if (k_ptr->tried) tmp8u |= 0x02;
+ if (k_ptr->artifact) tmp8u |= 0x80;
+
+ do_byte(&tmp8u, flag);
+ return;
+ }
+ case ls_flag_t::LOAD:
+ {
+ do_byte(&tmp8u, flag);
+ k_ptr->aware = ((tmp8u & 0x01) ? TRUE : FALSE);
+ k_ptr->tried = ((tmp8u & 0x02) ? TRUE : FALSE);
+ k_ptr->artifact = ((tmp8u & 0x80) ? TRUE : FALSE);
+ return;
+ }
+ }
+}
+
+static void save_string(const char *str)
+{
+ while (*str)
+ {
+ do_byte((byte*)str, ls_flag_t::SAVE);
+ str++;
+ }
+ do_byte((byte*)str, ls_flag_t::SAVE);
+}
+
+static void load_string(char *str, int max)
+{
+ int i;
+
+ /* Read the string */
+ for (i = 0; TRUE; i++)
+ {
+ byte tmp8u;
+
+ /* Read a byte */
+ do_byte(&tmp8u, ls_flag_t::LOAD);
+
+ /* Collect string while legal */
+ if (i < max) str[i] = tmp8u;
+
+ /* End of string */
+ if (!tmp8u) break;
+ }
+ /* Terminate */
+ str[max - 1] = '\0';
+}
+
+static void do_string(char *str, int max, ls_flag_t flag)
+/* Max is ignored for writing */
+{
+ switch(flag) {
+ case ls_flag_t::LOAD:
+ {
+ load_string(str, max);
+ return;
+ }
+ case ls_flag_t::SAVE:
+ {
+ save_string(str);
+ return;
+ }
+ }
+}
+
+/*
+ * Load/Save quick start data
+ */
+static void do_quick_start(ls_flag_t flag)
+{
+ s16b tmp16s;
+ u32b tmp32u;
+ 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(&tmp16s, flag);
+ do_u32b(&tmp32u, 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(ls_flag_t flag)
+{
+ player_race_mod *sr_ptr = &race_mod_info[SUBRACE_SAVE];
+ int i;
+ char buf[81];
+
+ buf[80] = '\0'; // Make sure string is always NUL terminated
+
+ if (flag == ls_flag_t::SAVE)
+ {
+ strncpy(buf, sr_ptr->title, 80);
+ }
+ do_string(buf, 80, flag);
+ if (flag == ls_flag_t::LOAD)
+ {
+ set_subrace_title(sr_ptr, buf);
+ }
+
+ if (flag == ls_flag_t::SAVE)
+ {
+ strncpy(buf, sr_ptr->desc, 80);
+ }
+ do_string(buf, 80, flag);
+ if (flag == ls_flag_t::LOAD)
+ {
+ set_subrace_description(sr_ptr, 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);
+ }
+}
+
+
+/* Load/Save the random spells info */
+static void do_spells(int i, ls_flag_t 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);
+}
+
+
+/*
+ * 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();
+}
+
+
+static void skip_ver_byte(u32b version, ls_flag_t flag)
+/* Reads and discards a byte if the savefile is as old as/older than version */
+{
+ if ((flag == ls_flag_t::LOAD) && (vernum <= version))
+ {
+ byte forget;
+ do_byte(&forget, flag);
+ }
+ return;
+}
+
+static void do_ver_s16b(s16b *v, u32b version, s16b defval, ls_flag_t flag)
+{
+ if ((flag == ls_flag_t::LOAD) && (vernum < version))
+ {
+ *v = defval;
+ return;
+ }
+ do_s16b(v, flag);
+}
+
+/*
+ * Misc. other data
+ */
+static char loaded_game_module[80];
+static bool_ do_extra(ls_flag_t flag)
+{
+ int i, j;
+ byte tmp8u = 0;
+ s16b tmp16s = 0;
+ u32b tmp32u = 0;
+ s32b tmp32s = 0;
+ u16b tmp16b = 0;
+ u32b dummy32u = 0;
+
+ do_string(player_name, 32, flag);
+
+ do_string(died_from, 80, flag);
+
+ for (i = 0; i < 4; i++)
+ {
+ do_string(history[i], 60, flag);
+ }
+
+ /* Handle the special levels info */
+ if (flag == ls_flag_t::SAVE)
+ {
+ tmp8u = max_d_idx;
+ tmp16s = MAX_DUNGEON_DEPTH;
+ }
+ do_byte(&tmp8u, flag);
+
+ if (flag == ls_flag_t::LOAD)
+ {
+ if (tmp8u > max_d_idx)
+ {
+ note(format("Too many (%d) dungeon types!", tmp8u));
+ }
+ }
+
+ do_s16b(&tmp16s, flag);
+
+ if (flag == ls_flag_t::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_flag_t::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_flag_t::LOAD) && (tmp16s > MAX_SKILLS))
+ {
+ quit("Too many skills");
+ }
+
+ if (flag == ls_flag_t::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_flag_t::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);
+
+ 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(&tmp16s, flag);
+ do_s16b(&tmp16s, flag);
+ do_s16b(&p_ptr->inside_quest, flag);
+ do_byte(&tmp8u, 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_flag_t::SAVE) tmp8u = MAX_PLOTS;
+ do_byte(&tmp8u, flag);
+
+ if ((flag == ls_flag_t::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_flag_t::SAVE)
+ {
+ tmp8u = MAX_RANDOM_QUEST;
+ }
+ do_byte(&tmp8u, flag);
+
+ if ((flag == ls_flag_t::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_s32b(&p_ptr->grace_delay, 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_flag_t::SAVE)
+ tmp8u = max_d_idx;
+ do_byte(&tmp8u, flag);
+ for (i = 0; i < tmp8u; i++)
+ {
+ if (flag == ls_flag_t::SAVE)
+ tmp16s = max_dlv[i];
+ do_s16b(&tmp16s, flag);
+ if ((flag == ls_flag_t::LOAD) && (i <= max_d_idx))
+ max_dlv[i] = tmp16s;
+ }
+ /* Repair max player level??? */
+ if ((flag == ls_flag_t::LOAD) && (p_ptr->max_plv < p_ptr->lev))
+ p_ptr->max_plv = p_ptr->lev;
+
+ do_byte((byte*)&(p_ptr->help.enabled), flag);
+ for (i = 0; i < HELP_MAX; i++)
+ {
+ do_bool(&(p_ptr->help.activated[i]), 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_precognition, 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(&tmp16s, flag);
+ do_ver_s16b(&p_ptr->tim_poison, SAVEFILE_VERSION, 0, flag);
+ do_s16b(&tmp16s, 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(&tmp16s, flag);
+ do_s16b(&tmp16s, flag);
+ do_s16b(&p_ptr->immov_cntr, flag);
+ do_s16b(&p_ptr->strike, flag);
+ do_s16b(&tmp16s, flag);
+ do_s16b(&p_ptr->tim_reflect, flag);
+ do_s16b(&tmp16s, 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(&tmp32s, flag);
+ do_s32b(&tmp32s, flag);
+ do_s16b(&p_ptr->absorb_soul, flag);
+ do_s32b(&p_ptr->inertia_controlled_spell, flag);
+ do_s16b(&p_ptr->last_rewarded_level, flag);
+
+ do_s16b(&tmp16s, flag); /* compat */
+
+ if (flag == ls_flag_t::SAVE) { tmp16s = CORRUPTIONS_MAX; }
+ do_s16b(&tmp16s, flag);
+ if (tmp16s > CORRUPTIONS_MAX) {
+ quit("Too many corruptions");
+ }
+
+ for (i = 0; i < tmp16s; i++)
+ {
+ if (flag == ls_flag_t::SAVE)
+ tmp8u = p_ptr->corruptions[i];
+
+ do_byte(&tmp8u, flag);
+
+ if (flag == ls_flag_t::LOAD)
+ p_ptr->corruptions[i] = tmp8u;
+ }
+
+ do_byte((byte*)&p_ptr->corrupt_anti_teleport_stopped, flag);
+
+ 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(&tmp8u, 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(&tmp16s, flag);
+
+ do_byte(&tmp8u, flag);
+
+ do_s16b(&no_breeds, flag);
+ do_s16b(&p_ptr->protgood, flag);
+
+ /* Auxilliary variables */
+ do_u32b(&p_ptr->mimic_extra, flag);
+ do_u32b(&p_ptr->antimagic_extra, flag);
+ do_u32b(&tmp32u, flag);
+ do_u32b(&tmp32u, flag);
+ do_u32b(&tmp32u, flag);
+ do_u32b(&p_ptr->music_extra, flag);
+ do_u32b(&tmp32u, flag);
+ do_u32b(&p_ptr->necro_extra, flag);
+ do_u32b(&p_ptr->necro_extra2, flag);
+
+ do_u32b(&tmp32u, flag);
+ do_u32b(&tmp32u, flag);
+ do_u32b(&tmp32u, flag);
+ do_u32b(&tmp32u, flag);
+ do_u32b(&tmp32u, flag);
+ do_u32b(&tmp32u, flag);
+ do_u32b(&tmp32u, 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_flag_t::SAVE) tmp16s = POWER_MAX;
+ do_s16b(&tmp16s, flag);
+ if ((flag == ls_flag_t::LOAD) && (tmp16s > POWER_MAX))
+ note(format("Too many (%u) powers!", tmp16s));
+ for (i = 0; i < POWER_MAX; 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 -- kept only for load-compatibility with old savefiles. */
+ for (i = 0; i < 24; i++) {
+ tmp16s = 0; do_s16b(&tmp16s, flag);
+ tmp16s = 0; do_s16b(&tmp16s, flag);
+ }
+ tmp32u = 0; do_u32b(&tmp32u, flag);
+
+ /* Spells */
+ do_s16b(&spell_num, flag);
+ for (i = 0; i < MAX_SPELLS; i++)
+ do_spells(i, flag);
+ do_s16b(&rune_num, flag);
+ for (i = 0; i < MAX_RUNES; i++)
+ {
+ do_string(rune_spells[i].name, 30, flag);
+ do_s16b(&rune_spells[i].type, flag);
+ do_s16b(&rune_spells[i].rune2, flag);
+ do_s16b(&rune_spells[i].mana, flag);
+ }
+
+ /* Load random seeds */
+ do_u32b(&dummy32u, flag); /* Load-compatibility with old savefiles. */
+ do_u32b(&seed_flavor, flag); /* For consistent object flavors. */
+ do_u32b(&dummy32u, flag); /* Load-compatibility with old savefiles. */
+
+ /* Special stuff */
+ do_u16b(&tmp16b, flag); /* Dummy */
+ do_u16b(&total_winner, flag);
+ do_u16b(&has_won, flag);
+ do_u16b(&noscore, flag);
+
+ /* Write death */
+ if (flag == ls_flag_t::SAVE) tmp8u = death;
+ do_byte(&tmp8u, flag);
+ if (flag == ls_flag_t::LOAD) death = tmp8u;
+
+ /* Incompatible module? */
+ if (flag == ls_flag_t::LOAD)
+ {
+ s32b ok;
+
+ ok = module_savefile_loadable(loaded_game_module);
+
+ /* 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_flag_t::SAVE) tmp8u = feeling;
+ do_byte(&tmp8u, flag);
+ if (flag == ls_flag_t::LOAD) feeling = tmp8u;
+
+ /* Turn of last "feeling" */
+ do_s32b(&old_turn, flag);
+
+ /* Current turn */
+ do_s32b(&turn, flag);
+
+ return TRUE;
+}
+
+
+/*
+ * Read a monster
+ */
+static void do_monster(monster_type *m_ptr, ls_flag_t flag)
+{
+ int i;
+
+ /* 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_s32b(&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_flag_t::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);
+ }
+}
+
+
+
+/*
+ * 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, ls_flag_t 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_flag_t::LOAD)
+ {
+ do_byte(&old_dd, ls_flag_t::LOAD);
+ do_byte(&old_ds, ls_flag_t::LOAD);
+ }
+ if (flag == ls_flag_t::SAVE)
+ {
+ do_byte(&o_ptr->dd, ls_flag_t::SAVE);
+ do_byte(&o_ptr->ds, ls_flag_t::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_flag_t::LOAD)
+ {
+ char buf[128];
+ /* Inscription */
+ load_string(buf, 128);
+ if (buf[0])
+ {
+ o_ptr->note = quark_add(buf);
+ }
+ /* Artifact name */
+ load_string(buf, 128);
+ if (buf[0])
+ {
+ o_ptr->art_name = quark_add(buf);
+ }
+ }
+ if (flag == ls_flag_t::SAVE)
+ {
+ /* Save the inscription (if any) */
+ if (o_ptr->note)
+ {
+ save_string(quark_str(o_ptr->note));
+ }
+ else
+ {
+ save_string("");
+ }
+ if (o_ptr->art_name)
+ {
+ save_string(quark_str(o_ptr->art_name));
+ }
+ else
+ {
+ save_string("");
+ }
+ }
+
+ if (flag == ls_flag_t::SAVE) return ; /* Stick any more shared code before this. The rest
+ of this function is reserved for ls_flag_t::LOAD's
+ cleanup functions */
+ /*********** END OF ls_flag_t::SAVE ***************/
+
+ /* Obtain the "kind" template */
+ k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Obtain tval/sval from k_info */
+ o_ptr->tval = k_ptr->tval;
+ if (o_ptr->tval != TV_RANDART) o_ptr->sval = k_ptr->sval;
+
+
+ /* Repair non "wearable" items */
+ if (!wearable_p(o_ptr))
+ {
+ /* Acquire correct fields */
+ o_ptr->to_h = k_ptr->to_h;
+ o_ptr->to_d = k_ptr->to_d;
+ o_ptr->to_a = k_ptr->to_a;
+
+ /* Acquire correct fields */
+ o_ptr->ac = k_ptr->ac;
+ o_ptr->dd = k_ptr->dd;
+ o_ptr->ds = k_ptr->ds;
+
+ /* All done */
+ return;
+ }
+
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Paranoia */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr;
+
+ /* Obtain the artifact info */
+ a_ptr = &a_info[o_ptr->name1];
+
+ /* Verify that artifact */
+ if (!a_ptr->name) o_ptr->name1 = 0;
+ }
+
+ /* Paranoia */
+ if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr;
+
+ /* Obtain the ego-item info */
+ e_ptr = &e_info[o_ptr->name2];
+
+ /* Verify that ego-item */
+ if (!e_ptr->name) o_ptr->name2 = 0;
+ }
+
+
+ /* Acquire standard fields */
+ o_ptr->ac = k_ptr->ac;
+ o_ptr->dd = k_ptr->dd;
+ o_ptr->ds = k_ptr->ds;
+
+ /* Artifacts */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr;
+
+ /* Obtain the artifact info */
+ a_ptr = &a_info[o_ptr->name1];
+
+ /* Acquire new artifact fields */
+ o_ptr->ac = a_ptr->ac;
+ o_ptr->dd = a_ptr->dd;
+ o_ptr->ds = a_ptr->ds;
+
+ /* Acquire new artifact weight */
+ o_ptr->weight = a_ptr->weight;
+ }
+
+ /* Ego items */
+ if (o_ptr->name2)
+ {
+ o_ptr->dd = old_dd;
+ o_ptr->ds = old_ds;
+ }
+
+ if (o_ptr->art_name) /* A random artifact */
+ {
+ o_ptr->dd = old_dd;
+ o_ptr->ds = old_ds;
+ }
+}
+
+static void do_cave_type(cave_type *c_ptr, ls_flag_t flag)
+{
+ do_u16b(&c_ptr->info, flag);
+ do_byte(&c_ptr->feat, flag);
+ do_byte(&c_ptr->mimic, flag);
+ do_s16b(&c_ptr->special, flag);
+ do_s16b(&c_ptr->special2, flag);
+ do_s16b(&c_ptr->t_idx, flag);
+ do_s16b(&c_ptr->inscription, flag);
+ do_byte(&c_ptr->mana, flag);
+ do_s16b(&c_ptr->effect, flag);
+}
+
+static void do_grid(ls_flag_t flag)
+{
+ for (int y = 0; y < cur_hgt; y++)
+ {
+ for (int x = 0; x < cur_wid; x++)
+ {
+ do_cave_type(&cave[y][x], flag);
+ }
+ }
+}
+
+static void my_sentinel(const char *place, u16b value, ls_flag_t flag)
+/* This function lets us know exactly where a savefile is
+ broken by reading/writing conveniently a sentinel at this
+ spot */
+{
+ if (flag == ls_flag_t::SAVE)
+ {
+ do_u16b(&value, flag);
+ return;
+ }
+ if (flag == ls_flag_t::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);
+}
+
+
+
+/*
+ * 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(ls_flag_t 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_flag_t::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_flag_t::LOAD) && (!dun_level && !p_ptr->inside_quest))
+ {
+ int xstart = 0;
+ int ystart = 0;
+ /* Init the wilderness */
+ process_dungeon_file("w_info.txt", &ystart, &xstart, cur_hgt, cur_wid,
+ TRUE, FALSE);
+
+ /* Init the town */
+ xstart = 0;
+ ystart = 0;
+ init_flags = 0;
+ process_dungeon_file("t_info.txt", &ystart, &xstart, cur_hgt, cur_wid,
+ TRUE, FALSE);
+ }
+
+ do_grid(flag);
+
+ /*** Objects ***/
+
+ if (flag == ls_flag_t::SAVE) compact_objects(0);
+ if (flag == ls_flag_t::SAVE) compact_monsters(0);
+ if (flag == ls_flag_t::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_flag_t::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_flag_t::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_flag_t::SAVE);
+ continue; /* Saving is easy */
+ }
+ /* Until the end of the loop, this is all ls_flag_t::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_flag_t::LOAD);
+
+ /* Monster */
+ if (o_ptr->held_m_idx)
+ {
+ /* Monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Place the object */
+ m_ptr->hold_o_idxs.push_back(o_idx);
+ }
+
+ /* Dungeon */
+ else
+ {
+ /* Access the item location */
+ c_ptr = &cave[o_ptr->iy][o_ptr->ix];
+
+ /* Place the object */
+ c_ptr->o_idxs.push_back(o_idx);
+ }
+ }
+
+ /*** Monsters ***/
+
+ if (flag == ls_flag_t::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_flag_t::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_flag_t::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_flag_t::SAVE);
+ continue; /* Easy to save a monster */
+ }
+ /* From here on, it's all ls_flag_t::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_flag_t::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_flag_t::SAVE && !no_companions) ? max_m_idx : 0;
+
+ /* Read the monster count */
+ do_u16b(&tmp16b, flag);
+
+ /* Hack -- verify */
+ if ((flag == ls_flag_t::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_flag_t::LOAD) character_dungeon = TRUE;
+
+ /* Success */
+ return (TRUE);
+}
+
+/* Save the current persistent dungeon -SC- */
+void save_dungeon(void)
+{
+ char tmp[16];
+ char name[1024], buf[5];
+
+ /* Save only persistent dungeons */
+ if (!get_dungeon_save(buf) || (!dun_level)) return;
+
+ /* Construct filename */
+ sprintf(tmp, "%s.%s", player_base, buf);
+ path_build(name, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Open the file */
+ fff = my_fopen(name, "wb");
+
+ /* Save the dungeon */
+ do_dungeon(ls_flag_t::SAVE, TRUE);
+
+ my_fclose(fff);
+}
+
+bool_ file_exist(cptr buf)
+{
+ int fd;
+ bool_ result;
+
+ /* Open savefile */
+ fd = fd_open(buf, O_RDONLY);
+
+ /* File exists */
+ if (fd >= 0)
+ {
+ fd_close(fd);
+ result = TRUE;
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+/*
+ * Handle monster lore
+ */
+static void do_lore(int r_idx, ls_flag_t 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_flag_t::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 void do_store(store_type *str, ls_flag_t flag)
+{
+ byte store_inven_max = STORE_INVEN_MAX;
+
+ /* Some basic info */
+ do_s32b(&str->store_open, flag);
+ do_u16b(&str->owner, flag);
+
+ /* Could be cleaner, done this way for benefit of the for loop later on */
+ byte num;
+ if (flag == ls_flag_t::SAVE) num = str->stock_num;
+ do_byte(&num, flag);
+
+ /* Last visit */
+ do_s32b(&str->last_visit, flag);
+
+ /* Items */
+ for (int j = 0; j < num; j++)
+ {
+ if (flag == ls_flag_t::LOAD)
+ /* Can't this be cleaner? */
+ {
+ object_type forge;
+ /* Wipe the object */
+ object_wipe(&forge);
+ /* Read the item */
+ do_item(&forge, ls_flag_t::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_flag_t::SAVE) do_item(&str->stock[j], flag);
+ }
+}
+
+/*
+ * RNG state
+ */
+static void do_randomizer(ls_flag_t 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_flag_t::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(ls_flag_t 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_flag_t::LOAD) /* There *MUST* be some nice way to unify this! */
+ {
+ u16b c;
+ do_u16b(&c, ls_flag_t::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_flag_t::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_flag_t::SAVE);
+ }
+
+ do_byte((byte*)&autosave_l, flag);
+ do_byte((byte*)&autosave_t, flag);
+ do_s16b(&autosave_freq, flag);
+
+ if (flag == ls_flag_t::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_flag_t::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);
+ }
+}
+
+
+/*
+ * 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(ls_flag_t flag)
+{
+ if (flag == ls_flag_t::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_flag_t::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_flag_t::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_flag_t::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_flag_t::SAVE); /* Sentinel */
+ }
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Read the saved messages
+ */
+static void do_messages(ls_flag_t flag) /* FIXME! We should be able to unify this better */
+{
+ int i;
+ char buf[128];
+ byte color;
+
+ s16b num;
+
+ /* Total */
+ if (flag == ls_flag_t::SAVE) num = message_num();
+ do_s16b(&num, flag);
+
+ /* Read the messages */
+ if (flag == ls_flag_t::LOAD)
+ {
+ byte tmp8u = 0;
+ for (i = 0; i < num; i++)
+ {
+ /* Read the message */
+ do_string(buf, 128, ls_flag_t::LOAD);
+ do_byte(&color, flag);
+ do_byte(&tmp8u, flag);
+
+ /* Save the message */
+ message_add(buf, color);
+ }
+ }
+ if (flag == ls_flag_t::SAVE)
+ {
+ byte holder;
+ byte zero = 0;
+ for (i = num - 1; i >= 0; i--)
+ {
+ do_string((char *)message_str((s16b)i), 0, ls_flag_t::SAVE);
+ holder = message_color((s16b)i);
+ do_byte(&holder, flag);
+ do_byte(&zero, flag);
+ }
+ }
+}
+
+/* Returns TRUE if we successfully load the dungeon */
+bool_ load_dungeon(char *ext)
+{
+ char tmp[16];
+ char name[1024];
+ byte old_dungeon_type = dungeon_type;
+ s16b old_dun = dun_level;
+
+ /* Construct name */
+ sprintf(tmp, "%s.%s", player_base, ext);
+ path_build(name, 1024, ANGBAND_DIR_SAVE, tmp);
+
+ /* Open the file */
+ fff = my_fopen(name, "rb");
+
+ if (fff == NULL)
+ {
+ dun_level = old_dun;
+ dungeon_type = old_dungeon_type;
+
+ my_fclose(fff);
+ return (FALSE);
+ }
+
+ /* Read the dungeon */
+ if (!do_dungeon(ls_flag_t::LOAD, FALSE))
+ {
+ dun_level = old_dun;
+ dungeon_type = old_dungeon_type;
+
+ my_fclose(fff);
+ return (FALSE);
+ }
+
+ dun_level = old_dun;
+ dungeon_type = old_dungeon_type;
+
+ /* Done */
+ my_fclose(fff);
+ return (TRUE);
+}
+
+void do_fate(int i, ls_flag_t flag)
+{
+ if ((flag == ls_flag_t::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);
+}
+
+/*
+ * Load/save timers.
+ */
+static void do_timers(ls_flag_t flag)
+{
+ timer_type *t_ptr;
+
+ for (t_ptr = gl_timers; t_ptr != NULL; t_ptr = t_ptr->next)
+ {
+ do_bool(&t_ptr->enabled, flag);
+ do_s32b(&t_ptr->delay, flag);
+ do_s32b(&t_ptr->countdown, flag);
+ }
+}
+
+/*
+ * Load/save stores.
+ */
+static void do_stores(ls_flag_t flag)
+{
+ u16b tmp16u;
+ u16b real_max = 0;
+
+ /* Note that this forbids max_towns from shrinking, but that is fine */
+ std::unique_ptr<byte[]> reals(new byte[max_towns]);
+
+ /* Find the real towns */
+ if (flag == ls_flag_t::SAVE)
+ {
+ for (int i = 1; i < max_towns; i++)
+ {
+ if (!(town_info[i].flags & (TOWN_REAL))) continue;
+ reals[real_max++] = i;
+ }
+ }
+ do_u16b(&real_max, flag);
+ for (int i = 0; i < real_max; i++)
+ {
+ do_byte((byte*)&reals[i], flag);
+ }
+
+ /* Read the stores */
+ if (flag == ls_flag_t::SAVE) tmp16u = max_st_idx;
+ do_u16b(&tmp16u, flag);
+ assert(tmp16u <= max_st_idx);
+
+ /* Ok now read the real towns */
+ for (int i = 0; i < real_max; i++)
+ {
+ int z = reals[i];
+
+ /* Ultra paranoia */
+ if (!town_info[z].stocked) create_stores_stock(z);
+
+ for (int j = 0; j < tmp16u; j++)
+ {
+ do_store(&town_info[z].store[j], flag);
+ }
+ }
+}
+
+/*
+ * 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->inside_quest = 0;
+ 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];
+}
+
+
+/*
+ * Actually read the savefile
+ */
+static bool_ do_savefile_aux(ls_flag_t flag)
+{
+ int i, j;
+
+ byte tmp8u;
+ u16b tmp16u;
+
+ /* Mention the savefile version */
+ if (flag == ls_flag_t::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_flag_t::SAVE)
+ {
+ sf_when = time((time_t *) 0); /* Note when file was saved */
+ sf_saves++; /* Increment the saves ctr */
+ }
+
+ /* Handle version bytes. FIXME! DG wants me to change this all around */
+ if (flag == ls_flag_t::LOAD)
+ {
+ u32b mt32b;
+ byte mtbyte;
+
+ /* Discard all this, we've already read it */
+ do_u32b(&mt32b, flag);
+ do_byte(&mtbyte, flag);
+ }
+ if (flag == ls_flag_t::SAVE)
+ {
+ u32b saver;
+ saver = SAVEFILE_VERSION;
+ do_u32b(&saver, flag);
+ tmp8u = (byte)rand_int(256);
+ do_byte(&tmp8u, flag); /* 'encryption' */
+ }
+
+ /* Kept only for compatibility; always set to 0 */
+ {
+ u32b tmp32u = 0;
+ do_u32b(&tmp32u, 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_flag_t::SAVE)
+ strcpy(loaded_game_module, game_module);
+ do_string(loaded_game_module, 80, flag);
+
+ /* Timers */
+ do_timers(flag);
+
+ /* Read RNG state */
+ do_randomizer(flag);
+
+ /* Automatizer state */
+ do_byte((byte*)&automatizer_enabled, flag);
+
+ /* Then the options */
+ do_options(flag);
+
+ /* Then the "messages" */
+ do_messages(flag);
+
+ /* Monster Memory */
+ if (flag == ls_flag_t::SAVE) tmp16u = max_r_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::LOAD) && (tmp16u > max_r_idx))
+ {
+ note(format("Too many (%u) monster races!", tmp16u));
+ return (FALSE);
+ }
+
+ /* Read the available records */
+ for (i = 0; i < tmp16u; i++)
+ {
+ /* Read the lore */
+ do_lore(i, flag);
+ }
+
+ /* Object Memory */
+ if (flag == ls_flag_t::SAVE) tmp16u = max_k_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::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_flag_t::LOAD) junkinit();
+
+ {
+ u16b max_towns_ldsv;
+ u16b max_quests_ldsv;
+ if (flag == ls_flag_t::SAVE) max_towns_ldsv = max_towns;
+ /* Number of towns */
+ do_u16b(&max_towns_ldsv, flag);
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::LOAD) && (max_towns_ldsv > max_towns))
+ {
+ note(format("Too many (%u) towns!", max_towns_ldsv));
+ return (FALSE);
+ }
+ /* Min of random towns */
+ if (flag == ls_flag_t::SAVE) max_towns_ldsv = TOWN_RANDOM;
+ do_u16b(&max_towns_ldsv, flag);
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::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_flag_t::LOAD))
+ {
+ create_stores_stock(i);
+ }
+ }
+ }
+
+ /* Number of dungeon */
+ if (flag == ls_flag_t::SAVE) max_towns_ldsv = max_d_idx;
+ do_u16b(&max_towns_ldsv, flag);
+
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::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_flag_t::SAVE) max_quests_ldsv = TOWN_DUNGEON;
+ do_u16b(&max_quests_ldsv, flag);
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::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);
+ }
+
+ /* Sanity check number of quests */
+ if (flag == ls_flag_t::SAVE) max_quests_ldsv = MAX_Q_IDX;
+ do_u16b(&max_quests_ldsv, flag);
+
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::LOAD) && (max_quests_ldsv != MAX_Q_IDX))
+ {
+ note(format("Invalid number of quests (%u)!", max_quests_ldsv));
+ return (FALSE);
+ }
+
+ for (i = 0; i < MAX_Q_IDX; i++)
+ {
+ do_s16b(&quest[i].status, flag);
+ for (auto &quest_data : quest[i].data)
+ {
+ do_s32b(&quest_data, flag);
+ }
+
+ /* Init the hooks */
+ if ((flag == ls_flag_t::LOAD) && (quest[i].init != NULL))
+ {
+ 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_flag_t::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_flag_t::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);
+ }
+ }
+ }
+ }
+
+ /* Load the random artifacts. */
+ if (flag == ls_flag_t::SAVE) tmp16u = MAX_RANDARTS;
+ do_u16b(&tmp16u, flag);
+ if ((flag == ls_flag_t::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_flag_t::SAVE) tmp16u = max_a_idx;
+ do_u16b(&tmp16u, flag);
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::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);
+ }
+
+ /* Fates */
+ if (flag == ls_flag_t::SAVE) tmp16u = MAX_FATES;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::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);
+ }
+
+ /* Load the Traps */
+ if (flag == ls_flag_t::SAVE) tmp16u = max_t_idx;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::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);
+ }
+
+ /* inscription knowledge */
+ if (flag == ls_flag_t::SAVE) tmp16u = MAX_INSCRIPTIONS;
+ do_u16b(&tmp16u, flag);
+
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::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);
+
+
+ /* Read the extra stuff */
+ if (!do_extra(flag))
+ return FALSE;
+
+
+ /* player_hp array */
+ if (flag == ls_flag_t::SAVE) tmp16u = PY_MAX_LEVEL;
+ do_u16b(&tmp16u, flag);
+ /* Incompatible save files */
+ if ((flag == ls_flag_t::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_flag_t::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);
+
+ /* Dripping Tread */
+ do_s16b(&p_ptr->dripping_tread, flag);
+
+ /* Read the inventory */
+ if (!do_inventory(flag) && (flag == ls_flag_t::LOAD)) /* do NOT reverse this ordering */
+ {
+ note("Unable to read inventory");
+ return (FALSE);
+ }
+
+ /* Stores */
+ do_stores(flag);
+
+ /* I'm not dead yet... */
+ if (!death)
+ {
+ /* Dead players have no dungeon */
+ if (flag == ls_flag_t::LOAD) note("Restoring Dungeon...");
+ if ((flag == ls_flag_t::LOAD) && (!do_dungeon(ls_flag_t::LOAD, FALSE)))
+ {
+ note("Error reading dungeon data");
+ return (FALSE);
+ }
+ if (flag == ls_flag_t::SAVE) do_dungeon(ls_flag_t::SAVE, FALSE);
+ my_sentinel("Before ghost data", 435, flag);
+ my_sentinel("After ghost data", 320, flag);
+ }
+
+ {
+ byte foo = 0;
+ if (flag == ls_flag_t::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_flag_t::SAVE);
+ }
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Actually read the savefile
+ */
+static errr rd_savefile(void)
+{
+ errr err = 0;
+
+ /* The savefile is a binary file */
+ fff = my_fopen(savefile, "rb");
+
+ /* Paranoia */
+ if (!fff) return ( -1);
+
+ /* Call the sub-function */
+ err = !do_savefile_aux(ls_flag_t::LOAD);
+
+ /* Check for errors */
+ if (ferror(fff)) err = -1;
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Result */
+ return (err);
+}
+
+
+/*
+ * Attempt to Load a "savefile"
+ *
+ * On multi-user systems, you may only "read" a savefile if you will be
+ * allowed to "write" it later, this prevents painful situations in which
+ * the player loads a savefile belonging to someone else, and then is not
+ * allowed to save his game when he quits.
+ *
+ * We return "TRUE" if the savefile was usable, and we set the global
+ * flag "character_loaded" if a real, living, character was loaded.
+ *
+ * Note that we always try to load the "current" savefile, even if
+ * there is no such file, so we must check for "empty" savefile names.
+ */
+bool_ load_player(void)
+{
+ int fd = -1;
+
+ errr err = 0;
+
+ cptr what = "generic";
+
+ /* Paranoia */
+ turn = 0;
+
+ /* Paranoia */
+ death = FALSE;
+
+
+ /* Allow empty savefile name */
+ if (!savefile[0]) return (TRUE);
+
+
+ /* XXX XXX XXX Fix this */
+
+ /* Verify the existance of the savefile */
+ if (!file_exist(savefile))
+ {
+ /* Give a message */
+ msg_format("Savefile does not exist: %s", savefile);
+ msg_print(NULL);
+
+ /* Allow this */
+ return (TRUE);
+ }
+
+ /* Okay */
+ if (!err)
+ {
+ /* Open the savefile */
+ fd = fd_open(savefile, O_RDONLY);
+
+ /* No file */
+ if (fd < 0) err = -1;
+
+ /* Message (below) */
+ if (err) what = "Cannot open savefile";
+ }
+
+ /* Process file */
+ if (!err)
+ {
+ byte tmp8u = 0;
+
+ /* Open the file XXX XXX XXX XXX Should use Angband file interface */
+ fff = my_fopen(savefile, "rb");
+/* fff = fdopen(fd, "r"); */
+
+ /* Read the first four bytes */
+ do_u32b(&vernum, ls_flag_t::LOAD);
+ do_byte(&tmp8u, ls_flag_t::LOAD); // For comatibility with old savefiles
+
+ /* 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;
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Attempt to load */
+ err = rd_savefile();
+
+ /* Message (below) */
+ if (err) what = "Cannot parse savefile";
+ }
+
+ /* Paranoia */
+ if (!err)
+ {
+ /* Invalid turn */
+ if (!turn) err = -1;
+
+ /* Message (below) */
+ if (err) what = "Broken savefile";
+ }
+
+
+ /* Okay */
+ if (!err)
+ {
+ /* Player is dead */
+ if (death)
+ {
+ /* Player is no longer "dead" */
+ death = FALSE;
+
+ /* Cheat death (unless the character retired) */
+ if (arg_wizard && !total_winner)
+ {
+ /* A character was loaded */
+ character_loaded = TRUE;
+
+ /* Done */
+ return (TRUE);
+ }
+
+ /* Count lives */
+ sf_lives++;
+
+ /* Forget turns */
+ turn = old_turn = 0;
+
+ /* Done */
+ return (TRUE);
+ }
+
+ /* A character was loaded */
+ character_loaded = TRUE;
+
+ /* Still alive */
+ if (p_ptr->chp >= 0)
+ {
+ /* Reset cause of death */
+ (void)strcpy(died_from, "(alive and well)");
+ }
+
+ /* Success */
+ return (TRUE);
+ }
+
+
+ /* Message */
+ msg_format("Error (%s) reading %d.%d.%d savefile.",
+ what, sf_major, sf_minor, sf_patch);
+ msg_print(NULL);
+
+ /* Oops */
+ return (FALSE);
+}
+
+
+
+/*
+ * 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;
+
+ /* Create the savefile */
+ fd = fd_make(name, mode);
+
+ /* File is okay */
+ if (fd >= 0)
+ {
+ /* Close the "fd" */
+ (void)fd_close(fd);
+
+ /* Open the savefile */
+ fff = my_fopen(name, "wb");
+
+ /* Successful open */
+ if (fff)
+ {
+ /* Write the savefile */
+ if (do_savefile_aux(ls_flag_t::SAVE)) ok = TRUE;
+
+ /* Attempt to close it */
+ if (my_fclose(fff)) ok = FALSE;
+ }
+
+ /* "broken" savefile */
+ if (!ok)
+ {
+ /* Remove "broken" files */
+ (void)fd_kill(name);
+ }
+ }
+
+ /* Failure */
+ if (!ok) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+/*
+ * Attempt to save the player in a savefile
+ */
+bool_ save_player(void)
+{
+ int result = FALSE;
+ char safe[1024];
+
+ /* New savefile */
+ strcpy(safe, savefile);
+ strcat(safe, ".new");
+
+ /* Remove it */
+ fd_kill(safe);
+
+ /* Attempt to save the player */
+ if (save_player_aux(safe))
+ {
+ char temp[1024];
+
+ /* Old savefile */
+ strcpy(temp, savefile);
+ strcat(temp, ".old");
+
+ /* Remove it */
+ fd_kill(temp);
+
+ /* Preserve old savefile */
+ fd_move(savefile, temp);
+
+ /* Activate new savefile */
+ fd_move(safe, savefile);
+
+ /* Remove preserved savefile */
+ fd_kill(temp);
+
+ /* Hack -- Pretend the character was loaded */
+ character_loaded = TRUE;
+
+ /* Success */
+ result = TRUE;
+ }
+
+ save_savefile_names();
+
+ /* Return the result */
+ return (result);
+}
diff --git a/src/loadsave.h b/src/loadsave.h
new file mode 100644
index 00000000..61bfced7
--- /dev/null
+++ b/src/loadsave.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "h-basic.h"
+
+// C linkage required for these functions since main-* code uses them.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* loadsave.c */
+extern void save_dungeon(void);
+extern bool_ save_player(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/loadsave.hpp b/src/loadsave.hpp
new file mode 100644
index 00000000..a9eb9dc8
--- /dev/null
+++ b/src/loadsave.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern bool_ file_exist(cptr buf);
+extern bool_ load_dungeon(char *ext);
+extern bool_ load_player(void);
diff --git a/src/lua_bind.cc b/src/lua_bind.cc
new file mode 100644
index 00000000..aa2c3a2a
--- /dev/null
+++ b/src/lua_bind.cc
@@ -0,0 +1,277 @@
+/*
+ * 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 "lua_bind.hpp"
+
+#include "cmd7.hpp"
+#include "corrupt.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "player_type.hpp"
+#include "range.hpp"
+#include "skills.hpp"
+#include "skill_type.hpp"
+#include "spell_type.hpp"
+#include "spells2.hpp"
+#include "spells5.hpp"
+#include "tables.hpp"
+#include "timer_type.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#include <cassert>
+#include <functional>
+
+/*
+ * Misc
+ */
+
+/* Change this fct if I want to switch to learnable spells */
+s32b lua_get_level(spell_type *spell, s32b lvl, s32b max, s32b min, s32b bonus)
+{
+ s32b tmp;
+
+ tmp = lvl - ((spell_type_skill_level(spell) - 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;
+}
+
+/* static */ s32b get_level_device(spell_type *spell, s32b max, s32b min, s32b device_skill, std::function<s32b(spell_type *, s32b, s32b, s32b, s32b)> lua_get_level_ = lua_get_level)
+{
+ /* No max specified ? assume 50 */
+ if (max <= 0) {
+ max = 50;
+ }
+ /* No min specified ? */
+ if (min <= 0) {
+ min = 1;
+ }
+
+ int lvl = device_skill + (get_level_use_stick * SKILL_STEP);
+
+ /* Sticks are limited */
+ if (lvl - ((spell_type_skill_level(spell) + 1) * SKILL_STEP) >= get_level_max_stick * SKILL_STEP)
+ {
+ lvl = (get_level_max_stick + spell_type_skill_level(spell) - 1) * SKILL_STEP;
+ }
+
+ /* / 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;
+ lvl = lua_get_level_(spell, lvl, max, min, 0);
+
+ return lvl;
+}
+
+static s32b get_level_device_1(spell_type *spell, s32b max, s32b min)
+{
+ // Must be in "device" mode.
+ assert(get_level_use_stick > -1);
+ // Delegate
+ auto device_skill = s_info[SKILL_DEVICE].value;
+ return get_level_device(spell, max, min, device_skill);
+}
+
+static s32b get_level_school_1(spell_type *spell, s32b max, s32b min)
+{
+ // Delegate
+ s32b level;
+ bool_ na;
+ get_level_school(spell, max, min, &level, &na);
+ // Note: It is tempting to add an assertion here for "na == FALSE" here,
+ // but there are cases where we haven't actually checked if the spell is
+ // really castable before calling this function (indirectly). Thus we
+ // MUST NOT assert anything about "na" as the code currently stands.
+ return level;
+}
+
+int get_mana(s32b s)
+{
+ // Does not make sense in "device" mode.
+ assert(get_level_use_stick == -1);
+ // Extract the spell's mana range.
+ spell_type *spell = spell_at(s);
+ range_type mana_range;
+ spell_type_mana_range(spell, &mana_range);
+ // Scale
+ return get_level_school_1(spell, mana_range.max, mana_range.min);
+}
+
+/** Returns spell chance of failure for a school spell. */
+static s32b spell_chance_school(s32b s)
+{
+ spell_type *s_ptr = spell_at(s);
+ int level = get_level_school_1(s_ptr, 50, 1);
+ s32b chance = spell_type_failure_rate(s_ptr);
+ int mana = get_mana(s);
+ int cur_mana = get_power(s);
+ int stat = spell_type_casting_stat(s_ptr);
+ int stat_ind = p_ptr->stat_ind[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[stat_ind] - 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[stat_ind];
+
+ /* Must have Perfect Casting to get below 5% */
+ if (!(has_ability(AB_PERFECT_CASTING)))
+ {
+ if (minfail < 5) minfail = 5;
+ }
+
+ /* Hack -- Priest prayer penalty for "edged" weapons -DGK */
+ if ((forbid_non_blessed()) && (p_ptr->icky_wield)) chance += 25;
+
+ /* Return the chance */
+ return clamp_failure_chance(chance, minfail);
+}
+
+s32b spell_chance_device(spell_type *spell_ptr)
+{
+ // Device parameters initialized?
+ assert(get_level_use_stick > -1);
+
+ // Calculate the chance.
+ int level = get_level_device_1(spell_ptr, 50, 1);
+ s32b chance = spell_type_failure_rate(spell_ptr);
+
+ /* Reduce failure rate by "effective" level adjustment */
+ chance -= (level - 1);
+
+ /* Extract the minimum failure rate */
+ int minfail = 15 - get_skill_scale(SKILL_DEVICE, 25);
+
+ /* Return the chance */
+ return clamp_failure_chance(chance, minfail);
+}
+
+s32b spell_chance_book(s32b s)
+{
+ // Must NOT be a device!
+ assert(get_level_use_stick < 0);
+ // Delegate
+ return spell_chance_school(s);
+}
+
+s32b get_level(s32b s, s32b max, s32b min)
+{
+ auto spell = spell_at(s);
+ /** Ahah shall we use Magic device instead ? */
+ if (get_level_use_stick > -1) {
+ return get_level_device_1(spell, max, min);
+ } else {
+ return get_level_school_1(spell, max, min);
+ }
+}
+
+/* Level gen */
+void get_map_size(const char *name, int *ysize, int *xsize)
+{
+ *xsize = 0;
+ *ysize = 0;
+ init_flags = INIT_GET_SIZE;
+ process_dungeon_file(name, ysize, xsize, cur_hgt, cur_wid, TRUE, TRUE);
+}
+
+void load_map(const char *name, int *y, int *x)
+{
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(name, y, x, cur_hgt, cur_wid, TRUE, TRUE);
+}
+
+/*
+ * 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 buf;
+ return buf;
+}
+
+char lua_msg_box(cptr title)
+{
+ int wid, hgt;
+
+ Term_get_size(&wid, &hgt);
+ return msg_box(title, hgt / 2, wid / 2);
+}
+
+
+
+void increase_mana(int delta)
+{
+ p_ptr->csp += delta;
+ p_ptr->redraw |= PR_FRAME;
+
+ if (p_ptr->csp < 0)
+ {
+ p_ptr->csp = 0;
+ }
+ if (p_ptr->csp > p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ }
+}
+
+timer_type *TIMER_AGGRAVATE_EVIL = 0;
+
+void timer_aggravate_evil_enable()
+{
+ TIMER_AGGRAVATE_EVIL->enabled = TRUE;
+}
+
+void timer_aggravate_evil_callback()
+{
+ if ((p_ptr->prace == RACE_MAIA) &&
+ (!player_has_corruption(CORRUPT_BALROG_AURA)) &&
+ (!player_has_corruption(CORRUPT_BALROG_WINGS)) &&
+ (!player_has_corruption(CORRUPT_BALROG_STRENGTH)) &&
+ (!player_has_corruption(CORRUPT_BALROG_FORM)))
+ {
+ dispel_evil(0);
+ }
+}
diff --git a/src/lua_bind.hpp b/src/lua_bind.hpp
new file mode 100644
index 00000000..b2a6c9a7
--- /dev/null
+++ b/src/lua_bind.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "h-basic.h"
+#include "spell_type_fwd.hpp"
+#include "timer_type_fwd.hpp"
+
+/** Calculate spell failure rate for a device, i.e. a wand or staff. */
+extern s32b spell_chance_device(spell_type *spell_ptr);
+
+/** Calculate spell failure rate for a spell book. */
+extern s32b spell_chance_book(s32b s);
+
+
+extern s32b lua_get_level(struct spell_type *spell, s32b lvl, s32b max, s32b min, s32b bonus);
+extern int get_mana(s32b s);
+extern s32b get_power(s32b s);
+extern s32b get_level(s32b s, s32b max, s32b min);
+extern void get_level_school(struct spell_type *spell, s32b max, s32b min, s32b *level, bool_ *na);
+
+extern s32b get_level_max_stick;
+extern s32b get_level_use_stick;
+
+extern void get_map_size(const char *name, int *ysize, int *xsize);
+extern void load_map(const char *name, int *y, int *x);
+
+extern char *lua_input_box(cptr title, int max);
+extern char lua_msg_box(cptr title);
+
+extern void increase_mana(int delta);
+
+extern timer_type *TIMER_AGGRAVATE_EVIL;
+
+void timer_aggravate_evil_enable();
+void timer_aggravate_evil_callback();
diff --git a/src/magic_power.hpp b/src/magic_power.hpp
new file mode 100644
index 00000000..b02c6c14
--- /dev/null
+++ b/src/magic_power.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Powers, used by Mindcrafters and Necromancers
+ */
+struct magic_power
+{
+ int min_lev;
+ int mana_cost;
+ int fail;
+ cptr name;
+ cptr desc;
+};
diff --git a/src/main-gcu.c b/src/main-gcu.c
new file mode 100644
index 00000000..c253daf2
--- /dev/null
+++ b/src/main-gcu.c
@@ -0,0 +1,1021 @@
+/* File: main-gcu.c */
+
+/*
+ * Copyright (c) 1997 Ben Harrison, and others
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+
+/*
+ * This file helps Angband run on Unix/Curses machines.
+ *
+ *
+ * To use this file, you must define "USE_GCU" in the Makefile.
+ *
+ *
+ * Note that this file is not "intended" to support non-Unix machines,
+ * nor is it intended to support VMS or other bizarre setups.
+ *
+ * Also, this package assumes that the underlying "curses" handles both
+ * the "nonl()" and "cbreak()" commands correctly, see the "OPTION" below.
+ *
+ * This code should work with most versions of "curses" or "ncurses",
+ * and the "main-ncu.c" file (and USE_NCU define) are no longer used.
+ *
+ * This file provides up to 4 term windows.
+ *
+ * This file will attempt to redefine the screen colors to conform to
+ * standard Angband colors. It will only do so if the terminal type
+ * indicates that it can do so. See the page:
+ *
+ * http://www.umr.edu/~keldon/ang-patch/ncurses_color.html
+ *
+ * for information on this.
+ *
+ * Consider the use of "savetty()" and "resetty()". XXX XXX XXX
+ */
+
+#include "util.h"
+#include "variable.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(linux)
+# define USE_TERMIO
+# else
+# define USE_TCHARS
+# endif
+# endif
+# endif
+#endif
+
+/*
+ * POSIX stuff
+ */
+#ifdef USE_TPOSIX
+# include <sys/ioctl.h>
+# include <termios.h>
+#endif
+
+/*
+ * One version needs these files
+ */
+#ifdef USE_TERMIO
+# include <sys/ioctl.h>
+# include <termio.h>
+#endif
+
+/*
+ * The other needs these files
+ */
+#ifdef USE_TCHARS
+# include <sys/ioctl.h>
+# include <sys/resource.h>
+# include <sys/param.h>
+# include <sys/file.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+
+
+
+/*
+ * XXX XXX Hack -- POSIX uses "O_NONBLOCK" instead of "O_NDELAY"
+ *
+ * They should both work due to the "(i != 1)" test below.
+ */
+#ifndef O_NDELAY
+# define O_NDELAY O_NONBLOCK
+#endif
+
+
+/*
+ * OPTION: some machines lack "cbreak()"
+ * On these machines, we use an older definition
+ */
+/* #define cbreak() crmode() */
+
+
+/*
+ * OPTION: some machines cannot handle "nonl()" and "nl()"
+ * On these machines, we can simply ignore those commands.
+ */
+/* #define nonl() */
+/* #define nl() */
+
+
+/*
+ * Save the "normal" and "angband" terminal settings
+ */
+
+#ifdef USE_TPOSIX
+
+static struct termios norm_termios;
+
+static struct termios game_termios;
+
+#endif
+
+#ifdef USE_TERMIO
+
+static struct termio norm_termio;
+
+static struct termio game_termio;
+
+#endif
+
+#ifdef USE_TCHARS
+
+static struct ltchars norm_special_chars;
+static struct sgttyb norm_ttyb;
+static struct tchars norm_tchars;
+static int norm_local_chars;
+
+static struct ltchars game_special_chars;
+static struct sgttyb game_ttyb;
+static struct tchars game_tchars;
+static int game_local_chars;
+
+#endif
+
+/*
+ * Information about a term
+ */
+typedef struct term_data term_data;
+
+struct term_data
+{
+ term t; /* All term info */
+
+ WINDOW *win; /* Pointer to the curses window */
+};
+
+/* Max number of windows on screen */
+#define MAX_TERM_DATA 4
+
+/* Information about our windows */
+static term_data data[MAX_TERM_DATA];
+
+
+/*
+ * Hack -- Number of initialized "term" structures
+ */
+static int active = 0;
+
+
+#ifdef A_COLOR
+
+/*
+ * Hack -- define "A_BRIGHT" to be "A_BOLD", because on many
+ * machines, "A_BRIGHT" produces ugly "inverse" video.
+ */
+#ifndef A_BRIGHT
+# define A_BRIGHT A_BOLD
+#endif
+
+/*
+ * Software flag -- we are allowed to use color
+ */
+static int can_use_color = FALSE;
+
+/*
+ * Software flag -- we are allowed to change the colors
+ */
+static int can_fix_color = FALSE;
+
+/*
+ * Simple Angband to Curses color conversion table
+ */
+static int colortable[16];
+
+#endif
+
+
+
+/*
+ * Place the "keymap" into its "normal" state
+ */
+static void keymap_norm(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* restore the saved values of the special chars */
+ (void)tcsetattr(0, TCSAFLUSH, &norm_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TCSETA, (char *)&norm_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TIOCSLTC, (char *)&norm_special_chars);
+ (void)ioctl(0, TIOCSETP, (char *)&norm_ttyb);
+ (void)ioctl(0, TIOCSETC, (char *)&norm_tchars);
+ (void)ioctl(0, TIOCLSET, (char *)&norm_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Place the "keymap" into the "game" state
+ */
+static void keymap_game(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* restore the saved values of the special chars */
+ (void)tcsetattr(0, TCSAFLUSH, &game_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TCSETA, (char *)&game_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* restore the saved values of the special chars */
+ (void)ioctl(0, TIOCSLTC, (char *)&game_special_chars);
+ (void)ioctl(0, TIOCSETP, (char *)&game_ttyb);
+ (void)ioctl(0, TIOCSETC, (char *)&game_tchars);
+ (void)ioctl(0, TIOCLSET, (char *)&game_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Save the normal keymap
+ */
+static void keymap_norm_prepare(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* Get the normal keymap */
+ tcgetattr(0, &norm_termios);
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* Get the normal keymap */
+ (void)ioctl(0, TCGETA, (char *)&norm_termio);
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* Get the normal keymap */
+ (void)ioctl(0, TIOCGETP, (char *)&norm_ttyb);
+ (void)ioctl(0, TIOCGLTC, (char *)&norm_special_chars);
+ (void)ioctl(0, TIOCGETC, (char *)&norm_tchars);
+ (void)ioctl(0, TIOCLGET, (char *)&norm_local_chars);
+
+#endif
+
+}
+
+
+/*
+ * Save the keymaps (normal and game)
+ */
+static void keymap_game_prepare(void)
+{
+
+#ifdef USE_TPOSIX
+
+ /* Acquire the current mapping */
+ tcgetattr(0, &game_termios);
+
+ /* Force "Ctrl-C" to interupt */
+ game_termios.c_cc[VINTR] = (char)3;
+
+ /* Force "Ctrl-Z" to suspend */
+ game_termios.c_cc[VSUSP] = (char)26;
+
+ /* Hack -- Leave "VSTART/VSTOP" alone */
+
+ /* Disable the standard control characters */
+ game_termios.c_cc[VQUIT] = (char) - 1;
+ game_termios.c_cc[VERASE] = (char) - 1;
+ game_termios.c_cc[VKILL] = (char) - 1;
+ game_termios.c_cc[VEOF] = (char) - 1;
+ game_termios.c_cc[VEOL] = (char) - 1;
+
+ /* Normally, block until a character is read */
+ game_termios.c_cc[VMIN] = 1;
+ game_termios.c_cc[VTIME] = 0;
+
+#endif
+
+#ifdef USE_TERMIO
+
+ /* Acquire the current mapping */
+ (void)ioctl(0, TCGETA, (char *)&game_termio);
+
+ /* Force "Ctrl-C" to interupt */
+ game_termio.c_cc[VINTR] = (char)3;
+
+ /* Force "Ctrl-Z" to suspend */
+ game_termio.c_cc[VSUSP] = (char)26;
+
+ /* Hack -- Leave "VSTART/VSTOP" alone */
+
+ /* Disable the standard control characters */
+ game_termio.c_cc[VQUIT] = (char) - 1;
+ game_termio.c_cc[VERASE] = (char) - 1;
+ game_termio.c_cc[VKILL] = (char) - 1;
+ game_termio.c_cc[VEOF] = (char) - 1;
+ game_termio.c_cc[VEOL] = (char) - 1;
+
+ /* Normally, block until a character is read */
+ game_termio.c_cc[VMIN] = 1;
+ game_termio.c_cc[VTIME] = 0;
+
+#endif
+
+#ifdef USE_TCHARS
+
+ /* Get the default game characters */
+ (void)ioctl(0, TIOCGETP, (char *)&game_ttyb);
+ (void)ioctl(0, TIOCGLTC, (char *)&game_special_chars);
+ (void)ioctl(0, TIOCGETC, (char *)&game_tchars);
+ (void)ioctl(0, TIOCLGET, (char *)&game_local_chars);
+
+ /* Force suspend (^Z) */
+ game_special_chars.t_suspc = (char)26;
+
+ /* Cancel some things */
+ game_special_chars.t_dsuspc = (char) - 1;
+ game_special_chars.t_rprntc = (char) - 1;
+ game_special_chars.t_flushc = (char) - 1;
+ game_special_chars.t_werasc = (char) - 1;
+ game_special_chars.t_lnextc = (char) - 1;
+
+ /* Force interupt (^C) */
+ game_tchars.t_intrc = (char)3;
+
+ /* Force start/stop (^Q, ^S) */
+ game_tchars.t_startc = (char)17;
+ game_tchars.t_stopc = (char)19;
+
+ /* Cancel some things */
+ game_tchars.t_quitc = (char) - 1;
+ game_tchars.t_eofc = (char) - 1;
+ game_tchars.t_brkc = (char) - 1;
+
+#endif
+
+}
+
+
+
+
+/*
+ * Suspend/Resume
+ */
+static errr Term_xtra_gcu_alive(int v)
+{
+ int x, y;
+
+
+ /* Suspend */
+ if (!v)
+ {
+ /* Go to normal keymap mode */
+ keymap_norm();
+
+ /* Restore modes */
+ noraw();
+ 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 */
+ raw();
+ noecho();
+ nonl();
+
+ /* Go to angband keymap mode */
+ keymap_game();
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Init the "curses" system
+ */
+static void Term_init_gcu(term *t)
+{
+ term_data *td = (term_data *)(t->data);
+
+ /* Count init's, handle first */
+ if (active++ != 0) return;
+
+ /* Erase the window */
+ (void)wclear(td->win);
+
+ /* Reset the cursor */
+ (void)wmove(td->win, 0, 0);
+
+ /* Flush changes */
+ (void)wrefresh(td->win);
+
+ /* Game keymap */
+ keymap_game();
+}
+
+
+/*
+ * Nuke the "curses" system
+ */
+static void Term_nuke_gcu(term *t)
+{
+ int x, y;
+ term_data *td = (term_data *)(t->data);
+
+ /* Delete this window */
+ delwin(td->win);
+
+ /* Count nuke's, handle last */
+ if (--active != 0) return;
+
+ /* Hack -- make sure the cursor is visible */
+ Term_xtra(TERM_XTRA_SHAPE, 1);
+
+#ifdef A_COLOR
+ /* Reset colors to defaults */
+ start_color();
+#endif
+
+ /* Get current cursor position */
+ getyx(curscr, y, x);
+
+ /* Move the cursor to bottom right corner */
+ mvcur(y, x, LINES - 1, 0);
+
+ /* Flush the curses buffer */
+ (void)refresh();
+
+ /* Exit curses */
+ endwin();
+
+ /* Flush the output */
+ (void)fflush(stdout);
+
+ /* Normal keymap */
+ keymap_norm();
+}
+
+
+/*
+* Process events (with optional wait)
+*/
+static errr Term_xtra_gcu_event(int v)
+{
+ int i, k;
+
+ char buf[2];
+
+ /* Wait */
+ if (v)
+ {
+ /* Wait for one byte */
+ i = read(0, buf, 1);
+
+ /* Hack -- Handle bizarre "errors" */
+ if ((i <= 0) && (errno != EINTR)) abort();
+ }
+
+ /* Do not wait */
+ else
+ {
+ /* Get the current flags for stdin */
+ k = fcntl(0, F_GETFL, 0);
+
+ /* Oops */
+ if (k < 0) return (1);
+
+ /* Tell stdin not to block */
+ if (fcntl(0, F_SETFL, k | O_NDELAY) < 0) return (1);
+
+ /* Read one byte, if possible */
+ i = read(0, buf, 1);
+
+ /* Replace the flags for stdin */
+ if (fcntl(0, F_SETFL, k)) return (1);
+ }
+
+ /* Ignore "invalid" keys */
+ if ((i != 1) || (!buf[0])) return (1);
+
+ /* Enqueue the keypress */
+ Term_keypress(buf[0]);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * 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);
+
+
+ /* 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);
+
+ /* 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);
+}
+
+
+/*
+ * 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;
+
+#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++)
+ {
+
+ /* 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;
+
+ /* 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->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;
+ }
+
+ fprintf(stderr, "Ignoring option: %s", argv[i]);
+ }
+
+
+ /* Extract the normal keymap */
+ keymap_norm_prepare();
+
+
+/* Initialize for other systems */
+ if (initscr() == (WINDOW*)ERR) return ( -1);
+
+ /* Activate hooks */
+ quit_aux = hook_quit;
+
+ /* Require standard size screen */
+ if ((LINES < 24) || (COLS < 80))
+ {
+ quit("Angband needs at least an 80x24 'curses' screen");
+ }
+
+
+
+#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 ***/
+
+
+ /* Prepare */
+ raw();
+ noecho();
+ nonl();
+
+ /* Extract the game keymap */
+ keymap_game_prepare();
+
+
+ /*** Now prepare the term(s) ***/
+
+ /* Big screen -- one big term */
+ if (use_big_screen)
+ {
+ /* Create a term */
+ term_data_init_gcu(&data[0], LINES, COLS, 0, 0);
+
+ /* Remember the term */
+ angband_term[0] = &data[0].t;
+ }
+
+ /* No big screen -- create as many term windows as possible */
+ else
+ {
+ /* Create several terms */
+ for (i = 0; i < num_term; i++)
+ {
+ int rows, cols, y, x;
+
+ /* Decide on size and position */
+ switch (i)
+ {
+ /* Upper left */
+ case 0:
+ {
+ rows = 24;
+ cols = 80;
+ y = x = 0;
+ break;
+ }
+
+ /* Lower left */
+ case 1:
+ {
+ rows = LINES - 25;
+ cols = 80;
+ y = 25;
+ x = 0;
+ break;
+ }
+
+ /* Upper right */
+ case 2:
+ {
+ rows = 24;
+ cols = COLS - 81;
+ y = 0;
+ x = 81;
+ break;
+ }
+
+ /* Lower right */
+ case 3:
+ {
+ rows = LINES - 25;
+ cols = COLS - 81;
+ y = 25;
+ x = 81;
+ break;
+ }
+
+ /* XXX */
+ default:
+ {
+ rows = cols = y = x = 0;
+ break;
+ }
+ }
+
+ /* Skip non-existant windows */
+ if (rows <= 0 || cols <= 0) continue;
+
+ /* Create a term */
+ term_data_init_gcu(&data[next_win], rows, cols, y, x);
+
+ /* Remember the term */
+ angband_term[next_win] = &data[next_win].t;
+
+ /* One more window */
+ next_win++;
+ }
+ }
+
+ /* Activate the "Angband" window screen */
+ Term_activate(&data[0].t);
+
+ /* Remember the active screen */
+ term_screen = &data[0].t;
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* USE_GCU */
+
+
diff --git a/src/main-gtk2.c b/src/main-gtk2.c
new file mode 100644
index 00000000..ca3eff60
--- /dev/null
+++ b/src/main-gtk2.c
@@ -0,0 +1,1977 @@
+/* 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 "files.h"
+#include "util.h"
+#include "variable.h"
+
+
+/*
+ * Activate variant-specific features
+ */
+
+#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>
+#include <assert.h>
+
+
+/*
+ * 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.
+ */
+
+
+
+/*
+ * 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;
+
+
+ char *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)
+
+
+/*
+ * 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 ****/
+
+/*
+ * 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);
+}
+
+
+/*
+ * New global flag to indicate if it's safe to save now
+ */
+#define can_save TRUE
+
+
+
+
+/**** Low level routines - colours and graphics ****/
+
+
+/*
+ * Remeber RGB values for sixteen Angband colours, in a format
+ * that is convinient for GdkRGB GC functions.
+ *
+ * XXX XXX Duplication of maid-x11.c is far from the Angband
+ * ideal of code cleanliness, but the whole point of using GdkRGB
+ * is to let it handle colour allocation which it does in a very
+ * clever fashion. Ditto for the tile scaling code and the BMP loader
+ * below.
+ */
+static void init_colours(void)
+{
+ int i;
+
+
+ /* Process each colour */
+ for (i = 0; i < 16; i++)
+ {
+ u32b red, green, blue;
+
+ /* Retrieve RGB values from the game */
+ red = angband_color_table[i][1];
+ green = angband_color_table[i][2];
+ blue = angband_color_table[i][3];
+
+ /* Remember a GdkRGB value, that is 0xRRGGBB */
+ angband_colours[i] = (red << 16) | (green << 8) | blue;
+ }
+}
+
+
+/*
+ * Set foreground colour of window td to attr, only when it is necessary
+ */
+static void term_data_set_fg(term_data *td, byte attr)
+{
+ /* We can use the current gc */
+ if (td->last_attr == attr) return;
+
+ /* Activate the colour */
+ gdk_rgb_gc_set_foreground(td->gc, angband_colours[attr]);
+
+ /* Remember it */
+ td->last_attr = attr;
+}
+
+
+
+
+
+
+/**** 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) 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;
+
+}
+
+
+/*
+ * 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);
+
+ /* 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);
+}
+
+
+
+
+/*
+ * 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());
+
+ /* 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();
+
+
+ /* 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 */
+ do_cmd_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);
+}
+
+
+/*
+ * 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;
+
+}
+
+
+/*
+ * Process Options-Font-* menu command
+ */
+static void change_font_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *widget)
+{
+ /* Not implemented */
+}
+
+
+/*
+ * Process Terms-* menu command - hide/show terminal window
+ */
+static void term_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *widget)
+{
+ term_data *td = &data[user_action];
+
+ /* We don't mess with the Angband window */
+ if (td == &data[0]) return;
+
+ /* It's shown */
+ if (td->shown)
+ {
+ /* Hide the window */
+ gtk_widget_hide_all(td->window);
+ }
+
+ /* It's hidden */
+ else
+ {
+ /* Show the window */
+ gtk_widget_show_all(td->window);
+ }
+}
+
+
+/*
+ * Toggles the boolean value of use_backing_store and
+ * setup / remove backing store for each term
+ */
+static void change_backing_store_event_handler(
+ gpointer user_data,
+ guint user_action,
+ GtkWidget *was_clicked)
+{
+ int i;
+
+ /* Toggle the backing store mode */
+ use_backing_store = !use_backing_store;
+
+ /* Reset terms */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term_data_set_backing_store(&data[i]);
+ }
+}
+
+
+
+
+/*
+ * 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];
+
+ /* Hack - do not do anything until the player picks from the menu */
+ if (!game_in_progress) return (TRUE);
+
+ /* Hack - Ignore parameters */
+ (void) widget;
+ (void) user_data;
+
+ /* Extract four "modifier flags" */
+ mc = (event->state & GDK_CONTROL_MASK) ? TRUE : FALSE;
+ ms = (event->state & GDK_SHIFT_MASK) ? TRUE : FALSE;
+ mo = (event->state & GDK_MOD1_MASK) ? TRUE : FALSE;
+ mx = (event->state & GDK_MOD3_MASK) ? TRUE : FALSE;
+
+ /*
+ * Hack XXX
+ * Parse shifted numeric (keypad) keys specially.
+ */
+ if ((event->state == GDK_SHIFT_MASK)
+ && (event->keyval >= GDK_KP_0) && (event->keyval <= GDK_KP_9))
+ {
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%cS_%X%c", 31, event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+ }
+
+ /* Normal keys with no modifiers */
+ if (event->length && !mo && !mx)
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; i < event->length; i++) Term_keypress(event->string[i]);
+
+ /* All done */
+ return (TRUE);
+ }
+
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch ((uint) event->keyval)
+ {
+ case GDK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return (TRUE);
+ }
+
+ case GDK_Return:
+ {
+ Term_keypress('\r');
+ return (TRUE);
+ }
+
+ case GDK_Tab:
+ {
+ Term_keypress('\t');
+ return (TRUE);
+ }
+
+ case GDK_Delete:
+ case GDK_BackSpace:
+ {
+ Term_keypress('\010');
+ return (TRUE);
+ }
+
+ case GDK_Shift_L:
+ case GDK_Shift_R:
+ case GDK_Control_L:
+ case GDK_Control_R:
+ case GDK_Caps_Lock:
+ case GDK_Shift_Lock:
+ case GDK_Meta_L:
+ case GDK_Meta_R:
+ case GDK_Alt_L:
+ case GDK_Alt_R:
+ case GDK_Super_L:
+ case GDK_Super_R:
+ case GDK_Hyper_L:
+ case GDK_Hyper_R:
+ {
+ /* Hack - do nothing to control characters */
+ return (TRUE);
+ }
+ }
+
+ /* Build the macro trigger string */
+ strnfmt(msg, 128, "%c%s%s%s%s_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ event->keyval, 13);
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+ /* Hack -- auto-define macros as needed */
+ if (event->length && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, event->string);
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * 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 */
+ assert(angband_term_name[i] != NULL);
+ td->name = strdup(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;
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_gtk;
+ t->text_hook = Term_text_gtk;
+ t->curs_hook = Term_curs_gtk;
+ 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
+ },
+ { "/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 },
+
+
+ /* "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*) strdup(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*) strdup(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) free(term_entry[i].path);
+
+ /* XXX XXX Free Font menu path */
+ if (font_entry[i].path) free(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)
+{
+ bool_ save_ok, quit_ok;
+
+ /* 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 */
+ 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);
+}
+
+
+
+
+/*
+ * 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);
+
+}
+
+
+/*
+ * 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();
+
+
+ /* Terminate the program */
+ gtk_exit(0);
+}
+
+
+/*
+ * 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 */
+ quit_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;
+ }
+
+
+ /* None of the above */
+ fprintf(stderr, "Ignoring option: %s", argv[i]);
+ }
+
+
+ /* 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);
+
+ /* 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;
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_GTK2 */
diff --git a/src/main-sdl.c b/src/main-sdl.c
new file mode 100644
index 00000000..9a177cbb
--- /dev/null
+++ b/src/main-sdl.c
@@ -0,0 +1,2100 @@
+/* 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 "loadsave.h"
+#include "util.h"
+#include "variable.h"
+
+#include <SDL.h>
+#include <SDL_image.h>
+#include <SDL_ttf.h>
+
+#include <assert.h>
+#include <math.h>
+
+/*************************************************
+ 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;
+
+/**************/
+
+/* 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
+
+/**************/
+
+/* 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 const SDL_VideoInfo *videoInfo;
+
+/* a flag to suspend updating of the screen;
+this is in place so that when a large area is being
+redrawn -- like when doing a Term_redraw() or when
+redoing the entire screen -- all of the changes
+can be stored up before doing an update. This
+should cut down on screen flicker */
+static bool_ suspendUpdate = FALSE;
+
+/* some helper surfaces that are used for rendering
+characters */
+static SDL_Surface *worksurf;
+static SDL_Surface *crayon;
+
+/* the cursor surface */
+static SDL_Surface *cursor = NULL;
+
+/* the array of pre-rendered characters
+(see loadAndRenderFont() below) */
+SDL_Surface *text[128];
+
+/* the actual TTF_Font used (XXX should get rid of this)*/
+TTF_Font *font=0;
+
+/* the width and height of the uniformly-sized pre-rendered
+characters */
+int t_width = 0, t_height = 0;
+
+
+/*************************************************
+ COLOR SETUP
+ *************************************************/
+
+/* Simple black, mapped using the format of the main screen */
+int screen_black;
+
+/* The color to use for the cursor */
+static int cursor_color = 0;
+/* default cursor color is a semi-transparent yellow */
+#define DEF_CURSOR_COLOR 255,255,0,128
+
+/* The array of colors, mapped to the format of the crayon surface,
+since this is ultimately the surface that color is begin applied to */
+static int color_data[16];
+
+/* The following macro is for color defining...
+ Note that the color is fully opaque... */
+#define COLOR(r,g,b) \
+ SDL_MapRGBA(crayon->format,r,g,b,SDL_ALPHA_OPAQUE)
+
+/*These color macros will setup the colors to use, but must be called after
+ the SDL video has been set. That way SDL can correct for any funky video
+ setttings. */
+
+#define BLACK COLOR( 0, 0, 0) /* 0*/
+#define WHITE COLOR(255,255,255) /* 1*/
+#define MID_GREY COLOR(128,128,128) /* 2*/
+#define BRIGHT_ORANGE COLOR(255,128, 0) /* 3*/
+#define RED COLOR(192, 0, 0) /* 4*/
+#define GREEN COLOR( 0,128, 64) /* 5*/
+#define BRIGHT_BLUE COLOR( 0, 0,255) /* 6*/
+#define DARK_ORANGE COLOR(128, 64, 0) /* 7*/
+#define DARK_GREY COLOR( 64, 64, 64) /* 8*/
+#define BRIGHT_GREY COLOR(192,192,192) /* 9*/
+#define PURPLE COLOR(255, 0,255) /*10*/
+#define YELLOW COLOR(255,255, 0) /*11*/
+#define BRIGHT_RED COLOR(255, 0, 0) /*12*/
+#define BRIGHT_GREEN COLOR( 0,255, 0) /*13*/
+#define AQUAMARINE COLOR( 0,255,255) /*14*/
+#define BROWN COLOR(192,128, 64) /*15*/
+
+/*************************************************
+ TERMINAL DATA STRUCTURE SETUP
+ *************************************************/
+
+/* Forward declare */
+typedef struct _term_data term_data;
+
+/* A structure for each "term" */
+struct _term_data
+{
+ term t; /* the term structure, defined in z-term.h */
+ cptr name; /* name of this term sub-window */
+
+ uint rows, cols; /* row/column count */
+ SDL_Rect rect; /* the bounding rectangle for the entire box;
+ includes border and empty space as well */
+ /* this rectangle is in screen coordinates */
+
+ int border_thick; /* thickness of border to draw around window */
+ int border_color; /* current color of the border */
+ uint cushion_x_top, cushion_x_bot, cushion_y_top, cushion_y_bot;
+ /* empty space cushion between border and tiles */
+
+ uint tile_width; /* the width of each tile (graphic or otherwise)*/
+ uint tile_height; /* the height of each tile (graphic or otherwise)*/
+
+ SDL_Surface *surf; /* the surface that graphics for this screen are
+ rendered to before blitting to main screen */
+ int black,white,purple; /* basic colors keyed to this terminal's surface */
+
+};
+
+/* The array of term data structures */
+static term_data data[MAX_CONSOLE_COUNT];
+
+/* Ordered array of pointers to term data structures, placed in order of
+priority: lowest is on top of all others, the higher the index, the further
+back into the screen that it is drawn */
+static term_data *term_order[MAX_CONSOLE_COUNT];
+
+/*************************************************
+ FILE-SPECIFIC MACROS
+ *************************************************/
+
+/* Debug macros! */
+#define DB(str) \
+ printf("main-sdl: %s\n",str);
+
+/* Prints out the RGBA values of a given color */
+#define TYPECOLOR32(color) printf(" R:%d\tG:%d\tB:%d\tA:%d\t\n",\
+ color>>24,(color&0x00ff0000)>>16,\
+ (color&0x0000ff00)>>8,(color&0x000000ff))
+
+#define TYPECOLOR16(color) printf(" R:%d\tG:%d\tB:%d\tA:%d\t\n",\
+ (color&0xf000)>>12,(color&0x0f00)>>8,\
+ (color&0x00f0)>>4,(color&0x000f))
+
+/* SDL Surface locking and unlocking */
+#define SDL_LOCK(surf) \
+ if (SDL_MUSTLOCK(surf) ){ \
+ if (SDL_LockSurface(surf) < 0) { \
+ printf("Can't lock the screen: %s\n", SDL_GetError()); \
+ exit(1); \
+ } \
+ }
+
+#define SDL_UNLOCK(surf) \
+ if (SDL_MUSTLOCK(surf) ){ \
+ SDL_UnlockSurface(surf); \
+ }
+
+/* Wrapped SDL_UpdateRects function, to take into
+account the fact that updates may be suspended by
+the suspendUpdate flag... this macro should be used
+whenever a rect needs updated */
+#define SDL_UPDATE(rect) \
+ if (!suspendUpdate) \
+ SDL_UpdateRects(screen,1,&rect)
+
+/* A complete screen redraw macro */
+#define SDL_REDRAW_SCREEN \
+ SDL_UpdateRect(screen,0,0,arg_width,arg_height)
+
+/*************************************************
+ QUITTING
+ *************************************************/
+
+/* function prototype */
+void dumpWindowSettings(void);
+
+/* SDL Quitting function... declare a few functions first.*/
+void killFontAndAlphabet(void);
+static void sdl_quit(cptr string)
+{
+ printf("sdl_quit called.\n");
+ printf("message: %s\n",string);
+ /* Need to take care of font and rendered characters */
+ killFontAndAlphabet();
+ if (TTF_WasInit())
+ TTF_Quit();
+ /* Then exit SDL */
+ SDL_Quit();
+
+ /* Dump the window properties, if available */
+ if (window_properties_set)
+ dumpWindowSettings();
+
+ /* And now for the default quit behavior */
+ quit_aux = 0;
+ quit(string);
+}
+
+/*************************************************
+ FONT SUPPORT FUNCTIONS
+ *************************************************/
+
+/* function prototype for creating surfaces */
+SDL_Surface *createSurface(int width, int height);
+
+/* killFontAndAlphabet will effectively de-initialize the font system;
+it does this by closing the font and destroying any pre-rendered
+text in memory */
+void killFontAndAlphabet(void)
+{
+ int i;
+ /* need to close a font and free all of its corresponding pre-rendered
+ surfaces */
+ if (font)
+ {
+ TTF_CloseFont(font);
+ font = 0;
+ }
+ for (i=0;i<128;i++)
+ {
+ if(text[i])
+ {
+ SDL_FreeSurface(text[i]);
+ text[i] = NULL;
+ }
+ }
+}
+
+/* loadAndRenderFont is responsible for loading and initializing
+a font. First, SDL_ttf calls are made to load and set the style
+for the desired font. Next, a character alphabet is rendered and
+each character is placed onto a uniformly-sized surface within
+the text[] array. Whenever text is needed for displaying on-screen,
+this array is referenced and the desired character picture is used. */
+void loadAndRenderFont(char *fname, int size)
+{
+ int minx,maxx,miny,maxy,advance,i,midline = 0;
+ char filename[PATH_MAX + 1];
+ char fontdir[PATH_MAX + 1];
+ SDL_Color base_color = {255,255,255,255};
+ SDL_Surface *temp_surf;
+ SDL_Rect tgt = {0,0,0,0};
+
+
+ /* Assuming that the filename is valid,open the font */
+ path_build(fontdir, PATH_MAX, ANGBAND_DIR_XTRA, "font");
+ path_build(filename, PATH_MAX, fontdir, fname);
+ font = TTF_OpenFont(filename,size);
+ if (font == NULL)
+ sdl_quit("Error loading that font!");
+ /* Set the font style to normal */
+ TTF_SetFontStyle(font,TTF_STYLE_NORMAL);
+
+ /* Collect some measurements on this font -
+ arbitrarily choose the letter 'a' to get width*/
+ TTF_GlyphMetrics(font,'a',&minx,&maxx,&miny,&maxy,&advance);
+ /* the width of each character tile */
+ t_width = advance;
+ /* the height of each character tile */
+ t_height = TTF_FontHeight(font);
+ /* position of the y=0 line in each tile */
+ midline = TTF_FontAscent(font);
+
+ /* now... render each of the individual characters */
+ for (i=0;i<128;i++)
+ {
+ /* make a pretty blended glyph */
+ temp_surf=TTF_RenderGlyph_Blended(font,i,base_color);
+ /* and make sure that we got it right! */
+ if (temp_surf == NULL)
+ sdl_quit("Glyph failed to render!");
+ /* get the metrics of this particular glyph so we can position it */
+ TTF_GlyphMetrics(font,i,&minx,&maxx,&miny,&maxy,&advance);
+ /* copy rendered glyph into text queue, at the right position*/
+ tgt.x = minx;
+ tgt.y = midline-maxy;
+ /* but first... we'll need a surface in the text queue to blit to! */
+ text[i] = createSurface(t_width,t_height);
+ /* turn OFF src-alpha... results in brute
+ copy of the RGBA contents of surf */
+ SDL_SetAlpha(temp_surf,0,0);
+ SDL_BlitSurface(temp_surf,NULL,text[i],&tgt);
+ /* turn OFF src-alpha since we'll be using worksurf for blitting */
+ SDL_SetAlpha(text[i],0,0);
+ /* kill the surface to patch up memory leaks */
+ SDL_FreeSurface(temp_surf);
+ }
+}
+
+/* KEYPRESS_STRING repeatedly sends characters to the terminal
+XXX - should implement routine from maim-sdl.c, it's sooo much
+cleaner */
+#define KEYPRESS_STRING(str) \
+strcpy(buf,str); \
+n = buf; \
+while (*n != '\0') { \
+ Term_keypress((int)(*(n++))); \
+}
+
+/* function prototype */
+void manipulationMode(void);
+void redrawAllTerminals(void);
+/* This is the main event handling routine that will be called
+whenever an event is pulled off of the queue (in Term_xtra_sdl())*/
+void handleEvent(SDL_Event *event)
+{
+ static char buf[24]; /* a buffer used when passing key names */
+ char *n; /* and a pointer to manipulate this buffer */
+
+ switch( event->type )
+ {
+ case SDL_KEYDOWN:
+ {
+ /* handle key presses */
+
+ /* I'm reading that as long as the upper 9 bits of the unicode
+ * value are zero, then the lower 7 bits are direct ASCII characters.
+ * Furthermore, it seems that all basic keys return non-zero values
+ * for the lower 7 bits, but function keys and other various things
+ * return 0000000 for the lower 7 bits.
+ * Basically, if the lower 7 bits are zero, do something special
+ * (like start a macro), but otherwise just pass along the ASCII
+ * code!
+ */
+ byte ascii_part = event->key.keysym.unicode & 0x00ff;
+
+ /* gimme the key name */
+ printf("Key is: %s\n",SDL_GetKeyName(event->key.keysym.sym));
+
+ /* allow for full screen toggling! */
+ if ((event->key.keysym.sym == SDLK_RETURN) && \
+ (SDL_GetModState() & KMOD_ALT))
+ {
+ SDL_WM_ToggleFullScreen(screen);
+ /* toggle the internal full screen flag */
+ arg_full_screen = (arg_full_screen ? FALSE : TRUE);
+ }
+
+ /* entry into window manipulation mode */
+ if ((event->key.keysym.sym == SDLK_RETURN) && \
+ (SDL_GetModState() & KMOD_CTRL))
+ {
+ DB("Manipulation mode!");
+ manipulationMode();
+ }
+
+ /*printf("ascii_part: %d\n",ascii_part);*/
+ if (ascii_part)
+ {
+ /* We have now determined that the ASCII part is not '0', so
+ we can safely pass along the ASCII value! */
+ Term_keypress(ascii_part);
+ }
+ else
+ {
+ /* We want to ignore keypresses that are simply the modifier
+ keys*/
+ if (!( (event->key.keysym.sym == SDLK_RSHIFT) |
+ (event->key.keysym.sym == SDLK_LSHIFT) |
+ (event->key.keysym.sym == SDLK_RALT) |
+ (event->key.keysym.sym == SDLK_LALT) |
+ (event->key.keysym.sym == SDLK_RCTRL) |
+ (event->key.keysym.sym == SDLK_LCTRL) ))
+ {
+
+ /* now build a macro string using the modifiers together
+ with the key that was just pressed*/
+
+ /* As for the formatting...
+ * We pass the key press and modifiers as follows:
+ * \[ctrl-alt-shift-"key name"]
+ * following the previously established convention...
+ *
+ * All of the things that happen are defined in pref-sdl.prf
+ */
+
+ KEYPRESS_STRING("\["); /*Output the first part... */
+ /* See if a control key is down */
+ if (event->key.keysym.mod & KMOD_CTRL)
+ {
+ KEYPRESS_STRING("ctrl-");
+ }
+ /* See if an alt key is down */
+ if (event->key.keysym.mod & KMOD_ALT)
+ {
+ KEYPRESS_STRING("alt-");
+ }
+ /* See if a shift key is down */
+ if (event->key.keysym.mod & KMOD_SHIFT)
+ {
+ KEYPRESS_STRING("shift-");
+ }
+
+ /* Add in the name of whatever key was pressed */
+ KEYPRESS_STRING(SDL_GetKeyName(event->key.keysym.sym));
+
+ /* and end it... */
+ KEYPRESS_STRING("]");
+ }
+ }
+ break;
+ }
+ case SDL_QUIT:
+ {
+ /* handle quit requests */
+ DB("Emergency Blit");
+ redrawAllTerminals();
+ /*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_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.
+ */
+
+ /* If terminal display has been held for any reason,
+ then update the whole thing now!*/
+ DB("TERM_XTRA_FRESH");
+ if (suspendUpdate)
+ {
+ DB(" update WAS suspended... updating now");
+ td = (term_data*)(Term->data);
+ suspendUpdate = FALSE;
+ drawTermStuff(td,NULL);
+ }
+ return (0);
+ }
+
+ case TERM_XTRA_NOISE:
+ {
+ /*
+ * Make a noise XXX XXX XXX
+ *
+ * This action should produce a "beep" noise.
+ *
+ * This action is optional, but convenient.
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_BORED:
+ {
+ /* Perform event checking without blocking */
+ if (SDL_PollEvent(&event)){
+ /* We found an event! */
+ handleEvent(&event);
+ }
+ return(0);
+ }
+
+ case TERM_XTRA_REACT:
+ {
+ /*
+ * React to global changes XXX XXX XXX
+ *
+ * For example, this action can be used to react to
+ * changes in the global "color_table[256][4]" array.
+ *
+ * This action is optional, but can be very useful for
+ * handling "color changes" and the "arg_sound" and/or
+ * "arg_graphics" options.
+ */
+ return (1);
+ }
+
+ case TERM_XTRA_ALIVE:
+ {
+ /*
+ * Change the "hard" level XXX XXX XXX
+ *
+ * This action is used if the program changes "aliveness"
+ * by being either "suspended" (v=0) or "resumed" (v=1)
+ * This action is optional, unless the computer uses the
+ * same "physical screen" for multiple programs, in which
+ * case this action should clean up to let other programs
+ * use the screen, or resume from such a cleaned up state.
+ *
+ * This action is currently only used by "main-gcu.c",
+ * on UNIX machines, to allow proper "suspending".
+ */
+
+ return (1);
+ }
+
+ case TERM_XTRA_LEVEL:
+ {
+ /*
+ * Change the "soft" level XXX XXX XXX
+ *
+ * This action is used when the term window changes "activation"
+ * either by becoming "inactive" (v=0) or "active" (v=1)
+ *
+ * This action can be used to do things like activate the proper
+ * font / drawing mode for the newly active term window. This
+ * action should NOT change which window has the "focus", which
+ * window is "raised", or anything like that.
+ *
+ * This action is optional if all the other things which depend
+ * on what term is active handle activation themself, or if only
+ * one "term_data" structure is supported by this file.
+ */
+
+ return (1);
+ }
+
+ }
+
+ /* Unknown or Unhandled action */
+ return (1);
+}
+
+/*************************************************
+ GRAPHICS ROUTINES
+ *************************************************/
+
+/* wrapper routine for creating an RGB surface with given height and
+ width that corresponds to desired color depth and respects the system
+ pixel format */
+SDL_Surface *createSurface(int width, int height)
+{
+ SDL_Surface *surf;
+ int surface_type;
+
+ if (videoInfo->hw_available)
+ surface_type = SDL_HWSURFACE;
+ else
+ surface_type = SDL_SWSURFACE;
+
+ /* XXX need to make RGBA masks correspond to system pixel format! */
+ switch (arg_bpp)
+ {
+ case 8:
+ {
+ /* I really don't know if 8 bpp is even possible, but here it is */
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,8,0xc0,0x30,0x0c,0x03);
+ break;
+ }
+ case 16:
+ {
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,16,0xf000,0x0f00,0x00f0,0x000f);
+ break;
+ }
+ case 24:
+ {
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,24,0xfc0000,0x03f000,0x000fc0,0x000030);
+ break;
+ }
+ case 32:
+ {
+ surf = SDL_CreateRGBSurface(surface_type,width,\
+ height,32,0xff000000,0x00ff0000,0x0000ff00,0x000000ff);
+ break;
+ }
+ default:
+ {
+ surf = NULL;
+ break;
+ }
+ }
+
+ if (surf == NULL)
+ sdl_quit("Bad Surface Creation!");
+
+ return surf;
+}
+
+/* Take a rectangle in terminal coordinates and then transform it into
+screen coordinates; td is the term_data that this rect belongs to */
+void term_to_screen(SDL_Rect *termrect, term_data *td)
+{
+ termrect->x += td->rect.x;
+ termrect->y += td->rect.y;
+}
+
+/* Do the opposite, take a rectangle in screen coordinates and transform
+it into the terminal coordinates of the given term_data */
+void screen_to_term(SDL_Rect *scrrect, term_data *td)
+{
+ scrrect->x -= td->rect.x;
+ scrrect->y -= td->rect.y;
+}
+
+/* A macro that determines if the 'top' rectangle completely occludes the
+'bottom' rectangle */
+#define BLOCKS(top,bottom) \
+( (top.x <= bottom.x) & ((top.x+top.w)>=(bottom.x+bottom.w)) & \
+ (top.y <= bottom.y) & ((top.y+top.h)>=(bottom.y+bottom.h)) )
+
+#define INTERSECT(r1,r2) \
+!( ( (r1.x > (r2.x+r2.w)) | (r2.x > (r1.x+r1.w)) ) & \
+ ( (r1.y > (r2.y+r2.h)) | (r2.y > (r1.y+r1.h)) ) )
+
+/* A function to calculate the intersection of two rectangles. Takes base
+rectangle and then updates it to include only the rectangles that intersect
+with the test rectangle. If there is an intersection, the function returns
+TRUE and base now contains the intersecting rectangle. If there is no
+intersection, then the function returns FALSE */
+bool_ intersectRects(SDL_Rect *base, SDL_Rect *test)
+{
+ if (INTERSECT((*base),(*test)))
+ {
+ /* Scoot the x-coordinates for the left side*/
+ if ( test->x > base->x )
+ {
+ base->w -= test->x - base->x;
+ base->x = test->x;
+ }
+ /* Scoot the x-coordinates for the right side*/
+ if ( (test->x + test->w) < (base->x + base->w) )
+ {
+ base->w = test->x + test->w - base->x;
+ }
+ /* Scoot the upper y-coordinates */
+ if ( test->y > base->y )
+ {
+ base->h -= test->y - base->y;
+ base->y = test->y;
+ }
+ /* Scoot the lower y-coordinates */
+ if ( (test->y + test->h) < (base->y + base->h) )
+ {
+ base->h = test->y + test->h - base->y;
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/* A function to calculate the join of two rectangles; the first argument is
+changed to the joined rectangle */
+SDL_Rect joinRects(SDL_Rect *r1, SDL_Rect *r2)
+{
+ SDL_Rect out = {0,0,0,0};
+
+ if ( (r1 != NULL) & (r2 != NULL) )
+ {
+ /* Lower x-coordinate */
+ if ( r2->x < r1->x )
+ out.x = r2->x;
+ else
+ out.x = r1->x;
+ /* Upper x-coordinate */
+ if ( (r2->x+r2->w) > (r1->x+r1->w) )
+ out.w = (r2->x+r2->w) - out.x;
+ else
+ out.w = (r1->x+r1->w) - out.x;
+ /* Lower y-coordinate */
+ if ( r2->y < r1->y )
+ out.y = r2->y;
+ else
+ out.y = r1->y;
+ if ( (r2->y+r2->h) > (r1->y+r1->h) )
+ out.h = (r2->y+r2->h) - out.y;
+ else
+ out.h = (r1->y+r1->h) - out.y;
+ }
+ return out;
+}
+
+/* Given a term_data (and its associated screen) and a rectangle in terminal
+coordinates (with NULL signifying to take the whole terminal surface), blit
+graphics from the term_data surface to the screen, using the term_data's rect
+to indicate how terminal coordinates transform into screen coordinates.
+This is complicated, however, by the possibility that the indicated area may be
+occluded by overlaying terminals. In this case, if the target area is
+completely occluded, nothing will be done. If partially occluded, it will be
+drawn, but occluding terminals will then re-blit to re-cover the area. */
+void drawTermStuff(term_data *td, SDL_Rect *rect)
+{
+ int n = 0, i;
+ bool_ block = FALSE, cover = FALSE;
+ SDL_Rect spot, isect_term, isect_scr;
+
+ /* first of all, if updating is suspended, do nothing! */
+ if (!suspendUpdate)
+ {
+ /* find out which number in the ordered stack of screens that this
+ terminal is */
+ while ((term_order[n] != td) & (n < MAX_CONSOLE_COUNT))
+ {
+ n++;
+ }
+ if (n == MAX_CONSOLE_COUNT)
+ printf("Could not find terminal in display list...\n");
+ /* now loop through and see if any terminals completely occlude
+ the desired spot; if num=0, note that this will be skipped */
+ if (rect == NULL)
+ {
+ /* Grab the whole terminal screen */
+ spot.x = 0; spot.y = 0;
+ spot.w = td->surf->w; spot.h = td->surf->h;
+ }
+ else
+ {
+ /* Just copy the given area */
+ spot.x = rect->x; spot.y = rect->y;
+ spot.w = rect->w; spot.h = rect->h;
+ }
+ term_to_screen(&spot,td);
+ i = n;
+ while (i--)
+ {
+ if (BLOCKS(term_order[i]->rect,spot))
+ {
+ /* Higher terminal completely occludes this spot */
+ block = TRUE;
+ DB(" Blocks!");
+ }
+ else if (INTERSECT(term_order[i]->rect,spot))
+ {
+ /* Partial occlusion */
+ cover = TRUE;
+ DB(" Covers!");
+ }
+ }
+ /* If any of the higher terminals blocked, then don't do
+ anything */
+ if (!block)
+ {
+ /*printf("Blitting to %d %d %d %d\n",spot.x,spot.y,spot.w,spot.h);*/
+ /* First of all, draw the graphics */
+ SDL_BlitSurface(td->surf,rect,screen,&spot);
+ if (cover)
+ {
+ printf("covering...");
+ /* There are covering terminals, so go through and blit all
+ partially occluding ones */
+ while (n--)
+ {
+ /* copy spot to find the intersect */
+ isect_scr.x = spot.x; isect_scr.y = spot.y;
+ isect_scr.w = spot.w; isect_scr.h = spot.h;
+ if (intersectRects(&isect_scr,&(term_order[n]->rect)))
+ {
+ /* this terminal intersects... re-blit */
+ /* first, convert to term coordinates */
+ isect_term.x = isect_scr.x; isect_term.y = isect_scr.y;
+ isect_term.w = isect_scr.w; isect_term.h = isect_scr.h;
+ screen_to_term(&isect_term,term_order[n]);
+ /* blit from term coordinates to screen coordinates */
+ SDL_BlitSurface(term_order[n]->surf,&isect_term,\
+ screen,&isect_scr);
+ }
+ }
+ }
+ /* Now update what was drawn */
+ DB("Update");
+ SDL_UpdateRects(screen,1,&spot);
+ }
+ }
+}
+
+/* utility routine for creating and setting the color of the cursor;
+it could be useful for setting a new cursor color if desired.
+Could later be expanded to do other stuff with the cursor,
+like a hollow rectangle a la main-win.c or even a graphic */
+void createCursor(byte r, byte g, byte b, byte a)
+{
+ /* free the cursor if it exists */
+ if (cursor != NULL)
+ SDL_FreeSurface(cursor);
+
+ /* and create it anew! (or the first time) */
+ cursor = createSurface(t_width,t_height);
+
+ /* be sure to use alpha channel when blitting! */
+ SDL_SetAlpha(cursor,SDL_SRCALPHA,0);
+
+ /* just set the color for now - drawing rectangles
+ needs surface locking for some setups */
+ cursor_color = SDL_MapRGBA(cursor->format,r,g,b,a);
+ SDL_LOCK(cursor);
+ SDL_FillRect(cursor,NULL,cursor_color);
+ SDL_UNLOCK(cursor);
+}
+
+/* Cursor Display routine - just blits the global cursor
+surface onto the correct location */
+static errr Term_curs_sdl(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+ static SDL_Rect base;
+
+ /* calculate the position to place the cursor */
+ base.x = td->surf->clip_rect.x + x*t_width;
+ base.y = td->surf->clip_rect.y + y*t_height;
+ base.w = t_width;
+ base.h = t_height;
+
+ /* blit the cursor over top of the given spot;
+ note that surface should not be locked
+ (see note in Term_text_sdl() below) */
+ SDL_BlitSurface(cursor,NULL,td->surf,&base);
+
+ /* Now draw to the main screen */
+ drawTermStuff(td,&base);
+ /* Success */
+ return (0);
+}
+
+/* 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.
+ *
+ */
+static errr Term_text_sdl(int x, int y, int n, byte a, const char *cp)
+{
+ term_data *td = (term_data*)(Term->data);
+ static SDL_Rect base;
+ SDL_Rect base_back;
+ int i = n;
+ char old = 0;
+
+ /* calculate place to clear off and draw to */
+ base.x = td->surf->clip_rect.x + x*td->tile_width;
+ base.y = td->surf->clip_rect.y + y*td->tile_height;
+ base.w = n*td->tile_width;
+ base.h = td->tile_height;
+
+ base_back = base;
+
+ SDL_LOCK(screen);
+
+ /* blank the drawing area */
+ SDL_FillRect(td->surf, &base, td->black);
+
+ SDL_UNLOCK(screen);
+
+ /* Note that SDL docs specify that SDL_BlitSurface should not be called
+ on locked surfaces... since the character printing routine below revolves
+ around blitting, the surface has been unlocked first*/
+
+ /* loop through the input string, drawing characters */
+ i = n;
+ old = 0;
+ while (i--)
+ {
+ /* Output the character... */
+ /* If character has not changed, then just blit the old surface into
+ the new location to save effort*/
+ if (*cp == old)
+ {
+ /* the desired character/color combo is already on the work surf */
+ /* just blit it! */
+ SDL_BlitSurface(worksurf,NULL,td->surf,&base);
+ } else {
+ /* copy the desired character onto working surface */
+ assert(*cp >= 0); // Make sure cast is valid
+ SDL_BlitSurface(text[(size_t)(*cp)],NULL,worksurf,NULL);
+ /* color our crayon surface with the desired color */
+ SDL_FillRect(crayon,NULL,color_data[a&0x0f]);
+ /* apply the color to the character on the working surface */
+ SDL_BlitSurface(crayon,NULL,worksurf,NULL);
+ /* and blit it onto our screen! */
+ SDL_BlitSurface(worksurf,NULL,td->surf,&base);
+ }
+ /* Move to the next position */
+ base.x += t_width;
+ /* Store the old character */
+ old = *cp;
+ /* Increment the character pointer */
+ cp++;
+ }
+
+ /* And update */
+ drawTermStuff(td,&base_back);
+
+ /* Success */
+ return (0);
+}
+
+/*************************************************
+ SPECIAL TERMINAL WINDOW MANIPULATION ROUTINES
+ *************************************************/
+
+/* macro for bounding a value between two given values */
+#define BOUND(val,low,high) \
+if (val < low) \
+{ \
+ val = low; \
+} \
+else if (val > high) \
+{ \
+ val = high; \
+}
+
+/* two macros to get the adjusted maximums for window
+positions... eg the screen width minus the width of the
+window is the maximum x-position that the window can
+be set at. */
+#define MAX_X(td) \
+( arg_width - td->rect.w )
+
+#define MAX_Y(td) \
+( arg_height - td->rect.h )
+
+/* another two macros that give maximum window widths
+based on screen size and current window position together
+width tile widths/heights */
+#define MAX_WIDTH(td) \
+( (int)floorf((arg_width - td->rect.x - 2*td->border_thick - \
+ td->cushion_x_bot - td->cushion_x_top )/td->tile_width))
+
+#define MAX_HEIGHT(td) \
+( (int)floorf((arg_height - td->rect.y - 2*td->border_thick - \
+ td->cushion_y_bot - td->cushion_y_top )/td->tile_height))
+
+/* update the width and height of given term's rectangle by simply
+multiplying the tile count by tile size and adding in cushions and
+borders */
+#define UPDATE_SIZE(td) \
+ td->rect.w = (td->cols)*td->tile_width + td->cushion_x_top \
+ + td->cushion_x_bot + 2*td->border_thick; \
+ td->rect.h = (td->rows)*td->tile_height + td->cushion_y_top \
+ + td->cushion_y_bot + 2*td->border_thick
+
+void recompose(void);
+
+/* Resize the active terminal with new width and height.
+Note that his involves a complicated sequence of events...
+Details to follow below! */
+void resizeTerminal(int width, int height)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* First of all, bound the input width and height to satisfy
+ these conditions:
+ - The main ToME window should be at least 80 cols, 24 rows
+ - no part of each window should be drawn off screen....
+ I'm including borders in this restriction!
+ But no bounds checking needs to take place if the input width
+ and height are unchanged....
+ */
+
+ if (td == &data[0])
+ {
+ /* The active terminal is the main ToME window...
+ don't let the width get below 80, don't let the heights below
+ 24, and don't let it leak off of the edge! */
+ if (width != td->cols)
+ {
+ BOUND(width,80,MAX_WIDTH(td));
+ }
+ if (height != td->rows)
+ {
+ BOUND(height,24,MAX_HEIGHT(td));
+ }
+ }
+ else
+ {
+ /* This is not the main window... just make sure it
+ doesn't shrink to nothing or go past the edge */
+ if (width != td->cols)
+ {
+ BOUND(width,1,MAX_WIDTH(td));
+ }
+ if (height != td->rows)
+ {
+ BOUND(height,1,MAX_HEIGHT(td));
+ }
+ }
+
+ /* Okay, now make sure that something has ACTUALLY changed
+ before doing anything */
+ if ((width != td->cols) || (height != td->rows))
+ {
+
+ /* Now, ask zterm to please resize the term structure! */
+ Term_resize(width,height);
+
+ /* Reactivate, since Term_resize seems to activate the
+ main window again...*/
+ Term_activate(&td->t);
+
+ /* It might not have resized completely to the new
+ size we wanted (some windows have size limits it seems,
+ like the message window). So, update our structure with
+ the size that were actually obtained.*/
+ td->cols = Term->wid;
+ td->rows = Term->hgt;
+
+ /* And recalculate the sizes */
+ UPDATE_SIZE(td);
+
+ /* Create a new surface that can hold the updated size */
+ SDL_FreeSurface(td->surf);
+ td->surf = createSurface(td->rect.w,td->rect.h);
+
+ /* Now we should be in business for a complete redraw! */
+ Term_redraw();
+
+ /* Re-blit everything so it looks good */
+ recompose();
+
+ /* That's it! */
+ }
+}
+
+/* Move the terminal around... a much simpler action that involves
+just changing the pos_x/pos_y values and redrawing!*/
+void moveTerminal(int x, int y)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Now, the window is being shifted about... much simpler
+ situation to handle! But of course, the window must not
+ drift too far or else parts will be hanging off the screen
+ and may lead to errors - bound the input positions to
+ prevent this unfortunate situation... do nothing if the
+ input is no different than the current */
+ if (x != td->rect.x)
+ {
+ BOUND(x,0,MAX_X(td));
+ }
+ if (y != td->rect.y)
+ {
+ BOUND(y,0,MAX_Y(td));
+ }
+
+ /* Okay, now make sure that something changed before doing
+ anything */
+ if ((x != td->rect.x) || (y != td->rect.y))
+ {
+ /* Now update OUR structure */
+ td->rect.x = x;
+ td->rect.y = y;
+
+ /* Then do a reblit to see the results */
+ recompose();
+
+ /* That's it! */
+ }
+}
+
+/* Routine to bring a given term_data to the top of the drawing stack */
+void bringToTop(int current)
+{
+ term_data *td;
+ 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;
+
+ /* 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;
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_sdl;
+ t->curs_hook = Term_curs_sdl;
+ t->text_hook = Term_text_sdl;
+
+ /* Save the data */
+ t->data = td;
+
+ /* Activate (important) */
+ Term_activate(t);
+
+ /************* finish term_data intializing */
+
+ /* name of this term window */
+ td->name = angband_term_name[i];
+
+ /* For now, all font is the same size... use global t_width/height */
+ td->tile_width = t_width;
+ td->tile_height = t_height;
+
+ td->cushion_x_top = 1;
+ td->cushion_x_bot = 1;
+ td->cushion_y_top = 1;
+ td->cushion_y_bot = 1;
+
+ /* Now calculate the total width and height*/
+ UPDATE_SIZE(td);
+
+ /* Create a surface to draw to */
+ td->surf = createSurface(td->rect.w,td->rect.h);
+ SDL_SetAlpha(td->surf,0,0);
+
+ /* Key some colors to this surface */
+ td->black = SDL_MapRGB(td->surf->format, 0, 0, 0);
+ td->white = SDL_MapRGB(td->surf->format,255,255,255);
+ td->purple = SDL_MapRGB(td->surf->format,255, 0,255);
+
+ /* Turn on a border, thickness specified by BORDER_THICKNESS */
+ td->border_thick = BORDER_THICKNESS;
+
+ /* make the default terminal border color to be white */
+ td->border_color = td->white;
+
+
+ printf("Init-int term: %d\n",i);
+
+ /* Success */
+ return (0);
+}
+
+/* dumpWindowSettings is responsible for exporting all current
+values of the window positions, etc. to the screen, so that
+the user can see what the final values were after tweaking */
+void dumpWindowSettings(void)
+{
+ char name[80];
+ char value[8];
+ int i;
+
+ DB("Dumping settings");
+ printf("---------------------------\n");
+ /* cycle through each available terminal */
+ for (i=0; i<arg_console_count; i++)
+ {
+ printf("Terminal %d:\n",i);
+ /* get the name, and value of each value to dump */
+ sprintf(name,"TOME_X_POS_%d",i);
+ sprintf(value,"%d",data[i].rect.x);
+ printf("%s=%s\n",name,value);
+
+ sprintf(name,"TOME_Y_POS_%d",i);
+ sprintf(value,"%d",data[i].rect.y);
+ printf("%s=%s\n",name,value);
+
+ sprintf(name,"TOME_NUM_COLS_%d",i);
+ sprintf(value,"%d",data[i].cols);
+ printf("%s=%s\n",name,value);
+
+ sprintf(name,"TOME_NUM_ROWS_%d",i);
+ sprintf(value,"%d",data[i].rows);
+ printf("%s=%s\n",name,value);
+
+ /* Simple! */
+ printf("\n");
+ }
+}
+
+/* The main-sdl initialization routine!
+This routine processes arguments, opens the SDL
+window, loads fonts, etc. */
+errr init_sdl(int argc, char **argv)
+{
+ int i;
+ 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;
+ }
+ }
+ /* 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);
+
+ /* Initialize the working surface and crayon surface used for rendering
+ text in different colors. */
+
+ worksurf = createSurface(t_width,t_height);
+ crayon = createSurface(t_width,t_height);
+
+ /* The working surface will blit using alpha values... */
+ SDL_SetAlpha(worksurf,SDL_SRCALPHA,0);
+
+ /* Set up the colors using the great little color macros! */
+ color_data[0] = BLACK;
+ color_data[1] = WHITE;
+ color_data[2] = MID_GREY;
+ color_data[3] = BRIGHT_ORANGE;
+ color_data[4] = RED;
+ color_data[5] = GREEN;
+ color_data[6] = BRIGHT_BLUE;
+ color_data[7] = DARK_ORANGE;
+ color_data[8] = DARK_GREY;
+ color_data[9] = BRIGHT_GREY;
+ color_data[10] = PURPLE;
+ color_data[11] = YELLOW;
+ color_data[12] = BRIGHT_RED;
+ color_data[13] = BRIGHT_GREEN;
+ color_data[14] = AQUAMARINE;
+ color_data[15] = BROWN;
+
+ /* And setup the cursor, using the default color...
+ XXX - in the future, this should (and will) be loaded from prefs */
+ createCursor(DEF_CURSOR_COLOR);
+
+ /* Initialize the windows, or whatever that means in this case.
+ Do this in reverse order so that the main window is on top.*/
+ suspendUpdate = TRUE; /* draw everything at the end */
+ i = arg_console_count;
+ while (i--)
+ {
+ term_data *td = &data[i];
+
+ /* Initialize the term_data */
+ term_data_init(td, i);
+
+ /* Save global entry */
+ angband_term[i] = Term;
+
+ /* Add into term_order */
+ term_order[i] = td;
+
+ }
+
+ /* And setup the basic screen colors -- these are keyed to the format of
+ the main terminal surface */
+ screen_black = SDL_MapRGB(screen->format, 0, 0, 0);
+
+ suspendUpdate = FALSE; /* now draw everything */
+ redrawAllTerminals();
+ /*SDL_REDRAW_SCREEN;*/
+
+ /* now that the windows have been set, their settings can
+ be dumped upon quit! */
+ window_properties_set = TRUE;
+
+ /* Enable UNICODE keysyms - needed for current eventHandling routine */
+ SDL_EnableUNICODE(1);
+
+ /* Enable key repeat! */
+ SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);
+
+ /* main-sdl initialized! */
+ return 0;
+}
+
+#endif
diff --git a/src/main-win.c b/src/main-win.c
new file mode 100644
index 00000000..a2daffbe
--- /dev/null
+++ b/src/main-win.c
@@ -0,0 +1,3356 @@
+/* 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.
+ * In any case, some "*.fon" files (including "8X13.FON" if nothing
+ * else) must be placed into "lib/xtra/font/".
+ *
+ *
+ * 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"
+#include "dungeon.h"
+#include "files.h"
+#include "init2.h"
+#include "util.h"
+#include "variable.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
+#define IDM_FILE_SCORE 120
+#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_UNUSED 410
+#define IDM_OPTIONS_SAVER 411
+
+
+/*
+ * This may need to be removed for some compilers XXX XXX XXX
+ */
+#define STRICT
+
+/*
+ * Exclude parts of WINDOWS.H that are not needed
+ */
+#define NOCOMM /* Comm driver APIs and definitions */
+#define NOLOGERROR /* LogError() and related definitions */
+#define NOPROFILER /* Profiler APIs */
+#define NOLFILEIO /* _l* file I/O routines */
+#define NOOPENFILE /* OpenFile and related definitions */
+#define NORESOURCE /* Resource management */
+#define NOATOM /* Atom management */
+#define NOLANGUAGE /* Character test routines */
+#define NOLSTRING /* lstr* string management routines */
+#define NODBCS /* Double-byte character set routines */
+#define NOKEYBOARDINFO /* Keyboard driver routines */
+#define NOCOLOR /* COLOR_* color values */
+#define NODRAWTEXT /* DrawText() and related definitions */
+#define NOSCALABLEFONT /* Truetype scalable font support */
+#define NOMETAFILE /* Metafile support */
+#define NOSYSTEMPARAMSINFO /* SystemParametersInfo() and SPI_* definitions */
+#define NODEFERWINDOWPOS /* DeferWindowPos and related definitions */
+#define NOKEYSTATES /* MK_* message key state flags */
+#define NOWH /* SetWindowsHook and related WH_* definitions */
+#define NOCLIPBOARD /* Clipboard APIs and definitions */
+#define NOICONS /* IDI_* icon IDs */
+#define NOMDI /* MDI support */
+#define NOHELP /* Help support */
+
+/* Not defined since it breaks Borland C++ 5.5 */
+/* #define NOCTLMGR */ /* Control management and controls */
+
+/*
+ * Exclude parts of WINDOWS.H that are not needed (Win32)
+ */
+#define WIN32_LEAN_AND_MEAN
+#define NONLS /* All NLS defines and routines */
+#define NOSERVICE /* All Service Controller routines, SERVICE_ equates, etc. */
+#define NOKANJI /* Kanji support stuff. */
+#define NOMCX /* Modem Configuration Extensions */
+
+/*
+ * Include the "windows" support file
+ */
+#include <windows.h>
+
+
+/*
+ * Exclude parts of MMSYSTEM.H that are not needed
+ */
+#define MMNODRV /* Installable driver support */
+#define MMNOWAVE /* Waveform support */
+#define MMNOMIDI /* MIDI support */
+#define MMNOAUX /* Auxiliary audio support */
+#define MMNOJOY /* Joystick support */
+#define MMNOMCI /* MCI support */
+#define MMNOMMIO /* Multimedia file I/O support */
+#define MMNOMMSYSTEM /* General MMSYSTEM functions */
+
+/*
+ * Include some more files
+ */
+#include <mmsystem.h>
+#include <commdlg.h>
+
+/*
+ * Include the support for loading bitmaps
+ */
+
+/*
+ * 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 */
+
+
+
+
+
+
+/*
+ * 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_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];
+
+/*
+ * 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
+};
+
+
+/*
+ * 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 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 = strdup(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];
+
+ /* 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;
+
+
+ /* Size of palette */
+ pLogPalSize = sizeof(LOGPALETTE) + (nEntries + 16) * sizeof(PALETTEENTRY);
+
+ /* Allocate palette */
+ pLogPal = (LPLOGPALETTE) calloc(1, pLogPalSize);
+ if (pLogPal == NULL)
+ {
+ abort();
+ }
+
+ /* 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)
+ {
+ free(lppe);
+ lppe = NULL;
+ }
+
+ /* Create a new palette, or fail */
+ hNewPal = CreatePalette(pLogPal);
+ if (!hNewPal) quit("Cannot create palette!");
+
+ /* Free the palette */
+ free(pLogPal);
+ pLogPal = NULL;
+
+ /* 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);
+}
+
+
+
+/*
+ * 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 */
+ free(td->font_file);
+ 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 = strdup(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" ***/
+
+
+/*
+ * React to global changes
+ */
+static errr Term_xtra_win_react(void)
+{
+ int i;
+
+
+ /* Simple color */
+ if (colors16)
+ {
+ /* Save the default colors */
+ for (i = 0; i < 256; i++)
+ {
+ /* Simply accept the desired colors */
+ win_pal[i] = angband_color_table[i][0];
+ }
+ }
+
+ /* Complex color */
+ else
+ {
+ COLORREF code;
+
+ byte rv, gv, bv;
+
+ bool_ change = FALSE;
+
+ /* Save the default colors */
+ for (i = 0; i < 256; i++)
+ {
+ /* Extract desired values */
+ rv = angband_color_table[i][1];
+ gv = angband_color_table[i][2];
+ bv = angband_color_table[i][3];
+
+ /* Extract a full color code */
+ code = PALETTERGB(rv, gv, bv);
+
+ /* Activate changes */
+ if (win_clr[i] != code)
+ {
+ /* Note the change */
+ change = TRUE;
+
+ /* Apply the desired color */
+ win_clr[i] = code;
+ }
+ }
+
+ /* Activate the palette if needed */
+ if (change) (void)new_palette();
+ }
+
+
+
+
+
+
+ /* Clean up windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term *old = Term;
+
+ term_data *td = &data[i];
+
+ /* Update resized windows */
+ if ((td->cols != td->t.wid) || (td->rows != td->t.hgt))
+ {
+ /* Activate */
+ Term_activate(&td->t);
+
+ /* Hack -- Resize the term */
+ Term_resize(td->cols, td->rows);
+
+ /* Redraw the contents */
+ Term_redraw();
+
+ /* Restore */
+ Term_activate(old);
+ }
+ }
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Process at least one event
+ */
+static errr Term_xtra_win_event(int v)
+{
+ MSG msg;
+
+ /* Wait for an event */
+ if (v)
+ {
+ /* Block */
+ if (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ /* Check for an event */
+ else
+ {
+ /* Check */
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Process all pending events
+ */
+static errr Term_xtra_win_flush(void)
+{
+ MSG msg;
+
+ /* Process all pending events */
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Hack -- clear the screen
+ *
+ * Make this more efficient XXX XXX XXX
+ */
+static errr Term_xtra_win_clear(void)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ HDC hdc;
+ RECT rc;
+
+ /* Rectangle to erase */
+ rc.left = td->size_ow1;
+ rc.right = rc.left + td->cols * td->tile_wid;
+ rc.top = td->size_oh1;
+ rc.bottom = rc.top + td->rows * td->tile_hgt;
+
+ /* Erase it */
+ hdc = GetDC(td->w);
+ SetBkColor(hdc, RGB(0, 0, 0));
+ SelectObject(hdc, td->font_id);
+ ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
+ ReleaseDC(td->w, hdc);
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Hack -- make a noise
+ */
+static errr Term_xtra_win_noise(void)
+{
+ MessageBeep(MB_ICONASTERISK);
+ return (0);
+}
+
+
+/*
+ * 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());
+ }
+
+ /* Process random events */
+ case TERM_XTRA_BORED:
+ {
+ return (Term_xtra_win_event(0));
+ }
+
+ /* Process an event */
+ case TERM_XTRA_EVENT:
+ {
+ return (Term_xtra_win_event(v));
+ }
+
+ /* Flush all events */
+ case TERM_XTRA_FLUSH:
+ {
+ return (Term_xtra_win_flush());
+ }
+
+ /* Clear the screen */
+ case TERM_XTRA_CLEAR:
+ {
+ return (Term_xtra_win_clear());
+ }
+
+ /* React to global changes */
+ case TERM_XTRA_REACT:
+ {
+ return (Term_xtra_win_react());
+ }
+
+ /* Delay for some milliseconds */
+ case TERM_XTRA_DELAY:
+ {
+ return (Term_xtra_win_delay(v));
+ }
+
+ /* 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;
+
+ /* 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.
+ *
+ * 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;
+}
+
+
+
+/*** 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;
+
+ /* Prepare the template hooks */
+ t->xtra_hook = Term_xtra_win;
+ t->curs_hook = Term_curs_win;
+ t->text_hook = Term_text_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];
+ memset(td, 0, sizeof(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];
+ memset(td, 0, sizeof(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);
+ EnableMenuItem(hm, IDM_FILE_SCORE,
+ MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
+ 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);
+ }
+
+ /* Menu "File", Item "Score" */
+ if (initialized && character_generated && !character_icky)
+ {
+ EnableMenuItem(hm, IDM_FILE_SCORE,
+ MF_BYCOMMAND | MF_ENABLED);
+ }
+
+ /* 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_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_UNUSED,
+ (0 ? MF_CHECKED : MF_UNCHECKED));
+ CheckMenuItem(hm, IDM_OPTIONS_SAVER,
+ (hwndSaver ? MF_CHECKED : MF_UNCHECKED));
+
+
+
+#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;
+ }
+
+ /* Score */
+ case IDM_FILE_SCORE:
+ {
+ predict_score_gui(&initialized, &game_in_progress);
+
+ /* Done */
+ break;
+ }
+
+ 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_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
+
+}
+
+
+
+LRESULT FAR PASCAL AngbandWndProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+{
+ 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);
+}
+
+
+LRESULT FAR PASCAL AngbandListProc(HWND hWnd, UINT uMsg,
+ WPARAM wParam, LPARAM lParam)
+{
+ 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
+
+LRESULT FAR PASCAL AngbandSaverProc(HWND hWnd, UINT uMsg,
+WPARAM wParam, LPARAM lParam)
+{
+ 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;
+ }
+
+ 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)
+ {
+ free(data[i].font_want);
+ data[i].font_want = NULL;
+ }
+ 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 = strdup(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 = strdup(path);
+
+
+ /*** Validate the paths to ensure we have a working install ***/
+
+ 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);
+
+
+
+
+
+
+ /* Build the "help" path */
+ path_build(path, 1024, ANGBAND_DIR_XTRA, "help");
+
+ /* Allocate the path */
+ ANGBAND_DIR_XTRA_HELP = strdup(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;
+
+ /* 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..b4b242e5
--- /dev/null
+++ b/src/main-x11.c
@@ -0,0 +1,2588 @@
+/* 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 "loadsave.h"
+#include "util.h"
+#include "variable.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>
+
+
+/*
+ * This file is designed to be "included" by "main-x11.c" or "main-xaw.c",
+ * which will have already "included" several relevant header files.
+ */
+
+#ifndef IsModifierKey
+
+/*
+ * Keysym macros, used on Keysyms to test for classes of symbols
+ * These were stolen from one of the X11 header files
+ *
+ * Also appears in "main-x11.c".
+ */
+
+#define IsKeypadKey(keysym) \
+(((unsigned)(keysym) >= XK_KP_Space) && ((unsigned)(keysym) <= XK_KP_Equal))
+
+#define IsCursorKey(keysym) \
+(((unsigned)(keysym) >= XK_Home) && ((unsigned)(keysym) < XK_Select))
+
+#define IsPFKey(keysym) \
+(((unsigned)(keysym) >= XK_KP_F1) && ((unsigned)(keysym) <= XK_KP_F4))
+
+#define IsFunctionKey(keysym) \
+(((unsigned)(keysym) >= XK_F1) && ((unsigned)(keysym) <= XK_F35))
+
+#define IsMiscFunctionKey(keysym) \
+(((unsigned)(keysym) >= XK_Select) && ((unsigned)(keysym) < XK_KP_Space))
+
+#define IsModifierKey(keysym) \
+(((unsigned)(keysym) >= XK_Shift_L) && ((unsigned)(keysym) <= XK_Hyper_R))
+
+#endif /* IsModifierKey */
+
+
+/*
+ * Checks if the keysym is a special key or a normal key
+ * Assume that XK_MISCELLANY keysyms are special
+ *
+ * Also appears in "main-x11.c".
+ */
+#define IsSpecialKey(keysym) \
+((unsigned)(keysym) >= 0xFF00)
+
+
+/*
+ * Hack -- Convert an RGB value to an X11 Pixel, or die.
+ *
+ * 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).
+ */
+static unsigned long create_pixel(Display *dpy, byte red, byte green, byte blue)
+{
+ Colormap cmap = DefaultColormapOfScreen(DefaultScreenOfDisplay(dpy));
+
+ char cname[8];
+
+ XColor xcolour;
+
+ /* Build the color */
+
+ xcolour.red = red * 255 + red;
+ xcolour.green = green * 255 + green;
+ xcolour.blue = blue * 255 + blue;
+ xcolour.flags = DoRed | DoGreen | DoBlue;
+
+ /* Attempt to Allocate the Parsed color */
+ if (!(XAllocColor(dpy, cmap, &xcolour)))
+ {
+ quit_fmt("Couldn't allocate bitmap color '%s'\n", cname);
+ }
+
+ return (xcolour.pixel);
+}
+
+
+
+
+/*
+ * 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_ppn(F,B,O,M) \
+Infoclr_init_ppo(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);
+}
+
+
+/*
+ * 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);
+}
+
+
+/*
+ * 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);
+}
+
+
+/*
+ * 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 */
+ memset(Infowin, 0, sizeof(struct 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);
+}
+
+
+/*
+ * Request that Infowin be raised
+ */
+static errr Infowin_raise(void)
+{
+ /* Raise towards visibility */
+ XRaiseWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * 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);
+}
+
+
+/*
+ * Visually clear Infowin
+ */
+static errr Infowin_wipe(void)
+{
+ /* Execute the request */
+ XClearWindow(Metadpy->dpy, Infowin->win);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * 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);
+}
+
+
+/*
+ * 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 */
+ memset(iclr, 0, sizeof(struct 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);
+}
+
+
+
+/*
+ * 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;
+ ifnt->twid = ifnt->wid;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * 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 */
+ memset(Infofnt, 0, sizeof(struct 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 = strdup(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;
+
+
+};
+
+
+/*
+ * The number of term data structures
+ */
+#define MAX_TERM_DATA 8
+
+/*
+ * The array of term data structures
+ */
+static term_data data[MAX_TERM_DATA];
+
+/* Use short names for the most commonly used elements of various structures. */
+#define DPY (Metadpy->dpy)
+#define WIN (Infowin->win)
+
+/*
+ * Simply push a set of co-ordinates around.
+ */
+typedef struct co_ord co_ord;
+struct co_ord
+{
+ int x;
+ int y;
+};
+
+/*
+ * A special structure to store information about the text currently
+ * selected.
+ */
+typedef struct x11_selection_type x11_selection_type;
+struct x11_selection_type
+{
+ bool_ select; /* The selection is currently in use. */
+ bool_ drawn; /* The selection is currently displayed. */
+ term *t; /* The window where the selection is found. */
+ co_ord init; /* The starting co-ordinates. */
+ co_ord cur; /* The end co-ordinates (the current ones if still copying). */
+ co_ord old; /* The previous end co-ordinates. */
+ Time time; /* The time at which the selection was finalised. */
+};
+
+static x11_selection_type s_ptr[1];
+
+
+
+/*
+ * Process a keypress event
+ *
+ * Also appears in "main-xaw.c".
+ */
+static void react_keypress(XKeyEvent *xev)
+{
+ int i, n, mc, ms, mo, mx;
+
+ uint ks1;
+
+ XKeyEvent *ev = (XKeyEvent*)(xev);
+
+ KeySym ks;
+
+ char buf[128];
+ char msg[128];
+
+
+ /* Check for "normal" keypresses */
+ n = XLookupString(ev, buf, 125, &ks, NULL);
+
+ /* Terminate */
+ buf[n] = '\0';
+
+
+ /* Hack -- Ignore "modifier keys" */
+ if (IsModifierKey(ks)) return;
+
+
+ /* Hack -- convert into an unsigned int */
+ ks1 = (uint)(ks);
+
+ /* Extract four "modifier flags" */
+ mc = (ev->state & ControlMask) ? TRUE : FALSE;
+ ms = (ev->state & ShiftMask) ? TRUE : FALSE;
+ mo = (ev->state & Mod1Mask) ? TRUE : FALSE;
+ mx = (ev->state & Mod2Mask) ? TRUE : FALSE;
+
+
+ /* Normal keys with no modifiers */
+ if (n && !mo && !mx && !IsSpecialKey(ks))
+ {
+ /* Enqueue the normal key(s) */
+ for (i = 0; buf[i]; i++) Term_keypress(buf[i]);
+
+ /* All done */
+ return;
+ }
+
+
+ /* Handle a few standard keys (bypass modifiers) XXX XXX XXX */
+ switch (ks1)
+ {
+ case XK_Escape:
+ {
+ Term_keypress(ESCAPE);
+ return;
+ }
+
+ case XK_Return:
+ {
+ Term_keypress('\r');
+ return;
+ }
+
+ case XK_Tab:
+ {
+ Term_keypress('\t');
+ return;
+ }
+
+ case XK_Delete:
+ case XK_BackSpace:
+ {
+ Term_keypress('\010');
+ return;
+ }
+ }
+
+
+ /* Hack -- Use the KeySym */
+ if (ks)
+ {
+ sprintf(msg, "%c%s%s%s%s_%lX%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ (unsigned long)(ks), 13);
+ }
+
+ /* Hack -- Use the Keycode */
+ else
+ {
+ sprintf(msg, "%c%s%s%s%sK_%X%c", 31,
+ mc ? "N" : "", ms ? "S" : "",
+ mo ? "O" : "", mx ? "M" : "",
+ ev->keycode, 13);
+ }
+
+ /* Enqueue the "macro trigger" string */
+ for (i = 0; msg[i]; i++) Term_keypress(msg[i]);
+
+
+ /* Hack -- auto-define macros as needed */
+ if (n && (macro_find_exact(msg) < 0))
+ {
+ /* Create a macro */
+ macro_add(msg, buf);
+ }
+}
+
+
+/*
+ * Find the square a particular pixel is part of.
+ */
+static void pixel_to_square(int * const x, int * const y,
+ const int ox, const int oy)
+{
+ (*x) = (ox - Infowin->ox) / Infofnt->wid;
+ (*y) = (oy - Infowin->oy) / Infofnt->hgt;
+}
+
+/*
+ * Find the pixel at the top-left corner of a square.
+ */
+static void square_to_pixel(int * const x, int * const y,
+ const int ox, const int oy)
+{
+ (*x) = ox * Infofnt->wid + Infowin->ox;
+ (*y) = oy * Infofnt->hgt + Infowin->oy;
+}
+
+/*
+ * Convert co-ordinates from starting corner/opposite corner to minimum/maximum.
+ */
+static void sort_co_ord(co_ord *min, co_ord *max,
+ const co_ord *b, const co_ord *a)
+{
+ min->x = MIN(a->x, b->x);
+ min->y = MIN(a->y, b->y);
+ max->x = MAX(a->x, b->x);
+ max->y = MAX(a->y, b->y);
+}
+
+/*
+ * Remove the selection by redrawing it.
+ */
+static void mark_selection_clear(int x1, int y1, int x2, int y2)
+{
+ Term_redraw_section(x1, y1, x2, y2);
+}
+
+/*
+ * Select an area by drawing a grey box around it.
+ * NB. These two functions can cause flicker as the selection is modified,
+ * as the game redraws the entire marked section.
+ */
+static void mark_selection_mark(int x1, int y1, int x2, int y2)
+{
+ square_to_pixel(&x1, &y1, x1, y1);
+ square_to_pixel(&x2, &y2, x2, y2);
+ XDrawRectangle(Metadpy->dpy, Infowin->win, clr[2]->gc, x1, y1,
+ x2 - x1 + Infofnt->wid - 1, y2 - y1 + Infofnt->hgt - 1);
+}
+
+/*
+ * Mark a selection by drawing boxes around it (for now).
+ */
+static void mark_selection(void)
+{
+ co_ord min, max;
+ term *old = Term;
+ bool_ draw = s_ptr->select;
+ bool_ clear = s_ptr->drawn;
+
+ /* Open the correct term if necessary. */
+ if (s_ptr->t != old) Term_activate(s_ptr->t);
+
+ if (clear)
+ {
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->old);
+ mark_selection_clear(min.x, min.y, max.x, max.y);
+ }
+ if (draw)
+ {
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
+ mark_selection_mark(min.x, min.y, max.x, max.y);
+ }
+
+ /* Finish on the current term. */
+ if (s_ptr->t != old) Term_activate(old);
+
+ s_ptr->old.x = s_ptr->cur.x;
+ s_ptr->old.y = s_ptr->cur.y;
+ s_ptr->drawn = s_ptr->select;
+}
+
+/*
+ * Forget a selection for one reason or another.
+ */
+static void copy_x11_release(void)
+{
+ /* Deselect the current selection. */
+ s_ptr->select = FALSE;
+
+ /* Remove its graphical represesntation. */
+ mark_selection();
+}
+
+/*
+ * Start to select some text on the screen.
+ */
+static void copy_x11_start(int x, int y)
+{
+ if (s_ptr->select) copy_x11_release();
+
+ /* Remember where the selection started. */
+ s_ptr->t = Term;
+ s_ptr->init.x = s_ptr->cur.x = s_ptr->old.x = x;
+ s_ptr->init.y = s_ptr->cur.y = s_ptr->old.y = y;
+}
+
+/*
+ * Respond to movement of the mouse when selecting text.
+ */
+static void copy_x11_cont(int x, int y, unsigned int buttons)
+{
+ /* Use the nearest square within bounds if the mouse is outside. */
+ x = MIN(MAX(x, 0), Term->wid - 1);
+ y = MIN(MAX(y, 0), Term->hgt - 1);
+
+ /* The left mouse button isn't pressed. */
+ if (~buttons & Button1Mask) return;
+
+ /* Not a selection in this window. */
+ if (s_ptr->t != Term) return;
+
+ /* Not enough movement. */
+ if (x == s_ptr->old.x && y == s_ptr->old.y && s_ptr->select) return;
+
+ /* Something is being selected. */
+ s_ptr->select = TRUE;
+
+ /* Track the selection. */
+ s_ptr->cur.x = x;
+ s_ptr->cur.y = y;
+
+ /* Hack - display it inefficiently. */
+ mark_selection();
+}
+
+/*
+ * Respond to release of the left mouse button by putting the selected text in
+ * the primary buffer.
+ */
+static void copy_x11_end(const Time time)
+{
+ /* No selection. */
+ if (!s_ptr->select) return;
+
+ /* Not a selection in this window. */
+ if (s_ptr->t != Term) return;
+
+ /* Remember when the selection was finalised. */
+ s_ptr->time = time;
+
+ /* Acquire the primary selection. */
+ XSetSelectionOwner(Metadpy->dpy, XA_PRIMARY, Infowin->win, time);
+ if (XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY) != Infowin->win)
+ {
+ /* Failed to acquire the selection, so forget it. */
+ bell();
+ s_ptr->select = FALSE;
+ mark_selection();
+ }
+}
+
+/*
+ * Send a message to request that the PRIMARY buffer be sent here.
+ */
+static void paste_x11_request(const Time time)
+{
+ XEvent event[1];
+ XSelectionRequestEvent *ptr = &(event->xselectionrequest);
+
+ /* Set various things. */
+ ptr->type = SelectionRequest;
+ ptr->display = Metadpy->dpy;
+ ptr->owner = XGetSelectionOwner(Metadpy->dpy, XA_PRIMARY);
+ ptr->requestor = Infowin->win;
+ ptr->selection = XA_PRIMARY;
+ ptr->target = XA_STRING;
+ ptr->property = XA_STRING; /* Unused */
+ ptr->time = time;
+
+ /* Check the owner. */
+ if (ptr->owner == None)
+ {
+ /* No selection. */
+ bell();
+ return;
+ }
+
+ /* Send the SelectionRequest event. */
+ XSendEvent(Metadpy->dpy, ptr->owner, False, NoEventMask, event);
+}
+
+/*
+ * Add a character to a string in preparation for sending it to another
+ * client as a STRING.
+ * This doesn't change anything, as clients tend not to have difficulty in
+ * receiving this format (although the standard specifies a restricted set).
+ * Strings do not have a colour.
+ */
+static int add_char_string(char *buf, byte a, char c)
+{
+ *buf = c;
+ return 1;
+}
+
+/*
+ * Send some text requested by another X client.
+ */
+static void paste_x11_send(XSelectionRequestEvent *rq)
+{
+ XEvent event;
+ XSelectionEvent *ptr = &(event.xselection);
+ int (*add)(char *, byte, char) = 0;
+
+ /* Set the event parameters. */
+ ptr->type = SelectionNotify;
+ ptr->property = rq->property;
+ ptr->display = rq->display;
+ ptr->requestor = rq->requestor;
+ ptr->selection = rq->selection;
+ ptr->target = rq->target;
+ ptr->time = rq->time;
+
+ /* Determine the correct "add a character" function.
+ * As Term->wid is at most 255, these can add up to 4 characters of
+ * output per character of input without problem.
+ * The mechanism will need to change if much more than this is needed.
+ */
+ switch (rq->target)
+ {
+ case XA_STRING:
+ add = add_char_string;
+ break;
+ default:
+ goto error;
+ }
+
+ /* Reply to a known target received recently with data. */
+ if (rq->time >= s_ptr->time && add)
+ {
+ char buf[1024];
+ co_ord max, min;
+ int x, y, l;
+ byte a;
+ char c;
+
+ /* Work out which way around to paste. */
+ sort_co_ord(&min, &max, &s_ptr->init, &s_ptr->cur);
+
+ /* Paranoia. */
+ if (XGetSelectionOwner(DPY, XA_PRIMARY) != WIN)
+ {
+ bell();
+ goto error;
+ }
+
+ /* Delete the old value of the property. */
+ XDeleteProperty(DPY, rq->requestor, rq->property);
+
+ for (y = 0; y < Term->hgt; y++)
+ {
+ if (y < min.y) continue;
+ if (y > max.y) break;
+
+ for (x = l = 0; x < Term->wid; x++)
+ {
+ if (x < min.x) continue;
+ if (x > max.x) break;
+
+ /* Find the character. */
+ Term_what(x, y, &a, &c);
+
+ /* Add it. */
+ l += (*add)(buf + l, a, c);
+ }
+
+ /* Terminate all but the last line in an appropriate way. */
+ if (y != max.y) l += (*add)(buf + l, TERM_WHITE, '\n');
+
+ /* Send the (non-empty) string. */
+ XChangeProperty(DPY, rq->requestor, rq->property, rq->target, 8,
+ PropModeAppend, (unsigned char*)buf, l);
+ }
+ }
+ else
+ {
+ /* Respond to all bad requests with property None. */
+error:
+ ptr->property = None;
+ }
+
+ /* Send whatever event we're left with. */
+ XSendEvent(DPY, rq->requestor, FALSE, NoEventMask, &event);
+}
+
+extern errr type_string(char *str, uint len);
+
+/*
+ * Add the contents of the PRIMARY buffer to the input queue.
+ *
+ * Hack - This doesn't use the "time" of the event, and so accepts anything a
+ * client tries to send it.
+ */
+static void paste_x11_accept(const XSelectionEvent *ptr)
+{
+ long offset;
+ unsigned long left;
+
+ /* Failure. */
+ if (ptr->property == None)
+ {
+ bell();
+ return;
+ }
+
+ if (ptr->selection != XA_PRIMARY)
+ {
+ bell();
+ return;
+ }
+ if (ptr->target != XA_STRING)
+ {
+ bell();
+ return;
+ }
+
+ for (offset = 0; ; offset += left)
+ {
+ errr err;
+
+ /* A pointer for the pasted information. */
+ unsigned char *data;
+
+ Atom type;
+ int fmt;
+ unsigned long nitems;
+
+ /* Set data to the string, and catch errors. */
+ if (XGetWindowProperty(Metadpy->dpy, Infowin->win, XA_STRING, offset,
+ 32767, TRUE, XA_STRING, &type, &fmt, &nitems, &left, &data)
+ != Success) break;
+
+ /* Paste the text. */
+ err = type_string((char*)data, (uint)nitems);
+
+ /* Free the data pasted. */
+ XFree(data);
+
+ /* No room. */
+ if (err == 7)
+ {
+ bell();
+ break;
+ }
+ /* Paranoia? - strange errors. */
+ else if (err)
+ {
+ break;
+ }
+
+ /* Pasted everything. */
+ if (!left) return;
+ }
+
+ /* An error has occurred, so free the last bit of data before returning. */
+ XFree(data);
+}
+
+/*
+ * Handle various events conditional on presses of a mouse button.
+ */
+static void handle_button(Time time, int x, int y, int button,
+ bool_ press)
+{
+ /* The co-ordinates are only used in Angband format. */
+ pixel_to_square(&x, &y, x, y);
+
+ if (press && button == 1) copy_x11_start(x, y);
+ if (!press && button == 1) copy_x11_end(time);
+ if (!press && button == 2) paste_x11_request(time);
+}
+
+
+/*
+ * Process events
+ */
+static errr CheckEvent(bool_ wait)
+{
+ term_data *old_td = (term_data*)(Term->data);
+
+ XEvent xev_body, *xev = &xev_body;
+
+ term_data *td = NULL;
+ infowin *iwin = NULL;
+
+ int i;
+
+
+ /* Do not wait unless requested */
+ if (!wait && !XPending(Metadpy->dpy)) return (1);
+
+ /* Hack - redraw the selection, if needed.
+ * This doesn't actually check that one of its squares was drawn to,
+ * only that this may have happened.
+ */
+ if (s_ptr->select && !s_ptr->drawn) mark_selection();
+
+ /* Load the Event */
+ XNextEvent(Metadpy->dpy, xev);
+
+
+ /* Notice new keymaps */
+ if (xev->type == MappingNotify)
+ {
+ XRefreshKeyboardMapping(&xev->xmapping);
+ return 0;
+ }
+
+
+ /* Scan the windows */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ if (xev->xany.window == data[i].win->win)
+ {
+ td = &data[i];
+ iwin = td->win;
+ break;
+ }
+ }
+
+ /* Unknown window */
+ if (!td || !iwin) return (0);
+
+
+ /* Hack -- activate the Term */
+ Term_activate(&td->t);
+
+ /* Hack -- activate the window */
+ Infowin_set(iwin);
+
+
+ /* Switch on the Type */
+ switch (xev->type)
+ {
+
+ case ButtonPress:
+ case ButtonRelease:
+ {
+ bool_ press = (xev->type == ButtonPress);
+
+ /* Where is the mouse */
+ int x = xev->xbutton.x;
+ int y = xev->xbutton.y;
+
+ int z;
+
+ /* Which button is involved */
+ if (xev->xbutton.button == Button1) z = 1;
+ else if (xev->xbutton.button == Button2) z = 2;
+ else if (xev->xbutton.button == Button3) z = 3;
+ else if (xev->xbutton.button == Button4) z = 4;
+ else if (xev->xbutton.button == Button5) z = 5;
+ else z = 0;
+
+ /* Where is the mouse */
+ x = xev->xbutton.x;
+ y = xev->xbutton.y;
+
+ /* XXX Handle */
+ handle_button(xev->xbutton.time, x, y, z, press);
+
+ break;
+ }
+
+ case EnterNotify:
+ case LeaveNotify:
+ {
+ /* Where is the mouse */
+ /* XXX Handle */
+
+ break;
+ }
+
+ case MotionNotify:
+ {
+ int x = xev->xmotion.x;
+ int y = xev->xmotion.y;
+ unsigned int z = xev->xmotion.state;
+
+ /* Convert to co-ordinates Angband understands. */
+ pixel_to_square(&x, &y, x, y);
+
+ /* Alter the selection if appropriate. */
+ copy_x11_cont(x, y, z);
+
+ /* XXX Handle */
+
+ break;
+ }
+
+ case SelectionNotify:
+ {
+ paste_x11_accept(&(xev->xselection));
+ break;
+ }
+
+ case SelectionRequest:
+ {
+ paste_x11_send(&(xev->xselectionrequest));
+ break;
+ }
+
+ case SelectionClear:
+ {
+ s_ptr->select = FALSE;
+ mark_selection();
+ break;
+ }
+
+ case KeyRelease:
+ {
+ /* Nothing */
+ break;
+ }
+
+ case KeyPress:
+ {
+ /* Hack -- use "old" term */
+ Term_activate(&old_td->t);
+
+ /* Process the key */
+ react_keypress(&(xev->xkey));
+
+ break;
+ }
+
+ case Expose:
+ {
+ /* Ignore "extra" exposes */
+ if (xev->xexpose.count) break;
+
+ /* Clear the window */
+ Infowin_wipe();
+
+ /* Redraw */
+ Term_redraw();
+
+ break;
+ }
+
+ case MapNotify:
+ {
+ Infowin->mapped = 1;
+ Term->mapped_flag = TRUE;
+ break;
+ }
+
+ case UnmapNotify:
+ {
+ Infowin->mapped = 0;
+ Term->mapped_flag = FALSE;
+ break;
+ }
+
+ /* Move and/or Resize */
+ case ConfigureNotify:
+ {
+ int cols, rows;
+
+ int ox = Infowin->ox;
+ int oy = Infowin->oy;
+
+ /* Save the new Window Parms */
+ Infowin->x = xev->xconfigure.x;
+ Infowin->y = xev->xconfigure.y;
+ Infowin->w = xev->xconfigure.width;
+ Infowin->h = xev->xconfigure.height;
+
+ /* Determine "proper" number of rows/cols */
+ cols = ((Infowin->w - (ox + ox)) / td->fnt->wid);
+ rows = ((Infowin->h - (oy + oy)) / td->fnt->hgt);
+
+ /* Hack -- minimal size */
+ if (td == &data[0])
+ {
+ if (cols < 80) cols = 80;
+ if (rows < 24) rows = 24;
+ }
+
+ else
+ {
+ if (cols < 1) cols = 1;
+ if (rows < 1) rows = 1;
+ }
+
+ /* Paranoia */
+ if (cols > 255) cols = 255;
+ if (rows > 255) rows = 255;
+
+ /* Resize the Term (if needed) */
+ Term_resize(cols, rows);
+ break;
+ }
+ }
+
+
+ /* Hack -- Activate the old term */
+ Term_activate(&old_td->t);
+
+ /* Hack -- Activate the proper window */
+ Infowin_set(old_td->win);
+
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle "activation" of a term
+ */
+static errr Term_xtra_x11_level(int v)
+{
+ term_data *td = (term_data*)(Term->data);
+
+ /* Handle "activate" */
+ if (v)
+ {
+ /* Activate the window */
+ Infowin_set(td->win);
+
+ /* Activate the font */
+ Infofnt_set(td->fnt);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_x11_react(void)
+{
+ int i;
+
+ if (Metadpy->color)
+ {
+ /* Check the colors */
+ for (i = 0; i < 256; i++)
+ {
+ if ((color_table[i][0] != angband_color_table[i][0]) ||
+ (color_table[i][1] != angband_color_table[i][1]) ||
+ (color_table[i][2] != angband_color_table[i][2]) ||
+ (color_table[i][3] != angband_color_table[i][3]))
+ {
+ Pixell pixel;
+
+ /* Save new values */
+ color_table[i][0] = angband_color_table[i][0];
+ color_table[i][1] = angband_color_table[i][1];
+ color_table[i][2] = angband_color_table[i][2];
+ color_table[i][3] = angband_color_table[i][3];
+
+ /* Create pixel */
+ pixel = create_pixel(Metadpy->dpy,
+ color_table[i][1],
+ color_table[i][2],
+ color_table[i][3]);
+
+ /* Change the foreground */
+ Infoclr_set(clr[i]);
+ Infoclr_change_fg(pixel);
+ }
+ }
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Handle a "special request"
+ */
+static errr Term_xtra_x11(int n, int v)
+{
+ /* Handle a subset of the legal requests */
+ switch (n)
+ {
+ /* Make a noise */
+ case TERM_XTRA_NOISE:
+ Metadpy_do_beep(); return (0);
+
+ /* Flush the output XXX XXX */
+ case TERM_XTRA_FRESH: Metadpy_update(1, 0, 0); return (0);
+
+ /* Process random events XXX */
+ case TERM_XTRA_BORED:
+ {
+ return (CheckEvent(0));
+ }
+
+ /* Process Events XXX */
+ case TERM_XTRA_EVENT:
+ {
+ return (CheckEvent(v));
+ }
+
+ /* Flush the events XXX */
+ case TERM_XTRA_FLUSH: while (!CheckEvent(FALSE)); return (0);
+
+ /* Handle change in the "level" */
+ case TERM_XTRA_LEVEL: return (Term_xtra_x11_level(v));
+
+ /* Clear the screen, and redraw any selection later. */
+ case TERM_XTRA_CLEAR: Infowin_wipe(); s_ptr->drawn = FALSE; return (0);
+
+ /* 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);
+
+ /* 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);
+}
+
+
+/*
+ * 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);
+}
+
+
+
+
+
+/*
+ * 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 */
+ td->fnt = calloc(1, sizeof(struct infofnt));
+ if (td->fnt == NULL)
+ {
+ abort();
+ }
+ 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 */
+ td->win = calloc(1, sizeof(struct infowin));
+ if (td->win == NULL)
+ {
+ abort();
+ }
+ 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;
+
+ /* Hooks */
+ t->xtra_hook = Term_xtra_x11;
+ t->curs_hook = Term_curs_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;
+
+
+
+ /* Parse args */
+ for (i = 1; i < argc; i++)
+ {
+ if (prefix(argv[i], "-d"))
+ {
+ dpy_name = &argv[i][2];
+ continue;
+ }
+
+
+ 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;
+ }
+
+ fprintf(stderr, "Ignoring option: %s", argv[i]);
+ }
+
+
+ /* Init the Metadpy if possible */
+ if (Metadpy_init_name(dpy_name)) return ( -1);
+
+
+ /* Prepare cursor color */
+ xor = calloc(1, sizeof(struct infoclr));
+ if (xor == NULL)
+ {
+ abort();
+ }
+ Infoclr_set(xor);
+ Infoclr_init_ppn(Metadpy->fg, Metadpy->bg, "xor", 0);
+
+
+ /* Prepare normal colors */
+ for (i = 0; i < 256; ++i)
+ {
+ Pixell pixel;
+
+ clr[i] = calloc(1, sizeof(struct infoclr));
+ if (clr[i] == NULL)
+ {
+ abort();
+ }
+ 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);
+
+
+
+
+ /* Success */
+ return (0);
+}
+
+#endif /* USE_X11 */
+
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 00000000..680e5c5a
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,374 @@
+/* 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 "birth.h"
+#include "dungeon.h"
+#include "files.h"
+#include "init2.h"
+#include "modules.h"
+#include "script.h"
+#include "util.h"
+#include "variable.h"
+
+
+
+/*
+ * Some machines have a "main()" function in their "main-xxx.c" file,
+ * all the others use this file for their "main()" function.
+ */
+
+
+#if !defined(WINDOWS)
+
+
+/*
+ * A hook for "quit()".
+ *
+ * Close down, then fall back into "quit()".
+ */
+static void quit_hook(cptr s)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 8 - 1; j >= 0; j--)
+ {
+ /* Unused */
+ if (!angband_term[j]) continue;
+
+ /* Nuke it */
+ term_nuke(angband_term[j]);
+ }
+}
+
+
+
+/*
+ * Check 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 void init_save_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");
+
+ if (!private_check_user_directory(dirpath))
+ {
+ quit_fmt("Cannot create directory '%s'", dirpath);
+ }
+
+ if (!private_check_user_directory(versionpath))
+ {
+ quit_fmt("Cannot create directory '%s'", versionpath);
+ }
+
+ if (!private_check_user_directory(savepath))
+ {
+ quit_fmt("Cannot create directory '%s'", savepath);
+ }
+}
+
+
+static void init_player_name()
+{
+ /* Get the user id (?) */
+ int player_uid = getuid();
+
+ /* Acquire the "user name" as a default player name */
+ user_name(player_name, player_uid);
+}
+
+
+
+/*
+ * 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;
+
+ cptr mstr = NULL;
+
+ bool_ args = TRUE;
+
+ /* Get the file paths */
+ init_file_paths_with_env();
+
+ /* Initialize the player name */
+ init_player_name();
+
+ /* Make sure save directory exists */
+ init_save_dir();
+
+
+ /* 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 'W':
+ case 'w':
+ {
+ arg_wizard = TRUE;
+ break;
+ }
+
+ case 'R':
+ case 'r':
+ {
+ arg_force_roguelike = TRUE;
+ break;
+ }
+
+ case 'O':
+ case 'o':
+ {
+ arg_force_original = TRUE;
+ 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 = &argv[i][2];
+ break;
+ }
+
+ case 'h':
+ {
+ goto usage;
+ break;
+ }
+
+ case 'H':
+ {
+ char *s;
+ int j;
+
+ init_lua_init();
+
+ 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 '-':
+ {
+ 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(" -w Request wizard mode");
+ puts(" -o Request original keyset");
+ puts(" -r Request rogue-like keyset");
+ puts(" -H <list of files> Convert helpfile to html");
+ puts(" -u<who> Use your <who> savefile");
+ puts(" -M<which> Use the <which> module");
+ puts(" -m<sys> Force 'main-<sys>.c' usage");
+
+#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");
+#endif /* USE_GTK2 */
+
+#ifdef USE_X11
+ puts(" -mx11 To use X11");
+ puts(" -- Sub options");
+ puts(" -- -n# Number of terms to use");
+ puts(" -- -d<name> Display to use");
+#endif /* USE_X11 */
+
+#ifdef USE_GCU
+ puts(" -mgcu To use curses");
+ puts(" -- Sub options");
+ puts(" -- -b Requests big screen");
+#endif /* USE_GCU */
+
+#ifdef USE_SDL
+ puts(" -msdl To use SDL");
+ puts(" -- Sub options");
+ puts(" -- -n # Number of virtual consoles to use");
+ puts(" -- -w # Request screen width in pixels");
+ puts(" -- -h # Request screen height in pixels");
+ puts(" -- -bpp # Request screen color depth in bits");
+ puts(" -- -fs Start with full-screen display");
+ puts(" -- -s # Request font size");
+ puts(" -- -f <font> Request true-type font by name");
+#endif /* USE_SDL */
+
+ /* Actually abort the process */
+ quit(NULL);
+ }
+ }
+ }
+
+ /* Hack -- Forget standard args */
+ if (args)
+ {
+ argc = 1;
+ argv[1] = NULL;
+ }
+
+
+ /* Process the player name */
+ process_player_name(TRUE);
+
+
+ /* Install "quit" hook */
+ quit_aux = quit_hook;
+
+
+#ifdef USE_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_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_SDL
+ /* Attempt to use the "main-sdl.c" support */
+ if (!done && (!mstr || (streq(mstr, "sdl"))))
+ {
+ extern errr init_sdl(int, char**);
+ if (0 == init_sdl(argc, argv))
+ {
+ ANGBAND_SYS = "sdl";
+ done = TRUE;
+ }
+ }
+#endif
+
+ /* Make sure we have a display! */
+ if (!done) quit("Unable to prepare any 'display module'!");
+
+
+ /* Initialize */
+ init_angband();
+
+ /* Wait for response */
+ pause_line(23);
+
+ /* Play the game */
+ play_game(new_game);
+
+ /* Quit */
+ quit(NULL);
+
+ /* Exit */
+ return (0);
+}
+
+#endif
diff --git a/src/martial_arts.hpp b/src/martial_arts.hpp
new file mode 100644
index 00000000..1f2f0cbe
--- /dev/null
+++ b/src/martial_arts.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Martial arts descriptors
+ */
+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 */
+};
+
diff --git a/src/melee1.cc b/src/melee1.cc
new file mode 100644
index 00000000..bb4c06d1
--- /dev/null
+++ b/src/melee1.cc
@@ -0,0 +1,3048 @@
+/*
+ * 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 "melee1.hpp"
+
+#include "cave.hpp"
+#include "cmd5.hpp"
+#include "gods.hpp"
+#include "mimic.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "store.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+
+using boost::algorithm::iequals;
+
+/*
+ * Critical blow. All hits that do 95% of total possible damage,
+ * and which also do at least 20 damage, or, sometimes, N damage.
+ * This is used only to determine "cuts" and "stuns".
+ */
+static int monster_critical(int dice, int sides, int dam)
+{
+ int max = 0;
+ int total = dice * sides;
+
+ /* Must do at least 95% of perfect */
+ if (dam < total * 19 / 20) return (0);
+
+ /* Weak blows rarely work */
+ if ((dam < 20) && (rand_int(100) >= dam)) return (0);
+
+ /* Perfect damage */
+ if (dam == total) max++;
+
+ /* Super-charge */
+ if (dam >= 20)
+ {
+ while (rand_int(100) < 2) max++;
+ }
+
+ /* Critical damage */
+ if (dam > 45) return (6 + max);
+ if (dam > 33) return (5 + max);
+ if (dam > 25) return (4 + max);
+ if (dam > 18) return (3 + max);
+ if (dam > 11) return (2 + max);
+ return (1 + max);
+}
+
+
+
+
+
+/*
+ * Determine if a monster attack against the player succeeds.
+ * Always miss 5% of the time, Always hit 5% of the time.
+ * Otherwise, match monster power against player armor.
+ */
+static int check_hit(int power, int level)
+{
+ int i, k, ac;
+
+ /* Percentile dice */
+ k = rand_int(100);
+
+ /* Hack -- Always miss or hit */
+ if (k < 10) return (k < 5);
+
+ /* Calculate the "attack quality" */
+ i = (power + (level * 3));
+
+ /* Total armor */
+ ac = p_ptr->ac + p_ptr->to_a;
+
+ /* Power and Level compete against Armor */
+ if ((i > 0) && (randint(i - luck( -10, 10)) > ((ac * 3) / 4))) return (TRUE);
+
+ /* Assume miss */
+ return (FALSE);
+}
+
+
+
+/*
+ * Hack -- possible "insult" messages
+ */
+static cptr desc_insult[] =
+{
+ "insults you!",
+ "insults your mother!",
+ "jumps around you!",
+ "humiliates you!",
+ "defiles you!",
+ "dances around you!",
+ "makes obnoxious gestures!",
+ "pokes you!!!"
+};
+
+
+
+/*
+ * Hack -- possible "insult" messages
+ */
+static cptr desc_moan[] =
+{
+ "seems sad about something.",
+ "asks if you have seen his dogs.",
+ "tells you to get off his land.",
+ "mumbles something about mushrooms.",
+
+ /* Mathilde's sentence */
+ "giggles at you.",
+ "asks you if you want to giggle with her.",
+ "says she is always happy."
+};
+
+
+/*
+ * Get the "power" of an attack of given effect type.
+ */
+int get_attack_power(int effect)
+{
+ switch (effect)
+ {
+ case RBE_HURT:
+ return 60;
+ case RBE_POISON:
+ return 5;
+ case RBE_UN_BONUS:
+ return 20;
+ case RBE_UN_POWER:
+ return 15;
+ case RBE_EAT_GOLD:
+ return 5;
+ case RBE_EAT_ITEM:
+ return 5;
+ case RBE_EAT_FOOD:
+ return 5;
+ case RBE_EAT_LITE:
+ return 5;
+ case RBE_ACID:
+ return 0;
+ case RBE_ELEC:
+ return 10;
+ case RBE_FIRE:
+ return 10;
+ case RBE_COLD:
+ return 10;
+ case RBE_BLIND:
+ return 2;
+ case RBE_CONFUSE:
+ return 10;
+ case RBE_TERRIFY:
+ return 10;
+ case RBE_PARALYZE:
+ return 2;
+ case RBE_LOSE_STR:
+ return 0;
+ case RBE_LOSE_DEX:
+ return 0;
+ case RBE_LOSE_CON:
+ return 0;
+ case RBE_LOSE_INT:
+ return 0;
+ case RBE_LOSE_WIS:
+ return 0;
+ case RBE_LOSE_CHR:
+ return 0;
+ case RBE_LOSE_ALL:
+ return 2;
+ case RBE_SHATTER:
+ return 60;
+ case RBE_EXP_10:
+ return 5;
+ case RBE_EXP_20:
+ return 5;
+ case RBE_EXP_40:
+ return 5;
+ case RBE_EXP_80:
+ return 5;
+ case RBE_DISEASE:
+ return 5;
+ case RBE_TIME:
+ return 5;
+ case RBE_SANITY:
+ return 60;
+ case RBE_HALLU:
+ return 10;
+ case RBE_PARASITE:
+ return 5;
+ case RBE_ABOMINATION:
+ return 30;
+ }
+ /* Unknown effects have no power */
+ return 0;
+}
+
+/*
+ * Attack the player via physical attacks.
+ */
+bool_ carried_make_attack_normal(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int ap_cnt;
+
+ int k, tmp, ac, rlev;
+ int do_cut, do_stun;
+
+ char ddesc[80] = "your symbiote";
+ cptr sym_name = symbiote_name(TRUE);
+
+ bool_ touched = FALSE, alive = TRUE;
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & (RF1_NEVER_BLOW)) return (FALSE);
+
+ /* Total armor */
+ ac = p_ptr->ac + p_ptr->to_a;
+
+ /* Extract the effective monster level */
+ rlev = ((r_ptr->level >= 1) ? r_ptr->level : 1);
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = r_ptr->blow[ap_cnt].effect;
+ int method = r_ptr->blow[ap_cnt].method;
+ int d_dice = r_ptr->blow[ap_cnt].d_dice;
+ int d_side = r_ptr->blow[ap_cnt].d_side;
+
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Extract visibility (before blink) */
+ visible = TRUE;
+
+ /* Extract the attack "power" */
+ power = get_attack_power(effect);
+
+
+ /* Monster hits player */
+ if (!effect || check_hit(power, rlev))
+ {
+ /* Always disturbing */
+ disturb(1);
+
+ /* Hack -- Apply "protection from evil" */
+ if ((p_ptr->protevil > 0) &&
+ (r_ptr->flags3 & (RF3_EVIL)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Evil-ness */
+ r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Message */
+ msg_format("%s is repelled.", sym_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Hack -- Apply "protection from good" */
+ if ((p_ptr->protgood > 0) &&
+ (r_ptr->flags3 & (RF3_GOOD)) &&
+ (p_ptr->lev >= rlev) &&
+ ((rand_int(100) + p_ptr->lev) > 50))
+ {
+ /* Remember the Good-ness */
+ r_ptr->r_flags3 |= (RF3_GOOD);
+
+ /* Message */
+ msg_format("%s is repelled.", sym_name);
+
+ /* Hack -- Next attack */
+ continue;
+ }
+
+ /* Assume no cut or stun */
+ do_cut = do_stun = 0;
+
+ /* Describe the attack method */
+ switch (method)
+ {
+ case RBM_HIT:
+ {
+ act = "hits you.";
+ do_cut = do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_TOUCH:
+ {
+ act = "touches you.";
+ touched = TRUE;
+ sound(SOUND_TOUCH);
+ break;
+ }
+
+ case RBM_PUNCH:
+ {
+ act = "punches you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_KICK:
+ {
+ act = "kicks you.";
+ touched = TRUE;
+ do_stun = 1;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CLAW:
+ {
+ act = "claws you.";
+ touched = TRUE;
+ do_cut = 1;
+ sound(SOUND_CLAW);
+ break;
+ }
+
+ case RBM_BITE:
+ {
+ act = "bites you.";
+ do_cut = 1;
+ touched = TRUE;
+ sound(SOUND_BITE);
+ break;
+ }
+
+ case RBM_STING:
+ {
+ act = "stings you.";
+ touched = TRUE;
+ sound(SOUND_STING);
+ break;
+ }
+
+ case RBM_XXX1:
+ {
+ act = "XXX1's you.";
+ break;
+ }
+
+ case RBM_BUTT:
+ {
+ act = "butts you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_HIT);
+ break;
+ }
+
+ case RBM_CRUSH:
+ {
+ act = "crushes you.";
+ do_stun = 1;
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_ENGULF:
+ {
+ act = "engulfs you.";
+ touched = TRUE;
+ sound(SOUND_CRUSH);
+ break;
+ }
+
+ case RBM_CHARGE:
+ {
+ act = "charges you.";
+ touched = TRUE;
+ sound(SOUND_BUY); /* Note! This is "charges", not "charges at". */
+ break;
+ }
+
+ case RBM_CRAWL:
+ {
+ act = "crawls on you.";
+ touched = TRUE;
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_DROOL:
+ {
+ act = "drools on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_SPIT:
+ {
+ act = "spits on you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_EXPLODE:
+ {
+ act = "explodes.";
+ break;
+ }
+
+ case RBM_GAZE:
+ {
+ act = "gazes at you.";
+ break;
+ }
+
+ case RBM_WAIL:
+ {
+ act = "wails at you.";
+ sound(SOUND_WAIL);
+ break;
+ }
+
+ case RBM_SPORE:
+ {
+ act = "releases spores at you.";
+ sound(SOUND_SLIME);
+ break;
+ }
+
+ case RBM_XXX4:
+ {
+ act = "projects XXX4's at you.";
+ break;
+ }
+
+ case RBM_BEG:
+ {
+ act = "begs you for money.";
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_INSULT:
+ {
+ act = desc_insult[rand_int(8)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_MOAN:
+ {
+ act = desc_moan[rand_int(4)];
+ sound(SOUND_MOAN);
+ break;
+ }
+
+ case RBM_SHOW:
+ {
+ if (randint(3) == 1)
+ act = "sings 'We are a happy family.'";
+ else
+ act = "sings 'I love you, you love me.'";
+ sound(SOUND_SHOW);
+ break;
+ }
+ }
+
+ /* Message */
+ if (act) msg_format("%s %s", sym_name, act);
+
+
+ /* Hack -- assume all attacks are obvious */
+ obvious = TRUE;
+
+ /* Roll out the damage */
+ damage = damroll(d_dice, d_side);
+
+ /* Apply appropriate damage */
+ switch (effect)
+ {
+ case 0:
+ {
+ /* Hack -- Assume obvious */
+ obvious = TRUE;
+
+ /* Hack -- No damage */
+ damage = 0;
+
+ break;
+ }
+
+ case RBE_HURT:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Hack -- Player armor reduces total damage */
+ damage -= (damage * ((ac < 150) ? ac : 150) / 250);
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ break;
+ }
+
+ case RBE_ABOMINATION:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Morph, but let mimicry skill have a chance to stop this */
+ if (magik(60 - get_skill(SKILL_MIMICRY)))
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers twisting your body!");
+
+ set_mimic(damage, resolve_mimic_name("Abomination"), 50);
+ }
+ else
+ {
+ /* Message */
+ cmsg_print(TERM_VIOLET, "You feel the dark powers trying to twisting your body, but they fail.");
+ }
+
+ break;
+ }
+
+ case RBE_SANITY:
+ {
+ obvious = TRUE;
+
+ take_sanity_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_POISON:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Take "poison" effect */
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ if (set_poisoned(p_ptr->poisoned + randint(rlev) + 5))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ case RBE_UN_BONUS:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Allow complete resist */
+ if (!p_ptr->resist_disen)
+ {
+ /* Apply disenchantment */
+ if (apply_disenchant(0)) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case RBE_UN_POWER:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_GOLD:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_ITEM:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_EAT_LITE:
+ {
+ /* Take some damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered in acid!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ acid_dam(damage, ddesc);
+
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are struck by electricity!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ elec_dam(damage, ddesc);
+
+
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are enveloped in flames!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ fire_dam(damage, ddesc);
+
+
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered with frost!");
+
+ /* Special damage */
+ carried_monster_hit = TRUE;
+ cold_dam(damage, ddesc);
+
+
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "blind" */
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + 10 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_CONFUSE:
+ {
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "confused" */
+ if (!p_ptr->resist_conf)
+ {
+ if (set_confused(p_ptr->confused + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "afraid" */
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_afraid(p_ptr->afraid + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ /* Hack -- Prevent perma-paralysis via damage */
+ if (p_ptr->paralyzed && (damage < 1)) damage = 1;
+
+ /* Take damage */
+ carried_monster_hit = TRUE;
+ take_hit(damage, ddesc);
+
+ /* Increase "paralyzed" */
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_paralyzed(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);
+
+ /* 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];
+
+ 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? */
+ auto r_ptr = m_ptr->race();
+ if (r_ptr->flags1 & (RF1_NEVER_BLOW)) return (FALSE);
+
+ /* ...nor if friendly */
+ if (is_friend(m_ptr) >= 0)
+ {
+ if (p_ptr->control == m_idx) swap_position(m_ptr->fy, m_ptr->fx);
+ return FALSE;
+ }
+
+ /* Cannot attack the player if mortal and player fated to never die by the ... */
+ if ((r_ptr->flags7 & RF7_MORTAL) && (p_ptr->no_mortal)) return (FALSE);
+
+ /* Total armor */
+ ac = p_ptr->ac + p_ptr->to_a;
+
+ /* Extract the effective monster level */
+ rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+
+ /* Get the monster name (or "it") */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get the "died from" information (i.e. "a kobold") */
+ monster_desc(ddesc, m_ptr, 0x88);
+
+
+ /* Assume no blink */
+ blinked = FALSE;
+
+ /* Scan through all four blows */
+ for (ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = m_ptr->blow[ap_cnt].effect;
+ int method = m_ptr->blow[ap_cnt].method;
+ int d_dice = m_ptr->blow[ap_cnt].d_dice;
+ int d_side = m_ptr->blow[ap_cnt].d_side;
+
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+
+ /* Stop if player is dead or gone */
+ if (!alive || death) break;
+
+ /* Handle "leaving" */
+ if (p_ptr->leaving) break;
+
+ /* Extract visibility (before blink) */
+ if (m_ptr->ml) visible = TRUE;
+
+ /* Extract the attack "power" */
+ switch (effect)
+ {
+ case RBE_HURT:
+ power = 60;
+ break;
+ case RBE_POISON:
+ power = 5;
+ break;
+ case RBE_UN_BONUS:
+ power = 20;
+ break;
+ case RBE_UN_POWER:
+ power = 15;
+ break;
+ case RBE_EAT_GOLD:
+ power = 5;
+ break;
+ case RBE_EAT_ITEM:
+ power = 5;
+ break;
+ case RBE_EAT_FOOD:
+ power = 5;
+ break;
+ case RBE_EAT_LITE:
+ power = 5;
+ break;
+ case RBE_ACID:
+ power = 0;
+ break;
+ case RBE_ELEC:
+ power = 10;
+ break;
+ case RBE_FIRE:
+ power = 10;
+ break;
+ case RBE_COLD:
+ power = 10;
+ break;
+ case RBE_BLIND:
+ power = 2;
+ break;
+ case RBE_CONFUSE:
+ power = 10;
+ break;
+ case RBE_TERRIFY:
+ power = 10;
+ break;
+ case RBE_PARALYZE:
+ power = 2;
+ break;
+ case RBE_LOSE_STR:
+ power = 0;
+ break;
+ case RBE_LOSE_DEX:
+ power = 0;
+ break;
+ case RBE_LOSE_CON:
+ power = 0;
+ break;
+ case RBE_LOSE_INT:
+ power = 0;
+ break;
+ case RBE_LOSE_WIS:
+ power = 0;
+ break;
+ case RBE_LOSE_CHR:
+ power = 0;
+ break;
+ case RBE_LOSE_ALL:
+ power = 2;
+ break;
+ case RBE_SHATTER:
+ power = 60;
+ break;
+ case RBE_EXP_10:
+ power = 5;
+ break;
+ case RBE_EXP_20:
+ power = 5;
+ break;
+ case RBE_EXP_40:
+ power = 5;
+ break;
+ case RBE_EXP_80:
+ power = 5;
+ break;
+ case RBE_DISEASE:
+ power = 5;
+ break;
+ case RBE_TIME:
+ power = 5;
+ break;
+ case RBE_SANITY:
+ power = 60;
+ break;
+ case RBE_HALLU:
+ power = 10;
+ break;
+ case RBE_PARASITE:
+ power = 5;
+ break;
+ case RBE_ABOMINATION:
+ power = 20;
+ break;
+ }
+
+
+ /* Monster hits player */
+ if (!effect || check_hit(power, rlev))
+ {
+ int chance = p_ptr->dodge_chance - ((rlev * 5) / 6);
+
+ /* Always disturbing */
+ disturb(1);
+
+ if ((chance > 0) && magik(chance))
+ {
+ char m_poss[80];
+ monster_desc(m_poss, m_ptr, 0x06);
+ msg_format("You dodge %s attack!", m_poss);
+ continue;
+ }
+
+ /* Eru can help you */
+ if (praying_to(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) && iequals(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_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_FRAME);
+
+ /* 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_FRAME);
+
+ /* 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));
+
+ /* Copy into inventory of monster */
+ {
+ 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 */
+ m_ptr->hold_o_idxs.push_back(o_idx);
+ }
+ }
+
+ /* Steal the items */
+ inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Blink away */
+ blinked = TRUE;
+
+ /* Done */
+ break;
+ }
+
+ break;
+ }
+
+ case RBE_EAT_FOOD:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Steal some food */
+ for (k = 0; k < 10; k++)
+ {
+ /* Pick an item from the pack */
+ i = rand_int(INVEN_PACK);
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Skip non-food objects */
+ if (o_ptr->tval != TV_FOOD) continue;
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Message */
+ msg_format("%sour %s (%c) was eaten!",
+ ((o_ptr->number > 1) ? "One of y" : "Y"),
+ o_name, index_to_label(i));
+
+ /* Steal the items */
+ inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Done */
+ break;
+ }
+
+ break;
+ }
+
+ case RBE_EAT_LITE:
+ {
+ /* Take some damage */
+ take_hit(damage, ddesc);
+
+ /* Access the lite */
+ o_ptr = &p_ptr->inventory[INVEN_LITE];
+
+ /* Drain fuel */
+ if ((o_ptr->pval > 0) && (!artifact_p(o_ptr)))
+ {
+ /* Reduce fuel */
+ o_ptr->pval -= (250 + randint(250));
+ if (o_ptr->pval < 1) o_ptr->pval = 1;
+
+ /* Notice */
+ if (!p_ptr->blind)
+ {
+ msg_print("Your light dims.");
+ obvious = TRUE;
+ }
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ break;
+ }
+
+ case RBE_ACID:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered in acid!");
+
+ /* Special damage */
+ acid_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_ACID);
+
+ break;
+ }
+
+ case RBE_ELEC:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are struck by electricity!");
+
+ /* Special damage */
+ elec_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_ELEC);
+
+ break;
+ }
+
+ case RBE_FIRE:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are enveloped in flames!");
+
+ /* Special damage */
+ fire_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_FIRE);
+
+ break;
+ }
+
+ case RBE_COLD:
+ {
+ /* Obvious */
+ obvious = TRUE;
+
+ /* Message */
+ msg_print("You are covered with frost!");
+
+ /* Special damage */
+ cold_dam(damage, ddesc);
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_COLD);
+
+ break;
+ }
+
+ case RBE_BLIND:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "blind" */
+ if (!p_ptr->resist_blind)
+ {
+ if (set_blind(p_ptr->blind + 10 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_BLIND);
+
+ break;
+ }
+
+ case RBE_CONFUSE:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "confused" */
+ if (!p_ptr->resist_conf)
+ {
+ if (set_confused(p_ptr->confused + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_CONF);
+
+ break;
+ }
+
+ case RBE_TERRIFY:
+ {
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "afraid" */
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You stand your ground!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_afraid(p_ptr->afraid + 3 + randint(rlev)))
+ {
+ obvious = TRUE;
+ }
+ }
+
+ /* Learn about the player */
+ update_smart_learn(m_idx, DRS_FEAR);
+
+ break;
+ }
+
+ case RBE_PARALYZE:
+ {
+ /* Hack -- Prevent perma-paralysis via damage */
+ if (p_ptr->paralyzed && (damage < 1)) damage = 1;
+
+ /* Take damage */
+ take_hit(damage, ddesc);
+
+ /* Increase "paralyzed" */
+ if (p_ptr->free_act)
+ {
+ msg_print("You are unaffected!");
+ obvious = TRUE;
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ obvious = TRUE;
+ }
+ else
+ {
+ if (set_paralyzed(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))))
+ gain_corruption("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);
+
+ /* 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/melee1.hpp b/src/melee1.hpp
new file mode 100644
index 00000000..e84c8f03
--- /dev/null
+++ b/src/melee1.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern int get_attack_power(int effect);
+extern bool_ carried_make_attack_normal(int r_idx);
+extern bool_ make_attack_normal(int m_idx, byte divis);
diff --git a/src/melee2.cc b/src/melee2.cc
new file mode 100644
index 00000000..b3aa5c61
--- /dev/null
+++ b/src/melee2.cc
@@ -0,0 +1,7475 @@
+/*
+ * 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 "melee2.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd1.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "hook_mon_speak_in.hpp"
+#include "hook_monster_ai_in.hpp"
+#include "hook_monster_ai_out.hpp"
+#include "hooks.hpp"
+#include "melee1.hpp"
+#include "messages.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "skills.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define SPEAK_CHANCE 8
+#define GRINDNOISE 20
+
+#define FOLLOW_DISTANCE 6
+
+static void cmonster_msg(char a, cptr fmt, ...);
+
+/*
+ * 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];
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_FRAME);
+
+ /* Some monsters are immune to death */
+ auto const r_ptr = m_ptr->race();
+ 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 */
+ s32b div = p_ptr->max_plv;
+
+ /* Give some experience for the kill */
+ s32b new_exp = ((long)r_ptr->mexp * m_ptr->level) / div;
+
+ /* Handle fractional experience */
+ s32b 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);
+ }
+
+ }
+
+ /* Apply fear */
+ mon_handle_fear(m_ptr, dam, fear);
+
+ /* Not dead yet */
+ return (FALSE);
+}
+
+
+void mon_handle_fear(monster_type *m_ptr, int dam, bool_ *fear)
+{
+ assert(m_ptr != NULL);
+
+ /* 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 */
+ auto const r_ptr = m_ptr->race();
+ 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)));
+ }
+ }
+}
+
+
+/*
+* 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.
+*/
+
+
+
+/*
+* Internal probability routine
+*/
+static bool_ int_outof(std::shared_ptr<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];
+
+ u32b f4 = (*f4p);
+ u32b f5 = (*f5p);
+ u32b f6 = (*f6p);
+
+ u32b smart = 0L;
+
+
+ /* Too stupid to know anything? */
+ auto const r_ptr = m_ptr->race();
+ if (r_ptr->flags2 & (RF2_STUPID)) return;
+
+
+ /* Must be cheating or learning */
+ if (!smart_learn) return;
+
+
+ /* Update acquired knowledge */
+ if (smart_learn)
+ {
+ /* Hack -- Occasionally forget player status */
+ if (m_ptr->smart && magik(1)) m_ptr->smart = 0L;
+
+ /* Use the memorized flags */
+ smart = m_ptr->smart;
+ }
+
+
+ /* Nothing known */
+ if (!smart) return;
+
+
+ if (smart & (SM_IMM_ACID))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_ACID);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_ACID);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ACID);
+ }
+ else if ((smart & (SM_OPP_ACID)) && (smart & (SM_RES_ACID)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_ACID);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_ACID);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_ACID);
+ }
+ else if ((smart & (SM_OPP_ACID)) || (smart & (SM_RES_ACID)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_ACID);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_ACID);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_ACID);
+ }
+
+
+ if (smart & (SM_IMM_ELEC))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_ELEC);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_ELEC);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ELEC);
+ }
+ else if ((smart & (SM_OPP_ELEC)) && (smart & (SM_RES_ELEC)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_ELEC);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_ELEC);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_ELEC);
+ }
+ else if ((smart & (SM_OPP_ELEC)) || (smart & (SM_RES_ELEC)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_ELEC);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_ELEC);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_ELEC);
+ }
+
+
+ if (smart & (SM_IMM_FIRE))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_FIRE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_FIRE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_FIRE);
+ }
+ else if ((smart & (SM_OPP_FIRE)) && (smart & (SM_RES_FIRE)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_FIRE);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_FIRE);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_FIRE);
+ }
+ else if ((smart & (SM_OPP_FIRE)) || (smart & (SM_RES_FIRE)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_FIRE);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_FIRE);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_FIRE);
+ }
+
+
+ if (smart & (SM_IMM_COLD))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BA_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ICEE);
+ }
+ else if ((smart & (SM_OPP_COLD)) && (smart & (SM_RES_COLD)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_COLD);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_COLD);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BO_ICEE);
+ }
+ else if ((smart & (SM_OPP_COLD)) || (smart & (SM_RES_COLD)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_COLD);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_COLD);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BO_ICEE);
+ }
+
+
+ if ((smart & (SM_OPP_POIS)) && (smart & (SM_RES_POIS)))
+ {
+ if (int_outof(r_ptr, 80)) f4 &= ~(RF4_BR_POIS);
+ if (int_outof(r_ptr, 80)) f5 &= ~(RF5_BA_POIS);
+ if (int_outof(r_ptr, 40)) f4 &= ~(RF4_BA_NUKE);
+ if (int_outof(r_ptr, 40)) f4 &= ~(RF4_BR_NUKE);
+ }
+ else if ((smart & (SM_OPP_POIS)) || (smart & (SM_RES_POIS)))
+ {
+ if (int_outof(r_ptr, 30)) f4 &= ~(RF4_BR_POIS);
+ if (int_outof(r_ptr, 30)) f5 &= ~(RF5_BA_POIS);
+ }
+
+
+ if (smart & (SM_RES_NETH))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_NETH);
+ if (int_outof(r_ptr, 50)) f5 &= ~(RF5_BA_NETH);
+ if (int_outof(r_ptr, 50)) f5 &= ~(RF5_BO_NETH);
+ }
+
+ if (smart & (SM_RES_LITE))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_LITE);
+ }
+
+ if (smart & (SM_RES_DARK))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_DARK);
+ if (int_outof(r_ptr, 50)) f5 &= ~(RF5_BA_DARK);
+ }
+
+ if (smart & (SM_RES_FEAR))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_SCARE);
+ }
+
+ if (smart & (SM_RES_CONF))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_CONF);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_CONF);
+ }
+
+ if (smart & (SM_RES_CHAOS))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_CONF);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_CONF);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_CHAO);
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BA_CHAO);
+ }
+
+ if (smart & (SM_RES_DISEN))
+ {
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_BR_DISE);
+ }
+
+ if (smart & (SM_RES_BLIND))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BLIND);
+ }
+
+ if (smart & (SM_RES_NEXUS))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_NEXU);
+ if (int_outof(r_ptr, 50)) f6 &= ~(RF6_TELE_LEVEL);
+ }
+
+ if (smart & (SM_RES_SOUND))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_SOUN);
+ }
+
+ if (smart & (SM_RES_SHARD))
+ {
+ if (int_outof(r_ptr, 50)) f4 &= ~(RF4_BR_SHAR);
+ if (int_outof(r_ptr, 20)) f4 &= ~(RF4_ROCKET);
+ }
+
+ if (smart & (SM_IMM_REFLECT))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_COLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_FIRE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ACID);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ELEC);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_POIS);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_NETH);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_WATE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_MANA);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_PLAS);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_BO_ICEE);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_MISSILE);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_1);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_2);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_3);
+ if (int_outof(r_ptr, 100)) f4 &= ~(RF4_ARROW_4);
+ }
+
+ if (smart & (SM_IMM_FREE))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_HOLD);
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_SLOW);
+ }
+
+ if (smart & (SM_IMM_MANA))
+ {
+ if (int_outof(r_ptr, 100)) f5 &= ~(RF5_DRAIN_MANA);
+ }
+
+ /* XXX XXX XXX No spells left? */
+ /* if (!f4 && !f5 && !f6) ... */
+
+ (*f4p) = f4;
+ (*f5p) = f5;
+ (*f6p) = f6;
+}
+
+
+/*
+ * Determine if there is a space near the player in which
+ * a summoned creature can appear
+ */
+static bool_ summon_possible(int y1, int x1)
+{
+ int y, x;
+
+ /* Start at the player's location, and check 2 grids in each dir */
+ for (y = y1 - 2; y <= y1 + 2; y++)
+ {
+ for (x = x1 - 2; x <= x1 + 2; x++)
+ {
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Only check a circular area */
+ if (distance(y1, x1, y, x) > 2) continue;
+
+ /* Hack: no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) return (FALSE);
+
+ /* ...nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START)
+ && (cave[y][x].feat <= FEAT_PATTERN_XTRA2)) continue;
+
+ /* Require empty floor grid in line of sight */
+ if (cave_empty_bold(y, x) && los(y1, x1, y, x)) return (TRUE);
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+/*
+ * Determine if a bolt spell will hit the player.
+ *
+ * This is exactly like "projectable", but it will return FALSE if a monster
+ * is in the way.
+ */
+static bool_ clean_shot(int y1, int x1, int y2, int x2)
+{
+ int dist, y, x;
+
+ /* Start at the initial location */
+ y = y1, x = x1;
+
+ /* See "project()" and "projectable()" */
+ for (dist = 0; dist <= MAX_RANGE; dist++)
+ {
+ /* Never pass through walls */
+ if (dist && (!cave_sight_bold(y, x) || !cave_floor_bold(y, x))) break;
+
+ /* Never pass through monsters */
+ if (dist && cave[y][x].m_idx > 0)
+ {
+ if (is_friend(&m_list[cave[y][x].m_idx]) < 0) break;
+ }
+
+ /* Check for arrival at "final target" */
+ if ((x == x2) && (y == y2)) return (TRUE);
+
+ /* Calculate the new location */
+ mmove2(&y, &x, y1, x1, y2, x2);
+ }
+
+ /* Assume obstruction */
+ return (FALSE);
+}
+
+
+/*
+ * Cast a bolt at the player
+ * Stop if we hit a monster
+ * Affect monsters and the player
+ */
+static void bolt(int m_idx, int typ, int dam_hp)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+
+ /* Target the player with a bolt attack */
+ (void)project(m_idx, 0, p_ptr->py, p_ptr->px, dam_hp, typ, flg);
+}
+
+
+/*
+ * Return TRUE if a spell is good for hurting the player (directly).
+ */
+static bool_ spell_attack(byte spell)
+{
+ /* All RF4 spells hurt (except for shriek, multiply, summon animal) */
+ if (spell >= 96 + 3 && spell <= 96 + 31) return (TRUE);
+
+ /* Various "ball" spells */
+ if (spell >= 128 && spell <= 128 + 8) return (TRUE);
+
+ /* "Cause wounds" and "bolt" spells */
+ if (spell >= 128 + 12 && spell <= 128 + 26) return (TRUE);
+
+ /* Hand of Doom */
+ if (spell == 160 + 1) return (TRUE);
+
+ /* Doesn't hurt */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell is good for escaping.
+ */
+static bool_ spell_escape(byte spell)
+{
+ /* Blink or Teleport */
+ if (spell == 160 + 4 || spell == 160 + 5) return (TRUE);
+
+ /* Teleport the player away */
+ if (spell == 160 + 7 || spell == 160 + 8) return (TRUE);
+
+ /* Isn't good for escaping */
+ return (FALSE);
+}
+
+/*
+ * Return TRUE if a spell is good for annoying the player.
+ */
+static bool_ spell_annoy(byte spell)
+{
+ /* Shriek */
+ if (spell == 96 + 0) return (TRUE);
+
+ /* Brain smash, et al (added curses) */
+ if (spell >= 128 + 9 && spell <= 128 + 14) return (TRUE);
+
+ /* Scare, confuse, blind, slow, paralyze */
+ if (spell >= 128 + 27 && spell <= 128 + 31) return (TRUE);
+
+ /* Teleport to */
+ if (spell == 160 + 6) return (TRUE);
+
+ /* Darkness, make traps, cause amnesia */
+ if (spell >= 160 + 9 && spell <= 160 + 11) return (TRUE);
+
+ /* Doesn't annoy */
+ return (FALSE);
+}
+
+/*
+ * Return TRUE if a spell summons help.
+ */
+static bool_ spell_summon(byte spell)
+{
+ /* RF4_S_ANIMAL, RF6_S_ANIMALS */
+ if (spell == 96 + 2 || spell == 160 + 3) return (TRUE);
+ /* All other summon spells */
+ if (spell >= 160 + 13 && spell <= 160 + 31) return (TRUE);
+
+ /* Doesn't summon */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell is good in a tactical situation.
+ */
+static bool_ spell_tactic(byte spell)
+{
+ /* Blink */
+ if (spell == 160 + 4) return (TRUE);
+
+ /* Not good */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell hastes.
+ */
+static bool_ spell_haste(byte spell)
+{
+ /* Haste self */
+ if (spell == 160 + 0) return (TRUE);
+
+ /* Not a haste spell */
+ return (FALSE);
+}
+
+
+/*
+ * Return TRUE if a spell is good for healing.
+ */
+static bool_ spell_heal(byte spell)
+{
+ /* Heal */
+ if (spell == 160 + 2) return (TRUE);
+
+ /* No healing */
+ return (FALSE);
+}
+
+
+/*
+ * Have a monster choose a spell from a list of "useful" spells.
+ *
+ * Note that this list does NOT include spells that will just hit
+ * other monsters, and the list is restricted when the monster is
+ * "desperate". Should that be the job of this function instead?
+ *
+ * Stupid monsters will just pick a spell randomly. Smart monsters
+ * will choose more "intelligently".
+ *
+ * Use the helper functions above to put spells into categories.
+ *
+ * This function may well be an efficiency bottleneck.
+ */
+static int choose_attack_spell(int m_idx, byte spells[], byte num)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ 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;
+
+ /* Stupid monsters choose randomly */
+ auto const r_ptr = m_ptr->race();
+ if (r_ptr->flags2 & (RF2_STUPID))
+ {
+ /* Pick at random */
+ return (spells[rand_int(num)]);
+ }
+
+ /* Categorize spells */
+ for (int 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];
+ auto const r_ptr = m_ptr->race();
+
+ /* 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];
+ auto const r_ptr = m_ptr->race();
+
+ /* 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);
+}
+
+
+static 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);
+
+ /* Print */
+ monster_msg_simple(buf);
+}
+
+void monster_msg_simple(cptr s)
+{
+ /* Display */
+ if (disturb_other)
+ {
+ msg_print(s);
+ }
+ else
+ {
+ message_add(s, 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(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;
+ int thrown_spell;
+ byte spell[96], num = 0;
+ char m_name[80], t_name[80];
+ char m_poss[80];
+ char ddesc[80];
+ monster_type *m_ptr = &m_list[m_idx]; /* Attacker */
+ 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 */
+ const auto r_ptr = m_ptr->race();
+ const int 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;
+
+
+ {
+ int t_idx = i;
+
+ monster_type *t_ptr = &m_list[t_idx];
+ auto const tr_ptr = t_ptr->race();
+
+ /* 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 */
+ const int 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 (int k = 0; k < 32; k++)
+ {
+ if (f4 & (1L << k)) spell[num++] = k + 32 * 3;
+ }
+
+ /* Extract the "normal" spells */
+ for (int k = 0; k < 32; k++)
+ {
+ if (f5 & (1L << k)) spell[num++] = k + 32 * 4;
+ }
+
+ /* Extract the "bizarre" spells */
+ for (int 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);
+
+ int count = 0;
+ switch (thrown_spell)
+ {
+ /* RF4_SHRIEK */
+ case 96 + 0:
+ {
+ if (!direct) break;
+ if (disturb_other) disturb(1);
+ if (!see_m) monster_msg("You hear a shriek.");
+ else monster_msg("%^s shrieks at %s.", m_name, t_name);
+ wake_up = TRUE;
+ break;
+ }
+
+ /* RF4_MULTIPLY */
+ case 96 + 1:
+ {
+ break;
+ }
+
+ /* RF4_S_ANIMAL */
+ case 96 + 2:
+ {
+ if (disturb_other) disturb(1);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons an animal!", m_name);
+ for (int 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);
+ 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);
+ 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);
+ 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);
+
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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_FRAME);
+
+ /* 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);
+
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+
+ /* 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_FRAME);
+
+ /* 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons some animals!", m_name);
+ for (int 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically codes some software bugs.", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically codes some RNGs.", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a Thunderlord!", m_name);
+ for (int 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);
+ 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 (int 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);
+ 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons help!", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons monsters!", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons ants.", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons spiders.", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons hounds.", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons hydras.", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons an angel!", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a demon!", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons an undead adversary!", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a dragon!", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons greater undead!", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons ancient dragons!", m_name);
+ for (int 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);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons a wraith!", m_name);
+
+
+ for (int k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_WRAITH);
+ }
+
+ if (blind && count)
+ {
+ monster_msg("You hear immortal beings appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_UNIQUE */
+ case 160 + 31:
+ {
+ if (disturb_other) disturb(1);
+ if (blind || !see_m) monster_msg("%^s mumbles.", m_name);
+ else monster_msg("%^s magically summons special opponents!", m_name);
+ for (int k = 0; k < 8; k++)
+ {
+ if (!friendly)
+ count += summon_specific(y, x, rlev, SUMMON_UNIQUE);
+ }
+ for (int 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.
+ *
+ * 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 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.
+ */
+static bool_ make_attack_spell(int m_idx)
+{
+ int k, chance, thrown_spell, rlev, failrate;
+ byte spell[96], num = 0;
+ u32b f4, f5, f6;
+ 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);
+
+ /* Get a pointer to the monster */
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Extract the "see-able-ness" */
+ bool_ seen = (!blind && m_ptr->ml);
+
+ /* Assume "normal" target */
+ bool_ normal = 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 ... */
+ auto const r_ptr = m_ptr->race();
+ 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);
+
+ /* Only do spells occasionally */
+ if (rand_int(100) >= chance) return (FALSE);
+
+ /* Sometimes forbid inate attacks (breaths) */
+ if (rand_int(100) >= (chance * 2)) no_inate = TRUE;
+
+ /* 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;
+
+ /* 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);
+
+ /* 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);
+
+ /* Choose a spell to cast */
+ 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:
+ {
+ disturb(1);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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 (p_ptr->csp)
+ {
+ int r1;
+
+ /* Disturb if legal */
+ disturb(1);
+
+ /* 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_FRAME);
+
+ /* 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_FRAME);
+
+ /* 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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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(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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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(rand_int(4) + 4);
+ }
+ update_smart_learn(m_idx, DRS_FREE);
+ break;
+ }
+
+
+
+ /* RF6_HASTE */
+ case 160 + 0:
+ {
+ disturb(1);
+ 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);
+ 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);
+
+ /* 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_FRAME);
+
+ /* 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);
+ 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);
+ msg_format("%^s blinks away.", m_name);
+ teleport_away(m_idx, 10);
+ break;
+ }
+
+ /* RF6_TPORT */
+ case 160 + 5:
+ {
+ disturb(1);
+ msg_format("%^s teleports away.", m_name);
+ teleport_away(m_idx, MAX_SIGHT * 2 + 5);
+ break;
+ }
+
+ /* RF6_TELE_TO */
+ case 160 + 6:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ msg_format("%^s teleports you away.", m_name);
+ teleport_player(100);
+ break;
+ }
+
+ /* RF6_TELE_LEVEL */
+ case 160 + 8:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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:
+ {
+ disturb(1);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ if (blind) msg_format("%^s mumbles.", m_name);
+ else msg_format("%^s magically summons Wraith!", m_name);
+
+
+ for (k = 0; k < 8; k++)
+ {
+ count += summon_specific(y, x, rlev, SUMMON_WRAITH);
+ }
+
+ if (blind && count)
+ {
+ msg_print("You hear immortal beings appear nearby.");
+ }
+ break;
+ }
+
+ /* RF6_S_UNIQUE */
+ case 160 + 31:
+ {
+ disturb(1);
+ 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];
+ u16b p_lev, m_lev;
+ u16b p_chp, p_mhp;
+ u16b m_chp, m_mhp;
+ u32b p_val, m_val;
+
+ /* 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);
+
+ /* 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);
+
+ /* Assume no terror */
+ return (FALSE);
+}
+
+
+
+
+/*
+* Choose the "best" direction for "flowing"
+*
+* Note that ghosts and rock-eaters are never allowed to "flow",
+* since they should move directly towards the player.
+*
+* Prefer "non-diagonal" directions, but twiddle them a little
+* to angle slightly towards the player's actual location.
+*
+* Allow very perceptive monsters to track old "spoor" left by
+* previous locations occupied by the player. This will tend
+* to have monsters end up either near the player or on a grid
+* recently occupied by the player (and left via "teleport").
+*
+* Note that if "smell" is turned on, all monsters get vicious.
+*
+* Also note that teleporting away from a location will cause
+* the monsters who were chasing you to converge on that location
+* as long as you are still near enough to "annoy" them without
+* being close enough to chase directly. I have no idea what will
+* happen if you combine "smell" with low "aaf" values.
+*/
+
+/*
+* Provide a location to flee to, but give the player a wide berth.
+*
+* A monster may wish to flee to a location that is behind the player,
+* but instead of heading directly for it, the monster should "swerve"
+* around the player so that he has a smaller chance of getting hit.
+*/
+static bool_ get_fear_moves_aux(int m_idx, int *yp, int *xp)
+{
+ /* Monster flowing disabled */
+ if (!flow_by_sound) return (FALSE);
+
+ /* Monster location */
+ monster_type *m_ptr = &m_list[m_idx];
+ const int fy = m_ptr->fy;;
+ const int fx = m_ptr->fx;
+
+ /* Desired destination */
+ int y1 = fy - (*yp);
+ int 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 */
+ auto const r_ptr = m_ptr->race();
+ if (cave[fy][fx].cost > MONSTER_FLOW_DEPTH) return (FALSE);
+ if (cave[fy][fx].cost > r_ptr->aaf) return (FALSE);
+
+ /* Loop state */
+ int when = 0;
+ int gy = 0;
+ int gx = 0;
+ int score = -1;
+
+ /* Check nearby grids, diagonals first */
+ for (int i = 7; i >= 0; i--)
+ {
+ int dis, s;
+
+ /* Get the location */
+ const int y = fy + ddy_ddd[i];
+ const int x = fx + ddx_ddd[i];
+
+ /* Ignore illegal locations */
+ if (cave[y][x].when == 0) continue;
+
+ /* Ignore ancient locations */
+ if (cave[y][x].when < when) continue;
+
+ /* Calculate distance of this grid from our destination */
+ dis = distance(y, x, y1, x1);
+
+ /* Score this grid */
+ s = 5000 / (dis + 3) - 500 / (cave[y][x].cost + 1);
+
+ /* No negative scores */
+ if (s < 0) s = 0;
+
+ /* Ignore lower scores */
+ if (s < score) continue;
+
+ /* Save the score and time */
+ when = cave[y][x].when;
+ score = s;
+
+ /* Save the location */
+ gy = y;
+ gx = x;
+ }
+
+ /* No legal move (?) */
+ if (!when) return (FALSE);
+
+ /* Find deltas */
+ (*yp) = fy - gy;
+ (*xp) = fx - gx;
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Choose a "safe" location near a monster for it to run toward.
+*
+* A location is "safe" if it can be reached quickly and the player
+* is not able to fire into it (it isn't a "clean shot"). So, this will
+* cause monsters to "duck" behind walls. Hopefully, monsters will also
+* try to run towards corridor openings if they are in a room.
+*
+* This function may take lots of CPU time if lots of monsters are
+* fleeing.
+*
+* Return TRUE if a safe location is available.
+*/
+static bool_ find_safety(int m_idx, int *yp, int *xp)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ int fy = m_ptr->fy;
+ int fx = m_ptr->fx;
+
+ int y, x, d, dis;
+ int gy = 0, gx = 0, gdis = 0;
+
+ /* Start with adjacent locations, spread further */
+ for (d = 1; d < 10; d++)
+ {
+ /* Check nearby locations */
+ for (y = fy - d; y <= fy + d; y++)
+ {
+ for (x = fx - d; x <= fx + d; x++)
+ {
+ /* Skip illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Skip locations in a wall */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Check distance */
+ if (distance(y, x, fy, fx) != d) continue;
+
+ /* Check for "availability" (if monsters can flow) */
+ if (flow_by_sound)
+ {
+ /* Ignore grids very far from the player */
+ if (cave[y][x].when < cave[p_ptr->py][p_ptr->px].when) continue;
+
+ /* Ignore too-distant grids */
+ if (cave[y][x].cost > cave[fy][fx].cost + 2 * d) continue;
+ }
+
+ /* Check for absence of shot */
+ if (!projectable(y, x, p_ptr->py, p_ptr->px))
+ {
+ /* Calculate distance from player */
+ dis = distance(y, x, p_ptr->py, p_ptr->px);
+
+ /* Remember if further than previous */
+ if (dis > gdis)
+ {
+ gy = y;
+ gx = x;
+ gdis = dis;
+ }
+ }
+ }
+ }
+
+ /* Check for success */
+ if (gdis > 0)
+ {
+ /* Good location */
+ (*yp) = fy - gy;
+ (*xp) = fx - gx;
+
+ /* Found safe place */
+ return (TRUE);
+ }
+ }
+
+ /* No safe place */
+ return (FALSE);
+}
+
+
+/*
+ * Choose a good hiding place near a monster for it to run toward.
+ *
+ * Pack monsters will use this to "ambush" the player and lure him out
+ * of corridors into open space so they can swarm him.
+ *
+ * Return TRUE if a good location is available.
+ */
+static bool_ find_hiding(int m_idx, int *yp, int *xp)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ int fy = m_ptr->fy;
+ int fx = m_ptr->fx;
+
+ int y, x, d, dis;
+ int gy = 0, gx = 0, gdis = 999, min;
+
+ /* Closest distance to get */
+ min = distance(p_ptr->py, p_ptr->px, fy, fx) * 3 / 4 + 2;
+
+ /* Start with adjacent locations, spread further */
+ for (d = 1; d < 10; d++)
+ {
+ /* Check nearby locations */
+ for (y = fy - d; y <= fy + d; y++)
+ {
+ for (x = fx - d; x <= fx + d; x++)
+ {
+ /* Skip illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Skip locations in a wall */
+ if (!cave_floor_bold(y, x)) continue;
+
+ /* Check distance */
+ if (distance(y, x, fy, fx) != d) continue;
+
+ /* Check for hidden, available grid */
+ if (!player_can_see_bold(y, x) && clean_shot(fy, fx, y, x))
+ {
+ /* Calculate distance from player */
+ dis = distance(y, x, p_ptr->py, p_ptr->px);
+
+ /* Remember if closer than previous */
+ if (dis < gdis && dis >= min)
+ {
+ gy = y;
+ gx = x;
+ gdis = dis;
+ }
+ }
+ }
+ }
+
+ /* Check for success */
+ if (gdis < 999)
+ {
+ /* Good location */
+ (*yp) = fy - gy;
+ (*xp) = fx - gx;
+
+ /* Found good place */
+ return (TRUE);
+ }
+ }
+
+ /* No good place */
+ return (FALSE);
+}
+
+
+/* Find an appropriate corpse */
+void find_corpse(monster_type *m_ptr, int *y, int *x)
+{
+ int k, last = -1;
+
+ for (k = 0; k < max_o_idx; k++)
+ {
+ object_type *o_ptr = &o_list[k];
+ monster_race *rt_ptr, *rt2_ptr;
+
+ if (!o_ptr->k_idx) continue;
+
+ if (o_ptr->tval != TV_CORPSE) continue;
+ if ((o_ptr->sval != SV_CORPSE_CORPSE) && (o_ptr->sval != SV_CORPSE_SKELETON)) continue;
+
+ rt_ptr = &r_info[o_ptr->pval2];
+
+ /* Cannot incarnate into a higher level monster */
+ if (rt_ptr->level > m_ptr->level) continue;
+
+ /* Must be in LOS */
+ if (!los(m_ptr->fy, m_ptr->fx, o_ptr->iy, o_ptr->ix)) continue;
+
+ if (last != -1)
+ {
+ rt2_ptr = &r_info[o_list[last].pval2];
+ if (rt_ptr->level > rt2_ptr->level) last = k;
+ else continue;
+ }
+ else
+ {
+ last = k;
+ }
+ }
+
+ /* Must be ok now */
+ if (last != -1)
+ {
+ *y = o_list[last].iy;
+ *x = o_list[last].ix;
+ }
+}
+
+/*
+ * Choose target
+ */
+static void get_target_monster(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int i, t = -1, d = 9999;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *t_ptr = &m_list[i];
+ /* hack should call the function for ego monsters ... but no_target i not meant to be added by ego and it speeds up the code */
+ monster_race *rt_ptr = &r_info[t_ptr->r_idx];
+ int dd;
+
+ /* Ignore "dead" monsters */
+ if (!t_ptr->r_idx) continue;
+
+ if (m_idx == i) continue;
+
+ /* Cannot be targeted */
+ if (rt_ptr->flags7 & RF7_NO_TARGET) continue;
+
+ if (is_enemy(m_ptr, t_ptr) && (los(m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx) &&
+ ((dd = distance(m_ptr->fy, m_ptr->fx, t_ptr->fy, t_ptr->fx)) < d)))
+ {
+ t = i;
+ d = dd;
+ }
+ }
+ /* Hack */
+ if ((is_friend(m_ptr) < 0) && los(m_ptr->fy, m_ptr->fx, p_ptr->py, p_ptr->px) && (distance(m_ptr->fy, m_ptr->fx, p_ptr->py, p_ptr->px) < d)) t = 0;
+
+ m_ptr->target = t;
+}
+
+/*
+ * Choose "logical" directions for monster movement
+ */
+static bool_ get_moves(int m_idx, int *mm)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ int move_val = 0;
+
+ int y2 = p_ptr->py;
+ int x2 = p_ptr->px;
+ bool_ done = FALSE;
+
+ /* Oups get nearer */
+ if ((is_friend(m_ptr) > 0) && (m_ptr->cdis > p_ptr->pet_follow_distance))
+ {
+ y2 = p_ptr->py;
+ x2 = p_ptr->px;
+ }
+ /* Use the target */
+ else if (!m_ptr->target)
+ {
+ y2 = p_ptr->py;
+ x2 = p_ptr->px;
+ }
+ else if (m_ptr->target > 0)
+ {
+ y2 = m_list[m_ptr->target].fy;
+ x2 = m_list[m_ptr->target].fx;
+ }
+
+ /* Hack doppleganger confuses monsters(even pets) */
+ if (doppleganger)
+ {
+ if (magik(70))
+ {
+ y2 = m_list[doppleganger].fy;
+ x2 = m_list[doppleganger].fx;
+ }
+ }
+
+ /* Get the race */
+ const auto r_ptr = m_ptr->race();
+
+ /* 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)
+ {
+ struct hook_monster_ai_in in = { m_idx, &m_list[m_idx] };
+ struct hook_monster_ai_out out = { 0, 0 };
+ if (process_hooks_new(HOOK_MONSTER_AI, &in, &out))
+ {
+ y2 = out.y;
+ x2 = out.x;
+ }
+ }
+
+ 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" */
+ int y = m_ptr->fy - y2;
+ int 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 (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 (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);
+ }
+ }
+ }
+ }
+
+
+ /* Check for no move */
+ if (!x && !y) return (FALSE);
+
+ /* Extract the "absolute distances" */
+ int ay = ABS(y);
+ int ax = ABS(x);
+
+ /* 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)
+{
+ char temp[80];
+ bool_ blinked = FALSE;
+ bool_ touched = FALSE;
+ bool_ explode = FALSE;
+ bool_ fear = FALSE;
+ monster_type *t_ptr = &m_list[t_idx];
+ byte y_saver = t_ptr->fy;
+ byte x_saver = t_ptr->fx;
+
+ /* Get the racial information on the two monsters */
+ monster_type *m_ptr = &m_list[m_idx];
+ const auto r_ptr = m_ptr->race();
+ const auto tr_ptr = t_ptr->race();
+
+ /* Not allowed to attack */
+ if (r_ptr->flags1 & RF1_NEVER_BLOW) return FALSE;
+
+ /* Total armor */
+ const int ac = t_ptr->ac;
+
+ /* Extract the effective monster level */
+ const int rlev = ((m_ptr->level >= 1) ? m_ptr->level : 1);
+
+ /* Get the monster name (or "it") */
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get the monster name (or "it") */
+ char t_name[80];
+ monster_desc(t_name, t_ptr, 0);
+
+ /* Get the "died from" information (i.e. "a kobold") */
+ char ddesc[80];
+ 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 (int ap_cnt = 0; ap_cnt < 4; ap_cnt++)
+ {
+ bool_ visible = FALSE;
+ bool_ obvious = FALSE;
+
+ int power = 0;
+ int damage = 0;
+
+ cptr act = NULL;
+
+ /* Extract the attack infomation */
+ int effect = m_ptr->blow[ap_cnt].effect;
+ int method = m_ptr->blow[ap_cnt].method;
+ int d_dice = m_ptr->blow[ap_cnt].d_dice;
+ int d_side = m_ptr->blow[ap_cnt].d_side;
+
+ if (t_ptr == m_ptr) /* Paranoia */
+ {
+ if (wizard)
+ monster_msg("Monster attacking self?");
+ break;
+ }
+
+ /* Stop attacking if the target dies! */
+ if (t_ptr->fx != x_saver || t_ptr->fy != y_saver)
+ break;
+
+ /* Hack -- no more attacks */
+ if (!method) break;
+
+ if (blinked) /* Stop! */
+ {
+ /* break; */
+ }
+
+ /* Extract visibility (before blink) */
+ if (m_ptr->ml) visible = TRUE;
+
+ /* Extract the attack "power" */
+ power = get_attack_power(effect);
+
+
+ /* Monster hits*/
+ if (!effect || check_hit2(power, rlev, ac))
+ {
+ /* Always disturbing */
+ if (disturb_other) disturb(1);
+
+ /* 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)
+ {
+ strnfmt(temp, sizeof(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;
+
+ int 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);
+
+ /* 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)
+{
+ const auto r_ptr = m_ptr->race();
+ s16b inv = p_ptr->invis;
+ s16b mlv = 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)
+{
+ int i, d, oy, ox, ny, nx;
+
+ int mm[8];
+
+ monster_type *m_ptr = &m_list[m_idx];
+ const bool_ inv = player_invis(m_ptr);
+
+ auto const r_ptr = m_ptr->race();
+ 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 */
+ bool_ xxx = FALSE;
+ 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_FRAME);
+ }
+ }
+ }
+
+ /* Handle "poisoned" */
+ if (m_ptr->poisoned)
+ {
+ int d = (m_ptr->poisoned) / 10;
+ if (d < 1) d = 1;
+
+ /* Exit if the monster dies */
+ bool_ xxx = FALSE;
+ 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_FRAME);
+ }
+ }
+ }
+
+ /* 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);
+ }
+ }
+ }
+
+ /* Do the monster get angry? */
+ bool_ gets_angry = FALSE;
+
+ /* 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 (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.... */
+
+ struct hook_mon_speak_in in = { m_idx, m_name };
+ if (!process_hooks_new(HOOK_MON_SPEAK, &in, NULL))
+ {
+ 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
+ {
+ /* Logical moves, may do nothing */
+ if (!get_moves(m_idx, mm)) return;
+ }
+
+ /* Paranoia -- quest code could delete it */
+ cave_type *c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+ if (!c_ptr->m_idx) return;
+
+ /* Assume nothing */
+ bool_ do_turn = FALSE;
+ bool_ do_move = FALSE;
+ bool_ do_view = FALSE;
+
+ /* Assume nothing */
+ bool_ did_open_door = FALSE;
+ bool_ did_bash_door = FALSE;
+ bool_ did_take_item = FALSE;
+ bool_ did_kill_item = FALSE;
+ bool_ did_move_body = FALSE;
+ bool_ did_kill_body = FALSE;
+ bool_ did_pass_wall = FALSE;
+ bool_ 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 */
+ monster_type *y_ptr = &m_list[c_ptr->m_idx];
+
+
+ /* Floor is open? */
+ if (cave_floor_bold(ny, nx))
+ {
+ /* Go ahead and move */
+ do_move = TRUE;
+ }
+
+ /* Floor is trapped? */
+ else if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ /* Go ahead and move */
+ do_move = TRUE;
+ }
+
+ /* Hack -- check for Glyph of Warding */
+ if ((c_ptr->feat == FEAT_GLYPH) &&
+ !(r_ptr->flags1 & RF1_NEVER_BLOW))
+ {
+ /* Assume no move allowed */
+ do_move = FALSE;
+
+ /* Break the ward */
+ if (randint(BREAK_GLYPH) < m_ptr->level)
+ {
+ /* Describe observable breakage */
+ if (c_ptr->info & CAVE_MARK)
+ {
+ msg_print("The rune of protection is broken!");
+ }
+
+ /* Forget the rune */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Break the rune */
+ place_floor_convert_glass(ny, nx);
+
+ /* Allow movement */
+ do_move = TRUE;
+ }
+ }
+
+ /* Hack -- trees are obstacle */
+ else if ((cave[ny][nx].feat == FEAT_TREES) && (r_ptr->flags9 & RF9_KILL_TREES))
+ {
+ do_move = TRUE;
+
+ /* Forget the tree */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ cave_set_feat(ny, nx, FEAT_GRASS);
+ }
+
+ /* Hack -- player 'in' wall */
+ else if ((ny == p_ptr->py) && (nx == p_ptr->px))
+ {
+ do_move = TRUE;
+ }
+
+ else if (c_ptr->m_idx)
+ {
+ /* Possibly a monster to attack */
+ do_move = TRUE;
+ }
+
+ /* Permanent wall */
+ else if (f_info[c_ptr->feat].flags1 & FF1_PERMANENT)
+ {
+ /* Nothing */
+ }
+
+
+ /* Some monsters can fly */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_LEVITATE) && (r_ptr->flags7 & (RF7_CAN_FLY)))
+ {
+ /* Pass through walls/doors/rubble */
+ do_move = TRUE;
+ }
+
+ /* Some monsters can fly */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_FLY) && (r_ptr->flags7 & (RF7_CAN_FLY)))
+ {
+ /* Pass through trees/... */
+ do_move = TRUE;
+ }
+
+ /* Monster moves through walls (and doors) */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_PASS) && (r_ptr->flags2 & (RF2_PASS_WALL)))
+ {
+ /* Pass through walls/doors/rubble */
+ do_move = TRUE;
+
+ /* Monster went through a wall */
+ did_pass_wall = TRUE;
+ }
+
+ /* Monster destroys walls (and doors) */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_PASS) && (r_ptr->flags2 & (RF2_KILL_WALL)))
+ {
+ /* Eat through walls/doors/rubble */
+ do_move = TRUE;
+
+ /* Monster destroyed a wall */
+ did_kill_wall = TRUE;
+
+ if (randint(GRINDNOISE) == 1)
+ {
+ msg_print("There is a grinding sound.");
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ cave_set_feat(ny, nx, FEAT_FLOOR);
+
+ /* Note changes to viewable region */
+ if (player_has_los_bold(ny, nx)) do_view = TRUE;
+ }
+
+ /* Monster moves through walls (and doors) */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_CAN_PASS) && (r_ptr->flags2 & (RF2_PASS_WALL)))
+ {
+ /* Pass through walls/doors/rubble */
+ do_move = TRUE;
+
+ /* Monster went through a wall */
+ did_pass_wall = TRUE;
+ }
+
+ /* Monster moves through webs */
+ else if ((f_info[c_ptr->feat].flags1 & FF1_WEB) &&
+ (r_ptr->flags7 & RF7_SPIDER))
+ {
+ /* Pass through webs */
+ do_move = TRUE;
+ }
+
+ /* Handle doors and secret doors */
+ else if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) ||
+ (c_ptr->feat == FEAT_SECRET))
+ {
+ bool_ may_bash = TRUE;
+
+ /* Take a turn */
+ do_turn = TRUE;
+
+ if ((r_ptr->flags2 & (RF2_OPEN_DOOR)) &&
+ ((is_friend(m_ptr) <= 0) || p_ptr->pet_open_doors))
+ {
+ /* Closed doors and secret doors */
+ if ((c_ptr->feat == FEAT_DOOR_HEAD) ||
+ (c_ptr->feat == FEAT_SECRET))
+ {
+ /* The door is open */
+ did_open_door = TRUE;
+
+ /* Do not bash the door */
+ may_bash = FALSE;
+ }
+
+ /* Locked doors (not jammed) */
+ else if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08)
+ {
+ int k;
+
+ /* Door power */
+ k = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07);
+
+ /* Try to unlock it XXX XXX XXX */
+ if (rand_int(m_ptr->hp / 10) > k)
+ {
+ /* Unlock the door */
+ cave_set_feat(ny, nx, FEAT_DOOR_HEAD + 0x00);
+
+ /* Do not bash the door */
+ may_bash = FALSE;
+ }
+ }
+ }
+
+ /* Stuck doors -- attempt to bash them down if allowed */
+ if (may_bash && (r_ptr->flags2 & RF2_BASH_DOOR) &&
+ ((is_friend(m_ptr) <= 0) || p_ptr->pet_open_doors))
+ {
+ int k;
+
+ /* Door power */
+ k = ((c_ptr->feat - FEAT_DOOR_HEAD) & 0x07);
+
+ /* Attempt to Bash XXX XXX XXX */
+ if (rand_int(m_ptr->hp / 10) > k)
+ {
+ /* Message */
+ msg_print("You hear a door burst open!");
+
+ /* Disturb (sometimes) */
+ if (disturb_minor) disturb(0);
+
+ /* The door was bashed open */
+ did_bash_door = TRUE;
+
+ /* Hack -- fall into doorway */
+ do_move = TRUE;
+ }
+ }
+
+
+ /* Deal with doors in the way */
+ if (did_open_door || did_bash_door)
+ {
+ /* It's no longer hidden */
+ cave[ny][nx].mimic = 0;
+
+ /* Break down the door */
+ if (did_bash_door && (rand_int(100) < 50))
+ {
+ cave_set_feat(ny, nx, FEAT_BROKEN);
+ }
+
+ /* Open the door */
+ else
+ {
+ cave_set_feat(ny, nx, FEAT_OPEN);
+ }
+
+ /* Handle viewable doors */
+ if (player_has_los_bold(ny, nx)) do_view = TRUE;
+ }
+ }
+ else if (do_move && (c_ptr->feat == FEAT_MINOR_GLYPH)
+ && !(r_ptr->flags1 & RF1_NEVER_BLOW))
+ {
+ /* Assume no move allowed */
+ do_move = FALSE;
+
+ /* Break the ward */
+ if (randint(BREAK_MINOR_GLYPH) < m_ptr->level)
+ {
+ /* Describe observable breakage */
+ if (c_ptr->info & CAVE_MARK)
+ {
+ if (ny == p_ptr->py && nx == p_ptr->px)
+ {
+ msg_print("The rune explodes!");
+ fire_ball(GF_MANA, 0,
+ 2 * ((p_ptr->lev / 2) + damroll(7, 7)), 2);
+ }
+ else
+ msg_print("An explosive rune was disarmed.");
+ }
+
+ /* Forget the rune */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Break the rune */
+ place_floor_convert_glass(ny, nx);
+
+ /* Allow movement */
+ do_move = TRUE;
+ }
+ }
+
+ /* Hack -- the Between teleport the monsters too */
+ else if (cave[ny][nx].feat == FEAT_BETWEEN)
+ {
+ nx = cave[ny][nx].special & 255;
+ ny = cave[ny][nx].special >> 8;
+ get_pos_player(10, &ny, &nx);
+
+ /* Access that cave grid */
+ c_ptr = &cave[ny][nx];
+
+ /* Access that cave grid's contents */
+ y_ptr = &m_list[c_ptr->m_idx];
+
+ if (!(r_ptr->flags3 & RF3_IM_COLD))
+ {
+ if ((m_ptr->hp - distance(ny, nx, oy, ox)*2) <= 0)
+ {
+ ny = oy + ddy[d];
+ nx = ox + ddx[d];
+ do_move = FALSE;
+ }
+ else
+ {
+ m_ptr->hp -= distance(ny, nx, oy, ox) * 2;
+ do_move = TRUE;
+ }
+ }
+ else
+ {
+ do_move = TRUE;
+ }
+ }
+
+ /* Execute the inscription -- MEGA HACK -- */
+ if ((c_ptr->inscription) && (c_ptr->inscription != INSCRIP_CHASM))
+ {
+ if (inscription_info[c_ptr->inscription].when & INSCRIP_EXEC_MONST_WALK)
+ {
+ bool_ t;
+ t = execute_inscription(c_ptr->inscription, ny, nx);
+ if (!t && do_move)
+ {
+ /* Hack -- attack the player even if on the inscription */
+ if ((ny == p_ptr->py) && (nx == p_ptr->px))
+ do_move = TRUE;
+ else
+ do_move = FALSE;
+ }
+ }
+ }
+
+ /* Some monsters never attack */
+ if (do_move && (ny == p_ptr->py) && (nx == p_ptr->px) &&
+ (r_ptr->flags1 & RF1_NEVER_BLOW))
+ {
+ /* Do not move */
+ do_move = FALSE;
+ }
+
+ /* The player is in the way. Attack him. */
+ if (do_move && (ny == p_ptr->py) && (nx == p_ptr->px))
+ {
+ /* Do the attack */
+ (void)make_attack_normal(m_idx, 1);
+
+ /* Do not move */
+ do_move = FALSE;
+
+ /* Took a turn */
+ do_turn = TRUE;
+ }
+
+ if ((cave[ny][nx].feat >= FEAT_PATTERN_START) &&
+ (cave[ny][nx].feat <= FEAT_PATTERN_XTRA2) &&
+ do_turn == FALSE)
+ {
+ do_move = FALSE;
+ }
+
+
+ /* A monster is in the way */
+ if (do_move && c_ptr->m_idx)
+ {
+ auto z_ptr = y_ptr->race();
+ 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)
+ {
+ /* 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);
+ }
+
+ /* Check for monster trap */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ if (mon_hit_trap(m_idx)) return;
+ }
+ else
+ {
+ /* Copy list of objects; we need a copy because we're mutating the list. */
+ auto const object_idxs(c_ptr->o_idxs);
+
+ /* Scan all objects in the grid */
+ for (auto const this_o_idx: object_idxs)
+ {
+ /* Acquire object */
+ object_type * o_ptr = &o_list[this_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);
+ }
+
+ /* Put into inventory of monster */
+ {
+ /* 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;
+
+ /* Carry object */
+ m_ptr->hold_o_idxs.push_back(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 &&
+ (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;
+
+ int old_monster_race_idx;
+
+ u32b old_r_flags1 = 0L;
+ u32b old_r_flags2 = 0L;
+ u32b old_r_flags3 = 0L;
+ u32b old_r_flags4 = 0L;
+ u32b old_r_flags5 = 0L;
+ u32b old_r_flags6 = 0L;
+
+ byte old_r_blows0 = 0;
+ byte old_r_blows1 = 0;
+ byte old_r_blows2 = 0;
+ byte old_r_blows3 = 0;
+
+ byte old_r_cast_inate = 0;
+ byte old_r_cast_spell = 0;
+
+ /* Check the doppleganger */
+ if (doppleganger && !(r_info[m_list[doppleganger].r_idx].flags9 & RF9_DOPPLEGANGER))
+ doppleganger = 0;
+
+ /* Memorize old race */
+ old_monster_race_idx = monster_race_idx;
+
+ /* Acquire knowledge */
+ if (monster_race_idx)
+ {
+ /* Acquire current monster */
+ monster_race *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 */
+ auto const r_ptr = m_ptr->race();
+
+ /* Access the location */
+ fx = m_ptr->fx;
+ fy = m_ptr->fy;
+
+
+ /* Assume no move */
+ test = FALSE;
+
+ /* Control monster aint affected by distance */
+ if (p_ptr->control == i)
+ {
+ test = TRUE;
+ }
+
+ /* No free upkeep on partial summons just because they're out
+ * of line of sight. */
+ else if (m_ptr->mflag & MFLAG_PARTIAL) test = TRUE;
+
+ /* Handle "sensing radius" */
+ else if (m_ptr->cdis <= r_ptr->aaf)
+ {
+ /* We can "sense" the player */
+ test = TRUE;
+ }
+
+ /* Handle "sight" and "aggravation" */
+ else if ((m_ptr->cdis <= MAX_SIGHT) &&
+ (player_has_los_bold(fy, fx) ||
+ p_ptr->aggravate))
+ {
+ /* We can "see" or "feel" the player */
+ test = TRUE;
+ }
+
+ /* Hack -- Monsters can "smell" the player from far away */
+ /* Note that most monsters have "aaf" of "20" or so */
+ else if (flow_by_sound &&
+ (cave[p_ptr->py][p_ptr->px].when == cave[fy][fx].when) &&
+ (cave[fy][fx].cost < MONSTER_FLOW_DEPTH) &&
+ (cave[fy][fx].cost < r_ptr->aaf))
+ {
+ /* We can "smell" the player */
+ test = TRUE;
+ }
+
+ /* Running away wont save them ! */
+ if (m_ptr->poisoned || m_ptr->bleeding) test = TRUE;
+
+ /* Do nothing */
+ if (!test) continue;
+
+ /* Save global index */
+ hack_m_idx = i;
+
+ if (is_friend(m_ptr) > 0) is_frien = TRUE;
+
+ /* Process the monster */
+ process_monster(i, is_frien);
+
+ /* Hack -- notice death or departure */
+ if (!alive || death) break;
+
+ /* If it's still alive and friendly, charge upkeep. */
+ if (m_ptr->mflag & MFLAG_PARTIAL) summon_maint(i);
+
+ /* Notice leaving */
+ if (p_ptr->leaving) break;
+ }
+
+ /* Reset global index */
+ hack_m_idx = 0;
+
+
+ /* Tracking a monster race (the same one we were before) */
+ if (monster_race_idx && (monster_race_idx == old_monster_race_idx))
+ {
+ /* Acquire monster race */
+ 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/melee2.hpp b/src/melee2.hpp
new file mode 100644
index 00000000..fece0564
--- /dev/null
+++ b/src/melee2.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "h-basic.h"
+#include "monster_type_fwd.hpp"
+
+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 void mon_handle_fear(monster_type *m_ptr, int dam, bool_ *fear);
+extern int check_hit2(int power, int level, int ac);
+extern void process_monsters(void);
+extern void curse_equipment(int chance, int heavy_chance);
+extern void curse_equipment_dg(int chance, int heavy_chance);
diff --git a/src/messages.cc b/src/messages.cc
new file mode 100644
index 00000000..a4ce949d
--- /dev/null
+++ b/src/messages.cc
@@ -0,0 +1,368 @@
+#include "messages.hpp"
+
+#include "tome/make_array.hpp"
+
+#include "z-term.h"
+#include "z-form.h"
+#include "z-util.h"
+
+/*
+ * OPTION: Maximum number of messages to remember (see "io.c")
+ * Default: assume maximal memorization of 2048 total messages
+ */
+#define MESSAGE_MAX 2048
+
+/*
+ * 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
+
+
+
+
+/*
+ * The next "free" index to use
+ */
+static u16b message__next;
+
+/*
+ * The index of the oldest message (none yet)
+ */
+static u16b message__last;
+
+/*
+ * The next "free" offset
+ */
+static u16b message__head;
+
+/*
+ * The offset to the oldest used char (none yet)
+ */
+static u16b message__tail;
+
+/*
+ * The array of offsets, by index [MESSAGE_MAX]
+ */
+static u16b *message__ptr;
+
+/*
+ * The array of colors, by index [MESSAGE_MAX]
+ */
+static byte *message__color;
+
+/*
+ * The array of message counts, by index [MESSAGE_MAX]
+ */
+static u16b *message__count;
+
+/*
+ * The array of chars, by offset [MESSAGE_BUF]
+ */
+static char *message__buf;
+
+
+/*
+* 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.
+*/
+
+void message_init()
+{
+ /* Message variables */
+ message__ptr = make_array<u16b>(MESSAGE_MAX);
+ message__color = make_array<byte>(MESSAGE_MAX);
+ message__count = make_array<u16b>(MESSAGE_MAX);
+ message__buf = make_array<char>(MESSAGE_BUF);
+
+ /* Hack -- No messages yet */
+ message__tail = MESSAGE_BUF;
+}
+
+/*
+* 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);
+}
+
+
+/*
+* Add a new message, with great efficiency
+*/
+void message_add(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__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__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;
+}
diff --git a/src/messages.hpp b/src/messages.hpp
new file mode 100644
index 00000000..22943ab9
--- /dev/null
+++ b/src/messages.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "h-basic.h"
+
+void message_init();
+s16b message_num();
+cptr message_str(int age);
+byte message_color(int age);
+void message_add(cptr msg, byte color);
diff --git a/src/meta_class_type.hpp b/src/meta_class_type.hpp
new file mode 100644
index 00000000..e74e75b3
--- /dev/null
+++ b/src/meta_class_type.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct meta_class_type
+{
+ char name[80]; /* Name */
+ byte color;
+ s16b *classes; /* list of classes */
+};
diff --git a/src/meta_class_type_fwd.hpp b/src/meta_class_type_fwd.hpp
new file mode 100644
index 00000000..2d0e482a
--- /dev/null
+++ b/src/meta_class_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct meta_class_type;
diff --git a/src/mimic.cc b/src/mimic.cc
new file mode 100644
index 00000000..edf79f4b
--- /dev/null
+++ b/src/mimic.cc
@@ -0,0 +1,728 @@
+#include "mimic.hpp"
+
+#include "player_type.hpp"
+#include "skill_type.hpp"
+#include "stats.hpp"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+/**
+ * Mimicry forms
+ */
+typedef struct mimic_duration_type mimic_duration_type;
+struct mimic_duration_type
+{
+ s16b min;
+ s16b max;
+};
+
+typedef struct mimic_form_type mimic_form_type;
+struct mimic_form_type
+{
+ int modules[3]; /* Modules where this mimicry form is available; terminated with a -1 entry */
+ cptr name; /* Name of mimicry form */
+ cptr obj_name; /* Object mimicry form name */
+ cptr desc; /* Description */
+ cptr realm; /* Realm of mimicry */
+ bool_ limit; /* If true, the form is not available except through special means */
+ byte level;
+ byte rarity;
+ mimic_duration_type duration;
+ s32b (*calc)(); /* Callback to calculate bonuses; return number of blows to add */
+ void (*power)(); /* Callback to calculate powers */
+};
+
+static s32b abomination_calc()
+{
+ apply_flags(TR1_SPEED + TR1_STR + TR1_INT + TR1_WIS + TR1_DEX + TR1_CON + TR1_CHR, 0, 0, 0, 0, 0, -10, 0, 0, 0, 0);
+ p_ptr->xtra_f3 |= TR3_AGGRAVATE;
+
+ return 0;
+}
+
+static s32b mouse_calc()
+{
+ /* Mice run! */
+ p_ptr->pspeed += 5 + (p_ptr->mimic_level / 7);
+
+ /* They can crawl under your armor to hit you ;) */
+ p_ptr->to_h = p_ptr->to_h + 10 + (p_ptr->mimic_level / 5);
+ p_ptr->dis_to_h = p_ptr->dis_to_h + 10 + (p_ptr->mimic_level / 5);
+
+ /* But they are not very powerfull */
+ p_ptr->to_d = p_ptr->to_d / 5;
+ p_ptr->dis_to_d = p_ptr->dis_to_d / 5;
+
+ /* But they are stealthy */
+ p_ptr->skill_stl = p_ptr->skill_stl + 10 + (p_ptr->mimic_level / 5);
+
+ /* Stat mods */
+ p_ptr->stat_add[A_STR] += -5;
+ p_ptr->stat_add[A_DEX] += 3;
+ p_ptr->stat_add[A_CON] += 1;
+
+ return 0;
+}
+
+static void mouse_power()
+{
+ if (p_ptr->mimic_level >= 30)
+ {
+ p_ptr->powers[POWER_INVISIBILITY] = TRUE;
+ }
+}
+
+static s32b eagle_calc()
+{
+ p_ptr->ffall = TRUE;
+ p_ptr->pspeed = p_ptr->pspeed + 2 + (p_ptr->mimic_level / 6);
+
+ p_ptr->stat_add[A_STR] += -3;
+ p_ptr->stat_add[A_DEX] += 2 + (p_ptr->mimic_level / 15);
+ p_ptr->stat_add[A_CON] += 4 + (p_ptr->mimic_level / 20);
+ p_ptr->stat_add[A_INT] += -1;
+ p_ptr->stat_add[A_WIS] += 1;
+ p_ptr->stat_add[A_CHR] += -1;
+
+ if (p_ptr->mimic_level >= 20)
+ {
+ p_ptr->xtra_f4 |= TR4_FLY;
+ p_ptr->xtra_f3 |= TR3_SEE_INVIS;
+ }
+
+ if (p_ptr->mimic_level >= 25)
+ {
+ p_ptr->xtra_f2 |= TR2_FREE_ACT;
+ }
+
+ if (p_ptr->mimic_level >= 30)
+ {
+ p_ptr->xtra_f2 |= TR2_RES_ELEC;
+ }
+
+ if (p_ptr->mimic_level >= 35)
+ {
+ p_ptr->xtra_f3 |= TR3_SH_ELEC;
+ }
+
+ return 0;
+}
+
+static s32b wolf_calc()
+{
+ p_ptr->stat_add[A_STR] += 2 + (p_ptr->mimic_level / 20);
+ p_ptr->stat_add[A_DEX] += 3 + (p_ptr->mimic_level / 20);
+ p_ptr->stat_add[A_INT] += -3;
+ p_ptr->stat_add[A_CHR] += -2;
+
+ p_ptr->pspeed = p_ptr->pspeed + 10 + (p_ptr->mimic_level / 5);
+
+ p_ptr->xtra_f2 |= TR2_FREE_ACT;
+ p_ptr->xtra_f2 |= TR2_RES_FEAR;
+
+ if (p_ptr->mimic_level >= 10)
+ {
+ p_ptr->xtra_f2 |= TR2_RES_COLD;
+ }
+
+ if (p_ptr->mimic_level >= 15)
+ {
+ p_ptr->xtra_f3 |= TR3_SEE_INVIS;
+ }
+
+ if (p_ptr->mimic_level >= 30)
+ {
+ p_ptr->xtra_f2 |= TR2_RES_DARK;
+ }
+
+ if (p_ptr->mimic_level >= 35)
+ {
+ p_ptr->xtra_f2 |= TR2_RES_CONF;
+ }
+
+ return 0;
+}
+
+static s32b spider_calc()
+{
+ p_ptr->stat_add[A_STR] += -4;
+ p_ptr->stat_add[A_DEX] += 1 + (p_ptr->mimic_level / 8);
+ p_ptr->stat_add[A_INT] += 1 + (p_ptr->mimic_level / 5);
+ p_ptr->stat_add[A_WIS] += 1 + (p_ptr->mimic_level / 5);
+ p_ptr->stat_add[A_CON] += -5;
+ p_ptr->stat_add[A_CHR] += -10;
+
+ p_ptr->pspeed = p_ptr->pspeed + 5;
+
+ p_ptr->xtra_f2 |= TR2_RES_POIS;
+ p_ptr->xtra_f2 |= TR2_RES_FEAR;
+ p_ptr->xtra_f2 |= TR2_RES_DARK;
+
+ if (p_ptr->mimic_level >= 40)
+ {
+ p_ptr->xtra_f4 |= TR4_CLIMB;
+ }
+
+ return 0;
+}
+
+static void spider_power()
+{
+ if (p_ptr->mimic_level >= 25)
+ {
+ p_ptr->powers[POWER_WEB] = TRUE;
+ }
+}
+
+static s32b ent_calc()
+{
+ p_ptr->pspeed = p_ptr->pspeed - 5 - (p_ptr->mimic_level / 10);
+
+ p_ptr->to_a = p_ptr->to_a + 10 + p_ptr->mimic_level;
+ p_ptr->dis_to_a = p_ptr->dis_to_a + 10 + p_ptr->mimic_level;
+
+ p_ptr->stat_add[A_STR] += p_ptr->mimic_level / 5;
+ p_ptr->stat_add[A_INT] += - (p_ptr->mimic_level / 7);
+ p_ptr->stat_add[A_WIS] += - (p_ptr->mimic_level / 7);
+ p_ptr->stat_add[A_DEX] += -4;
+ p_ptr->stat_add[A_CON] += p_ptr->mimic_level / 5;
+ p_ptr->stat_add[A_CHR] += -7;
+
+ p_ptr->xtra_f2 |= TR2_RES_POIS;
+ p_ptr->xtra_f2 |= TR2_RES_COLD;
+ p_ptr->xtra_f2 |= TR2_FREE_ACT;
+ p_ptr->xtra_f3 |= TR3_REGEN;
+ p_ptr->xtra_f3 |= TR3_SEE_INVIS;
+ p_ptr->xtra_f2 |= TR2_SENS_FIRE;
+
+ return 0;
+}
+
+static void ent_power()
+{
+ p_ptr->powers[PWR_GROW_TREE] = TRUE;
+}
+
+static s32b vapour_calc()
+{
+ p_ptr->pspeed = p_ptr->pspeed + 5;
+
+ /* Try to hit a cloud! */
+ p_ptr->to_a = p_ptr->to_a + 40 + p_ptr->mimic_level;
+ p_ptr->dis_to_a = p_ptr->dis_to_a + 40 + p_ptr->mimic_level;
+
+ /* Try to hit WITH a cloud! */
+ p_ptr->to_h = p_ptr->to_h - 40;
+ p_ptr->dis_to_h = p_ptr->dis_to_h - 40;
+
+ /* Stat mods */
+ p_ptr->stat_add[A_STR] += -4;
+ p_ptr->stat_add[A_DEX] += 5;
+ p_ptr->stat_add[A_CON] += -4;
+ p_ptr->stat_add[A_CHR] += -10;
+
+ /* But they are stealthy */
+ p_ptr->skill_stl = p_ptr->skill_stl + 10 + (p_ptr->mimic_level / 5);
+ p_ptr->xtra_f2 |= TR2_RES_POIS;
+ p_ptr->xtra_f2 |= TR2_RES_SHARDS;
+ p_ptr->xtra_f2 |= TR2_IM_COLD;
+ p_ptr->xtra_f2 |= TR2_FREE_ACT;
+ p_ptr->xtra_f3 |= TR3_REGEN;
+ p_ptr->xtra_f3 |= TR3_SEE_INVIS;
+ p_ptr->xtra_f2 |= TR2_SENS_FIRE;
+ p_ptr->xtra_f3 |= TR3_FEATHER;
+
+ return 0;
+}
+
+static s32b serpent_calc()
+{
+ p_ptr->pspeed = p_ptr->pspeed + 10 + (p_ptr->mimic_level / 6);
+
+ p_ptr->to_a = p_ptr->to_a + 3 + (p_ptr->mimic_level / 8);
+ p_ptr->dis_to_a = p_ptr->dis_to_a + 3 + (p_ptr->mimic_level / 8);
+
+ p_ptr->stat_add[A_STR] += p_ptr->mimic_level / 8;
+ p_ptr->stat_add[A_INT] += -6;
+ p_ptr->stat_add[A_WIS] += -6;
+ p_ptr->stat_add[A_DEX] += -4;
+ p_ptr->stat_add[A_CON] += p_ptr->mimic_level / 7;
+ p_ptr->stat_add[A_CHR] += -6;
+
+ p_ptr->xtra_f2 |= TR2_RES_POIS;
+ if (p_ptr->mimic_level >= 25)
+ {
+ p_ptr->xtra_f2 |= TR2_FREE_ACT;
+ }
+
+ return 0;
+}
+
+static s32b mumak_calc()
+{
+ p_ptr->pspeed = p_ptr->pspeed - 5 - (p_ptr->mimic_level / 10);
+
+ p_ptr->to_a = p_ptr->to_a + 10 + (p_ptr->mimic_level / 6);
+ p_ptr->dis_to_a = p_ptr->dis_to_a + 10 + (p_ptr->mimic_level / 6);
+ p_ptr->to_d = p_ptr->to_d + 5 + ((p_ptr->mimic_level * 2) / 3);
+ p_ptr->dis_to_d = p_ptr->dis_to_d + 5 + ((p_ptr->mimic_level * 2) / 3);
+
+ p_ptr->stat_add[A_STR] += p_ptr->mimic_level / 4;
+ p_ptr->stat_add[A_INT] += -8;
+ p_ptr->stat_add[A_WIS] += -4;
+ p_ptr->stat_add[A_DEX] += -5;
+ p_ptr->stat_add[A_CON] += p_ptr->mimic_level / 3;
+ p_ptr->stat_add[A_CHR] += -10;
+
+ if (p_ptr->mimic_level >= 10)
+ {
+ p_ptr->xtra_f2 |= TR2_RES_FEAR;
+ }
+
+ if (p_ptr->mimic_level >= 25)
+ {
+ p_ptr->xtra_f2 |= TR2_RES_CONF;
+ }
+
+ if (p_ptr->mimic_level >= 30)
+ {
+ p_ptr->xtra_f2 |= TR2_FREE_ACT;
+ }
+
+ if (p_ptr->mimic_level >= 35)
+ {
+ p_ptr->xtra_f2 |= TR2_RES_NEXUS;
+ }
+
+ return 0;
+}
+
+static s32b bear_calc()
+{
+ p_ptr->pspeed = p_ptr->pspeed - 5 + (p_ptr->mimic_level / 5);
+
+ p_ptr->to_a = p_ptr->to_a + 5 + ((p_ptr->mimic_level * 2) / 3);
+ p_ptr->dis_to_a = p_ptr->dis_to_a + 5 + ((p_ptr->mimic_level * 2) / 3);
+
+ p_ptr->stat_add[A_STR] += p_ptr->mimic_level / 11;
+ p_ptr->stat_add[A_INT] += p_ptr->mimic_level / 11;
+ p_ptr->stat_add[A_WIS] += p_ptr->mimic_level / 11;
+ p_ptr->stat_add[A_DEX] += -1;
+ p_ptr->stat_add[A_CON] += p_ptr->mimic_level / 11;
+ p_ptr->stat_add[A_CHR] += -10;
+
+ if (p_ptr->mimic_level >= 10)
+ {
+ p_ptr->xtra_f2 |= TR2_FREE_ACT;
+ }
+
+ if (p_ptr->mimic_level >= 20)
+ {
+ p_ptr->xtra_f3 |= TR3_REGEN;
+ }
+
+ if (p_ptr->mimic_level >= 30)
+ {
+ p_ptr->xtra_f2 |= TR2_RES_CONF;
+ }
+
+ if (p_ptr->mimic_level >= 35)
+ {
+ p_ptr->xtra_f2 |= TR2_RES_NEXUS;
+ }
+
+ /* activate the skill */
+ s_info[SKILL_BEAR].hidden = FALSE;
+
+ return 0;
+}
+
+static s32b balrog_calc()
+{
+ p_ptr->stat_add[A_STR] += 5 + p_ptr->mimic_level / 5;
+ p_ptr->stat_add[A_INT] += p_ptr->mimic_level / 10;
+ p_ptr->stat_add[A_WIS] += - ( 5 + p_ptr->mimic_level / 10);
+ p_ptr->stat_add[A_DEX] += p_ptr->mimic_level / 10;
+ p_ptr->stat_add[A_CON] += 5 + p_ptr->mimic_level / 5;
+ p_ptr->stat_add[A_CHR] += - ( 5 + p_ptr->mimic_level / 10);
+
+ p_ptr->xtra_f2 |= TR2_IM_ACID;
+ p_ptr->xtra_f2 |= TR2_IM_FIRE;
+ p_ptr->xtra_f2 |= TR2_IM_ELEC;
+ p_ptr->xtra_f2 |= TR2_RES_DARK;
+ p_ptr->xtra_f2 |= TR2_RES_CHAOS;
+ p_ptr->xtra_f2 |= TR2_RES_POIS;
+ p_ptr->xtra_f2 |= TR2_HOLD_LIFE;
+ p_ptr->xtra_f3 |= TR3_FEATHER;
+ p_ptr->xtra_f3 |= TR3_REGEN;
+ p_ptr->xtra_f3 |= TR3_SH_FIRE;
+ p_ptr->xtra_f3 |= TR3_LITE1;
+
+ return 1; /* Adds a blow */
+}
+
+static s32b maia_calc()
+{
+ p_ptr->stat_add[A_STR] += 5 + p_ptr->mimic_level / 5;
+ p_ptr->stat_add[A_INT] += 5 + p_ptr->mimic_level / 5;
+ p_ptr->stat_add[A_WIS] += 5 + p_ptr->mimic_level / 5;
+ p_ptr->stat_add[A_DEX] += 5 + p_ptr->mimic_level / 5;
+ p_ptr->stat_add[A_CON] += 5 + p_ptr->mimic_level / 5;
+ p_ptr->stat_add[A_CHR] += 5 + p_ptr->mimic_level / 5;
+
+ p_ptr->xtra_f2 |= TR2_IM_FIRE;
+ p_ptr->xtra_f2 |= TR2_IM_ELEC;
+ p_ptr->xtra_f2 |= TR2_IM_ACID;
+ p_ptr->xtra_f2 |= TR2_IM_COLD;
+ p_ptr->xtra_f2 |= TR2_RES_POIS;
+ p_ptr->xtra_f2 |= TR2_RES_LITE;
+ p_ptr->xtra_f2 |= TR2_RES_DARK;
+ p_ptr->xtra_f2 |= TR2_RES_CHAOS;
+ p_ptr->xtra_f2 |= TR2_HOLD_LIFE;
+ p_ptr->xtra_f3 |= TR3_FEATHER;
+ p_ptr->xtra_f3 |= TR3_REGEN;
+
+ return 2; /* Add two blows */
+}
+
+static s32b fire_elemental_calc()
+{
+ p_ptr->stat_add[A_STR] += 5 + (p_ptr->mimic_level / 5);
+ p_ptr->stat_add[A_DEX] += 5 + (p_ptr->mimic_level / 5);
+ p_ptr->stat_add[A_WIS] += -5 - (p_ptr->mimic_level / 5);
+
+ p_ptr->xtra_f2 |= TR2_IM_FIRE;
+ p_ptr->xtra_f2 |= TR2_RES_POIS;
+ p_ptr->xtra_f3 |= TR3_SH_FIRE;
+ p_ptr->xtra_f3 |= TR3_LITE1;
+
+ return 0;
+}
+
+/*
+ * Mimicry forms
+ */
+static mimic_form_type mimic_forms[MIMIC_FORMS_MAX] =
+{
+ { /* 0 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Abomination", /* MUST be at index 0! */
+ "Abominable Cloak",
+ "Abominations are failed experiments of powerful wizards.",
+ NULL /* no realm */,
+ FALSE,
+ 1, 101, {20, 100},
+ abomination_calc,
+ NULL,
+ },
+
+ /*
+ * Nature forms
+ */
+
+ { /* 1 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Mouse",
+ "Mouse Fur",
+ "Mice are small, fast and very stealthy",
+ "nature",
+ FALSE,
+ 1, 10, {20, 40},
+ mouse_calc,
+ mouse_power,
+ },
+
+ { /* 2 */
+ { MODULE_TOME, -1 },
+ "Eagle",
+ "Feathers Cloak",
+ "Eagles are master of the air, good hunters with excellent vision.",
+ "nature",
+ FALSE,
+ 10, 30, {10, 50},
+ eagle_calc,
+ NULL,
+ },
+
+ { /* 3 */
+ { MODULE_THEME, -1 },
+ "Eagle",
+ "Feathered Cloak",
+ "Eagles are master of the air, good hunters with excellent vision.",
+ "nature",
+ FALSE,
+ 10, 30, {10, 50},
+ eagle_calc,
+ NULL,
+ },
+
+ { /* 4 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Wolf",
+ "Wolf Pelt",
+ "Wolves are masters of movement, strong and have excellent eyesight.",
+ "nature",
+ FALSE,
+ 20, 40, {10, 50},
+ wolf_calc,
+ NULL,
+ },
+
+ { /* 5 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Spider",
+ "Spider Web",
+ "Spiders are clever and become good climbers.",
+ "nature",
+ FALSE,
+ 25, 50, {10, 50},
+ spider_calc,
+ spider_power,
+ },
+
+ { /* 6 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Elder Ent",
+ "Entish Bark",
+ "Ents are powerful tree-like beings dating from the dawn of time.",
+ "nature",
+ TRUE,
+ 40, 60, {10, 30},
+ ent_calc,
+ ent_power,
+ },
+
+ { /* 7 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Vapour",
+ "Cloak of Mist",
+ "A sentient cloud, darting around",
+ "nature",
+ FALSE,
+ 15, 10, {10, 40},
+ vapour_calc,
+ NULL,
+ },
+
+ { /* 8 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Serpent",
+ "Snakeskin Cloak",
+ "Serpents are fast, lethal predators.",
+ "nature",
+ FALSE,
+ 30, 25, {15, 20},
+ serpent_calc,
+ NULL,
+ },
+
+ { /* 9 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Mumak",
+ "Mumak Hide",
+ "A giant, elaphantine form.",
+ "nature",
+ FALSE,
+ 40, 40, {15, 20},
+ mumak_calc,
+ NULL,
+ },
+
+ /*
+ * Extra shapes
+ */
+
+ { /* 10 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Bear",
+ NULL,
+ "A fierce, terrible bear.",
+ NULL /* no realm */,
+ TRUE,
+ 1, 101, {50, 200},
+ bear_calc,
+ NULL,
+ },
+
+ { /* 11 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Balrog",
+ NULL,
+ "A corrupted maia.",
+ NULL /* no realm */,
+ TRUE,
+ 1, 101, {30, 70},
+ balrog_calc,
+ NULL,
+ },
+
+ { /* 12 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Maia",
+ NULL,
+ "A near god-like being.",
+ NULL /* no realm */,
+ TRUE,
+ 1, 101, {30, 70},
+ maia_calc,
+ NULL,
+ },
+
+ { /* 13 */
+ { MODULE_TOME, MODULE_THEME, -1 },
+ "Fire Elem.",
+ NULL,
+ "A towering column of flames",
+ NULL /* no realm */,
+ TRUE,
+ 1, 101, {10, 10},
+ fire_elemental_calc,
+ NULL,
+ },
+
+};
+
+/*
+ * Is the mimicry form enabled for the current module?
+ */
+static bool_ mimic_form_enabled(mimic_form_type *f)
+{
+ int i;
+
+ for (i = 0; f->modules[i] >= 0; i++)
+ {
+ if (f->modules[i] == game_module_idx)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Get a mimic form by index
+ */
+static mimic_form_type *get_mimic_form(int mf_idx)
+{
+ assert(mf_idx >= 0);
+ assert(mf_idx < MIMIC_FORMS_MAX);
+ return &mimic_forms[mf_idx];
+}
+
+/*
+ * Find a mimic by name
+ */
+s16b resolve_mimic_name(cptr name)
+{
+ s16b i;
+
+ for (i = 0; i < MIMIC_FORMS_MAX; i++)
+ {
+ mimic_form_type *mf_ptr = get_mimic_form(i);
+ if (mimic_form_enabled(mf_ptr) && streq(mf_ptr->name, name))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Find a random mimic form
+ */
+s16b find_random_mimic_shape(byte level, bool_ limit)
+{
+ int tries = 1000;
+
+ while (tries > 0)
+ {
+ int mf_idx = 0;
+ mimic_form_type *mf_ptr = NULL;
+
+ tries = tries - 1;
+
+ mf_idx = rand_int(MIMIC_FORMS_MAX);
+ mf_ptr = get_mimic_form(mf_idx);
+
+ if (mimic_form_enabled(mf_ptr))
+ {
+ if (limit >= mf_ptr->limit)
+ {
+ if ((rand_int(mf_ptr->level * 3) < level) &&
+ (mf_ptr->rarity < 100) &&
+ (magik(100 - mf_ptr->rarity)))
+ {
+ return mf_idx;
+ }
+ }
+ }
+ }
+ /* Abomination */
+ return 0;
+}
+
+/*
+ * Get mimic name
+ */
+cptr get_mimic_name(s16b mf_idx)
+{
+ return get_mimic_form(mf_idx)->name;
+}
+
+/*
+ * Get mimic object name
+ */
+cptr get_mimic_object_name(s16b mf_idx)
+{
+ return get_mimic_form(mf_idx)->obj_name;
+}
+
+/*
+ * Get mimic object level
+ */
+byte get_mimic_level(s16b mf_idx)
+{
+ return get_mimic_form(mf_idx)->level;
+}
+
+/*
+ * Get a random duration for the given mimic form
+ */
+s32b get_mimic_random_duration(s16b mf_idx)
+{
+ mimic_form_type *mf_ptr = get_mimic_form(mf_idx);
+ return rand_range(mf_ptr->duration.min, mf_ptr->duration.max);
+}
+
+/*
+ * Calculate bonuses for player's current mimic form
+ */
+byte calc_mimic()
+{
+ mimic_form_type *mf_ptr = get_mimic_form(p_ptr->mimic_form);
+ if (mf_ptr->calc != NULL)
+ {
+ return mf_ptr->calc();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/*
+ * Calculate powers for player's current mimic form
+ */
+void calc_mimic_power()
+{
+ mimic_form_type *mf_ptr = get_mimic_form(p_ptr->mimic_form);
+ if (mf_ptr->power != NULL)
+ {
+ mf_ptr->power();
+ }
+}
diff --git a/src/mimic.hpp b/src/mimic.hpp
new file mode 100644
index 00000000..d9c8f3bd
--- /dev/null
+++ b/src/mimic.hpp
@@ -0,0 +1,10 @@
+#include "h-basic.h"
+
+extern s16b resolve_mimic_name(cptr name);
+extern s16b find_random_mimic_shape(byte level, bool_ limit);
+extern cptr get_mimic_name(s16b mf_idx);
+extern cptr get_mimic_object_name(s16b mf_idx);
+extern byte get_mimic_level(s16b mf_idx);
+extern s32b get_mimic_random_duration(s16b mf_idx);
+extern byte calc_mimic();
+extern void calc_mimic_power();
diff --git a/src/module_type.hpp b/src/module_type.hpp
new file mode 100644
index 00000000..96938856
--- /dev/null
+++ b/src/module_type.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Module descriptor.
+ */
+struct module_type
+{
+ /* Metadata about the module: author, description, etc. */
+ struct {
+ /* Module name */
+ cptr name;
+
+ /* Module version number */
+ struct {
+ s32b major;
+ s32b minor;
+ s32b patch;
+ } version;
+
+ /* Module author */
+ struct {
+ cptr name;
+ cptr email;
+ } author;
+
+ /* Module description */
+ cptr desc;
+
+ /* Save file tag */
+ cptr save_file_tag;
+
+ /* Module directory */
+ cptr module_dir;
+ } meta;
+
+ /* Random artifact generation chances */
+ struct {
+ s32b weapon_chance;
+ s32b armor_chance;
+ s32b jewelry_chance;
+ } randarts;
+
+ /* Max player level. */
+ int max_plev;
+
+ /* Skills */
+ struct {
+ /* Skill points per level */
+ s32b skill_per_level;
+ /* Maximum "overage" for skill points, i.e. how many skill
+ points you can go above your current level. */
+ s32b max_skill_overage;
+ } skills;
+
+ /* Function to show introduction to module */
+ void (*intro)();
+
+ /* Function to compute race status, i.e. whether monsters
+ are friendly/neutral towards the player. Returns NULL
+ to indicate that no override happens. */
+ s16b *(*race_status)(int r_idx);
+};
diff --git a/src/modules.cc b/src/modules.cc
new file mode 100644
index 00000000..c5d065f4
--- /dev/null
+++ b/src/modules.cc
@@ -0,0 +1,1279 @@
+/*
+ * 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 "modules.hpp"
+#include "modules.h"
+
+#include "birth.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "corrupt.hpp"
+#include "files.hpp"
+#include "hook_eat_in.hpp"
+#include "hook_give_in.hpp"
+#include "hook_move_in.hpp"
+#include "hook_stair_in.hpp"
+#include "hook_stair_out.hpp"
+#include "hook_new_monster_end_in.hpp"
+#include "hooks.hpp"
+#include "joke.hpp"
+#include "lua_bind.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+
+#include <cassert>
+#include <chrono>
+#include <thread>
+
+using std::this_thread::sleep_for;
+using std::chrono::milliseconds;
+
+/*
+ * 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);
+ }
+}
+
+static void module_reset_dir_aux(char **dir, cptr new_path)
+{
+ char buf[1024];
+
+ /* Build the new path */
+ strnfmt(buf, sizeof (buf), "%s%s%s", *dir, PATH_SEP, new_path);
+
+ free(*dir);
+ *dir = strdup(buf);
+
+ /* Make it if needed */
+ if (!private_check_user_directory(*dir))
+ quit(format("Unable to create module dir %s\n", *dir));
+}
+
+static void module_reset_dir(cptr dir, cptr new_path)
+{
+ char **d = 0;
+ char buf[1025];
+
+ 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, "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, "user") ||
+ !strcmp(dir, "note"))
+ {
+ 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);
+
+ free(*d);
+ *d = strdup(buf);
+
+ // Make it if needed */
+ if (!private_check_user_directory(*d))
+ {
+ quit(format("Unable to create module dir %s\n", *d));
+ }
+ }
+ else if (!strcmp(dir, "save"))
+ {
+ module_reset_dir_aux(&ANGBAND_DIR_SAVE, new_path);
+ }
+ else
+ {
+ /* Build the new path */
+ strnfmt(buf, 1024, "%s%s%s%s%s", ANGBAND_DIR_MODULES, PATH_SEP, new_path, PATH_SEP, dir);
+
+ free(*d);
+ *d = strdup(buf);
+ }
+}
+
+static void dump_modules(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 = ')';
+ }
+
+ strnfmt(buf, 40, "%c%c%c %s", pre, ind, post, modules[i].meta.name);
+
+ if (sel == i)
+ {
+ print_desc_aux(modules[i].meta.desc, 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(int module_idx)
+{
+ module_type *module_ptr = &modules[module_idx];
+
+ /* Initialize the module table */
+ game_module_idx = module_idx;
+
+ /* Do misc inits */
+ max_plev = module_ptr->max_plev;
+
+ RANDART_WEAPON = module_ptr->randarts.weapon_chance;
+ RANDART_ARMOR = module_ptr->randarts.armor_chance;
+ RANDART_JEWEL = module_ptr->randarts.jewelry_chance;
+
+ VERSION_MAJOR = module_ptr->meta.version.major;
+ VERSION_MINOR = module_ptr->meta.version.minor;
+ VERSION_PATCH = module_ptr->meta.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();
+}
+
+static void init_module(module_type *module_ptr)
+{
+ /* Set up module directories? */
+ cptr dir = module_ptr->meta.module_dir;
+ if (dir) {
+ module_reset_dir("core", dir);
+ module_reset_dir("data", dir);
+ module_reset_dir("dngn", dir);
+ module_reset_dir("edit", dir);
+ module_reset_dir("file", dir);
+ module_reset_dir("help", dir);
+ module_reset_dir("note", dir);
+ module_reset_dir("save", dir);
+ module_reset_dir("user", dir);
+ module_reset_dir("pref", dir);
+ }
+}
+
+bool_ module_savefile_loadable(cptr savefile_mod)
+{
+ return (strcmp(savefile_mod, modules[game_module_idx].meta.save_file_tag) == 0);
+}
+
+/* Did the player force a module on command line */
+cptr force_module = NULL;
+
+/* Find module index by name. Returns -1 if matching module not found */
+int find_module(cptr name)
+{
+ int i = 0;
+
+ for (i=0; i<MAX_MODULES; i++)
+ {
+ if (streq(name, modules[i].meta.name))
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* Display possible modules and select one */
+bool_ select_module()
+{
+ s32b k, sel, max;
+
+ /* How many modules? */
+ max = MAX_MODULES;
+
+ /* No need to bother the player if there is only one module */
+ sel = -1;
+ if (force_module) {
+ /* Find module by name */
+ sel = find_module(force_module);
+ }
+ /* Only a single choice */
+ if (max == 1) {
+ sel = 0;
+ }
+ /* No module selected */
+ if (sel != -1)
+ {
+ /* Process the module */
+ init_module(&modules[sel]);
+
+ game_module = modules[sel].meta.name;
+
+ activate_module(sel);
+
+ 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;
+
+ if (islower(k)) x = A2I(k);
+ else x = A2I(tolower(k)) + 26;
+
+ if ((x < 0) || (x >= max)) continue;
+
+ /* Process the module */
+ init_module(&modules[x]);
+
+ game_module = modules[x].meta.name;
+
+ activate_module(x);
+
+ return (FALSE);
+ }
+ }
+
+ /* Shouldnt happen */
+ return (FALSE);
+}
+
+static bool_ dleft(byte c, cptr str, int y, int o)
+{
+ int i = strlen(str);
+ int x = 39 - (strlen(str) / 2) + o;
+ while (i > 0)
+ {
+ int a = 0;
+ int time = 0;
+
+ if (str[i-1] != ' ')
+ {
+ while (a < x + i - 1)
+ {
+ Term_putch(a - 1, y, c, 32);
+ Term_putch(a, y, c, str[i-1]);
+ time = time + 1;
+ if (time >= 4)
+ {
+ sleep_for(milliseconds(1));
+ time = 0;
+ }
+ Term_redraw_section(a - 1, y, a, y);
+ a = a + 1;
+
+ if (inkey_scan()) {
+ return TRUE;
+ }
+ }
+ }
+
+ i = i - 1;
+ }
+ return FALSE;
+}
+
+static bool_ dright(byte c, cptr str, int y, int o)
+{
+ int n = strlen(str); // Conversion to int to avoid warnings
+ int x = 39 - (n / 2) + o;
+ for (int i = 1; i <= n; i++)
+ {
+ int a = 79;
+ int time = 0;
+
+ if (str[i-1] != ' ') {
+ while (a >= x + i - 1)
+ {
+ Term_putch(a + 1, y, c, 32);
+ Term_putch(a, y, c, str[i-1]);
+ time = time + 1;
+ if (time >= 4) {
+ sleep_for(milliseconds(1));
+ time = 0;
+ }
+ Term_redraw_section(a, y, a + 1, y);
+ a = a - 1;
+
+ if (inkey_scan()) {
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+typedef struct intro_text intro_text;
+struct intro_text
+{
+ bool_ (*drop_func)(byte, cptr, int, int);
+ byte color;
+ cptr text;
+ int y0;
+ int x0;
+};
+
+static bool_ show_intro(intro_text intro_texts[])
+{
+ int i = 0;
+
+ Term_clear();
+ for (i = 0; ; i++)
+ {
+ intro_text *it = &intro_texts[i];
+ if (it->drop_func == NULL)
+ {
+ break;
+ }
+ else if (it->drop_func(it->color, it->text, it->y0, it->x0))
+ {
+ /* Abort */
+ return TRUE;
+ }
+ }
+
+ /* Wait for key */
+ Term_putch(0, 0, TERM_DARK, 32);
+ inkey();
+
+ /* Continue */
+ return FALSE;
+}
+
+void tome_intro()
+{
+ intro_text intro1[] =
+ {
+ { dleft , TERM_L_BLUE, "Art thou an adventurer,", 10, 0, },
+ { dright, TERM_L_BLUE, "One who passes through the waterfalls we call danger", 11, -1, },
+ { dleft , TERM_L_BLUE, "to find the true nature of the legends beyond them?", 12, 0, },
+ { dright, TERM_L_BLUE, "If this is so, then seeketh me.", 13, -1, },
+ { dleft , TERM_WHITE , "[Press any key to continue]", 23, -1, },
+ { NULL , TERM_WHITE , NULL, 0, 0, }
+ };
+ intro_text intro2[] =
+ {
+ { dleft , TERM_L_BLUE , "DarkGod", 8, 0, },
+ { dright, TERM_WHITE , "in collaboration with", 9, -1, },
+ { dleft , TERM_L_GREEN, "Eru Iluvatar,", 10, 0, },
+ { dright, TERM_L_GREEN, "Manwe", 11, -1, },
+ { dleft , TERM_WHITE , "and", 12, 0, },
+ { dright, TERM_L_GREEN, "All the T.o.M.E. contributors(see credits.txt)", 13, -1, },
+ { dleft , TERM_WHITE , "present", 15, 1, },
+ { dright, TERM_YELLOW , "T.o.M.E.", 16, 0, },
+ { dleft , TERM_WHITE , "[Press any key to continue]", 23, -1, },
+ { NULL , TERM_WHITE , NULL, 0, 0, }
+ };
+
+ screen_save();
+
+ /* Intro 1 */
+ if (show_intro(intro1))
+ {
+ goto exit;
+ }
+
+ /* Intro 2 */
+ if (show_intro(intro2))
+ {
+ goto exit;
+ }
+
+exit:
+ screen_load();
+}
+
+void theme_intro()
+{
+ struct intro_text intro1[] =
+ {
+ { dleft , TERM_L_BLUE , "Three Rings for the Elven-kings under the sky,", 10, 0, },
+ { dright, TERM_L_BLUE , "Seven for the Dwarf-lords in their halls of stone,", 11, -1, },
+ { dleft , TERM_L_BLUE , "Nine for Mortal Men doomed to die,", 12, 0, },
+ { dright, TERM_L_BLUE , "One for the Dark Lord on his dark throne", 13, -1, },
+ { dleft , TERM_L_BLUE , "In the land of Mordor, where the Shadows lie.", 14, 0, },
+ { dright, TERM_L_BLUE , "One Ring to rule them all, One Ring to find them,", 15, -1, },
+ { dleft , TERM_L_BLUE , "One Ring to bring them all and in the darkness bind them", 16, 0, },
+ { dright, TERM_L_BLUE , "In the land of Mordor, where the Shadows lie.", 17, -1, },
+ { dright, TERM_L_GREEN, "--J.R.R. Tolkien", 18, 0, },
+ { dleft , TERM_WHITE , "[Press any key to continue]", 23, -1, },
+ { NULL , TERM_WHITE , NULL, 0, 0, },
+ };
+ struct intro_text intro2[] =
+ {
+ { dleft , TERM_L_BLUE , "furiosity", 8, 0, },
+ { dright, TERM_WHITE , "in collaboration with", 9, -1, },
+ { dleft , TERM_L_GREEN, "DarkGod and all the ToME contributors,", 10, 0, },
+ { dright, TERM_L_GREEN, "module creators, t-o-m-e.net forum posters,", 11, -1, },
+ { dleft , TERM_WHITE , "and", 12, 0, },
+ { dright, TERM_L_GREEN, "by the grace of the Valar", 13, -1, },
+ { dleft , TERM_WHITE , "present", 15, 1, },
+ { dright, TERM_YELLOW , "Theme (a module for ToME)", 16, 0, },
+ { dleft , TERM_WHITE , "[Press any key to continue]", 23, -1, },
+ { NULL , TERM_WHITE , NULL, 0, 0, },
+ };
+
+ screen_save();
+
+ /* Intro 1 */
+ if (show_intro(intro1))
+ {
+ goto exit;
+ }
+
+ /* Intro 2 */
+ if (show_intro(intro2))
+ {
+ goto exit;
+ }
+
+exit:
+ screen_load();
+}
+
+static bool_ auto_stat_gain_hook(void *data, void *in, void *out)
+{
+ while (p_ptr->last_rewarded_level * 5 <= p_ptr->lev)
+ {
+ do_inc_stat(A_STR);
+ do_inc_stat(A_INT);
+ do_inc_stat(A_WIS);
+ do_inc_stat(A_DEX);
+ do_inc_stat(A_CON);
+ do_inc_stat(A_CHR);
+
+ p_ptr->last_rewarded_level += 1;
+ }
+
+ return FALSE;
+}
+
+static bool_ drunk_takes_wine(void *data, void *in_, void *out)
+{
+ hook_give_in *in = (hook_give_in *) in_;
+ monster_type *m_ptr = &m_list[in->m_idx];
+ object_type *o_ptr = get_object(in->item);
+
+ if ((m_ptr->r_idx == test_monster_name("Singing, happy drunk")) &&
+ (o_ptr->tval == TV_FOOD) &&
+ ((o_ptr->sval == 38) ||
+ (o_ptr->sval == 39)))
+ {
+ cmsg_print(TERM_YELLOW, "'Hic!'");
+
+ /* Destroy item */
+ inc_stack_size_ex(in->item, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* Create empty bottle */
+ {
+ object_type forge;
+ object_prep(&forge, lookup_kind(TV_BOTTLE,1));
+ drop_near(&forge, 50, p_ptr->py, p_ptr->px);
+ return TRUE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static bool_ hobbit_food(void *data, void *in_, void *out)
+{
+ hook_give_in *in = (hook_give_in *) in_;
+ monster_type *m_ptr = &m_list[in->m_idx];
+ object_type *o_ptr = get_object(in->item);
+
+ if ((m_ptr->r_idx == test_monster_name("Scruffy-looking hobbit")) &&
+ (o_ptr->tval == TV_FOOD))
+ {
+ cmsg_print(TERM_YELLOW, "'Yum!'");
+
+ inc_stack_size_ex(in->item, -1, OPTIMIZE, NO_DESCRIBE);
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static bool_ smeagol_ring(void *data, void *in_, void *out)
+{
+ hook_give_in *in = (hook_give_in *) in_;
+ monster_type *m_ptr = &m_list[in->m_idx];
+ object_type *o_ptr = get_object(in->item);
+
+ if ((m_ptr->r_idx == test_monster_name("Smeagol")) &&
+ (o_ptr->tval == TV_RING))
+ {
+ cmsg_print(TERM_YELLOW, "'MY... PRECIOUSSSSS!!!'");
+
+ inc_stack_size_ex(in->item, -1, OPTIMIZE, NO_DESCRIBE);
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static bool_ longbottom_leaf(void *data, void *in_, void *out_)
+{
+ hook_eat_in *in = (hook_eat_in *) in_;
+
+ if ((in->o_ptr->tval == TV_FOOD) &&
+ (in->o_ptr->sval == 45))
+ {
+ msg_print("What a stress reliever!");
+ heal_insanity(1000);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static bool_ food_vessel(void *data, void *in_, void *out)
+{
+ hook_eat_in *in = (hook_eat_in *) in_;
+
+ if (((in->o_ptr->tval == TV_FOOD) && (in->o_ptr->sval == 43)) ||
+ ((in->o_ptr->tval == TV_FOOD) && (in->o_ptr->sval == 44)))
+ {
+ object_type forge;
+
+ object_prep(&forge, lookup_kind(TV_JUNK, 3));
+
+ forge.ident |= IDENT_MENTAL | IDENT_KNOWN;
+ inven_carry(&forge, FALSE);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Player must have appropriate keys to enter Erebor.
+ */
+static bool_ erebor_stair(void *data, void *in_, void *out_)
+{
+ hook_stair_in *in = (hook_stair_in *) in_;
+ hook_stair_out *out = (hook_stair_out *) out_;
+
+ if ((dungeon_type == 20) &&
+ (dun_level == 60) &&
+ (in->direction == STAIRS_DOWN))
+ {
+ int i, keys;
+
+ keys = 0;
+ for (i = 0; i < INVEN_TOTAL - 1; i++)
+ {
+ if ((p_ptr->inventory[i].name1 == 209) ||
+ (p_ptr->inventory[i].name1 == 210))
+ {
+ keys += 1;
+ }
+ }
+
+ if (keys >= 2)
+ {
+ msg_print("The moon-letters on the map show you "
+ "the keyhole! You use the key to enter.");
+ out->allow = TRUE;
+ }
+ else
+ {
+ msg_print("You have found a door, but you cannot "
+ "find a way to enter. Ask in Dale, perhaps?");
+ out->allow = FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Orthanc requires a key.
+ */
+static bool_ orthanc_stair(void *data, void *in_, void *out_)
+{
+ hook_stair_in *in = (hook_stair_in *) in_;
+ hook_stair_out *out = (hook_stair_out *) out_;
+
+ if ((dungeon_type == 36) &&
+ (dun_level == 39) &&
+ (in->direction == STAIRS_DOWN))
+ {
+ int i, keys;
+
+ keys = 0;
+ for (i = 0; i < INVEN_TOTAL - 1; i++)
+ {
+ if (p_ptr->inventory[i].name1 == 15)
+ {
+ keys += 1;
+ }
+ }
+
+ if (keys >= 1)
+ {
+ msg_print("#BYou have the key to the tower of Orthanc! You may proceed.#w");
+ out->allow = TRUE;
+ }
+ else
+ {
+ msg_print("#yYou may not enter Orthanc without the key to the gates!#w Rumours say the key was lost in the Mines of Moria...");
+ out->allow = FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Movement from Theme
+ */
+static bool_ theme_push_past(void *data, void *in_, void *out_)
+{
+ hook_move_in *p = (hook_move_in *) in_;
+ cave_type *c_ptr = &cave[p->y][p->x];
+
+ if (c_ptr->m_idx > 0)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ auto const mr_ptr = m_ptr->race();
+
+ if (m_ptr->status >= MSTATUS_NEUTRAL)
+ {
+ if (cave_floor_bold(p->y, p->x) ||
+ (mr_ptr->flags2 == RF2_PASS_WALL))
+ {
+ char buf[128];
+
+ monster_desc(buf, m_ptr, 0);
+ msg_print(format("You push past %s.", buf));
+
+ 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;
+ }
+ else
+ {
+ char buf[128];
+
+ monster_desc(buf, m_ptr, 0);
+ msg_print(format("%s is in your way!", buf));
+ energy_use = 0;
+
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Check if monster race is in list. The list is terminated
+ * with a -1.
+ */
+static bool_ race_in_list(int r_idx, int race_idxs[])
+{
+ int i;
+
+ for (i = 0; race_idxs[i] >= 0; i++)
+ {
+ if (r_idx == race_idxs[i])
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Monster racial alignment from Theme.
+ */
+s16b *theme_race_status(int r_idx)
+{
+ static s16b FRIEND_ = MSTATUS_FRIEND;
+ static s16b *FRIEND = &FRIEND_;
+ static s16b NEUTRAL_ = MSTATUS_NEUTRAL;
+ static s16b *NEUTRAL = &NEUTRAL_;
+
+ object_type *o_ptr = NULL;
+
+ switch (p_ptr->prace)
+ {
+ case RACE_MAIA:
+ {
+ int good_race_idxs[] = {
+ 25, 29, 45, 97, 109,
+ 147, 225, 335, 346, 443,
+ 581, 629, 699, 853, 984,
+ 1007, 1017, -1
+ };
+
+ if (!(player_has_corruption(CORRUPT_BALROG_AURA)) &&
+ !(player_has_corruption(CORRUPT_BALROG_WINGS)) &&
+ !(player_has_corruption(CORRUPT_BALROG_STRENGTH)) &&
+ !(player_has_corruption(CORRUPT_BALROG_FORM)) &&
+ race_in_list(r_idx, good_race_idxs))
+ {
+ /* Good beings (except swans, GWoPs, Wyrm
+ * Spirits, and some joke uniques) are
+ * coaligned with Maiar */
+ return FRIEND;
+ }
+
+ break;
+ }
+
+ case RACE_HUMAN:
+ case RACE_DUNADAN:
+ case RACE_DRUADAN:
+ case RACE_ROHANKNIGHT:
+ {
+ int nonevil_humanoid_race_idxs[] = {
+ 43, 45, 46, 83, 93,
+ 97, 109, 110, 142, 147,
+ 216, 225, 293, 345, 346,
+ 693, 699, 937, 988, 997,
+ 998, 1000, -1
+ };
+
+ if (race_in_list(r_idx, nonevil_humanoid_race_idxs))
+ {
+ return NEUTRAL;
+ }
+
+ break;
+ }
+
+ case RACE_ELF:
+ case RACE_HOBBIT:
+ case RACE_WOOD_ELF:
+ {
+ int nonevil_sentient_race_idxs[] = {
+ 43, 45, 46, 83, 93,
+ 97, 109, 110, 142, 147,
+ 216, 225, 293, 345, 346,
+ 693, 699, 937, 988, 997,
+ 998, 1000, 74, 103, 882,
+ 1017, -1
+ };
+
+ if (race_in_list(r_idx, nonevil_sentient_race_idxs))
+ {
+ return NEUTRAL;
+ }
+
+ break;
+ }
+
+ case RACE_GNOME:
+ {
+ int gnomish_race_idxs[] = {
+ 103, 281, 680, 984, 1001,
+ 1003, 1007, 1011, 1014, 1016,
+ -1
+ };
+
+ if (race_in_list(r_idx, gnomish_race_idxs))
+ {
+ return NEUTRAL;
+ }
+
+ break;
+ }
+
+ case RACE_DWARF:
+ case RACE_PETTY_DWARF:
+ {
+ int dwarvish_race_idxs[] = {
+ 111, 112, 179, 180, 181,
+ 182, -1
+ };
+
+ if (race_in_list(r_idx, dwarvish_race_idxs))
+ {
+ return NEUTRAL;
+ }
+
+ break;
+ }
+
+ case RACE_ORC:
+ {
+ int low_orc_race_idxs[] = {
+ 87, 118, 126, 149, 244,
+ 251, 264, -1
+ };
+
+ if ((p_ptr->pgod == GOD_MELKOR) &&
+ race_in_list(r_idx, low_orc_race_idxs))
+ {
+ return FRIEND;
+ }
+
+ break;
+ }
+
+ case RACE_TROLL:
+ {
+ int low_troll_race_idxs[] = {
+ 297, 401, 403, 424, 454,
+ 491, 496, 509, 538, -1
+ };
+
+ if ((p_ptr->pgod == GOD_MELKOR) &&
+ race_in_list(r_idx, low_troll_race_idxs))
+ {
+ return NEUTRAL;
+ }
+
+ break;
+ }
+
+ case RACE_HALF_OGRE:
+ {
+ int ogre_race_idxs[] = {
+ 262, 285, 415, 430, 479,
+ 745, 918, -1
+ };
+
+ if (race_in_list(r_idx, ogre_race_idxs))
+ {
+ return NEUTRAL;
+ }
+
+ break;
+ }
+
+ case RACE_BEORNING:
+ {
+ /* Bears; not werebears. */
+ int bear_race_idxs[] = {
+ 160, 173, 191, 854,
+ 855, 867, 873, -1
+ };
+
+ if (race_in_list(r_idx, bear_race_idxs))
+ {
+ return NEUTRAL;
+ }
+
+ break;
+ }
+
+ case RACE_DARK_ELF:
+ {
+ int dark_elven_race_idxs[] = {
+ 122, 178, 183, 226, 348,
+ 375, 400, 657, -1
+ };
+
+ if (race_in_list(r_idx, dark_elven_race_idxs))
+ {
+ return FRIEND;
+ }
+
+ break;
+ }
+
+ case RACE_ENT:
+ {
+ int plant_race_idxs[] = {
+ 248, 266, 317, 329, 396,
+ -1
+ };
+
+ if (race_in_list(r_idx, plant_race_idxs))
+ {
+ return FRIEND;
+ }
+
+ /* And since the above is largely useless except out
+ in the wild... If an Ent worships Yavanna,
+ lower-level animals are coaligned should make the
+ early game a bit easier for Ents. */
+
+ if (p_ptr->pgod == GOD_YAVANNA)
+ {
+ int lower_animal_race_idxs[] = {
+ 21, 23, 24, 25, 26,
+ 27, 28, 29, 30, 31,
+ 33, 35, 36, 37, 38,
+ 39, 41, 49, 50, 52,
+ 56, 57, 58, 59, 60,
+ 61, 62, 69, 70, 75,
+ 77, 78, 79, 86, 88,
+ 89, 90, 95, 96, 105,
+ 106, 114, 119, 120, 121,
+ 123, 127, 134, 141, 143,
+ 151, 154, 155, 156, 160,
+ 161, 168, 171, 173, 174,
+ 175, 176, 187, 191, 196,
+ 197, 198, 210, 211, 213,
+ 230, 236, 250, 259, -1
+ };
+
+ if (race_in_list(r_idx, lower_animal_race_idxs))
+ {
+ return FRIEND;
+ }
+ }
+
+ break;
+ }
+
+ case RACE_EAGLE:
+ {
+ int nonevil_nonneurtal_bird_race_idxs[] = {
+ 61, 141, 151, 279, -1
+ };
+
+ if (race_in_list(r_idx, nonevil_nonneurtal_bird_race_idxs))
+ {
+ return FRIEND;
+ }
+
+ break;
+ }
+
+ case RACE_DRAGON:
+ {
+ int hatchling_dragon_race_idxs[] = {
+ 163, 164, 165, 166, 167,
+ 204, 218, 219, 911, -1
+ };
+
+ if (race_in_list(r_idx, hatchling_dragon_race_idxs))
+ {
+ return FRIEND;
+ }
+
+ break;
+ }
+
+ case RACE_YEEK:
+ {
+ int yeek_race_idxs[] = {
+ 580, 583, 594, 653, 655,
+ 659, 661, -1
+ };
+
+ if (race_in_list(r_idx, yeek_race_idxs))
+ {
+ return NEUTRAL;
+ }
+
+ break;
+ }
+
+ };
+
+ /* Oathbreakers are coaligned if player is wielding Anduril.
+ It's dirty, but it works, and it doesn't bother checking
+ demons and the races who can't wield weapons. */
+ o_ptr = get_object(INVEN_WIELD);
+ if (o_ptr != NULL &&
+ o_ptr->name1 == ART_ANDURIL)
+ {
+ switch (p_ptr->prace)
+ {
+ case RACE_HUMAN:
+ case RACE_HALF_ELF:
+ case RACE_ELF:
+ case RACE_HOBBIT:
+ case RACE_GNOME:
+ case RACE_DWARF:
+ case RACE_ORC:
+ case RACE_TROLL:
+ case RACE_DUNADAN:
+ case RACE_HIGH_ELF:
+ case RACE_HALF_OGRE:
+ case RACE_BEORNING:
+ case RACE_DRUADAN:
+ case RACE_PETTY_DWARF:
+ case RACE_DARK_ELF:
+ case RACE_ENT:
+ case RACE_ROHANKNIGHT:
+ case RACE_YEEK:
+ case RACE_WOOD_ELF:
+ case RACE_MAIA:
+ case RACE_EASTERLING:
+ case RACE_DEMON:
+ {
+ int oathbreaker_race_idxs[] = {
+ 731, -1
+ };
+
+ if (race_in_list(r_idx, oathbreaker_race_idxs))
+ {
+ return FRIEND;
+ }
+
+ break;
+ }
+ }
+ }
+
+ /* No status override */
+ return NULL;
+}
+
+static bool_ theme_level_end_gen(void *data, void *in, void *out)
+{
+ int i = 0;
+
+ for (i = 0; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ int r_idx = m_ptr->r_idx;
+ s16b *status = theme_race_status(r_idx);
+ if (status)
+ {
+ m_ptr->status = *status;
+ }
+ }
+
+ return FALSE;
+}
+
+static bool_ theme_new_monster_end(void *data, void *in_, void *out)
+{
+ hook_new_monster_end_in *in = (hook_new_monster_end_in *) in_;
+ s16b *status = theme_race_status(in->m_ptr->r_idx);
+
+ if (status)
+ {
+ in->m_ptr->status = *status;
+ }
+
+ return FALSE;
+}
+
+void init_hooks_module()
+{
+ /*
+ * Common hooks
+ */
+ add_hook_new(HOOK_GIVE,
+ drunk_takes_wine,
+ "drunk_takes_wine",
+ NULL);
+
+ add_hook_new(HOOK_LEVEL_END_GEN,
+ gen_joke_monsters,
+ "gen_joke_monsters",
+ NULL);
+
+ /*
+ * Module-specific hooks
+ */
+ switch (game_module_idx)
+ {
+ case MODULE_TOME:
+ {
+ break;
+ }
+
+ case MODULE_THEME:
+ {
+ timer_aggravate_evil_enable();
+
+ add_hook_new(HOOK_PLAYER_LEVEL,
+ auto_stat_gain_hook,
+ "auto_stat_gain",
+ NULL);
+
+ add_hook_new(HOOK_GIVE,
+ hobbit_food,
+ "hobbit_food",
+ NULL);
+
+ add_hook_new(HOOK_GIVE,
+ smeagol_ring,
+ "smeagol_ring",
+ NULL);
+
+ add_hook_new(HOOK_EAT,
+ longbottom_leaf,
+ "longbottom_leaf",
+ NULL);
+
+ add_hook_new(HOOK_EAT,
+ food_vessel,
+ "food_vessel",
+ NULL);
+
+ add_hook_new(HOOK_STAIR,
+ erebor_stair,
+ "erebor_stair",
+ NULL);
+
+ add_hook_new(HOOK_STAIR,
+ orthanc_stair,
+ "orthanc_stair",
+ NULL);
+
+ add_hook_new(HOOK_MOVE,
+ theme_push_past,
+ "__hook_push_past",
+ NULL);
+
+ add_hook_new(HOOK_LEVEL_END_GEN,
+ theme_level_end_gen,
+ "theme_level_end_gen",
+ NULL);
+
+ add_hook_new(HOOK_NEW_MONSTER_END,
+ theme_new_monster_end,
+ "theme_new_monster_end",
+ NULL);
+
+ break;
+ }
+
+ default:
+ assert(FALSE);
+ }
+}
diff --git a/src/modules.h b/src/modules.h
new file mode 100644
index 00000000..8a3b88b0
--- /dev/null
+++ b/src/modules.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "h-basic.h"
+
+// C linkage required for these functions since main-* code uses them.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern bool_ private_check_user_directory(cptr dirpath);
+extern cptr force_module;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/modules.hpp b/src/modules.hpp
new file mode 100644
index 00000000..d83e3e2e
--- /dev/null
+++ b/src/modules.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern bool_ select_module(void);
+extern bool_ module_savefile_loadable(cptr savefile_mod);
+extern void tome_intro();
+extern void theme_intro();
+extern s16b *theme_race_status(int r_idx);
+extern void init_hooks_module();
+extern int find_module(cptr name);
diff --git a/src/monster1.cc b/src/monster1.cc
new file mode 100644
index 00000000..4e3e2c42
--- /dev/null
+++ b/src/monster1.cc
@@ -0,0 +1,1887 @@
+/*
+ * 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 "monster1.hpp"
+
+#include "cave_type.hpp"
+#include "monster2.hpp"
+#include "monster_ego.hpp"
+#include "monster_race.hpp"
+#include "player_type.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+
+/*
+ * 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(std::shared_ptr<monster_race const> r_ptr)
+{
+ 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(std::shared_ptr<monster_race const> r_ptr, int i)
+{
+ 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(std::shared_ptr<monster_race> r_ptr, int remem)
+{
+ bool_ old = FALSE;
+ bool_ sin = FALSE;
+
+ int m, n, r;
+
+ cptr p, q;
+
+ int msex = 0;
+
+ bool_ breath = FALSE;
+ bool_ magic = FALSE;
+
+ u32b flags1;
+ u32b flags2;
+ u32b flags3;
+ u32b flags4;
+ u32b flags5;
+ u32b flags6;
+ u32b flags7;
+ u32b flags9;
+
+ int vn = 0;
+ byte color[64];
+ cptr vp[64];
+
+ monster_race save_mem;
+
+ /* Cheat -- Know everything */
+ if (cheat_know)
+ {
+ /* XXX XXX XXX */
+
+ /* Save the "old" memory */
+ save_mem = *r_ptr;
+
+ /* Hack -- Maximal kills */
+ r_ptr->r_tkills = MAX_SHORT;
+
+ /* Hack -- Maximal info */
+ r_ptr->r_wake = r_ptr->r_ignore = MAX_UCHAR;
+
+ /* Observe "maximal" attacks */
+ for (m = 0; m < 4; m++)
+ {
+ /* Examine "actual" blows */
+ if (r_ptr->blow[m].effect || r_ptr->blow[m].method)
+ {
+ /* Hack -- maximal observations */
+ r_ptr->r_blows[m] = MAX_UCHAR;
+ }
+ }
+
+ /* Hack -- maximal drops */
+ r_ptr->r_drop_gold = r_ptr->r_drop_item =
+ (((r_ptr->flags1 & (RF1_DROP_4D2)) ? 8 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_3D2)) ? 6 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_2D2)) ? 4 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_1D2)) ? 2 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_90)) ? 1 : 0) +
+ ((r_ptr->flags1 & (RF1_DROP_60)) ? 1 : 0));
+
+ /* Hack -- but only "valid" drops */
+ if (r_ptr->flags1 & (RF1_ONLY_GOLD)) r_ptr->r_drop_item = 0;
+ if (r_ptr->flags1 & (RF1_ONLY_ITEM)) r_ptr->r_drop_gold = 0;
+
+ /* Hack -- observe many spells */
+ r_ptr->r_cast_inate = MAX_UCHAR;
+ r_ptr->r_cast_spell = MAX_UCHAR;
+
+ /* Hack -- know all the flags */
+ r_ptr->r_flags1 = r_ptr->flags1;
+ r_ptr->r_flags2 = r_ptr->flags2;
+ r_ptr->r_flags3 = r_ptr->flags3;
+ r_ptr->r_flags4 = r_ptr->flags4;
+ r_ptr->r_flags5 = r_ptr->flags5;
+ r_ptr->r_flags6 = r_ptr->flags6;
+ r_ptr->r_flags7 = r_ptr->flags7;
+ r_ptr->r_flags8 = r_ptr->flags8;
+ r_ptr->r_flags9 = r_ptr->flags9;
+ }
+
+
+ /* Extract a gender (if applicable) */
+ if (r_ptr->flags1 & (RF1_FEMALE)) msex = 2;
+ else if (r_ptr->flags1 & (RF1_MALE)) msex = 1;
+
+
+ /* Obtain a copy of the "known" flags */
+ flags1 = (r_ptr->flags1 & r_ptr->r_flags1);
+ flags2 = (r_ptr->flags2 & r_ptr->r_flags2);
+ flags3 = (r_ptr->flags3 & r_ptr->r_flags3);
+ flags4 = (r_ptr->flags4 & r_ptr->r_flags4);
+ flags5 = (r_ptr->flags5 & r_ptr->r_flags5);
+ flags6 = (r_ptr->flags6 & r_ptr->r_flags6);
+ flags7 = (r_ptr->flags7 & r_ptr->r_flags7);
+ flags9 = (r_ptr->flags9 & r_ptr->r_flags9);
+
+
+ /* Assume some "obvious" flags */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) flags1 |= (RF1_UNIQUE);
+ if (r_ptr->flags1 & (RF1_MALE)) flags1 |= (RF1_MALE);
+ if (r_ptr->flags1 & (RF1_FEMALE)) flags1 |= (RF1_FEMALE);
+
+ /* Assume some "creation" flags */
+ if (r_ptr->flags1 & (RF1_FRIEND)) flags1 |= (RF1_FRIEND);
+ if (r_ptr->flags1 & (RF1_FRIENDS)) flags1 |= (RF1_FRIENDS);
+ if (r_ptr->flags1 & (RF1_ESCORT)) flags1 |= (RF1_ESCORT);
+ if (r_ptr->flags1 & (RF1_ESCORTS)) flags1 |= (RF1_ESCORTS);
+
+ /* Killing a monster reveals some properties */
+ if (r_ptr->r_tkills)
+ {
+ /* Know "race" flags */
+ if (r_ptr->flags3 & (RF3_ORC)) flags3 |= (RF3_ORC);
+ if (r_ptr->flags3 & (RF3_TROLL)) flags3 |= (RF3_TROLL);
+ if (r_ptr->flags3 & (RF3_GIANT)) flags3 |= (RF3_GIANT);
+ if (r_ptr->flags3 & (RF3_DRAGON)) flags3 |= (RF3_DRAGON);
+ if (r_ptr->flags3 & (RF3_DEMON)) flags3 |= (RF3_DEMON);
+ if (r_ptr->flags3 & (RF3_UNDEAD)) flags3 |= (RF3_UNDEAD);
+ if (r_ptr->flags3 & (RF3_EVIL)) flags3 |= (RF3_EVIL);
+ if (r_ptr->flags3 & (RF3_GOOD)) flags3 |= (RF3_GOOD);
+ if (r_ptr->flags3 & (RF3_ANIMAL)) flags3 |= (RF3_ANIMAL);
+ if (r_ptr->flags3 & (RF3_THUNDERLORD)) flags3 |= (RF3_THUNDERLORD);
+ if (r_ptr->flags7 & (RF7_SPIDER)) flags7 |= (RF7_SPIDER);
+
+ /* Know "forced" flags */
+ if (r_ptr->flags1 & (RF1_FORCE_DEPTH)) flags1 |= (RF1_FORCE_DEPTH);
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP)) flags1 |= (RF1_FORCE_MAXHP);
+ }
+
+
+ /* Treat uniques differently */
+ 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 */
+ {
+ char buf[2048];
+
+ /* Simple method */
+ strcpy(buf, r_ptr->text);
+
+ /* Dump it */
+ text_out(buf);
+ text_out(" ");
+ }
+
+
+ /* Nothing yet */
+ old = FALSE;
+
+ /* Describe location */
+ if (r_ptr->flags7 & RF7_PET)
+ {
+ text_out(format("%^s is ", wd_he[msex]));
+ text_out_c(TERM_L_BLUE, "friendly");
+ text_out(" to you");
+ old = TRUE;
+ }
+
+ /* Describe location */
+ if (r_ptr->level == 0)
+ {
+ if (old)
+ text_out(", ");
+ else
+ text_out(format("%^s ", wd_he[msex]));
+ text_out_c(TERM_L_GREEN, "lives in the town or the wilderness");
+ old = TRUE;
+ }
+ else if (r_ptr->r_tkills)
+ {
+ if (old)
+ text_out(", ");
+ else
+ text_out(format("%^s ", wd_he[msex]));
+
+ 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 = static_cast<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 = (((static_cast<long>(r_ptr->mexp) * r_ptr->level % p_ptr->lev) *
+ 1000L / p_ptr->lev + 5) / 10);
+
+ /* Mention the experience */
+ text_out(" is worth ");
+ text_out_c(TERM_ORANGE, format("%ld.%02ld", i, 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, 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_ptr))
+ {
+ /* 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 (((static_cast<int>(r_ptr->r_wake) * static_cast<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_ptr, 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 */
+ *r_ptr = save_mem;
+ }
+}
+
+/*
+ * Hack -- Display the "name" and "attr/chars" of a monster race
+ */
+static void roff_name(int r_idx, int ego)
+{
+ const auto r_ptr = race_info_idx(r_idx, ego);
+
+ /* Access the chars */
+ const char c1 = r_ptr->d_char;
+ const char c2 = r_ptr->x_char;
+
+ /* Access the attrs */
+ const byte a1 = r_ptr->d_attr;
+ const byte a2 = r_ptr->x_attr;
+
+ /* A title (use "The" for non-uniques) */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ Term_addstr( -1, TERM_WHITE, "The ");
+ }
+
+ /* Dump the name */
+ if (ego)
+ {
+ if (re_info[ego].before)
+ {
+ Term_addstr( -1, TERM_WHITE, format("%s %s", re_info[ego].name, r_ptr->name));
+ }
+ else
+ {
+ Term_addstr( -1, TERM_WHITE, format("%s %s", r_ptr->name, re_info[ego].name));
+ }
+ }
+ else
+ {
+ Term_addstr( -1, TERM_WHITE, r_ptr->name);
+ }
+
+ /* Append the "standard" attr/char info */
+ Term_addstr( -1, TERM_WHITE, " ('");
+ Term_addch(a1, c1);
+ Term_addstr( -1, TERM_WHITE, "')");
+
+ /* Append the "optional" attr/char info */
+ Term_addstr( -1, TERM_WHITE, "/('");
+ Term_addch(a2, c2);
+ 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)
+{
+ auto r_ptr = race_info_idx(r_idx, ego);
+
+ /* Flush messages */
+ msg_print(NULL);
+
+ /* Begin recall */
+ Term_erase(0, 1, 255);
+
+ /* Recall monster */
+ roff_aux(r_ptr, remember);
+
+ /* Describe monster */
+ roff_top(r_idx, ego);
+}
+
+/*
+ * Describe the given monster race at the current pos of the "term" window
+ */
+void monster_description_out(int r_idx, int ego)
+{
+ auto r_ptr = race_info_idx(r_idx, ego);
+ roff_name(r_idx, ego);
+ roff_aux(r_ptr, 0);
+}
+
+/*
+ * Hack -- describe the given monster race in the current "term" window
+ */
+void display_roff(int r_idx, int ego)
+{
+ int y;
+ int hgt;
+ Term_get_size(nullptr, &hgt);
+
+ /* Erase the window */
+ for (y = 0; y < hgt; y++)
+ {
+ /* Erase the line */
+ Term_erase(0, y, 255);
+ }
+
+ /* Begin recall */
+ Term_gotoxy(0, 1);
+
+ /* Recall monster */
+ auto r_ptr = race_info_idx(r_idx, ego);
+ roff_aux(r_ptr, 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;
+}
+
+
+static 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;
+}
+
+
+static 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;
+}
+
+
+static 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;
+}
+
+
+static 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;
+}
+
+
+static 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;
+}
+
+
+static 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;
+}
+
+
+static 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;
+}
+
+
+static 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;
+}
+
+
+static 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;
+}
+
+
+static 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;
+}
+
+
+static 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, std::shared_ptr<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/monster1.hpp b/src/monster1.hpp
new file mode 100644
index 00000000..1d71fef1
--- /dev/null
+++ b/src/monster1.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+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);
diff --git a/src/monster2.cc b/src/monster2.cc
new file mode 100644
index 00000000..3debb27a
--- /dev/null
+++ b/src/monster2.cc
@@ -0,0 +1,3985 @@
+/*
+ * 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 "monster2.hpp"
+
+#include "alloc_entry.hpp"
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "dungeon_info_type.hpp"
+#include "files.hpp"
+#include "hook_new_monster_in.hpp"
+#include "hook_new_monster_end_in.hpp"
+#include "hooks.hpp"
+#include "levels.hpp"
+#include "mimic.hpp"
+#include "monster3.hpp"
+#include "monster_ego.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "randart.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <algorithm>
+#include <string>
+
+#define MAX_HORROR 20
+#define MAX_FUNNY 22
+#define MAX_COMMENT 5
+
+#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)
+
+s32b monster_exp(s16b level)
+{
+ s32b capped_level = std::min(level, static_cast<s16b>(MONSTER_LEVEL_MAX));
+ return (capped_level * capped_level * capped_level * 6);
+}
+
+/* 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 >= monster_exp(m_ptr->level + 1)))
+ {
+ /* Gain a level */
+ m_ptr->level++;
+
+ if (m_ptr->ml && (!silent)) cmsg_format(TERM_L_BLUE, "%^s gains a level.", m_name);
+
+ /* Gain hp */
+ if (magik(80))
+ {
+ m_ptr->maxhp += r_ptr->hside;
+ m_ptr->hp += r_ptr->hside;
+ }
+
+ /* Gain speed */
+ if (magik(40))
+ {
+ int speed = randint(2);
+ m_ptr->speed += speed;
+ m_ptr->mspeed += speed;
+ }
+
+ /* Gain ac */
+ if (magik(50))
+ {
+ m_ptr->ac += (r_ptr->ac / 10) ? r_ptr->ac / 10 : 1;
+ }
+
+ /* Gain melee power */
+ if (magik(30))
+ {
+ int i = rand_int(3), tries = 20;
+
+ while ((tries--) && !m_ptr->blow[i].d_dice) i = rand_int(3);
+
+ m_ptr->blow[i].d_dice++;
+ }
+ }
+}
+
+/* Monster gain some xp */
+void monster_gain_exp(int m_idx, u32b exp, bool_ silent)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ m_ptr->exp += exp;
+ if (wizard)
+ {
+ char m_name[80];
+
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ if (!silent) msg_format("%^s gains %ld exp.", m_name, exp);
+ }
+
+ monster_check_experience(m_idx, silent);
+}
+
+void monster_set_level(int m_idx, int level)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (level > 150) level = 150;
+
+ if (m_ptr->level < level)
+ {
+ m_ptr->exp = monster_exp(level);
+ monster_check_experience(m_idx, TRUE);
+ }
+}
+
+/* Will add, sub, .. */
+s32b modify_aux(s32b a, s32b b, char mod)
+{
+ switch (mod)
+ {
+ case MEGO_ADD:
+ return (a + b);
+ break;
+ case MEGO_SUB:
+ return (a - b);
+ break;
+ case MEGO_FIX:
+ return (b);
+ break;
+ case MEGO_PRC:
+ return (a * b / 100);
+ break;
+ default:
+ msg_format("WARNING, unmatching MEGO(%d).", mod);
+ return (0);
+ }
+}
+
+/* Is this ego ok for this monster ? */
+bool_ mego_ok(monster_race const *r_ptr, int ego)
+{
+ monster_ego *re_ptr = &re_info[ego];
+ 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 */
+static int pick_ego_monster(monster_race const *r_ptr)
+{
+ /* 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_ptr->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_ptr, ego)) continue;
+
+ /* Not too much OoD */
+ lvl = r_ptr->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_ptr, 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
+ */
+std::shared_ptr<monster_race> race_info_idx(int r_idx, int ego)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* We don't need to allocate anything if it's an ordinary monster. */
+ if (!ego) {
+ return std::shared_ptr<monster_race>(r_ptr, [](monster_race *) {
+ // No need to delete -- will be freed when the r_info array is freed.
+ });
+ }
+
+ /* We allocate a copy of the "base" monster race to refer to. */
+ auto nr_ptr = std::make_shared<monster_race>();
+ *nr_ptr = *r_ptr;
+
+ /* Get a reference to the ego monster modifiers */
+ monster_ego *re_ptr = &re_info[ego];
+
+ /* Adjust the values */
+ for (int i = 0; i < 4; i++)
+ {
+ s32b 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;
+
+ s32b 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!"
+};
+
+
+/*
+ * Delete a monster by index.
+ *
+ * When a monster is deleted, all of its objects are deleted.
+ */
+void delete_monster_idx(int i)
+{
+ /* Get location */
+ monster_type *m_ptr = &m_list[i];
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+
+ /* Hack -- Reduce the racial counter */
+ auto const r_ptr = m_ptr->race();
+ 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 */
+ bool_ had_lite = FALSE;
+ 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 (int 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;
+
+ /* Copy list of objects; need a copy since we're
+ * manipulating the list itself below. */
+ auto const object_idxs(m_ptr->hold_o_idxs);
+
+ /* Delete objects */
+ for (auto const this_o_idx: object_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_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);
+ }
+
+ /* Wipe the Monster */
+ m_ptr->wipe();
+
+ /* 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)
+{
+ /* Do nothing */
+ if (i1 == i2) return;
+
+ /* Old monster */
+ monster_type *m_ptr = &m_list[i1];
+
+ /* Location */
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+
+ /* Cave grid */
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Update the cave */
+ c_ptr->m_idx = i2;
+
+ /* Repair objects being carried by monster */
+ for (auto const this_o_idx: m_ptr->hold_o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_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 (int 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 */
+ m_list[i2] = m_list[i1];
+
+ /* Wipe the hole */
+ m_list[i1].wipe();
+}
+
+
+/*
+ * 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];
+ auto const r_ptr = m_ptr->race();
+
+ /* 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];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Mega-Hack -- preserve Unique's XXX XXX XXX */
+
+ /* Hack -- Reduce the racial counter */
+ auto r_ptr = m_ptr->race();
+ r_ptr->cur_num--;
+
+ /* Monster is gone */
+ cave[m_ptr->fy][m_ptr->fx].m_idx = 0;
+
+ /* Wipe the Monster */
+ m_ptr->wipe();
+ }
+
+ /* 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)
+{
+ auto r_ptr = m_ptr->race();
+ 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_info[m_ptr->ego].name, r_ptr->name);
+ }
+ else
+ {
+ sprintf(name, "%s %s", r_ptr->name, re_info[m_ptr->ego].name);
+ }
+ }
+ else
+ {
+ sprintf(name, "%s", r_ptr->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, 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 */
+ cptr 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];
+ char name[80];
+
+ if (ego)
+ {
+ if (re_info[ego].before)
+ {
+ sprintf(name, "%s %s", re_info[ego].name, r_ptr->name);
+ }
+ else
+ {
+ sprintf(name, "%s %s", r_ptr->name, re_info[ego].name);
+ }
+ }
+ else
+ {
+ sprintf(name, "%s", r_ptr->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);
+ }
+}
+
+
+
+static void sanity_blast(monster_type * m_ptr, bool_ necro)
+{
+ bool_ happened = FALSE;
+ int power = 100;
+
+ if (!necro)
+ {
+ if (m_ptr == nullptr) {
+ return;
+ }
+
+ auto const r_ptr = m_ptr->race();
+
+ power = (m_ptr->level) + 10;
+
+ if (m_ptr != NULL)
+ {
+ char m_name[80];
+ 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 ((race_flags1_p(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(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];
+
+ /* The current monster location */
+ const int fy = m_ptr->fy;
+ const int fx = m_ptr->fx;
+
+ const 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;
+
+ auto const r_ptr = m_ptr->race();
+
+ /* Calculate distance */
+ if (full)
+ {
+ /* Distance components */
+ const int dy = (p_ptr->py > fy) ? (p_ptr->py - fy) : (fy - p_ptr->py);
+ const int dx = (p_ptr->px > fx) ? (p_ptr->px - fx) : (fx - p_ptr->px);
+
+ /* Approximate distance */
+ const int d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
+
+ /* Save the distance (in a byte) */
+ m_ptr->cdis = (d < 255) ? d : 255;
+ }
+
+
+ /* Process "distant" monsters */
+ if (m_ptr->cdis > MAX_SIGHT)
+ {
+ /* Ignore unseen monsters */
+ if (!m_ptr->ml) return;
+
+ /* Detected */
+ if (m_ptr->mflag & (MFLAG_MARK)) flag = TRUE;
+ }
+
+ /* Process "nearby" monsters on the current "panel" */
+ else if (panel_contains(fy, fx))
+ {
+ /* Normal line of sight, and player is not blind */
+ if (player_has_los_bold(fy, fx) && !p_ptr->blind)
+ {
+ /* Use "infravision" */
+ if (m_ptr->cdis <= (byte)(p_ptr->see_infra))
+ {
+ /* Infravision only works on "warm" creatures */
+ /* Below, we will need to know that infravision failed */
+ if (r_ptr->flags2 & (RF2_COLD_BLOOD)) do_cold_blood = TRUE;
+
+ /* Infravision works */
+ if (!do_cold_blood) easy = flag = TRUE;
+ }
+
+ /* Use "illumination" */
+ if (player_can_see_bold(fy, fx))
+ {
+ /* Take note of invisibility */
+ if (r_ptr->flags2 & (RF2_INVISIBLE)) do_invisible = TRUE;
+
+ /* Visible, or detectable, monsters get seen */
+ if (!do_invisible || p_ptr->see_inv) easy = flag = TRUE;
+ }
+ }
+
+ /* Telepathy can see all "nearby" monsters with "minds" */
+ if (p_ptr->telepathy)
+ {
+ /* Assume we cant see */
+ bool_ can_esp = FALSE;
+
+ /* Different ESP */
+ if ((p_ptr->telepathy & ESP_ORC) && (r_ptr->flags3 & RF3_ORC)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_SPIDER) && (r_ptr->flags7 & RF7_SPIDER)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_TROLL) && (r_ptr->flags3 & RF3_TROLL)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_DRAGON) && (r_ptr->flags3 & RF3_DRAGON)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_GIANT) && (r_ptr->flags3 & RF3_GIANT)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_DEMON) && (r_ptr->flags3 & RF3_DEMON)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_UNDEAD) && (r_ptr->flags3 & RF3_UNDEAD)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_EVIL) && (r_ptr->flags3 & RF3_EVIL)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_ANIMAL) && (r_ptr->flags3 & RF3_ANIMAL)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_THUNDERLORD) && (r_ptr->flags3 & RF3_THUNDERLORD)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_GOOD) && (r_ptr->flags3 & RF3_GOOD)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_NONLIVING) && (r_ptr->flags3 & RF3_NONLIVING)) can_esp = TRUE;
+ if ((p_ptr->telepathy & ESP_UNIQUE) && ((r_ptr->flags1 & RF1_UNIQUE) || (r_ptr->flags3 & RF3_UNIQUE_4))) can_esp = TRUE;
+ if (p_ptr->telepathy & ESP_ALL) can_esp = TRUE;
+
+ /* Only do this when we can really detect monster */
+ if (can_esp)
+ {
+ /* Empty mind, no telepathy */
+ if (r_ptr->flags2 & (RF2_EMPTY_MIND))
+ {
+ do_empty_mind = TRUE;
+ }
+
+ /* Weird mind, occasional telepathy */
+ else if (r_ptr->flags2 & (RF2_WEIRD_MIND))
+ {
+ do_weird_mind = TRUE;
+ if (rand_int(100) < 10)
+ {
+ hard = TRUE;
+ flag = TRUE;
+ }
+ }
+
+ /* Normal mind, allow telepathy */
+ else
+ {
+ hard = TRUE;
+ flag = TRUE;
+ }
+ }
+ }
+
+ /* Apply "detection" spells */
+ if (m_ptr->mflag & (MFLAG_MARK)) flag = TRUE;
+
+ /* Hack -- Wizards have "perfect telepathy" */
+ if (wizard) flag = TRUE;
+ }
+
+
+ /* The monster is now visible */
+ if (flag)
+ {
+ /* It was previously unseen */
+ if (!m_ptr->ml)
+ {
+ /* Mark as visible */
+ m_ptr->ml = TRUE;
+
+ /* Draw the monster */
+ lite_spot(fy, fx);
+
+ /* Update health bar as needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_FRAME);
+
+ /* 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);
+ }
+ }
+
+ /* 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_FRAME);
+
+ /* 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);
+ }
+ }
+ }
+
+
+ /* 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);
+ }
+
+ }
+ }
+
+ /* 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);
+ }
+ }
+ }
+}
+
+
+
+
+/*
+ * 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->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_ptr->hold_o_idxs.push_back(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;
+static int place_monster_result = 0;
+bool_ place_monster_one_no_drop = FALSE;
+static s16b hack_m_idx_ii = 0;
+s16b place_monster_one(int y, int x, int r_idx, int ego, bool_ slp, int status)
+{
+ int i;
+ char dummy[5];
+ bool_ add_level = FALSE;
+ int min_level = 0, max_level = 0;
+
+ /* DO NOT PLACE A MONSTER IN THE SMALL SCALE WILDERNESS !!! */
+ if (p_ptr->wild_mode)
+ {
+ return 0;
+ }
+
+ /* Verify location */
+ if (!in_bounds(y, x))
+ {
+ 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);
+ 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);
+ return 0;
+ }
+
+ /* Hack -- no creation on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH)
+ {
+ return 0;
+ }
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH)
+ {
+ return 0;
+ }
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN)
+ {
+ return 0;
+ }
+
+ /* Nor on the altars */
+ if ((cave[y][x].feat >= FEAT_ALTAR_HEAD)
+ && (cave[y][x].feat <= FEAT_ALTAR_TAIL))
+ {
+ return 0;
+ }
+
+ /* Nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START)
+ && (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ {
+ return 0;
+ }
+
+ /* Paranoia */
+ if (!r_idx)
+ {
+ return 0;
+ }
+
+ /* Check for original monster race flags */
+ {
+ monster_race *r_ptr = &r_info[r_idx];
+
+ /* Paranoia */
+ if (!r_ptr->name)
+ {
+ return 0;
+ }
+
+ /* Are we allowed to continue ? */
+ {
+ struct hook_new_monster_in in = { r_idx };
+ if (process_hooks_new(HOOK_NEW_MONSTER, &in, NULL))
+ {
+ return 0;
+ }
+ }
+
+ /* Ego Uniques are NOT to be created */
+ if ((r_ptr->flags1 & RF1_UNIQUE) && ego)
+ {
+ return 0;
+ }
+ }
+
+ /* Now could we generate an Ego Monster */
+ /* Grab the special race if needed */
+ auto 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");
+ 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);
+ 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);
+ return 0;
+ }
+
+ /* Fully forbid it */
+ if (r_ptr->flags9 & RF9_NEVER_GENE)
+ {
+ if (wizard) cmsg_print(TERM_L_RED, "WARNING: Refused monster: NEVER_GENE");
+ 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);
+ 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");
+ return 0;
+ }
+
+ /* Hack -- "unique" monsters must be "unique" */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) && (r_ptr->cur_num >= r_ptr->max_num) && (r_ptr->max_num != -1) && (!bypass_r_ptr_max_num))
+ {
+ /* Cannot create */
+ if (wizard) cmsg_format(TERM_L_RED, "WARNING: Refused monster %d: cur_num >= max_num", r_idx);
+ 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");
+ 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).", r_ptr->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).", r_ptr->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).", r_ptr->name);
+ }
+
+
+ /* Access the location */
+ cave_type *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)
+ {
+ return 0;
+ }
+
+
+ /* Get a new monster record */
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Save the race */
+ m_ptr->r_idx = r_idx;
+ m_ptr->ego = ego;
+
+ /* 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_idxs.clear();
+
+ 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))
+ {
+ const bool_ good = (r_ptr->flags1 & (RF1_DROP_GOOD)) ? TRUE : FALSE;
+ const bool_ great = (r_ptr->flags1 & (RF1_DROP_GREAT)) ? TRUE : FALSE;
+
+ const bool_ do_gold = (!(r_ptr->flags1 & (RF1_ONLY_ITEM)));
+ const bool_ do_item = (!(r_ptr->flags1 & (RF1_ONLY_GOLD)));
+ const bool_ do_mimic = (r_ptr->flags9 & (RF9_MIMIC));
+
+ const 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;
+
+ /* 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();
+
+ int 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 (int j = 0; j < number; j++)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Make Gold */
+ if ((!do_mimic) && do_gold && (!do_item || (rand_int(100) < 50)))
+ {
+ /* Make some gold */
+ if (!make_gold(q_ptr)) continue;
+
+ /* XXX XXX XXX */
+ dump_gold++;
+ }
+
+ /* Make Object */
+ else
+ {
+ /* Make an object */
+ if (!do_mimic)
+ {
+ if (!make_object(q_ptr, good, great, r_ptr->drops)) continue;
+ }
+ else
+ {
+ /* Try hard for mimics */
+ int tries = 1000;
+
+ while (tries--)
+ {
+ if (make_object(q_ptr, good, great, r_ptr->drops)) break;
+ }
+ /* BAD */
+ if (!tries) continue;
+ }
+
+ /* XXX XXX XXX */
+ dump_item++;
+ }
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+ monster_carry(m_ptr, c_ptr->m_idx, q_ptr);
+ }
+
+ /* Reset the object level */
+ object_level = dun_level;
+
+ /* Reset "coin" type */
+ coin_type = 0;
+ }
+ place_monster_one_no_drop = FALSE;
+
+
+ /* Assign maximal hitpoints */
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP))
+ {
+ m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
+ }
+ else
+ {
+ m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside);
+ }
+
+ /* And start out fully healthy */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Some basic info */
+ for (i = 0; i < 4; i++)
+ {
+ m_ptr->blow[i].method = r_ptr->blow[i].method;
+ m_ptr->blow[i].effect = r_ptr->blow[i].effect;
+ m_ptr->blow[i].d_dice = r_ptr->blow[i].d_dice;
+ m_ptr->blow[i].d_side = r_ptr->blow[i].d_side;
+ }
+ m_ptr->ac = r_ptr->ac;
+ m_ptr->level = r_ptr->level;
+ m_ptr->speed = r_ptr->speed;
+ m_ptr->exp = monster_exp(m_ptr->level);
+
+ /* Extract the monster base speed */
+ m_ptr->mspeed = m_ptr->speed;
+
+ /* Hack -- small racial variety */
+ if (!(r_ptr->flags1 & (RF1_UNIQUE)))
+ {
+ /* Allow some small variation per monster */
+ i = extract_energy[m_ptr->speed] / 10;
+ if (i) m_ptr->mspeed += rand_spread(0, i);
+ }
+
+
+ if (dungeon_flags2 & DF2_ADJUST_LEVEL_1_2)
+ {
+ min_level = max_level = dun_level / 2;
+ add_level = TRUE;
+ }
+ if (dungeon_flags1 & DF1_ADJUST_LEVEL_1)
+ {
+ if (!min_level) min_level = dun_level;
+ max_level = dun_level;
+ add_level = TRUE;
+ }
+ if (dungeon_flags1 & DF1_ADJUST_LEVEL_2)
+ {
+ if (!min_level) min_level = dun_level * 2;
+ max_level = dun_level * 2;
+ add_level = TRUE;
+ }
+ if (add_level) monster_set_level(c_ptr->m_idx, rand_range(min_level, max_level));
+
+ /* Give a random starting energy */
+ m_ptr->energy = (byte)rand_int(100);
+
+ /* Force monster to wait for player */
+ if (r_ptr->flags1 & (RF1_FORCE_SLEEP))
+ {
+ /* Monster is still being nice */
+ m_ptr->mflag |= (MFLAG_NICE);
+
+ /* Must repair monsters */
+ repair_monsters = TRUE;
+ }
+
+ /* Hack -- see "process_monsters()" */
+ if (c_ptr->m_idx < hack_m_idx)
+ {
+ /* Monster is still being born */
+ m_ptr->mflag |= (MFLAG_BORN);
+ }
+
+
+ /* Update the monster */
+ update_mon(c_ptr->m_idx, TRUE);
+
+
+ /* Hack -- Count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro++;
+
+
+ /* Hack -- Notice new multi-hued monsters */
+ if (r_ptr->flags1 & (RF1_ATTR_MULTI)) shimmer_monsters = TRUE;
+
+ /* Count monsters on the level */
+ {
+ /* Hack -- we need to modify the REAL r_info, not the fake one */
+ monster_race *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;
+ }
+
+ /* Processs hooks */
+ {
+ hook_new_monster_end_in in = { m_ptr };
+ process_hooks_new(HOOK_NEW_MONSTER_END, &in, 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_ptr), 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_ptr), slp, status)) return (FALSE);
+
+
+ /* Require the "group" flag */
+ if (!grp) return (TRUE);
+
+
+ /* Friends for certain monsters */
+ if (r_ptr->flags1 & (RF1_FRIENDS))
+ {
+ /* Attempt to place a group */
+ (void)place_monster_group(y, x, r_idx, slp, status);
+ }
+
+
+ /* Escorts for certain monsters */
+ if (r_ptr->flags1 & (RF1_ESCORT))
+ {
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Set the escort index */
+ place_monster_idx = r_idx;
+
+ /* Set the escort hook */
+ get_mon_num_hook = place_monster_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Try to place several "escorts" */
+ for (i = 0; i < 50; i++)
+ {
+ int nx, ny, z, d = 3;
+
+ /* Pick a location */
+ scatter(&ny, &nx, y, x, d);
+
+ /* Require empty grids */
+ if (!cave_empty_bold(ny, nx)) continue;
+
+ set_mon_num2_hook(ny, nx);
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a random race */
+ z = get_mon_num(r_ptr->level);
+
+ /* Reset restriction */
+ get_mon_num2_hook = NULL;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Handle failure */
+ if (!z) break;
+
+ /* Place a single escort */
+ place_monster_one(ny, nx, z, pick_ego_monster(&r_info[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);
+}
+
+
+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;
+}
+
+/*
+ * 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);
+ }
+
+
+ if (randint(5000) <= dun_level)
+ {
+ if (alloc_horde(y, x))
+ {
+ if ((cheat_hear) || (p_ptr->precognition)) msg_print("Monster horde.");
+ return (TRUE);
+ }
+ }
+ else
+ {
+
+ /* Attempt to place the monster, allow groups */
+ if (place_monster(y, x, slp, TRUE)) return (TRUE);
+
+ }
+
+ /* 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
+ */
+static 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_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_ptr->name, "Phantom")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_ELEMENTAL:
+ {
+ okay = ((strstr(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_ptr->name, "lue horror")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_BUG:
+ {
+ okay = ((strstr(r_ptr->name, "Software bug")) &&
+ !(r_ptr->flags1 & (RF1_UNIQUE)));
+ break;
+ }
+
+ case SUMMON_RNG:
+ {
+ okay = ((strstr(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;
+ }
+
+ }
+
+ /* Result */
+ return (okay);
+}
+
+
+/*
+ * Place a monster (of the specified "type") near the given
+ * location. Return TRUE if a monster was actually summoned.
+ *
+ * We will attempt to place the monster up to 10 times before giving up.
+ *
+ * Note: SUMMON_UNIQUE and SUMMON_WRAITH (XXX) will summon Unique's
+ * Note: SUMMON_HI_UNDEAD and SUMMON_HI_DRAGON may summon Unique's
+ * Note: None of the other summon codes will ever summon Unique's.
+ *
+ * This function has been changed. We now take the "monster level"
+ * of the summoning monster as a parameter, and use that, along with
+ * the current dungeon level, to help determine the level of the
+ * desired monster. Note that this is an upper bound, and also
+ * tends to "prefer" monsters of that level. Currently, we use
+ * the average of the dungeon and monster levels, and then add
+ * five to allow slight increases in monster power.
+ *
+ * Note that we use the new "monster allocation table" creation code
+ * to restrict the "get_mon_num()" function to the set of "legal"
+ * monsters, making this function much faster and more reliable.
+ *
+ * Note that this function may not succeed, though this is very rare.
+ */
+int summon_specific_level = 0;
+bool_ summon_specific(int y1, int x1, int lev, int type)
+{
+ int i, x, y, r_idx;
+ bool_ Group_ok = TRUE;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ /* Look for a location */
+ for (i = 0; i < 20; ++i)
+ {
+ /* Pick a distance */
+ int d = (i / 15) + 1;
+
+ /* Pick a location */
+ scatter(&y, &x, y1, x1, d);
+
+ /* Require "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) return (FALSE);
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START) &&
+ (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ continue;
+
+ /* Okay */
+ break;
+ }
+
+ /* Failure */
+ if (i == 20) return (FALSE);
+
+ /* Save the "summon" type */
+ summon_specific_type = type;
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = summon_specific_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+
+ /* Pick a monster, using the level calculation */
+ summon_hack = TRUE;
+ r_idx = get_mon_num((dun_level + lev) / 2 + 5);
+ summon_hack = FALSE;
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+
+ /* Handle failure */
+ if (!r_idx) return (FALSE);
+
+
+ if ((type == SUMMON_DAWN) || (type == SUMMON_BLUE_HORROR))
+ {
+ Group_ok = FALSE;
+ }
+
+ /* Attempt to place the monster (awake, allow groups) */
+ if (!place_monster_aux(y, x, r_idx, FALSE, Group_ok, MSTATUS_ENEMY)) return (FALSE);
+ if (summon_specific_level)
+ {
+ monster_set_level(place_monster_result, summon_specific_level);
+ summon_specific_level = 0;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+bool_ summon_specific_friendly(int y1, int x1, int lev, int type, bool_ Group_ok)
+{
+ int i, x, y, r_idx;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ /* Look for a location */
+ for (i = 0; i < 20; ++i)
+ {
+ /* Pick a distance */
+ int d = (i / 15) + 1;
+
+ /* Pick a location */
+ scatter(&y, &x, y1, x1, d);
+
+ /* Require "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) return (FALSE);
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START) &&
+ (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ continue;
+
+ /* Okay */
+ break;
+ }
+
+ /* Failure */
+ if (i == 20) return (FALSE);
+
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Save the "summon" type */
+ summon_specific_type = type;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = summon_specific_okay;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a monster, using the level calculation */
+ r_idx = get_mon_num((dun_level + lev) / 2 + 5);
+
+ /* 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];
+ auto const r_ptr = m_ptr->race();
+
+ bool_ result = FALSE;
+
+ if (no_breeds)
+ {
+ msg_print("It tries to breed but it fails!");
+ return FALSE;
+ }
+
+ /* Try up to 18 times */
+ for (int i = 0; i < 18; i++)
+ {
+ /* Pick a location */
+ int x;
+ int y;
+ scatter(&y, &x, m_ptr->fy, m_ptr->fx, 1);
+
+ /* Require an "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ int 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 message, cptr name)
+{
+ std::string buf;
+ buf += name;
+ buf += " ";
+ buf += message;
+
+ if (hack_message_pain_may_silent)
+ {
+ monster_msg_simple(buf.c_str());
+ }
+ else
+ {
+ msg_print(buf.c_str());
+ }
+}
+
+void message_pain(int m_idx, int dam)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+
+ /* Get the monster name */
+ char m_name[80];
+ monster_desc(m_name, m_ptr, 0);
+ capitalize(m_name);
+
+ /* Notice non-damage */
+ if (dam == 0)
+ {
+ message_pain_hook("is unharmed.", m_name);
+ return;
+ }
+
+ /* Note -- subtle fix -CFT */
+ long newhp = (long)(m_ptr->hp);
+ long oldhp = newhp + (long)(dam);
+ long tmp = (newhp * 100L) / oldhp;
+ int percentage = (int)(tmp);
+
+ /* Get racial information */
+ auto const r_ptr = m_ptr->race();
+
+ /* Jelly's, Mold's, Vortex's, Quthl's */
+ if (strchr("jmvQ", r_ptr->d_char))
+ {
+ if (percentage > 95)
+ message_pain_hook("barely notices.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("flinches.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("squelches.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("quivers in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("writhes about.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("writhes in agony.", m_name);
+ else
+ message_pain_hook("jerks limply.", m_name);
+ }
+
+ /* Dogs and Hounds */
+ else if (strchr("CZ", r_ptr->d_char))
+ {
+ if (percentage > 95)
+ message_pain_hook("shrugs off the attack.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("snarls with pain.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("yelps in pain.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("howls in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("howls in agony.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("writhes in agony.", m_name);
+ else
+ message_pain_hook("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("ignores the attack.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("grunts with pain.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("squeals in pain.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("shrieks in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("shrieks in agony.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("writhes in agony.", m_name);
+ else
+ message_pain_hook("cries out feebly.", m_name);
+ }
+
+ /* Another type of monsters (shrug,cry,scream) */
+ else
+ {
+ if (percentage > 95)
+ message_pain_hook("shrugs off the attack.", m_name);
+ else if (percentage > 75)
+ message_pain_hook("grunts with pain.", m_name);
+ else if (percentage > 50)
+ message_pain_hook("cries out in pain.", m_name);
+ else if (percentage > 35)
+ message_pain_hook("screams in pain.", m_name);
+ else if (percentage > 20)
+ message_pain_hook("screams in agony.", m_name);
+ else if (percentage > 10)
+ message_pain_hook("writhes in agony.", m_name);
+ else
+ message_pain_hook("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];
+
+ /* Not allowed to learn */
+ if (!smart_learn) return;
+
+ /* Get racial flags */
+ auto const r_ptr = m_ptr->race();
+
+ /* Too stupid to learn anything */
+ if (r_ptr->flags2 & (RF2_STUPID)) return;
+
+ /* Not intelligent, only learn sometimes */
+ if (!(r_ptr->flags2 & (RF2_SMART)) && magik(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)
+{
+ /* Copy list of objects; we need a copy since
+ we're manipulating the list itself below. */
+ auto const object_idxs(m_ptr->hold_o_idxs);
+
+ /* Drop objects being carried */
+ for (auto const this_o_idx: object_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_o_idx];
+
+ /* Paranoia */
+ o_ptr->held_m_idx = 0;
+
+ /* Get local object */
+ object_type forge;
+ object_type *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_idxs.clear();
+}
diff --git a/src/monster2.hpp b/src/monster2.hpp
new file mode 100644
index 00000000..84f79e36
--- /dev/null
+++ b/src/monster2.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include "h-basic.h"
+#include "monster_race_fwd.hpp"
+#include "monster_type_fwd.hpp"
+#include "object_type_fwd.hpp"
+#include <memory>
+
+extern s32b monster_exp(s16b level);
+extern void monster_set_level(int m_idx, int level);
+extern s32b modify_aux(s32b a, s32b b, char mod);
+extern void monster_msg_simple(cptr s);
+extern bool_ mego_ok(monster_race const *r_ptr, 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 std::shared_ptr<monster_race> race_info_idx(int r_idx, int ego);
+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 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 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 void set_mon_num_hook(void);
+extern void set_mon_num2_hook(int y, int x);
+extern bool_ monster_can_cross_terrain(byte feat, std::shared_ptr<monster_race> r_ptr);
diff --git a/src/monster3.cc b/src/monster3.cc
new file mode 100644
index 00000000..0d26538c
--- /dev/null
+++ b/src/monster3.cc
@@ -0,0 +1,722 @@
+/*
+ * 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 "monster3.hpp"
+
+#include "cave_type.hpp"
+#include "cmd2.hpp"
+#include "gods.hpp"
+#include "melee2.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+/*
+ * Is the mon,ster in friendly state(pet, friend, ..)
+ * -1 = enemy, 0 = neutral, 1 = friend
+ */
+int is_friend(monster_type *m_ptr)
+{
+ switch (m_ptr->status)
+ {
+ /* pets/friends/companion attacks monsters */
+ case MSTATUS_NEUTRAL_P:
+ case MSTATUS_FRIEND:
+ case MSTATUS_PET:
+ case MSTATUS_COMPANION:
+ return 1;
+ break;
+ case MSTATUS_NEUTRAL_M:
+ case MSTATUS_ENEMY:
+ return -1;
+ break;
+ case MSTATUS_NEUTRAL:
+ return 0;
+ break;
+ }
+
+ /* OUPS */
+ return (0);
+}
+
+/* Should they attack each others */
+bool_ is_enemy(monster_type *m_ptr, monster_type *t_ptr)
+{
+ monster_race *r_ptr = &r_info[m_ptr->r_idx], *rt_ptr = &r_info[t_ptr->r_idx];
+ int s1 = is_friend(m_ptr), s2 = is_friend(t_ptr);
+
+ /* Monsters hates breeders */
+ if ((m_ptr->status != MSTATUS_NEUTRAL) && (rt_ptr->flags4 & RF4_MULTIPLY) && (num_repro > MAX_REPRO * 2 / 3) && (r_ptr->d_char != rt_ptr->d_char)) return TRUE;
+ if ((t_ptr->status != MSTATUS_NEUTRAL) && (r_ptr->flags4 & RF4_MULTIPLY) && (num_repro > MAX_REPRO * 2 / 3) && (r_ptr->d_char != rt_ptr->d_char)) return TRUE;
+
+ /* No special conditions, lets test normal flags */
+ if (s1 && s2 && (s1 == -s2)) return TRUE;
+
+ /* Not ennemy */
+ return (FALSE);
+}
+
+bool_ change_side(monster_type *m_ptr)
+{
+ auto const r_ptr = m_ptr->race();
+
+ /* neutrals and companions */
+ switch (m_ptr->status)
+ {
+ case MSTATUS_FRIEND:
+ m_ptr->status = MSTATUS_ENEMY;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, -m_ptr->level * 4);
+ break;
+ case MSTATUS_NEUTRAL_P:
+ m_ptr->status = MSTATUS_NEUTRAL_M;
+ break;
+ case MSTATUS_NEUTRAL_M:
+ m_ptr->status = MSTATUS_NEUTRAL_P;
+ break;
+ case MSTATUS_PET:
+ m_ptr->status = MSTATUS_ENEMY;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, -m_ptr->level * 4);
+ break;
+ case MSTATUS_COMPANION:
+ return FALSE;
+ break;
+ }
+ /* changed */
+ return TRUE;
+}
+
+/* Multiply !! */
+bool_ ai_multiply(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ int k, y, x, oy = m_ptr->fy, ox = m_ptr->fx;
+ bool_ is_frien = (is_friend(m_ptr) > 0);
+
+ /* Count the adjacent monsters */
+ for (k = 0, y = oy - 1; y <= oy + 1; y++)
+ {
+ for (x = ox - 1; x <= ox + 1; x++)
+ {
+ if (cave[y][x].m_idx) k++;
+ }
+ }
+
+ if (is_friend(m_ptr) > 0)
+ {
+ is_frien = TRUE;
+ }
+ else
+ {
+ is_frien = FALSE;
+ }
+
+ /* Hack -- multiply slower in crowded areas */
+ if ((k < 4) && (!k || !rand_int(k * MON_MULT_ADJ)))
+ {
+ /* Try to multiply */
+ if (multiply_monster(m_idx, (is_frien), FALSE))
+ {
+ /* Take note if visible */
+ if (m_ptr->ml)
+ {
+ r_ptr->r_flags4 |= (RF4_MULTIPLY);
+ }
+
+ /* Multiplying takes energy */
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/* Possessor incarnates */
+bool_ ai_possessor(int m_idx, int o_idx)
+{
+ object_type *o_ptr = &o_list[o_idx];
+ monster_type *m_ptr = &m_list[m_idx];
+ int r_idx = m_ptr->r_idx, r2_idx = o_ptr->pval2;
+ int i;
+ monster_race *r_ptr = &r_info[r2_idx];
+ char m_name[80], m_name2[80];
+
+ monster_desc(m_name, m_ptr, 0x00);
+ monster_race_desc(m_name2, r2_idx, 0);
+
+ if (m_ptr->ml) msg_format("%^s incarnates into a %s!", m_name, m_name2);
+
+ /* Remove the old one */
+ delete_object_idx(o_idx);
+
+ m_ptr->r_idx = r2_idx;
+ m_ptr->ego = 0;
+
+ /* No "damage" yet */
+ m_ptr->stunned = 0;
+ m_ptr->confused = 0;
+ m_ptr->monfear = 0;
+
+ /* No target yet */
+ m_ptr->target = -1;
+
+ /* Assume no sleeping */
+ m_ptr->csleep = 0;
+
+ /* Assign maximal hitpoints */
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP))
+ {
+ m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
+ }
+ else
+ {
+ m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside);
+ }
+
+ /* And start out fully healthy */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Some basic info */
+ for (i = 0; i < 4; i++)
+ {
+ m_ptr->blow[i].method = r_ptr->blow[i].method;
+ m_ptr->blow[i].effect = r_ptr->blow[i].effect;
+ m_ptr->blow[i].d_dice = r_ptr->blow[i].d_dice;
+ m_ptr->blow[i].d_side = r_ptr->blow[i].d_side;
+ }
+ m_ptr->ac = r_ptr->ac;
+ m_ptr->level = r_ptr->level;
+ m_ptr->speed = r_ptr->speed;
+ m_ptr->exp = monster_exp(m_ptr->level);
+
+ /* Extract the monster base speed */
+ m_ptr->mspeed = m_ptr->speed;
+
+ m_ptr->energy = 0;
+
+ /* Hack -- Count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro++;
+
+ /* Hack -- Notice new multi-hued monsters */
+ if (r_ptr->flags1 & (RF1_ATTR_MULTI)) shimmer_monsters = TRUE;
+
+ /* Hack -- Count the monsters on the level */
+ r_ptr->cur_num++;
+ r_info[r_idx].cur_num--;
+
+ m_ptr->possessor = r_idx;
+
+ /* Update the monster */
+ update_mon(m_idx, TRUE);
+
+ return TRUE;
+}
+
+void ai_deincarnate(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int r2_idx = m_ptr->possessor, r_idx = m_ptr->r_idx;
+ monster_race *r_ptr = &r_info[r2_idx];
+ int i;
+ char m_name[80];
+
+ monster_desc(m_name, m_ptr, 0x04);
+
+ if (m_ptr->ml) msg_format("The soul of %s deincarnates!", m_name);
+
+ m_ptr->r_idx = r2_idx;
+ m_ptr->ego = 0;
+
+ /* No "damage" yet */
+ m_ptr->stunned = 0;
+ m_ptr->confused = 0;
+ m_ptr->monfear = 0;
+
+ /* No target yet */
+ m_ptr->target = -1;
+
+ /* Assume no sleeping */
+ m_ptr->csleep = 0;
+
+ /* Assign maximal hitpoints */
+ if (r_ptr->flags1 & (RF1_FORCE_MAXHP))
+ {
+ m_ptr->maxhp = maxroll(r_ptr->hdice, r_ptr->hside);
+ }
+ else
+ {
+ m_ptr->maxhp = damroll(r_ptr->hdice, r_ptr->hside);
+ }
+
+ /* And start out fully healthy */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Some basic info */
+ for (i = 0; i < 4; i++)
+ {
+ m_ptr->blow[i].method = r_ptr->blow[i].method;
+ m_ptr->blow[i].effect = r_ptr->blow[i].effect;
+ m_ptr->blow[i].d_dice = r_ptr->blow[i].d_dice;
+ m_ptr->blow[i].d_side = r_ptr->blow[i].d_side;
+ }
+ m_ptr->ac = r_ptr->ac;
+ m_ptr->level = r_ptr->level;
+ m_ptr->speed = r_ptr->speed;
+ m_ptr->exp = monster_exp(m_ptr->level);
+
+ /* Extract the monster base speed */
+ m_ptr->mspeed = m_ptr->speed;
+
+ m_ptr->energy = 0;
+
+ /* Hack -- Count the number of "reproducers" */
+ if (r_ptr->flags4 & (RF4_MULTIPLY)) num_repro++;
+
+ /* Hack -- Notice new multi-hued monsters */
+ if (r_ptr->flags1 & (RF1_ATTR_MULTI)) shimmer_monsters = TRUE;
+
+ /* Hack -- Count the monsters on the level */
+ r_ptr->cur_num++;
+ r_info[r_idx].cur_num--;
+
+ m_ptr->possessor = 0;
+
+ /* Update the monster */
+ update_mon(m_idx, TRUE);
+}
+
+/* Returns if a new companion is allowed */
+bool_ can_create_companion(void)
+{
+ int i, mcnt = 0;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status == MSTATUS_COMPANION) mcnt++;
+ }
+
+ if (mcnt < (1 + get_skill_scale(SKILL_LORE, 6))) return (TRUE);
+ else return (FALSE);
+}
+
+
+/* Player controlled monsters */
+bool_ do_control_walk(void)
+{
+ /* Get a "repeated" direction */
+ if (p_ptr->control)
+ {
+ int dir;
+
+ if (get_rep_dir(&dir))
+ {
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Actually move the monster */
+ p_ptr->control_dir = dir;
+ }
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+bool_ do_control_inven(void)
+{
+ if (!p_ptr->control) return FALSE;
+ screen_save();
+ prt("Carried items", 0, 0);
+ (void) show_monster_inven(p_ptr->control);
+ inkey();
+ screen_load();
+ return TRUE;
+}
+
+bool_ do_control_pickup(void)
+{
+ if (!p_ptr->control) return FALSE;
+
+ monster_type *m_ptr = &m_list[p_ptr->control];
+
+ cave_type *c_ptr = &cave[m_ptr->fy][m_ptr->fx];
+
+ /* Copy list of all objects in the grid; we need a
+ * copy since we're going to be excising objects
+ * from lists. */
+ auto const object_idxs(c_ptr->o_idxs);
+
+ /* Scan all objects in the grid */
+ bool done = false;
+ for (auto const this_o_idx: object_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_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;
+
+ /* Carry object */
+ m_ptr->hold_o_idxs.push_back(this_o_idx);
+
+ /* Picked up at least one object */
+ done = true;
+ }
+
+ /* Feedback */
+ if (done)
+ {
+ msg_print("You pick up all objects on the floor.");
+ }
+
+ return TRUE;
+}
+
+bool_ do_control_drop(void)
+{
+ monster_type *m_ptr = &m_list[p_ptr->control];
+
+ if (!p_ptr->control) return FALSE;
+ monster_drop_carried_objects(m_ptr);
+ return TRUE;
+}
+
+bool_ do_control_magic(void)
+{
+ int power = -1;
+ int num = 0, i;
+ int powers[96];
+ bool_ flag, redraw;
+ int ask;
+ char choice;
+ char out_val[160];
+ monster_race *r_ptr = &r_info[m_list[p_ptr->control].r_idx];
+ int label;
+
+ if (!p_ptr->control) return FALSE;
+
+ if (get_check("Do you want to abandon the creature?"))
+ {
+ if (get_check("Abandon it permanently?"))
+ delete_monster_idx(p_ptr->control);
+ p_ptr->control = 0;
+ return TRUE;
+ }
+
+ /* List the monster powers -- RF4_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags4 & BIT(i))
+ {
+ if (!monster_powers[i].power) continue;
+ powers[num++] = i;
+ }
+ }
+
+ /* List the monster powers -- RF5_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags5 & BIT(i))
+ {
+ if (!monster_powers[i + 32].power) continue;
+ powers[num++] = i + 32;
+ }
+ }
+
+ /* List the monster powers -- RF6_* */
+ for (i = 0; i < 32; i++)
+ {
+ if (r_ptr->flags6 & BIT(i))
+ {
+ if (!monster_powers[i + 64].power) continue;
+ powers[num++] = i + 64;
+ }
+ }
+
+ if (!num)
+ {
+ msg_print("You have no powers you can use.");
+ return (TRUE);
+ }
+
+ /* Nothing chosen yet */
+ flag = FALSE;
+
+ /* No redraw yet */
+ redraw = FALSE;
+
+ /* Get the last label */
+ label = (num <= 26) ? I2A(num - 1) : I2D(num - 1 - 26);
+
+ /* Build a prompt (accept all spells) */
+ strnfmt(out_val, 78,
+ "(Powers a-%c, *=List, ESC=exit) Use which power of your golem? ",
+ label);
+
+ /* Get a spell from the user */
+ while (!flag && get_com(out_val, &choice))
+ {
+ /* Request redraw */
+ if ((choice == ' ') || (choice == '*') || (choice == '?'))
+ {
+ /* Show the list */
+ if (!redraw)
+ {
+ byte y = 1, x = 0;
+ int ctr = 0;
+ char dummy[80];
+
+ strcpy(dummy, "");
+
+ /* Show list */
+ redraw = TRUE;
+
+ /* Save the screen */
+ character_icky = TRUE;
+ Term_save();
+
+ prt ("", y++, x);
+
+ while (ctr < num)
+ {
+ monster_power *mp_ptr = &monster_powers[powers[ctr]];
+
+ label = (ctr < 26) ? I2A(ctr) : I2D(ctr - 26);
+
+ strnfmt(dummy, 80, " %c) %s",
+ label, mp_ptr->name);
+
+ if (ctr < 17)
+ {
+ prt(dummy, y + ctr, x);
+ }
+ else
+ {
+ prt(dummy, y + ctr - 17, x + 40);
+ }
+
+ ctr++;
+ }
+
+ if (ctr < 17)
+ {
+ prt ("", y + ctr, x);
+ }
+ else
+ {
+ prt ("", y + 17, x);
+ }
+ }
+
+ /* Hide the list */
+ else
+ {
+ /* Hide list */
+ redraw = FALSE;
+
+ /* Restore the screen */
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Redo asking */
+ continue;
+ }
+
+ if (choice == '\r' && num == 1)
+ {
+ choice = 'a';
+ }
+
+ if (isalpha(choice))
+ {
+ /* Note verify */
+ ask = (isupper(choice));
+
+ /* Lowercase */
+ if (ask) choice = tolower(choice);
+
+ /* Extract request */
+ i = (islower(choice) ? A2I(choice) : -1);
+ }
+ else
+ {
+ /* Can't uppercase digits XXX XXX XXX */
+ ask = FALSE;
+
+ i = choice - '0' + 26;
+ }
+
+ /* Totally Illegal */
+ if ((i < 0) || (i >= num))
+ {
+ bell();
+ continue;
+ }
+
+ /* Save the spell index */
+ power = powers[i];
+
+ /* Verify it */
+ if (ask)
+ {
+ char tmp_val[160];
+
+ /* Prompt */
+ strnfmt(tmp_val, 78, "Use %s? ", monster_powers[power].name);
+
+ /* Belay that order */
+ if (!get_check(tmp_val)) continue;
+ }
+
+ /* Stop the loop */
+ flag = TRUE;
+ }
+
+ /* Restore the screen */
+ if (redraw)
+ {
+ Term_load();
+ character_icky = FALSE;
+ }
+
+ /* Take a turn */
+ if (flag)
+ {
+ energy_use = 100;
+ monst_spell_monst_spell = power + 96;
+ }
+ return TRUE;
+}
+
+/* Finds the controlled monster and "reconnect" to it */
+bool_ do_control_reconnect()
+{
+ int i;
+
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->mflag & MFLAG_CONTROL)
+ {
+ p_ptr->control = i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/*
+ * Turns a simple pet into a faithful companion
+ */
+void do_cmd_companion()
+{
+ monster_type *m_ptr;
+ int ii, jj;
+
+ if (!can_create_companion())
+ {
+ msg_print("You cannot have any more companions.");
+ return;
+ }
+
+ if (!tgt_pt(&ii, &jj))
+ {
+ msg_print("You must target a pet.");
+ return;
+ }
+
+ if (cave[jj][ii].m_idx)
+ {
+ char m_name[100];
+
+ m_ptr = &m_list[cave[jj][ii].m_idx];
+
+ monster_desc(m_name, m_ptr, 0);
+ if (m_ptr->status == MSTATUS_PET)
+ {
+ m_ptr->status = MSTATUS_COMPANION;
+ msg_format("%^s agrees to follow you.", m_name);
+ }
+ else
+ {
+ msg_format("%^s is not your pet!", m_name);
+ }
+ }
+ else
+ msg_print("You must target a pet.");
+}
+
+/*
+ * List companions to the character sheet.
+ */
+void dump_companions(FILE *outfile)
+{
+ int i;
+ int done_hdr = 0;
+
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ /* Ignore "dead" monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->status == MSTATUS_COMPANION)
+ {
+ char pet_name[80];
+
+ /* Output the header if we haven't yet. */
+ if (!done_hdr)
+ {
+ done_hdr = 1;
+ fprintf(outfile, "\n\n [Current companions]\n\n");
+ }
+
+ /* List the monster. */
+ monster_desc(pet_name, m_ptr, 0x88);
+ fprintf(outfile, "%s (level %d)\n", pet_name, m_ptr->level);
+ }
+ }
+}
diff --git a/src/monster3.hpp b/src/monster3.hpp
new file mode 100644
index 00000000..7cf8ccd0
--- /dev/null
+++ b/src/monster3.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "h-basic.h"
+#include "monster_type_fwd.hpp"
+
+extern void dump_companions(FILE *outfile);
+extern void do_cmd_companion(void);
+extern bool_ do_control_reconnect(void);
+extern bool_ do_control_drop(void);
+extern bool_ do_control_magic(void);
+extern bool_ do_control_pickup(void);
+extern bool_ do_control_inven(void);
+extern bool_ do_control_walk(void);
+extern bool_ can_create_companion(void);
+extern void ai_deincarnate(int m_idx);
+extern bool_ ai_possessor(int m_idx, int o_idx);
+extern bool_ ai_multiply(int m_idx);
+extern bool_ change_side(monster_type *m_ptr);
+extern int is_friend(monster_type *m_ptr);
+extern bool_ is_enemy(monster_type *m_ptr, monster_type *t_ptr);
diff --git a/src/monster_blow.hpp b/src/monster_blow.hpp
new file mode 100644
index 00000000..0f19f64c
--- /dev/null
+++ b/src/monster_blow.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Monster blow descriptor.
+ *
+ * - Method (RBM_*)
+ * - Effect (RBE_*)
+ * - Damage Dice
+ * - Damage Sides
+ */
+struct monster_blow
+{
+ byte method;
+ byte effect;
+ byte d_dice;
+ byte d_side;
+};
diff --git a/src/monster_ego.hpp b/src/monster_ego.hpp
new file mode 100644
index 00000000..00464c2e
--- /dev/null
+++ b/src/monster_ego.hpp
@@ -0,0 +1,81 @@
+#pragma once
+
+#include "h-basic.h"
+#include "monster_blow.hpp"
+
+/**
+ * Monster ego descriptors.
+ */
+struct monster_ego
+{
+ const char *name; /* Name */
+ 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 */
+};
diff --git a/src/monster_ego_fwd.hpp b/src/monster_ego_fwd.hpp
new file mode 100644
index 00000000..5b47f3e9
--- /dev/null
+++ b/src/monster_ego_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct monster_ego;
diff --git a/src/monster_power.hpp b/src/monster_power.hpp
new file mode 100644
index 00000000..03d0f8a9
--- /dev/null
+++ b/src/monster_power.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Monster powers that players can use via e.g. Symbiosis.
+ */
+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 */
+};
diff --git a/src/monster_race.hpp b/src/monster_race.hpp
new file mode 100644
index 00000000..7e5d5082
--- /dev/null
+++ b/src/monster_race.hpp
@@ -0,0 +1,116 @@
+#pragma once
+
+#include "body.hpp"
+#include "h-basic.h"
+#include "monster_blow.hpp"
+#include "obj_theme.hpp"
+
+/**
+ * Monster race descriptors and runtime data, 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.
+ */
+struct monster_race
+{
+ const char *name; /* Name */
+ char *text; /* Text */
+
+ 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 */
+};
+
+
diff --git a/src/monster_race_fwd.hpp b/src/monster_race_fwd.hpp
new file mode 100644
index 00000000..9ee9658a
--- /dev/null
+++ b/src/monster_race_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct monster_race;
diff --git a/src/monster_type.cc b/src/monster_type.cc
new file mode 100644
index 00000000..5731e430
--- /dev/null
+++ b/src/monster_type.cc
@@ -0,0 +1,8 @@
+#include "monster_type_fwd.hpp"
+#include "monster_type.hpp"
+#include "monster2.hpp"
+
+std::shared_ptr<monster_race> monster_type::race() const
+{
+ return race_info_idx(r_idx, ego);
+}
diff --git a/src/monster_type.hpp b/src/monster_type.hpp
new file mode 100644
index 00000000..8353f228
--- /dev/null
+++ b/src/monster_type.hpp
@@ -0,0 +1,98 @@
+#pragma once
+
+#include "h-basic.h"
+#include "monster_blow.hpp"
+#include "monster_race_fwd.hpp"
+
+#include <cassert>
+#include <vector>
+#include <memory>
+
+/**
+ * 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).
+ */
+struct monster_type
+{
+ s16b r_idx = 0; /* Monster race index */
+
+ u16b ego = 0; /* Ego monster type */
+
+ byte fy = 0; /* Y location on map */
+ byte fx = 0; /* X location on map */
+
+ s32b hp = 0; /* Current Hit points */
+ s32b maxhp = 0; /* Max Hit points */
+
+ monster_blow blow[4] = { /* Up to four blows per round */
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ };
+
+ byte speed = 0; /* Speed (normally 110) */
+ byte level = 0; /* Level of creature */
+ s16b ac = 0; /* Armour Class */
+ s32b exp = 0; /* Experience */
+
+ s16b csleep = 0; /* Inactive counter */
+
+ byte mspeed = 0; /* Monster "speed" */
+ byte energy = 0; /* Monster "energy" */
+
+ byte stunned = 0; /* Monster is stunned */
+ byte confused = 0; /* Monster is confused */
+ byte monfear = 0; /* Monster is afraid */
+
+ s16b bleeding = 0; /* Monster is bleeding */
+ s16b poisoned = 0; /* Monster is poisoned */
+
+ byte cdis = 0; /* Current dis from player */
+
+ s32b mflag = 0; /* Extra monster flags */
+
+ bool_ ml = FALSE; /* Monster is "visible" */
+
+ std::vector<s16b> hold_o_idxs { }; /* Objects being held */
+
+ u32b smart = 0; /* Field for "smart_learn" */
+
+ s16b status = 0; /* Status(friendly, pet, companion, ..) */
+
+ s16b target = 0; /* Monster target */
+
+ s16b possessor = 0; /* Is it under the control of a possessor ? */
+
+ /**
+ * @brief get the "effective race" of the monster. This incorporates
+ * the effects of the "ego" of the monster, if any.
+ */
+ std::shared_ptr<monster_race> race() const;
+
+ /**
+ * @brief wipe the object's state
+ */
+ void wipe()
+ {
+ /* Reset to defaults */
+ *this = monster_type();
+ }
+
+ /**
+ * Get the o_idx of the object being mimicked
+ */
+ s16b mimic_o_idx() const
+ {
+ // We *should* also assert that the monster has flag RF9_MIMIC,
+ // but it's currently not safe since the functions we need for
+ // that are a) expensive, and b) side-effecting via statics.
+ assert(hold_o_idxs.size() == 1); // Mimics are defined by exactly one object
+ return hold_o_idxs.front();
+ }
+
+};
diff --git a/src/monster_type_fwd.hpp b/src/monster_type_fwd.hpp
new file mode 100644
index 00000000..a44baa9d
--- /dev/null
+++ b/src/monster_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct monster_type;
diff --git a/src/move_info_type.hpp b/src/move_info_type.hpp
new file mode 100644
index 00000000..5f8aa3d8
--- /dev/null
+++ b/src/move_info_type.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Movement typse
+ */
+struct move_info_type
+{
+ s16b to_speed;
+ s16b to_search;
+ s16b to_stealth;
+ s16b to_percep;
+ cptr name;
+};
diff --git a/src/music.hpp b/src/music.hpp
new file mode 100644
index 00000000..5f37c570
--- /dev/null
+++ b/src/music.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Music descriptor.
+ */
+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) */
+};
diff --git a/src/notes.cc b/src/notes.cc
new file mode 100644
index 00000000..326381c9
--- /dev/null
+++ b/src/notes.cc
@@ -0,0 +1,185 @@
+/*
+ * 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 "notes.hpp"
+
+#include "files.hpp"
+#include "player_class.hpp"
+#include "player_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+
+/*
+ * 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];
+
+ /* 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 */
+ fprintf(fff, "%s\n", final_note);
+
+ /* 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 turn_s[50];
+ char depths[32];
+
+ /* Get the first 60 chars - so do not have an overflow */
+ memset(buf, 0, sizeof(buf));
+ strncpy(buf, note, 60);
+
+ /* Get date and time */
+ sprintf(turn_s, "Turn % 12ld", static_cast<long int>(turn));
+
+ /* Get depth */
+ if (!dun_level) strcpy(depths, " Town");
+ else sprintf(depths, "Lev%3d", dun_level);
+
+ /* Make note */
+ sprintf(final_note, "%-20s %s %c: %s", turn_s, 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 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));
+
+ /* 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);
+
+ /* 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, true_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/notes.hpp b/src/notes.hpp
new file mode 100644
index 00000000..e8c22bb7
--- /dev/null
+++ b/src/notes.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+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);
diff --git a/src/obj_theme.hpp b/src/obj_theme.hpp
new file mode 100644
index 00000000..13f185e8
--- /dev/null
+++ b/src/obj_theme.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Object theme. Probability in percent for each class of
+ * objects to be dropped.
+ */
+struct obj_theme
+{
+ byte treasure;
+ byte combat;
+ byte magic;
+ byte tools;
+};
diff --git a/src/obj_theme_fwd.hpp b/src/obj_theme_fwd.hpp
new file mode 100644
index 00000000..6c0d19a3
--- /dev/null
+++ b/src/obj_theme_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct obj_theme;
diff --git a/src/object1.cc b/src/object1.cc
new file mode 100644
index 00000000..6bbf23e9
--- /dev/null
+++ b/src/object1.cc
@@ -0,0 +1,6698 @@
+/*
+ * 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 "object1.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd2.hpp"
+#include "cmd6.hpp"
+#include "dungeon.hpp"
+#include "dungeon_info_type.hpp"
+#include "ego_item_type.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "hook_get_in.hpp"
+#include "hooks.hpp"
+#include "lua_bind.hpp"
+#include "mimic.hpp"
+#include "monster1.hpp"
+#include "monster2.hpp"
+#include "monster_ego.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "options.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "set_type.hpp"
+#include "skills.hpp"
+#include "spell_type.hpp"
+#include "spells5.hpp"
+#include "squeltch.hpp"
+#include "stats.hpp"
+#include "store_info_type.hpp"
+#include "tables.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "wilderness_type_info.hpp"
+#include "xtra1.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+static bool_ apply_flags_set(s16b a_idx, s16b set_idx,
+ u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp);
+
+/*
+ * 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
+
+
+/**
+ * Show all equipment/inventory slots, even when empty?
+ */
+static bool item_tester_full;
+
+
+/*
+ * 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 byte 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);
+}
+
+
+/*
+ * 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:
+ {
+ 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". 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;
+ }
+
+
+ /* Normal symbols */
+ process_pref_file("font.prf");
+}
+
+
+/*
+ * Extract "xtra" flags from object.
+ */
+static void object_flags_xtra(object_type const *o_ptr, u32b *f2, u32b *f3, u32b *esp)
+{
+ switch (o_ptr->xtra1)
+ {
+ case EGO_XTRA_SUSTAIN:
+ {
+ /* Choose a sustain */
+ switch (o_ptr->xtra2 % 6)
+ {
+ case 0:
+ (*f2) |= (TR2_SUST_STR);
+ break;
+ case 1:
+ (*f2) |= (TR2_SUST_INT);
+ break;
+ case 2:
+ (*f2) |= (TR2_SUST_WIS);
+ break;
+ case 3:
+ (*f2) |= (TR2_SUST_DEX);
+ break;
+ case 4:
+ (*f2) |= (TR2_SUST_CON);
+ break;
+ case 5:
+ (*f2) |= (TR2_SUST_CHR);
+ break;
+ }
+
+ break;
+ }
+
+ case EGO_XTRA_POWER:
+ {
+ /* Choose a power */
+ switch (o_ptr->xtra2 % 11)
+ {
+ case 0:
+ (*f2) |= (TR2_RES_BLIND);
+ break;
+ case 1:
+ (*f2) |= (TR2_RES_CONF);
+ break;
+ case 2:
+ (*f2) |= (TR2_RES_SOUND);
+ break;
+ case 3:
+ (*f2) |= (TR2_RES_SHARDS);
+ break;
+ case 4:
+ (*f2) |= (TR2_RES_NETHER);
+ break;
+ case 5:
+ (*f2) |= (TR2_RES_NEXUS);
+ break;
+ case 6:
+ (*f2) |= (TR2_RES_CHAOS);
+ break;
+ case 7:
+ (*f2) |= (TR2_RES_DISEN);
+ break;
+ case 8:
+ (*f2) |= (TR2_RES_POIS);
+ break;
+ case 9:
+ (*f2) |= (TR2_RES_DARK);
+ break;
+ case 10:
+ (*f2) |= (TR2_RES_LITE);
+ break;
+ }
+
+ break;
+ }
+
+ case EGO_XTRA_ABILITY:
+ {
+ /* Choose an ability */
+ switch (o_ptr->xtra2 % 8)
+ {
+ case 0:
+ (*f3) |= (TR3_FEATHER);
+ break;
+ case 1:
+ (*f3) |= (TR3_LITE1);
+ break;
+ case 2:
+ (*f3) |= (TR3_SEE_INVIS);
+ break;
+ case 3:
+ (*esp) |= (ESP_ALL);
+ break;
+ case 4:
+ (*f3) |= (TR3_SLOW_DIGEST);
+ break;
+ case 5:
+ (*f3) |= (TR3_REGEN);
+ break;
+ case 6:
+ (*f2) |= (TR2_FREE_ACT);
+ break;
+ case 7:
+ (*f2) |= (TR2_HOLD_LIFE);
+ break;
+ }
+
+ break;
+ }
+
+ }
+}
+
+
+/*
+ * Obtain the "flags" for an item
+ */
+bool_ object_flags_no_set = FALSE;
+void object_flags(object_type const *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Base object */
+ (*f1) = k_ptr->flags1;
+ (*f2) = k_ptr->flags2;
+ (*f3) = k_ptr->flags3;
+ (*f4) = k_ptr->flags4;
+ (*f5) = k_ptr->flags5;
+ (*esp) = k_ptr->esp;
+
+ /* Artifact */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ (*f1) = a_ptr->flags1;
+ (*f2) = a_ptr->flags2;
+ (*f3) = a_ptr->flags3;
+ (*f4) = a_ptr->flags4;
+ (*f5) = a_ptr->flags5;
+ (*esp) = a_ptr->esp;
+
+ if ((!object_flags_no_set) && (a_ptr->set != -1))
+ apply_flags_set(o_ptr->name1, a_ptr->set, f1, f2, f3, f4, f5, esp);
+ }
+
+ /* Random artifact ! */
+ if (o_ptr->art_flags1 || o_ptr->art_flags2 || o_ptr->art_flags3 || o_ptr->art_flags4 || o_ptr->art_flags5 || o_ptr->art_esp)
+ {
+ (*f1) |= o_ptr->art_flags1;
+ (*f2) |= o_ptr->art_flags2;
+ (*f3) |= o_ptr->art_flags3;
+ (*f4) |= o_ptr->art_flags4;
+ (*f5) |= o_ptr->art_flags5;
+ (*esp) |= o_ptr->art_esp;
+ }
+
+ /* Extra powers */
+ if (!(o_ptr->art_name))
+ {
+ object_flags_xtra(o_ptr, f2, f3, esp);
+ }
+}
+
+/* Return object granted power */
+int object_power(object_type *o_ptr)
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+ int power = -1;
+
+ /* Base object */
+ power = k_ptr->power;
+
+ /* Ego-item */
+ if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2];
+
+ if (power == -1) power = e_ptr->power;
+
+ if (o_ptr->name2b)
+ {
+ ego_item_type *e_ptr = &e_info[o_ptr->name2b];
+
+ if (power == -1) power = e_ptr->power;
+ }
+ }
+
+ /* Artifact */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ if (power == -1) power = a_ptr->power;
+ }
+
+ return (power);
+}
+
+
+
+/*
+ * Obtain the "flags" for an item which are known to the player
+ */
+void object_flags_known(object_type const *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp)
+{
+ 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;
+
+ /* Artifact */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Need full knowledge or spoilers */
+ if ((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 ((o_ptr->ident & IDENT_MENTAL))
+ {
+ (*f1) |= o_ptr->art_flags1;
+ (*f2) |= o_ptr->art_flags2;
+ (*f3) |= o_ptr->art_flags3;
+ (*f4) |= o_ptr->art_flags4;
+ (*f5) |= o_ptr->art_flags5;
+ (*esp) |= o_ptr->art_esp;
+ }
+
+ (*f1) |= o_ptr->art_oflags1;
+ (*f2) |= o_ptr->art_oflags2;
+ (*f3) |= o_ptr->art_oflags3;
+ (*f4) |= o_ptr->art_oflags4;
+ (*f5) |= o_ptr->art_oflags5;
+ (*esp) |= o_ptr->art_oesp;
+ }
+
+ /* Full knowledge for *identified* objects */
+ if (!(o_ptr->ident & IDENT_MENTAL)) return;
+
+ if (!(o_ptr->art_name))
+ {
+ object_flags_xtra(o_ptr, f2, f3, esp);
+ }
+
+ /* Hack - Res Chaos -> Res Confusion */
+ if (*f2 & TR2_RES_CHAOS) (*f2) |= (TR2_RES_CONF);
+}
+
+
+
+
+
+/*
+ * Print a char "c" into a string "t", as if by sprintf(t, "%c", c),
+ * and return a pointer to the terminator (t + 1).
+ */
+static char *object_desc_chr(char *t, char c)
+{
+ /* Copy the char */
+ *t++ = c;
+
+ /* Terminate */
+ *t = '\0';
+
+ /* Result */
+ return (t);
+}
+
+
+/*
+ * Print a string "s" into a string "t", as if by strcpy(t, s),
+ * and return a pointer to the terminator.
+ */
+static char *object_desc_str(char *t, cptr s)
+{
+ /* Copy the string */
+ while (*s) *t++ = *s++;
+
+ /* Terminate */
+ *t = '\0';
+
+ /* Result */
+ return (t);
+}
+
+/*
+ * Do the actual conversion of a number for object_desc_num() and
+ * object_desc_int().
+ */
+static char *convert_number(char *result, u32b num)
+{
+ char *tp;
+ char temp[11];
+
+ tp = temp;
+ *tp = '0' + (num % 10);
+ for (num /= 10; num != 0; num /= 10)
+ {
+ *++tp = '0' + (num % 10);
+ }
+
+ while (tp != temp)
+ {
+ *result++ = *tp--;
+ }
+ *result++ = *tp;
+ *result = '\0';
+
+ return result;
+}
+
+/*
+ * Print a nnumber "n" into a string "t", as if by
+ * sprintf(t, "%u", n), and return a pointer to the terminator.
+ */
+static char *object_desc_num(char *result, s32b num)
+{
+ u32b n;
+
+ if (num < 0)
+ {
+ *result++ = '-';
+ n = -num;
+ }
+ else
+ n = num;
+
+ /* Result */
+ return convert_number(result, n);
+}
+
+/*
+ * Print an signed number "num" into a string "result", as if by
+ * sprintf(t, "%+d", n), and return a pointer to the terminator.
+ * Note that we always print a sign, either "+" or "-".
+ */
+static char *object_desc_int(char *result, s32b num)
+{
+ u32b n;
+
+ /* Negative */
+ if (num < 0)
+ {
+ /* Take the absolute value */
+ n = -num;
+
+ /* Use a "minus" sign */
+ *result++ = '-';
+ }
+ /* Positive (or zero) */
+ else
+ {
+ /* Use the actual number */
+ n = num;
+
+ /* Use a "plus" sign */
+ *result++ = '+';
+ }
+
+ /* Result */
+ return convert_number(result, n);
+}
+
+/*
+ * Creates a description of the item "o_ptr", and stores it in "out_val".
+ *
+ * One can choose the "verbosity" of the description, including whether
+ * or not the "number" of items should be described, and how much detail
+ * should be used when describing the item.
+ *
+ * The given "buf" must be 80 chars long to hold the longest possible
+ * description, which can get pretty long, including incriptions, such as:
+ * "no more Maces of Disruption (Defender) (+10,+10) [+5] (+3 to stealth)".
+ * Note that the inscription will be clipped to keep the total description
+ * under 79 chars (plus a terminator).
+ *
+ * Note the use of "object_desc_num()" and "object_desc_int()" as hyper-efficient,
+ * portable, versions of some common "sprintf()" commands.
+ *
+ * Note that all ego-items (when known) append an "Ego-Item Name", unless
+ * the item is also an artifact, which should NEVER happen.
+ *
+ * Note that all artifacts (when known) append an "Artifact Name", so we
+ * have special processing for "Specials" (artifact Lites, Rings, Amulets).
+ * The "Specials" never use "modifiers" if they are "known", since they
+ * have special "descriptions", such as "The Necklace of the Dwarves".
+ *
+ * Special Lite's use the "k_info" base-name (Phial, Star, or Arkenstone),
+ * plus the artifact name, just like any other artifact, if known.
+ *
+ * Special Ring's and Amulet's, if not "aware", use the same code as normal
+ * rings and amulets, and if "aware", use the "k_info" base-name (Ring or
+ * Amulet or Necklace). They will NEVER "append" the "k_info" name. But,
+ * they will append the artifact name, just like any artifact, if known.
+ *
+ * None of the Special Rings/Amulets are "EASY_KNOW", though they could be,
+ * at least, those which have no "pluses", such as the three artifact lites.
+ *
+ * Hack -- Display "The One Ring" as "a Plain Gold Ring" until aware.
+ *
+ * If "pref" then a "numeric" prefix will be pre-pended.
+ *
+ * Mode:
+ * 0 -- The Cloak of Death
+ * 1 -- The Cloak of Death [1,+3]
+ * 2 -- The Cloak of Death [1,+3] (+2 to Stealth)
+ * 3 -- The Cloak of Death [1,+3] (+2 to Stealth) {nifty}
+ */
+void object_desc(char *buf, object_type *o_ptr, int pref, int mode)
+{
+ bool_ hack_name = FALSE;
+ cptr basenm, modstr;
+ int indexx;
+
+ bool_ aware = FALSE;
+ bool_ known = FALSE;
+
+ bool_ append_name = FALSE;
+
+ bool_ show_weapon = FALSE;
+ bool_ show_armour = FALSE;
+
+ cptr s, u;
+ char *t;
+
+ char p1 = '(', p2 = ')';
+ char b1 = '[', b2 = ']';
+ char c1 = '{', c2 = '}';
+
+ char tmp_val[160];
+ char tmp_val2[90];
+
+ s32b power;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ cptr str;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ /* See if the object is "aware" */
+ if (object_aware_p(o_ptr)) aware = TRUE;
+
+ /* See if the object is "known" */
+ if (object_known_p(o_ptr)) known = TRUE;
+
+ /* Hack -- Extract the sub-type "indexx" */
+ indexx = o_ptr->sval;
+
+ /* Extract default "base" string */
+ basenm = k_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 (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;
+ }
+
+ break;
+ }
+
+ /* Rings (including a few "Specials") */
+ case TV_RING:
+ {
+ /* Color the object */
+ modstr = ring_adj[indexx];
+ if (aware) append_name = TRUE;
+
+ if (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;
+ }
+
+ break;
+ }
+
+ case TV_STAFF:
+ {
+ /* Color the object */
+ modstr = staff_adj[o_ptr->pval2 % MAX_WOODS];
+ if (aware) append_name = TRUE;
+ if (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 (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 (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:
+ {
+ modstr = k_info[lookup_kind(TV_ROD, o_ptr->pval)].name;
+ break;
+ }
+
+ case TV_SCROLL:
+ {
+ /* Color the object */
+ modstr = scroll_adj[indexx];
+ if (aware) append_name = TRUE;
+ if (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
+ {
+ modstr = get_mimic_name(o_ptr->pval2);
+ }
+ if (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 (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)
+ {
+ modstr = get_mimic_object_name(o_ptr->pval2);
+ }
+ 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_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_ptr->name);
+ else
+ basenm = format("& %s #~", r_ptr->name);
+ break;
+ }
+
+ case TV_EGG:
+ {
+ monster_race* r_ptr = &r_info[o_ptr->pval2];
+ modstr = basenm;
+
+ basenm = format("& %s #~", 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_ptr->name);
+ break;
+ }
+
+ case TV_TOTEM:
+ {
+ char name[80];
+ monster_type monster;
+
+ 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_ptr->name;
+ if (o_ptr->sval == 255)
+ {
+ modstr = spell_type_name(spell_at(o_ptr->pval));
+ }
+ break;
+ }
+
+ /* Used in the "inventory" routine */
+ default:
+ {
+ strcpy(buf, "(nothing)");
+ return;
+ }
+ }
+
+ /* Mega Hack */
+ if ((!hack_name) && known && (k_ptr->flags5 & TR5_FULL_NAME))
+ {
+ basenm = 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;
+ }
+ else if (e2_ptr->before)
+ {
+ ego = e2_ptr->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_ptr->name);
+ t = object_desc_chr(t, ' ');
+ }
+ if (e2_ptr->before)
+ {
+ t = object_desc_str(t, 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_ptr->name);
+ t = object_desc_chr(t, ' ');
+ }
+ if (e2_ptr->before)
+ {
+ t = object_desc_str(t, 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_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, spell_type_name(spell_at(o_ptr->pval2)));
+ 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_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_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_ptr->name);
+ }
+ if (o_ptr->name2b && !e2_ptr->before)
+ {
+ t = object_desc_chr(t, ' ');
+ t = object_desc_str(t, e2_ptr->name);
+ }
+ }
+ }
+
+ /* It contains a spell */
+ if ((known) && (f5 & TR5_SPELL_CONTAIN) && (o_ptr->pval2 != -1))
+ {
+ t = object_desc_str(t, format(" [%s]", spell_type_name(spell_at(o_ptr->pval2))));
+ }
+
+ /* 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 (o_ptr->elevel < PY_MAX_LEVEL)
+ {
+ /* Formula from check_experience_obj(). */
+ s32b need = player_exp[o_ptr->elevel - 1] * 5 / 2;
+ t = object_desc_num(t, need - o_ptr->exp);
+ }
+ else
+ {
+ t = object_desc_str(t, "*****");
+ }
+ t = object_desc_str(t, ", L:");
+ t = object_desc_num(t, o_ptr->elevel);
+ 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_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, '(');
+ t = object_desc_num(t, 100 * o_ptr->pval / 5);
+ t = object_desc_str(t, "%)");
+ }
+
+ if ((known) && (f2 & TR2_LIFE) ) /* Can disp neg now -- Improv */
+ {
+ t = object_desc_chr(t, '(');
+ t = object_desc_num(t, 100 * o_ptr->pval / 5);
+ 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_info[lookup_kind(TV_RUNE2, mod)].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";
+ }
+ }
+ }
+
+ return activation_aux(o_ptr, FALSE, 0);
+}
+
+/* Grab the tval desc */
+static 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
+ */
+static void describe_device(object_type *o_ptr)
+{
+ char buf[128];
+
+ /* 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);
+
+ // Spell reference
+ auto spell = spell_at(o_ptr->pval2);
+
+ text_out("\nSpell description:\n");
+ spell_type_description_foreach(spell,
+ [] (std::string const &text) -> void {
+ text_out("\n");
+ text_out(text.c_str());
+ });
+
+ text_out("\nSpell level: ");
+ sprintf(buf, FMTs32b, get_level(o_ptr->pval2, 50, 0));
+ text_out_c(TERM_L_BLUE, buf);
+
+ text_out("\nMinimum Magic Device level to increase spell level: ");
+ text_out_c(TERM_L_BLUE, format("%d", spell_type_skill_level(spell)));
+
+ text_out("\nSpell fail: ");
+ sprintf(buf, FMTs32b, spell_chance_device(spell));
+ text_out_c(TERM_GREEN, buf);
+
+ text_out("\nSpell info: ");
+ text_out_c(TERM_YELLOW, spell_type_info(spell));
+
+ /* 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);
+ }
+ else
+ {
+ sprintf(str, "in %s", wf_info[level].text);
+ }
+ }
+ else
+ {
+ sprintf(str, "on level %d of %s", level, d_info[dungeon].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;
+
+ const 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_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_ptr->text);
+ text_out("\n");
+
+ if (a_ptr->set != -1)
+ {
+ text_out_c(TERM_GREEN, set_info[a_ptr->set].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)
+ {
+ 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 / 5;
+
+
+ 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));
+ }
+ else if (o_ptr->found == OBJ_FOUND_STOLEN)
+ {
+ text_out(format("\nYou stole it from the %s.",
+ st_info[o_ptr->found_aux1].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
+ */
+static 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
+ */
+static 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
+ */
+static 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 const *o_ptr, bool_ ideal)
+{
+ /* Theme has restrictions for winged races. */
+ if (game_module_idx == MODULE_THEME)
+ {
+ cptr race_name = rp_ptr->title;
+
+ if (streq(race_name, "Dragon") ||
+ streq(race_name, "Eagle"))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_CLOAK:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ return -1;
+ }
+ }
+ }
+
+ /* 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;
+ }
+
+ case TV_DAEMON_BOOK:
+ {
+ int slot = -1;
+
+ switch (o_ptr->sval)
+ {
+ case SV_DEMONBLADE : slot = INVEN_WIELD; break;
+ case SV_DEMONSHIELD: slot = INVEN_ARM; break;
+ case SV_DEMONHORN : slot = INVEN_HEAD; break;
+ }
+
+ if ((slot >= 0) && (!ideal))
+ {
+ slot = get_slot(slot);
+ }
+
+ return slot;
+ }
+ }
+
+ /* 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 const *o_ptr)
+{
+ return wield_slot_ideal(o_ptr, FALSE);
+}
+
+/*
+ * Return a string mentioning how a given item is carried
+ */
+static 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 = nullptr;
+
+ 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
+ */
+static bool item_tester_okay(object_type const *o_ptr, object_filter_t const &filter)
+{
+ /* 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 against the filter */
+ return filter(o_ptr);
+}
+
+
+
+
+static void show_equip_aux(bool_ mirror, bool_ everything, object_filter_t const &filter);
+static void show_inven_aux(bool_ mirror, bool_ everything, object_filter_t const &filter);
+
+/*
+ * Choice window "shadow" of the "show_inven()" function
+ */
+void display_inven(void)
+{
+ show_inven_aux(TRUE, inventory_no_move, object_filter::True());
+}
+
+
+
+/*
+ * Choice window "shadow" of the "show_equip()" function
+ */
+void display_equip(void)
+{
+ show_equip_aux(TRUE, inventory_no_move, object_filter::True());
+}
+
+
+
+/* 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, const object_filter_t &filter)
+{
+ 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;
+
+ /* Space for weight */
+ lim -= 9;
+
+ /* Space for icon */
+ 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, filter))
+ {
+ 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 */
+ l += 9;
+
+ /* Account for icon */
+ 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 */
+ {
+ byte a = object_attr(o_ptr);
+ char c = object_char(o_ptr);
+
+ if (!o_ptr->k_idx) c = ' ';
+
+ Term_draw(col + 3, row + j, a, c);
+ }
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], row + j,
+ col + 5);
+
+ /* Display the weight */
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ sprintf(tmp_val, "%3d.%1d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, row + j, wid - 9);
+ }
+ }
+
+ /* Shadow windows */
+ if (mirror)
+ {
+ int hgt;
+ Term_get_size(nullptr, &hgt);
+ /* Erase the rest of the window */
+ for (j = row + k; j < 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);
+ }
+}
+
+
+static void show_inven(object_filter_t const &filter)
+{
+ show_inven_aux(FALSE, FALSE, filter);
+}
+
+void show_inven_full()
+{
+ item_tester_full = true;
+ show_inven(object_filter::True());
+ item_tester_full = false;
+}
+
+static void show_equip(object_filter_t const &filter)
+{
+ show_equip_aux(FALSE, FALSE, filter);
+}
+
+void show_equip_full()
+{
+ item_tester_full = true;
+ show_equip(object_filter::True());
+ item_tester_full = false;
+}
+
+/*
+ * Display the equipment.
+ */
+void show_equip_aux(bool_ mirror, bool_ everything, object_filter_t const &filter)
+{
+ 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 */
+ lim -= (14 + 2);
+
+ /* Require space for weight */
+ lim -= 9;
+
+ /* Require space for icon */
+ 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)", get_melee_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_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, filter))
+ {
+ 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) */
+ l += (14 + 2);
+
+ /* Increase length for weight */
+ l += 9;
+
+ /* Icon */
+ 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);
+
+ /* Show icon */
+ {
+ byte a = object_attr(o_ptr);
+ char c = object_char(o_ptr);
+
+ if (!o_ptr->k_idx) c = ' ';
+
+ Term_draw(col + 3, row + j, a, c);
+ }
+
+ /* Use labels */
+ {
+ /* Mention the use */
+ (void)sprintf(tmp_val, "%-14s: ", mention_use(out_rindex[j]));
+ put_str(tmp_val, row + j, col + 5);
+
+ /* Display the entry itself */
+ c_put_str(out_color[j], out_desc[j], row + j, col + 21);
+ }
+
+ /* Display the weight */
+ {
+ int wgt = o_ptr->weight * o_ptr->number;
+ sprintf(tmp_val, "%3d.%d lb", wgt / 10, wgt % 10);
+ put_str(tmp_val, row + j, wid - 9);
+ }
+ }
+
+
+ /* Shadow windows */
+ if (mirror)
+ {
+ int hgt;
+ Term_get_size(nullptr, &hgt);
+ /* Erase the rest of the window */
+ for (j = row + k; j < hgt; j++)
+ {
+ /* Erase the line */
+ Term_erase(0, j, 255);
+ }
+ }
+
+ /* Main window */
+ else
+ {
+ /* Make a "shadow" below the list (only if needed) */
+ if (j && (j < 23)) prt("", row + j, col ? col - 2 : col);
+ }
+}
+
+
+
+
+/*
+ * Flip "inven" and "equip" in any sub-windows
+ */
+void toggle_inven_equip(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ /* Unused */
+ if (!angband_term[j]) continue;
+
+ /* Flip inven to equip */
+ if (window_flag[j] & (PW_INVEN))
+ {
+ /* Flip flags */
+ window_flag[j] &= ~(PW_INVEN);
+ window_flag[j] |= (PW_EQUIP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ /* Flip inven to equip */
+ else if (window_flag[j] & (PW_EQUIP))
+ {
+ /* Flip flags */
+ window_flag[j] &= ~(PW_EQUIP);
+ window_flag[j] |= (PW_INVEN);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+ }
+}
+
+
+
+/*
+ * Verify the choice of an item.
+ *
+ * The item can be negative to mean "item on floor".
+ */
+bool_ verify(cptr prompt, int item)
+{
+ char o_name[80];
+
+ char out_val[160];
+
+ object_type *o_ptr;
+
+ /* Get object */
+ o_ptr = get_object(item);
+
+ /* Describe */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Prompt */
+ (void)sprintf(out_val, "%s %s? ", prompt, o_name);
+
+ /* Query */
+ return (get_check(out_val));
+}
+
+
+/*
+ * Hack -- allow user to "prevent" certain choices
+ *
+ * The item can be negative to mean "item on floor".
+ */
+static bool_ get_item_allow(int item)
+{
+ cptr s;
+
+ object_type *o_ptr;
+
+ /* Get object */
+ o_ptr = get_object(item);
+
+ /* No inscription */
+ if (!o_ptr->note) return (TRUE);
+
+ /* Find a '!' */
+ s = strchr(quark_str(o_ptr->note), '!');
+
+ /* Process preventions */
+ while (s)
+ {
+ /* Check the "restriction" */
+ if ((s[1] == command_cmd) || (s[1] == '*'))
+ {
+ /* Verify the choice */
+ if (!verify("Really try", item)) return (FALSE);
+ }
+
+ /* Find another '!' */
+ s = strchr(s + 1, '!');
+ }
+
+ /* Allow it */
+ return (TRUE);
+}
+
+
+
+/*
+ * Auxiliary function for "get_item()" -- test an index
+ */
+static bool get_item_okay(int i, object_filter_t const &filter)
+{
+ /* Illegal items */
+ if ((i < 0) || (i >= INVEN_TOTAL))
+ {
+ return (FALSE);
+ }
+
+ /* Verify the item */
+ return item_tester_okay(&p_ptr->inventory[i], filter);
+}
+
+
+
+/*
+ * 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.
+ */
+static std::vector<int> scan_floor(int y, int x, object_filter_t const &filter)
+{
+ std::vector<int> items;
+
+ /* Sanity */
+ if (!in_bounds(y, x))
+ {
+ return items;
+ }
+
+ /* Scan all objects in the grid */
+ for (auto const this_o_idx: cave[y][x].o_idxs)
+ {
+ /* Acquire object */
+ object_type * o_ptr = &o_list[this_o_idx];
+
+ /* Item tester */
+ if (!item_tester_okay(o_ptr, filter))
+ {
+ continue;
+ }
+
+ /* Accept this item */
+ items.push_back(this_o_idx);
+
+ /* XXX Hack -- Enforce limit */
+ if (items.size() == 23) break;
+ }
+
+ /* Result */
+ return items;
+}
+
+/*
+ * Display a list of the items on the floor at the given location.
+ */
+static void show_floor(int y, int x, object_filter_t const &filter)
+{
+ 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];
+
+ /* Default length */
+ len = 79 - 50;
+
+ /* Maximum space allowed for descriptions */
+ lim = 79 - 3;
+
+ /* Require space for weight */
+ lim -= 9;
+
+ /* Scan for objects in the grid, using item_tester_okay() */
+ auto const floor_list = scan_floor(y, x, filter);
+ assert(floor_list.size() <= 23);
+ int const floor_num = floor_list.size(); // "int" for warning avoidance
+
+ /* 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;
+
+ /* Account for the weight */
+ 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 */
+ {
+ 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.
+ */
+static bool_ get_item_floor(int *cp, cptr pmt, cptr str, int mode, object_filter_t const &filter, select_by_name_t const &select_by_name)
+{
+ 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_ 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_top = 0;
+
+ k = 0;
+
+ /* Get the item index */
+ if (repeat_pull(cp))
+ {
+ /* Floor item? */
+ if (*cp < 0)
+ {
+ object_type *o_ptr;
+
+ /* Special index */
+ k = 0 - (*cp);
+
+ /* Acquire object */
+ o_ptr = &o_list[k];
+
+ /* Validate the item */
+ if (item_tester_okay(o_ptr, filter))
+ {
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Verify the item */
+ else if (get_item_okay(*cp, filter))
+ {
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+
+ /* Extract args */
+ if (mode & (USE_EQUIP)) equip = TRUE;
+ if (mode & (USE_INVEN)) inven = TRUE;
+ if (mode & (USE_FLOOR)) floor = TRUE;
+ if (mode & (USE_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, filter))) i1++;
+ while ((i1 <= i2) && (!get_item_okay(i2, filter))) 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, filter))) e1++;
+ while ((e1 <= e2) && (!get_item_okay(e2, filter))) e2--;
+
+
+ /* Floor items? */
+ auto const floor_list =
+ floor ? scan_floor(p_ptr->py, p_ptr->px, filter)
+ : std::vector<int>();
+ assert(floor_list.size() <= 23);
+ int const floor_num = floor_list.size(); // "int" for warning avoidance
+
+ /* Accept inventory */
+ if (i1 <= i2) allow_inven = TRUE;
+
+ /* Accept equipment */
+ if (e1 <= e2) allow_equip = TRUE;
+
+ /* Accept floor */
+ if (!floor_list.empty()) allow_floor = TRUE;
+
+ /* Require at least one legal choice */
+ if (!allow_inven && !allow_equip && !allow_floor)
+ {
+ /* Oops */
+ oops = TRUE;
+
+ /* Done */
+ done = TRUE;
+ }
+
+ /* Analyze choices */
+ else
+ {
+ /* Hack -- Start on equipment if requested */
+ if ((command_wrk == (USE_EQUIP)) && allow_equip)
+ {
+ command_wrk = (USE_EQUIP);
+ }
+
+ /* Use inventory if allowed */
+ else if (allow_inven)
+ {
+ command_wrk = (USE_INVEN);
+ }
+
+ /* Use equipment if allowed */
+ else if (allow_equip)
+ {
+ command_wrk = (USE_EQUIP);
+ }
+
+ /* Use floor if allowed */
+ else if (allow_floor)
+ {
+ command_wrk = (USE_FLOOR);
+ }
+ }
+
+ /* Save screen */
+ screen_save();
+
+ /* Repeat until done */
+ while (!done)
+ {
+ /* Show choices */
+ {
+ int ni = 0;
+ int ne = 0;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ /* Unused */
+ if (!angband_term[j]) continue;
+
+ /* Count windows displaying inven */
+ if (window_flag[j] & (PW_INVEN)) ni++;
+
+ /* Count windows displaying equip */
+ if (window_flag[j] & (PW_EQUIP)) ne++;
+ }
+
+ /* Toggle if needed */
+ if ((command_wrk == (USE_EQUIP) && ni && !ne) ||
+ (command_wrk == (USE_INVEN) && !ni && ne))
+ {
+ /* Toggle */
+ toggle_inven_equip();
+
+ /* Track toggles */
+ toggle = !toggle;
+ }
+
+ /* Update */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Redraw windows */
+ window_stuff();
+ }
+
+ /* Inventory screen */
+ if (command_wrk == (USE_INVEN))
+ {
+ /* Extract the legal requests */
+ n1 = I2A(i1);
+ n2 = I2A(i2);
+
+ /* Redraw */
+ show_inven(filter);
+ }
+
+ /* Equipment screen */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ /* Extract the legal requests */
+ n1 = I2A(e1 - INVEN_WIELD);
+ n2 = I2A(e2 - INVEN_WIELD);
+
+ /* Redraw */
+ show_equip(filter);
+ }
+
+ /* Floor screen */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ j = floor_top;
+ k = MIN(floor_top + 23, floor_num) - 1;
+
+ /* Extract the legal requests */
+ n1 = I2A(j - floor_top);
+ n2 = I2A(k - floor_top);
+
+ /* Redraw */
+ show_floor(p_ptr->py, p_ptr->px, filter);
+ }
+
+ /* Viewing inventory */
+ if (command_wrk == (USE_INVEN))
+ {
+ /* Begin the prompt */
+ sprintf(out_val, "Inven:");
+
+ /* Build the prompt */
+ sprintf(tmp_val, " %c-%c,",
+ index_to_label(i1), index_to_label(i2));
+
+ /* Append */
+ strcat(out_val, tmp_val);
+
+ /* Append */
+ if (allow_equip) strcat(out_val, " / for Equip,");
+
+ /* Append */
+ if (allow_floor) strcat(out_val, " - for floor,");
+ }
+
+ /* Viewing equipment */
+ else if (command_wrk == (USE_EQUIP))
+ {
+ /* Begin the prompt */
+ sprintf(out_val, "Equip:");
+
+ /* Build the prompt */
+ sprintf(tmp_val, " %c-%c,",
+ index_to_label(e1), index_to_label(e2));
+
+ /* Append */
+ strcat(out_val, tmp_val);
+
+ /* Append */
+ if (allow_inven) strcat(out_val, " / for Inven,");
+
+ /* Append */
+ if (allow_floor) strcat(out_val, " - for floor,");
+ }
+
+ /* Viewing floor */
+ else if (command_wrk == (USE_FLOOR))
+ {
+ /* Begin the prompt */
+ sprintf(out_val, "Floor:");
+
+ /* Build the prompt */
+ sprintf(tmp_val, " %c-%c,", n1, n2);
+
+ /* Append */
+ strcat(out_val, tmp_val);
+
+ /* Append */
+ if (allow_inven)
+ {
+ strcat(out_val, " / for Inven,");
+ }
+ else if (allow_equip)
+ {
+ strcat(out_val, " / for Equip,");
+ }
+ }
+
+ /* Do we allow selection by name? */
+ if (select_by_name)
+ {
+ strcat(out_val, " @ for extra selection,");
+ }
+
+ /* Create automatizer rule?? */
+ if (automat)
+ {
+ if (automatizer_create)
+ strcat(out_val, " $ new automatizer rule(ON),");
+ else
+ strcat(out_val, " $ new automatizer rule(OFF),");
+ }
+
+ /* Finish the prompt */
+ strcat(out_val, " ESC");
+
+ /* Build the prompt */
+ sprintf(tmp_val, "(%s) %s", out_val, pmt);
+
+ /* Show the prompt */
+ prt(tmp_val, 0, 0);
+
+ /* Get a key */
+ which = inkey();
+
+ /* Parse it */
+ switch (which)
+ {
+ case ESCAPE:
+ {
+ done = TRUE;
+ break;
+ }
+
+ case '/':
+ {
+ if (command_wrk == (USE_INVEN))
+ {
+ if (!allow_equip)
+ {
+ bell();
+ break;
+ }
+ command_wrk = (USE_EQUIP);
+ }
+ else if (command_wrk == (USE_EQUIP))
+ {
+ if (!allow_inven)
+ {
+ bell();
+ break;
+ }
+ command_wrk = (USE_INVEN);
+ }
+ else if (command_wrk == (USE_FLOOR))
+ {
+ if (allow_inven)
+ {
+ command_wrk = (USE_INVEN);
+ }
+ else if (allow_equip)
+ {
+ command_wrk = (USE_EQUIP);
+ }
+ else
+ {
+ bell();
+ break;
+ }
+ }
+
+ /* Hack -- Fix screen */
+ screen_load();
+ screen_save();
+
+ /* Need to redraw */
+ break;
+ }
+
+ case '-':
+ {
+ if (!allow_floor)
+ {
+ bell();
+ break;
+ }
+
+ /*
+ * If we are already examining the floor, and there
+ * is only one item, we will always select it.
+ * If we aren't examining the floor and there is only
+ * one item, we will select it if floor_query_flag
+ * is FALSE.
+ */
+ if (floor_num == 1)
+ {
+ if (command_wrk == (USE_FLOOR))
+ {
+ /* Special index */
+ k = 0 - floor_list[0];
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+
+ break;
+ }
+ }
+
+ /* Hack -- Fix screen */
+ screen_load();
+ screen_save();
+
+ command_wrk = (USE_FLOOR);
+
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ /* Look up the tag */
+ if (!get_tag(&k, which))
+ {
+ bell();
+ break;
+ }
+
+ /* Hack -- Validate the item */
+ if ((k < INVEN_WIELD) ? !inven : !equip)
+ {
+ bell();
+ break;
+ }
+
+ /* Validate the item */
+ if (!get_item_okay(k, filter))
+ {
+ 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, filter))
+ {
+ 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 '@':
+ {
+ // Ignore if not "enabled"
+ if (!select_by_name)
+ {
+ break;
+ }
+ // Find by name
+ if (auto i = select_by_name(filter))
+ {
+ (*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, filter))
+ {
+ bell();
+ break;
+ }
+
+ /* Verify the item */
+ if (ver && !verify("Try", k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Allow player to "refuse" certain actions */
+ if (!get_item_allow(k))
+ {
+ done = TRUE;
+ break;
+ }
+
+ /* Accept that choice */
+ (*cp) = k;
+ item = TRUE;
+ done = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Fix the screen */
+ screen_load();
+
+ /* Track */
+ if (item && done)
+ {
+ if (*cp >= 0)
+ {
+ object_track(&p_ptr->inventory[*cp]);
+ }
+ else
+ {
+ object_track(&o_list[0 - *cp]);
+ }
+ }
+
+ /* Clean up */
+ {
+ /* Toggle again if needed */
+ if (toggle) toggle_inven_equip();
+
+ /* Update */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+
+ /* Window stuff */
+ window_stuff();
+ }
+
+
+ /* Clear the prompt line */
+ prt("", 0, 0);
+
+ /* Warning if needed */
+ if (oops && str) msg_print(str);
+
+
+ if (item) repeat_push(*cp);
+
+ /* Result */
+ return (item);
+}
+
+
+/*
+ * Let the user select an item, save its "index"
+ *
+ * Return TRUE only if an acceptable item was chosen by the user.
+ *
+ * The selected item must satisfy the "item_tester_hook()" function,
+ * if that hook is set, and the "item_tester_tval", if that value is set.
+ *
+ * All "item_tester" restrictions are cleared before this function returns.
+ *
+ * The user is allowed to choose acceptable items from the equipment,
+ * inventory, or floor, respectively, if the proper flag was given,
+ * and there are any acceptable items in that location.
+ *
+ * The equipment or inventory are displayed (even if no acceptable
+ * items are in that location) if the proper flag was given.
+ *
+ * If there are no acceptable items available anywhere, and "str" is
+ * not NULL, then it will be used as the text of a warning message
+ * before the function returns.
+ *
+ * Note that the user must press "-" to specify the item on the floor,
+ * and there is no way to "examine" the item on the floor, while the
+ * use of "capital" letters will "examine" an inventory/equipment item,
+ * and prompt for its use.
+ *
+ * If a legal item is selected from the inventory, we save it in "cp"
+ * directly (0 to 35), and return TRUE.
+ *
+ * If a legal item is selected from the floor, we save it in "cp" as
+ * a negative (-1 to -511), and return TRUE.
+ *
+ * If no item is available, we do nothing to "cp", and we display a
+ * warning message, using "str" if available, and return FALSE.
+ *
+ * If no item is selected, we do nothing to "cp", and return FALSE.
+ *
+ * Global "p_ptr->command_new" is used when viewing the inventory or equipment
+ * to allow the user to enter a command while viewing those screens, and
+ * also to induce "auto-enter" of stores, and other such stuff.
+ *
+ * Global "p_ptr->command_wrk" is used to choose between equip/inven listings.
+ * If it is TRUE then we are viewing inventory, else equipment.
+ *
+ * We always erase the prompt when we are done, leaving a blank line,
+ * or a warning message, if appropriate, if no items are available.
+ */
+bool_ get_item(int *cp, cptr pmt, cptr str, int mode, object_filter_t const &filter, select_by_name_t const &select_by_name)
+{
+ automatizer_create = FALSE;
+ return get_item_floor(cp, pmt, str, mode, filter, select_by_name);
+}
+
+/*
+ * Hook to determine if an object is getable
+ */
+static bool item_tester_hook_getable(object_type const *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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ return slot;
+}
+
+
+/*
+ * Try to pickup arrows
+ */
+void pickup_ammo()
+{
+ /* Copy list of objects since we're manipulating the list */
+ auto const object_idxs(cave[p_ptr->py][p_ptr->px].o_idxs);
+
+ /* Scan the pile of objects */
+ for (auto const this_o_idx: object_idxs)
+ {
+ /* Acquire object */
+ object_type *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.");
+ s16b slot = wear_ammo(o_ptr);
+
+ if (slot != -1)
+ {
+ /* Get the item again */
+ o_ptr = &p_ptr->inventory[slot];
+
+ /* Describe the object */
+ char o_name[80];
+ 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);
+ }
+ }
+ }
+}
+
+
+/**
+ * Check for encumberance if player were to pick up
+ * given item.
+ */
+static bool can_carry_heavy(object_type const *o_ptr)
+{
+ /* Extract the "weight limit" (in tenth pounds) */
+ int i = weight_limit();
+
+ /* Calculate current encumbarance */
+ int j = calc_total_weight();
+
+ /* Apply encumbarance from weight */
+ int old_enc = 0;
+ 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 */
+ int new_enc = 0;
+ if (j > i / 2) new_enc = ((j - (i / 2)) / (i / 10));
+
+ /* If the encumberance is the same, then we pick up without prompt */
+ return (new_enc <= old_enc);
+}
+
+/* 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
+ {
+ /* Hooks */
+ {
+ hook_get_in in = { o_ptr, this_o_idx };
+ if (process_hooks_new(HOOK_GET, &in, NULL))
+ {
+ return;
+ }
+ }
+
+ q_ptr = &p_ptr->inventory[INVEN_AMMO];
+
+ /* Carry the item */
+ if (object_similar(o_ptr, q_ptr))
+ {
+ msg_print("You add the ammo to your quiver.");
+ slot = wear_ammo(o_ptr);
+ }
+ else
+ {
+ slot = inven_carry(o_ptr, FALSE);
+ }
+
+ /* Sanity check */
+ if (slot != -1)
+ {
+ /* Get the item again */
+ o_ptr = &p_ptr->inventory[slot];
+
+ object_track(o_ptr);
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You have %s (%c).", o_name, index_to_label(slot));
+
+ /* Delete the object */
+ delete_object_idx(this_o_idx);
+
+ /* Sense object. */
+ sense_inventory();
+ }
+ }
+}
+
+
+static void absorb_gold(cave_type const *c_ptr)
+{
+ /* Copy list of objects since we're going to manipulate the list itself */
+ auto const object_idxs(c_ptr->o_idxs);
+
+ /* Go through everything */
+ for (auto const this_o_idx: object_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_o_idx];
+
+ /* Hack -- disturb */
+ disturb(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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Delete the gold */
+ delete_object_idx(this_o_idx);
+ }
+ }
+}
+
+static void sense_floor(cave_type const *c_ptr)
+{
+ /* Build a list of the floor objects. */
+ std::vector<int> floor_object_idxs;
+ {
+ /* Reserve the correct number of slots */
+ floor_object_idxs.reserve(c_ptr->o_idxs.size());
+ /* Fill in the indexes */
+ for (auto const this_o_idx: c_ptr->o_idxs)
+ {
+ // Note the "-"! We need it for get_object()
+ // lookups to function correctly.
+ floor_object_idxs.push_back(0 - this_o_idx);
+ }
+ }
+
+ /* 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)
+ {
+ for (auto const o_idx: floor_object_idxs)
+ {
+ object_type *o_ptr = get_object(o_idx);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ }
+ }
+
+ /* Sense floor tile */
+ sense_objects(floor_object_idxs);
+}
+
+void py_pickup_floor(int pickup)
+{
+ /* Get the tile */
+ auto c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ /* Hack -- ignore monster traps */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ return;
+ }
+
+ /* Try to grab ammo */
+ pickup_ammo();
+
+ /* Auto-ID and pseudo-ID */
+ sense_floor(c_ptr);
+
+ /* Squeltch the floor */
+ squeltch_grid();
+
+ /* Absorb gold on the tile */
+ absorb_gold(&cave[p_ptr->py][p_ptr->px]);
+
+ /* We handle 0, 1, or "many" items cases separately */
+ if (c_ptr->o_idxs.empty())
+ {
+ /* Nothing to do */
+ }
+ else if (c_ptr->o_idxs.size() == 1)
+ {
+ /* Acquire object */
+ auto floor_o_idx = c_ptr->o_idxs.front();
+ auto o_ptr = &o_list[floor_o_idx];
+
+ /* Describe or pick up? */
+ if (!pickup)
+ {
+ /* Describe */
+ char o_name[80] = "";
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You see %s.", o_name);
+ }
+ else
+ {
+ /* Are we actually going to pick up? */
+ bool_ do_pickup = TRUE;
+
+ /* Hack -- query every item */
+ if (carry_query_flag || (!can_carry_heavy(&o_list[floor_o_idx])))
+ {
+ char o_name[80] = "";
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ 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);
+ return; /* Done */
+ }
+ else
+ {
+ char out_val[160];
+ sprintf(out_val, "Pick up %s? ", o_name);
+ do_pickup = get_check(out_val);
+ }
+ }
+
+ /* Just pick it up; unless it's a symbiote and we don't have Symbiosis */
+ if (do_pickup && ((o_list[floor_o_idx].tval != TV_HYPNOS) || (get_skill(SKILL_SYMBIOTIC))))
+ {
+ object_pickup(floor_o_idx);
+ }
+ }
+ }
+ else
+ {
+ /* Describe or pick up? */
+ if (!pickup)
+ {
+ /* Message */
+ msg_format("You see a pile of %d items.", c_ptr->o_idxs.size());
+ }
+ else
+ {
+ /* Prompt for the item to pick up */
+ cptr q = "Get which item? ";
+ cptr s = "You have no room in your pack for any of the items here.";
+ int item;
+ if (get_item(&item, q, s, (USE_FLOOR), item_tester_hook_getable))
+ {
+ s16b this_o_idx = 0 - item;
+
+ bool_ do_pickup = TRUE;
+ if (!can_carry_heavy(&o_list[this_o_idx]))
+ {
+ /* Describe the object */
+ char o_name[80] = "";
+ object_desc(o_name, &o_list[this_o_idx], TRUE, 3);
+
+ /* Prompt */
+ char out_val[160];
+ sprintf(out_val, "Pick up %s? ", o_name);
+ do_pickup = get_check(out_val);
+ }
+
+ /* Pick up the item */
+ if (do_pickup)
+ {
+ object_pickup(this_o_idx);
+ }
+ }
+ }
+ }
+}
+
+/* Add a flags group */
+static void gain_flag_group(object_type *o_ptr)
+{
+ 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);
+
+ /* Message */
+ {
+ 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);
+ }
+}
+
+static 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 */
+static void gain_flag_group_flag(object_type *o_ptr)
+{
+ 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;
+ }
+
+ /* Message */
+ {
+ 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);
+ }
+ else
+ {
+ if (!o_ptr->pval3) gain_flag_group(o_ptr);
+
+ gain_flag_group_flag(o_ptr);
+
+ 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);
+ }
+ 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;
+
+ assert(s_ptr->num_use > 0);
+ s_ptr->num_use--;
+
+ if (s_ptr->num_use == s_ptr->num - 1)
+ {
+ cmsg_format(TERM_GREEN, "%s item set not complete anymore.", s_ptr->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);
+}
+
+static 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);
+}
+
+/*
+ * Return the "attr" for a given item.
+ * Use "flavor" if available.
+ * Default to user definitions.
+ */
+
+byte object_attr(object_type const *o_ptr)
+{
+ if (o_ptr->tval == TV_RANDART)
+ {
+ return random_artifacts[o_ptr->sval].attr;
+ }
+ else if (k_info[o_ptr->k_idx].flavor)
+ {
+ return misc_to_attr[k_info[o_ptr->k_idx].flavor];
+ }
+ else
+ {
+ return k_info[o_ptr->k_idx].x_attr;
+ }
+}
+
+byte object_attr_default(object_type *o_ptr)
+{
+ if (o_ptr->tval == TV_RANDART)
+ {
+ return random_artifacts[o_ptr->sval].attr;
+ }
+ else if (k_info[o_ptr->k_idx].flavor)
+ {
+ return misc_to_attr[k_info[o_ptr->k_idx].flavor];
+ }
+ else
+ {
+ return k_info[o_ptr->k_idx].d_attr;
+ }
+}
+
+/*
+ * Return the "char" for a given item.
+ * Use "flavor" if available.
+ * Default to user definitions.
+ */
+
+char object_char(object_type const *o_ptr)
+{
+ if (k_info[o_ptr->k_idx].flavor)
+ {
+ return misc_to_char[k_info[o_ptr->k_idx].flavor];
+ }
+ else
+ {
+ return k_info[o_ptr->k_idx].x_char;
+ }
+}
+
+char object_char_default(object_type const *o_ptr)
+{
+ if (k_info[o_ptr->k_idx].flavor)
+ {
+ return misc_to_char[k_info[o_ptr->k_idx].flavor];
+ }
+ else
+ {
+ return k_info[o_ptr->k_idx].d_char;
+ }
+}
+
+/**
+ * Is the given object an artifact?
+ */
+bool artifact_p(object_type const *o_ptr)
+{
+ return
+ (o_ptr->tval == TV_RANDART) ||
+ (o_ptr->name1 ? true : false) ||
+ (o_ptr->art_name ? true : false) ||
+ ((k_info[o_ptr->k_idx].flags3 & TR3_NORM_ART) ? true : false);
+}
+
+/**
+ * Is the given object an ego item?
+ */
+bool ego_item_p(object_type const *o_ptr)
+{
+ return o_ptr->name2 || (o_ptr->name2b ? TRUE : FALSE);
+}
+
+/*
+ * Is the given object an ego item of the given type?
+ */
+bool is_ego_p(object_type const *o_ptr, s16b ego)
+{
+ return (o_ptr->name2 == ego) || (o_ptr->name2b == ego);
+}
+
+/**
+ * Is the given object identified as cursed?
+ */
+bool cursed_p(object_type const *o_ptr)
+{
+ return o_ptr->ident & (IDENT_CURSED);
+}
diff --git a/src/object1.hpp b/src/object1.hpp
new file mode 100644
index 00000000..ec8f9367
--- /dev/null
+++ b/src/object1.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_filter.hpp"
+
+#include <boost/optional.hpp>
+#include <functional>
+
+typedef std::function<boost::optional<int>(object_filter_t const &filter)> select_by_name_t;
+
+extern byte get_item_letter_color(object_type *o_ptr);
+extern void object_pickup(int this_o_idx);
+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 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 const *o_ptr, u32b *f1, u32b *f2, u32b *f3, u32b *f4, u32b *f5, u32b *esp);
+extern void object_flags_known(object_type const *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 wield_slot_ideal(object_type const *o_ptr, bool_ ideal);
+extern s16b wield_slot(object_type const *o_ptr);
+extern cptr describe_use(int i);
+extern void display_inven(void);
+extern void display_equip(void);
+extern void show_inven_full();
+extern void show_equip_full();
+extern void toggle_inven_equip(void);
+extern bool_ get_item(int *cp, cptr pmt, cptr str, int mode, object_filter_t const &filter = object_filter::True(), select_by_name_t const &select_by_name = select_by_name_t());
+extern cptr item_activation(object_type *o_ptr,byte num);
+extern void py_pickup_floor(int pickup);
+extern void object_gain_level(object_type *o_ptr);
+extern byte object_attr(object_type const *o_ptr);
+extern byte object_attr_default(object_type *o_ptr);
+extern char object_char(object_type const *o_ptr);
+extern char object_char_default(object_type const *o_ptr);
+extern bool artifact_p(object_type const *o_ptr);
+extern bool ego_item_p(object_type const *o_ptr);
+extern bool is_ego_p(object_type const *o_ptr, s16b ego);
+extern bool cursed_p(object_type const *o_ptr);
diff --git a/src/object2.cc b/src/object2.cc
new file mode 100644
index 00000000..620037a3
--- /dev/null
+++ b/src/object2.cc
@@ -0,0 +1,6465 @@
+/*
+ * 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 "object2.hpp"
+
+#include "alloc_entry.hpp"
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "spell_type.hpp"
+#include "device_allocation.hpp"
+#include "dungeon_info_type.hpp"
+#include "ego_item_type.hpp"
+#include "feature_type.hpp"
+#include "hooks.hpp"
+#include "mimic.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "randart.hpp"
+#include "randart_part_type.hpp"
+#include "skills.hpp"
+#include "spells2.hpp"
+#include "spells3.hpp"
+#include "spells5.hpp"
+#include "tables.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "xtra1.hpp"
+#include "z-rand.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <type_traits>
+#include <vector>
+
+/*
+ * 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)
+{
+ /* Function to remove from list */
+ auto remove_it = [o_idx](std::vector<s16b> *v) -> void {
+ v->erase(
+ std::remove(
+ v->begin(),
+ v->end(),
+ o_idx),
+ v->end());
+ };
+
+ /* Object */
+ object_type *o_ptr = &o_list[o_idx];
+
+ /* Monster */
+ if (o_ptr->held_m_idx)
+ {
+ /* Monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Remove object from list of held objects, if present. */
+ remove_it(&m_ptr->hold_o_idxs);
+ }
+ /* Dungeon */
+ else
+ {
+ /* Grid */
+ cave_type *c_ptr = &cave[o_ptr->iy][o_ptr->ix];
+
+ /* Remove object from list of objects in the grid, if present. */
+ remove_it(&c_ptr->o_idxs);
+ }
+}
+
+
+/*
+ * 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)
+{
+ /* Refuse "illegal" locations */
+ if (!in_bounds(y, x)) return;
+
+ /* Grid */
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Scan all objects in the grid */
+ for (auto const this_o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_o_idx];
+
+ /* Wipe the object */
+ object_wipe(o_ptr);
+
+ /* Count objects */
+ o_cnt--;
+ }
+
+ /* Objects are gone */
+ c_ptr->o_idxs.clear();
+
+ /* 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)
+{
+ /* Do nothing */
+ if (i1 == i2) return;
+
+ /* Acquire object */
+ object_type *o_ptr = &o_list[i1];
+
+ /* Monster */
+ if (o_ptr->held_m_idx)
+ {
+ /* Acquire monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Repair monster */
+ for (auto &hold_o_idx: m_ptr->hold_o_idxs)
+ {
+ if (hold_o_idx == i1)
+ {
+ hold_o_idx = i2;
+ }
+ }
+ }
+
+ /* Dungeon */
+ else
+ {
+ /* Acquire grid */
+ cave_type *c_ptr = &cave[o_ptr->iy][o_ptr->ix];
+
+ /* Repair grid */
+ for (auto &o_idx: c_ptr->o_idxs)
+ {
+ if (o_idx == i1)
+ {
+ 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 */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+
+ /* Hack -- see above */
+ m_ptr->hold_o_idxs.clear();
+ }
+
+ /* 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_idxs.clear();
+ }
+
+ /* Wipe the object */
+ object_wipe(o_ptr);
+ }
+
+ /* 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);
+}
+
+
+
+/*
+ * Determine if a given inventory item is "known"
+ * Test One -- Check for special "known" tag
+ * Test Two -- Check for "Easy Know" + "Aware"
+ */
+bool object_known_p(object_type const *o_ptr)
+{
+ return ((o_ptr->ident & (IDENT_KNOWN)) ||
+ (k_info[o_ptr->k_idx].easy_know && k_info[o_ptr->k_idx].aware));
+}
+
+
+
+/*
+ * 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;
+}
+
+/**
+ * Is the player aware of the effects of the given object?
+ */
+bool object_aware_p(object_type const *o_ptr)
+{
+ return k_info[o_ptr->k_idx].aware;
+}
+
+
+/*
+ * 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;
+}
+
+
+/**
+ * Has the given object been "tried"?
+ */
+bool object_tried_p(object_type const *o_ptr)
+{
+ return k_info[o_ptr->k_idx].tried;
+}
+
+
+/*
+ * Return the "value" of an "unknown" item
+ * Make a guess at the value of non-aware items
+ */
+static s32b object_value_base(object_type const *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 const * 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 const *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 * spell_type_skill_level(spell_at(o_ptr->pval2));
+ else
+ value += 5000;
+ }
+
+ /* Analyze pval bonus */
+ switch (o_ptr->tval)
+ {
+ case TV_BOW:
+ case TV_BOOMERANG:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ case TV_LITE:
+ case TV_AMULET:
+ case TV_RING:
+ case TV_MSTAFF:
+ case TV_TRAPKIT:
+ case TV_INSTRUMENT:
+ {
+ /* No pval */
+ if (!o_ptr->pval) break;
+
+ /* Give credit for stat bonuses */
+ if (f1 & (TR1_STR)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_INT)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_WIS)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_DEX)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_CON)) value += (o_ptr->pval * 200L);
+ if (f1 & (TR1_CHR)) value += (o_ptr->pval * 200L);
+
+ if (f5 & (TR5_CRIT)) value += (o_ptr->pval * 500L);
+
+ /* Give credit for stealth and searching */
+ if (f1 & (TR1_STEALTH)) value += (o_ptr->pval * 100L);
+ if (f1 & (TR1_SEARCH)) value += (o_ptr->pval * 100L);
+
+ /* Give credit for infra-vision and tunneling */
+ if (f1 & (TR1_INFRA)) value += (o_ptr->pval * 50L);
+ if (f1 & (TR1_TUNNEL)) value += (o_ptr->pval * 50L);
+
+ /* Give credit for extra attacks */
+ if (f1 & (TR1_BLOWS)) value += (o_ptr->pval * 2000L);
+
+ /* Give credit for speed bonus */
+ if (f1 & (TR1_SPEED)) value += (o_ptr->pval * 30000L);
+
+ break;
+ }
+ }
+
+
+ /* Analyze the item */
+ switch (o_ptr->tval)
+ {
+ /* Eggs */
+ case TV_EGG:
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ /* Pay the monster level */
+ value += r_ptr->level * 100;
+
+ /* Done */
+ break;
+ }
+
+ /* Wands/Staffs */
+ case TV_WAND:
+ {
+ /* Par for the spell */
+ value *= spell_type_skill_level(spell_at(o_ptr->pval2));
+ /* 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 *= spell_type_skill_level(spell_at(o_ptr->pval2));
+ /* 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 * spell_type_skill_level(spell_at(o_ptr->pval));
+ }
+ /* 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 const *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 const *o_ptr, object_type const *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:
+ {
+ /* Fall through */
+ }
+
+ /* Rings, Amulets, Lites */
+ case TV_RING:
+ case TV_AMULET:
+ case TV_LITE:
+ {
+ /* Require full knowledge of both items */
+ if (!object_known_p(o_ptr) || !object_known_p(j_ptr)) return (0);
+
+ /* Require identical "turns of light" */
+ if (o_ptr->timeout != j_ptr->timeout) return (FALSE);
+
+ /* Fall through */
+ }
+
+ /* Missiles */
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_SHOT:
+ {
+ /* Require identical knowledge of both items */
+ if (object_known_p(o_ptr) != object_known_p(j_ptr)) return (0);
+
+ /* Require identical "bonuses" */
+ if (o_ptr->to_h != j_ptr->to_h) return (FALSE);
+ if (o_ptr->to_d != j_ptr->to_d) return (FALSE);
+ if (o_ptr->to_a != j_ptr->to_a) return (FALSE);
+
+ /* Require identical "pval" code */
+ if (o_ptr->pval != j_ptr->pval) return (FALSE);
+
+ /* Require identical exploding status code */
+ if (o_ptr->pval2 != j_ptr->pval2) return (FALSE);
+
+ /* Require identical "artifact" names */
+ if (o_ptr->name1 != j_ptr->name1) return (FALSE);
+
+ /* Require identical "ego-item" names */
+ if (o_ptr->name2 != j_ptr->name2) return (FALSE);
+
+ /* Do not combine different ego or normal ones */
+ if (o_ptr->name2b != j_ptr->name2b) return (FALSE);
+
+ /* Hack -- Never stack "powerful" items */
+ /*
+ Why?!
+ -- wilh
+ */
+ /* #if 0 */
+ if (o_ptr->xtra1 || j_ptr->xtra1) return (FALSE);
+ /* #endif */
+
+ /* Hack -- Never stack recharging items */
+ if ((o_ptr->timeout || j_ptr->timeout) &&
+ (o_ptr->tval != TV_LITE)) return (FALSE);
+
+ /* Require identical "values" */
+ if (o_ptr->ac != j_ptr->ac) return (FALSE);
+ if (o_ptr->dd != j_ptr->dd) return (FALSE);
+ if (o_ptr->ds != j_ptr->ds) return (FALSE);
+
+ /* Probably okay */
+ break;
+ }
+
+ /* UHH ugly hack for the mushroom quest, sorry */
+ case TV_FOOD:
+ {
+ if (o_ptr->pval2 != j_ptr->pval2) return (FALSE);
+ break;
+ }
+
+ /* 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);
+
+ /* 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 */
+ static_assert(std::is_pod<object_type>::value, "object_type must be POD type for memset to work");
+ memset(o_ptr, 0, sizeof(object_type));
+}
+
+
+/*
+ * Prepare an object based on an existing object
+ */
+void object_copy(object_type *o_ptr, object_type *j_ptr)
+{
+ /* Copy the structure */
+ *o_ptr = *j_ptr;
+}
+
+
+/*
+ * 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 */
+ object_wipe(o_ptr);
+
+ /* Save the kind index */
+ o_ptr->k_idx = k_idx;
+
+ /* Efficiency -- tval/sval */
+ o_ptr->tval = k_ptr->tval;
+ o_ptr->sval = k_ptr->sval;
+
+ /* Default "pval" */
+ o_ptr->pval = k_ptr->pval;
+ o_ptr->pval2 = k_ptr->pval2;
+
+ /* Default number */
+ o_ptr->number = 1;
+
+ /* Default weight */
+ o_ptr->weight = k_ptr->weight;
+
+ /* Default magic */
+ o_ptr->to_h = k_ptr->to_h;
+ o_ptr->to_d = k_ptr->to_d;
+ o_ptr->to_a = k_ptr->to_a;
+
+ /* Default power */
+ o_ptr->ac = k_ptr->ac;
+ o_ptr->dd = k_ptr->dd;
+ o_ptr->ds = k_ptr->ds;
+
+ /* Hack -- cursed items are always "cursed" */
+ if (k_ptr->flags3 & (TR3_CURSED)) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (k_ptr->flags4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ o_ptr->pval2 = 1; /* Start with one point */
+ o_ptr->pval3 = 0; /* No flags groups */
+ }
+}
+
+
+/*
+ * Help determine an "enchantment bonus" for an object.
+ *
+ * To avoid floating point but still provide a smooth distribution of bonuses,
+ * we simply round the results of division in such a way as to "average" the
+ * correct floating point value.
+ *
+ * This function has been changed. It uses "randnor()" to choose values from
+ * a normal distribution, whose mean moves from zero towards the max as the
+ * level increases, and whose standard deviation is equal to 1/4 of the max,
+ * and whose values are forced to lie between zero and the max, inclusive.
+ *
+ * Since the "level" rarely passes 100 before Morgoth is dead, it is very
+ * rare to get the "full" enchantment on an object, even a deep levels.
+ *
+ * It is always possible (albeit unlikely) to get the "full" enchantment.
+ *
+ * A sample distribution of values from "m_bonus(10, N)" is shown below:
+ *
+ * N 0 1 2 3 4 5 6 7 8 9 10
+ * --- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
+ * 0 66.37 13.01 9.73 5.47 2.89 1.31 0.72 0.26 0.12 0.09 0.03
+ * 8 46.85 24.66 12.13 8.13 4.20 2.30 1.05 0.36 0.19 0.08 0.05
+ * 16 30.12 27.62 18.52 10.52 6.34 3.52 1.95 0.90 0.31 0.15 0.05
+ * 24 22.44 15.62 30.14 12.92 8.55 5.30 2.39 1.63 0.62 0.28 0.11
+ * 32 16.23 11.43 23.01 22.31 11.19 7.18 4.46 2.13 1.20 0.45 0.41
+ * 40 10.76 8.91 12.80 29.51 16.00 9.69 5.90 3.43 1.47 0.88 0.65
+ * 48 7.28 6.81 10.51 18.27 27.57 11.76 7.85 4.99 2.80 1.22 0.94
+ * 56 4.41 4.73 8.52 11.96 24.94 19.78 11.06 7.18 3.68 1.96 1.78
+ * 64 2.81 3.07 5.65 9.17 13.01 31.57 13.70 9.30 6.04 3.04 2.64
+ * 72 1.87 1.99 3.68 7.15 10.56 20.24 25.78 12.17 7.52 4.42 4.62
+ * 80 1.02 1.23 2.78 4.75 8.37 12.04 27.61 18.07 10.28 6.52 7.33
+ * 88 0.70 0.57 1.56 3.12 6.34 10.06 15.76 30.46 12.58 8.47 10.38
+ * 96 0.27 0.60 1.25 2.28 4.30 7.60 10.77 22.52 22.51 11.37 16.53
+ * 104 0.22 0.42 0.77 1.36 2.62 5.33 8.93 13.05 29.54 15.23 22.53
+ * 112 0.15 0.20 0.56 0.87 2.00 3.83 6.86 10.06 17.89 27.31 30.27
+ * 120 0.03 0.11 0.31 0.46 1.31 2.48 4.60 7.78 11.67 25.53 45.72
+ * 128 0.02 0.01 0.13 0.33 0.83 1.41 3.24 6.17 9.57 14.22 64.07
+ */
+s16b m_bonus(int max, int level)
+{
+ int bonus, stand, extra, value;
+
+
+ /* Paranoia -- enforce maximal "level" */
+ if (level > MAX_DEPTH - 1) level = MAX_DEPTH - 1;
+
+
+ /* The "bonus" moves towards the max */
+ bonus = ((max * level) / MAX_DEPTH);
+
+ /* Hack -- determine fraction of error */
+ extra = ((max * level) % MAX_DEPTH);
+
+ /* Hack -- simulate floating point computations */
+ if (rand_int(MAX_DEPTH) < extra) bonus++;
+
+
+ /* The "stand" is equal to one quarter of the max */
+ stand = (max / 4);
+
+ /* Hack -- determine fraction of error */
+ extra = (max % 4);
+
+ /* Hack -- simulate floating point computations */
+ if (rand_int(4) < extra) stand++;
+
+
+ /* Choose an "interesting" value */
+ value = randnor(bonus, stand);
+
+ /* Enforce the minimum value */
+ if (value < 0) return (0);
+
+ /* Enforce the maximum value */
+ if (value > max) return (max);
+
+ /* Result */
+ return (value);
+}
+
+
+/*
+ * Tinker with the random artifact to make it acceptable
+ * for a certain depth; also connect a random artifact to an
+ * object.
+ */
+static void finalize_randart(object_type* o_ptr, int lev)
+{
+ int r;
+ int i = 0;
+ int foo = lev + randnor(0, 5);
+ bool_ flag = TRUE;
+
+ /* Paranoia */
+ if (o_ptr->tval != TV_RANDART) return;
+
+ if (foo < 1) foo = 1;
+ if (foo > 100) foo = 100;
+
+ while (flag)
+ {
+ r = rand_int(MAX_RANDARTS);
+
+ if (!(random_artifacts[r].generated) || i > 2000)
+ {
+ random_artifact* ra_ptr = &random_artifacts[r];
+
+ o_ptr->sval = r;
+ o_ptr->pval2 = ra_ptr->activation;
+ o_ptr->xtra2 = activation_info[ra_ptr->activation].spell;
+
+ ra_ptr->level = lev;
+ ra_ptr->generated = TRUE;
+ flag = FALSE;
+ }
+
+ i++;
+ }
+}
+
+
+
+/*
+ * Cheat -- describe a created object for the user
+ */
+static void object_mention(object_type *o_ptr)
+{
+ char o_name[80];
+
+ /* Describe */
+ object_desc_store(o_name, o_ptr, FALSE, 0);
+
+ /* Artifact */
+ if (artifact_p(o_ptr))
+ {
+ /* Silly message */
+ msg_format("Artifact (%s)", o_name);
+ }
+
+ /* Random Artifact */
+ else if (o_ptr->art_name)
+ {
+ msg_print("Random artifact");
+ }
+
+ /* Ego-item */
+ else if (ego_item_p(o_ptr))
+ {
+ /* Silly message */
+ msg_format("Ego-item (%s)", o_name);
+ }
+
+ /* Normal item */
+ else
+ {
+ /* Silly message */
+ msg_format("Object (%s)", o_name);
+ }
+}
+
+
+void random_artifact_resistance(object_type * o_ptr)
+{
+ bool_ give_resistance = FALSE, give_power = FALSE;
+
+ switch (o_ptr->name1)
+ {
+ case ART_CELEBORN:
+ case ART_ARVEDUI:
+ case ART_CASPANION:
+ case ART_TRON:
+ case ART_ROHIRRIM:
+ case ART_CELEGORM:
+ case ART_ANARION:
+ case ART_THRANDUIL:
+ case ART_LUTHIEN:
+ case ART_THROR:
+ case ART_THORIN:
+ case ART_NIMTHANC:
+ case ART_DETHANC:
+ case ART_NARTHANC:
+ case ART_STING:
+ case ART_TURMIL:
+ case ART_THALKETTOTH:
+ {
+ /* Give a resistance */
+ give_resistance = TRUE;
+ }
+ break;
+ case ART_MAEDHROS:
+ case ART_GLAMDRING:
+ case ART_ORCRIST:
+ case ART_ANDURIL:
+ case ART_ZARCUTHRA:
+ case ART_GURTHANG:
+ case ART_HARADEKKET:
+ case ART_CUBRAGOL:
+ case ART_DAWN:
+ {
+ /* Give a resistance OR a power */
+ if (randint(2) == 1) give_resistance = TRUE;
+ else give_power = TRUE;
+ }
+ break;
+ case ART_NENYA:
+ case ART_VILYA:
+ case ART_BERUTHIEL:
+ case ART_FINGOLFIN:
+ case ART_THINGOL:
+ case ART_ULMO:
+ case ART_OLORIN:
+ {
+ /* Give a power */
+ give_power = TRUE;
+ }
+ break;
+ case ART_POWER:
+ case ART_GONDOR:
+ case ART_AULE:
+ {
+ /* Give both */
+ give_power = TRUE;
+ give_resistance = TRUE;
+ }
+ break;
+ }
+
+ if (give_power)
+ {
+ o_ptr->xtra1 = EGO_XTRA_ABILITY;
+
+ /* Randomize the "xtra" power */
+ if (o_ptr->xtra1) o_ptr->xtra2 = randint(256);
+ }
+
+ artifact_bias = 0;
+
+ if (give_resistance)
+ {
+ random_resistance(o_ptr, FALSE, ((randint(22)) + 16));
+ }
+}
+
+
+/*
+ * Mega-Hack -- Attempt to create one of the "Special Objects"
+ *
+ * We are only called from "make_object()", and we assume that
+ * "apply_magic()" is called immediately after we return.
+ *
+ * Note -- see "make_artifact()" and "apply_magic()"
+ */
+static bool_ make_artifact_special(object_type *o_ptr)
+{
+ int i;
+ int k_idx = 0;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* No artifacts in the town */
+ if (!dun_level) return (FALSE);
+
+ /* Check the artifact list (just the "specials") */
+ for (i = 0; i < max_a_idx; i++)
+ {
+ artifact_type *a_ptr = &a_info[i];
+
+ /* Skip "empty" artifacts */
+ if (!a_ptr->name) continue;
+
+ /* Cannot make an artifact twice */
+ if (a_ptr->cur_num) continue;
+
+ /* Cannot generate non special ones */
+ if (!(a_ptr->flags3 & TR3_INSTA_ART)) continue;
+
+ /* Cannot generate some artifacts because they can only exists in special dungeons/quests/... */
+ if ((a_ptr->flags4 & TR4_SPECIAL_GENE) && (!a_allow_special[i])) continue;
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (a_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (a_ptr->level - dun_level) * 2;
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0) continue;
+ }
+
+ /* Artifact "rarity roll" */
+ if (rand_int(a_ptr->rarity - luck( -(a_ptr->rarity / 2), a_ptr->rarity / 2)) != 0) continue;
+
+ /* Find the base object */
+ k_idx = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* XXX XXX Enforce minimum "object" level (loosely) */
+ if (k_info[k_idx].level > object_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (k_info[k_idx].level - object_level) * 5;
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0) continue;
+ }
+
+ /* Assign the template */
+ object_prep(o_ptr, k_idx);
+
+ /* Mega-Hack -- mark the item as an artifact */
+ o_ptr->name1 = i;
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (f4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_info[k_idx].level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Failure */
+ return (FALSE);
+}
+
+
+/*
+ * Attempt to change an object into an artifact
+ *
+ * This routine should only be called by "apply_magic()"
+ *
+ * Note -- see "make_artifact_special()" and "apply_magic()"
+ */
+static bool_ make_artifact(object_type *o_ptr)
+{
+ int i;
+ u32b f1, f2, f3, f4, f5, esp;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* No artifacts in the town */
+ if (!dun_level) return (FALSE);
+
+ /* Paranoia -- no "plural" artifacts */
+ if (o_ptr->number != 1) return (FALSE);
+
+ /* Check the artifact list (skip the "specials") */
+ for (i = 0; i < max_a_idx; i++)
+ {
+ artifact_type *a_ptr = &a_info[i];
+
+ /* Skip "empty" items */
+ if (!a_ptr->name) continue;
+
+ /* Cannot make an artifact twice */
+ if (a_ptr->cur_num) continue;
+
+ /* Cannot generate special ones */
+ if (a_ptr->flags3 & TR3_INSTA_ART) continue;
+
+ /* Cannot generate some artifacts because they can only exists in special dungeons/quests/... */
+ if ((a_ptr->flags4 & TR4_SPECIAL_GENE) && (!a_allow_special[i])) continue;
+
+ /* Must have the correct fields */
+ if (a_ptr->tval != o_ptr->tval) continue;
+ if (a_ptr->sval != o_ptr->sval) continue;
+
+ /* XXX XXX Enforce minimum "depth" (loosely) */
+ if (a_ptr->level > dun_level)
+ {
+ /* Acquire the "out-of-depth factor" */
+ int d = (a_ptr->level - dun_level) * 2;
+
+ /* Roll for out-of-depth creation */
+ if (rand_int(d) != 0) continue;
+ }
+
+ /* We must make the "rarity roll" */
+ if (rand_int(a_ptr->rarity - luck( -(a_ptr->rarity / 2), a_ptr->rarity / 2)) != 0) continue;
+
+ /* Hack -- mark the item as an artifact */
+ o_ptr->name1 = i;
+
+ /* Hack: Some artifacts get random extra powers */
+ random_artifact_resistance(o_ptr);
+
+ /* Extract some flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack give a basic exp/exp level to an object that needs it */
+ if (f4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Success */
+ return (TRUE);
+ }
+
+ /* Failure */
+ return (FALSE);
+}
+
+/*
+ * Attempt to change an object into an ego
+ *
+ * This routine should only be called by "apply_magic()"
+ */
+static bool_ make_ego_item(object_type *o_ptr, bool_ good)
+{
+ bool_ ret = FALSE;
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ if (artifact_p(o_ptr) || o_ptr->name2) return (FALSE);
+
+ std::vector<size_t> ok_ego;
+
+ /* Grab the ok ego */
+ for (size_t 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 (size_t 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.push_back(i);
+ }
+
+ /* Now test them a few times */
+ for (size_t i = 0; i < ok_ego.size() * 10; i++)
+ {
+ size_t j = ok_ego[rand_int(ok_ego.size())];
+ ego_item_type *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 (size_t i = 0; i < ok_ego.size() * 10; i++)
+ {
+ int j = ok_ego[rand_int(ok_ego.size())]; // Explicit int conversion to avoid warning
+ ego_item_type *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;
+ }
+ }
+
+ /* Return */
+ return (ret);
+}
+
+
+/*
+ * Charge a new stick.
+ */
+void charge_stick(object_type *o_ptr)
+{
+ spell_type *spell = spell_at(o_ptr->pval2);
+ o_ptr->pval = spell_type_roll_charges(spell);
+}
+
+/*
+ * 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 */
+ 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 */
+ 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 = find_random_mimic_shape(level, TRUE);
+ 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 */
+ 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;
+ }
+ }
+}
+
+/*
+ * Randomized level
+ */
+static int randomized_level_in_range(range_type *range, int level)
+{
+ s32b r = range->max - range->min;
+
+ /* The basic idea is to have a max possible level of half the dungeon level */
+ if (r * 2 > level)
+ {
+ r = level / 2;
+ }
+
+ /* Randomize a bit */
+ r = m_bonus(r, dun_level);
+
+ /* get the result */
+ return range->min + r;
+}
+
+
+/*
+ * Get a random base level
+ */
+static int get_stick_base_level(byte tval, int level, int spl)
+{
+ spell_type *spell = spell_at(spl);
+ device_allocation *device_allocation = spell_type_device_allocation(spell, tval);
+ assert(device_allocation != NULL);
+ return randomized_level_in_range(&device_allocation->base_level, level);
+}
+
+/*
+ * Get a random max level
+ */
+static int get_stick_max_level(byte tval, int level, int spl)
+{
+ spell_type *spell = spell_at(spl);
+ device_allocation *device_allocation = spell_type_device_allocation(spell, tval);
+ assert(device_allocation != NULL);
+ return randomized_level_in_range(&device_allocation->max_level, level);
+}
+
+
+/*
+ * 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 */
+ 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 = get_random_spell(SKILL_MAGIC, level);
+ }
+ else
+ {
+ i = get_random_spell(SKILL_SPIRITUALITY, 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)
+ {
+ auto spl = get_random_stick(TV_WAND, dun_level);
+ if (spl == -1)
+ {
+ spl = 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 */
+ bonus_lvl = get_stick_base_level(TV_WAND, dun_level, o_ptr->pval2);
+ max_lvl = get_stick_max_level(TV_WAND, dun_level, o_ptr->pval2);
+ 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 = get_random_stick(TV_STAFF, dun_level);
+
+ if (spl == -1)
+ {
+ spl = GLOBELIGHT;
+ }
+
+ 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 */
+ bonus_lvl = get_stick_base_level(TV_STAFF, dun_level, o_ptr->pval2);
+ max_lvl = get_stick_max_level(TV_STAFF, dun_level, o_ptr->pval2);
+ 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 = find_random_mimic_shape(level, FALSE);
+ o_ptr->pval2 = mimic;
+ }
+ break;
+ case TV_INSTRUMENT:
+ {
+ if (o_ptr->sval != SV_HORN)
+ {
+ /* Nothing */
+ }
+ else
+ {
+ if (is_ego_p(o_ptr, EGO_INST_DRAGONKIND))
+ {
+ switch (randint(4))
+ {
+ case 1:
+ o_ptr->pval2 = GF_ELEC;
+ break;
+ case 2:
+ o_ptr->pval2 = GF_FIRE;
+ break;
+ case 3:
+ o_ptr->pval2 = GF_COLD;
+ break;
+ case 4:
+ o_ptr->pval2 = GF_ACID;
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ case TV_TOOL:
+ {
+ break;
+ }
+
+ }
+}
+
+void trap_hack(object_type *o_ptr)
+{
+ if (o_ptr->tval != TV_TRAPKIT) return;
+
+ switch (o_ptr->sval)
+ {
+ case SV_TRAPKIT_POTION:
+ case SV_TRAPKIT_SCROLL:
+ case SV_TRAPKIT_DEVICE:
+ o_ptr->to_h = 0;
+ o_ptr->to_d = 0;
+ default:
+ return;
+ }
+}
+
+/* Add a random glag to the ego item */
+void add_random_ego_flag(object_type *o_ptr, int fego, bool_ *limit_blows)
+{
+ if (fego & ETR4_SUSTAIN)
+ {
+ /* Make a random sustain */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ break;
+ case 2:
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ break;
+ case 3:
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ break;
+ case 4:
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ break;
+ case 5:
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ break;
+ case 6:
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ break;
+ }
+ }
+
+ if (fego & ETR4_OLD_RESIST)
+ {
+ /* Make a random resist, equal probabilities */
+ switch (randint(11))
+ {
+ case 1:
+ o_ptr->art_flags2 |= (TR2_RES_BLIND);
+ break;
+ case 2:
+ o_ptr->art_flags2 |= (TR2_RES_CONF);
+ break;
+ case 3:
+ o_ptr->art_flags2 |= (TR2_RES_SOUND);
+ break;
+ case 4:
+ o_ptr->art_flags2 |= (TR2_RES_SHARDS);
+ break;
+ case 5:
+ o_ptr->art_flags2 |= (TR2_RES_NETHER);
+ break;
+ case 6:
+ o_ptr->art_flags2 |= (TR2_RES_NEXUS);
+ break;
+ case 7:
+ o_ptr->art_flags2 |= (TR2_RES_CHAOS);
+ break;
+ case 8:
+ o_ptr->art_flags2 |= (TR2_RES_DISEN);
+ break;
+ case 9:
+ o_ptr->art_flags2 |= (TR2_RES_POIS);
+ break;
+ case 10:
+ o_ptr->art_flags2 |= (TR2_RES_DARK);
+ break;
+ case 11:
+ o_ptr->art_flags2 |= (TR2_RES_LITE);
+ break;
+ }
+ }
+
+ if (fego & ETR4_ABILITY)
+ {
+ /* Choose an ability */
+ switch (randint(8))
+ {
+ case 1:
+ o_ptr->art_flags3 |= (TR3_FEATHER);
+ break;
+ case 2:
+ o_ptr->art_flags3 |= (TR3_LITE1);
+ break;
+ case 3:
+ o_ptr->art_flags3 |= (TR3_SEE_INVIS);
+ break;
+ case 4:
+ o_ptr->art_esp |= (ESP_ALL);
+ break;
+ case 5:
+ o_ptr->art_flags3 |= (TR3_SLOW_DIGEST);
+ break;
+ case 6:
+ o_ptr->art_flags3 |= (TR3_REGEN);
+ break;
+ case 7:
+ o_ptr->art_flags2 |= (TR2_FREE_ACT);
+ break;
+ case 8:
+ o_ptr->art_flags2 |= (TR2_HOLD_LIFE);
+ break;
+ }
+ }
+
+ if (fego & ETR4_R_ELEM)
+ {
+ /* Make an acid/elec/fire/cold/poison resist */
+ random_resistance(o_ptr, FALSE, randint(14) + 4);
+ }
+ if (fego & ETR4_R_LOW)
+ {
+ /* Make an acid/elec/fire/cold resist */
+ random_resistance(o_ptr, FALSE, randint(12) + 4);
+ }
+
+ if (fego & ETR4_R_HIGH)
+ {
+ /* Make a high resist */
+ random_resistance(o_ptr, FALSE, randint(22) + 16);
+ }
+ if (fego & ETR4_R_ANY)
+ {
+ /* Make any resist */
+ random_resistance(o_ptr, FALSE, randint(34) + 4);
+ }
+
+ if (fego & ETR4_R_DRAGON)
+ {
+ /* Make "dragon resist" */
+ dragon_resist(o_ptr);
+ }
+
+ if (fego & ETR4_SLAY_WEAP)
+ {
+ /* Make a Weapon of Slaying */
+
+ if (randint(3) == 1) /* double damage */
+ o_ptr->dd *= 2;
+ else
+ {
+ do
+ {
+ o_ptr->dd++;
+ }
+ while (randint(o_ptr->dd) == 1);
+ do
+ {
+ o_ptr->ds++;
+ }
+ while (randint(o_ptr->ds) == 1);
+ }
+ if (randint(5) == 1)
+ {
+ o_ptr->art_flags1 |= TR1_BRAND_POIS;
+ }
+ if (o_ptr->tval == TV_SWORD && (randint(3) == 1))
+ {
+ o_ptr->art_flags1 |= TR1_VORPAL;
+ }
+ }
+
+ if (fego & ETR4_DAM_DIE)
+ {
+ /* Increase damage dice */
+ o_ptr->dd++;
+ }
+
+ if (fego & ETR4_DAM_SIZE)
+ {
+ /* Increase damage dice size */
+ o_ptr->ds++;
+ }
+
+ if (fego & ETR4_LIMIT_BLOWS)
+ {
+ /* Swap this flag */
+ *limit_blows = !(*limit_blows);
+ }
+
+ if (fego & ETR4_PVAL_M1)
+ {
+ /* Increase pval */
+ o_ptr->pval++;
+ }
+
+ if (fego & ETR4_PVAL_M2)
+ {
+ /* Increase pval */
+ o_ptr->pval += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_PVAL_M3)
+ {
+ /* Increase pval */
+ o_ptr->pval += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_PVAL_M5)
+ {
+ /* Increase pval */
+ o_ptr->pval += m_bonus(5, dun_level);
+ }
+ if (fego & ETR4_AC_M1)
+ {
+ /* Increase ac */
+ o_ptr->to_a++;
+ }
+
+ if (fego & ETR4_AC_M2)
+ {
+ /* Increase ac */
+ o_ptr->to_a += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_AC_M3)
+ {
+ /* Increase ac */
+ o_ptr->to_a += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_AC_M5)
+ {
+ /* Increase ac */
+ o_ptr->to_a += m_bonus(5, dun_level);
+ }
+
+ if (fego & ETR4_TH_M1)
+ {
+ /* Increase to hit */
+ o_ptr->to_h++;
+ }
+
+ if (fego & ETR4_TH_M2)
+ {
+ /* Increase to hit */
+ o_ptr->to_h += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_TH_M3)
+ {
+ /* Increase to hit */
+ o_ptr->to_h += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_TH_M5)
+ {
+ /* Increase to hit */
+ o_ptr->to_h += m_bonus(5, dun_level);
+ }
+
+ if (fego & ETR4_TD_M1)
+ {
+ /* Increase to dam */
+ o_ptr->to_d++;
+ }
+
+ if (fego & ETR4_TD_M2)
+ {
+ /* Increase to dam */
+ o_ptr->to_d += m_bonus(2, dun_level);
+ }
+
+ if (fego & ETR4_TD_M3)
+ {
+ /* Increase to dam */
+ o_ptr->to_d += m_bonus(3, dun_level);
+ }
+
+ if (fego & ETR4_TD_M5)
+ {
+ /* Increase to dam */
+ o_ptr->to_d += m_bonus(5, dun_level);
+ }
+
+ if (fego & ETR4_R_P_ABILITY)
+ {
+ /* Add a random pval-affected ability */
+ /* This might cause boots with + to blows */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr->art_flags1 |= TR1_STEALTH;
+ break;
+ case 2:
+ o_ptr->art_flags1 |= TR1_SEARCH;
+ break;
+ case 3:
+ o_ptr->art_flags1 |= TR1_INFRA;
+ break;
+ case 4:
+ o_ptr->art_flags1 |= TR1_TUNNEL;
+ break;
+ case 5:
+ o_ptr->art_flags1 |= TR1_SPEED;
+ break;
+ case 6:
+ o_ptr->art_flags1 |= TR1_BLOWS;
+ break;
+ }
+ }
+ if (fego & ETR4_R_STAT)
+ {
+ /* Add a random stat */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr->art_flags1 |= TR1_STR;
+ break;
+ case 2:
+ o_ptr->art_flags1 |= TR1_INT;
+ break;
+ case 3:
+ o_ptr->art_flags1 |= TR1_WIS;
+ break;
+ case 4:
+ o_ptr->art_flags1 |= TR1_DEX;
+ break;
+ case 5:
+ o_ptr->art_flags1 |= TR1_CON;
+ break;
+ case 6:
+ o_ptr->art_flags1 |= TR1_CHR;
+ break;
+ }
+ }
+
+ if (fego & ETR4_R_STAT_SUST)
+ {
+ /* Add a random stat and sustain it */
+ switch (randint(6))
+ {
+ case 1:
+ {
+ o_ptr->art_flags1 |= TR1_STR;
+ o_ptr->art_flags2 |= TR2_SUST_STR;
+ break;
+ }
+
+ case 2:
+ {
+ o_ptr->art_flags1 |= TR1_INT;
+ o_ptr->art_flags2 |= TR2_SUST_INT;
+ break;
+ }
+
+ case 3:
+ {
+ o_ptr->art_flags1 |= TR1_WIS;
+ o_ptr->art_flags2 |= TR2_SUST_WIS;
+ break;
+ }
+
+ case 4:
+ {
+ o_ptr->art_flags1 |= TR1_DEX;
+ o_ptr->art_flags2 |= TR2_SUST_DEX;
+ break;
+ }
+
+ case 5:
+ {
+ o_ptr->art_flags1 |= TR1_CON;
+ o_ptr->art_flags2 |= TR2_SUST_CON;
+ break;
+ }
+ case 6:
+ {
+ o_ptr->art_flags1 |= TR1_CHR;
+ o_ptr->art_flags2 |= TR2_SUST_CHR;
+ break;
+ }
+ }
+ }
+
+ if (fego & ETR4_R_IMMUNITY)
+ {
+ /* Give a random immunity */
+ switch (randint(4))
+ {
+ case 1:
+ {
+ o_ptr->art_flags2 |= TR2_IM_FIRE;
+ o_ptr->art_flags3 |= TR3_IGNORE_FIRE;
+ break;
+ }
+ case 2:
+ {
+ o_ptr->art_flags2 |= TR2_IM_ACID;
+ o_ptr->art_flags3 |= TR3_IGNORE_ACID;
+ break;
+ }
+ case 3:
+ {
+ o_ptr->art_flags2 |= TR2_IM_ELEC;
+ o_ptr->art_flags3 |= TR3_IGNORE_ELEC;
+ break;
+ }
+ case 4:
+ {
+ o_ptr->art_flags2 |= TR2_IM_COLD;
+ o_ptr->art_flags3 |= TR3_IGNORE_COLD;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Complete the "creation" of an object by applying "magic" to the item
+ *
+ * This includes not only rolling for random bonuses, but also putting the
+ * finishing touches on ego-items and artifacts, giving charges to wands and
+ * staffs, giving fuel to lites, and placing traps on chests.
+ *
+ * In particular, note that "Instant Artifacts", if "created" by an external
+ * routine, must pass through this function to complete the actual creation.
+ *
+ * The base "chance" of the item being "good" increases with the "level"
+ * parameter, which is usually derived from the dungeon level, being equal
+ * to the level plus 10, up to a maximum of 75. If "good" is true, then
+ * the object is guaranteed to be "good". If an object is "good", then
+ * the chance that the object will be "great" (ego-item or artifact), also
+ * increases with the "level", being equal to half the level, plus 5, up to
+ * a maximum of 20. If "great" is true, then the object is guaranteed to be
+ * "great". At dungeon level 65 and below, 15/100 objects are "great".
+ *
+ * If the object is not "good", there is a chance it will be "cursed", and
+ * if it is "cursed", there is a chance it will be "broken". These chances
+ * are related to the "good" / "great" chances above.
+ *
+ * Otherwise "normal" rings and amulets will be "good" half the time and
+ * "cursed" half the time, unless the ring/amulet is always good or cursed.
+ *
+ * If "okay" is true, and the object is going to be "great", then there is
+ * a chance that an artifact will be created. This is true even if both the
+ * "good" and "great" arguments are false. As a total hack, if "great" is
+ * true, then the item gets 3 extra "attempts" to become an artifact.
+ */
+void apply_magic(object_type *o_ptr, int lev, bool_ okay, bool_ good, bool_ great, boost::optional<int> force_power)
+{
+ 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 */
+ base_lvl = get_stick_base_level(o_ptr->tval, dun_level, o_ptr->pval2);
+ max_lvl = get_stick_max_level(o_ptr->tval, dun_level, o_ptr->pval2);
+ 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;
+ }
+
+ /* Override power with parameter? */
+ if (auto power_override = force_power)
+ {
+ power = *power_override;
+ }
+
+ /* Assume no rolls */
+ rolls = 0;
+
+ /* Get one roll if excellent */
+ if (power >= 2) rolls = 1;
+
+ /* Hack -- Get four rolls if forced great */
+ if (great) rolls = 4;
+
+ /* Hack -- Get no rolls if not allowed */
+ if (!okay || o_ptr->name1) rolls = 0;
+
+ /* Roll for artifacts if allowed */
+ for (i = 0; i < rolls; i++)
+ {
+ /* Roll for an artifact */
+ if (make_artifact(o_ptr)) break;
+ }
+
+ /* Mega hack -- to lazy to do it properly with hooks :) */
+ if ((o_ptr->name1 == ART_POWER) && (quest[QUEST_ONE].status < QUEST_STATUS_TAKEN))
+ {
+ o_ptr->name1 = 0;
+ o_ptr->name2 = 0;
+ o_ptr->name2b = 0;
+ object_prep(o_ptr, lookup_kind(TV_RING, SV_RING_INVIS));
+ }
+
+
+ /* Hack -- analyze artifacts */
+ if (o_ptr->name1)
+ {
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ /* Hack -- Mark the artifact as "created" */
+ a_ptr->cur_num = 1;
+
+ /* Extract the other fields */
+ o_ptr->pval = a_ptr->pval;
+ o_ptr->ac = a_ptr->ac;
+ o_ptr->dd = a_ptr->dd;
+ o_ptr->ds = a_ptr->ds;
+ o_ptr->to_a = a_ptr->to_a;
+ o_ptr->to_h = a_ptr->to_h;
+ o_ptr->to_d = a_ptr->to_d;
+ o_ptr->weight = a_ptr->weight;
+ o_ptr->number = 1;
+
+ /* Hack -- extract the "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) o_ptr->ident |= (IDENT_CURSED);
+
+ /* Mega-Hack -- increase the rating */
+ rating += 10;
+
+ /* Mega-Hack -- increase the rating again */
+ if (a_ptr->cost > 50000L) rating += 10;
+
+ /* Set the good item flag */
+ good_item_flag = TRUE;
+
+ /* Cheat -- peek at the item */
+ if ((cheat_peek) || (p_ptr->precognition)) object_mention(o_ptr);
+
+ /* Spell in it ? no ! */
+ if (a_ptr->flags5 & TR5_SPELL_CONTAIN)
+ o_ptr->pval2 = -1;
+
+ /* Give a basic exp/exp level to an artifact that needs it */
+ if (a_ptr->flags4 & TR4_LEVELS)
+ {
+ o_ptr->elevel = (k_ptr->level / 10) + 1;
+ o_ptr->exp = player_exp[o_ptr->elevel - 1];
+ }
+
+ /* Done */
+ return;
+ }
+
+
+ /* Apply magic */
+ switch (o_ptr->tval)
+ {
+ case TV_RANDART:
+ {
+ finalize_randart(o_ptr, lev);
+ break;
+ }
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_MSTAFF:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_TRAPKIT:
+ {
+ if (power) a_m_aux_1(o_ptr, lev, power);
+ break;
+ }
+
+ case TV_DAEMON_BOOK:
+ {
+ /* UGLY, but needed, depending of sval teh demon stuff are eitehr weapon or armor */
+ if (o_ptr->sval == SV_DEMONBLADE)
+ {
+ if (power) a_m_aux_1(o_ptr, lev, power);
+ }
+ else
+ {
+ if (power) a_m_aux_2(o_ptr, lev, power);
+ }
+ break;
+ }
+
+ case TV_DRAG_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_SOFT_ARMOR:
+ case TV_SHIELD:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_CLOAK:
+ case TV_GLOVES:
+ case TV_BOOTS:
+ {
+ a_m_aux_2(o_ptr, lev, power);
+ break;
+ }
+
+ case TV_RING:
+ case TV_AMULET:
+ {
+ if (!power && (rand_int(100) < 50)) power = -1;
+ a_m_aux_3(o_ptr, lev, power);
+ break;
+ }
+
+ default:
+ {
+ a_m_aux_4(o_ptr, lev, power);
+ break;
+ }
+ }
+
+ if (o_ptr->art_name) rating += 40;
+
+ /* Hack -- analyze ego-items */
+ else if (o_ptr->name2)
+ {
+ ego_item_type *e_ptr;
+ int j;
+ bool_ limit_blows = FALSE;
+ u32b f1, f2, f3, f4, f5, esp;
+ s16b e_idx;
+
+ e_idx = o_ptr->name2;
+
+ /* Ok now, THAT is truly ugly */
+try_an_other_ego:
+ e_ptr = &e_info[e_idx];
+
+ /* Hack -- extra powers */
+ for (j = 0; j < FLAG_RARITY_MAX; 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 const &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.
+ */
+static 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:
+ // FIXME: These cases can be shortened drastically
+ 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_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.
+ */
+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;
+
+ /* Assume legal */
+ return TRUE;
+}
+
+
+/*
+ * Hack -- determine if a template is "good"
+ */
+static 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 const &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)
+ {
+ /* Acquire object */
+ object_type *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];
+
+ /* Place the object */
+ c_ptr->o_idxs.push_back(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));
+
+ /* Multiply value by 5 if selling is disabled */
+ if (no_selling)
+ j_ptr->pval *= 5;
+
+ /* 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];
+
+ /* Place the object */
+ c_ptr->o_idxs.push_back(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;
+
+ 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 (auto const this_o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_o_idx];
+
+ /* Check for possible combination */
+ if (object_similar(o_ptr, j_ptr)) comb = TRUE;
+
+ /* Count objects */
+ k++;
+ }
+
+ /* Add new object */
+ if (!comb) k++;
+
+ /* 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 (auto const this_o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_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 */
+ s16b o_idx = 0;
+ 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;
+
+ /* Place the object */
+ c_ptr->o_idxs.push_back(o_idx);
+
+ /* Success */
+ done = TRUE;
+ }
+
+ /* Note the spot */
+ note_spot(by, bx);
+
+ /* Draw the spot */
+ lite_spot(by, bx);
+
+ /* Mega-Hack -- no message if "dropped" by player */
+ /* Message when an object falls under the player */
+ if (chance && (by == p_ptr->py) && (bx == p_ptr->px))
+ {
+ msg_print("You feel something roll beneath your feet.");
+ /* Sound */
+ sound(SOUND_DROP);
+ }
+
+ /* XXX XXX XXX */
+
+ /* Result */
+ return (o_idx);
+}
+
+
+
+
+/*
+ * Scatter some "great" objects near the player
+ */
+void acquirement(int y1, int x1, int num, bool_ great, bool_ known)
+{
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ /* Acquirement */
+ while (num--)
+ {
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Wipe the object */
+ object_wipe(i_ptr);
+
+ /* Make a good (or great) object (if possible) */
+ if (!make_object(i_ptr, TRUE, great, d_info[dungeon_type].objs)) continue;
+
+ if (known)
+ {
+ object_aware(i_ptr);
+ object_known(i_ptr);
+ }
+
+ /* Drop the object */
+ drop_near(i_ptr, -1, y1, x1);
+ }
+}
+
+
+
+/*
+ * Hack -- instantiate a trap
+ *
+ * XXX XXX XXX This routine should be redone to reflect trap "level".
+ * That is, it does not make sense to have spiked pits at 50 feet.
+ * Actually, it is not this routine, but the "trap instantiation"
+ * code, which should also check for "trap doors" on quest levels.
+ */
+void pick_trap(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Paranoia */
+ if ((c_ptr->t_idx == 0) || (c_ptr->info & CAVE_TRDT)) return;
+
+ /* Activate the trap */
+ c_ptr->info |= (CAVE_TRDT);
+
+ /* Notice and redraw */
+ note_spot(y, x);
+ lite_spot(y, x);
+}
+
+/*
+ * Describe the charges on an item in the inventory.
+ */
+void inven_item_charges(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Require staff/wand */
+ if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return;
+
+ /* Require known item */
+ if (!object_known_p(o_ptr)) return;
+
+ /* Multiple charges */
+ if (o_ptr->pval != 1)
+ {
+ /* Print a message */
+ msg_format("You have %d charges remaining.", o_ptr->pval);
+ }
+
+ /* Single charge */
+ else
+ {
+ /* Print a message */
+ msg_format("You have %d charge remaining.", o_ptr->pval);
+ }
+}
+
+
+/*
+ * Describe an item in the inventory.
+ */
+void inven_item_describe(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+ char o_name[80];
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Print a message */
+ msg_format("You have %s.", o_name);
+}
+
+
+/*
+ * Increase the "number" of an item in the inventory
+ */
+void inven_item_increase(int item, int num)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Apply */
+ num += o_ptr->number;
+
+ /* Bounds check */
+ if (num > 255) num = 255;
+ else if (num < 0) num = 0;
+
+ /* Un-apply */
+ num -= o_ptr->number;
+
+ /* Change the number and weight */
+ if (num)
+ {
+ /* Add the number */
+ o_ptr->number += num;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana XXX */
+ p_ptr->update |= (PU_MANA);
+
+ /* Combine the pack */
+ p_ptr->notice |= (PN_COMBINE);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP);
+ }
+}
+
+
+/*
+ * Erase an inventory slot if it has no more items
+ */
+bool_ inven_item_optimize(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Only optimize real items */
+ if (!o_ptr->k_idx) return (FALSE);
+
+ /* Only optimize empty items */
+ if (o_ptr->number) return (FALSE);
+
+ /* The item is in the pack */
+ if (item < INVEN_WIELD)
+ {
+ int i;
+
+ /* One less item */
+ inven_cnt--;
+
+ /* Slide everything down */
+ for (i = item; i < INVEN_PACK; i++)
+ {
+ /* Structure copy */
+ p_ptr->inventory[i] = p_ptr->inventory[i + 1];
+ }
+
+ /* Erase the "final" slot */
+ object_wipe(&p_ptr->inventory[i]);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /* The item is being wielded */
+ else
+ {
+ /* One less item */
+ equip_cnt--;
+
+ /* Take care of item sets*/
+ if (o_ptr->name1)
+ {
+ takeoff_set(p_ptr->inventory[item].name1, a_info[p_ptr->inventory[item].name1].set);
+ }
+
+ /* Erase the empty slot */
+ object_wipe(&p_ptr->inventory[item]);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Recalculate mana XXX */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP);
+ }
+
+ return (TRUE);
+}
+
+
+/*
+ * Describe the charges on an item on the floor.
+ */
+void floor_item_charges(int item)
+{
+ object_type *o_ptr = &o_list[item];
+
+ /* Require staff/wand */
+ if ((o_ptr->tval != TV_STAFF) && (o_ptr->tval != TV_WAND)) return;
+
+ /* Require known item */
+ if (!object_known_p(o_ptr)) return;
+
+ /* Multiple charges */
+ if (o_ptr->pval != 1)
+ {
+ /* Print a message */
+ msg_format("There are %d charges remaining.", o_ptr->pval);
+ }
+
+ /* Single charge */
+ else
+ {
+ /* Print a message */
+ msg_format("There is %d charge remaining.", o_ptr->pval);
+ }
+}
+
+
+
+/*
+ * Describe an item in the inventory.
+ */
+void floor_item_describe(int item)
+{
+ object_type *o_ptr = &o_list[item];
+ char o_name[80];
+
+ /* Get a description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Print a message */
+ msg_format("You see %s.", o_name);
+}
+
+
+/*
+ * Increase the "number" of an item on the floor
+ */
+void floor_item_increase(int item, int num)
+{
+ object_type *o_ptr = &o_list[item];
+
+ /* Apply */
+ num += o_ptr->number;
+
+ /* Bounds check */
+ if (num > 255) num = 255;
+ else if (num < 0) num = 0;
+
+ /* Un-apply */
+ num -= o_ptr->number;
+
+ /* Change the number */
+ o_ptr->number += num;
+}
+
+
+/*
+ * Optimize an item on the floor (destroy "empty" items)
+ */
+void floor_item_optimize(int item)
+{
+ object_type *o_ptr = &o_list[item];
+
+ /* Paranoia -- be sure it exists */
+ if (!o_ptr->k_idx) return;
+
+ /* Only optimize empty items */
+ if (o_ptr->number) return;
+
+ /* Delete the object */
+ delete_object_idx(item);
+}
+
+
+/*
+ * Increase stack size for item, describe and optimize.
+ */
+void inc_stack_size(int item, int delta) {
+ inc_stack_size_ex(item, delta, OPTIMIZE, DESCRIBE);
+}
+
+/*
+ * Increase stack size for item.
+ */
+void inc_stack_size_ex(int item, int delta, optimize_flag opt, describe_flag desc) {
+ /* Pack item? */
+ if (item >= 0)
+ {
+ inven_item_increase(item, delta);
+ if (desc == DESCRIBE)
+ {
+ inven_item_describe(item);
+ }
+ if (opt == OPTIMIZE)
+ {
+ inven_item_optimize(item);
+ }
+ }
+
+ /* Floor item? */
+ else
+ {
+ floor_item_increase(0 - item, delta);
+ if (desc == DESCRIBE)
+ {
+ floor_item_describe(0 - item);
+ }
+ if (opt == OPTIMIZE)
+ {
+ floor_item_optimize(0 - item);
+ }
+ }
+}
+
+
+
+/*
+ * Check if we have space for an item in the pack without overflow
+ */
+bool_ inven_carry_okay(object_type const *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->held_m_idx = 0;
+
+ /* Count the items */
+ inven_cnt++;
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine and Reorder pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Return the slot */
+ return (i);
+}
+
+
+
+/*
+ * Take off (some of) a non-cursed equipment item
+ *
+ * Note that only one item at a time can be wielded per slot.
+ *
+ * Note that taking off an item when "full" may cause that item
+ * to fall to the ground.
+ *
+ * Return the inventory slot into which the item is placed.
+ */
+s16b inven_takeoff(int item, int amt, bool_ force_drop)
+{
+ int slot;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ cptr act;
+
+ char o_name[80];
+
+
+ /* Get the item to take off */
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Paranoia */
+ if (amt <= 0) return ( -1);
+
+ /* Verify */
+ if (amt > o_ptr->number) amt = o_ptr->number;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain a local object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /* Describe the object */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Took off weapon */
+ if (item == INVEN_WIELD)
+ {
+ act = "You were wielding";
+ }
+
+ /* Took off bow */
+ else if (item == INVEN_BOW)
+ {
+ act = "You were holding";
+ }
+
+ /* Took off light */
+ else if (item == INVEN_LITE)
+ {
+ act = "You were holding";
+ }
+
+ /* Took off ammo */
+ else if (item == INVEN_AMMO)
+ {
+ act = "You were carrying in your quiver";
+ }
+
+ /* Took off tool */
+ else if (item == INVEN_TOOL)
+ {
+ act = "You were using";
+ }
+
+ /* Took off something */
+ else
+ {
+ act = "You were wearing";
+ }
+
+ /* Modify, Optimize */
+ inc_stack_size_ex(item, -amt, OPTIMIZE, NO_DESCRIBE);
+
+ if ((item == INVEN_CARRY) && (get_skill(SKILL_SYMBIOTIC)))
+ {
+ /* Drop the monster */
+ o_ptr->pval2 = 0;
+ msg_print("You carefully drop the poor monster on the floor.");
+ drop_near(q_ptr, 0, p_ptr->py, p_ptr->px);
+ slot = -1;
+ }
+ else if (force_drop)
+ {
+ drop_near(q_ptr, 0, p_ptr->py, p_ptr->px);
+ slot = -1;
+ }
+ else
+ {
+ /* Carry the object */
+ slot = inven_carry(q_ptr, FALSE);
+ }
+
+ /* Message */
+ msg_format("%s %s (%c).", act, o_name, index_to_label(slot));
+
+ /* Return slot */
+ return (slot);
+}
+
+
+
+
+/*
+ * Drop (some of) a non-cursed inventory/equipment item
+ *
+ * The object will be dropped "near" the current location
+ */
+void inven_drop(int item, int amt, int dy, int dx, bool_ silent)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Access original object */
+ o_ptr = &p_ptr->inventory[item];
+
+ /* Error check */
+ if (amt <= 0) return;
+
+ /* Not too many */
+ if (amt > o_ptr->number) amt = o_ptr->number;
+
+
+ /* Take off equipment */
+ if (item >= INVEN_WIELD)
+ {
+ /* Take off first */
+ item = inven_takeoff(item, amt, FALSE);
+
+ /* Access original object */
+ o_ptr = &p_ptr->inventory[item];
+ }
+
+ if (item > -1)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Obtain local object */
+ object_copy(q_ptr, o_ptr);
+
+ /*
+ * Hack -- If rods or wands are dropped, the total maximum timeout or
+ * charges need to be allocated between the two stacks. If all the items
+ * are being dropped, it makes for a neater message to leave the original
+ * stack's pval alone. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ if (amt < o_ptr->number) o_ptr->pval -= q_ptr->pval;
+ }
+ }
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /* Describe local object */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Message */
+ if (!silent) msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Drop it near the player */
+ drop_near(q_ptr, 0, dy, dx);
+
+ /* Modify, Describe, Optimize */
+ inc_stack_size(item, -amt);
+ }
+}
+
+
+
+/*
+ * Combine items in the pack
+ *
+ * Note special handling of the "overflow" slot
+ */
+void combine_pack(void)
+{
+ int i, j, k;
+ object_type *o_ptr;
+ object_type *j_ptr;
+ bool_ flag = FALSE;
+
+
+ /* Combine the pack (backwards) */
+ for (i = INVEN_PACK; i > 0; i--)
+ {
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty items */
+ if (!o_ptr->k_idx) continue;
+
+ /* Scan the items above that item */
+ for (j = 0; j < i; j++)
+ {
+ /* Get the item */
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Skip empty items */
+ if (!j_ptr->k_idx) continue;
+
+ /* Can we drop "o_ptr" onto "j_ptr"? */
+ if (object_similar(j_ptr, o_ptr))
+ {
+ /* Take note */
+ flag = TRUE;
+
+ /* Add together the item counts */
+ object_absorb(j_ptr, o_ptr);
+
+ /* One object is gone */
+ inven_cnt--;
+
+ /* Slide everything down */
+ for (k = i; k < INVEN_PACK; k++)
+ {
+ /* Structure copy */
+ p_ptr->inventory[k] = p_ptr->inventory[k + 1];
+ }
+
+ /* Erase the "final" slot */
+ object_wipe(&p_ptr->inventory[k]);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Done */
+ break;
+ }
+ }
+ }
+
+ /* Message */
+ if (flag) msg_print("You combine some items in your pack.");
+}
+
+
+/*
+ * Reorder items in the pack
+ *
+ * Note special handling of the "overflow" slot
+ */
+void reorder_pack(void)
+{
+ int i, j, k;
+ s32b o_value;
+ s32b j_value;
+ object_type forge;
+ object_type *q_ptr;
+ object_type *j_ptr;
+ object_type *o_ptr;
+ bool_ flag = FALSE;
+
+
+ /* Re-order the pack (forwards) */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ /* Mega-Hack -- allow "proper" over-flow */
+ if ((i == INVEN_PACK) && (inven_cnt == INVEN_PACK)) break;
+
+ /* Get the item */
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip empty slots */
+ if (!o_ptr->k_idx) continue;
+
+ /* Get the "value" of the item */
+ o_value = object_value(o_ptr);
+
+ /* Scan every occupied slot */
+ for (j = 0; j < INVEN_PACK; j++)
+ {
+ /* Get the item already there */
+ j_ptr = &p_ptr->inventory[j];
+
+ /* Use empty slots */
+ if (!j_ptr->k_idx) break;
+
+ /* Objects sort by decreasing type */
+ if (o_ptr->tval > j_ptr->tval) break;
+ if (o_ptr->tval < j_ptr->tval) continue;
+
+ /* Non-aware (flavored) items always come last */
+ if (!object_aware_p(o_ptr)) continue;
+ if (!object_aware_p(j_ptr)) break;
+
+ /* Objects sort by increasing sval */
+ if (o_ptr->sval < j_ptr->sval) break;
+ if (o_ptr->sval > j_ptr->sval) continue;
+
+ /* Unidentified objects always come last */
+ if (!object_known_p(o_ptr)) continue;
+ if (!object_known_p(j_ptr)) break;
+
+
+ /* Hack: otherwise identical rods sort by
+ increasing recharge time --dsb */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (o_ptr->timeout > j_ptr->timeout) break;
+ if (o_ptr->timeout < j_ptr->timeout) continue;
+ }
+
+ /* Determine the "value" of the pack item */
+ j_value = object_value(j_ptr);
+
+
+
+ /* Objects sort by decreasing value */
+ if (o_value > j_value) break;
+ if (o_value < j_value) continue;
+ }
+
+ /* Never move down */
+ if (j >= i) continue;
+
+ /* Take note */
+ flag = TRUE;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Save a copy of the moving item */
+ object_copy(q_ptr, &p_ptr->inventory[i]);
+
+ /* Slide the objects */
+ for (k = i; k > j; k--)
+ {
+ /* Slide the item */
+ object_copy(&p_ptr->inventory[k], &p_ptr->inventory[k - 1]);
+ }
+
+ /* Insert the moving item */
+ object_copy(&p_ptr->inventory[j], q_ptr);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+ }
+
+ /* Message */
+ if (flag) msg_print("You reorder some items in your pack.");
+}
+
+
+/*
+ * Let the floor carry an object
+ */
+s16b floor_carry(int y, int x, object_type *j_ptr)
+{
+ /* Scan objects in that grid for combination */
+ for (auto const this_o_idx: cave[y][x].o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_o_idx];
+
+ /* Check for combination */
+ if (object_similar(o_ptr, j_ptr))
+ {
+ /* Combine the items */
+ object_absorb(o_ptr, j_ptr);
+
+ /* Done */
+ return this_o_idx;
+ }
+ }
+
+ /* The stack is already too large */
+ if (cave[y][x].o_idxs.size() > 23)
+ {
+ return (0);
+ }
+
+ /* Make an object */
+ s16b o_idx = o_pop();
+
+ /* Success */
+ if (o_idx)
+ {
+ /* Acquire object */
+ object_type *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;
+
+ /* Place the object */
+ cave[y][x].o_idxs.push_back(o_idx);
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+ }
+
+ /* Result */
+ return (o_idx);
+}
+
+/*
+ * Notice a decaying object in the pack
+ */
+void pack_decay(int item)
+{
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ int amt = o_ptr->number;
+
+ s16b m_type;
+ s32b wt;
+
+ byte known = o_ptr->name1;
+
+ byte gone = 1;
+
+ char desc[80];
+
+ /* Player notices each decaying object */
+ object_desc(desc, o_ptr, TRUE, 3);
+ msg_format("You feel %s decompose.", desc);
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Obtain local object */
+ object_copy(i_ptr, o_ptr);
+
+ /* Remember what creature we were */
+ m_type = o_ptr->pval2;
+
+ /* and how much we weighed */
+ wt = r_ptr->weight;
+
+ /* Get rid of decayed object */
+ inc_stack_size_ex(item, -amt, OPTIMIZE, NO_DESCRIBE);
+
+ if (i_ptr->tval == TV_CORPSE)
+ {
+ /* Monster must have a skull for its head to become one */
+ if (i_ptr->sval == SV_CORPSE_HEAD)
+ {
+ /* Replace the head with a skull */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKULL));
+ i_ptr->weight = wt / 60 + rand_int(wt) / 600;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Monster must have a skeleton for its corpse to become one */
+ if ((i_ptr->sval == SV_CORPSE_CORPSE) && (r_ptr->flags3 & RF9_DROP_SKELETON))
+ {
+ /* Replace the corpse with a skeleton */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKELETON));
+ i_ptr->weight = wt / 4 + rand_int(wt) / 40;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Don't restore if the item is gone */
+ if (!gone)
+ {
+ i_ptr->number = amt;
+ i_ptr->pval2 = m_type;
+
+ /* Should become "The skull of Farmer Maggot", not "A skull" */
+ if (known)
+ {
+ object_aware(i_ptr);
+
+ /* Named skeletons are artifacts */
+ i_ptr->name1 = 201;
+ }
+ inven_carry(i_ptr, TRUE);
+ }
+ }
+}
+
+/*
+ * Decay an object on the floor
+ */
+void floor_decay(int item)
+{
+ object_type *o_ptr = &o_list[item];
+
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+
+ object_type *i_ptr;
+ object_type object_type_body;
+
+ int amt = o_ptr->number;
+
+ s16b m_type;
+ s32b wt;
+
+ byte known = o_ptr->name1;
+
+ /* Assume we disappear */
+ byte gone = 1;
+
+ byte x = o_ptr->ix;
+ byte y = o_ptr->iy;
+
+ /* Maybe the player sees it */
+ bool_ visible = player_can_see_bold(o_ptr->iy, o_ptr->ix);
+ char desc[80];
+
+ if (visible)
+ {
+ /* Player notices each decaying object */
+ object_desc(desc, o_ptr, TRUE, 3);
+ msg_format("You see %s decompose.", desc);
+ }
+
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Obtain local object */
+ object_copy(i_ptr, o_ptr);
+
+ /* Remember what creature we were */
+ m_type = o_ptr->pval2;
+
+ /* and how much we weighed */
+ wt = r_ptr->weight;
+
+ floor_item_increase(item, -amt);
+ floor_item_optimize(item);
+
+ if (i_ptr->tval == TV_CORPSE)
+ {
+ /* Monster must have a skull for its head to become one */
+ if (i_ptr->sval == SV_CORPSE_HEAD)
+ {
+ /* Replace the head with a skull */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKULL));
+ i_ptr->weight = wt / 60 + rand_int(wt) / 600;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Monster must have a skeleton for its corpse to become one */
+ if ((i_ptr->sval == SV_CORPSE_CORPSE) && (r_ptr->flags3 & RF9_DROP_SKELETON))
+ {
+ /* Replace the corpse with a skeleton */
+ object_prep(i_ptr, lookup_kind(TV_CORPSE, SV_CORPSE_SKELETON));
+ i_ptr->weight = wt / 4 + rand_int(wt) / 40;
+
+ /* Stay here */
+ gone = 0;
+ }
+
+ /* Don't restore if the item is gone */
+ if (!gone)
+ {
+ i_ptr->number = amt;
+ i_ptr->pval2 = m_type;
+
+ /* Should become "The skull of Farmer Maggot", not "A skull" */
+ if (known)
+ {
+ object_aware(i_ptr);
+
+ /* Named skeletons are artifacts */
+ i_ptr->name1 = 201;
+ }
+ floor_carry(y, x, i_ptr);
+ }
+ }
+}
+
+/* Return the item be it on the floor or in inven */
+object_type *get_object(int item)
+{
+ if (item >= 0)
+ {
+ assert(item < INVEN_TOTAL);
+ return &p_ptr->inventory[item];
+ }
+ else
+ {
+ return &o_list[0 - item];
+ }
+}
+
diff --git a/src/object2.hpp b/src/object2.hpp
new file mode 100644
index 00000000..26d07b25
--- /dev/null
+++ b/src/object2.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type_fwd.hpp"
+#include "obj_theme_fwd.hpp"
+
+#include <boost/optional.hpp>
+
+typedef enum { OPTIMIZE, NO_OPTIMIZE } optimize_flag;
+typedef enum { DESCRIBE, NO_DESCRIBE } describe_flag;
+
+extern void inc_stack_size(int item, int delta);
+extern void inc_stack_size_ex(int item, int delta, optimize_flag opt, describe_flag desc);
+extern 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 void init_match_theme(obj_theme const &theme);
+extern bool_ kind_is_artifactable(int k_idx);
+extern bool_ kind_is_legal(int k_idx);
+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 const *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 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 bool object_known_p(object_type const *o_ptr);
+extern void object_aware(object_type *o_ptr);
+extern bool object_aware_p(object_type const *o_ptr);
+extern void object_tried(object_type *o_ptr);
+extern bool object_tried_p(object_type const *o_ptr);
+extern s32b object_value(object_type const *o_ptr);
+extern s32b object_value_real(object_type const *o_ptr);
+extern bool_ object_similar(object_type const *o_ptr, object_type const *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 void apply_magic(object_type *o_ptr, int lev, bool_ okay, bool_ good, bool_ great, boost::optional<int> force_power = boost::none);
+extern bool_ make_object(object_type *j_ptr, bool_ good, bool_ great, obj_theme const &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 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 void combine_pack(void);
+extern void reorder_pack(void);
+extern void random_artifact_resistance (object_type * o_ptr);
+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 s16b m_bonus(int max, int level);
+extern s32b flag_cost(object_type const *o_ptr, int plusses);
diff --git a/src/object_filter.cc b/src/object_filter.cc
new file mode 100644
index 00000000..39961146
--- /dev/null
+++ b/src/object_filter.cc
@@ -0,0 +1,98 @@
+#include "object_filter.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+
+namespace object_filter {
+
+object_filter_t TVal(byte tval) {
+ return [=](object_type const *o_ptr) -> bool {
+ return o_ptr->tval == tval;
+ };
+}
+
+object_filter_t SVal(byte sval) {
+ return [=](object_type const *o_ptr) -> bool {
+ return o_ptr->sval == sval;
+ };
+}
+
+object_filter_t HasFlag3(u32b mask) {
+ return [=](object_type const *o_ptr) -> bool {
+ // Extract the flags
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ // Does the item have the flag?
+ return (f3 & mask);
+ };
+}
+
+object_filter_t HasFlag4(u32b mask) {
+ return [=](object_type const *o_ptr) -> bool {
+ // Extract the flags
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ // Does the item have the flag?
+ return (f4 & mask);
+ };
+}
+
+object_filter_t HasFlag5(u32b mask) {
+ return [=](object_type const *o_ptr) -> bool {
+ // Extract the flags
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ // Does the item have the flag?
+ return (f5 & mask);
+ };
+}
+
+object_filter_t IsArtifact() {
+ return [](object_type const *o_ptr) -> bool {
+ return o_ptr->name1 > 0;
+ };
+}
+
+object_filter_t IsArtifactP() {
+ return [](object_type const *o_ptr) -> bool {
+ return artifact_p(o_ptr);
+ };
+}
+
+object_filter_t IsEgo() {
+ return [](object_type const *o_ptr) -> bool {
+ return ego_item_p(o_ptr);
+ };
+}
+
+object_filter_t IsKnown() {
+ return [](object_type const *o_ptr) -> bool {
+ return object_known_p(o_ptr);
+ };
+}
+
+object_filter_t True() {
+ return [](object_type const *o_ptr) -> bool {
+ return true;
+ };
+}
+
+object_filter_t Not(object_filter_t p) {
+ return [=](object_type const *o_ptr) -> bool {
+ return !p(o_ptr);
+ };
+}
+
+object_filter_t And() {
+ return [](object_type const *) -> bool {
+ return true;
+ };
+}
+
+object_filter_t Or() {
+ return [](object_type const *) -> bool {
+ return false;
+ };
+}
+
+}
diff --git a/src/object_filter.hpp b/src/object_filter.hpp
new file mode 100644
index 00000000..9a22090b
--- /dev/null
+++ b/src/object_filter.hpp
@@ -0,0 +1,99 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type_fwd.hpp"
+
+#include <functional>
+#include <initializer_list>
+
+typedef std::function<bool (object_type const *)> object_filter_t;
+
+namespace object_filter {
+
+/**
+ * Is TVal equal to the given value?
+ */
+object_filter_t TVal(byte tval);
+
+/**
+ * Is SVal equal to the given value?
+ */
+object_filter_t SVal(byte sval);
+
+/**
+ * Has given bit mask in flag3 value.
+ */
+object_filter_t HasFlag3(u32b mask);
+
+/**
+ * Has given bit mask in flag4 value.
+ */
+object_filter_t HasFlag4(u32b mask);
+
+/**
+ * Has given bit mask in flag5 value.
+ */
+object_filter_t HasFlag5(u32b mask);
+
+/**
+ * Is the object an artifact?
+ */
+object_filter_t IsArtifact();
+
+/**
+ * Is the object an artifact as determined by artifact_p?
+ */
+object_filter_t IsArtifactP();
+
+/**
+ * Is the object an ego item?
+ */
+object_filter_t IsEgo();
+
+/**
+ * Is the object "known"?
+ */
+object_filter_t IsKnown();
+
+/**
+ * True always accepts all items.
+ */
+object_filter_t True();
+
+/**
+ * Invert an object filter.
+ */
+object_filter_t Not(object_filter_t p);
+
+/**
+ * Logical conjunction (AND)
+ */
+object_filter_t And();
+
+/**
+ * Logical conjunction (AND)
+ */
+template<typename Arg0, typename... Args> object_filter_t And(Arg0&& arg0, Args&&... args) {
+ auto argsFilter = And(args...);
+ return [=](object_type const *o_ptr) -> bool {
+ return arg0(o_ptr) && argsFilter(o_ptr);
+ };
+}
+
+/**
+ * Logical disjunction (OR)
+ */
+object_filter_t Or();
+
+/**
+ * Logical disjunction (OR)
+ */
+template<typename Arg0, typename... Args> object_filter_t Or(Arg0&& arg0, Args&&... args) {
+ auto argsFilter = Or(args...);
+ return [=](object_type const *o_ptr) -> bool {
+ auto x = arg0(o_ptr) || argsFilter(o_ptr);
+ return x;
+ };
+}
+
+}
diff --git a/src/object_kind.hpp b/src/object_kind.hpp
new file mode 100644
index 00000000..505f54d9
--- /dev/null
+++ b/src/object_kind.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Size of allocation table for objects
+ */
+constexpr int ALLOCATION_MAX = 8;
+
+/**
+ * Object "kind" descriptor. Includes player knowledge.
+ *
+ * Only "aware" and "tried" are saved in the savefile
+ */
+struct object_kind
+{
+ const char *name; /* Name */
+ char *text; /* Text */
+
+ 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[ALLOCATION_MAX]; /* Allocation level(s) */
+ byte chance[ALLOCATION_MAX]; /* Allocation chance(s) */
+
+ byte level; /* Level */
+
+
+ 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 */
+
+ 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) */
+};
diff --git a/src/object_kind_fwd.hpp b/src/object_kind_fwd.hpp
new file mode 100644
index 00000000..6d26db9f
--- /dev/null
+++ b/src/object_kind_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct object_kind;
diff --git a/src/object_type.hpp b/src/object_type.hpp
new file mode 100644
index 00000000..d7f003e6
--- /dev/null
+++ b/src/object_type.hpp
@@ -0,0 +1,104 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * 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".
+ */
+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 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 */
+};
diff --git a/src/object_type_fwd.hpp b/src/object_type_fwd.hpp
new file mode 100644
index 00000000..d99e60a6
--- /dev/null
+++ b/src/object_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct object_type;
diff --git a/src/option_type.hpp b/src/option_type.hpp
new file mode 100644
index 00000000..58834b79
--- /dev/null
+++ b/src/option_type.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Option descriptor.
+ */
+struct option_type
+{
+ /**
+ * Address of actual option variable. NULL signals the
+ * end of the table.
+ */
+ bool_ *o_var;
+
+ /**
+ * Default value.
+ */
+ byte o_norm;
+
+ /**
+ * Option page number.
+ */
+ byte o_page;
+
+ /**
+ * Savefile bit in the page-specific list of options.
+ */
+ byte o_bit;
+
+ /**
+ * Textual name.
+ */
+ cptr o_text;
+
+ /**
+ * Textual description
+ */
+ cptr o_desc;
+};
diff --git a/src/options.cc b/src/options.cc
new file mode 100644
index 00000000..5501ab52
--- /dev/null
+++ b/src/options.cc
@@ -0,0 +1,89 @@
+#include "options.hpp"
+
+//
+// Option Set 1 -- User Interface
+//
+bool_ rogue_like_commands; /* Rogue-like commands */
+bool_ quick_messages; /* Activate quick messages */
+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_ always_repeat; /* Repeat obvious commands */
+bool_ ring_bell; /* Ring the bell (on errors, etc) */
+
+//
+// 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_ small_levels; /* Allow unusually small dungeon levels */
+bool_ empty_levels; /* Allow empty 'arena' levels */
+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_scum; /* Auto-scum for good levels */
+bool_ view_perma_grids; /* Map remembers all perma-lit grids */
+bool_ view_torch_grids; /* Map remembers all torch-lit grids */
+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_ smart_learn; /* Monsters learn from their mistakes */
+
+//
+// Option Set 4 -- Efficiency
+//
+bool_ view_reduce_lite; /* Reduce lite-radius when running */
+bool_ avoid_abort; /* Avoid checking for user abort */
+bool_ avoid_shimmer; /* Avoid processing extra shimmering */
+bool_ avoid_other; /* Avoid processing special colors */
+bool_ flush_failure; /* Flush input on any failure */
+bool_ flush_disturb; /* Flush input on disturbance */
+bool_ flush_command; /* Flush input before every command */
+bool_ fresh_before; /* Flush output before normal commands */
+bool_ fresh_after; /* Flush output after normal commands */
+bool_ fresh_message; /* Flush output after all messages */
+bool_ hilite_player; /* Hilite the player with the cursor */
+bool_ view_yellow_lite; /* Use special colors for torch-lit grids */
+bool_ view_bright_lite; /* Use special colors for 'viewable' grids */
+bool_ view_granite_lite; /* Use special colors for wall grids (slow) */
+bool_ view_special_lite; /* Use special colors for floor grids (slow) */
+bool_ center_player; /* Center view on player */
+
+//
+// Option Set 5 - ToME options
+//
+bool_ linear_stats;
+bool_ player_char_health; /* Display the player as a special symbol when in bad health ? */
+bool_ option_ingame_help; /* Ingame contextual help */
+bool_ auto_more; /* Auto more */
+bool_ inventory_no_move; /* In inventory option window, just erase the letters,
+ * rather that displaying the list without the invalid
+ * selections */
+
+//
+// Option Set 6 - Birth options
+//
+bool_ always_small_level;
+bool_ autoroll;
+bool_ fate_option;
+bool_ ironman_rooms;
+bool_ joke_monsters;
+bool_ point_based;
+bool_ preserve;
+bool_ no_selling;
diff --git a/src/options.hpp b/src/options.hpp
new file mode 100644
index 00000000..45e19cf7
--- /dev/null
+++ b/src/options.hpp
@@ -0,0 +1,89 @@
+#pragma once
+
+#include "h-basic.h"
+
+//
+// Option Set 1 -- User Interface.
+//
+extern bool_ rogue_like_commands;
+extern bool_ quick_messages;
+extern bool_ carry_query_flag;
+extern bool_ use_old_target;
+extern bool_ always_pickup;
+extern bool_ always_repeat;
+extern bool_ ring_bell;
+
+//
+// Option Set 2 -- Disturbance
+//
+extern bool_ find_ignore_stairs;
+extern bool_ find_ignore_doors;
+extern bool_ find_cut;
+extern bool_ find_examine;
+extern bool_ disturb_move;
+extern bool_ disturb_near;
+extern bool_ disturb_panel;
+extern bool_ disturb_detect;
+extern bool_ disturb_state;
+extern bool_ disturb_minor;
+extern bool_ disturb_other;
+extern bool_ alert_hitpoint;
+extern bool_ alert_failure;
+extern bool_ last_words;
+extern bool_ small_levels;
+extern bool_ empty_levels;
+extern bool_ confirm_stairs;
+extern bool_ wear_confirm;
+extern bool_ disturb_pets;
+
+//
+// Option Set 3 -- Game-Play
+//
+extern bool_ auto_scum;
+extern bool_ view_perma_grids;
+extern bool_ view_torch_grids;
+extern bool_ dungeon_align;
+extern bool_ dungeon_stair;
+extern bool_ flow_by_sound;
+extern bool_ smart_learn;
+
+//
+// Option Set 4 -- Efficiency
+//
+extern bool_ view_reduce_lite;
+extern bool_ avoid_abort;
+extern bool_ avoid_shimmer;
+extern bool_ avoid_other;
+extern bool_ flush_failure;
+extern bool_ flush_disturb;
+extern bool_ flush_command;
+extern bool_ fresh_before;
+extern bool_ fresh_after;
+extern bool_ fresh_message;
+extern bool_ hilite_player;
+extern bool_ view_yellow_lite;
+extern bool_ view_bright_lite;
+extern bool_ view_granite_lite;
+extern bool_ view_special_lite;
+extern bool_ center_player;
+
+//
+// Option Set 5 - ToME options
+//
+extern bool_ linear_stats;
+extern bool_ player_char_health;
+extern bool_ option_ingame_help;
+extern bool_ auto_more;
+extern bool_ inventory_no_move;
+
+//
+// Option Set 6 - Birth options
+//
+extern bool_ always_small_level;
+extern bool_ autoroll;
+extern bool_ fate_option;
+extern bool_ ironman_rooms;
+extern bool_ joke_monsters;
+extern bool_ point_based;
+extern bool_ preserve;
+extern bool_ no_selling;
diff --git a/src/owner_type.hpp b/src/owner_type.hpp
new file mode 100644
index 00000000..703d3159
--- /dev/null
+++ b/src/owner_type.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "h-basic.h"
+
+/*
+ * Store owner descriptor.
+ */
+struct owner_type
+{
+ /**
+ * Name
+ */
+ const char *name;
+
+ /**
+ * Purse limit
+ */
+ s16b max_cost;
+
+ /**
+ * Inflation
+ */
+ s16b inflation;
+
+ /**
+ * Liked/hated races.
+ */
+ u32b races[2][2];
+
+ /**
+ * Liked/hated classes
+ */
+ u32b classes[2][2];
+
+ /**
+ * Costs for liked people
+ */
+ s16b costs[3];
+};
diff --git a/src/owner_type_fwd.hpp b/src/owner_type_fwd.hpp
new file mode 100644
index 00000000..20c25802
--- /dev/null
+++ b/src/owner_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct owner_type;
diff --git a/src/player_class.hpp b/src/player_class.hpp
new file mode 100644
index 00000000..d67d1d73
--- /dev/null
+++ b/src/player_class.hpp
@@ -0,0 +1,105 @@
+#pragma once
+
+#include "body.hpp"
+#include "h-basic.h"
+#include "player_defs.hpp"
+#include "player_spec.hpp"
+
+/**
+ * Maximum number of specialties.
+ */
+constexpr int MAX_SPEC = 20;
+
+/**
+ * Player descriptor and runtime data.
+ */
+struct player_class
+{
+ const char *title; /* Type of class */
+ char *desc; /* Small desc of the class */
+ const char *titles[PY_MAX_LEVEL / 5];
+ /* Titles */
+
+ 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) */
+};
+
diff --git a/src/player_class_fwd.hpp b/src/player_class_fwd.hpp
new file mode 100644
index 00000000..2402934d
--- /dev/null
+++ b/src/player_class_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct player_class;
diff --git a/src/player_defs.hpp b/src/player_defs.hpp
new file mode 100644
index 00000000..2f57409c
--- /dev/null
+++ b/src/player_defs.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+/**
+ * Maximum player level
+ */
+#define PY_MAX_LEVEL 50
diff --git a/src/player_race.hpp b/src/player_race.hpp
new file mode 100644
index 00000000..edb304f2
--- /dev/null
+++ b/src/player_race.hpp
@@ -0,0 +1,83 @@
+#pragma once
+
+#include "h-basic.h"
+#include "body.hpp"
+#include "player_defs.hpp"
+#include "skills_defs.hpp"
+
+/**
+ * Player racial descriptior.
+ */
+struct player_race
+{
+ const char *title; /* Type of race */
+ char *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) */
+};
diff --git a/src/player_race_fwd.hpp b/src/player_race_fwd.hpp
new file mode 100644
index 00000000..c3c3350b
--- /dev/null
+++ b/src/player_race_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct player_race;
diff --git a/src/player_race_mod.hpp b/src/player_race_mod.hpp
new file mode 100644
index 00000000..72f975ce
--- /dev/null
+++ b/src/player_race_mod.hpp
@@ -0,0 +1,87 @@
+#pragma once
+
+#include "body.hpp"
+#include "h-basic.h"
+#include "skills_defs.hpp"
+
+struct player_race_mod
+{
+ char *title; /* Type of race mod */
+ char *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) */
+};
+
diff --git a/src/player_race_mod_fwd.hpp b/src/player_race_mod_fwd.hpp
new file mode 100644
index 00000000..12eb468a
--- /dev/null
+++ b/src/player_race_mod_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct player_race_mod;
diff --git a/src/player_sex.hpp b/src/player_sex.hpp
new file mode 100644
index 00000000..5722f1a4
--- /dev/null
+++ b/src/player_sex.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+/**
+ * Player sex descriptor.
+ */
+struct player_sex
+{
+ /**
+ * Type of sex.
+ */
+ char const *title;
+
+ /**
+ * Winner title.
+ */
+ char const *winner;
+};
diff --git a/src/player_sex_fwd.hpp b/src/player_sex_fwd.hpp
new file mode 100644
index 00000000..eabea488
--- /dev/null
+++ b/src/player_sex_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct player_sex;
diff --git a/src/player_spec.hpp b/src/player_spec.hpp
new file mode 100644
index 00000000..28b32830
--- /dev/null
+++ b/src/player_spec.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "h-basic.h"
+#include "skills_defs.hpp"
+
+/**
+ * Player class descriptor.
+ */
+struct player_spec
+{
+ const char *title; /* Type of class spec */
+ char *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) */
+};
diff --git a/src/player_spec_fwd.hpp b/src/player_spec_fwd.hpp
new file mode 100644
index 00000000..9083acd0
--- /dev/null
+++ b/src/player_spec_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct player_spec;
diff --git a/src/player_type.hpp b/src/player_type.hpp
new file mode 100644
index 00000000..d9410dcb
--- /dev/null
+++ b/src/player_type.hpp
@@ -0,0 +1,423 @@
+#pragma once
+
+#include "corrupt.hpp"
+#include "h-basic.h"
+#include "help_info.hpp"
+#include "inventory.hpp"
+#include "object_type.hpp"
+#include "powers.hpp"
+
+/*
+ * 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.
+ */
+
+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 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 inside_quest; /* Inside quest level */
+
+ 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. */
+ s32b grace_delay; /* Delay factor for granting piety. */
+ 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 tim_esp; /* Timed ESP */
+ s16b tim_wraith; /* Timed wraithform */
+ s16b tim_ffall; /* Timed Levitation */
+ s16b tim_fly; /* Timed Levitation */
+ 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 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 strike; /* True Strike(+25 hit) */
+ s16b tim_reflect; /* Timed Reflection */
+ 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 */
+ 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 tim_precognition; /* Timed precognition */
+
+ s16b immov_cntr; /* Timed -- Last ``immovable'' command. */
+
+ 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 */
+
+ 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_ammo; /* Correct ammo tval */
+
+ s16b pspeed; /* Current speed */
+
+ u32b mimic_extra; /* Mimicry powers use that */
+ u32b antimagic_extra; /* Antimagic powers */
+ u32b music_extra; /* Music songs */
+ u32b necro_extra; /* Necro powers */
+ u32b necro_extra2; /* Necro powers */
+
+ 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[CORRUPTIONS_MAX];
+ bool_ corrupt_anti_teleport_stopped;
+
+ /*** Pet commands ***/
+ byte pet_follow_distance; /* Length of the imaginary "leash" for pets */
+ byte pet_open_doors; /* flag - allow pets to open doors */
+ byte pet_pickup_items; /* flag - allow pets to pickup items */
+
+ s16b control; /* Controlled monster */
+ byte control_dir; /* Controlled monster */
+
+ /*** Body changing variables ***/
+ u16b body_monster; /* In which body is the player */
+ bool_ disembodied; /* Is the player in a body ? */
+ byte body_parts[INVEN_TOTAL - INVEN_WIELD]; /* Which body parts does he have ? */
+
+ /* Astral */
+ bool_ astral; /* We started at the bottom ? */
+
+ /* Powers */
+ bool_ powers[POWER_MAX]; /* Actual powers */
+ bool_ powers_mod[POWER_MAX]; /* 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 */
+
+ /* Dripping Tread spell timer */
+ s16b dripping_tread;
+
+ /* Help */
+ help_info help;
+
+ /* Inertia control */
+ s32b inertia_controlled_spell;
+
+ /* For automatic stat-gain */
+ s16b last_rewarded_level;
+
+ /*** Temporary fields ***/
+
+ bool_ did_nothing; /* True if the last action wasnt a real action */
+ bool_ leaving; /* True if player is leaving */
+};
+
diff --git a/src/player_type_fwd.hpp b/src/player_type_fwd.hpp
new file mode 100644
index 00000000..45a4bbcf
--- /dev/null
+++ b/src/player_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct player_type;
diff --git a/src/power_type.hpp b/src/power_type.hpp
new file mode 100644
index 00000000..6cf8c29b
--- /dev/null
+++ b/src/power_type.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Power descriptor. (Racial, class, mutation, artifacts, ...)
+ */
+struct power_type
+{
+ const char *name; /* Name */
+ const char *desc_text; /* Text describing power */
+ const char *gain_text; /* Text displayed on gaining the power */
+ const 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 */
+};
diff --git a/src/powers.cc b/src/powers.cc
new file mode 100644
index 00000000..a7db2968
--- /dev/null
+++ b/src/powers.cc
@@ -0,0 +1,1221 @@
+/*
+ * 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 "powers.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd1.hpp"
+#include "cmd2.hpp"
+#include "cmd7.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "hooks.hpp"
+#include "mimic.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+/*
+ * Note: return value indicates the amount of mana to use
+ */
+static 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_FRAME);
+
+ /* 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_LAY_TRAP:
+ {
+ do_cmd_set_trap();
+ }
+ break;
+ case PWR_MAGIC_MAP:
+ {
+ msg_print("You sense the world around you.");
+ map_area();
+ }
+ break;
+
+ case PWR_PASSWALL:
+ {
+ if (!get_aim_dir(&dir))
+ break;
+ if (passwall(dir, TRUE))
+ msg_print("A passage opens, and you step through.");
+ else
+ msg_print("There is no wall there!");
+ }
+ break;
+
+ case PWR_COOK_FOOD:
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create the item */
+ object_prep(q_ptr, 21);
+
+ /* Drop the object from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+ msg_print("You cook some food.");
+ }
+ break;
+
+ case PWR_UNFEAR:
+ {
+ msg_print("You play tough.");
+ (void)set_afraid(0);
+ }
+ break;
+
+ case PWR_BERSERK:
+ {
+ msg_print("RAAAGH!");
+ (void)set_afraid(0);
+
+ (void)set_shero(p_ptr->shero + 10 + randint(plev));
+ (void)hp_player(30);
+ }
+ break;
+
+ case PWR_EXPL_RUNE:
+ {
+ msg_print("You carefully set an explosive rune...");
+ explosive_rune();
+ }
+ break;
+
+ case PWR_STM:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You bash at a stone wall.");
+ (void)wall_to_mud(dir);
+ }
+ break;
+
+ case PWR_ROHAN:
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("Use [F]lash aura or [L]ight speed jump? ", &ch))
+ {
+ amber_power = 0;
+ break;
+ }
+
+ if (ch == 'F' || ch == 'f')
+ {
+ amber_power = 1;
+ break;
+ }
+
+ if (ch == 'L' || ch == 'l')
+ {
+ amber_power = 2;
+ break;
+ }
+ }
+
+ if (amber_power == 1)
+ {
+ x_ptr_foo.level = 1;
+ x_ptr_foo.cost = 9;
+ x_ptr_foo.stat = A_CHR;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ if (!(get_aim_dir(&dir))) break;
+ msg_print("You flash a bright aura.");
+ if (p_ptr->lev < 10)
+ fire_bolt(GF_CONFUSION, dir, plev*2);
+ else
+ fire_ball(GF_CONFUSION, dir, plev*2, 2);
+ }
+ }
+ if (amber_power == 2)
+ {
+ x_ptr_foo.level = 30;
+ x_ptr_foo.cost = 30;
+ x_ptr_foo.stat = A_WIS;
+ x_ptr_foo.diff = 7;
+ if (power_chance(&x_ptr_foo))
+ {
+ (void)set_light_speed(p_ptr->lightspeed + 3);
+ }
+ }
+ break;
+
+
+ case PWR_POIS_DART:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You throw a dart of poison.");
+ fire_bolt(GF_POIS, dir, plev);
+ }
+ break;
+
+ case PWR_DETECT_TD:
+ {
+ msg_print("You examine your surroundings.");
+ (void)detect_traps(DEFAULT_RADIUS);
+ (void)detect_doors(DEFAULT_RADIUS);
+ (void)detect_stairs(DEFAULT_RADIUS);
+ }
+ break;
+
+ case PWR_MAGIC_MISSILE:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_print("You cast a magic missile.");
+ fire_bolt_or_beam(10, GF_MISSILE, dir,
+ damroll(3 + ((plev - 1) / 5), 4));
+ }
+ break;
+
+ case PWR_THUNDER:
+ /* Select power to use */
+ while (TRUE)
+ {
+ if (!get_com("Use [T]hunder strike, [R]ide the straight road, go [B]ack in town? ", &ch))
+ {
+ amber_power = 0;
+ break;
+ }
+
+ if (ch == 'T' || ch == 't')
+ {
+ amber_power = 1;
+ break;
+ }
+
+ if (ch == 'R' || ch == 'r')
+ {
+ amber_power = 2;
+ break;
+ }
+
+ if (ch == 'B' || ch == 'b')
+ {
+ amber_power = 3;
+ break;
+ }
+
+ }
+
+ if (amber_power == 1)
+ {
+ x_ptr_foo.level = 1;
+ x_ptr_foo.cost = p_ptr->lev;
+ x_ptr_foo.stat = A_CON;
+ x_ptr_foo.diff = 6;
+ if (power_chance(&x_ptr_foo))
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_format("You conjure up thunder!");
+ fire_beam(GF_ELEC, dir, p_ptr->lev * 2);
+ fire_beam(GF_SOUND, dir, p_ptr->lev * 2);
+ p_ptr->energy -= 100;
+ }
+ }
+ if (amber_power == 2)
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels ...");
+ break;
+ }
+ x_ptr_foo.level = 3;
+ x_ptr_foo.cost = 15;
+ x_ptr_foo.stat = A_CON;
+ x_ptr_foo.diff = 6;
+ if (power_chance(&x_ptr_foo))
+ {
+ msg_print("You enter the straight road and fly beside the world. Where to exit?");
+ if (!tgt_pt(&ii, &ij)) return;
+ p_ptr->energy -= 60 - plev;
+ if (!cave_empty_bold(ij, ii) || (cave[ij][ii].info & CAVE_ICKY) ||
+ (distance(ij, ii, p_ptr->py, p_ptr->px) > plev*20 + 2) || !(cave[ij][ii].info & CAVE_MARK))
+ {
+ msg_print("You fail to exit correctly!");
+ p_ptr->energy -= 100;
+ teleport_player(10);
+ }
+ else teleport_player_to(ij, ii);
+ }
+
+ }
+ if (amber_power == 3)
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No recall on special levels..");
+ break;
+ }
+ x_ptr_foo.level = 7;
+ x_ptr_foo.cost = 30;
+ x_ptr_foo.stat = A_CON;
+ x_ptr_foo.diff = 6;
+ if (power_chance(&x_ptr_foo))
+ {
+ if (dun_level == 0)
+ msg_print("You are already in town!");
+ else
+ {
+ msg_print("You enter the straight road and fly beside the world.");
+ p_ptr->energy -= 100;
+ p_ptr->word_recall = 1;
+ }
+ }
+ }
+ break;
+
+ case PWR_GROW_TREE:
+ {
+ msg_print("You make the trees grow!");
+ grow_trees((plev / 8 < 1) ? 1 : plev / 8);
+ }
+ break;
+
+ case PWR_DEATHMOLD:
+ do_cmd_immovable_special();
+ break;
+
+ case PWR_BR_COLD:
+ {
+ msg_print("You breathe cold...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_COLD, dir, p_ptr->lev * 2, 1 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_BR_CHAOS:
+ {
+ msg_print("You breathe chaos...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_CHAOS, dir, p_ptr->lev * 2, 1 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_BR_ELEM:
+ {
+ if (!get_aim_dir(&dir)) break;
+ msg_format("You breathe the elements.");
+ fire_ball(GF_MISSILE, dir, (p_ptr->lev)*2,
+ ((p_ptr->lev) / 15) + 1);
+ }
+ break;
+
+ case PWR_SUMMON_MONSTER:
+ {
+ do_cmd_beastmaster();
+ }
+ break;
+
+ case PWR_WRECK_WORLD:
+ msg_print("The power of Eru Iluvatar flows through you!");
+ msg_print("The world changes!");
+
+ autosave_checkpoint();
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ break;
+
+ case PWR_VAMPIRISM:
+ {
+ /* Only works on adjacent monsters */
+ if (!get_rep_dir(&dir)) break; /* was get_aim_dir */
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("You bite into thin air!");
+ break;
+ }
+
+ msg_print("You grin and bare your fangs...");
+ dummy = plev + randint(plev) * MAX(1, plev / 10); /* Dmg */
+ if (drain_life(dir, dummy))
+ {
+ if (p_ptr->food < PY_FOOD_FULL)
+ /* No heal if we are "full" */
+ (void)hp_player(dummy);
+ else
+ msg_print("You were not hungry.");
+ /* Gain nutritional sustenance: 150/hp drained */
+ /* A Food ration gives 5000 food points (by contrast) */
+ /* Don't ever get more than "Full" this way */
+ /* But if we ARE Gorged, it won't cure us */
+ dummy = p_ptr->food + MIN(5000, 100 * dummy);
+ if (p_ptr->food < PY_FOOD_MAX) /* Not gorged already */
+ (void)set_food(dummy >= PY_FOOD_MAX ? PY_FOOD_MAX - 1 : dummy);
+ }
+ else
+ msg_print("Yechh. That tastes foul.");
+ }
+ break;
+
+ case PWR_SCARE:
+ {
+ msg_print("You emit an eldritch howl!");
+ if (!get_aim_dir(&dir)) break;
+ (void)fear_monster(dir, plev);
+ }
+ break;
+
+ case PWR_REST_LIFE:
+ {
+ msg_print("You attempt to restore your lost energies.");
+ (void)restore_level();
+ }
+ break;
+
+ case PWR_HYPNO:
+ {
+ int dir, x, y;
+ cave_type *c_ptr;
+ monster_type *m_ptr;
+ 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];
+ auto const r_ptr = m_ptr->race();
+
+ 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;
+
+ /* Get an item */
+ q = "Awaken which monster? ";
+ s = "You have no monster to awaken.";
+ if (!get_item(&item, q, s, (USE_FLOOR), object_filter::TVal(TV_HYPNOS))) return;
+
+ o_ptr = &o_list[0 - item];
+
+ d = 2;
+ while (d < 100)
+ {
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ if (cave_floor_bold(y, x) && (!cave[y][x].m_idx)) break;
+
+ d++;
+ }
+
+ if (d >= 100) return;
+
+ if ((m_idx = place_monster_one(y, x, o_ptr->pval, 0, FALSE, MSTATUS_PET)) == 0) return;
+
+ m_ptr = &m_list[m_idx];
+ m_ptr->hp = o_ptr->pval2;
+
+ floor_item_increase(0 - item, -1);
+ floor_item_describe(0 - item);
+ floor_item_optimize(0 - item);
+ }
+ break;
+
+ case PWR_NECRO:
+ {
+ do_cmd_necromancer();
+ break;
+ }
+ case PWR_INCARNATE:
+ {
+ do_cmd_integrate_body();
+ break;
+ }
+
+ case PWR_SPIT_ACID:
+ {
+ msg_print("You spit acid...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_ACID, dir, p_ptr->lev, 1 + (p_ptr->lev / 30));
+ }
+ break;
+
+ case PWR_BR_FIRE:
+ {
+ msg_print("You breathe fire...");
+ if (get_aim_dir(&dir))
+ fire_ball(GF_FIRE, dir, p_ptr->lev * 2, 1 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_HYPN_GAZE:
+ {
+ msg_print("Your eyes look mesmerising...");
+ if (get_aim_dir(&dir))
+ (void) charm_monster(dir, p_ptr->lev);
+ }
+ break;
+
+ case PWR_TELEKINES:
+ {
+ msg_print("You concentrate...");
+ if (get_aim_dir(&dir))
+ fetch(dir, p_ptr->lev * 10, TRUE);
+ }
+ break;
+
+ case PWR_VTELEPORT:
+ {
+ msg_print("You concentrate...");
+ teleport_player(10 + 4*(p_ptr->lev));
+ }
+ break;
+
+ case PWR_MIND_BLST:
+ {
+ msg_print("You concentrate...");
+ if (!get_aim_dir(&dir)) return;
+ fire_bolt(GF_PSI, dir, damroll(3 + ((p_ptr->lev - 1) / 5), 3));
+ }
+ break;
+
+ case PWR_RADIATION:
+ {
+ msg_print("Radiation flows from your body!");
+ fire_ball(GF_NUKE, 0, (p_ptr->lev * 2), 3 + (p_ptr->lev / 20));
+ }
+ break;
+
+ case PWR_SMELL_MET:
+ {
+ (void)detect_treasure(DEFAULT_RADIUS);
+ }
+ break;
+
+ case PWR_SMELL_MON:
+ {
+ (void)detect_monsters_normal(DEFAULT_RADIUS);
+ }
+ break;
+
+ case PWR_BLINK:
+ {
+ teleport_player(10);
+ }
+ break;
+
+ case PWR_EAT_ROCK:
+ {
+ int x, y, ox, oy;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir)) break;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ c_ptr = &cave[y][x];
+ if (cave_floor_bold(y, x))
+ {
+ msg_print("You bite into thin air!");
+ break;
+ }
+ else if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT) || (c_ptr->feat == FEAT_MOUNTAIN))
+ {
+ msg_print("Ouch! This wall is harder than your teeth!");
+ break;
+ }
+ else if (c_ptr->m_idx)
+ {
+ msg_print("There's something in the way!");
+ break;
+ }
+ else if (c_ptr->feat == FEAT_TREES)
+ {
+ msg_print("You don't like the woody taste!");
+ break;
+ }
+ else
+ {
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_RUBBLE))
+ {
+ (void)set_food(p_ptr->food + 3000);
+ }
+ else if ((c_ptr->feat >= FEAT_MAGMA) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K))
+ {
+ (void)set_food(p_ptr->food + 5000);
+ }
+ else if ((c_ptr->feat >= FEAT_SANDWALL) &&
+ (c_ptr->feat <= FEAT_SANDWALL_K))
+ {
+ (void)set_food(p_ptr->food + 500);
+ }
+ else
+ {
+ msg_print("This granite is very filling!");
+ (void)set_food(p_ptr->food + 10000);
+ }
+ }
+ (void)wall_to_mud(dir);
+
+ oy = p_ptr->py;
+ ox = p_ptr->px;
+
+ p_ptr->py = y;
+ p_ptr->px = x;
+
+ lite_spot(p_ptr->py, p_ptr->px);
+ lite_spot(oy, ox);
+
+ verify_panel();
+
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+ p_ptr->update |= (PU_DISTANCE);
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+ break;
+
+ case PWR_SWAP_POS:
+ {
+ if (!get_aim_dir(&dir)) return;
+ (void)teleport_swap(dir);
+ }
+ break;
+
+ case PWR_SHRIEK:
+ {
+ (void)fire_ball(GF_SOUND, 0, 4 * p_ptr->lev, 8);
+ (void)aggravate_monsters(0);
+ }
+ break;
+
+ case PWR_ILLUMINE:
+ {
+ (void)lite_area(damroll(2, (p_ptr->lev / 2)), (p_ptr->lev / 10) + 1);
+ }
+ break;
+
+ case PWR_DET_CURSE:
+ {
+ int i;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ if (!o_ptr->k_idx) continue;
+ if (!cursed_p(o_ptr)) continue;
+
+ if (!o_ptr->sense) o_ptr->sense = SENSE_CURSED;
+ }
+ }
+ break;
+
+ case PWR_POLYMORPH:
+ {
+ do_poly_self();
+ }
+ break;
+
+ case PWR_MIDAS_TCH:
+ {
+ (void)alchemy();
+ }
+ break;
+
+ case PWR_GROW_MOLD:
+ {
+ int i;
+ for (i = 0; i < 8; i++)
+ {
+ summon_specific_friendly(p_ptr->py, p_ptr->px, p_ptr->lev, SUMMON_BIZARRE1, FALSE);
+ }
+ }
+ break;
+
+ case PWR_RESIST:
+ {
+ int num = p_ptr->lev / 10;
+ int dur = randint(20) + 20;
+
+ if (rand_int(5) < num)
+ {
+ (void)set_oppose_acid(p_ptr->oppose_acid + dur);
+ num--;
+ }
+ if (rand_int(4) < num)
+ {
+ (void)set_oppose_elec(p_ptr->oppose_elec + dur);
+ num--;
+ }
+ if (rand_int(3) < num)
+ {
+ (void)set_oppose_fire(p_ptr->oppose_fire + dur);
+ num--;
+ }
+ if (rand_int(2) < num)
+ {
+ (void)set_oppose_cold(p_ptr->oppose_cold + dur);
+ num--;
+ }
+ if (num)
+ {
+ (void)set_oppose_pois(p_ptr->oppose_pois + dur);
+ num--;
+ }
+ }
+ break;
+
+ case PWR_EARTHQUAKE:
+ {
+ /* Prevent destruction of quest levels and town */
+ if (!is_quest(dun_level) && dun_level)
+ earthquake(p_ptr->py, p_ptr->px, 10);
+ }
+ break;
+
+ case PWR_EAT_MAGIC:
+ {
+ object_type * o_ptr;
+ int lev, item;
+
+ /* Get an item */
+ q = "Drain which item? ";
+ s = "You have nothing to drain.";
+ if (!get_item(&item, q, s, (USE_INVEN | USE_FLOOR), item_tester_hook_recharge())) break;
+
+ o_ptr = get_object(item);
+
+ lev = k_info[o_ptr->k_idx].level;
+
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (!o_ptr->timeout)
+ {
+ msg_print("You can't absorb energy from a discharged rod.");
+ }
+ else
+ {
+ p_ptr->csp += o_ptr->timeout;
+ o_ptr->timeout = 0;
+ }
+ }
+ else
+ {
+ if (o_ptr->pval > 0)
+ {
+ p_ptr->csp += o_ptr->pval * lev;
+ o_ptr->pval = 0;
+ }
+ else
+ {
+ msg_print("There's no energy there to absorb!");
+ }
+ o_ptr->ident |= IDENT_EMPTY;
+ }
+
+ if (p_ptr->csp > p_ptr->msp)
+ {
+ p_ptr->csp = p_ptr->msp;
+ }
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ p_ptr->window |= (PW_INVEN);
+ }
+ break;
+
+ case PWR_WEIGH_MAG:
+ {
+ report_magics();
+ }
+ break;
+
+ case PWR_STERILITY:
+ {
+ /* Fake a population explosion. */
+ msg_print("You suddenly have a headache!");
+ take_hit(randint(30) + 30, "the strain of forcing abstinence");
+ num_repro += MAX_REPRO;
+ }
+ break;
+
+ case PWR_PANIC_HIT:
+ {
+ int x, y;
+
+ if (!get_rep_dir(&dir)) return;
+ y = p_ptr->py + ddy[dir];
+ x = p_ptr->px + ddx[dir];
+ if (cave[y][x].m_idx)
+ {
+ py_attack(y, x, -1);
+ teleport_player(30);
+ }
+ else
+ {
+ msg_print("You don't see any monster in this direction.");
+ msg_print(NULL);
+ }
+ }
+ break;
+
+ case PWR_DAZZLE:
+ {
+ stun_monsters(p_ptr->lev * 4);
+ confuse_monsters(p_ptr->lev * 4);
+ turn_monsters(p_ptr->lev * 4);
+ }
+ break;
+
+ case PWR_DARKRAY:
+ {
+ if (!get_aim_dir(&dir)) return;
+ fire_beam(GF_LITE, dir, 2*p_ptr->lev);
+ }
+ break;
+
+ case PWR_RECALL:
+ {
+ if (!(dungeon_flags2 & DF2_ASK_LEAVE) || ((dungeon_flags2 & DF2_ASK_LEAVE) && !get_check("Leave this unique level forever? ")))
+ {
+ recall_player(21, 15);
+ }
+ }
+ break;
+
+ case PWR_BANISH:
+ {
+ if (!get_rep_dir(&dir)) return;
+ const int x = p_ptr->px + ddx[dir];
+ const int y = p_ptr->py + ddy[dir];
+ cave_type *c_ptr = &cave[y][x];
+ if (!(c_ptr->m_idx))
+ {
+ msg_print("You sense no evil there!");
+ break;
+ }
+
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ auto const r_ptr = m_ptr->race();
+
+ 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;
+
+ case POWER_INVISIBILITY:
+ set_invis(20 + randint(30), 30);
+ break;
+
+ case POWER_WEB:
+ /* Warning, beware of f_info changes .. I hate to do that .. */
+ grow_things(16, 1 + (p_ptr->lev / 10));
+ break;
+
+ case POWER_COR_SPACE_TIME:
+ if (p_ptr->corrupt_anti_teleport_stopped)
+ {
+ p_ptr->corrupt_anti_teleport_stopped = FALSE;
+ msg_print("You stop controlling your corruption.");
+ p_ptr->update |= PU_BONUS;
+ }
+ else
+ {
+ p_ptr->corrupt_anti_teleport_stopped = TRUE;
+ msg_print("You start controlling your corruption, teleportation works once more.");
+ p_ptr->update |= PU_BONUS;
+ }
+ break;
+
+ default:
+ abort();
+ break;
+ }
+
+ p_ptr->redraw |= (PR_FRAME);
+ p_ptr->window |= (PW_PLAYER);
+}
+
+/*
+ * Print a batch of power.
+ */
+static void print_power_batch(int *p, int start, int max)
+{
+ char buff[80];
+ power_type* spell;
+ int i = start, j = 0;
+
+ 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);
+
+ prt(buff, 2 + j, 20);
+ j++;
+ }
+ 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;
+ int p[POWER_MAX];
+
+ /* 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);
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ *x_idx = -1;
+ ret = NULL;
+ break;
+ }
+ 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;
+ }
+
+ 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/powers.hpp b/src/powers.hpp
new file mode 100644
index 00000000..35d317b0
--- /dev/null
+++ b/src/powers.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+extern void do_cmd_power();
+
+/*
+ * Powers (mutation, activations, ...)
+ */
+#define POWER_MAX 65
+
+#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_COMPANION 58
+#define PWR_BEAR 59
+#define PWR_DODGE 60
+#define PWR_BALROG 61
+#define POWER_INVISIBILITY 62
+#define POWER_WEB 63
+#define POWER_COR_SPACE_TIME 64
diff --git a/src/q_betwen.cc b/src/q_betwen.cc
new file mode 100644
index 00000000..2bebe452
--- /dev/null
+++ b/src/q_betwen.cc
@@ -0,0 +1,212 @@
+#include "q_betwen.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_init_quest_in.hpp"
+#include "hook_move_in.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#define cquest (quest[QUEST_BETWEEN])
+
+static bool_ quest_between_move_hook(void *, void *in_, void *)
+{
+ struct hook_move_in *in = static_cast<struct hook_move_in *>(in_);
+ s32b y = in->y;
+ s32b x = in->x;
+ cave_type *c_ptr;
+
+ 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;
+}
+
+static bool_ quest_between_gen_hook(void *, void *, void *)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_BETWEEN) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("between.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ /* Otherwise instadeath */
+ energy_use = 0;
+
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ return TRUE;
+}
+
+static bool_ quest_between_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+ object_type forge, *q_ptr;
+
+ 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_new(HOOK_QUEST_FINISH, quest_between_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_between_death_hook(void *, void *, void *)
+{
+ 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;
+}
+
+static bool_ quest_between_dump_hook(void *, void *in_, void *)
+{
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(f, "\n You established a permanent void jumpgates liaison between Minas Anor and Gondolin,");
+ fprintf(f, "\n thus allowing the last alliance to exist.");
+ }
+ return (FALSE);
+}
+
+static bool_ quest_between_forbid_hook(void *, void *in_, void *)
+{
+ hook_init_quest_in *in = static_cast<struct hook_init_quest_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_new(HOOK_MOVE, quest_between_move_hook, "between_move", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_between_gen_hook, "between_gen", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_between_finish_hook, "between_finish", NULL);
+ add_hook_new(HOOK_MONSTER_DEATH, quest_between_death_hook, "between_death", NULL);
+ }
+ add_hook_new(HOOK_CHAR_DUMP, quest_between_dump_hook, "between_dump", NULL);
+ add_hook_new(HOOK_INIT_QUEST, quest_between_forbid_hook, "between_forbid", NULL);
+ return (FALSE);
+}
diff --git a/src/q_betwen.hpp b/src/q_betwen.hpp
new file mode 100644
index 00000000..d2fc08f0
--- /dev/null
+++ b/src/q_betwen.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_between_init_hook(int q_idx);
diff --git a/src/q_bounty.cc b/src/q_bounty.cc
new file mode 100644
index 00000000..bb84d48d
--- /dev/null
+++ b/src/q_bounty.cc
@@ -0,0 +1,170 @@
+#include "q_bounty.hpp"
+
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "skill_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#define cquest (quest[QUEST_BOUNTY])
+
+#define bounty_quest_monster (cquest.data[0])
+
+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);
+}
+
+static int 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;
+}
+
+static bool bounty_item_tester_hook(object_type const *o_ptr)
+{
+ return ((o_ptr->tval == TV_CORPSE) && (o_ptr->pval2 == bounty_quest_monster));
+}
+
+bool_ quest_bounty_init_hook(int dummy)
+{
+ return FALSE;
+}
+
+bool_ quest_bounty_drop_item()
+{
+ char mdesc[512];
+ char msg[512];
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ cquest.status = QUEST_STATUS_TAKEN;
+ bounty_quest_monster = get_new_bounty_monster(3 + (p_ptr->lev * 3) / 2);
+
+ monster_race_desc(mdesc, bounty_quest_monster, 0);
+ snprintf(msg, sizeof(msg), "You must bring me back %s corpse.", mdesc);
+ msg_print(msg);
+ }
+ else
+ {
+ monster_race_desc(mdesc, bounty_quest_monster, 0);
+ snprintf(msg, sizeof(msg), "You still must bring me back %s corpse.", mdesc);
+ msg_print(msg);
+ }
+ return FALSE;
+}
+
+bool_ quest_bounty_get_item()
+{
+ if (cquest.status != QUEST_STATUS_TAKEN)
+ {
+ msg_print("You do not have any bounty quest yet.");
+ return FALSE;
+ }
+
+ // Get the corpse.
+ int item = -1;
+ bool_ ret =
+ get_item(&item,
+ "What corpse to return?",
+ "You have no corpse to return.",
+ USE_INVEN,
+ bounty_item_tester_hook);
+ if (!ret) {
+ return FALSE;
+ }
+
+ // 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.");
+
+ skill_type *lore = &s_info[SKILL_LORE];
+ skill_type *preservation = &s_info[SKILL_PRESERVATION];
+
+ if (lore->mod == 0) {
+ lore->mod = 900;
+ lore->dev = TRUE;
+ }
+ lore->value += lore->mod;
+
+ if (preservation->mod == 0) {
+ preservation->value = 800;
+ preservation->mod = 800;
+ preservation->dev = TRUE;
+ msg_print("I see you don't know the corpse preservation skill, I shall teach you it too.");
+ }
+
+ // Need to ask for new quest.
+ cquest.status = QUEST_STATUS_UNTAKEN;
+ bounty_quest_monster = 0;
+ return FALSE;
+}
+
+bool_ quest_bounty_describe(FILE *fff)
+{
+ char mdesc[512];
+
+ if (cquest.status == QUEST_STATUS_TAKEN)
+ {
+ monster_race_desc(mdesc, bounty_quest_monster, 0);
+
+ fprintf(fff, "#####yBounty quest!\n");
+ fprintf(fff, "You must bring back %s corpse to the beastmaster.\n", mdesc);
+ fprintf(fff, "\n");
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/src/q_bounty.hpp b/src/q_bounty.hpp
new file mode 100644
index 00000000..234c036d
--- /dev/null
+++ b/src/q_bounty.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern bool_ quest_bounty_init_hook(int q_idx);
+extern bool_ quest_bounty_drop_item();
+extern bool_ quest_bounty_get_item();
+extern bool_ quest_bounty_describe(FILE *fff);
diff --git a/src/q_dragons.cc b/src/q_dragons.cc
new file mode 100644
index 00000000..6c6084d1
--- /dev/null
+++ b/src/q_dragons.cc
@@ -0,0 +1,164 @@
+#include "q_dragons.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "feature_type.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#define cquest (quest[QUEST_DRAGONS])
+
+static bool_ quest_dragons_gen_hook(void *, void *, void *)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_DRAGONS) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("dragons.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some columns */
+ for (i = 35; i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ /* Bar columns on even squares so the whole level is guaranteed to be
+ accessible */
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags % 2) && !(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ --i;
+ cave_set_feat(y, x, FEAT_MOUNTAIN);
+ }
+ }
+
+ /* Place some random dragons */
+ for (i = 25; i > 0; )
+ {
+ int m_idx, flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ /* blue, white, red, black, bronze, gold, green, multi-hued */
+ int baby_dragons[8] = {163, 164, 167, 166, 218, 219, 165, 204};
+ int young_dragons[8] = {459, 460, 563, 546, 462, 559, 461, 556};
+ int mature_dragons[8] = {560, 549, 589, 592, 562, 590, 561, 593};
+ int happy_dragons[8] = {601, 617, 644, 624, 602, 645, 618, 675};
+
+ int chance, dragon, color;
+
+ color = rand_int(8);
+ chance = rand_int(100);
+ if (chance == 0)
+ dragon = happy_dragons[color];
+ else if (chance < 33)
+ dragon = baby_dragons[color];
+ else if (chance < 66)
+ dragon = young_dragons[color];
+ else
+ dragon = mature_dragons[color];
+
+ --i;
+ m_idx = place_monster_one(y, x, dragon, 0, magik(33), MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_dragons_death_hook(void *, void *, void *)
+{
+ 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_new(HOOK_MONSTER_DEATH, quest_dragons_death_hook);
+ del_hook_new(HOOK_GEN_QUEST, quest_dragons_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Gondolin is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+static bool_ quest_dragons_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_new(HOOK_MONSTER_DEATH, quest_dragons_death_hook, "dragons_monster_death", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_dragons_finish_hook, "dragons_finish", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_dragons_gen_hook, "dragons_geb", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_dragons.hpp b/src/q_dragons.hpp
new file mode 100644
index 00000000..f0aa50f2
--- /dev/null
+++ b/src/q_dragons.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_dragons_init_hook(int q_idx);
diff --git a/src/q_eol.cc b/src/q_eol.cc
new file mode 100644
index 00000000..f27ce9df
--- /dev/null
+++ b/src/q_eol.cc
@@ -0,0 +1,226 @@
+#include "q_eol.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "generate.hpp"
+#include "hook_stair_in.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hook_quest_fail_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hooks.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define cquest (quest[QUEST_EOL])
+
+GENERATE_MONSTER_LOOKUP_FN(get_eol, "Eol, the Dark Elf")
+
+static bool_ quest_eol_gen_hook(void *, void *, void *)
+{
+ 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)
+ {
+ // Find Eol's r_info entry
+ int r_idx = get_eol();
+ // "Summon" Eol
+ m_allow_special[r_idx] = TRUE;
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[r_idx] = FALSE;
+ // Mark with the QUEST flag
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ }
+
+ if (magik(18))
+ {
+ place_trap(y, x);
+ }
+
+ /* Place player at one end */
+ p_ptr->py = y;
+ p_ptr->px = x;
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+
+ return TRUE;
+}
+
+static bool_ quest_eol_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+ object_type forge, *q_ptr;
+
+ 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_new(HOOK_QUEST_FINISH, quest_eol_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_eol_fail_hook(void *, void *in_, void *)
+{
+ struct hook_quest_fail_in *in = static_cast<struct hook_quest_fail_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_new(HOOK_QUEST_FAIL, quest_eol_fail_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_eol_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ s32b r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_EOL) return FALSE;
+
+ if (r_idx == get_eol())
+ {
+ cmsg_print(TERM_YELLOW, "Such a sad end...");
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ del_hook_new(HOOK_MONSTER_DEATH, quest_eol_death_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+ }
+
+ return FALSE;
+}
+
+static bool_ quest_eol_stair_hook(void *, void *in_, void *)
+{
+ struct hook_stair_in *in = static_cast<struct hook_stair_in *>(in_);
+ monster_race *r_ptr = &r_info[get_eol()];
+
+ 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 (in->direction == STAIRS_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_new(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_new(HOOK_MONSTER_DEATH, quest_eol_death_hook, "eol_death", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_eol_gen_hook, "eol_gen", NULL);
+ add_hook_new(HOOK_STAIR, quest_eol_stair_hook, "eol_stair", NULL);
+ add_hook_new(HOOK_QUEST_FAIL, quest_eol_fail_hook, "eol_fail", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_eol_finish_hook, "eol_finish", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_eol.hpp b/src/q_eol.hpp
new file mode 100644
index 00000000..ab7f1274
--- /dev/null
+++ b/src/q_eol.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_eol_init_hook(int q_idx);
diff --git a/src/q_evil.cc b/src/q_evil.cc
new file mode 100644
index 00000000..3bc953cd
--- /dev/null
+++ b/src/q_evil.cc
@@ -0,0 +1,132 @@
+#include "q_evil.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "feature_type.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#define cquest (quest[QUEST_EVIL])
+
+static bool_ quest_evil_gen_hook(void *, void *, void *)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_EVIL) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("evil.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some random balrogs */
+ for (i = 6; i > 0; )
+ {
+ int m_idx, flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ m_idx = place_monster_one(y, x, 996, 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_evil_death_hook(void *, void *, void *)
+{
+ 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_new(HOOK_MONSTER_DEATH, quest_evil_death_hook);
+ del_hook_new(HOOK_GEN_QUEST, quest_evil_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Khazad-Dum is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+static bool_ quest_evil_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_new(HOOK_MONSTER_DEATH, quest_evil_death_hook, "evil_monster_death", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_evil_finish_hook, "evil_finish", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_evil_gen_hook, "evil_geb", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_evil.hpp b/src/q_evil.hpp
new file mode 100644
index 00000000..06fb17e1
--- /dev/null
+++ b/src/q_evil.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_evil_init_hook(int q_idx);
diff --git a/src/q_fireprof.cc b/src/q_fireprof.cc
new file mode 100644
index 00000000..021cf2fb
--- /dev/null
+++ b/src/q_fireprof.cc
@@ -0,0 +1,577 @@
+#include "q_fireprof.hpp"
+
+#include "cave_type.hpp"
+#include "feature_type.hpp"
+#include "hook_get_in.hpp"
+#include "hooks.hpp"
+#include "lua_bind.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "tables.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define cquest (quest[QUEST_FIREPROOF])
+
+#define print_hook(fmt,...) do { fprintf(hook_file, fmt, ##__VA_ARGS__); } while (0)
+
+/*
+ * Per-module "settings"
+ */
+typedef struct fireproof_settings fireproof_settings;
+struct fireproof_settings
+{
+ byte tval; /* tval of object to use. */
+ cptr tval_name; /* descriptive name of tval */
+ cptr tval_name_plural; /* descriptive name of tval (plural) */
+ byte sval_max; /* max sval of object to use; sval will be 1<=X<=sval_max. */
+ s32b total_points; /* total number of points awarded */
+};
+
+static fireproof_settings const *fireproof_get_settings()
+{
+ static fireproof_settings fireproof_settings =
+ { TV_RUNE2, "rune", "runes", 5, 24 };
+ return &fireproof_settings;
+}
+
+/* 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. */
+#define FIREPROOF_BOOK_POINTS 4
+#define FIREPROOF_STAFF_POINTS 3
+#define FIREPROOF_SCROLL_POINTS 1
+
+static s32b get_item_points_remaining()
+{
+ fireproof_settings const *settings = fireproof_get_settings();
+ return settings->total_points - cquest.data[0];
+}
+
+static void set_item_points_remaining(s32b v)
+{
+ fireproof_settings const *settings = fireproof_get_settings();
+ cquest.data[0] = settings->total_points - v;
+}
+
+static void fireproof_set_sval(int sval_max)
+{
+ cquest.data[1] = sval_max;
+}
+
+static int fireproof_get_sval()
+{
+ return cquest.data[1];
+}
+
+static bool item_tester_hook_eligible(object_type const *o_ptr)
+{
+ /* check it's the 'marked' item */
+ return ((o_ptr->tval == fireproof_get_settings()->tval) &&
+ (o_ptr->sval == fireproof_get_sval()) &&
+ (o_ptr->pval2 == fireproof_get_sval()));
+}
+
+static object_filter_t const &item_tester_hook_proofable()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ // Must be the correct item base type
+ Or(
+ TVal(TV_BOOK),
+ TVal(TV_SCROLL),
+ TVal(TV_STAFF)),
+ // Must NOT already be fireproof
+ Not(
+ HasFlag3(TR3_IGNORE_FIRE)));
+ return instance;
+}
+
+/*
+ * This function makes sure the player has enough 'points' left to fireproof stuff.
+ */
+static bool_ fireproof_enough_points(object_type *o_ptr, int *stack)
+{
+ int item_value;
+
+ /* are the items in a stack? */
+ if (o_ptr->number > 1)
+ {
+ /* how many to fireproof? */
+ *stack = get_quantity("How many would you like fireproofed?", o_ptr->number);
+ }
+ else
+ {
+ *stack = 1;
+ }
+
+ /* check for item type and multiply number in the stack by the
+ * amount of points per item of that type */
+ switch (o_ptr->tval)
+ {
+ case TV_BOOK:
+ item_value = FIREPROOF_BOOK_POINTS * (*stack);
+ break;
+ case TV_STAFF:
+ item_value = FIREPROOF_STAFF_POINTS * (*stack);
+ break;
+ case TV_SCROLL:
+ item_value = FIREPROOF_SCROLL_POINTS * (*stack);
+ break;
+ default:
+ assert(FALSE);
+ }
+
+ /* do we have enough points? */
+ if (item_value > get_item_points_remaining())
+ {
+ 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 */
+ set_item_points_remaining(get_item_points_remaining() - item_value);
+ }
+
+ /* Used all the points? the quest is completely rewarded. */
+ if (get_item_points_remaining() == 0)
+ {
+ cquest.status = QUEST_STATUS_REWARDED;
+ }
+
+ return TRUE;
+}
+
+static bool_ fireproof()
+{
+ int item;
+ if (!get_item(&item,
+ "Which item shall I fireproof?",
+ "You have no more items I can fireproof, come back when you have some.",
+ USE_INVEN,
+ item_tester_hook_proofable()))
+ {
+ return FALSE;
+ }
+
+ /* get the object type from the number */
+ object_type *obj2 = get_object(item);
+
+ /* check we have enough points (if we 'got' an item) */
+ int stack = 0;
+ if (!fireproof_enough_points(obj2, &stack))
+ {
+ return FALSE;
+ }
+
+ /* Do the actual fireproofing */
+ {
+ bool_ carry_it;
+ object_type *obj3;
+ object_type obj_forge;
+ s32b oldpval, oldpval2, oldpval3;
+
+ /* are we part of the items from a stack? */
+ if (obj2->number != stack) {
+
+ /* 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;
+ }
+
+ /* 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!) */
+ oldpval = obj3->pval;
+ oldpval2 = obj3->pval2;
+ 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)
+ {
+ inven_carry(obj3, TRUE);
+ }
+
+ /* id and notice it */
+ object_known(obj3);
+ object_aware(obj3);
+
+ return TRUE;
+ }
+}
+
+
+void quest_fireproof_building(bool_ *paid, bool_ *recreate)
+{
+ fireproof_settings const *settings = fireproof_get_settings();
+ int num_books, num_staff, num_scroll;
+
+ num_books = get_item_points_remaining() / FIREPROOF_BOOK_POINTS;
+ num_staff = get_item_points_remaining() / FIREPROOF_STAFF_POINTS;
+ num_scroll = get_item_points_remaining() / FIREPROOF_SCROLL_POINTS;
+
+ /* the quest hasn't been requested already, right? */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ /* quest has been taken now */
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* issue instructions */
+ msg_format("I need a very special %s for a spell I am"
+ " working on. I am too old to ", settings->tval_name);
+ 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.");
+
+ *paid = FALSE;
+ *recreate = TRUE;
+ }
+
+ /* if quest completed (item was retrieved) */
+ else if (cquest.status == QUEST_STATUS_COMPLETED)
+ {
+ char p[512];
+ char pni[512];
+ int item_idx;
+ int ret;
+
+ /* generate prompt strings */
+ snprintf(p , sizeof(p) , "Which %s?", settings->tval_name);
+ snprintf(pni, sizeof(pni), "You have no %s to return", settings->tval_name_plural);
+
+ /* ask for item */
+ ret = get_item(&item_idx, p, pni, USE_INVEN, item_tester_hook_eligible);
+
+ /* didn't get the item? */
+ if (!ret)
+ {
+ return;
+ }
+
+ /* got the item! */
+ else
+ {
+ int items;
+
+ /* take item */
+ inc_stack_size_ex(item_idx, -1, OPTIMIZE, NO_DESCRIBE);
+
+ msg_print(format("Great! Let me fireproof some of your items in thanks. I can do %d books, ", num_books));
+ msg_print(format("%d staves, or %d scrolls.", num_staff, num_scroll));
+
+ /* how many items to proof? */
+ items = get_item_points_remaining();
+
+ /* repeat till up to 3 (value defined as constant) books fireproofed */
+ while (items > 0)
+ {
+ ret = fireproof();
+
+ /* don't loop the fireproof if there's nothing to fireproof */
+ if (ret == FALSE)
+ {
+ break;
+ }
+
+ /* subtract item points */
+ items = get_item_points_remaining();
+ }
+
+ /* have they all been done? */
+ if (get_item_points_remaining() == 0)
+ {
+ /* mark quest to make sure no more quests are given */
+ cquest.status = QUEST_STATUS_REWARDED;
+ }
+ else
+ {
+ /* mark in preparation of anymore books to fireproof */
+ cquest.status = QUEST_STATUS_FINISHED;
+ }
+ }
+ }
+
+ /* if the player asks for a quest when they already have it, but haven't failed it, give them some extra instructions */
+ else if (cquest.status == QUEST_STATUS_TAKEN)
+ {
+ msg_format("The %s is in a cave just behind the shop.",
+ settings->tval_name);
+ }
+
+ /* ok not all books have been fireproofed... lets do the rest */
+ else if (cquest.status == QUEST_STATUS_FINISHED)
+ {
+
+ /* how many books still to proof? */
+ int items = get_item_points_remaining();
+
+ /* repeat as necessary */
+ while (items > 0)
+ {
+ int ret = fireproof();
+
+ /* don't loop the fireproof if there's nothing to fireproof */
+ if (ret == FALSE)
+ {
+ break;
+ }
+ else
+ {
+ /* have they all been done? */
+ if (get_item_points_remaining() == 0)
+ {
+ cquest.status = QUEST_STATUS_REWARDED;
+ }
+ }
+
+ /* subtract item points */
+ items = get_item_points_remaining();
+ }
+
+ }
+
+ /* quest failed or completed, then give no more quests */
+ else if ((cquest.status == QUEST_STATUS_FAILED) ||
+ (cquest.status == QUEST_STATUS_REWARDED))
+ {
+ msg_print("I have no more quests for you");
+ }
+}
+
+static bool_ fireproof_get_hook(void *, void *in_, void *)
+{
+ struct hook_get_in *in = static_cast<struct hook_get_in *>(in_);
+ object_type *o_ptr = in->o_ptr;
+
+ /* check that player is in the quest, haven't picked up the
+ * item already, and check that it's the real item and not another one
+ * generated via random object placement */
+ if ((p_ptr->inside_quest == QUEST_FIREPROOF) &&
+ (cquest.status != QUEST_STATUS_COMPLETED) &&
+ (o_ptr->pval2 == fireproof_get_sval()))
+ {
+ /* ok mark the quest 'completed' */
+ cquest.status = QUEST_STATUS_COMPLETED;
+ cmsg_print(TERM_YELLOW, "Fine! Looks like you've found it.");
+ }
+
+ return FALSE;
+}
+
+static bool_ fireproof_stair_hook(void *, void *, void *)
+{
+ /* only ask this if player about to go up stairs of quest and
+ * hasn't retrieved item */
+ if ((p_ptr->inside_quest != QUEST_FIREPROOF) ||
+ (cquest.status == QUEST_STATUS_COMPLETED))
+ {
+ return FALSE;
+ }
+ else
+ {
+ bool_ ret;
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_LESS)
+ {
+ return FALSE;
+ }
+
+ /* flush all pending input */
+ flush();
+
+ /* confirm */
+ ret = get_check("Really abandon the quest?");
+
+ /* if yes, then */
+ if (ret == TRUE)
+ {
+ /* fail the quest */
+ cquest.status = QUEST_STATUS_FAILED;
+ return FALSE;
+ }
+ else
+ {
+ /* if no, they stay in the quest */
+ return TRUE;
+ }
+ }
+}
+
+bool_ quest_fireproof_describe(FILE *hook_file)
+{
+ fireproof_settings const *settings = fireproof_get_settings();
+ int num_books, num_staff, num_scroll;
+ int status = cquest.status;
+
+ num_books = get_item_points_remaining() / FIREPROOF_BOOK_POINTS;
+ num_staff = get_item_points_remaining() / FIREPROOF_STAFF_POINTS;
+ num_scroll = get_item_points_remaining() / FIREPROOF_SCROLL_POINTS;
+
+ if (status == QUEST_STATUS_TAKEN)
+ {
+ /* Quest taken */
+ print_hook("#####yAn Old Mages Quest!\n");
+ print_hook("Retrieve the strange %s for the old mage "
+ "in Lothlorien.\n", settings->tval_name);
+ print_hook("\n");
+ }
+ else if (status == QUEST_STATUS_COMPLETED)
+ {
+ /* essence retrieved, not taken to mage */
+ print_hook("#####yAn Old Mages Quest!\n");
+ print_hook("You have retrieved the %s for the old "
+ "mage in Lothlorien. Perhaps you \n", settings->tval_name);
+ print_hook("should see about a reward.\n");
+ print_hook("\n");
+ }
+ else if ((status == QUEST_STATUS_FINISHED) &&
+ (get_item_points_remaining() > 0))
+ {
+ /* essence returned, not all books fireproofed */
+ print_hook("#####yAn Old Mages Quest!\n");
+ print_hook("You have retrieved the %s for the old "
+ "mage in Lothlorien. He will still \n", settings->tval_name);
+ print_hook("fireproof %d book(s) or %d staff/staves "
+ "or %d scroll(s) for you.\n",
+ num_books, num_staff, num_scroll);
+ print_hook("\n");
+ }
+
+ return TRUE;
+}
+
+static bool_ fireproof_gen_hook(void *, void *, void *)
+{
+ fireproof_settings const *settings = fireproof_get_settings();
+
+ /* Only if player doing this quest */
+ if (p_ptr->inside_quest != QUEST_FIREPROOF)
+ {
+ return FALSE;
+ }
+
+ /* Go ahead */
+ {
+ int traps, trap_y, trap_x;
+
+ /* load the map */
+ {
+ int x0 = 2;
+ int y0 = 2;
+ load_map("fireprof.map", &y0, &x0);
+ }
+
+ /* no teleport */
+ dungeon_flags2 = DF2_NO_TELEPORT;
+
+ /* determine type of item */
+ fireproof_set_sval(randint(settings->sval_max));
+
+ /* create essence */
+ {
+ int x, y;
+ object_type forge;
+
+ object_prep(&forge, lookup_kind(settings->tval, fireproof_get_sval()));
+
+ /* mark item */
+ forge.pval2 = fireproof_get_sval();
+ forge.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(&forge, -1, y, x);
+ }
+
+ /* how many traps to generate */
+ traps = rand_range(10, 30);
+
+ /* generate the traps */
+ while (traps > 0)
+ {
+ int tries = 0, trap_level = 0;
+
+ /* make sure it's a safe place */
+ while (tries == 0)
+ {
+ /* get grid coordinates */
+ trap_y = randint(19) + 2;
+ trap_x = randint(45) + 2;
+ cave_type *c_ptr = &cave[trap_y][trap_x];
+
+ /* are the coordinates on a stair, or a wall? */
+ if (((f_info[c_ptr->feat].flags1 & FF1_PERMANENT) != 0) ||
+ ((f_info[c_ptr->feat].flags1 & FF1_FLOOR) == 0))
+ {
+ /* try again */
+ tries = 0;
+ }
+ else
+ {
+ /* not a stair, then stop this 'while' */
+ tries = 1;
+ }
+ }
+
+ /* randomise level of trap */
+ trap_level = rand_range(20, 40);
+
+ /* put the trap there */
+ place_trap_leveled(trap_y, trap_x, trap_level);
+
+ /* that's one less trap to place */
+ traps = traps - 1;
+ }
+
+ return TRUE;
+ }
+}
+
+bool_ quest_fireproof_init_hook(int q)
+{
+ /* Only need hooks if the quest is unfinished. */
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) &&
+ (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook_new(HOOK_GEN_QUEST, fireproof_gen_hook , "fireproof_gen_hook", NULL);
+ add_hook_new(HOOK_GET , fireproof_get_hook , "fireproof_get_hook", NULL);
+ add_hook_new(HOOK_STAIR , fireproof_stair_hook, "fireproof_stair_hook", NULL);
+ }
+
+ return FALSE;
+}
+
+#undef print_hook
diff --git a/src/q_fireprof.hpp b/src/q_fireprof.hpp
new file mode 100644
index 00000000..53d368b0
--- /dev/null
+++ b/src/q_fireprof.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern void quest_fireproof_building(bool_ *paid, bool_ *recreate);
+extern bool_ quest_fireproof_init_hook(int q);
+extern bool_ quest_fireproof_describe(FILE *fff);
diff --git a/src/q_god.cc b/src/q_god.cc
new file mode 100644
index 00000000..0a3b07e7
--- /dev/null
+++ b/src/q_god.cc
@@ -0,0 +1,1212 @@
+#include "q_god.hpp"
+
+#include "cave_type.hpp"
+#include "dungeon_info_type.hpp"
+#include "feature_type.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_get_in.hpp"
+#include "hook_enter_dungeon_in.hpp"
+#include "hook_player_level_in.hpp"
+#include "hooks.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "skill_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+#include "z-rand.hpp"
+
+#include <assert.h>
+
+#define cquest (quest[QUEST_GOD])
+#define cquest_quests_given (cquest.data[0])
+#define cquest_relics_found (cquest.data[1])
+#define cquest_dun_mindepth (cquest.data[2])
+#define cquest_dun_maxdepth (cquest.data[3])
+#define cquest_dun_minplev (cquest.data[4])
+#define cquest_relic_gen_tries (cquest.data[5])
+#define cquest_relic_generated (cquest.data[6])
+#define cquest_dung_x (cquest.data[7])
+#define cquest_dung_y (cquest.data[8])
+
+/* d_idx of the god_quest (Lost Temple) dungeon */
+#define DUNGEON_GOD 30
+#define CHANCE_OF_GOD_QUEST 21
+
+/*
+ * 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.
+ *
+ * The returned string is allocated with strdup().
+ */
+static char *compass(int y, int x, int y2, int x2)
+{
+ char compass_dir[64];
+
+ // is it close to the north/south meridian?
+ int y_diff = y2 - y;
+
+ // determine if y2, x2 is to the north or south of y, x
+ const char *y_axis;
+ if ((y_diff > -3) && (y_diff < 3))
+ {
+ y_axis = 0;
+ }
+ else if (y2 > y)
+ {
+ y_axis = "south";
+ }
+ else
+ {
+ y_axis = "north";
+ }
+
+ // is it close to the east/west meridian?
+ int x_diff = x2 - x;
+
+ // determine if y2, x2 is to the east or west of y, x
+ const char *x_axis;
+ if ((x_diff > -3) && (x_diff < 3))
+ {
+ x_axis = 0;
+ }
+ else if (x2 > x)
+ {
+ x_axis = "east";
+ }
+ else
+ {
+ x_axis = "west";
+ }
+
+ // Maybe it is very close
+ if ((!x_axis) && (!y_axis)) { strcpy(compass_dir, "close"); }
+ // Maybe it is (almost) due N/S
+ else if (!x_axis) { strcpy(compass_dir, y_axis); }
+ // Maybe it is (almost) due E/W
+ else if (!y_axis) { strcpy(compass_dir, x_axis); }
+ // or if it is neither
+ else { sprintf(compass_dir, "%s-%s", y_axis, x_axis); }
+
+ /* Return a copy */
+ return strdup(compass_dir);
+}
+
+/* Returns a relative approximation of the 'distance' of y2, x2 from y, x. */
+static cptr approximate_distance(int y, int x, int y2, int x2)
+{
+ // how far to away to the north/south?
+ int y_diff = abs(y2 - y);
+ // how far to away to the east/west?
+ int x_diff = abs(x2 - x);
+ // find which one is the larger distance
+ int most_dist = x_diff;
+ if (y_diff > most_dist) {
+ most_dist = y_diff;
+ }
+
+ // how far away then?
+ if (most_dist >= 41) {
+ return "a very long way";
+ } else if (most_dist >= 25) {
+ return "a long way";
+ } else if (most_dist >= 8) {
+ return "quite some way";
+ } else {
+ return "not very far";
+ }
+}
+
+static int MAX_NUM_GOD_QUESTS()
+{
+ if (game_module_idx == MODULE_TOME)
+ {
+ return 5;
+ }
+ if (game_module_idx == MODULE_THEME)
+ {
+ return 7;
+ }
+ /* Uh, oh. */
+ assert(FALSE);
+ return 0;
+}
+
+static byte get_relic_num()
+{
+ int i;
+ int sval_by_god[][2] = {
+ { GOD_ERU, 7 },
+ { GOD_MANWE, 8 },
+ { GOD_TULKAS, 9 },
+ { GOD_MELKOR, 10 },
+ { GOD_YAVANNA, 11 },
+ { GOD_AULE, 16 },
+ { GOD_VARDA, 17 },
+ { GOD_ULMO, 18 },
+ { GOD_MANDOS, 19 },
+ { -1, -1 },
+ };
+
+ for (i = 0; sval_by_god[i][1] != -1; i++)
+ {
+ if (p_ptr->pgod == sval_by_god[i][0])
+ {
+ int sval = sval_by_god[i][1];
+ return sval;
+ }
+ }
+
+ /* Uh, oh. */
+ assert(FALSE);
+}
+
+static void get_home_coordinates(int *home1_y, int *home1_x, const char **home1,
+ int *home2_y, int *home2_x, const char **home2)
+{
+ /* Which are the waypoints? */
+ if (p_ptr->pgod != GOD_MELKOR)
+ {
+ *home1 = "Bree";
+ *home2 = "Minas Anor";
+ }
+ else
+ {
+ *home1 = "the Pits of Angband";
+ *home2 = "the Land of Mordor";
+ }
+
+ /* Module specific locations */
+ if (game_module_idx == MODULE_TOME)
+ {
+ if (p_ptr->pgod != GOD_MELKOR)
+ {
+ *home1_y = 21;
+ *home1_x = 34;
+ *home2_y = 56;
+ *home2_x = 60;
+ }
+ else
+ {
+ *home1_y = 7;
+ *home1_x = 34;
+ *home2_y = 58;
+ *home2_x = 65;
+ }
+ }
+ else if (game_module_idx == MODULE_THEME)
+ {
+ if (p_ptr->pgod != GOD_MELKOR)
+ {
+ *home1_y = 21;
+ *home1_x = 35;
+ *home2_y = 56;
+ *home2_x = 60;
+ }
+ else
+ {
+ *home1_y = 7;
+ *home1_x = 11;
+ *home2_y = 49;
+ *home2_x = 70;
+ }
+ }
+ else
+ {
+ assert(FALSE); /* Uh, oh */
+ }
+}
+
+/* Print using cmsg_print. */
+static void print_using_cmsg(cptr line, void *dummy)
+{
+ cmsg_print(TERM_YELLOW, line);
+}
+
+/* Print using print_hook. */
+static void print_using_print_hook(cptr line, void *f_)
+{
+ FILE *f = (FILE *) f_;
+ fprintf(f, "%s\n", line);
+}
+
+/* Show directions */
+static void print_directions(bool_ feel_it, void (*pfunc)(cptr, void *), void *pfdata)
+{
+ int home1_y, home1_x;
+ int home2_y, home2_x;
+ const char *home1 = NULL;
+ const char *home2 = NULL;
+ char *home1_axis = NULL;
+ char *home2_axis = NULL;
+ cptr home1_distance = NULL;
+ cptr home2_distance = NULL;
+ cptr feel_it_str = feel_it ? ", I can feel it.'" : ".";
+ char buf[256];
+
+ get_home_coordinates(
+ &home1_y, &home1_x, &home1,
+ &home2_y, &home2_x, &home2);
+
+ home1_axis = compass(home1_y, home1_x, cquest_dung_y, cquest_dung_x);
+ home2_axis = compass(home2_y, home2_x, cquest_dung_y, cquest_dung_x);
+
+ home1_distance = approximate_distance(home1_y, home1_x, cquest_dung_y, cquest_dung_x);
+ home2_distance = approximate_distance(home2_y, home2_x, cquest_dung_y, cquest_dung_x);
+
+ /* Build the message */
+ if (!streq(home1_axis, "close"))
+ {
+ snprintf(buf, sizeof(buf),
+ "The temple lies %s to the %s of %s, ",
+ home1_distance,
+ home1_axis,
+ home1);
+ pfunc(buf, pfdata);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf),
+ "The temple lies very close to %s, ",
+ home1);
+ pfunc(buf, pfdata);
+ }
+
+ if (!streq(home2_axis, "close"))
+ {
+ snprintf(buf, sizeof(buf),
+ "and %s to the %s of %s%s",
+ home2_distance,
+ home2_axis,
+ home2,
+ feel_it_str);
+ pfunc(buf, pfdata);
+ }
+ else
+ {
+ snprintf(buf, sizeof(buf),
+ "and very close to %s%s",
+ home2,
+ feel_it_str);
+ pfunc(buf, pfdata);
+ }
+
+ /* Free dyanmically allocated strings */
+ free(home1_axis);
+ free(home2_axis);
+}
+
+bool_ quest_god_describe(FILE *fff)
+{
+ if (cquest.status == QUEST_STATUS_TAKEN)
+ {
+ fprintf(fff, "#####yGod quest " FMTs32b "!\n", cquest_quests_given);
+ fprintf(fff, "Thou art to find the lost temple of thy God and\n");
+ fprintf(fff, "to retrieve the lost part of the relic for thy God! \n");
+ print_directions(FALSE, print_using_print_hook, fff);
+ fprintf(fff, "\n");
+ }
+
+ return TRUE;
+}
+
+static void quest_god_place_rand_dung()
+{
+ int x = -1, y = -1, tries;
+
+ /* erase old dungeon */
+ if (cquest_quests_given > 0)
+ {
+ wild_map[cquest_dung_y][cquest_dung_x].entrance = 0;
+
+ /* erase old recall level */
+ max_dlv[DUNGEON_GOD] = 0;
+ }
+
+ /* initialise tries variable */
+ tries = 1000;
+ while (tries > 0)
+ {
+ wilderness_map *w_ptr = NULL;
+ wilderness_type_info *wf_ptr = NULL;
+ 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). */
+ x = rand_range(1, max_wild_x-2);
+ y = rand_range(1, max_wild_y-2);
+
+ /* Is there a town/dungeon/potentially impassable feature there, ? */
+ w_ptr = &wild_map[y][x];
+ wf_ptr = &wf_info[w_ptr->feat];
+
+ if ((w_ptr->entrance != 0) ||
+ (wf_ptr->entrance != 0) ||
+ (wf_ptr->terrain_idx == TERRAIN_EDGE) ||
+ (wf_ptr->terrain_idx == TERRAIN_DEEP_WATER) ||
+ (wf_ptr->terrain_idx == TERRAIN_TREES) ||
+ (wf_ptr->terrain_idx == TERRAIN_SHALLOW_LAVA) ||
+ (wf_ptr->terrain_idx == TERRAIN_DEEP_LAVA) ||
+ (wf_ptr->terrain_idx == TERRAIN_MOUNTAIN))
+ {
+ /* try again */
+ }
+ else
+ {
+ /* either player, nor wall, then stop this 'while' */
+ break;
+ }
+ }
+
+ assert(x >= 0);
+ assert(y >= 0);
+
+ if (tries == 0)
+ {
+ /* Use Bree as last resort */
+ x = 32;
+ y = 19;
+ }
+
+ /* create god dungeon in that place */
+ wild_map[y][x].entrance = 1000 + DUNGEON_GOD;
+
+ /* set quest variables */
+ cquest_dung_x = x;
+ cquest_dung_y = y;
+}
+
+static void quest_god_generate_relic()
+{
+ int tries = 1000, x = -1, y = -1;
+ object_type relic;
+
+ tries = 1000;
+
+ while (tries > 0)
+ {
+ cave_type *c_ptr;
+ 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);
+ c_ptr = &cave[y][x];
+
+ /* are the coordinates on a floor, not on a permanent feature (eg stairs), and not on a trap ? */
+ if ((f_info[c_ptr->feat].flags1 & FF1_FLOOR) &&
+ (!(f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) &&
+ (c_ptr->t_idx == 0))
+ {
+ break;
+ }
+ }
+
+ /* create relic */
+ object_prep(&relic, lookup_kind(TV_JUNK, get_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)
+ {
+ /* explain it */
+ cmsg_print(TERM_L_BLUE, "You luckily stumble across the relic on the stairs!");
+
+ if (inven_carry_okay(&relic))
+ {
+ inven_carry(&relic, FALSE);
+ }
+ else
+ {
+ /* no place found, drop it on the stairs */
+ drop_near(&relic, -1, p_ptr->py, p_ptr->px);
+ }
+ }
+ else
+ {
+ /* drop it */
+ drop_near(&relic, -1, y, x);
+ }
+
+ /* Only generate once! */
+ cquest_relic_generated = TRUE;
+
+ /* Reset some variables */
+ cquest_relic_gen_tries = 0;
+}
+
+static void quest_god_set_god_dungeon_attributes_eru()
+{
+ /* The Eru temple is based on Meneltarma. */
+
+ /* W: Not too many monsters (they'll be tough though, with big
+ * levels) */
+ d_info[DUNGEON_GOD].min_m_alloc_level = 14;
+ d_info[DUNGEON_GOD].max_m_alloc_chance = 200;
+
+ /* L: Dirt and grass. More dirt at bottom, more grass at
+ * top. rocky ground would be nice */
+ d_info[DUNGEON_GOD].floor1 = 88;
+ d_info[DUNGEON_GOD].floor2 = 89;
+ d_info[DUNGEON_GOD].floor_percent1[0] = 70;
+ d_info[DUNGEON_GOD].floor_percent2[0] = 30;
+ d_info[DUNGEON_GOD].floor_percent1[1] = 10;
+ d_info[DUNGEON_GOD].floor_percent2[1] = 90;
+
+ /* A: Outer wall mountain chain. other walls granite */
+ d_info[DUNGEON_GOD].fill_type1 = 97;
+ d_info[DUNGEON_GOD].fill_percent1[0] = 100;
+ d_info[DUNGEON_GOD].outer_wall = 57;
+ d_info[DUNGEON_GOD].inner_wall = 97;
+ d_info[DUNGEON_GOD].fill_method = 2;
+
+ /* O: "At Meneltarma no weapon or tool had ever been borne"
+ * (but invaders would have left a small number) */
+ d_info[DUNGEON_GOD].objs.treasure = 45;
+ d_info[DUNGEON_GOD].objs.combat = 5;
+ d_info[DUNGEON_GOD].objs.magic = 45;
+ d_info[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?) */
+ d_info[DUNGEON_GOD].flags1 =
+ DF1_BIG | DF1_NO_DOORS | DF1_CIRCULAR_ROOMS |
+ DF1_EMPTY | DF1_TOWER | DF1_FLAT | DF1_ADJUST_LEVEL_2;
+ d_info[DUNGEON_GOD].flags2 =
+ DF2_ADJUST_LEVEL_1_2 |
+ DF2_NO_SHAFT |
+ DF2_ADJUST_LEVEL_PLAYER;
+
+ /* R: */
+ d_info[DUNGEON_GOD].rules[0].mode = 3;
+ d_info[DUNGEON_GOD].rules[0].percent = 50;
+
+ /* M: We want evil or flying characters */
+ d_info[DUNGEON_GOD].rules[0].mflags3 = RF3_EVIL;
+
+ d_info[DUNGEON_GOD].rules[1].mode = 3;
+ d_info[DUNGEON_GOD].rules[1].percent = 50;
+
+ /* M: We want evil or flying characters */
+ d_info[DUNGEON_GOD].rules[1].mflags7 = RF7_CAN_FLY;
+}
+
+static void quest_god_set_god_dungeon_attributes_manwe()
+{
+ /* Manwe's lost temple is high in the clouds */
+
+ /* W: Has average number of monsters. */
+ d_info[DUNGEON_GOD].min_m_alloc_level = 18;
+ d_info[DUNGEON_GOD].max_m_alloc_chance = 160;
+
+ /* L: floor will be 'cloud-like vapour' and pools of
+ * 'condensing water' */
+ d_info[DUNGEON_GOD].floor1 = 208;
+ d_info[DUNGEON_GOD].floor2 = 209;
+ d_info[DUNGEON_GOD].floor_percent1[0] = 85;
+ d_info[DUNGEON_GOD].floor_percent2[0] = 15;
+
+ /* A: Outer wall is 'hail stone wall', inner wall 'dense
+ * fog'. FIlled at max smoothing, like islands. */
+ d_info[DUNGEON_GOD].fill_type1 = 211;
+ d_info[DUNGEON_GOD].fill_percent1[0] = 100;
+ d_info[DUNGEON_GOD].outer_wall = 210;
+ d_info[DUNGEON_GOD].inner_wall = 211;
+ d_info[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... */
+ d_info[DUNGEON_GOD].objs.treasure = 15;
+ d_info[DUNGEON_GOD].objs.combat = 25;
+ d_info[DUNGEON_GOD].objs.magic = 55;
+ d_info[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). */
+ d_info[DUNGEON_GOD].flags1 =
+ DF1_NO_DOORS | DF1_TOWER |
+ DF1_CAVERN | DF1_ADJUST_LEVEL_2;
+ d_info[DUNGEON_GOD].flags2 =
+ DF2_NO_SHAFT | DF2_ADJUST_LEVEL_PLAYER;
+
+ /* R: */
+ d_info[DUNGEON_GOD].rules[0].mode = 3;
+ d_info[DUNGEON_GOD].rules[0].percent = 20;
+ d_info[DUNGEON_GOD].rules[1].mode = 3;
+ d_info[DUNGEON_GOD].rules[1].percent = 20;
+ d_info[DUNGEON_GOD].rules[2].mode = 3;
+ d_info[DUNGEON_GOD].rules[2].percent = 20;
+ d_info[DUNGEON_GOD].rules[3].mode = 3;
+ d_info[DUNGEON_GOD].rules[3].percent = 20;
+ d_info[DUNGEON_GOD].rules[4].mode = 3;
+ d_info[DUNGEON_GOD].rules[4].percent = 20;
+
+ /* M: We want air(poison-type) or flying characters. Orcs
+ * too. They would have ransacked his elf-loving temple :) */
+ d_info[DUNGEON_GOD].rules[0].mflags2 = RF2_INVISIBLE;
+ d_info[DUNGEON_GOD].rules[1].mflags3 = RF3_ORC | RF3_IM_POIS;
+ d_info[DUNGEON_GOD].rules[2].mflags4 = RF4_BR_POIS | RF4_BR_GRAV;
+ d_info[DUNGEON_GOD].rules[3].mflags5 = RF5_BA_POIS;
+ d_info[DUNGEON_GOD].rules[4].mflags7 = RF7_CAN_FLY;
+}
+
+static void quest_god_set_god_dungeon_attributes_tulkas()
+{
+ /* 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 */
+ d_info[DUNGEON_GOD].min_m_alloc_level = 20;
+ d_info[DUNGEON_GOD].max_m_alloc_chance = 120;
+
+ /* L: floor is normal */
+ d_info[DUNGEON_GOD].floor1 = 1;
+ d_info[DUNGEON_GOD].floor_percent1[0] = 100;
+
+ /* A: Granite walls */
+ d_info[DUNGEON_GOD].fill_type1 = 56;
+ d_info[DUNGEON_GOD].fill_percent1[0] = 100;
+ d_info[DUNGEON_GOD].outer_wall = 58;
+ d_info[DUNGEON_GOD].inner_wall = 57;
+ d_info[DUNGEON_GOD].fill_method = 0;
+
+ /* O: Loads of combat drops */
+ d_info[DUNGEON_GOD].objs.treasure = 10;
+ d_info[DUNGEON_GOD].objs.combat = 70;
+ d_info[DUNGEON_GOD].objs.magic = 5;
+ d_info[DUNGEON_GOD].objs.tools = 15;
+
+ /* F: fairly standard */
+ d_info[DUNGEON_GOD].flags1 = DF1_NO_DESTROY | DF1_ADJUST_LEVEL_2;
+ d_info[DUNGEON_GOD].flags2 = DF2_ADJUST_LEVEL_PLAYER;
+
+ /* R: */
+ d_info[DUNGEON_GOD].rules[0].mode = 3;
+ d_info[DUNGEON_GOD].rules[0].percent = 100;
+
+ /* M: plenty demons please */
+ d_info[DUNGEON_GOD].rules[0].mflags3 = RF3_DEMON | RF3_EVIL;
+}
+
+static void quest_god_set_god_dungeon_attributes_melkor()
+{
+ /* Melkors dungeon will be dark, fiery and stuff */
+
+ /* Many many monsters! (but prob ADJUST_LEVEL_1_2) */
+ d_info[DUNGEON_GOD].min_m_alloc_level = 24;
+ d_info[DUNGEON_GOD].max_m_alloc_chance = 80;
+
+ /* L: floor is dirt/mud/nether */
+ d_info[DUNGEON_GOD].floor1 = 88;
+ d_info[DUNGEON_GOD].floor2 = 94;
+ d_info[DUNGEON_GOD].floor3 = 102;
+ d_info[DUNGEON_GOD].floor_percent1[0] = 45;
+ d_info[DUNGEON_GOD].floor_percent2[0] = 45;
+ d_info[DUNGEON_GOD].floor_percent3[0] = 10;
+ d_info[DUNGEON_GOD].floor_percent1[1] = 35;
+ d_info[DUNGEON_GOD].floor_percent2[1] = 35;
+ d_info[DUNGEON_GOD].floor_percent3[1] = 30;
+
+ /* A: Granite walls to fill but glass walls for room
+ * perimeters (you can see the nasty monsters coming) */
+ d_info[DUNGEON_GOD].fill_type1 = 188;
+ d_info[DUNGEON_GOD].fill_percent1[0] = 100;
+ d_info[DUNGEON_GOD].outer_wall = 188;
+ d_info[DUNGEON_GOD].inner_wall = 57;
+ d_info[DUNGEON_GOD].fill_method = 1;
+
+ /* O: Even drops */
+ d_info[DUNGEON_GOD].objs.treasure = 25;
+ d_info[DUNGEON_GOD].objs.combat = 25;
+ d_info[DUNGEON_GOD].objs.magic = 25;
+ d_info[DUNGEON_GOD].objs.tools = 25;
+
+ /* F: Small, lava rivers, nasty monsters hehehehehe */
+ d_info[DUNGEON_GOD].flags1 = DF1_SMALL | DF1_LAVA_RIVERS | DF1_ADJUST_LEVEL_1;
+ d_info[DUNGEON_GOD].flags2 = DF2_ADJUST_LEVEL_1_2 | DF2_ADJUST_LEVEL_PLAYER;
+
+ /* R: No restrictions on monsters here */
+ d_info[DUNGEON_GOD].rules[0].mode = 0;
+ d_info[DUNGEON_GOD].rules[0].percent = 80;
+
+ /* R: Apart from making sure we have some GOOD ones */
+ d_info[DUNGEON_GOD].rules[1].mode = 3;
+ d_info[DUNGEON_GOD].rules[1].percent = 20;
+
+ /* M: */
+ d_info[DUNGEON_GOD].rules[1].mflags3 = RF3_GOOD;
+}
+
+static void quest_god_set_god_dungeon_attributes_yavanna()
+{
+ /* Yavannas dungeon will be very natural, tress and stuff. */
+
+ d_info[DUNGEON_GOD].min_m_alloc_level = 22;
+ d_info[DUNGEON_GOD].max_m_alloc_chance = 100;
+
+ /* L: floor is grass/flowers, plus dirt so not always
+ * regenerating quick! */
+ d_info[DUNGEON_GOD].floor1 = 89;
+ d_info[DUNGEON_GOD].floor2 = 199;
+ d_info[DUNGEON_GOD].floor3 = 88;
+ d_info[DUNGEON_GOD].floor_percent1[0] = 40;
+ d_info[DUNGEON_GOD].floor_percent2[0] = 15;
+ d_info[DUNGEON_GOD].floor_percent3[0] = 45;
+
+ /* A: Tree walls to fill, small trees for inner walls */
+ d_info[DUNGEON_GOD].fill_type1 = 96;
+ d_info[DUNGEON_GOD].fill_percent1[0] = 100;
+ d_info[DUNGEON_GOD].outer_wall = 202;
+ d_info[DUNGEON_GOD].inner_wall = 96;
+ d_info[DUNGEON_GOD].fill_method = 1;
+
+ /* O: not much combat.. tools where ransackers have tried to
+ * chop trees down. */
+ d_info[DUNGEON_GOD].objs.treasure = 20;
+ d_info[DUNGEON_GOD].objs.combat = 10;
+ d_info[DUNGEON_GOD].objs.magic = 30;
+ d_info[DUNGEON_GOD].objs.tools = 40;
+
+ /* F: Natural looking */
+ d_info[DUNGEON_GOD].flags1 =
+ DF1_NO_DOORS | DF1_WATER_RIVERS |
+ DF1_NO_DESTROY | DF1_ADJUST_LEVEL_1 |
+ DF1_NO_RECALL;
+ d_info[DUNGEON_GOD].flags2 =
+ DF2_ADJUST_LEVEL_1_2 | DF2_NO_SHAFT |
+ DF2_NO_GENO | DF2_ADJUST_LEVEL_PLAYER;
+
+ /* R: Demons, Undead, non-living */
+ d_info[DUNGEON_GOD].rules[0].mode = 3;
+ d_info[DUNGEON_GOD].rules[0].percent = 100;
+
+ /* M: */
+ d_info[DUNGEON_GOD].rules[0].mflags3 =
+ RF3_DEMON | RF3_UNDEAD | RF3_NONLIVING;
+}
+
+static void quest_god_set_god_dungeon_attributes_aule()
+{
+ d_info[DUNGEON_GOD].min_m_alloc_level = 24;
+ d_info[DUNGEON_GOD].max_m_alloc_chance = 80;
+
+ /* L: floor is dirt/mud/shallow water */
+ d_info[DUNGEON_GOD].floor1 = 88;
+ d_info[DUNGEON_GOD].floor2 = 94;
+ d_info[DUNGEON_GOD].floor3 = 84;
+ d_info[DUNGEON_GOD].floor_percent1[0] = 45;
+ d_info[DUNGEON_GOD].floor_percent2[0] = 45;
+ d_info[DUNGEON_GOD].floor_percent3[0] = 10;
+ d_info[DUNGEON_GOD].floor_percent1[1] = 35;
+ d_info[DUNGEON_GOD].floor_percent2[1] = 35;
+ d_info[DUNGEON_GOD].floor_percent3[1] = 30;
+
+ /* A: Grey mountains, inner walls are low hills */
+ d_info[DUNGEON_GOD].fill_type1 = 216;
+ d_info[DUNGEON_GOD].fill_percent1[0] = 100;
+ d_info[DUNGEON_GOD].outer_wall = 216;
+ d_info[DUNGEON_GOD].inner_wall = 213;
+ d_info[DUNGEON_GOD].fill_method = 1;
+
+ /* O: Weapons and tools only */
+ d_info[DUNGEON_GOD].objs.treasure = 0;
+ d_info[DUNGEON_GOD].objs.combat = 50;
+ d_info[DUNGEON_GOD].objs.magic = 0;
+ d_info[DUNGEON_GOD].objs.tools = 50;
+
+ /* F: Small, no destroyed levels, min monster level = dungeon
+ * level */
+ d_info[DUNGEON_GOD].flags1 =
+ DF1_SMALL | DF1_NO_DESTROY |
+ DF1_ADJUST_LEVEL_1 | DF1_NO_STREAMERS;
+
+ /* R: No restrictions on monsters here */
+ d_info[DUNGEON_GOD].rules[0].mode = 0;
+ d_info[DUNGEON_GOD].rules[0].percent = 80;
+}
+
+static void quest_god_set_god_dungeon_attributes_varda()
+{
+ /* Varda lives with Manwe, so high in the clouds */
+
+ /* W: Has average number of monsters. */
+ d_info[DUNGEON_GOD].min_m_alloc_level = 18;
+ d_info[DUNGEON_GOD].max_m_alloc_chance = 160;
+
+ /* L: floor will be grass and flowers */
+ d_info[DUNGEON_GOD].floor1 = 89;
+ d_info[DUNGEON_GOD].floor2 = 82;
+ d_info[DUNGEON_GOD].floor_percent1[0] = 85;
+ d_info[DUNGEON_GOD].floor_percent2[0] = 15;
+
+ /* A: Outer wall is 'hail stone wall', inner wall 'dense
+ * fog'. Filled at max smoothing, like islands. */
+ d_info[DUNGEON_GOD].fill_type1 = 211;
+ d_info[DUNGEON_GOD].fill_percent1[0] = 100;
+ d_info[DUNGEON_GOD].outer_wall = 210;
+ d_info[DUNGEON_GOD].inner_wall = 211;
+ d_info[DUNGEON_GOD].fill_method = 4;
+
+ /* O: Varda likes magical items and tools, not much treasure
+ * or weapons */
+ d_info[DUNGEON_GOD].objs.treasure = 15;
+ d_info[DUNGEON_GOD].objs.combat = 5;
+ d_info[DUNGEON_GOD].objs.magic = 55;
+ d_info[DUNGEON_GOD].objs.tools = 25;
+
+ /* F: It's open, goes up like a tower, give it a few
+ * interesting rooms, make the monsters hard(ish). */
+ d_info[DUNGEON_GOD].flags1 =
+ DF1_NO_DOORS | DF1_TOWER |
+ DF1_CAVERN | DF1_ADJUST_LEVEL_1;
+ d_info[DUNGEON_GOD].flags2 =
+ DF2_NO_SHAFT | DF2_ADJUST_LEVEL_PLAYER;
+
+ /* R: */
+ d_info[DUNGEON_GOD].rules[0].mode = 3;
+ d_info[DUNGEON_GOD].rules[0].percent = 20;
+ d_info[DUNGEON_GOD].rules[1].mode = 3;
+ d_info[DUNGEON_GOD].rules[1].percent = 20;
+ d_info[DUNGEON_GOD].rules[2].mode = 3;
+ d_info[DUNGEON_GOD].rules[2].percent = 20;
+ d_info[DUNGEON_GOD].rules[3].mode = 3;
+ d_info[DUNGEON_GOD].rules[3].percent = 20;
+ d_info[DUNGEON_GOD].rules[4].mode = 3;
+ d_info[DUNGEON_GOD].rules[4].percent = 20;
+
+ /* M: We want air(poison-type) or flying characters. Orcs too. */
+ d_info[DUNGEON_GOD].rules[0].mflags2 = RF2_INVISIBLE;
+ d_info[DUNGEON_GOD].rules[1].mflags3 = RF3_ORC | RF3_IM_POIS;
+ d_info[DUNGEON_GOD].rules[2].mflags4 = RF4_BR_POIS | RF4_BR_GRAV;
+ d_info[DUNGEON_GOD].rules[3].mflags5 = RF5_BA_POIS;
+ d_info[DUNGEON_GOD].rules[4].mflags7 = RF7_CAN_FLY;
+}
+
+static void quest_god_set_god_dungeon_attributes_ulmo()
+{
+ /* Ulmo dungeon is basically Tulkas, except with acquatic creatures. */
+
+ /* W: but with lots of monsters */
+ d_info[DUNGEON_GOD].min_m_alloc_level = 20;
+ d_info[DUNGEON_GOD].max_m_alloc_chance = 120;
+
+ /* L: floor is dirt */
+ d_info[DUNGEON_GOD].floor1 = 88;
+ d_info[DUNGEON_GOD].floor_percent1[0] = 100;
+
+ /* A: Cheat: walls are water. */
+ d_info[DUNGEON_GOD].fill_type1 = 187;
+ d_info[DUNGEON_GOD].fill_percent1[0] = 100;
+ d_info[DUNGEON_GOD].outer_wall = 238;
+ d_info[DUNGEON_GOD].inner_wall = 84;
+ d_info[DUNGEON_GOD].fill_method = 0;
+
+ /* O: Lots of treasure, not much else. */
+ d_info[DUNGEON_GOD].objs.treasure = 90;
+ d_info[DUNGEON_GOD].objs.combat = 0;
+ d_info[DUNGEON_GOD].objs.magic = 5;
+ d_info[DUNGEON_GOD].objs.tools = 5;
+
+ /* F: fairly standard */
+ d_info[DUNGEON_GOD].flags1 = DF1_NO_DESTROY | DF1_ADJUST_LEVEL_2;
+ d_info[DUNGEON_GOD].flags2 = DF2_ADJUST_LEVEL_PLAYER;
+
+ /* R: */
+ d_info[DUNGEON_GOD].rules[0].mode = 3;
+ d_info[DUNGEON_GOD].rules[0].percent = 35;
+ d_info[DUNGEON_GOD].rules[1].mode = 3;
+ d_info[DUNGEON_GOD].rules[1].percent = 30;
+ d_info[DUNGEON_GOD].rules[2].mode = 3;
+ d_info[DUNGEON_GOD].rules[2].percent = 30;
+
+ /* M: Aquatic creatures only. */
+ d_info[DUNGEON_GOD].rules[0].mflags3 = RF7_CAN_FLY;
+ d_info[DUNGEON_GOD].rules[1].mflags3 = RF7_AQUATIC;
+ d_info[DUNGEON_GOD].rules[2].mflags3 = RF3_RES_WATE;
+}
+
+static void quest_god_set_god_dungeon_attributes_mandos()
+{
+ /* Mandos dungeon is basically Tulkas, except with undead. */
+
+ /* W: but with lots of monsters */
+ d_info[DUNGEON_GOD].min_m_alloc_level = 20;
+ d_info[DUNGEON_GOD].max_m_alloc_chance = 120;
+
+ /* L: floor is normal */
+ d_info[DUNGEON_GOD].floor1 = 1;
+ d_info[DUNGEON_GOD].floor_percent1[0] = 100;
+
+ /* A: Granite walls */
+ d_info[DUNGEON_GOD].fill_type1 = 56;
+ d_info[DUNGEON_GOD].fill_percent1[0] = 100;
+ d_info[DUNGEON_GOD].outer_wall = 58;
+ d_info[DUNGEON_GOD].inner_wall = 57;
+ d_info[DUNGEON_GOD].fill_method = 0;
+
+ /* O: Loads of combat drops */
+ d_info[DUNGEON_GOD].objs.treasure = 10;
+ d_info[DUNGEON_GOD].objs.combat = 70;
+ d_info[DUNGEON_GOD].objs.magic = 5;
+ d_info[DUNGEON_GOD].objs.tools = 15;
+
+ /* F: fairly standard */
+ d_info[DUNGEON_GOD].flags1 = DF1_NO_DESTROY | DF1_ADJUST_LEVEL_2;
+ d_info[DUNGEON_GOD].flags2 = DF2_ADJUST_LEVEL_PLAYER;
+
+ /* R: */
+ d_info[DUNGEON_GOD].rules[0].mode = 3;
+ d_info[DUNGEON_GOD].rules[0].percent = 100;
+
+ /* M: vampires! */
+ d_info[DUNGEON_GOD].rules[0].r_char[0] = 'V';
+ d_info[DUNGEON_GOD].rules[0].r_char[1] = '\0';
+ d_info[DUNGEON_GOD].rules[0].r_char[2] = '\0';
+ d_info[DUNGEON_GOD].rules[0].r_char[3] = '\0';
+ d_info[DUNGEON_GOD].rules[0].r_char[4] = '\0';
+ d_info[DUNGEON_GOD].rules[0].mflags3 = RF3_UNDEAD | RF3_EVIL;
+}
+
+static bool_ quest_god_level_end_gen_hook(void *, void *, void *)
+{
+ /* Check for dungeon */
+ if ((dungeon_type != DUNGEON_GOD) ||
+ (cquest.status == QUEST_STATUS_UNTAKEN))
+ {
+ return FALSE;
+ }
+
+ /* 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.*/
+
+ else if ((cquest_relic_generated == TRUE) &&
+ (cquest.status != QUEST_STATUS_FAILED))
+ {
+ /* fail the quest, don't give another one, don't give
+ * this message again */
+ cquest.status = QUEST_STATUS_FAILED;
+
+ /* God issues instructions */
+ cmsg_format(TERM_L_BLUE, "The voice of %s booms in your head:", deity_info[p_ptr->pgod].name);
+
+ 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!'");
+ }
+
+ /* Force relic generation on 5th attempt if others have been
+ * unsuccessful. */
+
+ else if ((cquest_relic_gen_tries == 4) &&
+ (cquest_relic_generated == FALSE))
+ {
+ quest_god_generate_relic();
+ }
+
+ else
+ {
+ /* 1/5 chance of generation */
+ if (magik(20))
+ {
+ quest_god_generate_relic();
+ }
+ else
+ {
+ cquest_relic_gen_tries = cquest_relic_gen_tries + 1;
+ }
+ }
+
+ return FALSE;
+}
+
+static bool_ quest_god_player_level_hook(void *, void *in_, void *)
+{
+ struct hook_player_level_in *in = static_cast<struct hook_player_level_in *>(in_);
+ s32b gained = in->gained_levels;
+
+ if (gained <= 0)
+ {
+ return FALSE;
+ }
+
+ /* check player is worshipping a god, not already on a god quest. */
+ if ((p_ptr->astral) ||
+ (p_ptr->pgod <= 0) ||
+ (cquest.status == QUEST_STATUS_TAKEN) ||
+ (cquest.status == QUEST_STATUS_FAILED) ||
+ (cquest_quests_given >= MAX_NUM_GOD_QUESTS()) ||
+ (magik(CHANCE_OF_GOD_QUEST) == FALSE) ||
+ ((dungeon_type == DUNGEON_GOD) &&
+ (dun_level > 0)) ||
+ (p_ptr->lev <= cquest_dun_minplev))
+ {
+ /* Don't let a player get quests with trickery */
+ if (p_ptr->lev > cquest_dun_minplev)
+ {
+ cquest_dun_minplev = p_ptr->lev;
+ }
+ return FALSE;
+ }
+ else
+ {
+ /* This var will need resetting */
+ cquest.status = QUEST_STATUS_TAKEN;
+ cquest_relic_generated = FALSE;
+ cquest_quests_given = cquest_quests_given + 1;
+
+ /* actually place the dungeon in a random place */
+ quest_god_place_rand_dung();
+
+ /* God issues instructions */
+ cmsg_format(TERM_L_BLUE, "The voice of %s booms in your head:", deity_info[p_ptr->pgod].name);
+
+ 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!");
+
+ print_directions(TRUE, print_using_cmsg, NULL);
+
+ /* 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! */
+ cquest_dun_mindepth = p_ptr->lev*2/3;
+ cquest_dun_maxdepth = cquest_dun_mindepth + 4;
+ }
+
+ return FALSE;
+}
+
+static bool_ quest_god_get_hook(void *, void *in_, void *)
+{
+ hook_get_in *in = static_cast<hook_get_in *>(in_);
+
+ s32b item = -in->o_idx; /* Note the negation */
+
+ object_type *o_ptr = get_object(item);
+
+ /* -- Is it the relic, and check to make sure the relic hasn't already been identified */
+ if ((cquest.status == QUEST_STATUS_TAKEN) &&
+ (o_ptr->tval == TV_JUNK) &&
+ (o_ptr->sval == get_relic_num()) &&
+ (o_ptr->pval != TRUE) &&
+ (cquest_relics_found < cquest_quests_given))
+ {
+ cmsg_format(TERM_L_BLUE, "%s speaks to you:", deity_info[p_ptr->pgod].name);
+
+ /* Is it the last piece of the relic? */
+ if (cquest_quests_given == MAX_NUM_GOD_QUESTS())
+ {
+ 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!'");
+ s_info[SKILL_PRAY].value += (10 * s_info[SKILL_PRAY].mod);
+ }
+ 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'");
+
+ /* reward player by increasing prayer skill */
+ cmsg_print(TERM_YELLOW, "'As a reward, I shall teach thee how to pray better'");
+ s_info[SKILL_PRAY].value += (5 * s_info[SKILL_PRAY].mod);
+ }
+
+ /* Take the relic piece */
+ inc_stack_size_ex(item, -1, OPTIMIZE, NO_DESCRIBE);
+
+ /* relic piece has been identified */
+ o_ptr->pval = TRUE;
+ cquest_relics_found = cquest_relics_found + 1;
+
+ /* Make sure quests can be given again if neccesary */
+ cquest.status = QUEST_STATUS_UNTAKEN;
+
+ /* Prevent further processing of 'take' action; we've
+ destroyed the item. */
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static bool_ quest_god_char_dump_hook(void *, void *in_, void *)
+{
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (cquest_quests_given > 0)
+ {
+ int relics = cquest_relics_found;
+ char relics_text[128];
+ cptr append_text = "";
+
+ snprintf(relics_text, sizeof(relics_text), "%d", relics);
+
+ if (relics == MAX_NUM_GOD_QUESTS())
+ {
+ strcpy(relics_text, "all");
+ append_text = " and pleased your god";
+ }
+ else
+ {
+ if (relics == 0)
+ {
+ strcpy(relics_text, "none");
+ }
+ if (cquest.status == QUEST_STATUS_FAILED)
+ {
+ append_text = " and failed in your quest";
+ }
+ }
+
+ fprintf(f, "\n You found %s of the relic pieces%s.", relics_text, append_text);
+ }
+
+ return FALSE;
+}
+
+static void set_god_dungeon_attributes()
+{
+ /* dungeon properties altered according to which god player is worshipping, */
+ if (p_ptr->pgod == GOD_ERU)
+ {
+ quest_god_set_god_dungeon_attributes_eru();
+ }
+ else if (p_ptr->pgod == GOD_MANWE)
+ {
+ quest_god_set_god_dungeon_attributes_manwe();
+ }
+ else if (p_ptr->pgod == GOD_TULKAS)
+ {
+ quest_god_set_god_dungeon_attributes_tulkas();
+ }
+ else if (p_ptr->pgod == GOD_MELKOR)
+ {
+ quest_god_set_god_dungeon_attributes_melkor();
+ }
+ else if (p_ptr->pgod == GOD_YAVANNA)
+ {
+ quest_god_set_god_dungeon_attributes_yavanna();
+ }
+ else if (p_ptr->pgod == GOD_AULE)
+ {
+ quest_god_set_god_dungeon_attributes_aule();
+ }
+ else if (p_ptr->pgod == GOD_VARDA)
+ {
+ quest_god_set_god_dungeon_attributes_varda();
+ }
+ else if (p_ptr->pgod == GOD_ULMO)
+ {
+ quest_god_set_god_dungeon_attributes_ulmo();
+ }
+ else if (p_ptr->pgod == GOD_MANDOS)
+ {
+ quest_god_set_god_dungeon_attributes_mandos();
+ }
+ else
+ {
+ assert(FALSE); /* Uh, oh! */
+ }
+
+ /* W: All dungeons are 5 levels deep, and created at 2/3 of
+ * the player clvl when the quest is given */
+ {
+ dungeon_info_type *d_ptr = &d_info[DUNGEON_GOD];
+ d_ptr->mindepth = cquest_dun_mindepth;
+ d_ptr->maxdepth = cquest_dun_maxdepth;
+ d_ptr->min_plev = cquest_dun_minplev;
+ }
+}
+
+static void quest_god_dungeon_setup(int d_idx)
+{
+ /* call the function to set the dungeon variables (dependant
+ * on pgod) the first time we enter the dungeon */
+ if (d_idx != DUNGEON_GOD)
+ {
+ return;
+ }
+
+ set_god_dungeon_attributes();
+}
+
+static bool_ quest_god_enter_dungeon_hook(void *, void *in_, void *)
+{
+ struct hook_enter_dungeon_in *in = static_cast<struct hook_enter_dungeon_in *>(in_);
+ quest_god_dungeon_setup(in->d_idx);
+ return FALSE;
+}
+
+static bool_ quest_god_gen_level_begin_hook(void *, void *, void *)
+{
+ quest_god_dungeon_setup(dungeon_type);
+ return FALSE;
+}
+
+static bool_ quest_god_stair_hook(void *, void *, void *)
+{
+ quest_god_dungeon_setup(dungeon_type);
+ return FALSE;
+}
+
+static bool_ quest_god_birth_objects_hook(void *, void *, void *)
+{
+ cquest_quests_given = 0;
+ cquest_relics_found = 0;
+ cquest_dun_mindepth = 1;
+ cquest_dun_maxdepth = 4;
+ cquest_dun_minplev = 0;
+ cquest_relic_gen_tries = 0;
+ cquest_relic_generated = FALSE;
+
+ return FALSE;
+}
+
+bool_ quest_god_init_hook(int q)
+{
+ /* Only need hooks if the quest is unfinished. */
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) &&
+ (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook_new(HOOK_LEVEL_END_GEN, quest_god_level_end_gen_hook, "q_god_level_end_gen", NULL);
+ add_hook_new(HOOK_ENTER_DUNGEON, quest_god_enter_dungeon_hook, "q_god_enter_dungeon", NULL);
+ add_hook_new(HOOK_GEN_LEVEL_BEGIN, quest_god_gen_level_begin_hook, "q_god_gen_level_begin", NULL);
+ add_hook_new(HOOK_STAIR, quest_god_stair_hook, "q_god_hook_stair", NULL);
+ add_hook_new(HOOK_GET, quest_god_get_hook, "q_god_get", NULL);
+ add_hook_new(HOOK_CHAR_DUMP, quest_god_char_dump_hook, "q_god_char_dump", NULL);
+ add_hook_new(HOOK_PLAYER_LEVEL, quest_god_player_level_hook, "q_god_player_level", NULL);
+ }
+
+ /* Need this to re-initialize at birth; the quest data is
+ * zeroed which isn't quite right. */
+ add_hook_new(HOOK_BIRTH_OBJECTS, quest_god_birth_objects_hook, "q_god_birth_objects", NULL);
+
+ return FALSE;
+}
diff --git a/src/q_god.hpp b/src/q_god.hpp
new file mode 100644
index 00000000..d9513bdb
--- /dev/null
+++ b/src/q_god.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_god_describe(FILE *);
+bool_ quest_god_init_hook(int q);
diff --git a/src/q_haunted.cc b/src/q_haunted.cc
new file mode 100644
index 00000000..57daa40e
--- /dev/null
+++ b/src/q_haunted.cc
@@ -0,0 +1,163 @@
+#include "q_haunted.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "feature_type.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "player_type.hpp"
+#include "traps.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#define cquest (quest[QUEST_HAUNTED])
+
+static bool_ quest_haunted_gen_hook(void *, void *, void *)
+{
+ int x, y, i, m_idx;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_HAUNTED) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("haunted.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some ghosts */
+ for (i = 12; i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ m_idx = place_monster_one(y, x, 477, 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ /* Place some random monsters to haunt us */
+ for (i = damroll(4, 4); i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ int monsters[22] = { 65, 100, 124, 125, 133, 231, 273, 327, 365, 416, 418,
+ 507, 508, 533, 534, 553, 554, 555, 577, 607, 622, 665};
+ int monster = monsters[rand_int(22)];
+ m_idx = place_monster_one(y, x, monster, 0, FALSE, MSTATUS_ENEMY);
+ m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ /* Place some random traps */
+ for (i = 10 + damroll(4, 4); i > 0; )
+ {
+ int flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ --i;
+ place_trap(y, x);
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_haunted_death_hook(void *, void *, void *)
+{
+ 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_new(HOOK_MONSTER_DEATH, quest_haunted_death_hook);
+ del_hook_new(HOOK_GEN_QUEST, quest_haunted_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Minas Anor is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+static bool_ quest_haunted_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_new(HOOK_MONSTER_DEATH, quest_haunted_death_hook, "haunted_monster_death", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_haunted_finish_hook, "haunted_finish", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_haunted_gen_hook, "haunted_geb", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_haunted.hpp b/src/q_haunted.hpp
new file mode 100644
index 00000000..4f51bce3
--- /dev/null
+++ b/src/q_haunted.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_haunted_init_hook(int q_idx);
diff --git a/src/q_hobbit.cc b/src/q_hobbit.cc
new file mode 100644
index 00000000..5a71711e
--- /dev/null
+++ b/src/q_hobbit.cc
@@ -0,0 +1,230 @@
+#include "q_hobbit.hpp"
+
+#include "cave.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_chat_in.hpp"
+#include "hook_give_in.hpp"
+#include "hook_mon_speak_in.hpp"
+#include "hook_wild_gen_in.hpp"
+#include "hooks.hpp"
+#include "messages.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define cquest (quest[QUEST_HOBBIT])
+
+GENERATE_MONSTER_LOOKUP_FN(get_melinda_proudfoot, "Melinda Proudfoot")
+GENERATE_MONSTER_LOOKUP_FN(get_merton_proudfoot, "Merton Proudfoot, the lost hobbit")
+
+static bool_ quest_hobbit_town_gen_hook(void *, void *in_, void *)
+{
+ struct hook_wild_gen_in *in = static_cast<struct hook_wild_gen_in *>(in_);
+ int x = 1, y = 1, tries = 10000;
+ bool_ small = in->small;
+
+ if ((turn < (cquest.data[1] + (DAY * 10L))) || (cquest.status > QUEST_STATUS_COMPLETED) || (small) || (p_ptr->town_num != 1)) return (FALSE);
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(20) + (cur_hgt / 2) - 10;
+ x = randint(20) + (cur_wid / 2) - 10;
+
+ /* Is it a good spot ? */
+ /* Not in player los, and avoid shop grids */
+ if (!los(p_ptr->py, p_ptr->px, y, x) && cave_empty_bold(y, x) &&
+ cave_plain_floor_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place Melinda */
+ int r_idx = get_melinda_proudfoot();
+ m_allow_special[r_idx] = TRUE;
+ place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[r_idx] = FALSE;
+
+ return FALSE;
+}
+
+static bool_ quest_hobbit_gen_hook(void *, void *, void *)
+{
+ int x = 1, y = 1, tries = 10000;
+
+ if ((cquest.status != QUEST_STATUS_TAKEN) || (dun_level != cquest.data[0]) || (dungeon_type != DUNGEON_MAZE)) return FALSE;
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(cur_hgt - 4) + 2;
+ x = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place the hobbit */
+ int r_idx = get_merton_proudfoot();
+ m_allow_special[r_idx] = TRUE;
+ place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_FRIEND);
+ m_allow_special[r_idx] = FALSE;
+
+ return FALSE;
+}
+
+static bool_ quest_hobbit_give_hook(void *, void *in_, void *)
+{
+ struct hook_give_in *in = static_cast<struct hook_give_in *>(in_);
+ object_type *o_ptr;
+ monster_type *m_ptr;
+ s32b m_idx = in->m_idx;
+ s32b item = in->item;
+
+ o_ptr = &p_ptr->inventory[item];
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != get_merton_proudfoot()) return (FALSE);
+
+ if ((o_ptr->tval != TV_SCROLL) || (o_ptr->sval != SV_SCROLL_WORD_OF_RECALL)) return (FALSE);
+
+ msg_print("'Oh, thank you, noble one!'");
+ msg_print("Merton Proudfoot reads the scroll and is recalled to the safety of his home.");
+
+ delete_monster_idx(m_idx);
+
+ inc_stack_size_ex(item, -1, OPTIMIZE, NO_DESCRIBE);
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ del_hook_new(HOOK_GIVE, quest_hobbit_give_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_hobbit_speak_hook(void *, void *in_, void *)
+{
+ struct hook_mon_speak_in *in = static_cast<struct hook_mon_speak_in *>(in_);
+ s32b m_idx = in->m_idx;
+
+ if (m_list[m_idx].r_idx != get_melinda_proudfoot())
+ {
+ return (FALSE);
+ }
+
+ if (cquest.status < QUEST_STATUS_COMPLETED)
+ {
+ msg_format("%^s begs for your help.", in->m_name);
+ }
+ return (TRUE);
+}
+
+static bool_ quest_hobbit_chat_hook(void *, void *in_, void *)
+{
+ struct hook_chat_in *in = static_cast<struct hook_chat_in *>(in_);
+ s32b m_idx = in->m_idx;
+ monster_type *m_ptr;
+
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != get_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_new(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;
+}
+
+static bool_ quest_hobbit_dump_hook(void *, void *in_, void *)
+{
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(f, "\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(format("Hobbit level %d", cquest.data[0]), TERM_BLUE);
+ }
+ }
+
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook_new(HOOK_GIVE, quest_hobbit_give_hook, "hobbit_give", NULL);
+ add_hook_new(HOOK_GEN_LEVEL, quest_hobbit_gen_hook, "hobbit_gen", NULL);
+ add_hook_new(HOOK_WILD_GEN, quest_hobbit_town_gen_hook, "hobbit_town_gen", NULL);
+ add_hook_new(HOOK_CHAT, quest_hobbit_chat_hook, "hobbit_chat", NULL);
+ add_hook_new(HOOK_MON_SPEAK, quest_hobbit_speak_hook, "hobbit_speak", NULL);
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook_new(HOOK_MON_SPEAK, quest_hobbit_speak_hook, "hobbit_speak", NULL);
+ add_hook_new(HOOK_WILD_GEN, quest_hobbit_town_gen_hook, "hobbit_town_gen", NULL);
+ add_hook_new(HOOK_CHAT, quest_hobbit_chat_hook, "hobbit_chat", NULL);
+ }
+ add_hook_new(HOOK_CHAR_DUMP, quest_hobbit_dump_hook, "hobbit_dump", NULL);
+ return (FALSE);
+}
diff --git a/src/q_hobbit.hpp b/src/q_hobbit.hpp
new file mode 100644
index 00000000..b1878748
--- /dev/null
+++ b/src/q_hobbit.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_hobbit_init_hook(int q_idx);
diff --git a/src/q_invas.cc b/src/q_invas.cc
new file mode 100644
index 00000000..64483d28
--- /dev/null
+++ b/src/q_invas.cc
@@ -0,0 +1,222 @@
+#include "q_invas.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hook_monster_ai_in.hpp"
+#include "hook_monster_ai_out.hpp"
+#include "hook_stair_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#define cquest (quest[QUEST_INVASION])
+
+static bool_ quest_invasion_gen_hook(void *, void *, void *)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("maeglin.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ cquest.data[0] = y;
+ cquest.data[1] = x;
+ p_ptr->py = y;
+ p_ptr->px = x;
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_LESS);
+ }
+ }
+
+ return TRUE;
+}
+
+static bool_ quest_invasion_ai_hook(void *, void *in_, void *out_)
+{
+ struct hook_monster_ai_in *in = static_cast<struct hook_monster_ai_in *>(in_);
+ struct hook_monster_ai_out *out = static_cast<struct hook_monster_ai_out *>(out_);
+ monster_type *m_ptr = in->m_ptr;
+ s32b m_idx = in->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
+ {
+ out->y = cquest.data[0];
+ out->x = cquest.data[1];
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+static bool_ quest_invasion_turn_hook(void *, void *, void *)
+{
+ 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 */
+ cmsg_print(TERM_YELLOW, "A Thunderlord appears in front of you and says:");
+ cmsg_print(TERM_YELLOW, "'Hello, noble hero. I am Liron, rider of Tolan. Turgon, King of Gondolin sent me.'");
+ cmsg_print(TERM_YELLOW, "'Gondolin is being invaded; he needs your help now or everything will be lost.'");
+ cmsg_print(TERM_YELLOW, "'Please come quickly!'");
+
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ quest_invasion_init_hook(QUEST_INVASION);
+ del_hook_new(HOOK_END_TURN, quest_invasion_turn_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+}
+
+static bool_ quest_invasion_dump_hook(void *, void *in_, void *)
+{
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (cquest.status == QUEST_STATUS_FAILED)
+ {
+ fprintf(f, "\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(f, "\n You saved Gondolin from destruction.");
+ }
+ return (FALSE);
+}
+
+static bool_ quest_invasion_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ s32b 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_new(HOOK_MONSTER_DEATH, quest_invasion_death_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+ }
+
+ return FALSE;
+}
+
+static bool_ quest_invasion_stair_hook(void *, void *in_, void *)
+{
+ struct hook_stair_in *in = static_cast<struct hook_stair_in *>(in_);
+
+ if (p_ptr->inside_quest != QUEST_INVASION) return FALSE;
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_LESS) return TRUE;
+
+ if (in->direction == STAIRS_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_new(HOOK_STAIR, quest_invasion_stair_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ return TRUE;
+}
+
+bool_ quest_invasion_init_hook(int q_idx)
+{
+ add_hook_new(HOOK_END_TURN, quest_invasion_turn_hook, "invasion_turn", NULL);
+ add_hook_new(HOOK_CHAR_DUMP, quest_invasion_dump_hook, "invasion_dump", NULL);
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook_new(HOOK_MONSTER_AI, quest_invasion_ai_hook, "invasion_ai", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_invasion_gen_hook, "invasion_gen", NULL);
+ add_hook_new(HOOK_MONSTER_DEATH, quest_invasion_death_hook, "invasion_death", NULL);
+ add_hook_new(HOOK_STAIR, quest_invasion_stair_hook, "invasion_stair", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_invas.hpp b/src/q_invas.hpp
new file mode 100644
index 00000000..87b8fc77
--- /dev/null
+++ b/src/q_invas.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_invasion_init_hook(int q_idx);
diff --git a/src/q_library.cc b/src/q_library.cc
new file mode 100644
index 00000000..52a67291
--- /dev/null
+++ b/src/q_library.cc
@@ -0,0 +1,526 @@
+#include "q_library.hpp"
+
+#include "cave_type.hpp"
+#include "hooks.hpp"
+#include "lua_bind.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "spells3.hpp"
+#include "spells4.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define cquest (quest[QUEST_LIBRARY])
+
+#define print_hook(fmt,...) do { fprintf(hook_file, fmt, ##__VA_ARGS__); } while (0)
+
+#define MONSTER_LICH 518
+#define MONSTER_MONASTIC_LICH 611
+#define MONSTER_FLESH_GOLEM 256
+#define MONSTER_CLAY_GOLEM 261
+#define MONSTER_IRON_GOLEM 367
+#define MONSTER_MITHRIL_GOLEM 464
+
+#define MAX_BOOKABLE_SPELLS 128
+
+static s16b bookable_spells[MAX_BOOKABLE_SPELLS];
+static s16b bookable_spells_size = 0;
+
+static void push_spell(s16b spell_idx)
+{
+ assert(bookable_spells_size < MAX_BOOKABLE_SPELLS);
+ bookable_spells[bookable_spells_size++] = spell_idx;
+}
+
+void initialize_bookable_spells()
+{
+ push_spell(MANATHRUST);
+ push_spell(DELCURSES);
+ push_spell(GLOBELIGHT);
+ push_spell(FIREGOLEM);
+ push_spell(FIREFLASH);
+ push_spell(FIREWALL);
+ push_spell(GEYSER);
+ push_spell(VAPOR);
+ push_spell(ENTPOTION);
+ push_spell(NOXIOUSCLOUD);
+ push_spell(POISONBLOOD);
+ push_spell(STONESKIN);
+ push_spell(DIG);
+ push_spell(RECHARGE);
+ push_spell(DISPERSEMAGIC);
+ push_spell(BLINK);
+ push_spell(DISARM);
+ push_spell(TELEPORT);
+ push_spell(SENSEMONSTERS);
+ push_spell(SENSEHIDDEN);
+ push_spell(REVEALWAYS);
+ push_spell(IDENTIFY);
+ push_spell(VISION);
+ push_spell(MAGELOCK);
+ push_spell(SLOWMONSTER);
+ push_spell(ESSENCESPEED);
+ push_spell(CHARM);
+ push_spell(CONFUSE);
+ push_spell(ARMOROFFEAR);
+ push_spell(STUN);
+ push_spell(GROWTREE);
+ push_spell(HEALING);
+ push_spell(RECOVERY);
+ push_spell(ERU_SEE);
+ push_spell(ERU_LISTEN);
+ push_spell(MANWE_BLESS);
+ push_spell(MANWE_SHIELD);
+ push_spell(YAVANNA_CHARM_ANIMAL);
+ push_spell(YAVANNA_GROW_GRASS);
+ push_spell(YAVANNA_TREE_ROOTS);
+ push_spell(TULKAS_AIM);
+ push_spell(TULKAS_SPIN);
+ push_spell(MELKOR_CURSE);
+ push_spell(MELKOR_CORPSE_EXPLOSION);
+ push_spell(DRAIN);
+
+ if (game_module_idx == MODULE_THEME)
+ {
+ push_spell(AULE_FIREBRAND);
+ push_spell(AULE_CHILD);
+ push_spell(VARDA_LIGHT_VALINOR);
+ push_spell(VARDA_EVENSTAR);
+ push_spell(ULMO_BELEGAER);
+ push_spell(ULMO_WRATH);
+ push_spell(MANDOS_TEARS_LUTHIEN);
+ push_spell(MANDOS_TALE_DOOM);
+ }
+}
+
+static s16b library_quest_place_random(int minY, int minX, int maxY, int maxX, int r_idx)
+{
+ int y = randint(maxY - minY + 1) + minY;
+ int x = randint(maxX - minX + 1) + minX;
+ return place_monster_one(y, x, r_idx, 0, TRUE, MSTATUS_ENEMY);
+}
+
+static void library_quest_place_nrandom(int minY, int minX, int maxY, int maxX, int r_idx, int n)
+{
+ while(n > 0)
+ {
+ if (0 < library_quest_place_random(minY, minX, maxY, maxX, r_idx))
+ {
+ n--;
+ }
+ }
+}
+
+static s32b library_quest_book_get_slot(int slot)
+{
+ return cquest.data[slot-1];
+}
+
+static void library_quest_book_set_slot(int slot, s32b spell)
+{
+ cquest.data[slot-1] = spell;
+}
+
+static int library_quest_book_slots_left()
+{
+ if (library_quest_book_get_slot(1) == -1) {
+ return 3;
+ } else if (library_quest_book_get_slot(2) == -1) {
+ return 2;
+ } else if (library_quest_book_get_slot(3) == -1) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static bool_ library_quest_book_contains_spell(int spell)
+{
+ int i;
+ for (i = 1; i <= 3; i++)
+ {
+ if (library_quest_book_get_slot(i) == spell)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static void quest_library_finalize_book()
+{
+ int i = 0;
+ for (i = 1; i <= 3; i++)
+ {
+ school_book *school_book = school_books_at(BOOK_PLAYER);
+ school_book_add_spell(school_book, library_quest_book_get_slot(i));
+ }
+}
+
+static void library_quest_add_spell(int spell) {
+ if (library_quest_book_get_slot(1) == -1) {
+ library_quest_book_set_slot(1, spell);
+ } else if (library_quest_book_get_slot(2) == -1) {
+ library_quest_book_set_slot(2, spell);
+ } else if (library_quest_book_get_slot(3) == -1) {
+ library_quest_book_set_slot(3, spell);
+ }
+}
+
+static void library_quest_remove_spell(int spell) {
+ if (library_quest_book_get_slot(1) == spell) {
+ library_quest_book_set_slot(1, library_quest_book_get_slot(2));
+ library_quest_book_set_slot(2, library_quest_book_get_slot(3));
+ library_quest_book_set_slot(3, -1);
+ } else if (library_quest_book_get_slot(2) == spell) {
+ library_quest_book_set_slot(2, library_quest_book_get_slot(3));
+ library_quest_book_set_slot(3, -1);
+ } else if (library_quest_book_get_slot(3) == spell) {
+ library_quest_book_set_slot(3, -1);
+ }
+}
+
+/* spell selection routines inspired by skills.c */
+static void library_quest_print_spells(int first, int current)
+{
+ int width, height;
+ int slots, row;
+ int index;
+
+ Term_clear();
+ Term_get_size(&width, &height);
+
+ 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) {
+ c_prt(TERM_L_RED, "The book can hold no more spells.", 2, 0);
+ } else if (slots == 1) {
+ c_prt(TERM_L_BLUE, "The book can hold 1 more spell.", 2, 0);
+ } else {
+ c_prt(TERM_L_BLUE, format("The book can hold %d more spells.", slots), 2, 0);
+ }
+
+ row = 3;
+
+ for (index = 0; index < bookable_spells_size; index++) {
+ int spell = bookable_spells[index];
+ if (index >= first) {
+ int color;
+ if (index == current) {
+ color = TERM_GREEN;
+ } else if (library_quest_book_contains_spell(spell)) {
+ color = TERM_WHITE;
+ } else {
+ color = TERM_ORANGE;
+ }
+
+ print_spell(NULL, color, row, spell);
+
+ if (row == height - 1) {
+ return;
+ }
+ row = row + 1;
+ }
+ }
+}
+
+static void library_quest_fill_book()
+{
+ int width, height, margin, first, current;
+ bool_ done;
+
+ /* Always start with a cleared book */
+ library_quest_book_set_slot(1, -1);
+ library_quest_book_set_slot(2, -1);
+ library_quest_book_set_slot(3, -1);
+
+ screen_save();
+ Term_get_size(&width, &height);
+
+ /* room for legend */
+ margin = 3;
+
+ first = 0;
+ current = 0;
+ done = FALSE;
+
+ while (done == FALSE)
+ {
+ char ch;
+ int dir, spell_idx;
+
+ library_quest_print_spells(first, current);
+
+ ch = inkey();
+ dir = get_keymap_dir(ch);
+
+ spell_idx = bookable_spells[current];
+
+ if (ch == ESCAPE) {
+ if (library_quest_book_slots_left() == 0) {
+ flush();
+ done = get_check("Really create the book?");
+ } else {
+ done = TRUE;
+ }
+ } else if (ch == '\r') {
+ /* TODO: make tree of schools */
+ } else if (ch == 'n') {
+ current = current + height;
+ } else if (ch == 'p') {
+ current = current - height;
+ } else if (ch == 'I') {
+ print_spell_desc(spell_idx, 0);
+ inkey();
+ } else if (dir == 2) {
+ current = current + 1;
+ } else if (dir == 8) {
+ current = current - 1;
+ } else if (dir == 6) {
+ if (library_quest_book_contains_spell(spell_idx) == FALSE)
+ {
+ library_quest_add_spell(spell_idx);
+ }
+ } else if (dir == 4) {
+ library_quest_remove_spell(spell_idx);
+ }
+
+ if (current >= bookable_spells_size) {
+ current = bookable_spells_size - 1;
+ } else if (current < 0) {
+ current = 0;
+ }
+
+ if (current > (first + height - margin - 1)) {
+ first = current - height + margin + 1;
+ } else if (first > current) {
+ first = current;
+ }
+ }
+
+ screen_load();
+}
+
+static bool_ quest_library_gen_hook(void *, void *, void *)
+{
+ /* Only if player doing this quest */
+ if (p_ptr->inside_quest != QUEST_LIBRARY)
+ {
+ return FALSE;
+ }
+
+ {
+ int y = 2;
+ int x = 2;
+ load_map("library.map", &y, &x);
+ dungeon_flags2 = DF2_NO_GENO;
+ }
+
+ /* Generate monsters */
+ library_quest_place_nrandom(
+ 4, 4, 14, 37, MONSTER_LICH, damroll(4,2));
+
+ library_quest_place_nrandom(
+ 14, 34, 37, 67, MONSTER_MONASTIC_LICH, damroll(1, 2));
+
+ library_quest_place_nrandom(
+ 4, 34, 14, 67, MONSTER_MONASTIC_LICH, damroll(1, 2) - 1);
+
+ library_quest_place_nrandom(
+ 14, 4, 37, 34, MONSTER_MONASTIC_LICH, damroll(1, 2) - 1);
+
+ library_quest_place_nrandom(
+ 10, 10, 37, 67, MONSTER_FLESH_GOLEM, 2);
+
+ library_quest_place_nrandom(
+ 10, 10, 37, 67, MONSTER_CLAY_GOLEM, 2);
+
+ library_quest_place_nrandom(
+ 10, 10, 37, 67, MONSTER_IRON_GOLEM, 2);
+
+ library_quest_place_nrandom(
+ 10, 10, 37, 67, MONSTER_MITHRIL_GOLEM, 1);
+
+ return TRUE;
+}
+
+static bool_ quest_library_stair_hook(void *, void *, void *)
+{
+ bool_ ret;
+
+ /* only ask this if player about to go up stairs of quest and hasn't won yet */
+ if ((p_ptr->inside_quest != QUEST_LIBRARY) ||
+ (cquest.status == QUEST_STATUS_COMPLETED))
+ {
+ return FALSE;
+ }
+
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_LESS)
+ {
+ return FALSE;
+ }
+
+ /* flush all pending input */
+ flush();
+
+ /* confirm */
+ ret = get_check("Really abandon the quest?");
+
+ /* if yes, then */
+ if (ret == TRUE)
+ {
+ /* fail the quest */
+ cquest.status = QUEST_STATUS_FAILED;
+ return FALSE;
+ }
+ else
+ {
+ /* if no, they stay in the quest */
+ return TRUE;
+ }
+}
+
+static bool_ quest_library_monster_death_hook(void *, void *, void *)
+{
+ int i, count = -1;
+
+ /* if they're in the quest and haven't won, continue */
+ if ((p_ptr->inside_quest != QUEST_LIBRARY) ||
+ (cquest.status == QUEST_STATUS_COMPLETED))
+ {
+ return FALSE;
+ }
+
+ /* Count all the enemies left alive */
+ for (i = 0; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ if ((m_ptr->r_idx > 0) &&
+ (m_ptr->status <= MSTATUS_ENEMY))
+ {
+ count = count + 1;
+ }
+ }
+
+ /* We've just killed the last monster */
+ if (count == 0)
+ {
+ cquest.status = QUEST_STATUS_COMPLETED;
+ cmsg_print(TERM_YELLOW, "The library is safe now.");
+ }
+
+ /* Normal processing */
+ return FALSE;
+}
+
+void quest_library_building(bool_ *paid, bool_ *recreate)
+{
+ int status = cquest.status;
+
+ /* the quest hasn't been requested already, right? */
+ if (status == QUEST_STATUS_UNTAKEN)
+ {
+ /* quest has been taken now */
+ cquest.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.");
+
+ *paid = FALSE;
+ *recreate = TRUE;
+ }
+
+ /* if quest completed */
+ else if (status == QUEST_STATUS_COMPLETED)
+ {
+ 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)
+ {
+ cquest.status = QUEST_STATUS_REWARDED;
+
+ {
+ object_type forge;
+ object_type *q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_BOOK, 61));
+ q_ptr->art_name = quark_add(player_name);
+ q_ptr->found = OBJ_FOUND_REWARD;
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ inven_carry(q_ptr, FALSE);
+ }
+
+ quest_library_finalize_book();
+ }
+ }
+
+ /* if the player asks for a quest when they already have it,
+ * but haven't failed it, give them some extra instructions */
+ else if (status == QUEST_STATUS_TAKEN)
+ {
+ msg_print("Please use the side entrance and vanquish the intruders for me.");
+ }
+
+ /* quest failed or completed, then give no more quests */
+ else if ((status == QUEST_STATUS_FAILED) || (status == QUEST_STATUS_REWARDED))
+ {
+ msg_print("I have no more quests for you.");
+ }
+}
+
+bool_ quest_library_describe(FILE *hook_file)
+{
+ if (cquest.status == QUEST_STATUS_TAKEN)
+ {
+ 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");
+ }
+ else if (cquest.status == QUEST_STATUS_COMPLETED)
+ {
+ /* Quest done, book not gotten yet */
+ 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");
+ }
+
+ /* Normal processing */
+ return TRUE;
+}
+
+bool_ quest_library_init_hook(int q)
+{
+ /* Only need hooks if the quest is unfinished. */
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) &&
+ (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook_new(HOOK_GEN_QUEST , quest_library_gen_hook , "library_gen_hook", NULL);
+ add_hook_new(HOOK_STAIR , quest_library_stair_hook , "library_stair_hook", NULL);
+ add_hook_new(HOOK_MONSTER_DEATH, quest_library_monster_death_hook, "library_monster_death_hook", NULL);
+ }
+
+ /* If quest was rewarded we need to initialize the real player's spellbook. */
+ if (cquest.status == QUEST_STATUS_REWARDED)
+ {
+ quest_library_finalize_book();
+ }
+
+ return FALSE;
+}
+
+#undef print_hook
diff --git a/src/q_library.hpp b/src/q_library.hpp
new file mode 100644
index 00000000..8150893e
--- /dev/null
+++ b/src/q_library.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_library_init_hook(int q);
+bool_ quest_library_describe(FILE *fff);
+void quest_library_building(bool_ *paid, bool_ *recreate);
+void initialize_bookable_spells();
diff --git a/src/q_main.cc b/src/q_main.cc
new file mode 100644
index 00000000..ed11b9dc
--- /dev/null
+++ b/src/q_main.cc
@@ -0,0 +1,210 @@
+#include "q_main.hpp"
+
+#include "hook_chardump_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hook_new_monster_in.hpp"
+#include "hooks.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#include <cassert>
+
+GENERATE_MONSTER_LOOKUP_FN(get_necromancer, "The Necromancer of Dol Guldur")
+GENERATE_MONSTER_LOOKUP_FN(get_sauron, "Sauron, the Sorcerer")
+GENERATE_MONSTER_LOOKUP_FN(get_morgoth, "Morgoth, Lord of Darkness")
+
+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++]);
+ }
+}
+
+static bool_ quest_main_monsters_hook(void *, void *in_, void *)
+{
+ struct hook_new_monster_in *in = static_cast<struct hook_new_monster_in *>(in_);
+ s32b r_idx = in->r_idx;
+
+ /* Sauron */
+ if (r_idx == get_sauron())
+ {
+ /* No Sauron until Necromancer dies */
+ if (r_info[get_necromancer()].max_num) return TRUE;
+ }
+ /* Morgoth */
+ else if (r_idx == get_morgoth())
+ {
+ /* No Morgoth until Sauron dies */
+ if (r_info[get_sauron()].max_num) return TRUE;
+ }
+ return FALSE;
+}
+
+static bool_ quest_morgoth_hook(void *, void *, void *)
+{
+ monster_race *r_ptr = &r_info[get_morgoth()];
+
+ /* 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_FRAME);
+
+ /* 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_new(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);
+}
+
+static bool_ quest_morgoth_dump_hook(void *, void *in_, void *)
+{
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (quest[QUEST_MORGOTH].status >= QUEST_STATUS_COMPLETED)
+ {
+ if (quest[QUEST_ONE].status == QUEST_STATUS_FINISHED)
+ fprintf(f, "\n You saved Arda and became a famed %s.", sp_ptr->winner);
+ else
+ fprintf(f, "\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_new(HOOK_MONSTER_DEATH, quest_morgoth_hook, "morgoth_death", NULL);
+ }
+ add_hook_new(HOOK_CHAR_DUMP, quest_morgoth_dump_hook, "morgoth_dump", NULL);
+ add_hook_new(HOOK_NEW_MONSTER, quest_main_monsters_hook, "main_new_monster", NULL);
+ return (FALSE);
+}
+
+static bool_ quest_sauron_hook(void *, void *, void *)
+{
+ monster_race *r_ptr = &r_info[get_sauron()];
+
+ /* 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_new(HOOK_MONSTER_DEATH, quest_sauron_hook);
+ add_hook_new(HOOK_MONSTER_DEATH, quest_morgoth_hook, "morgort_death", NULL);
+ *(quest[QUEST_SAURON].plot) = QUEST_MORGOTH;
+ quest_morgoth_init_hook(QUEST_MORGOTH);
+
+ process_hooks_restart = TRUE;
+ }
+ return (FALSE);
+}
+
+static bool_ quest_sauron_resurect_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ 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[get_sauron()].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 == get_sauron()) && (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_new(HOOK_MONSTER_DEATH, quest_sauron_hook, "sauron_death", NULL);
+ }
+ add_hook_new(HOOK_NEW_MONSTER, quest_main_monsters_hook, "main_new_monster", NULL);
+ add_hook_new(HOOK_MONSTER_DEATH, quest_sauron_resurect_hook, "sauron_resurect_death", NULL);
+ return (FALSE);
+}
+
+static bool_ quest_necro_hook(void *, void *, void *)
+{
+ monster_race *r_ptr = &r_info[get_necromancer()];
+
+ /* 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_new(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_new(HOOK_MONSTER_DEATH, quest_necro_hook, "necro_death", NULL);
+ }
+ add_hook_new(HOOK_NEW_MONSTER, quest_main_monsters_hook, "main_new_monster", NULL);
+ return (FALSE);
+}
diff --git a/src/q_main.hpp b/src/q_main.hpp
new file mode 100644
index 00000000..a88530fc
--- /dev/null
+++ b/src/q_main.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_necro_init_hook(int q_idx);
+bool_ quest_sauron_init_hook(int q_idx);
+bool_ quest_morgoth_init_hook(int q_idx);
diff --git a/src/q_narsil.cc b/src/q_narsil.cc
new file mode 100644
index 00000000..a6c0eed3
--- /dev/null
+++ b/src/q_narsil.cc
@@ -0,0 +1,122 @@
+#include "q_narsil.hpp"
+
+#include "cave_type.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_identify_in.hpp"
+#include "hook_move_in.hpp"
+#include "hooks.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#define cquest (quest[QUEST_NARSIL])
+
+static bool_ quest_narsil_move_hook(void *, void *in_, void *)
+{
+ struct hook_move_in *in = static_cast<struct hook_move_in *>(in_);
+ s32b y = in->y;
+ s32b x = in->x;
+ cave_type *c_ptr = &cave[y][x];
+ int i;
+ object_type *o_ptr;
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ /* The castle of Aragorn */
+ if ((c_ptr->feat != FEAT_SHOP) || (c_ptr->special != 14)) return FALSE;
+
+ /* Look out for Narsil */
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ o_ptr = get_object(i);
+
+ if (!o_ptr->k_idx) continue;
+
+ if (o_ptr->name1 == ART_NARSIL) break;
+ }
+
+ if (i == INVEN_TOTAL) return FALSE;
+
+ cmsg_print(TERM_YELLOW, "I heard that the broken sword had been found!");
+ cmsg_print(TERM_YELLOW, "I thought it was only a rumor... until now.");
+ cmsg_print(TERM_YELLOW, "What you have is really the sword that was broken.");
+ cmsg_print(TERM_YELLOW, "I will get it reforged...");
+ cmsg_print(TERM_L_BLUE, "Aragorn leaves for a few hours then comes back...");
+ cmsg_print(TERM_YELLOW, "Here it is, Anduril, the sword that was forged and is");
+ cmsg_print(TERM_YELLOW, "reforged again. Take it; you will surely need it for your quest.");
+
+ object_prep(o_ptr, lookup_kind(TV_SWORD, SV_LONG_SWORD));
+ o_ptr->name1 = ART_ANDURIL;
+ apply_magic(o_ptr, -1, TRUE, TRUE, TRUE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ inven_item_describe(i);
+ inven_item_optimize(i);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER | PW_INVEN);
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_FINISHED;
+
+ del_hook_new(HOOK_MOVE, quest_narsil_move_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_narsil_dump_hook(void *, void *in_, void *)
+{
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(f, "\n The sword that was broken is now reforged.");
+ }
+ return (FALSE);
+}
+
+static bool_ quest_narsil_identify_hook(void *, void *in_, void *)
+{
+ struct hook_identify_in *in = static_cast<struct hook_identify_in *>(in_);
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ if (in->o_ptr->name1 == ART_NARSIL)
+ {
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ for (int i = 0; i < 5; i++)
+ {
+ if (quest[QUEST_NARSIL].desc[i][0] != '\0')
+ {
+ cmsg_print(TERM_YELLOW, quest[QUEST_NARSIL].desc[i]);
+ }
+ }
+
+ add_hook_new(HOOK_MOVE, quest_narsil_move_hook, "narsil_move", NULL);
+ del_hook_new(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_new(HOOK_MOVE, quest_narsil_move_hook, "narsil_move", NULL);
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook_new(HOOK_IDENTIFY, quest_narsil_identify_hook, "narsil_id", NULL);
+ }
+ add_hook_new(HOOK_CHAR_DUMP, quest_narsil_dump_hook, "narsil_dump", NULL);
+ return (FALSE);
+}
diff --git a/src/q_narsil.hpp b/src/q_narsil.hpp
new file mode 100644
index 00000000..b83e63cf
--- /dev/null
+++ b/src/q_narsil.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_narsil_init_hook(int q_idx);
diff --git a/src/q_nazgul.cc b/src/q_nazgul.cc
new file mode 100644
index 00000000..15a6a843
--- /dev/null
+++ b/src/q_nazgul.cc
@@ -0,0 +1,145 @@
+#include "q_nazgul.hpp"
+
+#include "cave.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_init_quest_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hook_wild_gen_in.hpp"
+#include "hooks.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define cquest (quest[QUEST_NAZGUL])
+
+GENERATE_MONSTER_LOOKUP_FN(get_uvatha, "Uvatha the Horseman")
+
+static bool_ quest_nazgul_gen_hook(void *, void *in_, void *)
+{
+ struct hook_wild_gen_in *in = static_cast<struct hook_wild_gen_in *>(in_);
+ int m_idx, x = 1, y = 1, tries = 10000;
+ bool_ small = in->small;
+
+ if ((cquest.status != QUEST_STATUS_TAKEN) || (small) || (p_ptr->town_num != 1)) return (FALSE);
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(cur_hgt - 4) + 2;
+ x = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ /* Not in player los */
+ if ((!los(p_ptr->py, p_ptr->px, y, x)) && cave_empty_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place the nazgul */
+ int r_idx = get_uvatha();
+
+ m_allow_special[r_idx] = TRUE;
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[r_idx] = FALSE;
+
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+
+ return FALSE;
+}
+
+static bool_ quest_nazgul_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+ object_type forge, *q_ptr;
+
+ 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_new(HOOK_QUEST_FINISH, quest_nazgul_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_nazgul_dump_hook(void *, void *in_, void *)
+{
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(f, "\n You saved Bree from a dreadful Nazgul.");
+ }
+ return (FALSE);
+}
+
+static bool_ quest_nazgul_forbid_hook(void *, void *in_, void *)
+{
+ struct hook_init_quest_in *in = static_cast<struct hook_init_quest_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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);
+}
+
+static bool_ quest_nazgul_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ s32b r_idx = m_list[m_idx].r_idx;
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return (FALSE);
+ if (r_idx != get_uvatha()) return (FALSE);
+
+ cquest.status = QUEST_STATUS_COMPLETED;
+
+ del_hook_new(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_new(HOOK_MONSTER_DEATH, quest_nazgul_death_hook, "nazgul_death", NULL);
+ add_hook_new(HOOK_WILD_GEN, quest_nazgul_gen_hook, "nazgul_gen", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_nazgul_finish_hook, "nazgul_finish", NULL);
+ }
+ add_hook_new(HOOK_CHAR_DUMP, quest_nazgul_dump_hook, "nazgul_dump", NULL);
+ add_hook_new(HOOK_INIT_QUEST, quest_nazgul_forbid_hook, "nazgul_forbid", NULL);
+ return (FALSE);
+}
diff --git a/src/q_nazgul.hpp b/src/q_nazgul.hpp
new file mode 100644
index 00000000..32e3237c
--- /dev/null
+++ b/src/q_nazgul.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_nazgul_init_hook(int q_idx);
diff --git a/src/q_nirna.cc b/src/q_nirna.cc
new file mode 100644
index 00000000..a92ba6e4
--- /dev/null
+++ b/src/q_nirna.cc
@@ -0,0 +1,125 @@
+#include "q_nirna.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#define cquest (quest[QUEST_NIRNAETH])
+
+static bool_ quest_nirnaeth_gen_hook(void *, void *, void *)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_NIRNAETH) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("nirnaeth.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ /* Count the number of monsters */
+ cquest.data[0] = 0;
+ cquest.data[1] = 0;
+ for (x = 2; x < xstart; x++)
+ for (y = 2; y < ystart; y++)
+ {
+ if (cave[y][x].m_idx) cquest.data[0]++;
+ }
+
+ return TRUE;
+}
+
+static bool_ quest_nirnaeth_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_FRAME);
+
+ /* 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_new(HOOK_QUEST_FINISH, quest_nirnaeth_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_nirnaeth_death_hook(void *, void *, void *)
+{
+ if (p_ptr->inside_quest != QUEST_NIRNAETH) return FALSE;
+
+ cquest.data[1]++;
+
+ return FALSE;
+}
+
+static bool_ quest_nirnaeth_stair_hook(void *, void *, void *)
+{
+ 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_new(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_new(HOOK_MONSTER_DEATH, quest_nirnaeth_death_hook, "nirnaeth_death", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_nirnaeth_gen_hook, "nirnaeth_gen", NULL);
+ add_hook_new(HOOK_STAIR, quest_nirnaeth_stair_hook, "nirnaeth_stair", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_nirnaeth_finish_hook, "nirnaeth_finish", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_nirna.hpp b/src/q_nirna.hpp
new file mode 100644
index 00000000..24a8759f
--- /dev/null
+++ b/src/q_nirna.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_nirnaeth_init_hook(int q_idx);
diff --git a/src/q_one.cc b/src/q_one.cc
new file mode 100644
index 00000000..3741e009
--- /dev/null
+++ b/src/q_one.cc
@@ -0,0 +1,378 @@
+#include "q_one.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "gods.hpp"
+#include "hook_calculate_hp_in.hpp"
+#include "hook_calculate_hp_out.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_drop_in.hpp"
+#include "hook_identify_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hook_move_in.hpp"
+#include "hook_wield_in.hpp"
+#include "hooks.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#define cquest (quest[QUEST_ONE])
+
+static bool_ quest_one_move_hook(void *, void *in_, void *)
+{
+ struct hook_move_in *in = static_cast<struct hook_move_in *>(in_);
+ s32b y = in->y;
+ s32b x = in->x;
+ cave_type *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.'");
+
+ if (p_ptr->pgod == GOD_ERU)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Eru will abandon you if you wear it.'");
+ }
+
+ if (p_ptr->pgod == GOD_MANWE)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Manwe will abandon you if you wear it.'");
+ }
+
+ if (p_ptr->pgod == GOD_TULKAS)
+ {
+ cmsg_print(TERM_YELLOW, "'Also, Tulkas will abandon you if you wear it.'");
+ }
+
+ if (p_ptr->pgod == 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!'");
+
+ if (p_ptr->pgod == 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;
+}
+
+static bool_ quest_one_drop_hook(void *, void *in_, void *)
+{
+ struct hook_drop_in *in = static_cast<struct hook_drop_in *>(in_);
+ s32b o_idx = in->o_idx;
+ object_type *o_ptr = &p_ptr->inventory[o_idx];
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+
+ if (o_ptr->name1 != ART_POWER) return FALSE;
+ if (cave[p_ptr->py][p_ptr->px].feat != FEAT_GREAT_FIRE) return FALSE;
+
+ cmsg_print(TERM_YELLOW, "You throw the One Ring into the #RGreat Fire#y; it is rapidly consumed");
+ cmsg_print(TERM_YELLOW, "by the searing flames.");
+ cmsg_print(TERM_YELLOW, "You feel the powers of evil weakening.");
+ cmsg_print(TERM_YELLOW, "Now you can go onto the hunt for Sauron!");
+
+ inc_stack_size_ex(o_idx, -99, OPTIMIZE, NO_DESCRIBE);
+
+ abandon_god(GOD_MELKOR);
+
+ /* Continue the plot */
+ cquest.status = QUEST_STATUS_FINISHED;
+ *(quest[QUEST_ONE].plot) = QUEST_SAURON;
+ quest[*(quest[QUEST_ONE].plot)].status = QUEST_STATUS_TAKEN;
+ quest[*(quest[QUEST_ONE].plot)].init(*(quest[QUEST_ONE].plot));
+
+ return TRUE;
+}
+
+static bool_ quest_one_wield_hook(void *, void *in_, void *)
+{
+ struct hook_wield_in *in = static_cast<struct hook_wield_in *>(in_);
+ object_type *o_ptr = in->o_ptr;
+
+ 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;
+}
+
+static bool_ quest_one_hp_hook(void *, void *in_, void *out_)
+{
+ struct hook_calculate_hp_in *in = static_cast<struct hook_calculate_hp_in *>(in_);
+ struct hook_calculate_hp_out *out = static_cast<struct hook_calculate_hp_out *>(out_);
+
+ if (cquest.status == QUEST_STATUS_FAILED_DONE)
+ {
+ s32b mhp = in->mhp;
+
+ for (int i = 0; i < p_ptr->lives + 1; i++)
+ mhp = (mhp * 2) / 3;
+
+ out->mhp = mhp;
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+static bool_ quest_one_die_hook(void *, void *, void *)
+{
+ 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);
+}
+
+static bool_ quest_one_identify_hook(void *, void *in_, void *)
+{
+ struct hook_identify_in *in = static_cast<struct hook_identify_in *>(in_);
+ object_type *o_ptr = in->o_ptr;
+
+ if (cquest.status == QUEST_STATUS_TAKEN)
+ {
+ 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);
+}
+
+static bool_ quest_one_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ s32b r_idx = m_list[m_idx].r_idx;
+ bool_ ok = FALSE;
+
+ 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);
+}
+
+static bool_ quest_one_dump_hook(void *, void *in_, void *)
+{
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (cquest.status == QUEST_STATUS_FINISHED)
+ {
+ fprintf(f, "\n You destroyed the One Ring, thus weakening Sauron.");
+ }
+ if (cquest.status == QUEST_STATUS_FAILED_DONE)
+ {
+ fprintf(f, "\n You fell under the evil influence of the One Ring and decided to wear it.");
+ }
+ return (FALSE);
+}
+
+static bool_ quest_one_gen_hook(void *, void *, void *)
+{
+ s32b x, y, tries = 10000;
+
+ /* Paranoia */
+ if (cquest.status != QUEST_STATUS_TAKEN) return (FALSE);
+ if ((dungeon_type != DUNGEON_ANGBAND) || (dun_level != 99)) return (FALSE);
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(cur_hgt - 4) + 2;
+ x = randint(cur_wid - 4) + 2;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ if (tries)
+ {
+ int m_idx = place_monster_one(y, x, test_monster_name("Sauron, the Sorcerer"), 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ }
+
+ return (FALSE);
+}
+
+bool_ quest_one_init_hook(int q_idx)
+{
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook_new(HOOK_LEVEL_END_GEN, quest_one_gen_hook, "one_gen", NULL);
+ add_hook_new(HOOK_MONSTER_DEATH, quest_one_death_hook, "one_death", NULL);
+ add_hook_new(HOOK_DROP, quest_one_drop_hook, "one_drop", NULL);
+ add_hook_new(HOOK_WIELD, quest_one_wield_hook, "one_wield", NULL);
+ add_hook_new(HOOK_IDENTIFY, quest_one_identify_hook, "one_id", NULL);
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook_new(HOOK_MOVE, quest_one_move_hook, "one_move", NULL);
+ }
+ add_hook_new(HOOK_CHAR_DUMP, quest_one_dump_hook, "one_dump", NULL);
+ add_hook_new(HOOK_CALC_HP, quest_one_hp_hook, "one_hp", NULL);
+ add_hook_new(HOOK_DIE, quest_one_die_hook, "one_die", NULL);
+ return (FALSE);
+}
diff --git a/src/q_one.hpp b/src/q_one.hpp
new file mode 100644
index 00000000..a85a5733
--- /dev/null
+++ b/src/q_one.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_one_init_hook(int q_idx);
diff --git a/src/q_poison.cc b/src/q_poison.cc
new file mode 100644
index 00000000..a5b274b0
--- /dev/null
+++ b/src/q_poison.cc
@@ -0,0 +1,263 @@
+#include "q_poison.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_drop_in.hpp"
+#include "hook_init_quest_in.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "messages.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#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;
+}
+
+static bool_ quest_poison_gen_hook(void *, void *, void *)
+{
+ int cy = 1, cx = 1, x, y, tries = 10000, r_idx;
+ bool_ (*old_get_mon_num_hook)(int r_idx);
+
+ if (cquest.status != QUEST_STATUS_TAKEN) return FALSE;
+ if (p_ptr->wilderness_y != wild_locs[cquest.data[0]][0]) return FALSE;
+ if (p_ptr->wilderness_x != wild_locs[cquest.data[0]][1]) return FALSE;
+ if (p_ptr->wild_mode) return FALSE;
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ cy = randint(cur_hgt - 24) + 22;
+ cx = randint(cur_wid - 34) + 32;
+
+ /* Is it a good spot ? */
+ if (cave_empty_bold(cy, cx)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place the baddies */
+
+ /* Backup the old hook */
+ old_get_mon_num_hook = get_mon_num_hook;
+
+ /* Require "okay" monsters */
+ get_mon_num_hook = create_molds_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* Pick a monster, using the level calculation */
+ for (x = cx - 25; x <= cx + 25; x++)
+ for (y = cy - 25; y <= cy + 25; y++)
+ {
+ if (!in_bounds(y, x)) continue;
+
+ if (distance(cy, cx, y, x) > 25) continue;
+
+ if (magik(80) && ((cave[y][x].feat == FEAT_DEEP_WATER) || (cave[y][x].feat == FEAT_SHAL_WATER))) cave_set_feat(y, x, FEAT_TAINTED_WATER);
+
+ if (distance(cy, cx, y, x) > 10) continue;
+
+ if (magik(60))
+ {
+ int m_idx;
+
+ r_idx = get_mon_num(30);
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+
+ /* Sometimes make it up some levels */
+ if (magik(80) && m_idx)
+ {
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (m_ptr->level < p_ptr->lev)
+ {
+ m_ptr->exp = monster_exp(m_ptr->level + randint(p_ptr->lev - m_ptr->level));
+ monster_check_experience(m_idx, TRUE);
+ }
+ }
+ }
+ }
+
+ /* Reset restriction */
+ get_mon_num_hook = old_get_mon_num_hook;
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ return FALSE;
+}
+
+static bool_ quest_poison_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+ object_type forge, *q_ptr;
+
+ 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_new(HOOK_QUEST_FINISH, quest_poison_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_poison_dump_hook(void *, void *in_, void *)
+{
+ hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (cquest.status >= QUEST_STATUS_COMPLETED)
+ {
+ fprintf(f, "\n You saved the beautiful Mallorns of Lothlorien.");
+ }
+ return (FALSE);
+}
+
+static bool_ quest_poison_quest_hook(void *, void *in_, void *)
+{
+ struct hook_init_quest_in *in = static_cast<struct hook_init_quest_in *>(in_);
+ s32b q_idx = in->q_idx;
+ object_type forge, *q_ptr;
+
+ 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_new(HOOK_INIT_QUEST, quest_poison_quest_hook);
+ process_hooks_restart = TRUE;
+
+ return FALSE;
+}
+
+static bool_ quest_poison_drop_hook(void *, void *in_, void *)
+{
+ struct hook_drop_in *in = static_cast<struct hook_drop_in *>(in_);
+ s32b mcnt = 0, i, x, y;
+ s32b o_idx = in->o_idx;
+ object_type *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_new(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(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_new(HOOK_DROP, quest_poison_drop_hook, "poison_drop", NULL);
+ add_hook_new(HOOK_WILD_GEN, quest_poison_gen_hook, "poison_gen", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_poison_finish_hook, "poison_finish", NULL);
+ }
+ if (cquest.status < QUEST_STATUS_COMPLETED)
+ {
+ add_hook_new(HOOK_INIT_QUEST, quest_poison_quest_hook, "poison_iquest", NULL);
+ }
+ add_hook_new(HOOK_CHAR_DUMP, quest_poison_dump_hook, "poison_dump", NULL);
+ return (FALSE);
+}
diff --git a/src/q_poison.hpp b/src/q_poison.hpp
new file mode 100644
index 00000000..f8d97ace
--- /dev/null
+++ b/src/q_poison.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_poison_init_hook(int q_idx);
diff --git a/src/q_rand.cc b/src/q_rand.cc
new file mode 100644
index 00000000..4ef79928
--- /dev/null
+++ b/src/q_rand.cc
@@ -0,0 +1,655 @@
+#include "q_rand.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "dungeon_info_type.hpp"
+#include "generate.hpp"
+#include "hook_build_room1_in.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "lua_bind.hpp"
+#include "messages.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_type.hpp"
+#include "monster_race.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+static int randquest_hero[] = { 20, 13, 15, 16, 9, 17, 18, 8, -1 };
+
+/* Possible number(and layout) or random quests */
+#define MAX_RANDOM_QUESTS_TYPES ((8 * 3) + (8 * 1))
+static 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
+
+// Generate lookup function
+GENERATE_MONSTER_LOOKUP_FN(get_adventurer, "Adventurer")
+
+void initialize_random_quests(int n)
+{
+ int step, lvl, i, k;
+ int old_type = dungeon_type;
+
+ /* Zero out everything first */
+ for (i = 0; i < MAX_RANDOM_QUEST; i++) random_quests[i].type = 0;
+
+ if (n == 0) return;
+
+ /* 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 = nullptr;
+
+ 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;
+
+ /* If module says a monster race is friendly, then skip */
+ if (modules[game_module_idx].race_status != NULL)
+ {
+ s16b *status = (*modules[game_module_idx].race_status)(q_ptr->r_idx);
+ if ((status != NULL) && (*status >= 0))
+ {
+ 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(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(format("Quest for %d on lvl %d",
+ q_ptr->r_idx, rl), TERM_RED);
+ }
+ }
+
+ lvl += step;
+ }
+
+ dungeon_type = old_type;
+}
+
+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;
+}
+
+static void do_get_new_obj(int y, int x)
+{
+ obj_theme theme;
+ object_type *q_ptr[3], forge[3];
+ int res, i;
+
+ /* Create objects */
+ std::vector<std::string> items;
+ for (i = 0; i < 3; i++)
+ {
+ /* Get local object */
+ q_ptr[i] = &forge[i];
+
+ /* Wipe the object */
+ object_wipe(q_ptr[i]);
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+
+ /* Make a great object */
+ make_object(q_ptr[i], TRUE, TRUE, theme);
+ q_ptr[i]->found = OBJ_FOUND_REWARD;
+
+ char buf[100];
+ object_desc(buf, q_ptr[i], 0, 0);
+ items.push_back(buf);
+ }
+
+
+ while (TRUE)
+ {
+ res = ask_menu("Choose a reward to get(a-c to choose, ESC to cancel)?", items);
+
+ /* 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;
+ }
+ }
+ }
+}
+
+static 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;
+ }
+ }
+}
+
+static void hero_death(s32b m_idx, s32b r_idx)
+{
+ random_quests[dun_level].done = TRUE;
+
+ cmsg_print(TERM_YELLOW, "The adventurer steps out of the shadows and picks up his sword:");
+ cmsg_print(TERM_YELLOW, "'Ah! My sword! My trusty sword! Thanks.");
+
+ if (!can_create_companion())
+ {
+ cmsg_print(TERM_YELLOW, "I must go on my own way now.");
+ cmsg_print(TERM_YELLOW, "But before I go, I can help your skills.'");
+ cmsg_print(TERM_YELLOW, "He touches your forehead.");
+ do_get_new_skill();
+ return;
+ }
+ cmsg_print(TERM_YELLOW, "If you wish, I can help you in your adventures.'");
+
+ /* Flush input */
+ flush();
+
+ if (get_check("Do you want him to join you? "))
+ {
+ int x, y, i;
+
+ /* Look for a location */
+ for (i = 0; i < 20; ++i)
+ {
+ /* Pick a distance */
+ int d = (i / 15) + 1;
+
+ /* Pick a location */
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ /* Require "empty" floor grid */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Hack -- no summon on glyph of warding */
+ if (cave[y][x].feat == FEAT_GLYPH) continue;
+ if (cave[y][x].feat == FEAT_MINOR_GLYPH) continue;
+
+ /* Nor on the between */
+ if (cave[y][x].feat == FEAT_BETWEEN) continue;
+
+ /* ... nor on the Pattern */
+ if ((cave[y][x].feat >= FEAT_PATTERN_START) &&
+ (cave[y][x].feat <= FEAT_PATTERN_XTRA2))
+ continue;
+
+ /* Okay */
+ break;
+ }
+
+ if (i < 20)
+ {
+ int r_idx = get_adventurer();
+
+ m_allow_special[r_idx] = TRUE;
+ int m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_COMPANION);
+ m_allow_special[r_idx] = 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();
+ }
+}
+
+static bool_ quest_random_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ int 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);
+}
+
+static bool_ quest_random_turn_hook(void *, void *, void *)
+{
+ quest[QUEST_RANDOM].data[0] = 0;
+ quest[QUEST_RANDOM].data[1] = 0;
+ return (FALSE);
+}
+
+static bool_ quest_random_feeling_hook(void *, void *, void *)
+{
+ 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);
+ }
+ else
+ cmsg_format(TERM_YELLOW, "You hear someone shouting: 'Leave me alone, stupid %s'", r_info[random_quests[dun_level].r_idx].name);
+ return (FALSE);
+}
+
+static bool_ quest_random_gen_hero_hook(void *, void *, void *)
+{
+ 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);
+}
+
+static bool_ quest_random_gen_hook(void *, void *in_, void *)
+{
+ struct hook_build_room1_in *in = static_cast<struct hook_build_room1_in *>(in_);
+ s32b bx0 = in->x;
+ s32b by0 = in->y;
+ s32b x, y;
+ 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);
+
+ /* Pick a room size */
+ get_map_size(format("qrand%d.map", random_quests[dun_level].type), &ysize, &xsize);
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return FALSE;
+
+ /* Get corner values */
+ y1 = yval - ysize / 2;
+ x1 = xval - xsize / 2;
+ y2 = y1 + ysize - 1;
+ x2 = x1 + xsize - 1;
+
+ /* Place a full floor under the room */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+ cave[y][x].info |= (CAVE_ROOM|CAVE_GLOW);
+ }
+ }
+
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM | CAVE_GLOW);
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ xstart = x1;
+ ystart = y1;
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file(format("qrand%d.map", random_quests[dun_level].type), &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = x1; x < xstart; x++)
+ for (y = y1; y < ystart; y++)
+ {
+ cave[y][x].info |= CAVE_ICKY | CAVE_ROOM;
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ monster_type *m_ptr;
+ int i;
+
+ m_allow_special[random_quests[dun_level].r_idx] = TRUE;
+ i = place_monster_one(y, x, random_quests[dun_level].r_idx, 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[random_quests[dun_level].r_idx] = FALSE;
+ if (i)
+ {
+ m_ptr = &m_list[i];
+ m_ptr->mflag |= MFLAG_QUEST;
+ }
+ }
+ }
+
+ /* Dont try another one for this generation */
+ quest[QUEST_RANDOM].data[1] = 1;
+
+ /* Boost level feeling a bit - a la pits */
+ rating += 10;
+
+ return (TRUE);
+}
+
+static bool_ quest_random_dump_hook(void *, void *in_, void *)
+{
+ static const char *number[] = { "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" };
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+ 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(f, "\n You have completed %d princess quests.", pcnt);
+ else if (pcnt > 1)
+ fprintf(f, "\n You have completed %s princess quests.", number[pcnt-2]);
+ else if (pcnt == 1)
+ fprintf(f, "\n You have completed one princess quest.");
+ else
+ fprintf(f, "\n You haven't completed a single princess quest.");
+
+ if (lscnt > 10)
+ fprintf(f, "\n You have completed %d lost sword quests.", lscnt);
+ else if (lscnt > 1)
+ fprintf(f, "\n You have completed %s lost sword quests.", number[lscnt-2]);
+ else if (lscnt == 1)
+ fprintf(f, "\n You have completed one lost sword quest.");
+ else
+ fprintf(f, "\n You haven't completed a single lost sword quest.");
+ }
+
+ return (FALSE);
+}
+
+bool_ quest_random_describe(FILE *fff)
+{
+ if (!(dungeon_flags1 & DF1_PRINCIPAL)) return FALSE;
+ if ((dun_level < 1) || (dun_level >= MAX_RANDOM_QUEST)) return FALSE;
+ if (!random_quests[dun_level].type) return FALSE;
+ if (random_quests[dun_level].done) return FALSE;
+ if (p_ptr->inside_quest) return FALSE;
+ if (!dun_level) return FALSE;
+
+ if (!is_randhero(dun_level))
+ {
+ fprintf(fff, "#####yCaptured princess!\n");
+ fprintf(fff, "A princess is being held prisoner and tortured here!\n");
+ fprintf(fff, "Save her from the horrible %s.\n", r_info[random_quests[dun_level].r_idx].name);
+ }
+ 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);
+ fprintf(fff, "Kill them all to get it back.\n");
+ }
+ fprintf(fff, "Number: %d, Killed: %ld.\n",
+ random_quests[dun_level].type, (long int) quest[QUEST_RANDOM].data[0]);
+ fprintf(fff, "\n");
+ return TRUE;
+}
+
+bool_ quest_random_init_hook(int q_idx)
+{
+ add_hook_new(HOOK_MONSTER_DEATH, quest_random_death_hook, "rand_death", NULL);
+ add_hook_new(HOOK_NEW_LEVEL, quest_random_turn_hook, "rand_new_lvl", NULL);
+ add_hook_new(HOOK_LEVEL_REGEN, quest_random_turn_hook, "rand_regen_lvl", NULL);
+ add_hook_new(HOOK_LEVEL_END_GEN, quest_random_gen_hero_hook, "rand_gen_hero", NULL);
+ add_hook_new(HOOK_BUILD_ROOM1, quest_random_gen_hook, "rand_gen", NULL);
+ add_hook_new(HOOK_FEELING, quest_random_feeling_hook, "rand_feel", NULL);
+ add_hook_new(HOOK_CHAR_DUMP, quest_random_dump_hook, "rand_dump", NULL);
+ return (FALSE);
+}
diff --git a/src/q_rand.hpp b/src/q_rand.hpp
new file mode 100644
index 00000000..fe87289b
--- /dev/null
+++ b/src/q_rand.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "h-basic.h"
+
+void initialize_random_quests(int n);
+bool_ is_randhero(int level);
+bool_ quest_random_init_hook(int q_idx);
+bool_ quest_random_describe(FILE *fff);
diff --git a/src/q_shroom.cc b/src/q_shroom.cc
new file mode 100644
index 00000000..89b576d8
--- /dev/null
+++ b/src/q_shroom.cc
@@ -0,0 +1,311 @@
+#include "q_shroom.hpp"
+
+#include "cave.hpp"
+#include "hook_chat_in.hpp"
+#include "hook_give_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hook_mon_speak_in.hpp"
+#include "hook_wild_gen_in.hpp"
+#include "hooks.hpp"
+#include "messages.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define cquest (quest[QUEST_SHROOM])
+
+static bool_ quest_shroom_speak_hook(void *, void *, void *);
+static bool_ quest_shroom_chat_hook(void *, void *, void *);
+
+GENERATE_MONSTER_LOOKUP_FN(get_grip, "Grip, Farmer Maggot's dog")
+GENERATE_MONSTER_LOOKUP_FN(get_wolf, "Wolf, Farmer Maggot's dog")
+GENERATE_MONSTER_LOOKUP_FN(get_fang, "Fang, Farmer Maggot's dog")
+GENERATE_MONSTER_LOOKUP_FN(get_farmer_maggot, "Farmer Maggot")
+
+static bool_ quest_shroom_town_gen_hook(void *, void *in_, void *)
+{
+ struct hook_wild_gen_in *in = static_cast<struct hook_wild_gen_in *>(in_);
+ int m_idx, x = 1, y = 1, tries = 10000;
+ bool_ small = in->small;
+
+ /* 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[get_grip()] = TRUE;
+ m_idx = place_monster_one(y, x, get_grip(), 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ m_allow_special[get_grip()] = 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[get_wolf()] = TRUE;
+ m_idx = place_monster_one(y, x, get_wolf(), 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ m_allow_special[get_wolf()] = 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[get_fang()] = TRUE;
+ m_idx = place_monster_one(y, x, get_fang(), 0, FALSE, MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ m_allow_special[get_fang()] = FALSE;
+
+ msg_print("You hear frenzied barking.");
+ }
+
+ /* Generate maggot in town, in daylight */
+ if ((bst(HOUR, turn) < 6) || (bst(HOUR, turn) >= 18) || (cquest.status > QUEST_STATUS_COMPLETED) || (small) || (p_ptr->town_num != 1)) return (FALSE);
+
+ /* Find a good position */
+ while (tries)
+ {
+ /* Get a random spot */
+ y = randint(20) + (cur_hgt / 2) - 10;
+ x = randint(20) + (cur_wid / 2) - 10;
+
+ /* Is it a good spot ? */
+ /* Not in player los, and avoid shop grids */
+ if (!los(p_ptr->py, p_ptr->px, y, x) && cave_empty_bold(y, x) &&
+ cave_plain_floor_bold(y, x)) break;
+
+ /* One less try */
+ tries--;
+ }
+
+ /* Place Farmer Maggot */
+ m_allow_special[get_farmer_maggot()] = TRUE;
+ place_monster_one(y, x, get_farmer_maggot(), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[get_farmer_maggot()] = FALSE;
+
+ return FALSE;
+}
+
+static bool_ quest_shroom_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ s32b r_idx = m_list[m_idx].r_idx;
+
+ if (cquest.status > QUEST_STATUS_COMPLETED) return FALSE;
+
+ if ((r_idx == get_wolf()) ||
+ (r_idx == get_grip()) ||
+ (r_idx == get_fang()))
+ {
+ msg_print("The dog yells a last time and drops dead on the grass.");
+ }
+
+ return FALSE;
+}
+
+static bool_ quest_shroom_give_hook(void *, void *in_, void *)
+{
+ struct hook_give_in *in = static_cast<struct hook_give_in *>(in_);
+ object_type *o_ptr;
+ monster_type *m_ptr;
+
+ s32b m_idx = in->m_idx;
+ s32b item = in->item;
+
+ o_ptr = &p_ptr->inventory[item];
+ m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != get_farmer_maggot()) return (FALSE);
+
+ /* If one is dead .. its bad */
+ if ((r_info[get_grip()].max_num == 0) ||
+ (r_info[get_wolf()].max_num == 0) ||
+ (r_info[get_fang()].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_new(HOOK_GIVE, quest_shroom_give_hook);
+ del_hook_new(HOOK_CHAT, quest_shroom_speak_hook);
+ del_hook_new(HOOK_WILD_GEN, quest_shroom_town_gen_hook);
+ process_hooks_restart = TRUE;
+ return TRUE;
+ }
+
+ if ((o_ptr->tval != TV_FOOD) || (o_ptr->pval2 != 1)) return (FALSE);
+
+ /* Take a mushroom */
+ inc_stack_size_ex(item, -1, OPTIMIZE, NO_DESCRIBE);
+ cquest.data[0]++;
+
+ if (cquest.data[0] == cquest.data[1])
+ {
+ object_type forge, *q_ptr;
+
+ msg_print("Oh thank you!");
+ msg_print("Take my sling and those mushrooms, may they help you!");
+ msg_print("Farmer Maggot heads back to his house.");
+
+ /* Mushrooms */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_FOOD, SV_FOOD_CURE_SERIOUS));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = rand_range(15, 20);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->discount = 100;
+ q_ptr->ident |= IDENT_STOREB;
+ if (inven_carry_okay(q_ptr))
+ inven_carry(q_ptr, FALSE);
+ else
+ drop_near(q_ptr, 0, p_ptr->py, p_ptr->px);
+
+ /* The sling of farmer maggot */
+ q_ptr = &forge;
+ object_prep(q_ptr, lookup_kind(TV_BOW, SV_SLING));
+ q_ptr->found = OBJ_FOUND_REWARD;
+ q_ptr->number = 1;
+ q_ptr->name1 = 149;
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->discount = 100;
+ q_ptr->ident |= IDENT_STOREB;
+ (void)inven_carry(q_ptr, FALSE);
+
+ delete_monster_idx(m_idx);
+
+ cquest.status = QUEST_STATUS_FINISHED;
+
+ del_hook_new(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;
+}
+
+static void check_dogs_alive(s32b m_idx)
+{
+ if ((r_info[get_grip()].max_num == 0) ||
+ (r_info[get_wolf()].max_num == 0) ||
+ (r_info[get_fang()].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_new(HOOK_GIVE, quest_shroom_give_hook);
+ del_hook_new(HOOK_MON_SPEAK, quest_shroom_speak_hook);
+ del_hook_new(HOOK_CHAT, quest_shroom_chat_hook);
+ del_hook_new(HOOK_WILD_GEN, quest_shroom_town_gen_hook);
+ process_hooks_restart = TRUE;
+ }
+ else
+ {
+ msg_format("You still have %d mushrooms to bring back!", cquest.data[1] - cquest.data[0]);
+ }
+}
+
+static bool_ quest_shroom_speak_hook(void *, void *in_, void *)
+{
+ struct hook_mon_speak_in *in = static_cast<struct hook_mon_speak_in *>(in_);
+ s32b m_idx = in->m_idx;
+
+ if (m_list[m_idx].r_idx != get_farmer_maggot()) return (FALSE);
+
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ msg_format("%^s asks your help.", in->m_name);
+ process_hooks_new(HOOK_MON_ASK_HELP, NULL, NULL);
+ }
+ else
+ {
+ check_dogs_alive(m_idx);
+ }
+ return (TRUE);
+}
+
+static bool_ quest_shroom_chat_hook(void *, void *in_, void *)
+{
+ struct hook_chat_in *in = static_cast<struct hook_chat_in *>(in_);
+ s32b m_idx = in->m_idx;
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (m_ptr->r_idx != get_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
+ {
+ check_dogs_alive(m_idx);
+ }
+
+ 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(format("Shrooms number %d", cquest.data[1]), TERM_BLUE);
+ }
+ }
+
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook_new(HOOK_MONSTER_DEATH, quest_shroom_death_hook, "shroom_death", NULL);
+ add_hook_new(HOOK_GIVE, quest_shroom_give_hook, "shroom_give", NULL);
+ add_hook_new(HOOK_WILD_GEN, quest_shroom_town_gen_hook, "shroom_town_gen", NULL);
+ add_hook_new(HOOK_CHAT, quest_shroom_chat_hook, "shroom_chat", NULL);
+ add_hook_new(HOOK_MON_SPEAK, quest_shroom_speak_hook, "shroom_speak", NULL);
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook_new(HOOK_MON_SPEAK, quest_shroom_speak_hook, "shroom_speak", NULL);
+ add_hook_new(HOOK_WILD_GEN, quest_shroom_town_gen_hook, "shroom_town_gen", NULL);
+ add_hook_new(HOOK_CHAT, quest_shroom_chat_hook, "shroom_chat", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_shroom.hpp b/src/q_shroom.hpp
new file mode 100644
index 00000000..6124775b
--- /dev/null
+++ b/src/q_shroom.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_shroom_init_hook(int q_idx);
diff --git a/src/q_spider.cc b/src/q_spider.cc
new file mode 100644
index 00000000..07531b96
--- /dev/null
+++ b/src/q_spider.cc
@@ -0,0 +1,127 @@
+#include "q_spider.hpp"
+
+#include "cave.hpp"
+#include "gods.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#define cquest (quest[QUEST_SPIDER])
+
+static bool_ quest_spider_gen_hook(void *, void *, void *)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_SPIDER) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("spiders.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ return TRUE;
+}
+
+static bool_ quest_spider_death_hook(void *, void *, void *)
+{
+ 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 */
+ if (p_ptr->pgod == 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_new(HOOK_MONSTER_DEATH, quest_spider_death_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+ }
+
+ return (FALSE);
+}
+
+static bool_ quest_spider_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ object_type forge, *q_ptr;
+ s32b q_idx = in->q_idx;
+
+ 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_new(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_new(HOOK_MONSTER_DEATH, quest_spider_death_hook, "spider_death", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_spider_gen_hook, "spider_gen", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_spider_finish_hook, "spider_finish", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_spider.hpp b/src/q_spider.hpp
new file mode 100644
index 00000000..e17cb523
--- /dev/null
+++ b/src/q_spider.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_spider_init_hook(int q_idx);
diff --git a/src/q_thief.cc b/src/q_thief.cc
new file mode 100644
index 00000000..0f6543cc
--- /dev/null
+++ b/src/q_thief.cc
@@ -0,0 +1,193 @@
+#include "q_thief.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "skill_type.hpp"
+#include "spells2.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#define cquest (quest[QUEST_THIEVES])
+
+static bool_ quest_thieves_gen_hook(void *, void *, void *)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+ bool_ again = TRUE;
+
+ if (p_ptr->inside_quest != QUEST_THIEVES) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("thieves.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Rip the inventory from the player */
+ cmsg_print(TERM_YELLOW, "You feel a vicious blow on your head.");
+ while (again)
+ {
+ again = FALSE;
+ for (x = 0; x < INVEN_TOTAL; x++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[x];
+
+ if (!o_ptr->k_idx) continue;
+
+ if ((x >= INVEN_WIELD) && cursed_p(o_ptr)) continue;
+
+ inven_drop(x, 99, 4, 24, TRUE);
+
+ /* Thats ugly .. but it works */
+ again = TRUE;
+ break;
+ }
+ }
+
+ del_hook_new(HOOK_GEN_QUEST, quest_thieves_gen_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_thieves_hook(void *, void *, void *)
+{
+ 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_new(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;
+}
+
+static bool_ quest_thieves_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_new(HOOK_QUEST_FINISH, quest_thieves_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_thieves_feeling_hook(void *, void *, void *)
+{
+ 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_new(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_new(HOOK_END_TURN, quest_thieves_hook, "thieves_end_turn", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_thieves_finish_hook, "thieves_finish", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_thieves_gen_hook, "thieves_geb", NULL);
+ add_hook_new(HOOK_FEELING, quest_thieves_feeling_hook, "thieves_feel", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_thief.hpp b/src/q_thief.hpp
new file mode 100644
index 00000000..48e5dc8d
--- /dev/null
+++ b/src/q_thief.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_thieves_init_hook(int q_idx);
diff --git a/src/q_thrain.cc b/src/q_thrain.cc
new file mode 100644
index 00000000..05def27a
--- /dev/null
+++ b/src/q_thrain.cc
@@ -0,0 +1,261 @@
+#include "q_thrain.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "dungeon_info_type.hpp"
+#include "generate.hpp"
+#include "hook_build_room1_in.hpp"
+#include "hook_move_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "lua_bind.hpp"
+#include "object_type.hpp"
+#include "quark.hpp"
+#include "randart.hpp"
+#include "messages.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define cquest (quest[QUEST_THRAIN])
+
+GENERATE_MONSTER_LOOKUP_FN(get_thrain, "Thrain, the King Under the Mountain")
+GENERATE_MONSTER_LOOKUP_FN(get_dwar, "Dwar, Dog Lord of Waw")
+GENERATE_MONSTER_LOOKUP_FN(get_hoarmurath, "Hoarmurath of Dir")
+
+static bool_ quest_thrain_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ int r, x, y;
+ monster_type *m_ptr;
+
+ 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 != get_dwar()) && (m_ptr->r_idx != get_hoarmurath())) 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 == get_thrain())
+ {
+ 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_new(HOOK_MONSTER_DEATH, quest_thrain_death_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+}
+
+static bool_ quest_thrain_gen_hook(void *, void *in_, void *)
+{
+ struct hook_build_room1_in *in = static_cast<struct hook_build_room1_in *>(in_);
+ s32b bx0 = in->x;
+ s32b by0 = in->y;
+ s32b x, y;
+ int xstart;
+ int ystart;
+ int y2, x2, yval, xval;
+ int y1, x1, xsize, ysize;
+
+ if (dungeon_type != DUNGEON_DOL_GULDUR) return (FALSE);
+ if (cquest.data[0] != dun_level) return (FALSE);
+ if (cquest.data[1]) return (FALSE);
+ if ((cquest.status < QUEST_STATUS_TAKEN) || (cquest.status >= QUEST_STATUS_FINISHED)) return (FALSE);
+
+ /* Pick a room size */
+ get_map_size("thrain.map", &ysize, &xsize);
+
+ /* Try to allocate space for room. If fails, exit */
+ if (!room_alloc(xsize + 2, ysize + 2, FALSE, by0, bx0, &xval, &yval)) return FALSE;
+
+ /* Get corner values */
+ y1 = yval - ysize / 2;
+ x1 = xval - xsize / 2;
+ y2 = y1 + ysize - 1;
+ x2 = x1 + xsize - 1;
+
+ /* Place a full floor under the room */
+ for (y = y1; y <= y2; y++)
+ {
+ for (x = x1; x <= x2; x++)
+ {
+ cave_set_feat(y, x, floor_type[rand_int(100)]);
+ cave[y][x].info |= (CAVE_ROOM|CAVE_GLOW);
+ }
+ }
+
+ build_rectangle(y1 - 1, x1 - 1, y2 + 1, x2 + 1, feat_wall_outer, CAVE_ROOM | CAVE_GLOW);
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ xstart = x1;
+ ystart = y1;
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("thrain.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = x1; x < xstart; x++)
+ for (y = y1; y < ystart; y++)
+ {
+ cave[y][x].info |= CAVE_ICKY | CAVE_ROOM | CAVE_FREE;
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ m_allow_special[get_thrain()] = TRUE;
+ int i = place_monster_one(y, x, get_thrain(), 0, FALSE, MSTATUS_NEUTRAL);
+ m_allow_special[get_thrain()] = FALSE;
+
+ if (i) m_list[i].mflag |= MFLAG_QUEST;
+ }
+ }
+
+ /* Don't try another one for this generation */
+ cquest.data[1] = 1;
+
+ return (TRUE);
+}
+
+static bool_ quest_thrain_feeling_hook(void *, void *, void *)
+{
+ 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);
+}
+
+static bool_ quest_thrain_move_hook(void *, void *in_, void *)
+{
+ struct hook_move_in *in = static_cast<struct hook_move_in *>(in_);
+ s32b y = in->y;
+ s32b x = in->x;
+ cave_type *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);
+}
+
+static bool_ quest_thrain_turn_hook(void *, void *, void *)
+{
+ 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(format("Thrain lvl %d", cquest.data[0]), TERM_BLUE);
+ }
+ }
+ if ((cquest.status >= QUEST_STATUS_TAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook_new(HOOK_MOVE, quest_thrain_move_hook, "thrain_move", NULL);
+ }
+ if ((cquest.status >= QUEST_STATUS_UNTAKEN) && (cquest.status < QUEST_STATUS_FINISHED))
+ {
+ add_hook_new(HOOK_LEVEL_REGEN, quest_thrain_turn_hook, "thrain_regen_lvl", NULL);
+ add_hook_new(HOOK_NEW_LEVEL, quest_thrain_turn_hook, "thrain_new_lvl", NULL);
+ add_hook_new(HOOK_BUILD_ROOM1, quest_thrain_gen_hook, "thrain_gen", NULL);
+ add_hook_new(HOOK_FEELING, quest_thrain_feeling_hook, "thrain_feel", NULL);
+ add_hook_new(HOOK_MONSTER_DEATH, quest_thrain_death_hook, "thrain_death", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_thrain.hpp b/src/q_thrain.hpp
new file mode 100644
index 00000000..830da016
--- /dev/null
+++ b/src/q_thrain.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern bool_ quest_thrain_init_hook(int q_idx);
diff --git a/src/q_troll.cc b/src/q_troll.cc
new file mode 100644
index 00000000..fce3ad49
--- /dev/null
+++ b/src/q_troll.cc
@@ -0,0 +1,194 @@
+#include "q_troll.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define cquest (quest[QUEST_TROLL])
+
+GENERATE_MONSTER_LOOKUP_FN(get_tom, "Tom the Stone Troll")
+GENERATE_MONSTER_LOOKUP_FN(get_stone_troll, "Stone troll")
+GENERATE_MONSTER_LOOKUP_FN(get_forest_troll, "Forest troll")
+
+static bool_ quest_troll_gen_hook(void *, void *, void *)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_TROLL) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("trolls.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ m_allow_special[get_tom()] = TRUE;
+ int m_idx = place_monster_one(y, x, get_tom(), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[get_tom()] = FALSE;
+
+ if (m_idx)
+ {
+ int o_idx;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ m_list[m_idx].mflag |= MFLAG_QUEST;
+
+ a_allow_special[ART_GLAMDRING] = TRUE;
+
+ /* Mega-Hack -- Prepare to make "Glamdring" */
+ object_prep(q_ptr, lookup_kind(TV_SWORD, SV_BROAD_SWORD));
+
+ /* Mega-Hack -- Mark this item as "Glamdring" */
+ q_ptr->name1 = ART_GLAMDRING;
+
+ /* Mega-Hack -- Actually create "Glamdring" */
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ a_allow_special[ART_GLAMDRING] = FALSE;
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ if (o_idx)
+ {
+ /* Get the item */
+ object_type *o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Add to monster's inventory */
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+ m_list[m_idx].hold_o_idxs.push_back(o_idx);
+ }
+ else
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ }
+ }
+ }
+
+ /* Reinitialize the ambush ... hehehe */
+ cquest.data[0] = FALSE;
+ return TRUE;
+}
+
+static bool_ quest_troll_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_new(HOOK_QUEST_FINISH, quest_troll_finish_hook);
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_troll_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ s32b r_idx = m_list[m_idx].r_idx;
+ int x, y, xstart = 2, ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_TROLL) return FALSE;
+
+ if (r_idx == get_tom())
+ {
+ 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_new(HOOK_MONSTER_DEATH, quest_troll_death_hook);
+ process_hooks_restart = TRUE;
+ return (FALSE);
+ }
+
+ init_flags = INIT_GET_SIZE;
+ process_dungeon_file("trolls.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ if (cquest.data[0]) return FALSE;
+
+ cquest.data[0] = TRUE;
+
+ msg_print("Oops, seems like an ambush...");
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ cave_type *c_ptr = &cave[y][x];
+
+ /* Ahah ! */
+ if (c_ptr->info & CAVE_SPEC)
+ {
+ cave_set_feat(y, x, FEAT_GRASS);
+ c_ptr->info &= ~CAVE_SPEC;
+
+ int r_idx = (rand_int(2) == 0) ? get_forest_troll() : get_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_new(HOOK_MONSTER_DEATH, quest_troll_death_hook, "troll_death", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_troll_gen_hook, "troll_gen", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_troll_finish_hook, "troll_finish", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_troll.hpp b/src/q_troll.hpp
new file mode 100644
index 00000000..140fe0b2
--- /dev/null
+++ b/src/q_troll.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_troll_init_hook(int q_idx);
diff --git a/src/q_ultrae.cc b/src/q_ultrae.cc
new file mode 100644
index 00000000..d42b9c6f
--- /dev/null
+++ b/src/q_ultrae.cc
@@ -0,0 +1,8 @@
+#include "q_ultrae.hpp"
+
+#define cquest (quest[QUEST_ULTRA_EVIL])
+
+bool_ quest_ultra_evil_init_hook(int q)
+{
+ return (FALSE);
+}
diff --git a/src/q_ultrae.hpp b/src/q_ultrae.hpp
new file mode 100644
index 00000000..5b08127b
--- /dev/null
+++ b/src/q_ultrae.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_ultra_evil_init_hook(int q_idx);
diff --git a/src/q_ultrag.cc b/src/q_ultrag.cc
new file mode 100644
index 00000000..c7c0312b
--- /dev/null
+++ b/src/q_ultrag.cc
@@ -0,0 +1,292 @@
+#include "q_ultrag.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "hook_chardump_in.hpp"
+#include "hook_move_in.hpp"
+#include "hook_stair_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hooks.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+
+#define cquest (quest[QUEST_ULTRA_GOOD])
+
+static bool_ quest_ultra_good_move_hook(void *, void *in_, void *)
+{
+ struct hook_move_in *in = static_cast<struct hook_move_in *>(in_);
+ s32b y = in->y;
+ s32b x = in->x;
+ cave_type *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;
+}
+
+static bool_ quest_ultra_good_stair_hook(void *, void *in_, void *)
+{
+ struct hook_stair_in *in = static_cast<struct hook_stair_in *>(in_);
+ stairs_direction dir = in->direction;
+
+ if (dungeon_type != DUNGEON_VOID)
+ return FALSE;
+
+ /* Cant leave */
+ if ((dir == STAIRS_UP) && (dun_level == 128))
+ {
+ cmsg_print(TERM_YELLOW, "The portal to Arda is now closed.");
+ return TRUE;
+ }
+ /* there is no coming back */
+ if ((dir == STAIRS_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 ((dir == STAIRS_DOWN) && (dun_level == 149))
+ {
+ int i;
+ bool_ ultimate = FALSE;
+
+ /* Now look for an ULTIMATE artifact, that is, one imbued with the flame */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_type *o_ptr = get_object(i);
+
+ if (!o_ptr->k_idx) continue;
+
+ /* Examine the gloves */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if (f4 & TR4_ULTIMATE)
+ {
+ ultimate = TRUE;
+ break;
+ }
+ }
+
+ if (!ultimate)
+ {
+ cmsg_print(TERM_YELLOW, "It seems the level is protected by an impassable barrier of pure magic.");
+ cmsg_print(TERM_YELLOW, "Only the most powerful magic could remove it. You will need to use");
+ cmsg_print(TERM_YELLOW, "the Flame Imperishable to pass. The source of Eru Iluvatar's own power.");
+ return TRUE;
+ }
+ else
+ {
+ cmsg_print(TERM_YELLOW, "The power of the Flame Imperishable shatters the magical barrier.");
+ cmsg_print(TERM_YELLOW, "The way before you is free.");
+ }
+ }
+
+ return FALSE;
+}
+
+static bool_ quest_ultra_good_recall_hook(void *, void *, void *)
+{
+ 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;
+}
+
+static bool_ quest_ultra_good_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+
+ 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_FRAME);
+
+ /* 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_new(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);
+}
+
+static bool_ quest_ultra_good_dump_hook(void *, void *in_, void *)
+{
+ struct hook_chardump_in *in = static_cast<struct hook_chardump_in *>(in_);
+ FILE *f = in->file;
+
+ if (quest[QUEST_ULTRA_GOOD].status >= QUEST_STATUS_TAKEN)
+ {
+ /* Ultra winner ! */
+ if (total_winner == WINNER_ULTRA)
+ {
+ fprintf(f, "\n You destroyed Melkor forever and have been elevated to the status of Vala by Eru Iluvatar.");
+ fprintf(f, "\n Arda will forever be free.");
+ }
+ else
+ {
+ /* Tried and failed */
+ if (death)
+ {
+ fprintf(f, "\n You tried to destroy Melkor forever, but died in the attempt.");
+ fprintf(f, "\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_new(HOOK_STAIR, quest_ultra_good_stair_hook, "ultrag_stair", NULL);
+ add_hook_new(HOOK_RECALL, quest_ultra_good_recall_hook, "ultrag_recall", NULL);
+ add_hook_new(HOOK_MONSTER_DEATH, quest_ultra_good_death_hook, "ultrag_death", NULL);
+ }
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ {
+ add_hook_new(HOOK_MOVE, quest_ultra_good_move_hook, "ultrag_move", NULL);
+ }
+ add_hook_new(HOOK_CHAR_DUMP, quest_ultra_good_dump_hook, "ultrag_dump", NULL);
+ return (FALSE);
+}
diff --git a/src/q_ultrag.hpp b/src/q_ultrag.hpp
new file mode 100644
index 00000000..0064b878
--- /dev/null
+++ b/src/q_ultrag.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_ultra_good_init_hook(int q_idx);
diff --git a/src/q_wight.cc b/src/q_wight.cc
new file mode 100644
index 00000000..f2cc630c
--- /dev/null
+++ b/src/q_wight.cc
@@ -0,0 +1,179 @@
+#include "q_wight.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object2.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define cquest (quest[QUEST_WIGHT])
+
+GENERATE_MONSTER_LOOKUP_FN(get_wight_king, "The Wight-King of the Barrow-downs")
+
+static bool_ quest_wight_gen_hook(void *, void *, void *)
+{
+ int x, y;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_WIGHT) return FALSE;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("wights.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, TRUE);
+
+ for (x = 3; x < xstart; x++)
+ for (y = 3; y < ystart; y++)
+ {
+ if (cave[y][x].feat == FEAT_MARKER)
+ {
+ int m_idx = 0;
+
+ m_allow_special[get_wight_king()] = TRUE;
+ m_idx = place_monster_one(y, x, get_wight_king(), 0, FALSE, MSTATUS_ENEMY);
+ m_allow_special[get_wight_king()] = FALSE;
+
+ if (m_idx)
+ {
+ int o_idx;
+
+ /* Get local object */
+ object_type forge, *q_ptr = &forge;
+
+ m_list[m_idx].mflag |= MFLAG_QUEST;
+
+ /* Prepare to make the */
+ object_prep(q_ptr, lookup_kind(TV_SOFT_ARMOR, SV_FILTHY_RAG));
+
+ /* Name the rags */
+
+ q_ptr->art_name = quark_add("of the Wight");
+
+ q_ptr->art_flags1 |= ( TR1_INT | TR1_SEARCH );
+ q_ptr->art_flags2 |= ( TR2_RES_BLIND | TR2_SENS_FIRE | TR2_RES_CONF );
+ q_ptr->art_flags3 |= ( TR3_IGNORE_ACID | TR3_IGNORE_ELEC |
+ TR3_IGNORE_FIRE | TR3_IGNORE_COLD | TR3_SEE_INVIS);
+
+ /* For game balance... */
+ q_ptr->art_flags3 |= (TR3_CURSED | TR3_HEAVY_CURSE);
+ q_ptr->ident |= IDENT_CURSED;
+
+ if (randint(2) == 1)
+ {
+ q_ptr->art_flags1 |= (TR1_SPELL);
+ q_ptr->pval = 6;
+ }
+ else
+ {
+ q_ptr->art_flags1 |= (TR1_MANA);
+ q_ptr->pval = 2;
+ }
+
+ /* Get new object */
+ o_idx = o_pop();
+
+ if (o_idx)
+ {
+ /* Get the item */
+ object_type *o_ptr = &o_list[o_idx];
+
+ /* Structure copy */
+ object_copy(o_ptr, q_ptr);
+
+ /* Build a stack */
+ o_ptr->held_m_idx = m_idx;
+ o_ptr->ix = 0;
+ o_ptr->iy = 0;
+
+ m_list[m_idx].hold_o_idxs.push_back(o_idx);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static bool_ quest_wight_death_hook(void *, void *in_, void *)
+{
+ struct hook_monster_death_in *in = static_cast<struct hook_monster_death_in *>(in_);
+ s32b m_idx = in->m_idx;
+ s32b r_idx = m_list[m_idx].r_idx;
+
+ if (p_ptr->inside_quest != QUEST_WIGHT) return FALSE;
+
+ if (r_idx == get_wight_king())
+ {
+ 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_new(HOOK_MONSTER_DEATH, quest_wight_death_hook);
+ process_hooks_restart = TRUE;
+
+ return (FALSE);
+ }
+
+ return (FALSE);
+}
+
+static bool_ quest_wight_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_new(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_new(HOOK_MONSTER_DEATH, quest_wight_death_hook, "wight_death", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_wight_gen_hook, "wight_gen", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_wight_finish_hook, "wight_finish", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_wight.hpp b/src/q_wight.hpp
new file mode 100644
index 00000000..1252e4fa
--- /dev/null
+++ b/src/q_wight.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_wight_init_hook(int q_idx);
diff --git a/src/q_wolves.cc b/src/q_wolves.cc
new file mode 100644
index 00000000..c5863b59
--- /dev/null
+++ b/src/q_wolves.cc
@@ -0,0 +1,146 @@
+#include "q_wolves.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "feature_type.hpp"
+#include "hook_quest_finish_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "player_type.hpp"
+#include "quest_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#define cquest (quest[QUEST_WOLVES])
+
+static bool_ quest_wolves_gen_hook(void *, void *, void *)
+{
+ int x, y, i;
+ int xstart = 2;
+ int ystart = 2;
+
+ if (p_ptr->inside_quest != QUEST_WOLVES) return FALSE;
+
+ /* Just in case we didnt talk the the mayor */
+ if (cquest.status == QUEST_STATUS_UNTAKEN)
+ cquest.status = QUEST_STATUS_TAKEN;
+
+ /* Start with perm walls */
+ for (y = 0; y < cur_hgt; y++)
+ {
+ for (x = 0; x < cur_wid; x++)
+ {
+ cave_set_feat(y, x, FEAT_PERM_SOLID);
+ }
+ }
+
+ dun_level = quest[p_ptr->inside_quest].level;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("wolves.map", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ dungeon_flags2 |= DF2_NO_GENO;
+
+ /* Place some random wolves */
+ for (i = damroll(4, 4); i > 0; )
+ {
+ int m_idx, flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ m_idx = place_monster_one(y, x, 196, 0, magik(50), MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ /* Place some random wargs */
+ for (i = damroll(4, 4); i > 0; )
+ {
+ int m_idx, flags;
+ y = rand_int(21) + 3;
+ x = rand_int(31) + 3;
+ flags = f_info[cave[y][x].feat].flags1;
+ if (!(flags & FF1_PERMANENT) && (flags & FF1_FLOOR))
+ {
+ m_idx = place_monster_one(y, x, 257, 0, magik(50), MSTATUS_ENEMY);
+ if (m_idx) m_list[m_idx].mflag |= MFLAG_QUEST;
+ --i;
+ }
+ }
+
+ process_hooks_restart = TRUE;
+
+ return TRUE;
+}
+
+static bool_ quest_wolves_death_hook(void *, void *, void *)
+{
+ 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_new(HOOK_MONSTER_DEATH, quest_wolves_death_hook);
+ del_hook_new(HOOK_GEN_QUEST, quest_wolves_gen_hook);
+ process_hooks_restart = TRUE;
+
+ cmsg_print(TERM_YELLOW, "Lothlorien is safer now.");
+ return (FALSE);
+ }
+ return FALSE;
+}
+
+static bool_ quest_wolves_finish_hook(void *, void *in_, void *)
+{
+ struct hook_quest_finish_in *in = static_cast<struct hook_quest_finish_in *>(in_);
+ s32b q_idx = in->q_idx;
+
+ 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_new(HOOK_MONSTER_DEATH, quest_wolves_death_hook, "wolves_monster_death", NULL);
+ add_hook_new(HOOK_QUEST_FINISH, quest_wolves_finish_hook, "wolves_finish", NULL);
+ add_hook_new(HOOK_GEN_QUEST, quest_wolves_gen_hook, "wolves_geb", NULL);
+ }
+ return (FALSE);
+}
diff --git a/src/q_wolves.hpp b/src/q_wolves.hpp
new file mode 100644
index 00000000..59a83c56
--- /dev/null
+++ b/src/q_wolves.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "h-basic.h"
+
+bool_ quest_wolves_init_hook(int q_idx);
diff --git a/src/quark.cc b/src/quark.cc
new file mode 100644
index 00000000..45072ded
--- /dev/null
+++ b/src/quark.cc
@@ -0,0 +1,96 @@
+#include "quark.hpp"
+
+#include "z-util.h"
+
+#include <cassert>
+
+/*
+ * The number of quarks
+ */
+static s16b quark__num = 0;
+
+
+/*
+ * The pointers to the quarks [QUARK_MAX]
+ */
+static cptr *quark__str = NULL;
+
+
+/*
+ * Initialize the quark subsystem
+ */
+void quark_init()
+{
+ quark__num = 0;
+ quark__str = new cptr[QUARK_MAX];
+
+ for (int i = 0; i < QUARK_MAX; i++) {
+ quark__str[i] = nullptr;
+ }
+}
+
+
+/*
+* 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)
+{
+ assert(str != nullptr);
+
+ 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] = strdup(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);
+}
+
+
diff --git a/src/quark.hpp b/src/quark.hpp
new file mode 100644
index 00000000..0fce3932
--- /dev/null
+++ b/src/quark.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Maximum number of quarks.
+ */
+constexpr int QUARK_MAX = 768;
+
+void quark_init();
+cptr quark_str(s16b num);
+s16b quark_add(cptr str);
diff --git a/src/quest.cc b/src/quest.cc
new file mode 100644
index 00000000..a1aee67f
--- /dev/null
+++ b/src/quest.cc
@@ -0,0 +1,17 @@
+#include "quest.hpp"
+
+#include "tables.hpp"
+
+#include <cstddef>
+
+void init_hooks_quests()
+{
+ for (std::size_t i = 0; i < MAX_Q_IDX; i++)
+ {
+ if (quest[i].init != NULL)
+ {
+ quest[i].init(i);
+ }
+ }
+}
+
diff --git a/src/quest.hpp b/src/quest.hpp
new file mode 100644
index 00000000..7ff3cd3c
--- /dev/null
+++ b/src/quest.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+extern void init_hooks_quests();
diff --git a/src/quest_type.hpp b/src/quest_type.hpp
new file mode 100644
index 00000000..aa99f40a
--- /dev/null
+++ b/src/quest_type.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Quest descriptor and runtime data.
+ */
+struct quest_type
+{
+ bool_ silent;
+
+ 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? */
+
+ bool_ (*init)(int q); /* Function that takes care of generating hardcoded quests */
+
+ s32b data[9]; /* Various datas used by the quests */
+
+ bool_ (*gen_desc)(FILE *fff); /* Function for generating description. */
+};
diff --git a/src/randart.cc b/src/randart.cc
new file mode 100644
index 00000000..5135438a
--- /dev/null
+++ b/src/randart.cc
@@ -0,0 +1,481 @@
+/*
+ * 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 "randart.hpp"
+#include "mimic.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "randart_gen_type.hpp"
+#include "randart_part_type.hpp"
+#include "spells2.hpp"
+#include "util.hpp"
+#include "variable.h"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <memory>
+#include <vector>
+
+/* 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)
+{
+ bool_ ret = FALSE;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ std::vector<size_t> ok_ra;
+
+ /* Grab the ok randart */
+ for (size_t i = 0; i < max_ra_idx; i++)
+ {
+ randart_part_type *ra_ptr = &ra_info[i];
+ bool_ ok = FALSE;
+
+ /* Must have the correct fields */
+ for (size_t 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.push_back(i);
+ }
+
+ /* Now test them a few times */
+ for (size_t count = 0; count < ok_ra.size() * 10; count++)
+ {
+ size_t i = ok_ra[rand_int(ok_ra.size())];
+ randart_part_type *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;
+ }
+
+ /* Return */
+ return (ret);
+}
+
+void give_activation_power (object_type * o_ptr)
+{
+ o_ptr->xtra2 = 0;
+ o_ptr->art_flags3 &= ~TR3_ACTIVATE;
+ o_ptr->timeout = 0;
+}
+
+
+int get_activation_power()
+{
+ object_type *o_ptr, forge;
+
+ o_ptr = &forge;
+
+ give_activation_power(o_ptr);
+
+ return o_ptr->xtra2;
+}
+
+#define MIN_NAME_LEN 5
+#define MAX_NAME_LEN 9
+#define S_WORD 26
+#define E_WORD S_WORD
+
+static long lprobs[S_WORD + 1][S_WORD + 1][S_WORD + 1];
+static long ltotal[S_WORD + 1][S_WORD + 1];
+
+/*
+ * Use W. Sheldon Simms' random name generator. This function builds
+ * probability tables which are used later on for letter selection. It
+ * relies on the ASCII character set.
+ */
+void build_prob(cptr learn)
+{
+ int c_prev, c_cur, c_next;
+
+ /* Build raw frequencies */
+ while (1)
+ {
+ c_prev = c_cur = S_WORD;
+
+ do
+ {
+ c_next = *learn++;
+ }
+ while (!isalpha(c_next) && (c_next != '\0'));
+
+ if (c_next == '\0') break;
+
+ do
+ {
+ c_next = A2I(tolower(c_next));
+ lprobs[c_prev][c_cur][c_next]++;
+ ltotal[c_prev][c_cur]++;
+ c_prev = c_cur;
+ c_cur = c_next;
+ c_next = *learn++;
+ }
+ while (isalpha(c_next));
+
+ lprobs[c_prev][c_cur][E_WORD]++;
+ ltotal[c_prev][c_cur]++;
+ }
+}
+
+
+/*
+ * Use W. Sheldon Simms' random name generator. Generate a random word using
+ * the probability tables we built earlier. Relies on the ASCII character
+ * set. Relies on European vowels (a, e, i, o, u). The generated name should
+ * be copied/used before calling this function again.
+ */
+static char *make_word(void)
+{
+ static char word_buf[90];
+ int r, totalfreq;
+ int tries, lnum, vow;
+ int c_prev, c_cur, c_next;
+ char *cp;
+
+startover:
+ vow = 0;
+ lnum = 0;
+ tries = 0;
+ cp = word_buf;
+ c_prev = c_cur = S_WORD;
+
+ while (1)
+ {
+getletter:
+ c_next = 0;
+ r = rand_int(ltotal[c_prev][c_cur]);
+ totalfreq = lprobs[c_prev][c_cur][c_next];
+
+ while (totalfreq <= r)
+ {
+ c_next++;
+ totalfreq += lprobs[c_prev][c_cur][c_next];
+ }
+
+ if (c_next == E_WORD)
+ {
+ if ((lnum < MIN_NAME_LEN) || vow == 0)
+ {
+ tries++;
+ if (tries < 10) goto getletter;
+ goto startover;
+ }
+ *cp = '\0';
+ break;
+ }
+
+ if (lnum >= MAX_NAME_LEN) goto startover;
+
+ *cp = I2A(c_next);
+
+ if (is_a_vowel(*cp)) vow++;
+
+ cp++;
+ lnum++;
+ c_prev = c_cur;
+ c_cur = c_next;
+ }
+
+ word_buf[0] = toupper(word_buf[0]);
+
+ return (word_buf);
+}
+
+
+void get_random_name(char * return_name)
+{
+ char *word = make_word();
+
+ if (rand_int(3) == 0)
+ sprintf(return_name, "'%s'", word);
+ else
+ sprintf(return_name, "of %s", word);
+}
+
+
+bool_ create_artifact(object_type *o_ptr, bool_ a_scroll, bool_ get_name)
+{
+ char new_name[80];
+ int powers = 0, i;
+ s32b total_flags, total_power = 0;
+ bool_ a_cursed = FALSE;
+ u32b f1, f2, f3, f4, f5, esp;
+ s16b 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;
+
+ std::unique_ptr<s16b[]> max_times(new s16b[max_ra_idx]);
+ for (int i = 0; i < max_ra_idx; i++) {
+ max_times[i] = 0;
+ }
+
+ /* Main loop */
+ while (powers)
+ {
+ int ra_idx;
+ randart_part_type *ra_ptr;
+
+ powers--;
+
+ if (!grab_one_power(&ra_idx, o_ptr, TRUE, max_times.get())) 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;
+ };
+
+ 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 = find_random_mimic_shape(127, TRUE);
+ o_ptr->pval2 = mimic;
+ }
+ else if (f5 & TR5_SPELL_CONTAIN)
+ {
+ o_ptr->pval2 = -1;
+ }
+
+ return TRUE;
+}
+
+
+bool_ artifact_scroll(void)
+{
+ bool_ okay = FALSE;
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Enchant which item? ",
+ "You have nothing to enchant.",
+ (USE_EQUIP | USE_INVEN | USE_FLOOR),
+ item_tester_hook_artifactable()))
+ {
+ return (FALSE);
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Description */
+ char o_name[80];
+ 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/randart.hpp b/src/randart.hpp
new file mode 100644
index 00000000..31b70f08
--- /dev/null
+++ b/src/randart.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type_fwd.hpp"
+
+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);
diff --git a/src/randart_gen_type.hpp b/src/randart_gen_type.hpp
new file mode 100644
index 00000000..09aedcd9
--- /dev/null
+++ b/src/randart_gen_type.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+struct randart_gen_type
+{
+ int chance; /* Chance to have that number of powers */
+ int dd;
+ int ds;
+ int plus; /* xdy+plus power */
+};
diff --git a/src/randart_gen_type_fwd.hpp b/src/randart_gen_type_fwd.hpp
new file mode 100644
index 00000000..eba3e84e
--- /dev/null
+++ b/src/randart_gen_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct randart_gen_type;
diff --git a/src/randart_part_type.hpp b/src/randart_part_type.hpp
new file mode 100644
index 00000000..c2fa5386
--- /dev/null
+++ b/src/randart_part_type.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Random artifact part descriptor.
+ */
+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) */
+};
diff --git a/src/randart_part_type_fwd.hpp b/src/randart_part_type_fwd.hpp
new file mode 100644
index 00000000..979fd72c
--- /dev/null
+++ b/src/randart_part_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct randart_part_type;
diff --git a/src/random_artifact.hpp b/src/random_artifact.hpp
new file mode 100644
index 00000000..a3fc1c66
--- /dev/null
+++ b/src/random_artifact.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Random artifact descriptor.
+ */
+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? */
+};
diff --git a/src/random_quest.hpp b/src/random_quest.hpp
new file mode 100644
index 00000000..11ebe797
--- /dev/null
+++ b/src/random_quest.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "h-basic.h"
+
+struct random_quest
+{
+ byte type; /* Type/number of monsters to kill(0 = no quest) */
+ s16b r_idx; /* Monsters to crush */
+ bool_ done; /* Done ? */
+};
diff --git a/src/random_spell.hpp b/src/random_spell.hpp
new file mode 100644
index 00000000..01b5ba5e
--- /dev/null
+++ b/src/random_spell.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * A structure to describe the random spells of the Power Mages
+ */
+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? */
+};
diff --git a/src/range.cc b/src/range.cc
new file mode 100644
index 00000000..7bd407c7
--- /dev/null
+++ b/src/range.cc
@@ -0,0 +1,11 @@
+#include "range.hpp"
+
+#include <cassert>
+
+void range_init(range_type *range, s32b min, s32b max)
+{
+ assert(range != NULL);
+
+ range->min = min;
+ range->max = max;
+}
diff --git a/src/range.hpp b/src/range.hpp
new file mode 100644
index 00000000..3c185ba6
--- /dev/null
+++ b/src/range.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "range_fwd.hpp"
+#include "h-basic.h"
+
+/*
+ * Range
+ */
+struct range_type
+{
+ s32b min;
+ s32b max;
+};
+
+void range_init(range_type *range, s32b min, s32b max);
diff --git a/src/range_fwd.hpp b/src/range_fwd.hpp
new file mode 100644
index 00000000..b5eef3fa
--- /dev/null
+++ b/src/range_fwd.hpp
@@ -0,0 +1,4 @@
+#pragma once
+
+typedef struct range_type range_type;
+struct range_type;
diff --git a/src/rule_type.hpp b/src/rule_type.hpp
new file mode 100644
index 00000000..a8b35ffa
--- /dev/null
+++ b/src/rule_type.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "h-basic.h"
+
+/* Define monster generation rules */
+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 */
+};
diff --git a/src/rune_spell.hpp b/src/rune_spell.hpp
new file mode 100644
index 00000000..d04a8dc4
--- /dev/null
+++ b/src/rune_spell.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Runecrafter prefered spells
+ */
+struct rune_spell
+{
+ char name[30]; /* name */
+
+ s16b type; /* Type of the spell(GF) */
+ s16b rune2; /* Modifiers */
+ s16b mana; /* Mana involved */
+};
diff --git a/src/rune_spell_fwd.hpp b/src/rune_spell_fwd.hpp
new file mode 100644
index 00000000..eb540a2a
--- /dev/null
+++ b/src/rune_spell_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct rune_spell;
diff --git a/src/school_book.hpp b/src/school_book.hpp
new file mode 100644
index 00000000..51d3e6a7
--- /dev/null
+++ b/src/school_book.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "h-basic.h"
+
+#include <vector>
+
+/**
+ * School book.
+ */
+struct school_book {
+ /**
+ * Indexes of all the spells in the book.
+ */
+ std::vector<s32b> spell_idxs;
+};
diff --git a/src/school_book_fwd.hpp b/src/school_book_fwd.hpp
new file mode 100644
index 00000000..46363da6
--- /dev/null
+++ b/src/school_book_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct school_book;
diff --git a/src/school_type.hpp b/src/school_type.hpp
new file mode 100644
index 00000000..7a5702b4
--- /dev/null
+++ b/src/school_type.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "h-basic.h"
+#include "deity_type_fwd.hpp"
+
+struct school_type
+{
+ cptr name; /* Name */
+ s16b skill; /* Skill used for that school */
+ bool_ spell_power; /* Does spell power affect spells in this school? */
+ bool_ sorcery; /* Does Sorcery affect this school? */
+
+ int deity_idx; /* Deity; if <=0, no deity required */
+ deity_type *deity; /* Direct pointer to deity */
+
+ int (*bonus_levels)(); /* Calculate number of bonus levels */
+
+ bool_ (*depends_satisfied)(); /* Are dependendies satisfied? */
+
+ struct school_provider_list *providers; /* List of secondary providers of this school */
+};
diff --git a/src/school_type_fwd.hpp b/src/school_type_fwd.hpp
new file mode 100644
index 00000000..dea0d3b4
--- /dev/null
+++ b/src/school_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct school_type;
diff --git a/src/script.cc b/src/script.cc
new file mode 100644
index 00000000..84f7c3e4
--- /dev/null
+++ b/src/script.cc
@@ -0,0 +1,30 @@
+/*
+ * 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 "script.h"
+
+#include "init2.hpp"
+#include "q_library.hpp"
+#include "spells4.hpp"
+#include "spells5.hpp"
+#include "spells6.hpp"
+
+
+void init_lua_init()
+{
+ /* Initialize schooled spells */
+ schools_init();
+ school_spells_init();
+ init_school_books();
+
+ /* Post-spell creation initialization */
+ initialize_bookable_spells();
+
+ /* Finish up the corruptions */
+ init_corruptions();
+}
diff --git a/src/script.h b/src/script.h
new file mode 100644
index 00000000..3d1a0840
--- /dev/null
+++ b/src/script.h
@@ -0,0 +1,12 @@
+#pragma once
+
+// C linkage required for these functions since main-* code uses them.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void init_lua_init(void);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/set_type.hpp b/src/set_type.hpp
new file mode 100644
index 00000000..827c23ac
--- /dev/null
+++ b/src/set_type.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Item set descriptor and runtime information.
+ */
+struct set_type
+{
+ const char *name; /* Name */
+ char *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];
+};
diff --git a/src/set_type_fwd.hpp b/src/set_type_fwd.hpp
new file mode 100644
index 00000000..3b311808
--- /dev/null
+++ b/src/set_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct set_type;
diff --git a/src/skill_type.hpp b/src/skill_type.hpp
new file mode 100644
index 00000000..c6de1dc1
--- /dev/null
+++ b/src/skill_type.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "h-basic.h"
+#include "skills_defs.hpp"
+
+/**
+ * Skill descriptors and runtime data.
+ */
+struct skill_type
+{
+ const char *name; /* Name */
+ char *desc; /* Description */
+
+ const char *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 */
+};
diff --git a/src/skill_type_fwd.hpp b/src/skill_type_fwd.hpp
new file mode 100644
index 00000000..0a06dadb
--- /dev/null
+++ b/src/skill_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct skill_type;
diff --git a/src/skills.cc b/src/skills.cc
new file mode 100644
index 00000000..3a14a6ce
--- /dev/null
+++ b/src/skills.cc
@@ -0,0 +1,1829 @@
+/*
+ * 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 "skills.hpp"
+
+#include "ability_type.hpp"
+#include "birth.hpp"
+#include "cmd2.hpp"
+#include "cmd3.hpp"
+#include "cmd5.hpp"
+#include "cmd7.hpp"
+#include "gods.hpp"
+#include "help.hpp"
+#include "hooks.hpp"
+#include "lua_bind.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "player_class.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_spec.hpp"
+#include "player_type.hpp"
+#include "skill_type.hpp"
+#include "spells1.hpp"
+#include "spells4.hpp"
+#include "tables.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <algorithm>
+#include <boost/algorithm/string/predicate.hpp>
+#include <cassert>
+#include <cmath>
+#include <memory>
+#include <vector>
+#include <tuple>
+
+using boost::algorithm::iequals;
+
+/*
+ * Advance the skill point of the skill specified by i and
+ * modify related skills
+ */
+static 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 */
+ max_skill_overage = modules[game_module_idx].skills.max_skill_overage;
+ if (((s_info[i].value + s_info[i].mod) / SKILL_STEP) >= (p_ptr->lev + max_skill_overage + 1))
+ {
+ int hgt, wid;
+ char buf[256];
+
+ sprintf(buf,
+ "Cannot raise a skill value above " FMTs32b " + player level.",
+ max_skill_overage);
+
+ Term_get_size(&wid, &hgt);
+ msg_box(buf, hgt / 2, 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
+ */
+static 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++)
+ {
+ if (s_info[i].name && streq(s_info[i].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 (s_info[i].name && iequals(s_info[i].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);
+}
+
+static int get_idx(int i)
+{
+ for (int 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;
+}
+
+static void init_table_aux(int table[MAX_SKILLS][2], int *idx, int father, int lev, bool_ full)
+{
+ for (int j = 1; j < max_s_idx; j++)
+ {
+ int 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);
+ }
+}
+
+static void init_table(int table[MAX_SKILLS][2], int *max, bool_ full)
+{
+ *max = 0;
+ init_table_aux(table, max, -1, 0, full);
+}
+
+static 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));
+ }
+ else
+ {
+ strcat(buf, format(" - %s", s_info[i].name));
+ }
+
+ fprintf(fff, "%-49s%s%06.3f [%05.3f]",
+ buf, s_info[i].value < 0 ? "-" : " ",
+ static_cast<double>(ABS(s_info[i].value)) / SKILL_STEP,
+ static_cast<double>(s_info[i].mod) / 1000);
+ }
+
+ fprintf(fff, "\n");
+}
+
+
+/*
+ * Draw the skill tree
+ */
+void print_skills(int table[MAX_SKILLS][2], int max, int sel, int start)
+{
+ int i, j;
+ int wid, hgt;
+ cptr keys;
+
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ c_prt(TERM_WHITE, format("%s Skills Screen", game_module), 0, 28);
+ keys = format("#BEnter#W to develop a branch, #Bup#W/#Bdown#W to move, #Bright#W/#Bleft#W to modify, #B?#W for help");
+ display_message(0, 1, strlen(keys), TERM_WHITE, keys);
+ c_prt((p_ptr->skill_points) ? TERM_L_BLUE : TERM_L_RED,
+ format("Skill points left: %d", p_ptr->skill_points), 2, 0);
+ print_desc_aux(s_info[table[sel][0]].desc, 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),
+ 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),
+ j + 7 - start, table[j][1] * 4);
+ }
+ else
+ {
+ c_prt(color, format("%c+%c%s", deb, end, s_info[i].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);
+ }
+
+ /* Antimagic means you don't believe in gods. */
+ if ((p_ptr->pgod != GOD_NONE) &&
+ (s_info[SKILL_ANTIMAGIC].value > 0))
+ {
+ msg_print("You no longer believe.");
+ abandon_god(GOD_ALL);
+ }
+
+ process_hooks_new(HOOK_RECALC_SKILLS, NULL, NULL);
+
+ /* 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_FRAME | PR_MAP);
+ }
+}
+
+/*
+ * Recalc the skill value
+ */
+static 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;
+
+ recalc_skills(TRUE);
+
+ /* Save the screen */
+ screen_save();
+
+ /* Allocate arrays to save skill values */
+ std::unique_ptr<s32b[]> skill_values_save(new s32b[MAX_SKILLS]);
+ std::unique_ptr<s32b[]> skill_mods_save(new s32b[MAX_SKILLS]);
+ std::unique_ptr<s16b[]> skill_rates_save(new s16b[MAX_SKILLS]);
+ std::unique_ptr<s16b[]> skill_invest(new s16b[MAX_SKILLS]);
+ std::unique_ptr<s32b[]> skill_bonus(new s32b[MAX_SKILLS]);
+
+ /* 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;
+ skill_bonus[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.get(), skill_values_save.get(), skill_mods_save.get(), skill_bonus.get());
+ 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 ((sel >= 0) && (sel < max) && table[sel][0] == SKILL_MISC) continue;
+
+ /* Increase the current skill */
+ if (dir == 6) increase_skill(table[sel][0], skill_invest.get());
+
+ /* Decrease the current skill */
+ if (dir == 4) decrease_skill(table[sel][0], skill_invest.get());
+
+ /* 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 == '?')
+ {
+ help_skill(s_info[table[sel][0]].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)", hgt / 2, 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];
+ }
+ }
+ }
+
+ /* Load the screen */
+ screen_load();
+
+ recalc_skills(FALSE);
+}
+
+
+
+/*
+ * List of melee skills
+ */
+static s16b melee_skills[MAX_MELEE] =
+{
+ SKILL_MASTERY,
+ SKILL_HAND,
+ SKILL_BEAR,
+};
+static const 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);
+}
+
+cptr get_melee_name()
+{
+ return melee_names[get_melee_skill()];
+}
+
+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_FRAME);
+
+ 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(const std::vector<std::tuple<cptr, int>> &p, int start)
+{
+ char buff[80];
+ int j = 0;
+
+ prt(format(" %-31s", "Name"), 1, 20);
+
+ for (int i = start; i < (start + 20); i++)
+ {
+ if (static_cast<size_t>(i) >= p.size())
+ {
+ break;
+ }
+
+ sprintf(buff, " %c - %d) %-30s", I2A(j),
+ std::get<1>(p[i]),
+ std::get<0>(p[i]));
+
+ prt(buff, 2 + j, 20);
+ j++;
+ }
+ prt("", 2 + j, 20);
+ prt(format("Select a skill (a-%c), @ to select by name, +/- to scroll:", I2A(j - 1)), 0, 0);
+}
+
+static int do_cmd_activate_skill_aux()
+{
+ char which;
+ int start = 0;
+ int ret;
+
+ std::vector<std::tuple<cptr,int>> p;
+
+ /* More than 1 melee skill ? */
+ if (get_melee_skills() > 1)
+ {
+ p.push_back(std::make_tuple("Change melee mode", 0));
+ }
+
+ for (size_t i = 1; i < max_s_idx; i++)
+ {
+ if (s_info[i].action_mkey && s_info[i].value && ((!s_info[i].hidden) || (i == SKILL_LEARN)))
+ {
+ bool_ next = FALSE;
+
+ /* Already got it ? */
+ for (size_t j = 0; j < p.size(); j++)
+ {
+ if (s_info[i].action_mkey == std::get<1>(p[j]))
+ {
+ next = TRUE;
+ break;
+ }
+ }
+ if (next) continue;
+
+ p.push_back(std::make_tuple(s_info[i].action_desc,
+ s_info[i].action_mkey));
+ }
+ }
+
+ for (size_t i = 0; i < max_ab_idx; i++)
+ {
+ if (ab_info[i].action_mkey && ab_info[i].acquired)
+ {
+ bool_ next = FALSE;
+
+ /* Already got it ? */
+ for (size_t j = 0; j < p.size(); j++)
+ {
+ if (ab_info[i].action_mkey == std::get<1>(p[j]))
+ {
+ next = TRUE;
+ break;
+ }
+ }
+ if (next) continue;
+
+ p.push_back(std::make_tuple(ab_info[i].action_desc,
+ ab_info[i].action_mkey));
+ }
+ }
+
+ if (p.empty())
+ {
+ msg_print("You don't have any activable skills or abilities.");
+ return -1;
+ }
+
+ character_icky = TRUE;
+ Term_save();
+
+ while (1)
+ {
+ print_skill_batch(p, start);
+ which = inkey();
+
+ if (which == ESCAPE)
+ {
+ ret = -1;
+ break;
+ }
+ else if (which == '+')
+ {
+ start += 20;
+ if (static_cast<size_t>(start) >= p.size())
+ {
+ 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 */
+ size_t i = 0;
+ for (; i < p.size(); i++)
+ {
+ if (!strcmp(buf, std::get<0>(p[i])))
+ break;
+ }
+
+ if (i < p.size())
+ {
+ ret = std::get<1>(p[i]);
+ break;
+ }
+ }
+ else
+ {
+ which = tolower(which);
+ if (start + A2I(which) >= static_cast<int>(p.size()))
+ {
+ bell();
+ continue;
+ }
+ if (start + A2I(which) < 0)
+ {
+ bell();
+ continue;
+ }
+
+ ret = std::get<1>(p[start + A2I(which)]);
+ break;
+ }
+ }
+ Term_load();
+ character_icky = FALSE;
+
+ 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_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_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;
+ case MKEY_DEATH_TOUCH:
+ {
+ if (p_ptr->csp > 40)
+ {
+ increase_mana(-40);
+ set_project(randint(30) + 10,
+ GF_INSTA_DEATH,
+ 1,
+ 0,
+ PROJECT_STOP | PROJECT_KILL);
+ energy_use = 100;
+ }
+ else
+ {
+ msg_print("You need at least 40 mana.");
+ }
+ break;
+ }
+ case MKEY_GEOMANCY:
+ {
+ s32b s = -1;
+ object_type *o_ptr = NULL;
+
+ /* No magic */
+ if (p_ptr->antimagic > 0)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ break;
+ }
+
+ o_ptr = get_object(INVEN_WIELD);
+ if ((o_ptr->k_idx <= 0) ||
+ (o_ptr->tval != TV_MSTAFF))
+ {
+ msg_print("You must wield a magestaff to use Geomancy.");
+ break;
+ }
+
+ s = get_school_spell("cast", BOOK_GEOMANCY);
+ if (s >= 0)
+ {
+ lua_cast_school_spell(s, FALSE);
+ }
+
+ break;
+ }
+ case MKEY_REACH_ATTACK:
+ {
+ object_type *o_ptr = NULL;
+ int dir, dy, dx, targetx, targety, max_blows, flags;
+
+ o_ptr = get_object(INVEN_WIELD);
+ if (o_ptr->tval == TV_POLEARM)
+ {
+ msg_print("You will need a long polearm for this!");
+ return;
+ }
+
+ if (!get_rep_dir(&dir))
+ {
+ return;
+ }
+
+ dy = ddy[dir];
+ dx = ddx[dir];
+ dy = dy * 2;
+ dx = dx * 2;
+ targety = p_ptr->py + dy;
+ targetx = p_ptr->px + dx;
+
+ max_blows = get_skill_scale(SKILL_POLEARM, p_ptr->num_blow / 2);
+ if (max_blows == 0)
+ {
+ max_blows = 1;
+ }
+
+ energy_use = energy_use + 200;
+
+ flags = PROJECT_BEAM | PROJECT_KILL;
+ if (get_skill(SKILL_POLEARM) < 40)
+ {
+ flags |= PROJECT_STOP;
+ }
+
+ project(0, 0, targety, targetx,
+ max_blows, GF_ATTACK, flags);
+
+ break;
+ }
+ default:
+ 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()
+{
+ if (p_ptr->pgod == GOD_ERU) return (TRUE);
+ return (FALSE);
+}
+
+
+/*
+ * Gets the base value of a skill, given a race/class/...
+ */
+void compute_skills(s32b *v, s32b *m, int i)
+{
+ s32b value, mod;
+
+ /***** general skills *****/
+
+ /* If the skill correspond to the magic school lets pump them a bit */
+ value = gen_skill_base[i];
+ mod = gen_skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, gen_skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, gen_skill_modm[i]);
+
+ /***** race skills *****/
+
+ value = rp_ptr->skill_base[i];
+ mod = rp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, rp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, rp_ptr->skill_modm[i]);
+
+ /***** race mod skills *****/
+
+ value = rmp_ptr->skill_base[i];
+ mod = rmp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, rmp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, rmp_ptr->skill_modm[i]);
+
+ /***** class skills *****/
+
+ value = cp_ptr->skill_base[i];
+ mod = cp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, cp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, cp_ptr->skill_modm[i]);
+
+ /***** class spec skills *****/
+
+ value = spp_ptr->skill_base[i];
+ mod = spp_ptr->skill_mod[i];
+
+ *v = modify_aux(*v,
+ value, spp_ptr->skill_basem[i]);
+ *m = modify_aux(*m,
+ mod, spp_ptr->skill_modm[i]);
+}
+
+/*
+ * Initialize a skill with given values
+ */
+void init_skill(s32b value, s32b mod, int i)
+{
+ s_info[i].value = value;
+ s_info[i].mod = mod;
+
+ if (s_info[i].flags1 & SKF1_HIDDEN)
+ s_info[i].hidden = TRUE;
+ else
+ s_info[i].hidden = FALSE;
+}
+
+/*
+ * Perform weighted random shuffle according to the algorithm given in
+ * "Weighted Random Sampling" (2005, Efraimidis, Spirakis).
+ *
+ * @param weights is the vector of weights.
+ * @return an output vector of the same size as the input weights vector containing,
+ * in order of selection, the indices to select. For example, if you
+ * need to choose two items, you would use v[0], v[1] as the indices
+ * to pick.
+ */
+static std::vector<size_t> wrs(const std::vector<s32b> &unscaled_weights)
+{
+ const size_t n = unscaled_weights.size();
+
+ /* Rescale weights into unit interval for numerical stability */
+ std::vector<double> weights(unscaled_weights.size());
+ {
+ s32b scale = 0;
+ for (s32b weight: unscaled_weights)
+ {
+ scale += weight;
+ }
+
+ for (size_t i = 0; i < n; i++) {
+ weights[i] =
+ ((double) unscaled_weights[i]) /
+ ((double) scale);
+ }
+ }
+
+ /* Generate the keys and indexes to use for selection. This
+ is the only randomized portion of the algorithm. */
+ std::vector<std::tuple<double,size_t>> keys_and_indexes(unscaled_weights.size());
+ for (size_t i = 0; i < n; i++) {
+ /* Randomized keys according to the algorithm. */
+ double u = static_cast<double>(rand_int(100000)) / 100000;
+ double k = std::pow(u, 1/weights[i]);
+ /* Set up the key and index. We negate the k value
+ here so that keys will be sorted in descending
+ order rather than ascending order. */
+ keys_and_indexes[i] = std::make_tuple(-k, i);
+ }
+
+ /* Sort indexes according to keys. Since the keys have been
+ negated and we're using a lexicographical sort, we're
+ effectively sorting in descending order of key. */
+ std::sort(std::begin(keys_and_indexes),
+ std::end(keys_and_indexes));
+
+ /* Produce the output vector consisting of indexes only. */
+ std::vector<size_t> indexes;
+ for (auto const &key_and_index: keys_and_indexes) {
+ indexes.push_back(std::get<1>(key_and_index));
+ }
+ return indexes;
+}
+
+void do_get_new_skill()
+{
+ std::vector<std::string> items;
+ int skl[LOST_SWORD_NSKILLS];
+ s32b val[LOST_SWORD_NSKILLS], mod[LOST_SWORD_NSKILLS];
+ int available_skills[MAX_SKILLS];
+ int max_a = 0, res, i;
+
+ /* Check if some skills didn't influence other stuff */
+ recalc_skills(TRUE);
+
+ /* Grab the ones we can gain */
+ max_a = 0;
+ for (i = 0; i < max_s_idx; i++)
+ {
+ if (s_info[i].flags1 & SKF1_RANDOM_GAIN) {
+ available_skills[max_a] = i;
+ max_a++;
+ }
+ }
+
+ /* Perform the selection */
+ std::vector<s32b> weights;
+ for (i = 0; i < max_a; i++) {
+ weights.push_back(s_info[available_skills[i]].random_gain_chance);
+ }
+
+ std::vector<size_t> indexes = wrs(weights);
+ assert(indexes.size() >= LOST_SWORD_NSKILLS);
+
+ /* Extract the information needed from the skills */
+ for (i = 0; i < LOST_SWORD_NSKILLS; i++)
+ {
+ s32b s_idx = available_skills[indexes[i]];
+ skill_type *s_ptr = &s_info[s_idx];
+
+ if (s_ptr->mod)
+ {
+ if (s_ptr->mod < 300)
+ {
+ val[i] = 1000;
+ mod[i] = 300 - s_ptr->mod;
+ }
+ else if (s_ptr->mod < 500)
+ {
+ val[i] = s_ptr->mod * 1;
+ mod[i] = 100;
+ if (mod[i] + s_ptr->mod > 500)
+ mod[i] = 500 - s_ptr->mod;
+ }
+ else
+ {
+ val[i] = s_ptr->mod * 3;
+ mod[i] = 0;
+ }
+ }
+ else
+ {
+ mod[i] = 300;
+ val[i] = 1000;
+ }
+
+ if (s_ptr->value + val[i] > SKILL_MAX) {
+ val[i] = SKILL_MAX - s_ptr->value;
+ }
+
+ skl[i] = s_idx;
+ items.push_back(format("%-40s: +%02ld.%03ld value, +%01d.%03d modifier",
+ s_ptr->name,
+ val[i] / SKILL_STEP,
+ val[i] % SKILL_STEP,
+ mod[i] / SKILL_STEP,
+ mod[i] % SKILL_STEP));
+ }
+
+ while (TRUE)
+ {
+ char last = 'a' + (LOST_SWORD_NSKILLS-1);
+ char buf[80];
+ sprintf(buf, "Choose a skill to learn(a-%c to choose, ESC to cancel)?", last);
+ res = ask_menu(buf, items);
+
+ /* 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);
+
+ /* 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);
+ }
+ else
+ {
+ msg_format("Your knowledge of the %s skill increases.",
+ s_ptr->name);
+ }
+ break;
+ }
+ }
+
+ /* 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++)
+ {
+ if (ab_info[i].name && streq(ab_info[i].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 */
+static 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;
+ }
+ }
+
+ return TRUE;
+}
+
+/* Learn an ability */
+static 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.", hgt / 2, 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)", hgt / 2, wid / 2) != 'y')
+ {
+ return;
+ }
+
+ ab_info[ab].acquired = TRUE;
+ p_ptr->skill_points -= ab_info[ab].cost;
+}
+
+static bool compare_abilities(const int ab_idx1, const int ab_idx2)
+{
+ return strcmp(ab_info[ab_idx1].name, ab_info[ab_idx2].name) < 0;
+}
+
+/*
+ * Print the abilities list
+ */
+void dump_abilities(FILE *fff)
+{
+ int i;
+
+ // Find all abilities that the player has.
+ std::vector<int> table;
+ for (i = 0; i < max_ab_idx; i++)
+ {
+ if (ab_info[i].name && has_ability(i))
+ {
+ table.push_back(i);
+ }
+ }
+
+ // Sort
+ std::sort(std::begin(table),
+ std::end(table),
+ compare_abilities);
+
+ // Show
+ if (!table.empty())
+ {
+ fprintf(fff, "\nAbilities");
+
+ for (int i : table)
+ {
+ fprintf(fff, "\n * %s", ab_info[i].name);
+ }
+
+ fprintf(fff, "\n");
+ }
+}
+
+/*
+ * Draw the abilities list
+ */
+static void print_abilities(const std::vector<int> &table, 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, 3, 0);
+
+ for (j = start; j < start + (hgt - 7); j++)
+ {
+ byte color = TERM_WHITE;
+ char deb = ' ', end = ' ';
+
+ assert(j >= 0);
+ if (static_cast<size_t>(j) >= table.size())
+ {
+ 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),
+ 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;
+ char c;
+ int i;
+ int wid, hgt;
+
+ /* Save the screen */
+ screen_save();
+
+ /* Clear the screen */
+ Term_clear();
+
+ /* Initialise the abilities list */
+ std::vector<int> table;
+ for (i = 0; i < max_ab_idx; i++)
+ {
+ if (ab_info[i].name)
+ {
+ table.push_back(i);
+ }
+ }
+
+ std::sort(std::begin(table),
+ std::end(table),
+ compare_abilities);
+
+ while (TRUE)
+ {
+ Term_get_size(&wid, &hgt);
+
+ /* Display list of skills */
+ print_abilities(table, 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);
+ assert(sel >= 0);
+ if (static_cast<size_t>(sel) >= table.size())
+ {
+ sel = table.size() - 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 == '?')
+ {
+ help_ability(ab_info[table[sel]].name);
+ }
+
+ /* Handle boundaries and scrolling */
+ if (sel < 0)
+ {
+ sel = table.size() - 1;
+ }
+ if (static_cast<size_t>(sel) >= table.size())
+ {
+ sel = 0;
+ }
+ if (sel < start)
+ {
+ start = sel;
+ }
+ if (sel >= start + (hgt - 7))
+ {
+ start = sel - (hgt - 7) + 1;
+ }
+ }
+ }
+
+ /* Load the screen */
+ screen_load();
+
+ /* 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_FRAME | 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_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_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_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_info[rmp_ptr->abilities[i].ability].name);
+ }
+ ab_info[rmp_ptr->abilities[i].ability].acquired = TRUE;
+ }
+ }
+}
diff --git a/src/skills.hpp b/src/skills.hpp
new file mode 100644
index 00000000..6c1880a6
--- /dev/null
+++ b/src/skills.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include "h-basic.h"
+
+/* Skill functions */
+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 cptr get_melee_name();
+extern s16b get_melee_skills(void);
+extern s16b get_melee_skill(void);
+extern bool_ forbid_gloves(void);
+extern bool_ forbid_non_blessed(void);
+extern void compute_skills(s32b *v, s32b *m, int i);
+extern void select_default_melee(void);
+extern void do_get_new_skill(void);
+extern void init_skill(s32b value, s32b mod, int i);
+extern s16b find_ability(cptr name);
+extern void dump_abilities(FILE *fff);
+extern void do_cmd_ability(void);
+extern bool_ has_ability(int ab);
+extern void apply_level_abilities(int level);
+extern void recalc_skills(bool_ init);
diff --git a/src/skills_defs.hpp b/src/skills_defs.hpp
new file mode 100644
index 00000000..1dbdee9f
--- /dev/null
+++ b/src/skills_defs.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+/*
+ * Skill constants
+ */
+#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
+#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
+#define MAX_SKILLS 200
diff --git a/src/spell_type.cc b/src/spell_type.cc
new file mode 100644
index 00000000..6cf6e35b
--- /dev/null
+++ b/src/spell_type.cc
@@ -0,0 +1,433 @@
+#include "spell_type.hpp"
+
+#include "range.hpp"
+#include "device_allocation.hpp"
+#include "dice.hpp"
+#include "skills_defs.hpp"
+#include "spells4.hpp"
+#include "stats.hpp"
+
+#include <cassert>
+#include <vector>
+#include <string>
+#include <type_traits>
+
+#define SCHOOL_IDXS_MAX 3
+
+/**
+ * Spell type definition.
+ */
+struct spell_type
+{
+ cptr name; /* Name */
+ byte skill_level; /* Required level (to learn) */
+ std::vector<std::string> m_description; /* List of strings */
+
+ casting_result (*effect_func)(); /* Spell effect function */
+ const char* (*info_func)(); /* Information function */
+ int (*lasting_func)(); /* Lasting effect function */
+ bool_ (*depend_func)(); /* Check dependencies */
+
+ s16b minimum_pval; /* Minimum required pval for item-based spells */
+
+ enum casting_type casting_type; /* Type of casting required */
+ s16b casting_stat; /* Stat used for casting */
+
+ bool_ castable_while_blind;
+ bool_ castable_while_confused;
+
+ dice_type device_charges; /* Number of charges for devices */
+ std::vector<device_allocation *> m_device_allocation; /* Allocation table for devices */
+
+ s16b random_type; /* Type of random items in which skill may appear */
+
+ s32b failure_rate; /* Failure rate */
+
+ s32b inertia_difficulty; /* Mana cost when used in Inertia Control */
+ s32b inertia_delay; /* Delay between castings */
+
+ range_type mana_range;
+
+ size_t school_idxs_count;
+ s32b school_idxs[3];
+
+public:
+
+ spell_type(cptr _name)
+ : name(_name)
+ , skill_level(0)
+ , m_description()
+ , effect_func(nullptr)
+ , info_func(nullptr)
+ , lasting_func(nullptr)
+ , depend_func(nullptr)
+ , minimum_pval(0)
+ , casting_type(USE_SPELL_POINTS)
+ , casting_stat(0)
+ , castable_while_blind(FALSE)
+ , castable_while_confused(FALSE)
+ , device_charges({ 0, 0, 0 })
+ , m_device_allocation()
+ , random_type(-1)
+ , failure_rate(0)
+ , inertia_difficulty(-1)
+ , inertia_delay(-1)
+ , mana_range({ -1, -1 })
+ , school_idxs_count(0)
+ , school_idxs{ -1, -1, -1 }
+ {
+ }
+
+};
+
+static void school_idx_add_new(spell_type *spell, s32b i)
+{
+ assert(spell != NULL);
+ assert(spell->school_idxs_count < SCHOOL_IDXS_MAX);
+
+ spell->school_idxs[spell->school_idxs_count] = i;
+ spell->school_idxs_count++;
+}
+
+void spell_type_set_inertia(spell_type *spell, s32b difficulty, s32b delay)
+{
+ assert(spell != NULL);
+ spell->inertia_difficulty = difficulty;
+ spell->inertia_delay = delay;
+}
+
+void spell_type_init_music(spell_type *spell,
+ s16b minimum_pval,
+ const char* (*info_func)(),
+ casting_result (*effect_func)())
+{
+ assert(spell != NULL);
+
+ /* Set up callbacks */
+ spell->info_func = info_func;
+ spell->effect_func = effect_func;
+
+ /* Use spell points, but CHR for success/failure calculations */
+ spell->casting_type = USE_SPELL_POINTS;
+ spell->casting_stat = A_CHR;
+ spell->random_type = SKILL_MUSIC;
+ spell->minimum_pval = minimum_pval;
+ /* Add school */
+ school_idx_add_new(spell, SCHOOL_MUSIC);
+}
+
+void spell_type_init_music_lasting(spell_type *spell,
+ s16b minimum_pval,
+ const char* (*info_func)(),
+ casting_result (*effect_func)(),
+ int (*lasting_func)())
+{
+ spell_type_init_music(
+ spell,
+ minimum_pval,
+ info_func,
+ effect_func);
+
+ spell->lasting_func = lasting_func;
+}
+
+void spell_type_init_mage(spell_type *spell,
+ random_type random_type,
+ s32b school_idx,
+ const char* (*info_func)(),
+ casting_result (*effect_func)())
+{
+ assert(spell != NULL);
+
+ spell->info_func = info_func;
+ spell->effect_func = effect_func;
+
+ spell->casting_type = USE_SPELL_POINTS;
+ spell->casting_stat = A_INT;
+
+ switch (random_type)
+ {
+ case RANDOM:
+ spell->random_type = SKILL_MAGIC;
+ break;
+ case NO_RANDOM:
+ spell->random_type = -1;
+ break;
+ default:
+ /* Cannot happen */
+ assert(FALSE);
+ }
+
+ /* Add first school */
+ spell_type_add_school(spell, school_idx);
+}
+
+void spell_type_init_priest(spell_type *spell,
+ s32b school_idx,
+ const char* (*info_func)(),
+ casting_result (*effect_func)())
+{
+ assert(spell != NULL);
+
+ spell->info_func = info_func;
+ spell->effect_func = effect_func;
+
+ spell->random_type = SKILL_SPIRITUALITY;
+ spell->casting_type = USE_PIETY;
+ spell->casting_stat = A_WIS;
+
+ school_idx_add_new(spell, school_idx);
+}
+
+void spell_type_init_device(spell_type *spell,
+ const char* (*info_func)(),
+ casting_result (*effect_func)())
+{
+ assert(spell != NULL);
+
+ spell_type_init_mage(spell,
+ NO_RANDOM,
+ SCHOOL_DEVICE,
+ info_func,
+ effect_func);
+}
+
+void spell_type_init_demonology(spell_type *spell,
+ const char* (*info_func)(),
+ casting_result (*effect_func)())
+{
+ spell_type_init_mage(spell,
+ NO_RANDOM,
+ SCHOOL_DEMON,
+ info_func,
+ effect_func);
+}
+
+void spell_type_init_geomancy(spell_type *spell,
+ const char* (*info_func)(),
+ casting_result (*effect_func)(),
+ bool_ (*depend_func)())
+{
+ spell_type_init_mage(spell,
+ NO_RANDOM,
+ SCHOOL_GEOMANCY,
+ info_func,
+ effect_func);
+
+ spell->depend_func = depend_func;
+}
+
+void spell_type_set_difficulty(spell_type *spell, byte skill_level, s32b failure_rate)
+{
+ assert(spell != NULL);
+
+ spell->skill_level = skill_level;
+ spell->failure_rate = failure_rate;
+}
+
+void spell_type_set_mana(spell_type *spell, s32b min, s32b max)
+{
+ assert(spell != NULL);
+
+ range_init(&spell->mana_range, min, max);
+}
+
+void spell_type_set_castable_while_blind(spell_type *spell, bool_ value)
+{
+ assert(spell != NULL);
+
+ spell->castable_while_blind = value;
+}
+
+void spell_type_set_castable_while_confused(spell_type *spell, bool_ value)
+{
+ assert(spell != NULL);
+
+ spell->castable_while_confused = value;
+}
+
+void spell_type_describe(spell_type *spell, cptr line)
+{
+ assert(spell != NULL);
+
+ spell->m_description.push_back(std::string(line));
+}
+
+void spell_type_add_school(spell_type *spell, s32b school_idx)
+{
+ school_idx_add_new(spell, school_idx);
+}
+
+void spell_type_set_device_charges(spell_type *spell, cptr charges_s)
+{
+ assert(spell != NULL);
+
+ dice_parse_checked(&spell->device_charges, charges_s);
+}
+
+void spell_type_add_device_allocation(spell_type *spell, struct device_allocation *a)
+{
+ assert(spell != NULL);
+ assert(a != NULL);
+ spell->m_device_allocation.push_back(a);
+}
+
+spell_type *spell_type_new(cptr name)
+{
+ spell_type *spell = new spell_type(name);
+ assert(spell != NULL);
+ return spell;
+}
+
+int spell_type_produce_effect_lasting(spell_type *spell)
+{
+ assert(spell->lasting_func != NULL);
+ return spell->lasting_func();
+}
+
+casting_result spell_type_produce_effect(spell_type *spell)
+{
+ assert(spell->effect_func != NULL);
+ return spell->effect_func();
+}
+
+cptr spell_type_name(spell_type *spell)
+{
+ assert(spell != NULL);
+
+ return spell->name;
+}
+
+int spell_type_skill_level(spell_type *spell)
+{
+ assert(spell != NULL);
+
+ return spell->skill_level;
+}
+
+void spell_type_description_foreach(spell_type *spell, std::function<void (std::string const &text)> callback)
+{
+ for (auto line: spell->m_description)
+ {
+ callback(line);
+ }
+}
+
+long spell_type_roll_charges(spell_type *spell)
+{
+ return dice_roll(&spell->device_charges);
+}
+
+device_allocation *spell_type_device_allocation(spell_type *spell, byte tval)
+{
+ for (auto device_allocation_ptr: spell->m_device_allocation)
+ {
+ if (device_allocation_ptr->tval == tval)
+ {
+ return device_allocation_ptr;
+ }
+ }
+
+ return NULL;
+}
+
+bool_ spell_type_uses_piety_to_cast(spell_type *spell)
+{
+ assert(spell != NULL);
+ return spell->casting_type == USE_PIETY;
+}
+
+bool_ spell_type_castable_while_blind(spell_type *spell)
+{
+ assert(spell != NULL);
+ return spell->castable_while_blind;
+}
+
+bool_ spell_type_castable_while_confused(spell_type *spell)
+{
+ assert(spell != NULL);
+ return spell->castable_while_confused;
+}
+
+s16b spell_type_minimum_pval(spell_type *spell)
+{
+ return spell->minimum_pval;
+}
+
+s16b spell_type_random_type(spell_type *spell)
+{
+ return spell->random_type;
+}
+
+std::vector<s32b> const spell_type_get_schools(spell_type *spell)
+{
+ std::vector<s32b> school_idxs;
+ for (size_t i = 0; i < spell->school_idxs_count; i++)
+ {
+ school_idxs.push_back(spell->school_idxs[i]);
+ }
+ return school_idxs;
+}
+
+bool_ spell_type_inertia(spell_type *spell, s32b *difficulty, s32b *delay)
+{
+ if ((spell->inertia_difficulty < 0) ||
+ (spell->inertia_delay < 0))
+ {
+ return FALSE;
+ }
+
+ if (difficulty != NULL)
+ {
+ *difficulty = spell->inertia_difficulty;
+ }
+
+ if (delay != NULL)
+ {
+ *delay = spell->inertia_delay;
+ }
+
+ return TRUE;
+}
+
+cptr spell_type_info(spell_type *spell)
+{
+ assert(spell != NULL);
+
+ return spell->info_func();
+}
+
+s32b spell_type_failure_rate(spell_type *spell)
+{
+ assert(spell != NULL);
+
+ return spell->failure_rate;
+}
+
+s16b spell_type_casting_stat(spell_type *spell)
+{
+ assert(spell != NULL);
+
+ return spell->casting_stat;
+}
+
+void spell_type_mana_range(spell_type *spell, range_type *range)
+{
+ assert(spell != NULL);
+
+ if (range != NULL)
+ {
+ *range = spell->mana_range;
+ }
+}
+
+bool_ spell_type_dependencies_satisfied(spell_type *spell)
+{
+ assert(spell != NULL);
+
+ if (spell->depend_func != NULL) {
+ return spell->depend_func();
+ } else {
+ return TRUE;
+ }
+}
diff --git a/src/spell_type.hpp b/src/spell_type.hpp
new file mode 100644
index 00000000..43758103
--- /dev/null
+++ b/src/spell_type.hpp
@@ -0,0 +1,86 @@
+#pragma once
+
+#include "spell_type_fwd.hpp"
+#include <vector>
+#include <string>
+#include <functional>
+#include "h-basic.h"
+#include "device_allocation_fwd.hpp"
+#include "range_fwd.hpp"
+
+/*
+ * Casting type
+ */
+enum casting_type { USE_SPELL_POINTS, USE_PIETY };
+
+/*
+ * Does the spell appear on spell random books?
+ */
+enum random_type { RANDOM, NO_RANDOM };
+
+/*
+ * Spell functions
+ */
+
+void spell_type_init_music(spell_type *spell,
+ s16b minimum_pval,
+ const char* (*info_func)(),
+ casting_result (*effect_func)());
+void spell_type_init_music_lasting(spell_type *spell,
+ s16b minimum_pval,
+ const char* (*info_func)(),
+ casting_result (*effect_func)(),
+ int (*lasting_func)());
+void spell_type_init_mage(spell_type *spell,
+ random_type random_type,
+ s32b school_idx,
+ const char* (*info_func)(),
+ casting_result (*effect_func)());
+void spell_type_init_priest(spell_type *spell,
+ s32b school_idx,
+ const char* (*info_func)(),
+ casting_result (*effect_func)());
+void spell_type_init_device(spell_type *spell,
+ const char* (*info_func)(),
+ casting_result (*effect_func)());
+void spell_type_init_demonology(spell_type *spell,
+ const char* (*info_func)(),
+ casting_result (*effect_func)());
+void spell_type_init_geomancy(spell_type *spell,
+ const char* (*info_func)(),
+ casting_result (*effect_func)(),
+ bool_ (*depend_func)());
+
+void spell_type_set_inertia(spell_type *spell, s32b difficulty, s32b delay);
+void spell_type_set_difficulty(spell_type *spell, byte skill_level, s32b failure_rate);
+void spell_type_set_mana(spell_type *spell, s32b min, s32b max);
+void spell_type_set_castable_while_blind(spell_type *spell, bool_ value);
+void spell_type_set_castable_while_confused(spell_type *spell, bool_ value);
+void spell_type_describe(spell_type *spell, cptr line);
+
+void spell_type_add_school(spell_type *spell, s32b school_idx);
+
+void spell_type_set_device_charges(spell_type *spell, cptr charges_s);
+void spell_type_add_device_allocation(spell_type *spell, device_allocation *a);
+
+spell_type *spell_type_new(cptr name);
+
+int spell_type_produce_effect_lasting(spell_type *spell);
+casting_result spell_type_produce_effect(spell_type *spell);
+cptr spell_type_name(spell_type *spell);
+int spell_type_skill_level(spell_type *spell);
+long spell_type_roll_charges(spell_type *spell);
+struct device_allocation *spell_type_device_allocation(spell_type *spell, byte tval);
+bool_ spell_type_uses_piety_to_cast(spell_type *spell);
+bool_ spell_type_castable_while_blind(spell_type *spell);
+bool_ spell_type_castable_while_confused(spell_type *spell);
+s16b spell_type_minimum_pval(spell_type *spell);
+s16b spell_type_random_type(spell_type *spell);
+std::vector<s32b> const spell_type_get_schools(spell_type *spell);
+bool_ spell_type_inertia(spell_type *spell, s32b *difficulty, s32b *delay);
+s32b spell_type_failure_rate(spell_type *spell);
+s16b spell_type_casting_stat(spell_type *spell);
+cptr spell_type_info(spell_type *spell);
+void spell_type_mana_range(spell_type *spell, struct range_type *range);
+bool_ spell_type_dependencies_satisfied(spell_type *spell);
+void spell_type_description_foreach(spell_type *spell, std::function<void (std::string const &text)>);
diff --git a/src/spell_type_fwd.hpp b/src/spell_type_fwd.hpp
new file mode 100644
index 00000000..a3b27d27
--- /dev/null
+++ b/src/spell_type_fwd.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+/*
+ * Spell effect function result
+ */
+typedef enum {
+ NO_CAST, /* Spell not cast; user aborted */
+ CAST_OBVIOUS, /* Cast; caster discovers effect (devices) */
+ CAST_HIDDEN /* Cast; caster does NOT discover effect (devices) */
+} casting_result;
+
+/*
+ * Forward declaration of the spell_type
+ */
+typedef struct spell_type spell_type;
+struct spell_type;
diff --git a/src/spells1.cc b/src/spells1.cc
new file mode 100644
index 00000000..5d6722af
--- /dev/null
+++ b/src/spells1.cc
@@ -0,0 +1,9275 @@
+/*
+ * 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 "spells1.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd1.hpp"
+#include "cmd3.hpp"
+#include "cmd5.hpp"
+#include "dungeon_info_type.hpp"
+#include "files.hpp"
+#include "feature_type.hpp"
+#include "gods.hpp"
+#include "melee2.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_type.hpp"
+#include "monster_race.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "spell_type.hpp"
+#include "spells2.hpp"
+#include "spells4.hpp"
+#include "spells5.hpp"
+#include "squeltch.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "traps.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.hpp"
+#include "wizard2.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <chrono>
+#include <thread>
+
+using std::this_thread::sleep_for;
+using std::chrono::milliseconds;
+
+/* 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
+
+/*
+ * 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)))
+
+
+/*
+ * Helper function -- return a "nearby" race for polymorphing
+ *
+ * Note that this function is one of the more "dangerous" ones...
+ */
+s16b poly_r_idx(int r_idx)
+{
+ monster_race *r_ptr = &r_info[r_idx];
+
+ int i, r;
+
+ /* Hack -- Uniques never polymorph */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ return (r_idx);
+
+ /* Pick a (possibly new) non-unique race */
+ for (i = 0; i < 1000; i++)
+ {
+ /* Pick a new race, using a level calculation */
+ r = get_mon_num((dun_level + r_ptr->level) / 2 + 5);
+
+ /* Handle failure */
+ if (!r) break;
+
+ /* Obtain race */
+ r_ptr = &r_info[r];
+
+ /* Ignore unique monsters */
+ if (r_ptr->flags1 & (RF1_UNIQUE)) continue;
+
+ /* Use that index */
+ r_idx = r;
+
+ /* Done */
+ break;
+ }
+
+ /* Result */
+ return (r_idx);
+}
+
+/*
+ * Teleport player, using a distance and a direction as a rough guide.
+ *
+ * This function is not at all obsessive about correctness.
+ * This function allows teleporting into vaults (!)
+ */
+void teleport_player_directed(int rad, int dir)
+{
+ int y = p_ptr->py;
+ int x = p_ptr->px;
+ int yfoo = ddy[dir];
+ int xfoo = ddx[dir];
+ int min = rad / 4;
+ int dis = rad;
+ int i, d;
+ bool_ look = TRUE;
+ bool_ y_major = FALSE;
+ bool_ x_major = FALSE;
+ int y_neg = 1;
+ int x_neg = 1;
+ cave_type *c_ptr;
+
+ if (xfoo == 0 && yfoo == 0)
+ {
+ teleport_player(rad);
+ return;
+ }
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ if (yfoo == 0) x_major = TRUE;
+ if (xfoo == 0) y_major = TRUE;
+ if (yfoo < 0) y_neg = -1;
+ if (xfoo < 0) x_neg = -1;
+
+ /* Look until done */
+ while (look)
+ {
+ /* Verify max distance */
+ if (dis > 200)
+ {
+ teleport_player(rad);
+ return;
+ }
+
+ /* Try several locations */
+ for (i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ if (y_major)
+ {
+ y = rand_spread(p_ptr->py + y_neg * dis / 2, dis / 2);
+ }
+ else
+ {
+ y = rand_spread(p_ptr->py, dis / 3);
+ }
+
+ if (x_major)
+ {
+ x = rand_spread(p_ptr->px + x_neg * dis / 2, dis / 2);
+ }
+ else
+ {
+ x = rand_spread(p_ptr->px, dis / 3);
+ }
+
+ d = distance(p_ptr->py, p_ptr->px, y, x);
+ if ((d >= min) && (d <= dis)) break;
+ }
+
+ /* Ignore illegal locations */
+ if (!in_bounds(y, x)) continue;
+
+ /* Require "naked" floor space */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* This grid looks good */
+ look = FALSE;
+
+ /* Stop looking */
+ break;
+ }
+
+ /* Increase the maximum distance */
+ dis = dis * 2;
+
+ /* Decrease the minimum distance */
+ min = min / 2;
+
+ }
+
+ /* Sound */
+ sound(SOUND_TELEPORT);
+
+ /* Move player */
+ teleport_player_to(y, x);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+
+ c_ptr = &cave[y][x];
+
+ /* Hack -- enter a store if we are on one */
+ if (c_ptr->feat == FEAT_SHOP)
+ {
+ /* Disturb */
+ disturb(0);
+
+ /* 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)
+{
+ bool_ look = TRUE;
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ 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 */
+ const int oy = m_ptr->fy;
+ const int ox = m_ptr->fx;
+
+ /* Minimum distance */
+ int min = dis / 2;
+
+ /* Look until done */
+ int tries = 0;
+ int ny = 0;
+ int nx = 0;
+ while (look)
+ {
+ tries++;
+
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (int i = 0; i < 500; i++)
+ {
+ /* Pick a (possibly illegal) location */
+ while (1)
+ {
+ ny = rand_spread(oy, dis);
+ nx = rand_spread(ox, dis);
+ int 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))
+ 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 */
+ auto const r_ptr = m_ptr->race();
+ if (r_ptr->flags9 & RF9_HAS_LITE)
+ {
+ p_ptr->update |= (PU_MON_LITE);
+ }
+}
+
+
+/*
+ * Teleport monster next to the player
+ */
+static void teleport_to_player(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ auto const r_ptr = m_ptr->race();
+
+ 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 */
+ const int oy = m_ptr->fy;
+ const int ox = m_ptr->fx;
+
+ /* Minimum distance */
+ int dis = 2;
+ int min = dis / 2;
+
+ /* End destination */
+ int ny = 0;
+ int nx = 0;
+
+ /* Look until done */
+ bool_ look = TRUE;
+ while (look && --attempts)
+ {
+ /* Verify max distance */
+ if (dis > 200) dis = 200;
+
+ /* Try several locations */
+ for (int 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);
+ const int 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)
+ {
+ auto const r_ptr = m_list[cave[oy + yy][ox + xx].m_idx].race();
+
+ 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_FRAME);
+
+ /* 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_FRAME);
+
+ /* 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_quest)
+ {
+ msg_print("There is no effect.");
+ return;
+ }
+ if (dungeon_flags2 & DF2_NO_TELEPORT)
+ {
+ msg_print("No teleport on special levels...");
+ return;
+ }
+ if (dungeon_flags2 & DF2_NO_EASY_MOVE)
+ {
+ msg_print("Some powerfull force prevents your from teleporting.");
+ return;
+ }
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ /* Hack -- when you are fated to die, you cant cheat :) */
+ if (dungeon_type == DUNGEON_DEATH)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ if (p_ptr->anti_tele)
+ {
+ msg_print("A mysterious force prevents you from teleporting!");
+ return;
+ }
+
+ /* Rooted means no move */
+ if (p_ptr->tim_roots) return;
+
+ if (!dun_level)
+ {
+ msg_print("You sink through the floor.");
+
+ autosave_checkpoint();
+
+ dun_level++;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ else if (is_quest(dun_level) || (dun_level >= MAX_DEPTH - 1))
+ {
+ msg_print("You rise up through the ceiling.");
+
+ autosave_checkpoint();
+
+ dun_level--;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ else if (rand_int(100) < 50)
+ {
+ msg_print("You rise up through the ceiling.");
+
+ autosave_checkpoint();
+
+ dun_level--;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+ else
+ {
+ msg_print("You sink through the floor.");
+
+ autosave_checkpoint();
+
+ dun_level++;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ }
+
+ /* Sound */
+ sound(SOUND_TPLEVEL);
+}
+
+
+
+/*
+ * Recall the player to town or dungeon
+ */
+void recall_player(int d, int f)
+{
+ /* Rooted means no move */
+ if (p_ptr->tim_roots)
+ {
+ msg_print("Your roots prevent the recall.");
+ return;
+ }
+
+
+ if (dun_level && (max_dlv[dungeon_type] > dun_level) &&
+ !p_ptr->inside_quest)
+ {
+ if (get_check("Reset recall depth? "))
+ max_dlv[dungeon_type] = dun_level;
+
+ }
+ if (!p_ptr->word_recall)
+ {
+ p_ptr->word_recall = rand_int(d) + f;
+ msg_print("The air about you becomes charged...");
+ }
+ else
+ {
+ p_ptr->word_recall = 0;
+ msg_print("A tension leaves the air around you...");
+ }
+ p_ptr->redraw |= (PR_FRAME);
+}
+
+
+/*
+ * Check the gods
+ */
+static void project_check_gods(int typ)
+{
+ if (p_ptr->pgod == GOD_VARDA)
+ {
+ if ((typ == GF_LITE) || (typ == GF_LITE_WEAK))
+ {
+ /* Raise piety for using lite */
+ set_grace(p_ptr->grace + 1);
+ }
+ }
+
+ if (p_ptr->pgod == GOD_ULMO)
+ {
+ if ((typ == GF_FIRE) ||
+ (typ == GF_HELL_FIRE) ||
+ (typ == GF_HOLY_FIRE) ||
+ (typ == GF_LAVA_FLOW) ||
+ (typ == GF_METEOR) ||
+ (typ == GF_NUKE) ||
+ (typ == GF_PLASMA))
+ {
+ /* Reduce piety for using any kind of fire magic */
+ set_grace(p_ptr->grace - 5);
+ }
+ }
+}
+
+
+/*
+ * 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)
+{
+ {
+ /* 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);
+ case GF_INSTA_DEATH:
+ return TERM_DARK;
+ case GF_ELEMENTAL_WALL:
+ case GF_ELEMENTAL_GROWTH:
+ return TERM_GREEN;
+ }
+ }
+
+ /* 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.", spell_type_name(spell_at(p_ptr->spellbinder[i])));
+ lua_cast_school_spell(p_ptr->spellbinder[i], TRUE);
+ }
+ 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);
+
+ /* 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_FRAME);
+ }
+
+ /* Hurt the wielded monster if any */
+ if ((o_ptr->k_idx) && (magik(5 + get_skill(SKILL_SYMBIOTIC))) && (!carried_monster_hit))
+ {
+ cptr sym_name = symbiote_name(TRUE);
+
+ if (o_ptr->pval2 - damage <= 0)
+ {
+ cmsg_format(TERM_L_RED,
+ "%s dies from protecting you, you feel very sad...",
+ sym_name);
+ inc_stack_size_ex(INVEN_CARRY, -1, OPTIMIZE, NO_DESCRIBE);
+ damage -= o_ptr->pval2;
+ o_ptr->pval2 = 0;
+ p_ptr->redraw |= PR_FRAME;
+ }
+ 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_FRAME);
+ }
+
+ /* Hurt the player */
+ if (!monster_take) p_ptr->chp -= damage;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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_FRAME);
+
+ /* 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)
+ {
+ if (praying_to(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);
+
+
+ /* Hurt the player */
+ p_ptr->csane -= damage;
+
+ /* Display the hitpoints */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Dead player */
+ if (p_ptr->csane < 0)
+ {
+ /* Sound */
+ sound(SOUND_DEATH);
+
+ /* Hack -- Note death */
+ cmsg_print(TERM_VIOLET, "You turn into an unthinking vegetable.");
+ if (!last_words)
+ {
+ cmsg_print(TERM_RED, "You die.");
+ msg_print(NULL);
+ }
+ else
+ {
+ (void)get_rnd_line("death.txt", death_message);
+ cmsg_print(TERM_RED, death_message);
+ }
+
+ /* Note cause of death */
+ (void)strcpy(died_from, hit_from);
+
+ if (p_ptr->image) strcat(died_from, "(?)");
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+
+ /* Note death */
+ death = TRUE;
+
+ if (get_check("Dump the screen? "))
+ {
+ do_cmd_html_dump();
+ }
+
+ /* Dead */
+ return;
+ }
+
+ /* Hitpoint warning */
+ if (p_ptr->csane < warning)
+ {
+ /* Hack -- bell on first notice */
+ if (alert_hitpoint && (old_csane > warning)) bell();
+
+ sound(SOUND_WARN);
+
+ /* Message */
+ cmsg_print(TERM_RED, "*** LOW SANITY WARNING! ***");
+ msg_print(NULL);
+ }
+}
+
+
+/*
+ * Note that amulets, rods, and high-level spell books are immune
+ * to "inventory damage" of any kind. Also sling ammo and shovels.
+ */
+
+
+/*
+ * Does a given class of objects (usually) hate acid?
+ * Note that acid can either melt or corrode something.
+ */
+static bool_ hates_acid(object_type *o_ptr)
+{
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Wearable items */
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_BOW:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_HELM:
+ case TV_CROWN:
+ case TV_SHIELD:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ return (TRUE);
+ }
+
+ /* Staffs/Scrolls are wood/paper */
+ case TV_STAFF:
+ case TV_SCROLL:
+ {
+ return (TRUE);
+ }
+
+ /* Ouch */
+ case TV_CHEST:
+ {
+ return (TRUE);
+ }
+
+ /* Junk is useless */
+ case TV_SKELETON:
+ case TV_BOTTLE:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Does a given object (usually) hate electricity?
+ */
+static bool_ hates_elec(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_RING:
+ case TV_WAND:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Does a given object (usually) hate fire?
+ * Hafted/Polearm weapons have wooden shafts.
+ * Arrows/Bows are mostly wooden.
+ */
+static bool_ hates_fire(object_type *o_ptr)
+{
+ /* Analyze the type */
+ switch (o_ptr->tval)
+ {
+ /* Special case for archers */
+ case TV_ARROW:
+ {
+ return TRUE;
+ };
+
+ /* Wearable */
+ case TV_LITE:
+ case TV_BOW:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ {
+ return (TRUE);
+ }
+
+ /* Books */
+ case TV_BOOK:
+ case TV_SYMBIOTIC_BOOK:
+ case TV_MUSIC_BOOK:
+ {
+ return (TRUE);
+ }
+
+ /* Chests */
+ case TV_CHEST:
+ {
+ return (TRUE);
+ }
+
+ /* Staffs/Scrolls burn */
+ case TV_STAFF:
+ case TV_SCROLL:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * Does a given object (usually) hate cold?
+ */
+static bool_ hates_cold(object_type *o_ptr)
+{
+ switch (o_ptr->tval)
+ {
+ case TV_POTION2:
+ case TV_POTION:
+ case TV_FLASK:
+ case TV_BOTTLE:
+ case TV_EGG:
+ {
+ return (TRUE);
+ }
+ }
+
+ return (FALSE);
+}
+
+
+
+
+
+
+
+
+
+/*
+ * Melt something
+ */
+static int set_acid_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_acid(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_ACID)) return (FALSE);
+ return (TRUE);
+}
+
+
+/*
+ * Electrical damage
+ */
+static int set_elec_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_elec(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_ELEC)) return (FALSE);
+ return (TRUE);
+}
+
+
+/*
+ * Burn something
+ */
+static int set_fire_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_fire(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_FIRE)) return (FALSE);
+ return (TRUE);
+}
+
+
+/*
+ * Freeze things
+ */
+static int set_cold_destroy(object_type *o_ptr)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ if (!hates_cold(o_ptr)) return (FALSE);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if (f3 & (TR3_IGNORE_COLD)) return (FALSE);
+ return (TRUE);
+}
+
+
+
+
+/*
+ * This seems like a pretty standard "typedef"
+ */
+typedef int (*inven_func)(object_type *);
+
+/*
+ * Destroys a type of item on a given percent chance
+ * Note that missiles are no longer necessarily all destroyed
+ * Destruction taken from "melee.c" code for "stealing".
+ * Returns number of items destroyed.
+ */
+static int inven_damage(inven_func typ, int perc)
+{
+ int i, j, k, amt;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+
+ /* Count the casualties */
+ k = 0;
+
+ /* Scan through the slots backwards */
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Hack -- for now, skip artifacts */
+ if (artifact_p(o_ptr) || o_ptr->art_name) continue;
+
+ /* Give this item slot a shot at death */
+ if ((*typ)(o_ptr))
+ {
+ /* Count the casualties */
+ for (amt = j = 0; j < o_ptr->number; ++j)
+ {
+ if (rand_int(100) < perc) amt++;
+ }
+
+ /* Some casualities */
+ if (amt)
+ {
+ /* Get a description */
+ object_desc(o_name, o_ptr, FALSE, 3);
+
+ /* Message */
+ msg_format("%sour %s (%c) %s destroyed!",
+ ((o_ptr->number > 1) ?
+ ((amt == o_ptr->number) ? "All of y" :
+ (amt > 1 ? "Some of y" : "One of y")) : "Y"),
+ o_name, index_to_label(i),
+ ((amt > 1) ? "were" : "was"));
+
+ /* Potions smash open */
+ if (k_info[o_ptr->k_idx].tval == TV_POTION)
+ {
+ (void)potion_smash_effect(0, p_ptr->py, p_ptr->px, o_ptr->sval);
+ }
+
+ /*
+ * Hack -- If rods or wand are destroyed, the total maximum
+ * timeout or charges of the stack needs to be reduced,
+ * unless all the items are being destroyed. -LM-
+ */
+ if ((o_ptr->tval == TV_WAND)
+ && (amt < o_ptr->number))
+ {
+ o_ptr->pval -= o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Destroy "amt" items */
+ inc_stack_size_ex(i, -amt, OPTIMIZE, NO_DESCRIBE);
+
+ /* Count the casualties */
+ k += amt;
+ }
+ }
+ }
+
+ /* Return the casualty count */
+ return (k);
+}
+
+
+
+
+/*
+ * Acid has hit the player, attempt to affect some armor.
+ *
+ * Note that the "base armor" of an object never changes.
+ *
+ * If any armor is damaged (or resists), the player takes less damage.
+ */
+static int minus_ac(void)
+{
+ object_type *o_ptr = NULL;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ char o_name[80];
+
+
+ /* Pick a (possibly empty) inventory slot */
+ switch (randint(6))
+ {
+ case 1:
+ o_ptr = &p_ptr->inventory[INVEN_BODY];
+ break;
+ case 2:
+ o_ptr = &p_ptr->inventory[INVEN_ARM];
+ break;
+ case 3:
+ o_ptr = &p_ptr->inventory[INVEN_OUTER];
+ break;
+ case 4:
+ o_ptr = &p_ptr->inventory[INVEN_HANDS];
+ break;
+ case 5:
+ o_ptr = &p_ptr->inventory[INVEN_HEAD];
+ break;
+ case 6:
+ o_ptr = &p_ptr->inventory[INVEN_FEET];
+ break;
+ }
+
+ /* Nothing to damage */
+ if (!o_ptr->k_idx) return (FALSE);
+
+ /* No damage left to be done */
+ if (o_ptr->ac + o_ptr->to_a <= 0) return (FALSE);
+
+
+ /* Describe */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Object resists */
+ if (f3 & (TR3_IGNORE_ACID))
+ {
+ msg_format("Your %s is unaffected!", o_name);
+
+ return (TRUE);
+ }
+
+ /* Message */
+ msg_format("Your %s is damaged!", o_name);
+
+ /* Damage the item */
+ o_ptr->to_a--;
+
+ /* Calculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_EQUIP | PW_PLAYER);
+
+ /* Item was damaged */
+ return (TRUE);
+}
+
+
+/*
+ * Hurt the player with Acid
+ */
+void acid_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Total Immunity */
+ if (p_ptr->immune_acid || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->resist_acid) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_acid) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_acid || p_ptr->resist_acid)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_CHR, STAT_DEC_NORMAL);
+
+ /* If any armor gets hit, defend the player */
+ if (minus_ac()) dam = (dam + 1) / 2;
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->oppose_acid && p_ptr->resist_acid))
+ inven_damage(set_acid_destroy, inv);
+}
+
+
+/*
+ * Hurt the player with electricity
+ */
+void elec_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Total immunity */
+ if (p_ptr->immune_elec || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->oppose_elec) dam = (dam + 2) / 3;
+ if (p_ptr->resist_elec) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_elec || p_ptr->resist_elec)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_DEX, STAT_DEC_NORMAL);
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->oppose_elec && p_ptr->resist_elec))
+ inven_damage(set_elec_destroy, inv);
+}
+
+
+
+
+/*
+ * Hurt the player with Fire
+ */
+void fire_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Totally immune */
+ if (p_ptr->immune_fire || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->sensible_fire) dam = (dam + 2) * 2;
+ if (p_ptr->resist_fire) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_fire) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_fire || p_ptr->resist_fire)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->resist_fire && p_ptr->oppose_fire))
+ inven_damage(set_fire_destroy, inv);
+}
+
+
+/*
+ * Hurt the player with Cold
+ */
+void cold_dam(int dam, cptr kb_str)
+{
+ int inv = (dam < 30) ? 1 : (dam < 60) ? 2 : 3;
+
+ /* Total immunity */
+ if (p_ptr->immune_cold || (dam <= 0)) return;
+
+ /* Resist the damage */
+ if (p_ptr->resist_cold) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_cold) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_cold || p_ptr->resist_cold)) &&
+ randint(HURT_CHANCE) == 1)
+ (void) do_dec_stat(A_STR, STAT_DEC_NORMAL);
+
+ /* Take damage */
+ take_hit(dam, kb_str);
+
+ /* Inventory damage */
+ if (!(p_ptr->resist_cold && p_ptr->oppose_cold))
+ inven_damage(set_cold_destroy, inv);
+}
+
+
+
+/*
+ * 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".
+ *
+ * This function returns the coordinates of all the grids on the path.
+ * The returned vector will be empty if and only if (y1,x1) and (y2,x2) are
+ * equal. 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 size of the result of this function should never be
+ * compared directly to "range". Note that the initial grid (y1,x1) is
+ * never saved into the returned grid array, not even if the initial grid
+ * is also the final grid.
+ *
+ * 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 algorithm is similar to, but slightly different from, the one used
+ * by "update_view_los()", and very different from the one used by "los()".
+ */
+static std::vector<std::tuple<int, int>> project_path(unsigned int range, int y1, int x1, int y2, int x2, int flg)
+{
+ int y, x, mana = 0, dir = 0;
+
+ /* Output grids */
+ std::vector<std::tuple<int, int>> gp;
+ gp.reserve(range + 10);
+
+ /* 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 gp;
+ }
+
+ /* 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.push_back(std::make_tuple(y, x));
+
+ /* Hack -- Check maximum range */
+ if (gp.size() >= range + 10)
+ {
+ return gp;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((!cave_sight_bold(y, x) || !cave_floor_bold(y, x)))
+ {
+ return gp;
+ }
+
+ /* Sometimes stop at non-initial monsters/players */
+ if ((flg & (PROJECT_STOP)) && (cave[y][x].m_idx != 0))
+ {
+ return gp;
+ }
+
+ /* Get the new direction */
+ dir = get_mana_path_dir(y, x, oy, ox, pdir, mana);
+ if (dir == 5)
+ {
+ return gp;
+ }
+
+ 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;
+
+ /* Counter for distance calculation */
+ int k = 0;
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp.push_back(std::make_tuple(y, x));
+
+ /* Hack -- Check maximum range */
+ if ((gp.size() + (k >> 1)) >= range)
+ {
+ break;
+ }
+
+ /* Sometimes stop at destination grid */
+ if (!(flg & (PROJECT_THRU)) && (x == x2) && (y == y2))
+ {
+ break;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((!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)) && (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;
+
+ /* Counter for distance calculation */
+ int k = 0;
+
+ /* Create the projection path */
+ while (1)
+ {
+ /* Save grid */
+ gp.push_back(std::make_tuple(y, x));
+
+ /* Hack -- Check maximum range */
+ if ((gp.size() + (k >> 1)) >= range)
+ {
+ break;
+ }
+
+ /* Sometimes stop at destination grid */
+ if (!(flg & (PROJECT_THRU)) && (x == x2) && (y == y2))
+ {
+ break;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((!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)) && (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.push_back(std::make_tuple(y, x));
+
+ /* Hack -- Check maximum range */
+ if ((gp.size() + (gp.size() >> 1)) >= range)
+ {
+ break;
+ }
+
+ /* Sometimes stop at destination grid */
+ if (!(flg & (PROJECT_THRU)) && (x == x2) && (y == y2))
+ {
+ break;
+ }
+
+ /* Always stop at non-initial wall grids */
+ if ((!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)) && (cave[y][x].m_idx != 0))
+ {
+ break;
+ }
+
+ /* Advance (Y) */
+ y += sy;
+
+ /* Advance (X) */
+ x += sx;
+ }
+ }
+
+ /* Done */
+ return gp;
+}
+
+
+
+/*
+ * 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);
+
+ /* Check gods */
+ project_check_gods(typ);
+
+ /* Analyze the type */
+ switch (typ)
+ {
+ /* Ignore most effects */
+ case GF_ELEC:
+ case GF_SOUND:
+ case GF_MANA:
+ case GF_PSI:
+ case GF_PSI_DRAIN:
+ case GF_TELEKINESIS:
+ case GF_DOMINATION:
+ {
+ break;
+ }
+
+ case GF_COLD:
+ case GF_ICE:
+ {
+ int percent = c_ptr->feat == GF_COLD ? 20 : 50;
+
+ /* Only affects "boring" grids */
+ if (!cave_plain_floor_bold(y, x)) break;
+
+ if (rand_int(100) < percent)
+ {
+ cave_set_feat(y, x, FEAT_ICE);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case GF_BETWEEN_GATE:
+ {
+ int y1 = randint(cur_hgt) - 1;
+ int x1 = randint(cur_wid) - 1;
+ int y2 = y1;
+ int x2 = x1;
+ int tries = 1000;
+
+ /*
+ * Avoid "interesting" and/or permanent features
+ *
+ * If we can make sure that all the "permanent" features
+ * have the remember flag set as well, we can simplify
+ * the conditional... -- pelpel
+ */
+ if (!cave_plain_floor_bold(y, x) ||
+ (f_info[cave[y][x].feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Destination shouldn't be "interesting" either */
+ while (tries &&
+ (!cave_plain_floor_bold(y2, x2) ||
+ (f_info[cave[y2][x2].feat].flags1 & FF1_PERMANENT)))
+ {
+ y2 = y1 = randint(cur_hgt) - 1;
+ x2 = x1 = randint(cur_wid) - 1;
+ scatter(&y2, &x2, y1, x1, 20);
+ tries --;
+ }
+
+ /* No boarding grids found */
+ if (!tries) break;
+
+ /* Place a pair of between gates */
+ cave_set_feat(y, x, FEAT_BETWEEN);
+ cave[y][x].special = x2 + (y2 << 8);
+
+ cave_set_feat(y2, x2, FEAT_BETWEEN);
+ cave[y2][x2].special = x + (y << 8);
+
+ if (seen)
+ {
+ obvious = TRUE;
+ note_spot(y, x);
+ }
+
+ if (player_can_see_bold(y2, x2))
+ {
+ obvious = TRUE;
+ note_spot(y2, x2);
+ }
+
+ break;
+ }
+
+ /* Burn trees & melt ice */
+ case GF_FIRE:
+ case GF_METEOR:
+ case GF_PLASMA:
+ case GF_HOLY_FIRE:
+ case GF_HELL_FIRE:
+ {
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Trees *will* burn */
+ if (c_ptr->feat == FEAT_TREES)
+ {
+ cave_set_feat(y, x, FEAT_DEAD_TREE);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ inc_piety(GOD_YAVANNA, -50);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Trees *will* burn */
+ if (c_ptr->feat == FEAT_SMALL_TREES)
+ {
+ cave_set_feat(y, x, FEAT_DEAD_SMALL_TREE);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ inc_piety(GOD_YAVANNA, -60);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Ice can melt (chance == 30%) */
+ else if (c_ptr->feat == FEAT_ICE)
+ {
+ int k = rand_int(100);
+
+ if (k >= 30) break;
+
+ /* Melt ice */
+ if (k < 10) cave_set_feat(y, x, FEAT_DIRT);
+ else if (k < 30) cave_set_feat(y, x, FEAT_SHAL_WATER);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Floors can become ash or lava (chance == 25%) */
+ else if (f_info[c_ptr->feat].flags1 & FF1_FLOOR)
+ {
+ int k = rand_int(100);
+
+ if (k >= 25) break;
+
+ /* Burn floor */
+ if (k < 10) cave_set_feat(y, x, FEAT_SHAL_LAVA);
+ else if (k < 25) cave_set_feat(y, x, FEAT_ASH);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Sandwall can be turned into glass (chance == 30%) */
+ else if ((c_ptr->feat == FEAT_SANDWALL) ||
+ (c_ptr->feat == FEAT_SANDWALL_H) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ int k = rand_int(100);
+
+ /* Glass it */
+ if (k < 30)
+ {
+ cave_set_feat(y, x, FEAT_GLASS_WALL);
+
+ /* Visibility change */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ if (seen) obvious = TRUE;
+ }
+
+ }
+
+ break;
+ }
+
+ case GF_WAVE:
+ case GF_WATER:
+ {
+ int p1 = 0;
+ int p2 = 0;
+ int f1 = 0;
+ int f2 = 0;
+ int f = 0;
+ int k;
+
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Needs more than 30 damage */
+ if (dam < 30) break;
+
+ if ((c_ptr->feat == FEAT_FLOOR) ||
+ (c_ptr->feat == FEAT_DIRT) ||
+ (c_ptr->feat == FEAT_GRASS))
+ {
+ /* 35% chance to create shallow water */
+ p1 = 35;
+ f1 = FEAT_SHAL_WATER;
+
+ /* 5% chance to create deep water */
+ p2 = 40;
+ f2 = FEAT_DEEP_WATER;
+ }
+ else if ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_MAGMA_H) ||
+ (c_ptr->feat == FEAT_MAGMA_K) ||
+ (c_ptr->feat == FEAT_SHAL_LAVA))
+ {
+ /* 15% chance to convert it to normal floor */
+ p1 = 15;
+ f1 = FEAT_FLOOR;
+ }
+ else if (c_ptr->feat == FEAT_DEEP_LAVA)
+ {
+ /* 10% chance to convert it to shallow lava */
+ p1 = 10;
+ f1 = FEAT_SHAL_LAVA;
+
+ /* 5% chance to convert it to normal floor */
+ p2 = 15;
+ f2 = FEAT_FLOOR;
+ }
+ else if ((c_ptr->feat == FEAT_SHAL_WATER) ||
+ (c_ptr->feat == FEAT_DARK_PIT))
+ {
+ /* 10% chance to convert it to deep water */
+ p1 = 10;
+ f1 = FEAT_DEEP_WATER;
+ }
+
+ k = rand_int(100);
+
+ if (k < p1) f = f1;
+ else if (k < p2) f = f2;
+
+ if (f)
+ {
+ if (f == FEAT_FLOOR) place_floor_convert_glass(y, x);
+ else cave_set_feat(y, x, f);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case GF_NETHER:
+ case GF_NEXUS:
+ case GF_ACID:
+ case GF_SHARDS:
+ case GF_TIME:
+ case GF_FORCE:
+ case GF_NUKE:
+ {
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ if ((c_ptr->feat == FEAT_TREES) ||
+ (c_ptr->feat == FEAT_SMALL_TREES))
+ {
+ /* Destroy the grid */
+ cave_set_feat(y, x, FEAT_DEAD_TREE);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ inc_piety(GOD_YAVANNA, -50);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ case GF_DISINTEGRATE:
+ {
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ if (((c_ptr->feat == FEAT_TREES) ||
+ (c_ptr->feat == FEAT_SMALL_TREES) ||
+ (f_info[c_ptr->feat].flags1 & FF1_FLOOR)) &&
+ (rand_int(100) < 30))
+ {
+ /* Flow change */
+ if (c_ptr->feat == FEAT_TREES) p_ptr->update |= (PU_FLOW);
+
+ cave_set_feat(y, x, FEAT_ASH);
+
+ /* Silly thing to destroy trees when a yavanna worshipper */
+ if (c_ptr->feat == FEAT_TREES || c_ptr->feat == FEAT_SMALL_TREES)
+ inc_piety(GOD_YAVANNA, -50);
+
+ /* Visibility change */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ if (seen) obvious = TRUE;
+ }
+
+ break;
+ }
+
+ /* Destroy Traps (and Locks) */
+ case GF_KILL_TRAP:
+ {
+ /* Destroy normal traps and disarm monster traps */
+ if ((c_ptr->t_idx != 0) || (c_ptr->feat == FEAT_MON_TRAP))
+ {
+ /* Check line of sight */
+ if (player_has_los_bold(y, x))
+ {
+ msg_print("There is a bright flash of light!");
+ obvious = TRUE;
+ }
+
+ /* Forget the trap */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT);
+
+ /* Destroy normal traps */
+ c_ptr->t_idx = 0;
+
+ /* Disarm monster traps */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ {
+ c_ptr->special = c_ptr->special2 = 0;
+
+ /* Remove the feature */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_PERMANENT))
+ place_floor_convert_glass(y, x);
+ }
+
+ /* Hack -- Force redraw */
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+
+ /* Secret / Locked doors are found and unlocked */
+ else if ((c_ptr->feat == FEAT_SECRET) ||
+ ((c_ptr->feat >= FEAT_DOOR_HEAD + 0x01) &&
+ (c_ptr->feat <= FEAT_DOOR_HEAD + 0x07)))
+ {
+
+ /* Check line of sound */
+ if (player_has_los_bold(y, x))
+ {
+ msg_print("Click!");
+ obvious = TRUE;
+ }
+
+ /* Remove feature mimic */
+ cave[y][x].mimic = 0;
+
+ /* Unlock the door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+ }
+
+ break;
+ }
+
+ /* Destroy Doors (and traps) */
+ case GF_KILL_DOOR:
+ {
+ /* Destroy all doors and traps, and disarm monster traps */
+ if ((c_ptr->feat == FEAT_OPEN) ||
+ (c_ptr->feat == FEAT_BROKEN) ||
+ (c_ptr->t_idx != 0) ||
+ (c_ptr->feat == FEAT_MON_TRAP) ||
+ ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)))
+ {
+ /* Check line of sight */
+ if (player_has_los_bold(y, x))
+ {
+ /* Message */
+ msg_print("There is a bright flash of light!");
+ obvious = TRUE;
+
+ /* Visibility change */
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+ }
+ }
+
+ /* Forget the door */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_TRDT);
+
+ /* Remove normal traps */
+ c_ptr->t_idx = 0;
+
+ /* Disarm monster traps */
+ if (c_ptr->feat == FEAT_MON_TRAP)
+ c_ptr->special = c_ptr->special2 = 0;
+
+ /* Remove the feature */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_PERMANENT))
+ place_floor_convert_glass(y, x);
+
+ /* Hack -- Force redraw */
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+
+ break;
+ }
+
+ case GF_JAM_DOOR: /* Jams a door (as if with a spike) */
+ {
+ if ((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL))
+ {
+ /* Convert "locked" to "stuck" XXX XXX XXX */
+ if (c_ptr->feat < FEAT_DOOR_HEAD + 0x08) c_ptr->feat += 0x08;
+
+ /* Add one spike to the door */
+ if (c_ptr->feat < FEAT_DOOR_TAIL) c_ptr->feat++;
+
+ /* Check line of sight */
+ if (player_has_los_bold(y, x))
+ {
+ /* Message */
+ msg_print("The door seems stuck.");
+ obvious = TRUE;
+ }
+ }
+
+ break;
+ }
+
+ /* Destroy walls (and doors) */
+ case GF_KILL_WALL:
+ {
+ /* Non-walls (etc) */
+ if (cave_floor_bold(y, x)) break;
+
+ /* "Permanent" features will stay */
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Granite -- How about other wall types? */
+ if ((c_ptr->feat >= FEAT_WALL_EXTRA) &&
+ (c_ptr->feat <= FEAT_WALL_SOLID))
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The wall turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the wall */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Quartz / Magma / Sand with treasure */
+ else if (((c_ptr->feat >= FEAT_MAGMA_H) &&
+ (c_ptr->feat <= FEAT_QUARTZ_K)) ||
+ (c_ptr->feat == FEAT_SANDWALL_K))
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The vein turns into mud!");
+ msg_print("You have found something!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the wall */
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Place some gold */
+ place_gold(y, x);
+ }
+
+ /* Quartz / Magma / Sand */
+ else if ((c_ptr->feat == FEAT_MAGMA) ||
+ (c_ptr->feat == FEAT_QUARTZ) ||
+ (c_ptr->feat == FEAT_SANDWALL) ||
+ (c_ptr->feat == FEAT_SANDWALL_H))
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The vein turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the wall */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Rubble */
+ else if (c_ptr->feat == FEAT_RUBBLE)
+ {
+ /* Message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The rubble turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Destroy the rubble */
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Hack -- place an object */
+ if (rand_int(100) < 10)
+ {
+ /* Found something */
+ if (seen)
+ {
+ msg_print("There was something buried in the rubble!");
+ obvious = TRUE;
+ }
+
+ /* Place gold */
+ place_object(y, x, FALSE, FALSE, OBJ_FOUND_RUBBLE);
+ }
+ }
+
+ /* Destroy doors (and secret doors) */
+ else if (((c_ptr->feat >= FEAT_DOOR_HEAD) &&
+ (c_ptr->feat <= FEAT_DOOR_TAIL)) ||
+ (c_ptr->feat == FEAT_SECRET))
+ {
+ /* Hack -- special message */
+ if (c_ptr->info & (CAVE_MARK))
+ {
+ msg_print("The door turns into mud!");
+ obvious = TRUE;
+ }
+
+ /* Forget the wall */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Remove mimic */
+ c_ptr->mimic = 0;
+
+ /* Destroy the feature */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+
+ break;
+ }
+
+ /* Make doors */
+ case GF_MAKE_DOOR:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Create a closed door */
+ cave_set_feat(y, x, FEAT_DOOR_HEAD + 0x00);
+
+ /* Observe */
+ if (c_ptr->info & (CAVE_MARK)) obvious = TRUE;
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ break;
+ }
+
+ /* Make traps */
+ case GF_MAKE_TRAP:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ /* Place a trap */
+ place_trap(y, x);
+
+ break;
+ }
+
+
+ case GF_MAKE_GLYPH:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ cave_set_feat(y, x, FEAT_GLYPH);
+
+ if (seen) obvious = TRUE;
+
+ break;
+ }
+
+
+
+ case GF_STONE_WALL:
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_clean_bold(y, x)) break;
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+ if (!(f_info[c_ptr->feat].flags1 & FF1_FLOOR)) break;
+
+ /* Place a wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+
+ if (seen) obvious = TRUE;
+
+ /* Update some things */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ break;
+ }
+
+ case GF_WINDS_MANA:
+ {
+ if (dam >= 256)
+ {
+ /* With erase mana */
+
+ /* Absorb some of the mana of the grid */
+ p_ptr->csp += cave[y][x].mana / 80;
+ if (p_ptr->csp > p_ptr->msp) p_ptr->csp = p_ptr->msp;
+
+ /* Set the new amount */
+ cave[y][x].mana = dam - 256;
+ }
+ else
+ {
+ /* Without erase mana */
+ int amt = cave[y][x].mana + dam;
+
+ /* Check if not overflow */
+ if (amt > 255) amt = 255;
+
+ /* Set the new amount */
+ cave[y][x].mana = amt;
+ }
+
+ break;
+ }
+
+ case GF_LAVA_FLOW:
+ {
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Shallow Lava */
+ if (dam == 1)
+ {
+ /* Require a "naked" floor grid */
+ if (!cave_naked_bold(y, x)) break;
+
+ /* Place a shallow lava */
+ cave_set_feat(y, x, FEAT_SHAL_LAVA);
+
+ if (seen) obvious = TRUE;
+ }
+
+ /* Deep Lava */
+ else
+ {
+ /* Require a "naked" floor grid */
+ if (cave_perma_bold(y, x) || !dam) break;
+
+ /* Place a deep lava */
+ cave_set_feat(y, x, FEAT_DEEP_LAVA);
+
+ if (seen) obvious = TRUE;
+
+ /* Dam is used as a counter for the number of grid to convert */
+ dam--;
+ }
+
+ break;
+ }
+
+ /* Lite up the grid */
+ case GF_LITE_WEAK:
+ case GF_LITE:
+ {
+ /* Turn on the light */
+ c_ptr->info |= (CAVE_GLOW);
+
+ /* Notice */
+ note_spot(y, x);
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /* Observe */
+ if (seen) obvious = TRUE;
+
+ /*
+ * Mega-Hack -- Update the monster in the affected grid
+ * This allows "spear of light" (etc) to work "correctly"
+ */
+ if (c_ptr->m_idx) update_mon(c_ptr->m_idx, FALSE);
+
+ break;
+ }
+
+ /* Darken the grid */
+ case GF_DARK_WEAK:
+ case GF_DARK:
+ {
+ /* Notice */
+ if (seen) obvious = TRUE;
+
+ /* Turn off the light. */
+ c_ptr->info &= ~(CAVE_GLOW);
+
+ /* Hack -- Forget "boring" grids */
+ if (cave_plain_floor_grid(c_ptr))
+ {
+ /* Forget */
+ c_ptr->info &= ~(CAVE_MARK);
+
+ /* Notice */
+ note_spot(y, x);
+ }
+
+ /* Redraw */
+ lite_spot(y, x);
+
+ /*
+ * Mega-Hack -- Update the monster in the affected grid
+ * This allows "spear of light" (etc) to work "correctly"
+ */
+ if (c_ptr->m_idx) update_mon(c_ptr->m_idx, FALSE);
+
+ /* All done */
+ break;
+ }
+
+ case GF_DESTRUCTION:
+ {
+ int t;
+
+ /* Lose room and vault */
+ c_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+
+ /* Lose light and knowledge */
+ c_ptr->info &= ~(CAVE_MARK | CAVE_GLOW);
+
+ /* Hack -- Notice player affect */
+ if ((x == p_ptr->px) && (y == p_ptr->py))
+ {
+ /* Hurt the player later */
+ flag = TRUE;
+
+ /* Do not hurt this grid */
+ break;
+ ;
+ }
+
+ /* Delete the monster (if any) */
+ delete_monster(y, x);
+
+ if ((f_info[c_ptr->feat].flags1 & FF1_PERMANENT)) break;
+
+ /* Destroy "valid" grids */
+ if (cave_valid_bold(y, x))
+ {
+ /* Delete objects */
+ delete_object(y, x);
+
+ /* Wall (or floor) type */
+ t = rand_int(200);
+
+ /* Granite */
+ if (t < 20)
+ {
+ /* Create granite wall */
+ cave_set_feat(y, x, FEAT_WALL_EXTRA);
+ }
+
+ /* Quartz */
+ else if (t < 60)
+ {
+ /* Create quartz vein */
+ cave_set_feat(y, x, FEAT_QUARTZ);
+ }
+
+ /* Magma */
+ else if (t < 90)
+ {
+ /* Create magma vein */
+ cave_set_feat(y, x, FEAT_MAGMA);
+ }
+
+ /* Sand */
+ else if (t < 110)
+ {
+ /* Create sand vein */
+ cave_set_feat(y, x, FEAT_SANDWALL);
+ }
+
+ /* Floor */
+ else
+ {
+ /* Create floor */
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+
+ /* Visibility and flow changes */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MONSTERS | PU_MON_LITE);
+ }
+
+ obvious = TRUE;
+ break;
+ }
+
+ case GF_ELEMENTAL_WALL:
+ {
+ if ((p_ptr->py != y) || (p_ptr->px != x)) {
+ geomancy_random_wall(y, x);
+ }
+ break;
+ }
+
+ case GF_ELEMENTAL_GROWTH:
+ {
+ geomancy_random_floor(y, x, FALSE);
+ 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];
+
+ bool_ obvious = FALSE;
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ char o_name[80];
+
+ int o_sval = 0;
+ bool_ is_potion = FALSE;
+
+
+ /* XXX XXX XXX */
+ who = who ? who : 0;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+ /* Check new gods. */
+ project_check_gods(typ);
+
+ /* Copy list of objects since we may destroy during iteration */
+ auto const object_idxs(c_ptr->o_idxs);
+
+ /* Scan all objects in the grid */
+ for (auto const this_o_idx: object_idxs)
+ {
+ bool_ is_art = FALSE;
+ bool_ ignore = FALSE;
+ bool_ plural = FALSE;
+ bool_ do_kill = FALSE;
+
+ cptr note_kill = NULL;
+
+ /* Acquire object */
+ object_type * o_ptr = &o_list[this_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 */
+ identify_hooks(0 - this_o_idx, o_ptr, IDENT_FULL);
+
+ /* Squelch ! */
+ squeltch_grid();
+
+ break;
+ }
+ case GF_IDENTIFY:
+ {
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Process the appropriate hooks */
+ identify_hooks(0 - this_o_idx, o_ptr, IDENT_NORMAL);
+
+ /* Squelch ! */
+ squeltch_grid();
+
+ break;
+ }
+ case GF_RAISE:
+ {
+ get_pos_player(7, &y, &x);
+
+ /* Only corpses can be raised */
+ if (o_ptr->tval == TV_CORPSE)
+ {
+ int ego = raise_ego[rand_int(MAX_RAISE)];
+
+ if (place_monster_one(y, x, o_ptr->pval2, ego, FALSE, (!who) ? MSTATUS_PET : MSTATUS_ENEMY))
+ msg_print("A monster rises from the grave!");
+ do_kill = TRUE;
+ }
+ break;
+ }
+ case GF_RAISE_DEMON:
+ {
+ monster_race *r_ptr = &r_info[o_ptr->pval2];
+ cptr name;
+
+ if (o_ptr->tval != TV_CORPSE) break;
+
+ if (randint(100) > r_ptr->level - p_ptr->lev)
+ {
+ if (r_ptr->level < 10) name = "Manes";
+ else if (r_ptr->level < 18) name = "Tengu";
+ else if (r_ptr->level < 26) name = "Imp";
+ else if (r_ptr->level < 34) name = "Arch-vile";
+ else if (r_ptr->level < 42) name = "Bodak";
+ else if (r_ptr->level < 50) name = "Erynies";
+ else if (r_ptr->level < 58) name = "Vrock";
+ else if (r_ptr->level < 66) name = "Hezrou";
+ else if (r_ptr->level < 74) name = "Glabrezu";
+ else if (r_ptr->level < 82) name = "Nalfeshnee";
+ else if (r_ptr->level < 90) name = "Marilith";
+ else name = "Nycadaemon";
+
+ if (place_monster_one(y, x, test_monster_name(name), 0, FALSE, (!who) ? MSTATUS_PET : MSTATUS_ENEMY))
+ msg_print("A demon emerges from Hell!");
+ }
+
+ do_kill = TRUE;
+ break;
+ }
+ default:
+ 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];
+
+ char killer [80];
+
+ /* Is the monster "seen"? */
+ bool_ seen;
+
+ /* Were the effects "obvious" (if seen)? */
+ bool_ obvious = FALSE;
+
+ /* Were the effects "irrelevant"? */
+ bool_ skipped = FALSE;
+
+
+ /* Move setting */
+ int x1 = 0;
+ int y1 = 0;
+ int a = 0;
+ int b = 0;
+ int do_move = 0;
+
+ /* Polymorph setting (true or false) */
+ int do_poly = 0;
+
+ /* Teleport setting (max distance) */
+ int do_dist = 0;
+
+ /* Confusion setting (amount to confuse) */
+ int do_conf = 0;
+
+ /* Stunning setting (amount to stun) */
+ int do_stun = 0;
+
+ /* Bleeding amount */
+ int do_cut = 0;
+
+ /* Poison amount */
+ int do_pois = 0;
+
+ /* Sleep amount (amount to sleep) */
+ int do_sleep = 0;
+
+ /* Fear amount (amount to fear) */
+ int do_fear = 0;
+
+
+ /* Hold the monster name */
+ char m_name[80];
+
+ /* Assume no note */
+ cptr note = NULL;
+
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+
+ /* Nobody here */
+ if (!c_ptr->m_idx) return (FALSE);
+
+ /* Never affect projector */
+ if (who && (c_ptr->m_idx == who)) return (FALSE);
+
+ /*
+ * Don't affect already dead monsters
+ * Prevents problems with chain reactions of exploding monsters
+ */
+ if (m_ptr->hp < 0) return (FALSE);
+
+
+ /* Remember if the monster is within player's line of sight */
+ seen = (m_ptr->ml && ((who != -101) && (who != -100))) ? TRUE : FALSE;
+
+ /* Reduce damage by distance */
+ dam = (dam + r) / (r + 1);
+
+
+ /* Check gods */
+ project_check_gods(typ);
+
+ /* Get the monster name (BEFORE polymorphing) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get race information */
+ auto r_ptr = m_ptr->race();
+
+ /* 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;
+ case GF_INSTA_DEATH:
+ get_angry = TRUE;
+ break;
+ case GF_ELEMENTAL_GROWTH:
+ case GF_ELEMENTAL_WALL:
+ get_angry = FALSE;
+ break;
+ }
+
+ /* 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(r_ptr->name, "W") ||
+ (strstr(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(r_ptr->name, "W") ||
+ (strstr(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(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_FRAME;
+ 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_FRAME;
+ }
+
+ note_dies = " collapses, a mindless husk.";
+ break;
+ }
+
+ case GF_TELEKINESIS:
+ {
+ if (seen) obvious = TRUE;
+ do_dist = 7;
+ /* 1. stun */
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > 5 + randint(dam)))
+ {
+ /* Resist */
+ do_stun = 0;
+ /* No obvious effect */
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ /* Meteor -- powerful magic missile */
+ case GF_METEOR:
+ {
+ if (seen) obvious = TRUE;
+ break;
+ }
+
+ case GF_DOMINATION:
+ {
+ if (is_friend(m_ptr) > 0) break;
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /*
+ * Powerful demons & undead can turn a mindcrafter's
+ * attacks back on them
+ */
+ if (((r_ptr->flags3 & RF3_UNDEAD) ||
+ (r_ptr->flags3 & RF3_DEMON)) &&
+ (m_ptr->level > p_ptr->lev / 2) &&
+ (randint(2) == 1))
+ {
+ note = NULL;
+ msg_format("%^s%s corrupted mind backlashes your attack!",
+ m_name, (seen ? "'s" : "s"));
+ /* Saving throw */
+ if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You resist the effects!");
+ }
+ else
+ {
+ /* Confuse, stun, terrify */
+ switch (randint(4))
+ {
+ case 1:
+ set_stun(p_ptr->stun + dam / 2);
+ break;
+ case 2:
+ set_confused(p_ptr->confused + dam / 2);
+ break;
+ default:
+ {
+ if (r_ptr->flags3 & (RF3_NO_FEAR))
+ note = " is unaffected.";
+ else
+ set_afraid(p_ptr->afraid + dam);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ }
+ else
+ {
+ if ((dam > 29) && (randint(100) < dam))
+ {
+ note = " is in your thrall!";
+ m_ptr->status = MSTATUS_PET;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+ else
+ {
+ switch (randint(4))
+ {
+ case 1:
+ do_stun = dam / 2;
+ break;
+ case 2:
+ do_conf = dam / 2;
+ break;
+ default:
+ do_fear = dam;
+ }
+ }
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+
+ /* Ice -- Cold + Cuts + Stun */
+ case GF_ICE:
+ {
+ if (seen) obvious = TRUE;
+ do_stun = (randint(15) + 1) / (r + 1);
+ if (magik(33)) do_cut = (10 + randint(15) + r) / (r + 1);
+ if (r_ptr->flags3 & (RF3_SUSCEP_COLD))
+ {
+ note = " is hit hard.";
+ dam *= 3;
+ do_cut *= 2;
+ if (seen) r_ptr->r_flags3 |= (RF3_SUSCEP_COLD);
+ }
+ if (r_ptr->flags3 & (RF3_IM_COLD))
+ {
+ note = " resists a lot.";
+ dam /= 9;
+ do_cut = 0;
+ if (seen) r_ptr->r_flags3 |= (RF3_IM_COLD);
+ }
+ break;
+ }
+
+
+ /* Drain Life */
+ case GF_OLD_DRAIN:
+ {
+ if (seen) obvious = TRUE;
+
+ if ((r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)) ||
+ (strchr("Egv", r_ptr->d_char)))
+ {
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+ if (r_ptr->flags3 & (RF3_DEMON))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_DEMON);
+ }
+
+ note = " is unaffected!";
+ obvious = FALSE;
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Death Ray */
+ case GF_DEATH_RAY:
+ {
+ if (seen) obvious = TRUE;
+ if ((r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags3 & (RF3_NONLIVING)))
+ {
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ }
+
+ note = " is immune.";
+ obvious = FALSE;
+ dam = 0;
+ }
+ else if (((r_ptr->flags1 & (RF1_UNIQUE)) &&
+ (randint(888) != 666)) ||
+ (((m_ptr->level + randint(20)) > randint((dam) + randint(10))) &&
+ randint(100) != 66 ))
+ {
+ note = " resists!";
+ obvious = FALSE;
+ dam = 0;
+ }
+
+ else dam = (p_ptr->lev) * 200;
+
+ break;
+ }
+
+ /* Polymorph monster (Use "dam" as "power") */
+ case GF_OLD_POLY:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt to polymorph (see below) */
+ do_poly = TRUE;
+
+ /* Powerful monsters can resist */
+ if ((r_ptr->flags1 & RF1_UNIQUE) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ note = " is unaffected!";
+ do_poly = FALSE;
+ obvious = FALSE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+
+ break;
+ }
+
+
+ /* Clone monsters (Ignore "dam") */
+ case GF_OLD_CLONE:
+ {
+ bool_ is_frien = FALSE;
+
+ if (seen) obvious = TRUE;
+ if ((is_friend(m_ptr) > 0) && (randint(3) != 1))
+ is_frien = TRUE;
+
+ /* Heal fully */
+ m_ptr->hp = m_ptr->maxhp;
+
+ /* Speed up */
+ if (m_ptr->mspeed < 150) m_ptr->mspeed += 10;
+
+ /* Attempt to clone. */
+ if (multiply_monster(c_ptr->m_idx, is_frien, TRUE))
+ {
+ note = " spawns!";
+ }
+
+ /* No "real" damage */
+ dam = 0;
+
+ break;
+ }
+
+
+ /* Heal Monster (use "dam" as amount of healing) */
+ case GF_OLD_HEAL:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Wake up */
+ m_ptr->csleep = 0;
+
+ /* Heal */
+ m_ptr->hp += dam;
+
+ /* No overflow */
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == c_ptr->m_idx) p_ptr->redraw |= (PR_FRAME);
+
+ /* Message */
+ note = " looks healthier.";
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Speed Monster (Ignore "dam") */
+ case GF_OLD_SPEED:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Speed up */
+ if (m_ptr->mspeed < m_ptr->speed + 15) m_ptr->mspeed += 10;
+ note = " starts moving faster.";
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Slow Monster (Use "dam" as "power") */
+ case GF_OLD_SLOW:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Powerful monsters can resist */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* Normal monsters slow down */
+ else
+ {
+ if (m_ptr->mspeed > 60) m_ptr->mspeed -= 10;
+ note = " starts moving slower.";
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Sleep (Use "dam" as "power") */
+ case GF_OLD_SLEEP:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_SLEEP)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_SLEEP))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_SLEEP);
+ }
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else
+ {
+ /* Go to sleep (much) later */
+ note = " falls asleep!";
+ do_sleep = 500;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Sleep (Use "dam" as "power") */
+ case GF_STASIS:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else
+ {
+ /* Go to sleep (much) later */
+ note = " is suspended!";
+ do_sleep = 500;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Charm monster */
+ case GF_CHARM:
+ {
+ dam += (adj_con_fix[p_ptr->stat_ind[A_CHR]] - 1);
+
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->mflag & MFLAG_QUEST) ||
+ (r_ptr->flags3 & RF3_NO_CONF) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 5))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ if (is_friend(m_ptr) < 0)
+ {
+ note = " suddenly seems friendly!";
+ m_ptr->status = MSTATUS_FRIEND;
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* *Charm* monster */
+ case GF_STAR_CHARM:
+ {
+ dam += (adj_con_fix[p_ptr->stat_ind[A_CHR]] - 1);
+
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->mflag & MFLAG_QUEST) ||
+ (r_ptr->flags3 & RF3_NO_CONF) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 5))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ if (is_friend(m_ptr) < 0)
+ {
+ note = " suddenly seems friendly!";
+ if (can_create_companion()) m_ptr->status = MSTATUS_COMPANION;
+ else m_ptr->status = MSTATUS_PET;
+
+ if ((r_ptr->flags3 & RF3_ANIMAL) && (!(r_ptr->flags3 & RF3_EVIL)))
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Control undead */
+ case GF_CONTROL_UNDEAD:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & RF1_UNIQUE) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & RF3_UNDEAD)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " is in your thrall!";
+ m_ptr->status = MSTATUS_PET;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Control never-moving */
+ case GF_CHARM_UNMOVING:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & RF1_UNIQUE) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags1 & RF1_NEVER_MOVE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " is in your thrall!";
+ m_ptr->status = MSTATUS_PET;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Tame animal */
+ case GF_CONTROL_ANIMAL:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & (RF3_ANIMAL))) ||
+ (r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " is tamed!";
+ m_ptr->status = MSTATUS_PET;
+ inc_piety(GOD_YAVANNA, m_ptr->level * 2);
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Control demon */
+ case GF_CONTROL_DEMON:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->mflag & MFLAG_QUEST) ||
+ (!(r_ptr->flags3 & (RF3_DEMON))) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ else if (p_ptr->aggravate)
+ {
+ note = " hates you too much!";
+ }
+ else
+ {
+ note = " obeys your commands!";
+ m_ptr->status = MSTATUS_PET;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Confusion (Use "dam" as "power") */
+ case GF_OLD_CONF:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Get confused later */
+ do_conf = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ case GF_STUN:
+ {
+ if (seen) obvious = TRUE;
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+ /* Confusion (Use "dam" as "power") */
+ case GF_CONF_DAM:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Get confused later */
+ do_conf = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ case GF_STUN_DAM:
+ {
+ if (seen) obvious = TRUE;
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ /* Implosion is the same than Stun_dam but only affect the living */
+ case GF_IMPLOSION:
+ {
+ if (seen) obvious = TRUE;
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ /* Non_living resists */
+ if (r_ptr->flags3 & (RF3_NONLIVING))
+ {
+ /* Resist */
+ do_stun = 0;
+ dam = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+ /* Confusion & Stunning (Use "dam" as "power") */
+ case GF_STUN_CONF:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Get confused later */
+ do_conf = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags3 & (RF3_NO_CONF)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Memorize a flag */
+ if (r_ptr->flags3 & (RF3_NO_CONF))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_NO_CONF);
+ }
+
+ /* Resist */
+ do_conf = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+
+ do_stun = damroll((p_ptr->lev / 10) + 3 , (dam)) + 1;
+
+ /* Attempt a saving throw */
+ if ((m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* Resist */
+ do_stun = 0;
+
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ }
+ break;
+ }
+
+
+ /* Lite, but only hurts susceptible creatures */
+ case GF_LITE_WEAK:
+ {
+ /* Hurt by light */
+ if (r_ptr->flags3 & (RF3_HURT_LITE))
+ {
+ /* Obvious effect */
+ if (seen) obvious = TRUE;
+
+ /* Memorize the effects */
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
+
+ /* Special effect */
+ note = " cringes from the light!";
+ note_dies = " shrivels away in the light!";
+ }
+
+ /* Normally no damage */
+ else
+ {
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+
+
+ /* Lite -- opposite of Dark */
+ case GF_LITE:
+ {
+ if (seen) obvious = TRUE;
+ if (r_ptr->flags4 & (RF4_BR_LITE))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ else if (r_ptr->flags3 & (RF3_HURT_LITE))
+ {
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_LITE);
+ note = " cringes from the light!";
+ note_dies = " shrivels away in the light!";
+ dam *= 2;
+ }
+ break;
+ }
+
+
+ /* Dark -- opposite of Lite */
+ case GF_DARK:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Likes darkness... */
+ if ((r_ptr->flags4 & (RF4_BR_DARK)) ||
+ (r_ptr->flags3 & RF3_ORC) ||
+ (r_ptr->flags3 & RF3_HURT_LITE))
+ {
+ note = " resists.";
+ dam *= 2;
+ dam /= (randint(6) + 6);
+ }
+ break;
+ }
+
+
+ /* Stone to Mud */
+ case GF_KILL_WALL:
+ {
+ /* Hurt by rock remover */
+ if (r_ptr->flags3 & (RF3_HURT_ROCK))
+ {
+ /* Notice effect */
+ if (seen) obvious = TRUE;
+
+ /* Memorize the effects */
+ if (seen) r_ptr->r_flags3 |= (RF3_HURT_ROCK);
+
+ /* Cute little message */
+ note = " loses some skin!";
+ note_dies = " dissolves!";
+ }
+
+ /* Usually, ignore the effects */
+ else
+ {
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+
+ /* Teleport undead (Use "dam" as "power") */
+ case GF_AWAY_UNDEAD:
+ {
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ /* Only affect undead */
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ bool_ resists_tele = FALSE;
+
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resists_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ if (seen) obvious = TRUE;
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+ do_dist = dam;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Teleport evil (Use "dam" as "power") */
+ case GF_AWAY_EVIL:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ /* Only affect evil */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ bool_ resists_tele = FALSE;
+
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resists_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ if (seen) obvious = TRUE;
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+ do_dist = dam;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Teleport monster (Use "dam" as "power") */
+ case GF_AWAY_ALL:
+ {
+ bool_ resists_tele = FALSE;
+
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ if (r_ptr->flags3 & (RF3_RES_TELE))
+ {
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " is unaffected!";
+ resists_tele = TRUE;
+ }
+ else if (m_ptr->level > randint(100))
+ {
+ if (seen) r_ptr->r_flags3 |= RF3_RES_TELE;
+ note = " resists!";
+ resists_tele = TRUE;
+ }
+ }
+
+ if (!resists_tele)
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Prepare to teleport */
+ do_dist = dam;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Turn undead (Use "dam" as "power") */
+ case GF_TURN_UNDEAD:
+ {
+ /* Only affect undead */
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Apply some fear */
+ do_fear = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10)
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ do_fear = 0;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Turn evil (Use "dam" as "power") */
+ case GF_TURN_EVIL:
+ {
+ /* Only affect evil */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Apply some fear */
+ do_fear = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10)
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ do_fear = 0;
+ }
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Turn monster (Use "dam" as "power") */
+ case GF_TURN_ALL:
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Apply some fear */
+ do_fear = damroll(3, (dam / 2)) + 1;
+
+ /* Attempt a saving throw */
+ if ((r_ptr->flags1 & (RF1_UNIQUE)) ||
+ (r_ptr->flags3 & (RF3_NO_FEAR)) ||
+ (m_ptr->level > randint((dam - 10) < 1 ? 1 : (dam - 10)) + 10))
+ {
+ /* No obvious effect */
+ note = " is unaffected!";
+ obvious = FALSE;
+ do_fear = 0;
+ }
+
+ /* No "real" damage */
+ dam = 0;
+ break;
+ }
+
+
+ /* Dispel undead */
+ case GF_DISP_UNDEAD:
+ {
+ /* Only affect undead */
+ if (r_ptr->flags3 & (RF3_UNDEAD))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_UNDEAD);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+
+ /* Dispel evil */
+ case GF_DISP_EVIL:
+ {
+ /* Only affect evil */
+ if (r_ptr->flags3 & (RF3_EVIL))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_EVIL);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel good */
+ case GF_DISP_GOOD:
+ {
+ /* Only affect good */
+ if (r_ptr->flags3 & (RF3_GOOD))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_GOOD);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel living */
+ case GF_DISP_LIVING:
+ {
+ /* Only affect non-undead */
+ if (!(r_ptr->flags3 & (RF3_UNDEAD)) &&
+ !(r_ptr->flags3 & (RF3_NONLIVING)))
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel demons */
+ case GF_DISP_DEMON:
+ {
+ /* Only affect demons */
+ if (r_ptr->flags3 & (RF3_DEMON))
+ {
+ /* Learn about type */
+ if (seen) r_ptr->r_flags3 |= (RF3_DEMON);
+
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+ }
+
+ /* Others ignore */
+ else
+ {
+ /* Irrelevant */
+ skipped = TRUE;
+
+ /* No damage */
+ dam = 0;
+ }
+
+ break;
+ }
+
+ /* Dispel monster */
+ case GF_DISP_ALL:
+ {
+ /* Obvious */
+ if (seen) obvious = TRUE;
+
+ /* Message */
+ note = " shudders.";
+ note_dies = " dissolves!";
+
+ break;
+ }
+
+ /* Raise Death -- Heal monster */
+ case GF_RAISE:
+ {
+ if (seen) obvious = TRUE;
+
+ /* Wake up */
+ m_ptr->csleep = 0;
+
+ /* Heal */
+ m_ptr->hp += dam;
+
+ /* No overflow */
+ if (m_ptr->hp > m_ptr->maxhp) m_ptr->hp = m_ptr->maxhp;
+
+ /* Redraw (later) if needed */
+ if (health_who == c_ptr->m_idx) p_ptr->redraw |= (PR_FRAME);
+
+ /* 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;
+ }
+
+ case GF_INSTA_DEATH:
+ {
+ if (magik(95) && !(r_ptr->flags1 & RF1_UNIQUE) && !(r_ptr->flags3 & RF3_UNDEAD) && !(r_ptr->flags3 & RF3_NONLIVING)) {
+ /* Kill outright, but reduce exp. */
+ m_ptr->level = m_ptr->level / 3;
+ dam = 32535; /* Should be enough */
+ note = " faints.";
+ note_dies = " is sucked out of life.";
+ } else {
+ /* No effect */
+ skipped = TRUE;
+ }
+
+ break;
+ }
+
+ default:
+ skipped = TRUE;
+ 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 = m_ptr->race();
+ }
+ }
+
+ /* 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);
+ 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_info[dungeon_type].name);
+ }
+ if (who == -101)
+ {
+ sprintf(killer, "%s", f_info[cave[p_ptr->py][p_ptr->px].feat].name);
+ }
+
+ if (who >= -1)
+ {
+ /* Get the source monster */
+ m_ptr = &m_list[who];
+
+ /* Get the monster name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Get the monster's real name */
+ monster_desc(killer, m_ptr, 0x88);
+ }
+
+ if (who == -2)
+ {
+ sprintf(killer, "%s",
+ t_info[cave[p_ptr->py][p_ptr->px].t_idx].name);
+ }
+
+ /* Analyze the damage */
+ switch (typ)
+ {
+ case GF_DEATH_RAY:
+ {
+ if (fuzzy) msg_print("You are hit by pure death!");
+ take_hit(32000, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_ACID:
+ {
+ if (fuzzy) msg_print("You are hit by acid!");
+ acid_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_FIRE:
+ {
+ if (fuzzy) msg_print("You are hit by fire!");
+ fire_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_COLD:
+ {
+ if (fuzzy) msg_print("You are hit by cold!");
+ cold_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- hurts inventory too */
+ case GF_ELEC:
+ {
+ if (fuzzy) msg_print("You are hit by lightning!");
+ elec_dam(dam, killer);
+ break;
+ }
+
+ /* Standard damage -- also poisons player */
+ case GF_POIS:
+ {
+ if (fuzzy) msg_print("You are hit by poison!");
+ if (p_ptr->resist_pois) dam = (dam + 2) / 3;
+ if (p_ptr->oppose_pois) dam = (dam + 2) / 3;
+
+ if ((!(p_ptr->oppose_pois || p_ptr->resist_pois)) &&
+ randint(HURT_CHANCE) == 1)
+ {
+ do_dec_stat(A_CON, STAT_DEC_NORMAL);
+ }
+
+ take_hit(dam, killer);
+
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(dam) + 10);
+ }
+ break;
+ }
+
+ /* Standard damage -- also poisons / mutates player */
+ case GF_NUKE:
+ {
+ if (fuzzy) msg_print("You are hit by radiation!");
+ if (p_ptr->resist_pois) dam = (2 * dam + 2) / 5;
+ if (p_ptr->oppose_pois) dam = (2 * dam + 2) / 5;
+ take_hit(dam, killer);
+ if (!(p_ptr->resist_pois || p_ptr->oppose_pois))
+ {
+ set_poisoned(p_ptr->poisoned + rand_int(dam) + 10);
+
+ if (randint(5) == 1) /* 6 */
+ {
+ msg_print("You undergo a freakish metamorphosis!");
+ if (randint(4) == 1) /* 4 */
+ do_poly_self();
+ else
+ corrupt_player();
+ }
+
+ if (randint(6) == 1)
+ {
+ inven_damage(set_acid_destroy, 2);
+ }
+ }
+ break;
+ }
+
+ /* Standard damage */
+ case GF_MISSILE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Holy Orb -- Player only takes partial damage */
+ case GF_HOLY_FIRE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ case GF_HELL_FIRE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Arrow -- XXX no dodging */
+ case GF_ARROW:
+ {
+ if (fuzzy) msg_print("You are hit by something sharp!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Plasma -- XXX No resist */
+ case GF_PLASMA:
+ {
+ if (fuzzy) msg_print("You are hit by something *HOT*!");
+ take_hit(dam, killer);
+
+ if (!p_ptr->resist_sound)
+ {
+ int k = (randint((dam > 40) ? 35 : (dam * 3 / 4 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+
+ if (!(p_ptr->resist_fire ||
+ p_ptr->oppose_fire ||
+ p_ptr->immune_fire))
+ {
+ inven_damage(set_acid_destroy, 3);
+ }
+
+ break;
+ }
+
+ /* Nether -- drain experience */
+ case GF_NETHER:
+ {
+ if (fuzzy) msg_print("You are hit by nether forces!");
+ {
+ if (p_ptr->immune_neth)
+ {
+ dam = 0;
+ }
+ else if (p_ptr->resist_neth)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(200 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(200 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+
+ take_hit(dam, killer);
+ }
+
+ break;
+ }
+
+ /* Water -- stun/confuse */
+ case GF_WAVE:
+ case GF_WATER:
+ {
+ if (fuzzy) msg_print("You are hit by something wet!");
+ if (!p_ptr->resist_sound)
+ {
+ set_stun(p_ptr->stun + randint(40));
+ }
+ if (!p_ptr->resist_conf)
+ {
+ set_confused(p_ptr->confused + randint(5) + 5);
+ }
+
+ if (randint(5) == 1)
+ {
+ inven_damage(set_cold_destroy, 3);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Chaos -- many effects */
+ case GF_CHAOS:
+ {
+ if (fuzzy) msg_print("You are hit by a wave of anarchy!");
+ if (p_ptr->resist_chaos)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + rand_int(20) + 10);
+ }
+ if (!p_ptr->resist_chaos)
+ {
+ (void)set_image(p_ptr->image + randint(10));
+ }
+ if (!p_ptr->resist_neth && !p_ptr->resist_chaos)
+ {
+ if (p_ptr->hold_life && (rand_int(100) < 75))
+ {
+ msg_print("You keep hold of your life force!");
+ }
+ else if (p_ptr->hold_life)
+ {
+ msg_print("You feel your life slipping away!");
+ lose_exp(500 + (p_ptr->exp / 1000) * MON_DRAIN_LIFE);
+ }
+ else
+ {
+ msg_print("You feel your life draining away!");
+ lose_exp(5000 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ }
+ }
+ if ((!p_ptr->resist_chaos) || (randint(9) == 1))
+ {
+ inven_damage(set_elec_destroy, 2);
+ inven_damage(set_fire_destroy, 2);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Shards -- mostly cutting */
+ case GF_SHARDS:
+ {
+ if (fuzzy) msg_print("You are hit by something sharp!");
+ if (p_ptr->resist_shard)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ (void)set_cut(p_ptr->cut + dam);
+ }
+
+ if ((!p_ptr->resist_shard) || (randint(13) == 1))
+ {
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Sound -- mostly stunning */
+ case GF_SOUND:
+ {
+ if (fuzzy) msg_print("You are hit by a loud noise!");
+ if (p_ptr->resist_sound)
+ {
+ dam *= 5;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ int k = (randint((dam > 90) ? 35 : (dam / 3 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+
+ if ((!p_ptr->resist_sound) || (randint(13) == 1))
+ {
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Pure confusion */
+ case GF_CONFUSION:
+ {
+ if (fuzzy) msg_print("You are hit by something puzzling!");
+ if (p_ptr->resist_conf)
+ {
+ dam *= 5;
+ dam /= (randint(6) + 6);
+ }
+ if (!p_ptr->resist_conf)
+ {
+ (void)set_confused(p_ptr->confused + randint(20) + 10);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Disenchantment -- see above */
+ case GF_DISENCHANT:
+ {
+ if (fuzzy) msg_print("You are hit by something static!");
+ if (p_ptr->resist_disen)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ (void)apply_disenchant(0);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Nexus -- see above */
+ case GF_NEXUS:
+ {
+ if (fuzzy) msg_print("You are hit by something strange!");
+ if (p_ptr->resist_nexus)
+ {
+ dam *= 6;
+ dam /= (randint(6) + 6);
+ }
+ else
+ {
+ apply_nexus(m_ptr);
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Force -- mostly stun */
+ case GF_FORCE:
+ {
+ if (fuzzy) msg_print("You are hit by kinetic force!");
+ if (!p_ptr->resist_sound)
+ {
+ (void)set_stun(p_ptr->stun + randint(20));
+ /*
+ * If fired by player, try pushing monster.
+ * First get vector from player to monster.
+ * x10 so we can use pseudo-fixed point maths.
+ *
+ * Really should use get_angle_to_grid (util.c)
+ */
+ if (who > 0)
+ {
+ a = 0;
+ b = 0;
+
+ /* Get vector from firer to target */
+ x1 = (p_ptr->px - m_ptr->fx) * 10;
+ y1 = (p_ptr->py - m_ptr->fy) * 10;
+
+ /* Make sure no zero divides */
+ if (x1 == 0) x1 = 1;
+ if (y1 == 0) y1 = 1;
+
+ /* Select direction player is being pushed */
+
+ /* Roughly horizontally */
+ if ((2*y1) / x1 == 0)
+ {
+ if (x1 > 0)
+ {
+ a = 1, b = 0;
+ }
+ else
+ {
+ a = -1, b = 0;
+ }
+ }
+
+ /* Roughly vertically */
+ else if ((2*x1) / y1 == 0)
+ {
+ if (y1 > 0)
+ {
+ a = 0, b = 1;
+ }
+ else
+ {
+ a = 0, b = -1;
+ }
+ }
+
+ /* Take diagonals */
+ else
+ {
+ if (y1 > 0)
+ {
+ b = 1;
+ }
+ else
+ {
+ b = -1;
+ }
+ if (x1 > 0)
+ {
+ a = 1;
+ }
+ else
+ {
+ a = -1;
+ }
+ }
+
+ /* Move monster 2 offsets back */
+ do_move = 2;
+
+ /* Old monster coords in x,y */
+ y1 = p_ptr->py;
+ x1 = p_ptr->px;
+ }
+ }
+ else
+ take_hit(dam, killer);
+ break;
+ }
+
+
+ /* Rocket -- stun, cut */
+ case GF_ROCKET:
+ {
+ if (fuzzy) msg_print("There is an explosion!");
+ if (!p_ptr->resist_sound)
+ {
+ (void)set_stun(p_ptr->stun + randint(20));
+ }
+ if (p_ptr->resist_shard)
+ {
+ dam /= 2;
+ }
+ else
+ {
+ (void)set_cut(p_ptr-> cut + ( dam / 2) );
+ }
+
+ if ((!p_ptr->resist_shard) || (randint(12) == 1))
+ {
+ inven_damage(set_cold_destroy, 3);
+ }
+
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Inertia -- slowness */
+ case GF_INERTIA:
+ {
+ if (fuzzy) msg_print("You are hit by something slow!");
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Lite -- blinding */
+ case GF_LITE:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ if (p_ptr->resist_lite)
+ {
+ dam *= 4;
+ dam /= (randint(6) + 6);
+ }
+ else if (!blind && !p_ptr->resist_blind)
+ {
+ (void)set_blind(p_ptr->blind + randint(5) + 2);
+ }
+ if (p_ptr->sensible_lite)
+ {
+ msg_print("The light scorches your flesh!");
+ dam *= 3;
+ }
+ take_hit(dam, killer);
+
+ if (p_ptr->tim_wraith)
+ {
+ p_ptr->tim_wraith = 0;
+ msg_print("The light forces you out of your incorporeal shadow form.");
+
+ p_ptr->redraw |= PR_MAP;
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+ }
+
+ break;
+ }
+
+ /* Dark -- blinding */
+ case GF_DARK:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ if (p_ptr->resist_dark)
+ {
+ dam *= 4;
+ dam /= (randint(6) + 6);
+ }
+ else if (!blind && !p_ptr->resist_blind)
+ {
+ (void)set_blind(p_ptr->blind + randint(5) + 2);
+ }
+ if (p_ptr->wraith_form) hp_player(dam);
+ else take_hit(dam, killer);
+ break;
+ }
+
+ /* Time -- bolt fewer effects XXX */
+ case GF_TIME:
+ {
+ if (fuzzy) msg_print("You are hit by a blast from the past!");
+
+ if (p_ptr->resist_continuum)
+ {
+ dam *= 4;
+ dam /= (randint(6) + 6);
+ msg_print("You feel as if time is passing you by.");
+ }
+ else
+ {
+ switch (randint(10))
+ {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ {
+ msg_print("You feel life has clocked back.");
+ lose_exp(100 + (p_ptr->exp / 100) * MON_DRAIN_LIFE);
+ break;
+ }
+
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ {
+ switch (randint(6))
+ {
+ case 1:
+ k = A_STR;
+ act = "strong";
+ break;
+ case 2:
+ k = A_INT;
+ act = "bright";
+ break;
+ case 3:
+ k = A_WIS;
+ act = "wise";
+ break;
+ case 4:
+ k = A_DEX;
+ act = "agile";
+ break;
+ case 5:
+ k = A_CON;
+ act = "hardy";
+ break;
+ case 6:
+ k = A_CHR;
+ act = "beautiful";
+ break;
+ }
+
+ msg_format("You're not as %s as you used to be...", act);
+
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+
+ case 10:
+ {
+ msg_print("You're not as powerful as you used to be...");
+
+ for (k = 0; k < 6; k++)
+ {
+ p_ptr->stat_cur[k] = (p_ptr->stat_cur[k] * 3) / 4;
+ if (p_ptr->stat_cur[k] < 3) p_ptr->stat_cur[k] = 3;
+ }
+ p_ptr->update |= (PU_BONUS);
+ break;
+ }
+ }
+ }
+ take_hit(dam, killer);
+ break;
+ }
+
+ /* Gravity -- stun plus slowness plus teleport */
+ case GF_GRAVITY:
+ {
+ if (dungeon_flags2 & DF2_NO_TELEPORT) break; /* No teleport on special levels */
+ if (fuzzy) msg_print("You are hit by something heavy!");
+ msg_print("Gravity warps around you.");
+ if (!unsafe)
+ {
+ teleport_player(5);
+ if (!p_ptr->ffall)
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ if (!(p_ptr->resist_sound || p_ptr->ffall))
+ {
+ int k = (randint((dam > 90) ? 35 : (dam / 3 + 5)));
+ (void)set_stun(p_ptr->stun + k);
+ }
+ if (p_ptr->ffall)
+ {
+ dam = (dam * 2) / 3;
+ }
+
+ if ((!p_ptr->ffall) || (randint(13) == 1))
+ {
+ inven_damage(set_cold_destroy, 2);
+ }
+
+ take_hit(dam, killer);
+ }
+ else
+ teleport_player(dam);
+ break;
+ }
+
+ /* Standard damage */
+ case GF_DISINTEGRATE:
+ {
+ if (fuzzy) msg_print("You are hit by pure energy!");
+ take_hit(dam, killer);
+ break;
+ }
+
+ case GF_OLD_HEAL:
+ {
+ if (fuzzy) msg_print("You are hit by something invigorating!");
+ (void)hp_player(dam);
+ dam = 0;
+ break;
+ }
+
+ case GF_OLD_SPEED:
+ {
+ if (fuzzy) msg_print("You are hit by something!");
+ (void)set_fast(p_ptr->fast + randint(5), 10);
+ dam = 0;
+ break;
+ }
+
+ case GF_OLD_SLOW:
+ {
+ if (fuzzy) msg_print("You are hit by something slow!");
+ (void)set_slow(p_ptr->slow + rand_int(4) + 4);
+ break;
+ }
+
+ case GF_OLD_SLEEP:
+ {
+ if (p_ptr->free_act) break;
+ if (fuzzy) msg_print("You fall asleep!");
+ set_paralyzed(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(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:
+ {
+ /* 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);
+
+
+ /* 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 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);
+
+ /* Actual grids in the "path" */
+ std::vector<std::tuple<int, int>> path_g;
+
+ /* 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))
+ {
+ /* Leave path empty */
+ }
+ else
+ {
+ path_g = project_path(MAX_RANGE, y1, x1, y2, x2, flg);
+ }
+
+
+ /* Hack -- Handle stuff */
+ handle_stuff();
+
+ /* Project along the path */
+ for (auto const &grid_yx: path_g)
+ {
+ int oy = y;
+ int ox = x;
+
+ int ny = std::get<0>(grid_yx);
+ int nx = std::get<1>(grid_yx);
+
+ /* 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();
+ sleep_for(milliseconds(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 */
+ sleep_for(milliseconds(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 (int 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)
+ {
+ sleep_for(milliseconds(msec));
+ }
+ }
+
+ /* Flush the erasing */
+ if (drawn)
+ {
+ /* Erase the explosion drawn above */
+ for (int 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 (int 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 (int 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 (int 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
+ {
+ auto ref_ptr = m_list[cave[y][x].m_idx].race();
+
+ 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 (int 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));
+
+ // Silence warning. We may want to introuce an actual implementation
+ // and I want to preserve the original "ident" values if we do so.
+ (void) ident;
+
+ /* 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.
+ */
+
+static void describe_attack_fully(int type, char* r)
+{
+ switch (type)
+ {
+ case GF_ARROW:
+ strcpy(r, "arrows");
+ break;
+ case GF_MISSILE:
+ strcpy(r, "magic missiles");
+ break;
+ case GF_MANA:
+ strcpy(r, "mana");
+ break;
+ case GF_LITE_WEAK:
+ strcpy(r, "light");
+ break;
+ case GF_DARK_WEAK:
+ strcpy(r, "dark");
+ break;
+ case GF_WATER:
+ strcpy(r, "water");
+ break;
+ case GF_PLASMA:
+ strcpy(r, "plasma");
+ break;
+ case GF_METEOR:
+ strcpy(r, "meteors");
+ break;
+ case GF_ICE:
+ strcpy(r, "ice");
+ break;
+ case GF_GRAVITY:
+ strcpy(r, "gravity");
+ break;
+ case GF_INERTIA:
+ strcpy(r, "inertia");
+ break;
+ case GF_FORCE:
+ strcpy(r, "force");
+ break;
+ case GF_TIME:
+ strcpy(r, "pure time");
+ break;
+ case GF_ACID:
+ strcpy(r, "acid");
+ break;
+ case GF_ELEC:
+ strcpy(r, "lightning");
+ break;
+ case GF_FIRE:
+ strcpy(r, "flames");
+ break;
+ case GF_COLD:
+ strcpy(r, "cold");
+ break;
+ case GF_POIS:
+ strcpy(r, "poison");
+ break;
+ case GF_LITE:
+ strcpy(r, "pure light");
+ break;
+ case GF_DARK:
+ strcpy(r, "pure dark");
+ break;
+ case GF_CONFUSION:
+ strcpy(r, "confusion");
+ break;
+ case GF_SOUND:
+ strcpy(r, "sound");
+ break;
+ case GF_SHARDS:
+ strcpy(r, "shards");
+ break;
+ case GF_NEXUS:
+ strcpy(r, "nexus");
+ break;
+ case GF_NETHER:
+ strcpy(r, "nether");
+ break;
+ case GF_CHAOS:
+ strcpy(r, "chaos");
+ break;
+ case GF_DISENCHANT:
+ strcpy(r, "disenchantment");
+ break;
+ case GF_KILL_WALL:
+ strcpy(r, "wall destruction");
+ break;
+ case GF_KILL_DOOR:
+ strcpy(r, "door destruction");
+ break;
+ case GF_KILL_TRAP:
+ strcpy(r, "trap destruction");
+ break;
+ case GF_STONE_WALL:
+ strcpy(r, "wall creation");
+ break;
+ case GF_MAKE_DOOR:
+ strcpy(r, "door creation");
+ break;
+ case GF_MAKE_TRAP:
+ strcpy(r, "trap creation");
+ break;
+ case GF_DESTRUCTION:
+ strcpy(r, "destruction");
+ break;
+
+ default:
+ strcpy(r, "something unknown");
+ break;
+ }
+}
+
+/*
+ * Give a randomly-generated spell a name.
+ * Note that it only describes the first effect!
+ */
+
+static void name_spell(random_spell* s_ptr)
+{
+ char buff[30];
+ cptr buff2 = "???";
+
+ if (s_ptr->proj_flags & PROJECT_STOP && s_ptr->radius == 0)
+ {
+ buff2 = "Bolt";
+ }
+ else if (s_ptr->proj_flags & PROJECT_BEAM)
+ {
+ buff2 = "Beam";
+ }
+ else if (s_ptr->proj_flags & PROJECT_STOP && s_ptr->radius > 0)
+ {
+ buff2 = "Ball";
+ }
+ else if (s_ptr->proj_flags & PROJECT_BLAST)
+ {
+ buff2 = "Blast";
+ }
+ else if (s_ptr->proj_flags & PROJECT_METEOR_SHOWER)
+ {
+ buff2 = "Area";
+ }
+ else if (s_ptr->proj_flags & PROJECT_VIEWABLE)
+ {
+ buff2 = "View";
+ }
+
+ describe_attack_fully(s_ptr->GF, buff);
+ strnfmt(s_ptr->name, 30, "%s - %s", buff2, buff);
+}
+
+void generate_spell(int plev)
+{
+ random_spell* rspell;
+ int dice, sides, chance, mana, power;
+ bool_ destruc_gen = FALSE;
+ bool_ simple_gen = TRUE;
+ bool_ ball_desc = FALSE;
+
+ if (spell_num == MAX_SPELLS) return;
+
+ rspell = &random_spells[spell_num];
+
+ power = rand_int(5);
+
+ dice = plev / 2;
+ sides = plev;
+ mana = plev;
+
+ /* Make the spell more or less powerful. */
+ dice += power;
+ sides += power;
+ mana += (plev * power) / 15;
+
+ /* Stay within reasonable bounds. */
+ if (dice < 1) dice = 1;
+
+ if (sides < 5) sides = 5;
+
+ if (mana < 1) mana = 1;
+
+ rspell->level = plev;
+ rspell->mana = mana;
+ rspell->untried = TRUE;
+
+ /* Spells are always maximally destructive. */
+ rspell->proj_flags = PROJECT_KILL | PROJECT_ITEM | PROJECT_GRID;
+
+ chance = randint(100);
+
+ /* Hack -- Always start with Magic Missile or derivative at lev. 1 */
+ if (plev == 1 || chance < 25)
+ {
+ rspell->proj_flags |= PROJECT_STOP;
+ /* Swap dice and sides for better damage */
+ rspell->dam_dice = sides;
+ rspell->dam_sides = dice;
+ rspell->radius = 0;
+ }
+ else if (chance < 50)
+ {
+ rspell->proj_flags |= PROJECT_BEAM;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+ rspell->radius = 0;
+ }
+ else if (chance < 76)
+ {
+ rspell->proj_flags |= PROJECT_STOP;
+ rspell->radius = dice / 3;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+ ball_desc = TRUE;
+ }
+ else if (chance < 83)
+ {
+ rspell->proj_flags |= PROJECT_BLAST;
+ rspell->radius = sides / 3;
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides;
+
+ destruc_gen = TRUE;
+ simple_gen = FALSE;
+ }
+ else if (chance < 90)
+ {
+ rspell->proj_flags |= PROJECT_METEOR_SHOWER;
+ /* Area effect spells do way less damage "per shot" */
+ rspell->dam_dice = dice / 5;
+ rspell->dam_sides = sides / 5;
+ rspell->radius = sides / 3;
+ if (rspell->radius < 4) rspell->radius = 4;
+
+ destruc_gen = TRUE;
+ }
+ else
+ {
+ rspell->proj_flags |= PROJECT_VIEWABLE;
+ /* View spells do less damage */
+ rspell->dam_dice = dice;
+ rspell->dam_sides = sides / 2;
+ }
+
+ /* Both a destructive and a simple spell requested --
+ * pick one or the other. */
+ if (destruc_gen && simple_gen)
+ {
+ if (magik(25))
+ {
+ simple_gen = FALSE;
+ }
+ else
+ {
+ destruc_gen = FALSE;
+ }
+ }
+
+ /* Pick a simple spell */
+ if (simple_gen)
+ {
+ rspell->GF = attack_types[rand_int(25)];
+ }
+ /* Pick a destructive spell */
+ else
+ {
+ rspell->GF = destructive_attack_types[rand_int(10)];
+ }
+
+ /* Give the spell a name. */
+ name_spell(rspell);
+ if (ball_desc)
+ {
+ /* 30 character limit on the string! */
+ sprintf(rspell->desc, "Dam: %d, Rad: %d, Pow: %d",
+ sides, dice, power);
+ }
+ else
+ {
+ sprintf(rspell->desc, "Damage: %dd%d, Power: %d",
+ dice, sides, power);
+ }
+
+ spell_num++;
+}
+
+/*
+ * Polymorph a monster at given location.
+ */
+s16b do_poly_monster(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+
+ monster_type *m_ptr;
+
+ s16b hack_m_idx;
+ s16b old_m_idx;
+ s16b new_m_idx = 0;
+
+ s16b new_r_idx;
+
+ /* Get a "old" monster */
+ old_m_idx = c_ptr->m_idx;
+
+ /* Giga-Hack -- Remember monster */
+ hack_m_idx = old_m_idx;
+
+ /* Get a monster */
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ /* Pick a "new" monster race */
+ new_r_idx = poly_r_idx(m_ptr->r_idx);
+
+ /* No polymorph happend */
+ if (new_r_idx == m_ptr->r_idx) return 0;
+
+ /* Giga-Hack -- Removes the moster XXX XXX XXX XXX */
+ c_ptr->m_idx = 0;
+
+ /*
+ * Handle polymorph --
+ * Create a new monster (no groups)
+ */
+ if (place_monster_aux(y, x, new_r_idx, FALSE, FALSE, m_ptr->status))
+ {
+ /* Get a "new" monster */
+ new_m_idx = c_ptr->m_idx;
+
+ /* Giga-Hack -- Remember "new" monster */
+ hack_m_idx = new_m_idx;
+
+ /* "Kill" the "old" monster */
+ delete_monster_idx(old_m_idx);
+
+ p_ptr->redraw |= (PR_MAP);
+ }
+
+ /* Giga-Hack -- restore saved monster XXX XXX XXX */
+ c_ptr->m_idx = hack_m_idx;
+
+ return new_m_idx;
+}
diff --git a/src/spells1.hpp b/src/spells1.hpp
new file mode 100644
index 00000000..5512063f
--- /dev/null
+++ b/src/spells1.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "h-basic.h"
+
+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_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_ 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 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 s16b do_poly_monster(int y, int x);
diff --git a/src/spells2.cc b/src/spells2.cc
new file mode 100644
index 00000000..08a643c9
--- /dev/null
+++ b/src/spells2.cc
@@ -0,0 +1,6837 @@
+/*
+ * 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 "spells2.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd1.hpp"
+#include "cmd7.hpp"
+#include "dungeon_info_type.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "hook_identify_in.hpp"
+#include "hooks.hpp"
+#include "melee2.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "notes.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "spells1.hpp"
+#include "spells3.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <cassert>
+#include <chrono>
+#include <sstream>
+#include <thread>
+#include <vector>
+
+using boost::algorithm::iequals;
+using std::this_thread::sleep_for;
+using std::chrono::milliseconds;
+
+#define WEIRD_LUCK 12
+#define BIAS_LUCK 20
+/*
+ * Bias luck needs to be higher than weird luck,
+ * since it is usually tested several times...
+ */
+
+static 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_FRAME);
+
+ /* 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);
+}
+
+
+/*
+ * Increases a stat by one randomized level -RAK-
+ *
+ * Note that this function (used by stat potions) now restores
+ * the stat BEFORE increasing it.
+ */
+static 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);
+}
+
+
+/*
+ * 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);
+}
+
+
+/*
+ * Process all identify hooks
+ */
+void identify_hooks(int i, object_type *o_ptr, identify_mode mode)
+{
+ /* Process the appropriate hooks */
+ hook_identify_in in = { o_ptr, mode };
+ process_hooks_new(HOOK_IDENTIFY, &in, NULL);
+}
+
+
+/*
+ * Identify everything being carried.
+ * Done by a potion of "self knowledge".
+ */
+bool_ 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 */
+ identify_hooks(i, o_ptr, IDENT_NORMAL);
+ }
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ return TRUE;
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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 */
+ identify_hooks(i, o_ptr, IDENT_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
+};
+
+static 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;
+ char o_name[80];
+ char out_val[160];
+
+ /* Hack -- force destruction */
+ if (command_arg > 0) force = TRUE;
+
+ /* Get an item */
+ if (!get_item(&item,
+ "Turn which item to gold? ",
+ "You have nothing to turn to gold.",
+ (USE_INVEN | USE_FLOOR),
+ object_filter::True()))
+ {
+ return (FALSE);
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* See how many items */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return FALSE;
+ }
+
+
+ /* Describe the object */
+ old_number = o_ptr->number;
+ o_ptr->number = amt;
+ object_desc(o_name, o_ptr, TRUE, 3);
+ o_ptr->number = old_number;
+
+ /* Verify unless quantity given */
+ if (!force)
+ {
+ /* 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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ }
+
+ /* Eliminate the item */
+ inc_stack_size(item, -amt);
+
+ return TRUE;
+}
+
+
+
+
+/*
+ * self-knowledge... idea from nethack. Useful for determining powers and
+ * resistances of items. It saves the screen, clears it, then starts listing
+ * attributes, a screenful at a time. (There are a LOT of attributes to
+ * list. It will probably take 2 or 3 screens for a powerful character whose
+ * using several artifacts...) -CFT
+ *
+ * It is now a lot more efficient. -BEN-
+ *
+ * See also "identify_fully()".
+ *
+ * XXX XXX XXX Use the "show_file()" method, perhaps.
+ */
+void self_knowledge(FILE *fff)
+{
+ int i = 0, j, k;
+
+ u32b f1 = 0L, f2 = 0L, f3 = 0L, f4 = 0L, f5 = 0L, esp = 0L;
+
+ int iter; /* Iterator for a loop */
+
+ object_type *o_ptr;
+
+ char Dummy[80];
+
+ cptr info[200];
+
+ strcpy (Dummy, "");
+
+ /* Acquire item flags from equipment */
+ for (k = INVEN_WIELD; k < INVEN_TOTAL; k++)
+ {
+ u32b t1, t2, t3, t4, t5, esp;
+
+ o_ptr = &p_ptr->inventory[k];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &t1, &t2, &t3, &t4, &t5, &esp);
+
+ /* Extract flags */
+ f1 |= t1;
+ f2 |= t2;
+ f3 |= t3;
+ }
+
+ if (death)
+ {
+ static char buf[250];
+
+ sprintf(buf, "You are dead, killed by %s %s.",
+ died_from, describe_player_location());
+ info[i++] = buf;
+ }
+
+ /* Racial powers... */
+ if (p_ptr->body_monster != 0)
+ {
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ if (r_ptr->flags1 & RF1_CHAR_CLEAR ||
+ r_ptr->flags1 & RF1_ATTR_CLEAR)
+ info[i++] = "You are transparent.";
+ if ((r_ptr->flags1 & RF1_CHAR_MULTI) ||
+ (r_ptr->flags2 & RF2_SHAPECHANGER))
+ info[i++] = "Your form constantly changes.";
+ if (r_ptr->flags1 & RF1_ATTR_MULTI)
+ info[i++] = "Your color constantly changes.";
+ if (r_ptr->flags1 & RF1_NEVER_BLOW)
+ info[i++] = "You do not have a physical weapon.";
+ if (r_ptr->flags1 & RF1_NEVER_MOVE)
+ info[i++] = "You cannot move.";
+ if ((r_ptr->flags1 & RF1_RAND_25) &&
+ (r_ptr->flags1 & RF1_RAND_50))
+ info[i++] = "You move extremely erratically.";
+ else if (r_ptr->flags1 & RF1_RAND_50)
+ info[i++] = "You move somewhat erratically.";
+ else if (r_ptr->flags1 & RF1_RAND_25)
+ info[i++] = "You move a bit erratically.";
+ if (r_ptr->flags2 & RF2_STUPID)
+ info[i++] = "You are very stupid (INT -4).";
+ if (r_ptr->flags2 & RF2_SMART)
+ info[i++] = "You are very smart (INT +4).";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_CAN_SPEAK)
+ info[i++] = "You can speak.";
+ else
+ info[i++] = "You cannot speak.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_COLD_BLOOD)
+ info[i++] = "You are cold blooded.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_EMPTY_MIND)
+ info[i++] = "You have an empty mind.";
+ if (r_ptr->flags2 & RF2_WEIRD_MIND)
+ info[i++] = "You have a weird mind.";
+ if (r_ptr->flags4 & RF4_MULTIPLY)
+ info[i++] = "You can multiply.";
+ if (r_ptr->flags2 & RF2_POWERFUL)
+ info[i++] = "You have strong breath.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_ELDRITCH_HORROR)
+ info[i++] = "You are an eldritch horror.";
+ if (r_ptr->flags2 & RF2_OPEN_DOOR)
+ info[i++] = "You can open doors.";
+ else
+ info[i++] = "You cannot open doors.";
+ if (r_ptr->flags2 & RF2_BASH_DOOR)
+ info[i++] = "You can bash doors.";
+ else
+ info[i++] = "You cannot bash doors.";
+ if (r_ptr->flags2 & RF2_PASS_WALL)
+ info[i++] = "You can pass walls.";
+ if (r_ptr->flags2 & RF2_KILL_WALL)
+ info[i++] = "You destroy walls.";
+ /* Not implemented */
+ if (r_ptr->flags2 & RF2_MOVE_BODY)
+ info[i++] = "You can move monsters.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_ORC)
+ info[i++] = "You have orc blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_TROLL)
+ info[i++] = "You have troll blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_GIANT)
+ info[i++] = "You have giant blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_DRAGON)
+ info[i++] = "You have dragon blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_DEMON)
+ info[i++] = "You have demon blood in your veins.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_UNDEAD)
+ info[i++] = "You are an undead.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_ANIMAL)
+ info[i++] = "You are an animal.";
+ /* Not implemented */
+ else if (r_ptr->flags3 & RF3_THUNDERLORD)
+ info[i++] = "You have thunderlord blood in your veins.";
+ if (r_ptr->flags3 & RF3_EVIL)
+ info[i++] = "You are inherently evil.";
+ else if (r_ptr->flags3 & RF3_GOOD)
+ info[i++] = "You are inherently good.";
+ if (r_ptr->flags3 & RF3_AURA_COLD)
+ info[i++] = "You are surrounded by a chilly aura.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_NONLIVING)
+ info[i++] = "You are not living.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_HURT_LITE)
+ info[i++] = "Your eyes are vulnerable to bright light.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_HURT_ROCK)
+ info[i++] = "You can be hurt by rock remover.";
+ if (r_ptr->flags3 & RF3_SUSCEP_FIRE)
+ info[i++] = "You are vulnerable to fire.";
+ if (r_ptr->flags3 & RF3_SUSCEP_COLD)
+ info[i++] = "You are vulnerable to cold.";
+ if (r_ptr->flags3 & RF3_RES_TELE)
+ info[i++] = "You are resistant to teleportation.";
+ if (r_ptr->flags3 & RF3_RES_NETH)
+ info[i++] = "You are resistant to nether.";
+ if (r_ptr->flags3 & RF3_RES_WATE)
+ info[i++] = "You are resistant to water.";
+ if (r_ptr->flags3 & RF3_RES_PLAS)
+ info[i++] = "You are resistant to plasma.";
+ if (r_ptr->flags3 & RF3_RES_WATE)
+ info[i++] = "You are resistant to nexus.";
+ if (r_ptr->flags3 & RF3_RES_DISE)
+ info[i++] = "You are resistant to disease.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_NO_SLEEP)
+ info[i++] = "You cannot be slept.";
+ /* Not implemented */
+ if (r_ptr->flags3 & RF3_UNIQUE_4)
+ info[i++] = "You are a Nazgul.";
+ if (r_ptr->flags3 & RF3_NO_FEAR)
+ info[i++] = "You are immune to fear.";
+ if (r_ptr->flags3 & RF3_NO_STUN)
+ info[i++] = "You are immune to stun.";
+ if (r_ptr->flags3 & RF3_NO_CONF)
+ info[i++] = "You are immune to confusion.";
+ if (r_ptr->flags3 & RF3_NO_SLEEP)
+ info[i++] = "You are immune to sleep.";
+
+ if (r_ptr->flags4 & RF4_SHRIEK)
+ info[i++] = "You can aggravate monsters.";
+ if (r_ptr->flags4 & RF4_ROCKET)
+ info[i++] = "You can fire a rocket.";
+ if (r_ptr->flags4 & RF4_ARROW_1)
+ info[i++] = "You can fire a light arrow.";
+ if (r_ptr->flags4 & RF4_ARROW_2)
+ info[i++] = "You can fire a heavy arrow.";
+ if (r_ptr->flags4 & RF4_ARROW_3)
+ info[i++] = "You can fire a light missile.";
+ if (r_ptr->flags4 & RF4_ARROW_4)
+ info[i++] = "You can fire a heavy missile.";
+ if (r_ptr->flags4 & RF4_BR_ACID)
+ info[i++] = "You can breathe acid.";
+ if (r_ptr->flags4 & RF4_BR_ELEC)
+ info[i++] = "You can breathe electricity.";
+ if (r_ptr->flags4 & RF4_BR_FIRE)
+ info[i++] = "You can breathe fire.";
+ if (r_ptr->flags4 & RF4_BR_COLD)
+ info[i++] = "You can breathe cold.";
+ if (r_ptr->flags4 & RF4_BR_POIS)
+ info[i++] = "You can breathe poison.";
+ if (r_ptr->flags4 & RF4_BR_NETH)
+ info[i++] = "You can breathe nether.";
+ if (r_ptr->flags4 & RF4_BR_LITE)
+ info[i++] = "You can breathe light.";
+ if (r_ptr->flags4 & RF4_BR_DARK)
+ info[i++] = "You can breathe darkness.";
+ if (r_ptr->flags4 & RF4_BR_CONF)
+ info[i++] = "You can breathe confusion.";
+ if (r_ptr->flags4 & RF4_BR_SOUN)
+ info[i++] = "You can breathe sound.";
+ if (r_ptr->flags4 & RF4_BR_CHAO)
+ info[i++] = "You can breathe chaos.";
+ if (r_ptr->flags4 & RF4_BR_DISE)
+ info[i++] = "You can breathe disenchantment.";
+ if (r_ptr->flags4 & RF4_BR_NEXU)
+ info[i++] = "You can breathe nexus.";
+ if (r_ptr->flags4 & RF4_BR_TIME)
+ info[i++] = "You can breathe time.";
+ if (r_ptr->flags4 & RF4_BR_INER)
+ info[i++] = "You can breathe inertia.";
+ if (r_ptr->flags4 & RF4_BR_GRAV)
+ info[i++] = "You can breathe gravity.";
+ if (r_ptr->flags4 & RF4_BR_SHAR)
+ info[i++] = "You can breathe shards.";
+ if (r_ptr->flags4 & RF4_BR_PLAS)
+ info[i++] = "You can breathe plasma.";
+ if (r_ptr->flags4 & RF4_BR_WALL)
+ info[i++] = "You can breathe force.";
+ if (r_ptr->flags4 & RF4_BR_MANA)
+ info[i++] = "You can breathe mana.";
+ if (r_ptr->flags4 & RF4_BR_NUKE)
+ info[i++] = "You can breathe nuke.";
+ if (r_ptr->flags4 & RF4_BR_DISI)
+ info[i++] = "You can breathe disintegration.";
+ if (r_ptr->flags5 & RF5_BA_ACID)
+ info[i++] = "You can cast a ball of acid.";
+ if (r_ptr->flags5 & RF5_BA_ELEC)
+ info[i++] = "You can cast a ball of electricity.";
+ if (r_ptr->flags5 & RF5_BA_FIRE)
+ info[i++] = "You can cast a ball of fire.";
+ if (r_ptr->flags5 & RF5_BA_COLD)
+ info[i++] = "You can cast a ball of cold.";
+ if (r_ptr->flags5 & RF5_BA_POIS)
+ info[i++] = "You can cast a ball of poison.";
+ if (r_ptr->flags5 & RF5_BA_NETH)
+ info[i++] = "You can cast a ball of nether.";
+ if (r_ptr->flags5 & RF5_BA_WATE)
+ info[i++] = "You can cast a ball of water.";
+ /* Not implemented */
+ if (r_ptr->flags5 & RF5_DRAIN_MANA)
+ info[i++] = "You can drain mana.";
+ if (r_ptr->flags5 & RF5_MIND_BLAST)
+ info[i++] = "You can cause mind blasting.";
+ if (r_ptr->flags5 & RF5_BRAIN_SMASH)
+ info[i++] = "You can cause brain smashing.";
+ if (r_ptr->flags5 & RF5_CAUSE_1)
+ info[i++] = "You can cause light wounds.";
+ if (r_ptr->flags5 & RF5_CAUSE_2)
+ info[i++] = "You can cause serious wounds.";
+ if (r_ptr->flags5 & RF5_CAUSE_3)
+ info[i++] = "You can cause critical wounds.";
+ if (r_ptr->flags5 & RF5_CAUSE_4)
+ info[i++] = "You can cause mortal wounds.";
+ if (r_ptr->flags5 & RF5_BO_ACID)
+ info[i++] = "You can cast a bolt of acid.";
+ if (r_ptr->flags5 & RF5_BO_ELEC)
+ info[i++] = "You can cast a bolt of electricity.";
+ if (r_ptr->flags5 & RF5_BO_FIRE)
+ info[i++] = "You can cast a bolt of fire.";
+ if (r_ptr->flags5 & RF5_BO_COLD)
+ info[i++] = "You can cast a bolt of cold.";
+ if (r_ptr->flags5 & RF5_BO_POIS)
+ info[i++] = "You can cast a bolt of poison.";
+ if (r_ptr->flags5 & RF5_BO_NETH)
+ info[i++] = "You can cast a bolt of nether.";
+ if (r_ptr->flags5 & RF5_BO_WATE)
+ info[i++] = "You can cast a bolt of water.";
+ if (r_ptr->flags5 & RF5_BO_MANA)
+ info[i++] = "You can cast a bolt of mana.";
+ if (r_ptr->flags5 & RF5_BO_PLAS)
+ info[i++] = "You can cast a bolt of plasma.";
+ if (r_ptr->flags5 & RF5_BO_ICEE)
+ info[i++] = "You can cast a bolt of ice.";
+ if (r_ptr->flags5 & RF5_MISSILE)
+ info[i++] = "You can cast magic missile.";
+ if (r_ptr->flags5 & RF5_SCARE)
+ info[i++] = "You can terrify.";
+ if (r_ptr->flags5 & RF5_BLIND)
+ info[i++] = "You can blind.";
+ if (r_ptr->flags5 & RF5_CONF)
+ info[i++] = "You can use confusion.";
+ if (r_ptr->flags5 & RF5_SLOW)
+ info[i++] = "You can cast slow.";
+ if (r_ptr->flags5 & RF5_HOLD)
+ info[i++] = "You can touch to paralyze.";
+ if (r_ptr->flags6 & RF6_HASTE)
+ info[i++] = "You can haste yourself.";
+ if (r_ptr->flags6 & RF6_HAND_DOOM)
+ info[i++] = "You can invoke Hand of Doom.";
+ if (r_ptr->flags6 & RF6_HEAL)
+ info[i++] = "You can heal yourself.";
+ if (r_ptr->flags6 & RF6_BLINK)
+ info[i++] = "You can blink.";
+ if (r_ptr->flags6 & RF6_TPORT)
+ info[i++] = "You can teleport.";
+ if (r_ptr->flags6 & RF6_TELE_TO)
+ info[i++] = "You can go between places.";
+ if (r_ptr->flags6 & RF6_TELE_AWAY)
+ info[i++] = "You can teleport away.";
+ if (r_ptr->flags6 & RF6_TELE_LEVEL)
+ info[i++] = "You can teleport level.";
+ if (r_ptr->flags6 & RF6_DARKNESS)
+ info[i++] = "You can create darkness.";
+ if (r_ptr->flags6 & RF6_TRAPS)
+ info[i++] = "You can create traps.";
+ /* Not implemented */
+ if (r_ptr->flags6 & RF6_FORGET)
+ info[i++] = "You can fade memories.";
+ if (r_ptr->flags6 & RF6_RAISE_DEAD)
+ info[i++] = "You can Raise the Dead.";
+ if (r_ptr->flags6 & RF6_S_BUG)
+ info[i++] = "You can magically summon a Software Bugs.";
+ if (r_ptr->flags6 & RF6_S_RNG)
+ info[i++] = "You can magically summon the RNG.";
+ if (r_ptr->flags6 & RF6_S_THUNDERLORD)
+ info[i++] = "You can magically summon some Thunderlords.";
+ if (r_ptr->flags6 & RF6_S_KIN)
+ info[i++] = "You can magically summon some Kins.";
+ if (r_ptr->flags6 & RF6_S_HI_DEMON)
+ info[i++] = "You can magically summon greater demons.";
+ if (r_ptr->flags6 & RF6_S_MONSTER)
+ info[i++] = "You can magically summon a monster.";
+ if (r_ptr->flags6 & RF6_S_MONSTERS)
+ info[i++] = "You can magically summon monsters.";
+ if (r_ptr->flags6 & RF6_S_ANT)
+ info[i++] = "You can magically summon ants.";
+ if (r_ptr->flags6 & RF6_S_SPIDER)
+ info[i++] = "You can magically summon spiders.";
+ if (r_ptr->flags6 & RF6_S_HOUND)
+ info[i++] = "You can magically summon hounds.";
+ if (r_ptr->flags6 & RF6_S_HYDRA)
+ info[i++] = "You can magically summon hydras.";
+ if (r_ptr->flags6 & RF6_S_ANGEL)
+ info[i++] = "You can magically summon an angel.";
+ if (r_ptr->flags6 & RF6_S_DEMON)
+ info[i++] = "You can magically summon a demon.";
+ if (r_ptr->flags6 & RF6_S_UNDEAD)
+ info[i++] = "You can magically summon an undead.";
+ if (r_ptr->flags6 & RF6_S_DRAGON)
+ info[i++] = "You can magically summon a dragon.";
+ if (r_ptr->flags6 & RF6_S_HI_UNDEAD)
+ info[i++] = "You can magically summon greater undead.";
+ if (r_ptr->flags6 & RF6_S_HI_DRAGON)
+ info[i++] = "You can magically summon greater dragons.";
+ if (r_ptr->flags6 & RF6_S_WRAITH)
+ info[i++] = "You can magically summon a wraith.";
+ /* Not implemented */
+ if (r_ptr->flags6 & RF6_S_UNIQUE)
+ info[i++] = "You can magically summon an unique monster.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ info[i++] = "You are aquatic.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_CAN_SWIM)
+ info[i++] = "You can swim.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ info[i++] = "You can fly.";
+ if ((r_ptr->flags7 & RF7_MORTAL) == 0)
+ info[i++] = "You are immortal.";
+ /* Not implemented */
+ if (r_ptr->flags7 & RF7_NAZGUL)
+ info[i++] = "You are a Nazgul.";
+
+ if (r_ptr->flags7 & RF7_SPIDER)
+ info[i++] = "You are a spider.";
+
+ if (r_ptr->flags8 & RF8_WILD_TOWN)
+ info[i++] = "You appear in towns.";
+ if (r_ptr->flags8 & RF8_WILD_SHORE)
+ info[i++] = "You appear on the shore.";
+ if (r_ptr->flags8 & RF8_WILD_OCEAN)
+ info[i++] = "You appear in the ocean.";
+ if (r_ptr->flags8 & RF8_WILD_WASTE)
+ info[i++] = "You appear in the waste.";
+ if (r_ptr->flags8 & RF8_WILD_WOOD)
+ info[i++] = "You appear in woods.";
+ if (r_ptr->flags8 & RF8_WILD_VOLCANO)
+ info[i++] = "You appear in volcanos.";
+ if (r_ptr->flags8 & RF8_WILD_MOUNTAIN)
+ info[i++] = "You appear in the mountains.";
+ if (r_ptr->flags8 & RF8_WILD_GRASS)
+ info[i++] = "You appear in grassy areas.";
+
+ if (r_ptr->flags9 & RF9_SUSCEP_ACID)
+ info[i++] = "You are vulnerable to acid.";
+ if (r_ptr->flags9 & RF9_SUSCEP_ELEC)
+ info[i++] = "You are vulnerable to electricity.";
+ if (r_ptr->flags9 & RF9_SUSCEP_POIS)
+ info[i++] = "You are vulnerable to poison.";
+ if (r_ptr->flags9 & RF9_KILL_TREES)
+ info[i++] = "You can eat trees.";
+ if (r_ptr->flags9 & RF9_WYRM_PROTECT)
+ info[i++] = "You are protected by great wyrms of power.";
+ }
+
+ /* List powers */
+ for (iter = 0; iter < POWER_MAX; iter++)
+ {
+ if (p_ptr->powers[iter])
+ {
+ info[i++] = powers_type[iter].desc_text;
+ }
+ }
+
+ if (p_ptr->allow_one_death)
+ {
+ info[i++] = "The Blood of Life flows through your veins.";
+ }
+ if (p_ptr->blind)
+ {
+ info[i++] = "You cannot see.";
+ }
+ if (p_ptr->confused)
+ {
+ info[i++] = "You are confused.";
+ }
+ if (p_ptr->afraid)
+ {
+ info[i++] = "You are terrified.";
+ }
+ if (p_ptr->cut)
+ {
+ info[i++] = "You are bleeding.";
+ }
+ if (p_ptr->stun)
+ {
+ info[i++] = "You are stunned.";
+ }
+ if (p_ptr->poisoned)
+ {
+ info[i++] = "You are poisoned.";
+ }
+ if (p_ptr->image)
+ {
+ info[i++] = "You are hallucinating.";
+ }
+ if (p_ptr->aggravate)
+ {
+ info[i++] = "You aggravate monsters.";
+ }
+ if (p_ptr->teleport)
+ {
+ info[i++] = "Your position is very uncertain.";
+ }
+ if (p_ptr->blessed)
+ {
+ info[i++] = "You feel righteous.";
+ }
+ if (p_ptr->hero)
+ {
+ info[i++] = "You feel heroic.";
+ }
+ if (p_ptr->shero)
+ {
+ info[i++] = "You are in a battle rage.";
+ }
+ if (p_ptr->protevil)
+ {
+ info[i++] = "You are protected from evil.";
+ }
+ if (p_ptr->protgood)
+ {
+ info[i++] = "You are protected from good.";
+ }
+ if (p_ptr->shield)
+ {
+ info[i++] = "You are protected by a mystic shield.";
+ }
+ if (p_ptr->invuln)
+ {
+ info[i++] = "You are temporarily invulnerable.";
+ }
+ if (p_ptr->confusing)
+ {
+ info[i++] = "Your hands are glowing dull red.";
+ }
+ if (p_ptr->searching)
+ {
+ info[i++] = "You are looking around very carefully.";
+ }
+ if (p_ptr->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_FRAME);
+
+ /* 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 monsters on current panel using a
+ * predicate function P and an update function U.
+ * The "update function" is called exactly once if
+ * the predicate succeeds. The
+ */
+template<typename P, typename U> static bool detect_monsters_fn(int radius, P p, U u) {
+ bool flag = false;
+ /* Scan monsters */
+ for (int i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Skip dead monsters */
+ if (!m_ptr->r_idx)
+ {
+ continue;
+ }
+
+ /* Location */
+ int const y = m_ptr->fy;
+ int const x = m_ptr->fx;
+
+ /* Only detect nearby monsters */
+ if (m_ptr->cdis > radius)
+ {
+ continue;
+ }
+ /* Detect monsters which fulfill the predicate */
+ auto r_ptr = m_ptr->race();
+ if (p(r_ptr.get()))
+ {
+ /* Update */
+ u(r_ptr.get());
+
+ /* We're assuming the update function does
+ * *something*, so we'll need to update
+ * the recall window if we're currently looking
+ * at it.
+ */
+ if (monster_race_idx == m_ptr->r_idx)
+ {
+ 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;
+ }
+ }
+ /* Result */
+ return flag;
+}
+
+/*
+ * Detect all (string) monsters on current panel
+ */
+static bool_ detect_monsters_string(cptr chars, int rad)
+{
+ auto predicate = [chars](monster_race *r_ptr) -> bool {
+ return strchr(chars, r_ptr->d_char);
+ };
+ auto update = [](monster_race *) -> void {
+ };
+
+ /* Describe */
+ if (detect_monsters_fn(rad, predicate, update))
+ {
+ /* Describe result */
+ msg_print("You sense the presence of monsters!");
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+/**
+ * Detect objects on the current panel.
+ */
+template <typename P> bool detect_objects_fn(int radius, const char *object_message, const char *monsters, P p)
+{
+ bool detect = false;
+
+ /* Scan objects */
+ for (int i = 1; i < o_max; i++)
+ {
+ object_type *o_ptr = &o_list[i];
+
+ /* Skip dead objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* Location */
+ int y, x;
+
+ /* Skip held objects */
+ if (o_ptr->held_m_idx)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[o_ptr->held_m_idx];
+ auto const r_ptr = m_ptr->race();
+
+ if (!(r_ptr->flags9 & RF9_MIMIC))
+ {
+ continue; /* Skip mimics completely */
+ }
+ 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) > radius)
+ {
+ continue;
+ }
+
+ /* Detect objects that satisfy predicate */
+ if (p(o_ptr))
+ {
+ /* Hack -- memorize it */
+ o_ptr->marked = TRUE;
+
+ /* Redraw */
+ if (panel_contains(y, x)) lite_spot(y, x);
+
+ /* Detect */
+ detect = TRUE;
+ }
+ }
+
+ /* Describe */
+ if (detect)
+ {
+ msg_print(object_message);
+ }
+
+ if (detect_monsters_string(monsters, radius))
+ {
+ detect = true;
+ }
+
+ /* Result */
+ return detect;
+}
+
+
+/*
+ * Detect all "gold" objects on the current panel
+ */
+bool detect_objects_gold(int rad)
+{
+ auto predicate = [](object_type const *o_ptr) -> bool {
+ return o_ptr->tval == TV_GOLD;
+ };
+
+ return detect_objects_fn(
+ rad,
+ "You sense the presence of treasure!",
+ "$",
+ predicate);
+}
+
+
+/*
+ * Detect all "normal" objects on the current panel
+ */
+bool detect_objects_normal(int rad)
+{
+ auto predicate = [](object_type const *o_ptr) -> bool {
+ return o_ptr->tval != TV_GOLD;
+ };
+ const char *object_message = "You sense the presence of objects!";
+ const char *monsters = "!=?|";
+
+ return detect_objects_fn(
+ rad,
+ object_message,
+ monsters,
+ predicate);
+}
+
+
+/*
+ * Detect all "normal" monsters on the current panel
+ */
+bool_ detect_monsters_normal(int rad)
+{
+ auto predicate = [](monster_race *r_ptr) -> bool {
+ return (!(r_ptr->flags2 & (RF2_INVISIBLE))) ||
+ p_ptr->see_inv || p_ptr->tim_invis;
+ };
+ auto update = [](monster_race *) -> void {
+ };
+
+ if (detect_monsters_fn(rad, predicate, update))
+ {
+ /* Describe result */
+ msg_print("You sense the presence of monsters!");
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+/*
+ * Detect all "invisible" monsters on current panel
+ */
+bool_ detect_monsters_invis(int rad)
+{
+ auto predicate = [](monster_race *r_ptr) -> bool {
+ return (r_ptr->flags2 & (RF2_INVISIBLE));
+ };
+ auto update = [](monster_race *r_ptr) -> void {
+ r_ptr->r_flags2 |= (RF2_INVISIBLE);
+ };
+
+ if (detect_monsters_fn(rad, predicate, update))
+ {
+ /* Describe result */
+ msg_print("You sense the presence of invisible creatures!");
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+
+/*
+ * A "generic" detect monsters routine, tagged to flags3
+ */
+bool_ detect_monsters_xxx(u32b match_flag, int rad)
+{
+ auto predicate = [match_flag](monster_race *r_ptr) -> bool {
+ return (r_ptr->flags3 & match_flag);
+ };
+ auto update = [match_flag](monster_race *r_ptr) -> void {
+ r_ptr->r_flags3 |= (match_flag);
+ };
+
+ if (detect_monsters_fn(rad, predicate, update))
+ {
+ cptr desc_monsters = "weird monsters";
+ 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;
+ case RF3_ORC:
+ desc_monsters = "orcs";
+ break;
+ }
+
+ /* Describe result */
+ msg_format("You sense the presence of %s!", desc_monsters);
+ msg_print(NULL);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+
+/*
+ * 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_quest)
+ {
+ /* 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 object_filter_t const &item_tester_hook_weapon()
+{
+ using namespace object_filter;
+ static auto instance =
+ Or(
+ TVal(TV_MSTAFF),
+ TVal(TV_BOOMERANG),
+ TVal(TV_SWORD),
+ TVal(TV_AXE),
+ TVal(TV_HAFTED),
+ TVal(TV_POLEARM),
+ TVal(TV_BOW),
+ TVal(TV_BOLT),
+ TVal(TV_ARROW),
+ TVal(TV_SHOT),
+ And(
+ TVal(TV_DAEMON_BOOK),
+ SVal(SV_DEMONBLADE)));
+ return instance;
+}
+
+
+/*
+ * Hook to specify "armour"
+ */
+static object_filter_t const &item_tester_hook_armour()
+{
+ using namespace object_filter;
+ static auto instance =
+ Or(
+ TVal(TV_DRAG_ARMOR),
+ TVal(TV_HARD_ARMOR),
+ TVal(TV_SOFT_ARMOR),
+ TVal(TV_SHIELD),
+ TVal(TV_CLOAK),
+ TVal(TV_CROWN),
+ TVal(TV_HELM),
+ TVal(TV_BOOTS),
+ TVal(TV_GLOVES),
+ And(
+ TVal(TV_DAEMON_BOOK),
+ Or(
+ SVal(SV_DEMONHORN),
+ SVal(SV_DEMONSHIELD))));
+ return instance;
+}
+
+
+/*
+ * Check if an object is artifactable
+ */
+object_filter_t const &item_tester_hook_artifactable()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ // Check base item family
+ Or(
+ item_tester_hook_weapon(),
+ item_tester_hook_armour(),
+ TVal(TV_DIGGING),
+ TVal(TV_RING),
+ TVal(TV_AMULET)),
+ // Only unenchanted items
+ Not(IsArtifactP()),
+ Not(IsEgo()));
+ return instance;
+}
+
+
+/*
+ * 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;
+ char o_name[80];
+ cptr q, s;
+
+
+ /* Assume enchant weapon */
+ object_filter_t object_filter = item_tester_hook_weapon();
+
+ /* Enchant armor if requested */
+ if (num_ac)
+ {
+ object_filter = 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), object_filter)) return (FALSE);
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /* Describe */
+ msg_format("%s %s glow%s brightly!",
+ ((item >= 0) ? "Your" : "The"), o_name,
+ ((o_ptr->number > 1) ? "" : "s"));
+
+ /* Enchant */
+ if (enchant(o_ptr, num_hit, ENCH_TOHIT)) okay = TRUE;
+ if (enchant(o_ptr, num_dam, ENCH_TODAM)) okay = TRUE;
+ if (enchant(o_ptr, num_ac, ENCH_TOAC)) okay = TRUE;
+ if (enchant(o_ptr, num_pval, ENCH_PVAL)) okay = TRUE;
+
+ /* Failure */
+ if (!okay)
+ {
+ /* Flush */
+ if (flush_failure) flush();
+
+ /* Message */
+ msg_print("The enchantment failed.");
+ }
+
+ /* Something happened */
+ return (TRUE);
+}
+
+void curse_artifact(object_type * o_ptr)
+{
+ if (o_ptr->pval) o_ptr->pval = 0 - ((o_ptr->pval) + randint(4));
+ if (o_ptr->to_a) o_ptr->to_a = 0 - ((o_ptr->to_a) + randint(4));
+ if (o_ptr->to_h) o_ptr->to_h = 0 - ((o_ptr->to_h) + randint(4));
+ if (o_ptr->to_d) o_ptr->to_d = 0 - ((o_ptr->to_d) + randint(4));
+ o_ptr->art_flags3 |= ( TR3_HEAVY_CURSE | TR3_CURSED );
+ if (randint(3) == 1) o_ptr-> art_flags3 |= TR3_TY_CURSE;
+ if (randint(2) == 1) o_ptr-> art_flags3 |= TR3_AGGRAVATE;
+ if (randint(3) == 1) o_ptr-> art_flags3 |= TR3_DRAIN_EXP;
+ if (randint(3) == 1) o_ptr-> art_flags4 |= TR4_BLACK_BREATH;
+ if (randint(2) == 1) o_ptr-> art_flags3 |= TR3_TELEPORT;
+ else if (randint(3) == 1) o_ptr->art_flags3 |= TR3_NO_TELE;
+ o_ptr->ident |= IDENT_CURSED;
+}
+
+
+
+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;
+ }
+}
+
+
+
+/*
+ * Make note of found artifacts.
+ */
+static void note_found_object(object_type *o_ptr)
+{
+ char note[150];
+ char item_name[80];
+
+ if (artifact_p(o_ptr) || o_ptr->name1)
+ {
+ object_desc(item_name, o_ptr, FALSE, 0);
+
+ /* Build note and write */
+ sprintf(note, "Found The %s", item_name);
+ add_note(note, 'A');
+ }
+}
+
+
+
+
+/*
+ * 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)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Identify which item? ",
+ "You have nothing to identify.",
+ (USE_EQUIP | USE_INVEN | USE_FLOOR),
+ object_filter::Not(object_known_p))) return (FALSE);
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Identify it fully */
+ object_aware(o_ptr);
+ object_known(o_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Description */
+ char o_name[80];
+ 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);
+ }
+
+ /* Make note of found artifacts */
+ note_found_object(o_ptr);
+
+ /* Process the appropriate hooks */
+ identify_hooks(item, o_ptr, IDENT_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);
+
+ /* Make note of found artifacts */
+ note_found_object(o_ptr);
+
+ /* Process the appropriate hooks */
+ identify_hooks(-i, o_ptr, IDENT_NORMAL);
+ }
+
+ /* Something happened */
+ return (TRUE);
+}
+
+
+
+/*
+ * Determine if an object is not fully identified
+ */
+static bool item_tester_hook_no_mental(object_type const *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)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Identify which item? ",
+ "You have nothing to identify.",
+ (USE_EQUIP | USE_INVEN | USE_FLOOR),
+ item_tester_hook_no_mental))
+ {
+ return (FALSE);
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Do the identification */
+ make_item_fully_identified(o_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Description */
+ char o_name[80];
+ 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);
+ }
+
+ /* Make note of found artifacts */
+ note_found_object(o_ptr);
+
+ /* Describe it fully */
+ object_out_desc(o_ptr, NULL, FALSE, TRUE);
+
+ /* Process the appropriate hooks */
+ identify_hooks(item, o_ptr, IDENT_FULL);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+
+/*
+ * Hook for "get_item()". Determine if something is rechargable.
+ */
+object_filter_t const &item_tester_hook_recharge()
+{
+ using namespace object_filter;
+ static auto instance =
+ And(
+ // Must NOT have NO_RECHARGE flag.
+ Not(HasFlag4(TR4_NO_RECHARGE)),
+ // ... and must be a device.
+ Or(
+ TVal(TV_STAFF),
+ TVal(TV_WAND),
+ TVal(TV_ROD_MAIN)));
+ return instance;
+}
+
+
+/*
+ * 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 lev;
+ bool_ fail = FALSE;
+ byte fail_type = 1;
+
+ char o_name[80];
+
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Recharge which item? ",
+ "You have nothing to recharge.",
+ (USE_INVEN | USE_FLOOR),
+ item_tester_hook_recharge()))
+ {
+ return (FALSE);
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+ /* Extract the flags */
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Extract the object "level" */
+ lev = k_info[o_ptr->k_idx].level;
+
+ /* Recharge a rod */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* Extract a recharge strength by comparing object level to power. */
+ recharge_strength = ((power > lev) ? (power - lev) : 0) / 5;
+
+ /* Paranoia */
+ if (recharge_strength < 0) recharge_strength = 0;
+
+ /* Back-fire */
+ if ((rand_int(recharge_strength) == 0) && (!(f4 & TR4_RECHARGE)))
+ {
+ /* Activate the failure code. */
+ fail = TRUE;
+ }
+
+ /* Recharge */
+ else
+ {
+ /* Recharge amount */
+ recharge_amount = (power * damroll(3, 2));
+
+ /* Recharge by that amount */
+ if (o_ptr->timeout + recharge_amount < o_ptr->pval2)
+ o_ptr->timeout += recharge_amount;
+ else
+ o_ptr->timeout = o_ptr->pval2;
+ }
+ }
+
+
+ /* Recharge wand/staff */
+ else
+ {
+ /* Extract a recharge strength by comparing object level to power.
+ * Divide up a stack of wands' charges to calculate charge penalty.
+ */
+ if ((o_ptr->tval == TV_WAND) && (o_ptr->number > 1))
+ recharge_strength = (100 + power - lev -
+ (8 * o_ptr->pval / o_ptr->number)) / 15;
+
+ /* All staffs, unstacked wands. */
+ else recharge_strength = (100 + power - lev -
+ (8 * o_ptr->pval)) / 15;
+
+
+ /* Back-fire XXX XXX XXX */
+ if (((rand_int(recharge_strength) == 0) && (!(f4 & TR4_RECHARGE))) ||
+ (f4 & TR4_NO_RECHARGE))
+ {
+ /* Activate the failure code. */
+ fail = TRUE;
+ }
+
+ /* If the spell didn't backfire, recharge the wand or staff. */
+ else
+ {
+ /* Recharge based on the standard number of charges. */
+ recharge_amount = randint((power / (lev + 2)) + 1);
+
+ /* Multiple wands in a stack increase recharging somewhat. */
+ if ((o_ptr->tval == TV_WAND) && (o_ptr->number > 1))
+ {
+ recharge_amount +=
+ (randint(recharge_amount * (o_ptr->number - 1))) / 2;
+ if (recharge_amount < 1) recharge_amount = 1;
+ if (recharge_amount > 12) recharge_amount = 12;
+ }
+
+ /* But each staff in a stack gets fewer additional charges,
+ * although always at least one.
+ */
+ if ((o_ptr->tval == TV_STAFF) && (o_ptr->number > 1))
+ {
+ recharge_amount /= o_ptr->number;
+ if (recharge_amount < 1) recharge_amount = 1;
+ }
+
+ /* Recharge the wand or staff. */
+ o_ptr->pval += recharge_amount;
+
+ if (!(f4 & TR4_RECHARGE))
+ {
+ /* Hack -- we no longer "know" the item */
+ o_ptr->ident &= ~(IDENT_KNOWN);
+ }
+
+ /* Hack -- we no longer think the item is empty */
+ o_ptr->ident &= ~(IDENT_EMPTY);
+ }
+ }
+
+ /* Mark as recharged */
+ o_ptr->art_flags4 |= TR4_RECHARGED;
+
+ /* Inflict the penalties for failing a recharge. */
+ if (fail)
+ {
+ /* Artifacts are never destroyed. */
+ if (artifact_p(o_ptr))
+ {
+ object_desc(o_name, o_ptr, TRUE, 0);
+ msg_format("The recharging backfires - %s is completely drained!", o_name);
+
+ /* Artifact rods. */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ o_ptr->timeout = 0;
+
+ /* Artifact wands and staffs. */
+ else if ((o_ptr->tval == TV_WAND) || (o_ptr->tval == TV_STAFF))
+ o_ptr->pval = 0;
+ }
+ else
+ {
+ /* Get the object description */
+ object_desc(o_name, o_ptr, FALSE, 0);
+
+ /*** Determine Seriousness of Failure ***/
+
+ /* Mages recharge objects more safely. */
+ if (has_ability(AB_PERFECT_CASTING))
+ {
+ /* 10% chance to blow up one rod, otherwise draining. */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (randint(10) == 1) fail_type = 2;
+ else fail_type = 1;
+ }
+ /* 75% chance to blow up one wand, otherwise draining. */
+ else if (o_ptr->tval == TV_WAND)
+ {
+ if (randint(3) != 1) fail_type = 2;
+ else fail_type = 1;
+ }
+ /* 50% chance to blow up one staff, otherwise no effect. */
+ else if (o_ptr->tval == TV_STAFF)
+ {
+ if (randint(2) == 1) fail_type = 2;
+ else fail_type = 0;
+ }
+ }
+
+ /* All other classes get no special favors. */
+ else
+ {
+ /* 33% chance to blow up one rod, otherwise draining. */
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (randint(3) == 1) fail_type = 2;
+ else fail_type = 1;
+ }
+ /* 20% chance of the entire stack, else destroy one wand. */
+ else if (o_ptr->tval == TV_WAND)
+ {
+ if (randint(5) == 1) fail_type = 3;
+ else fail_type = 2;
+ }
+ /* Blow up one staff. */
+ else if (o_ptr->tval == TV_STAFF)
+ {
+ fail_type = 2;
+ }
+ }
+
+ /*** Apply draining and destruction. ***/
+
+ /* Drain object or stack of objects. */
+ if (fail_type == 1)
+ {
+ if (o_ptr->tval == TV_ROD_MAIN)
+ {
+ msg_print("The recharge backfires, draining the rod further!");
+ if (o_ptr->timeout < 10000)
+ o_ptr->timeout = 0;
+ }
+ else if (o_ptr->tval == TV_WAND)
+ {
+ msg_format("You save your %s from destruction, but all charges are lost.", o_name);
+ o_ptr->pval = 0;
+ }
+ /* Staffs aren't drained. */
+ }
+
+ /* Destroy an object or one in a stack of objects. */
+ if (fail_type == 2)
+ {
+ if (o_ptr->number > 1)
+ msg_format("Wild magic consumes one of your %s!", o_name);
+ else
+ msg_format("Wild magic consumes your %s!", o_name);
+
+ /* Reduce rod stack maximum timeout, drain wands. */
+ if (o_ptr->tval == TV_WAND) o_ptr->pval = 0;
+
+ /* Reduce and describe */
+ inc_stack_size(item, -1);
+ }
+
+ /* Destroy all memebers of a stack of objects. */
+ if (fail_type == 3)
+ {
+ if (o_ptr->number > 1)
+ msg_format("Wild magic consumes all your %s!", o_name);
+ else
+ msg_format("Wild magic consumes your %s!", o_name);
+
+
+ /* Reduce and describe inventory */
+ inc_stack_size(item, -999);
+ }
+ }
+ }
+
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN);
+
+ /* Something was done */
+ return (TRUE);
+}
+
+
+
+/*
+ * Apply a "project()" directly to all viewable monsters
+ *
+ * Note that affected monsters are NOT auto-tracked by this usage.
+ */
+bool_ project_hack(int typ, int dam)
+{
+ int i, x, y;
+ int flg = PROJECT_JUMP | PROJECT_KILL | PROJECT_HIDE;
+ bool_ obvious = FALSE;
+
+
+ /* Affect all (nearby) monsters */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ /* Location */
+ y = m_ptr->fy;
+ x = m_ptr->fx;
+
+ /* Require line of sight */
+ if (!player_has_los_bold(y, x)) continue;
+
+ /* Jump directly to the target monster */
+ if (project(0, 0, y, x, dam, typ, flg)) obvious = TRUE;
+ }
+
+ /* Result */
+ return (obvious);
+}
+
+/*
+ * Apply a "project()" a la meteor shower
+ */
+void project_meteor(int radius, int typ, int dam, u32b flg)
+{
+ cave_type *c_ptr;
+ int x, y, dx, dy, d, count = 0, i;
+ int b = radius + randint(radius);
+ for (i = 0; i < b; i++)
+ {
+ for (count = 0; count < 1000; count++)
+ {
+ x = p_ptr->px - 5 + randint(10);
+ y = p_ptr->py - 5 + randint(10);
+ if ((x < 0) || (x >= cur_wid) ||
+ (y < 0) || (y >= cur_hgt)) continue;
+ dx = (p_ptr->px > x) ? (p_ptr->px - x) : (x - p_ptr->px);
+ dy = (p_ptr->py > y) ? (p_ptr->py - y) : (y - p_ptr->py);
+ /* Approximate distance */
+ d = (dy > dx) ? (dy + (dx >> 1)) : (dx + (dy >> 1));
+ c_ptr = &cave[y][x];
+ /* Check distance */
+ if ((d <= 5) &&
+ /* Check line of sight */
+ (player_has_los_bold(y, x)) &&
+ /* But don't explode IN a wall, letting the player attack through walls */
+ !(c_ptr->info & CAVE_WALL))
+ break;
+ }
+ if (count >= 1000) break;
+ project(0, 2, y, x, dam, typ, PROJECT_JUMP | flg);
+ }
+}
+
+
+/*
+ * Banish evil monsters
+ */
+bool_ banish_evil(int dist)
+{
+ return (project_hack(GF_AWAY_EVIL, dist));
+}
+
+
+
+/*
+ * 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));
+}
+
+
+/*
+ * 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];
+
+ /* 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))
+ {
+ auto const r_ptr = m_ptr->race();
+
+ /* 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
+ */
+static 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];
+ auto const r_ptr = m_ptr->race();
+
+ *typ = r_ptr->d_char;
+ return true;
+ }
+
+ msg_print("You must select a monster.");
+ return false;
+}
+
+
+/*
+ * 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];
+ auto const r_ptr = m_ptr->race();
+
+ /* 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;
+ char buf[256];
+
+ monster_race_desc(buf, m_ptr->r_idx, 0);
+
+ do
+ {
+ scatter(&wy, &wx, m_ptr->fy, m_ptr->fx, 10);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (place_monster_aux(wy, wx, m_ptr->r_idx, FALSE, TRUE, MSTATUS_ENEMY))
+ {
+ cmsg_format(TERM_L_BLUE, "The spell seems to produce an ... interesting effect on the %s.", buf);
+ }
+
+ 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 */
+ sleep_for(milliseconds(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_FRAME);
+
+ /* 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];
+ auto const r_ptr = m_ptr->race();
+
+ /* 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;
+ char buf[256];
+
+ monster_race_desc(buf, m_ptr->r_idx, 0);
+
+ do
+ {
+ scatter(&wy, &wx, m_ptr->fy, m_ptr->fx, 10);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (place_monster_aux(wy, wx, m_ptr->r_idx, FALSE, TRUE, MSTATUS_ENEMY))
+ {
+ cmsg_format(TERM_L_BLUE, "The spell seems to produce an ... interesting effect on the %s.", buf);
+ }
+
+ 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 */
+ sleep_for(milliseconds(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_FRAME);
+
+ /* 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);
+
+ {
+ std::ostringstream buf;
+ buf << " has " << m_ptr->exp
+ << " exp and needs " << monster_exp(m_ptr->level + 1) << ".";
+ msg_format("%^s%s", m_name, buf.str().c_str());
+ }
+ }
+
+ /* 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);
+}
+
+
+/*
+ * 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)
+{
+ int y, x, k, t;
+
+ 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);
+
+ /* 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 (race_flags1_p(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];
+ auto const r_ptr = m_ptr->race();
+
+ /* 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_FRAME);
+
+ /* 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];
+ auto const r_ptr = m_ptr->race();
+
+ /* 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));
+}
+
+
+
+void teleport_swap(int dir)
+{
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ int tx, ty;
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+ else
+ {
+ tx = p_ptr->px + ddx[dir];
+ ty = p_ptr->py + ddy[dir];
+ }
+
+ cave_type *c_ptr = &cave[ty][tx];
+
+ if (!c_ptr->m_idx)
+ {
+ msg_print("You can't trade places with that!");
+ }
+ else
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+ auto const r_ptr = m_ptr->race();
+
+ 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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+ }
+ }
+}
+
+void swap_position(int lty, int ltx)
+{
+ int tx = ltx, ty = lty;
+ cave_type * c_ptr;
+ monster_type * m_ptr;
+
+ if (p_ptr->resist_continuum)
+ {
+ msg_print("The space-time continuum can't be disrupted.");
+ return;
+ }
+
+ c_ptr = &cave[ty][tx];
+
+ if (!c_ptr->m_idx)
+ {
+ sound(SOUND_TELEPORT);
+
+ /* Keep trace of the old location */
+ tx = p_ptr->px;
+ ty = p_ptr->py;
+
+ /* Move the player */
+ p_ptr->px = ltx;
+ p_ptr->py = lty;
+
+ /* Redraw the old grid */
+ lite_spot(ty, tx);
+
+ /* Redraw the new grid */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff XXX XXX XXX */
+ handle_stuff();
+ }
+ else
+ {
+ m_ptr = &m_list[c_ptr->m_idx];
+
+ sound(SOUND_TELEPORT);
+
+ cave[p_ptr->py][p_ptr->px].m_idx = c_ptr->m_idx;
+
+ /* Update the old location */
+ c_ptr->m_idx = 0;
+
+ /* Move the monster */
+ m_ptr->fy = p_ptr->py;
+ m_ptr->fx = p_ptr->px;
+
+ /* Move the player */
+ p_ptr->px = tx;
+ p_ptr->py = ty;
+
+ tx = m_ptr->fx;
+ ty = m_ptr->fy;
+
+ /* Update the monster (new location) */
+ update_mon(cave[ty][tx].m_idx, TRUE);
+
+ /* Redraw the old grid */
+ lite_spot(ty, tx);
+
+ /* Redraw the new grid */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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 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));
+ }
+}
+
+
+/*
+ * 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_ disarm_trap(int dir)
+{
+ int flg = PROJECT_BEAM | PROJECT_GRID | PROJECT_ITEM;
+ return (project_hook(GF_KILL_TRAP, dir, 0, 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_ confuse_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_OLD_CONF, 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_ fear_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_TURN_ALL, 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));
+}
+
+
+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_ wall_stone(int y, int x)
+{
+ cave_type *c_ptr = &cave[y][x];
+ int flg = PROJECT_GRID | PROJECT_ITEM;
+ int featflags = f_info[c_ptr->feat].flags1;
+
+ bool_ dummy = (project(0, 1, y, x, 0, GF_STONE_WALL, flg));
+
+ if (!(featflags & FF1_PERMANENT) && !(featflags & FF1_WALL))
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ return dummy;
+}
+
+
+bool_ destroy_doors_touch(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_KILL_DOOR, flg));
+}
+
+bool_ destroy_traps_touch(void)
+{
+ int flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, 0, GF_KILL_TRAP, flg));
+}
+
+bool_ sleep_monsters_touch(void)
+{
+ int flg = PROJECT_KILL | PROJECT_HIDE;
+ return (project(0, 1, p_ptr->py, p_ptr->px, p_ptr->lev, GF_OLD_SLEEP, flg));
+}
+
+
+void call_chaos(void)
+{
+ int Chaos_type, dummy, dir;
+ int plev = p_ptr->lev;
+ bool_ line_chaos = FALSE;
+
+ int hurt_types[30] =
+ {
+ GF_ELEC, GF_POIS, GF_ACID, GF_COLD,
+ GF_FIRE, GF_MISSILE, GF_ARROW, GF_PLASMA,
+ GF_HOLY_FIRE, GF_WATER, GF_LITE, GF_DARK,
+ GF_FORCE, GF_INERTIA, GF_MANA, GF_METEOR,
+ GF_ICE, GF_CHAOS, GF_NETHER, GF_DISENCHANT,
+ GF_SHARDS, GF_SOUND, GF_NEXUS, GF_CONFUSION,
+ GF_TIME, GF_GRAVITY, GF_ROCKET, GF_NUKE,
+ GF_HELL_FIRE, GF_DISINTEGRATE
+ };
+
+ Chaos_type = hurt_types[randint(30) - 1];
+ if (randint(4) == 1) line_chaos = TRUE;
+
+ if (randint(6) == 1)
+ {
+ for (dummy = 1; dummy < 10; dummy++)
+ {
+ if (dummy - 5)
+ {
+ if (line_chaos)
+ fire_beam(Chaos_type, dummy, 75);
+ else
+ fire_ball(Chaos_type, dummy, 75, 2);
+ }
+ }
+ }
+ else if (randint(3) == 1)
+ {
+ fire_ball(Chaos_type, 0, 300, 8);
+ }
+ else
+ {
+ if (!get_aim_dir(&dir)) return;
+ if (line_chaos)
+ fire_beam(Chaos_type, dir, 150);
+ else
+ fire_ball(Chaos_type, dir, 150, 3 + (plev / 35));
+ }
+}
+
+
+static 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);
+ }
+ }
+}
+
+
+/*
+ * 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(randint(3));
+ else
+ set_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(randint(3));
+ else
+ set_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 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);
+ }
+}
+
+static 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);
+ }
+}
+
+
+
+/*
+ * 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));
+}
+
+
+/*
+ * Stun monsters
+ */
+bool_ stun_monsters(int dam)
+{
+ return (project_hack(GF_STUN, 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 everyone
+ */
+bool_ turn_monsters(int dam)
+{
+ return (project_hack(GF_TURN_ALL, dam));
+}
+
+
+bool_ charm_monster(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_CHARM, dir, plev, flg));
+}
+
+bool_ control_one_undead(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_CONTROL_UNDEAD, dir, plev, flg));
+}
+
+
+bool_ charm_animal(int dir, int plev)
+{
+ int flg = PROJECT_STOP | PROJECT_KILL;
+ return (project_hook(GF_CONTROL_ANIMAL, dir, plev, flg));
+}
+
+void change_wild_mode(void)
+{
+ if (p_ptr->immovable && !p_ptr->wild_mode)
+ {
+ msg_print("Hmm, blinking there will take time.");
+ }
+
+ if (p_ptr->word_recall && !p_ptr->wild_mode)
+ {
+ msg_print("You will soon be recalled.");
+ return;
+ }
+
+ p_ptr->wild_mode = !p_ptr->wild_mode;
+
+ autosave_checkpoint();
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+void alter_reality(void)
+{
+ msg_print("The world changes!");
+
+ autosave_checkpoint();
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+/* Heal insanity. */
+bool_ heal_insanity(int val)
+{
+ if (p_ptr->csane < p_ptr->msane)
+ {
+ p_ptr->csane += val;
+
+ if (p_ptr->csane >= p_ptr->msane)
+ {
+ p_ptr->csane = p_ptr->msane;
+ p_ptr->csane_frac = 0;
+ }
+
+ p_ptr->redraw |= (PR_FRAME);
+ p_ptr->window |= (PW_PLAYER);
+
+ if (val < 5)
+ {
+ msg_print("You feel a little better.");
+ }
+ else if (val < 15)
+ {
+ msg_print("You feel better.");
+ }
+ else if (val < 35)
+ {
+ msg_print("You feel much better.");
+ }
+ else
+ {
+ msg_print("You feel very good.");
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Send the player shooting through walls in the given direction until
+ * they reach a non-wall space, or a monster, or a permanent wall.
+ */
+bool_ passwall(int dir, bool_ safe)
+{
+ int x = p_ptr->px, y = p_ptr->py, ox = p_ptr->px, oy = p_ptr->py, lx = p_ptr->px, ly = p_ptr->py;
+ cave_type *c_ptr;
+ bool_ ok = FALSE;
+
+ if (p_ptr->wild_mode) return FALSE;
+ if (p_ptr->inside_quest) return FALSE;
+ if (dungeon_flags2 & DF2_NO_TELEPORT) return FALSE;
+
+ /* Must go somewhere */
+ if (dir == 5) return FALSE;
+
+ while (TRUE)
+ {
+ x += ddx[dir];
+ y += ddy[dir];
+ c_ptr = &cave[y][x];
+
+ /* Perm walls stops the transfer */
+ if ((!in_bounds(y, x)) && (f_info[c_ptr->feat].flags1 & FF1_PERMANENT))
+ {
+ /* get the last working position */
+ x -= ddx[dir];
+ y -= ddy[dir];
+ ok = FALSE;
+ break;
+ }
+
+ /* Never on a monster */
+ if (c_ptr->m_idx) continue;
+
+ /* Never stop in vaults */
+ if (c_ptr->info & CAVE_ICKY) continue;
+
+ /* From now on, the location COULD be used in special case */
+ lx = x;
+ ly = y;
+
+ /* Pass over walls */
+ if (f_info[c_ptr->feat].flags1 & FF1_WALL) continue;
+
+ /* So it must be ok */
+ ok = TRUE;
+ break;
+ }
+
+ if (!ok)
+ {
+ x = lx;
+ y = ly;
+
+ if (!safe)
+ {
+ msg_print("You emerge in the wall!");
+ take_hit(damroll(10, 8), "becoming one with a wall");
+ }
+ place_floor_convert_glass(y, x);
+ }
+
+ /* Move */
+ p_ptr->px = x;
+ p_ptr->py = y;
+
+ /* Redraw the old spot */
+ lite_spot(oy, ox);
+
+ /* Redraw the new spot */
+ lite_spot(p_ptr->py, p_ptr->px);
+
+ /* Check for new panel (redraw map) */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_VIEW | PU_FLOW | PU_MON_LITE);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_DISTANCE);
+
+ /* Redraw trap detection status */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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(std::vector<int> const &dungeon_idxs,
+ int start,
+ 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 < static_cast<int>(dungeon_idxs.size()); i++, j++)
+ {
+ dungeon_info_type *d_ptr = &d_info[dungeon_idxs[j]];
+
+ strnfmt(buf, 80, " %c) %-30s", I2A(i), 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);
+}
+
+static int find_dungeon_by_name(char const *name)
+{
+ /* Find the index corresponding to the name */
+ for (int i = 1; i < max_d_idx; i++)
+ {
+ /* Skip non-initialized entries. */
+ if (d_info[i].name == nullptr) {
+ continue;
+ }
+ if (iequals(name, d_info[i].name))
+ {
+ return i;
+ }
+ }
+ /* Not found */
+ return -1;
+}
+
+static int reset_recall_aux()
+{
+ char which;
+ int start = 0;
+ int ret;
+ bool_ mode = FALSE;
+
+ // Dungeons available for recall
+ std::vector<int> dungeons;
+ for (size_t i = 1; i < max_d_idx; i++)
+ {
+ /* skip "blocked" dungeons */
+ if (d_info[i].flags1 & DF1_NO_RECALL) continue;
+
+ if (max_dlv[i])
+ {
+ dungeons.push_back(i);
+ }
+ }
+
+ character_icky = TRUE;
+ Term_save();
+
+ while (1)
+ {
+ print_dungeon_batch(dungeons, start, 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;
+ assert(start > 0);
+ if (static_cast<size_t>(start) >= dungeons.size())
+ {
+ 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, d_info[p_ptr->recall_dungeon].name);
+ if (!get_string("Which dungeon? ", buf, 79)) continue;
+
+ /* Find the index corresponding to the name */
+ int i = find_dungeon_by_name(buf);
+
+ if (i < 0)
+ {
+ 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);
+ int i = start + A2I(which);
+
+ if (i < 0)
+ {
+ bell();
+ continue;
+ }
+ else if (static_cast<size_t>(i) >= dungeons.size()) // Cast to avoid compilation warning
+ {
+ bell();
+ continue;
+ }
+ else
+ {
+ ret = dungeons[i];
+ break;
+ }
+ }
+ }
+
+ Term_load();
+ character_icky = FALSE;
+
+ 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_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;
+}
+
+/*
+ * 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);
+ }
+}
+
+/**
+ * Geomancy
+ */
+typedef struct geomancy_entry {
+ int skill;
+ int feat;
+ int min_skill_level;
+} geomancy_entry;
+
+static int choose_geomancy_feature(int n, geomancy_entry *table)
+{
+ int feat = -1;
+ /* choose feature */
+ while (feat < 0) {
+ geomancy_entry *t = &table[rand_int(n)];
+
+ /* Do we meet the requirements ?
+ And then select the features based on skill proportions */
+ if ((get_skill(t->skill) >= t->min_skill_level) && magik(get_skill_scale(t->skill, 100)))
+ {
+ feat = t->feat;
+ }
+ }
+ /* return */
+ return feat;
+}
+
+static int rotate_dir(int dir, int mov)
+{
+ if (mov > 0)
+ {
+ switch (dir) {
+ case 7: return 8;
+ case 8: return 9;
+ case 9: return 6;
+ case 6: return 3;
+ case 3: return 2;
+ case 2: return 1;
+ case 1: return 4;
+ case 4: return 7;
+ }
+ }
+ else if (mov < 0)
+ {
+ switch (dir) {
+ case 7: return 4;
+ case 4: return 1;
+ case 1: return 2;
+ case 2: return 3;
+ case 3: return 6;
+ case 6: return 9;
+ case 9: return 8;
+ case 8: return 7;
+ }
+ }
+
+ return dir;
+}
+
+void geomancy_random_wall(int y, int x)
+{
+#define TABLE_SIZE 4
+ cave_type *c_ptr = &cave[y][x];
+ int feat = -1;
+ geomancy_entry table[TABLE_SIZE] = {
+ /* Fire element */
+ { SKILL_FIRE, FEAT_SANDWALL, 1},
+ /* Water element */
+ { SKILL_WATER, FEAT_TREES, 1},
+ { SKILL_WATER, FEAT_ICE_WALL, 12},
+ /* Earth element */
+ { SKILL_EARTH, FEAT_WALL_EXTRA, 1}
+ };
+
+ /* Do not destroy permanent things */
+ if (f_info[c_ptr->feat].flags1 & FF1_PERMANENT) {
+ return;
+ }
+
+ /* Choose feature */
+ feat = choose_geomancy_feature(TABLE_SIZE, table);
+ if (feat >= 0)
+ {
+ cave_set_feat(y, x, feat);
+ }
+#undef TABLE_SIZE
+}
+
+void geomancy_random_floor(int y, int x, bool_ kill_wall)
+{
+#define TABLE_SIZE 9
+ cave_type *c_ptr = &cave[y][x];
+ int feat = -1;
+ geomancy_entry table[TABLE_SIZE] = {
+ /* Fire element */
+ { SKILL_FIRE, FEAT_SAND, 1},
+ { SKILL_FIRE, FEAT_SHAL_LAVA, 8},
+ { SKILL_FIRE, FEAT_DEEP_LAVA, 18},
+ /* Water element */
+ { SKILL_WATER, FEAT_SHAL_WATER, 1},
+ { SKILL_WATER, FEAT_DEEP_WATER, 8},
+ { SKILL_WATER, FEAT_ICE, 18},
+ /* Earth element */
+ { SKILL_EARTH, FEAT_GRASS, 1},
+ { SKILL_EARTH, FEAT_FLOWER, 8},
+ { SKILL_EARTH, FEAT_DARK_PIT, 18}
+ };
+
+ /* Do not destroy permanent things */
+ if (f_info[c_ptr->feat].flags1 & FF1_PERMANENT) {
+ return;
+ }
+ if (!(kill_wall || (f_info[c_ptr->feat].flags1 & FF1_FLOOR))) {
+ return;
+ }
+
+ /* Choose feature */
+ feat = choose_geomancy_feature(TABLE_SIZE, table);
+ if (feat >= 0)
+ {
+ cave_set_feat(y, x, feat);
+ }
+#undef TABLE_SIZE
+}
+
+static bool_ geomancy_can_tunnel(int y, int x)
+{
+ switch (cave[y][x].feat)
+ {
+ case FEAT_WALL_EXTRA:
+ case FEAT_WALL_OUTER:
+ case FEAT_WALL_INNER:
+ case FEAT_WALL_SOLID:
+ case FEAT_MAGMA:
+ case FEAT_QUARTZ:
+ case FEAT_MAGMA_H:
+ case FEAT_QUARTZ_H:
+ case FEAT_MAGMA_K:
+ case FEAT_QUARTZ_K:
+ case FEAT_TREES:
+ case FEAT_DEAD_TREE:
+ case FEAT_SANDWALL:
+ case FEAT_SANDWALL_H:
+ case FEAT_SANDWALL_K:
+ case FEAT_ICE_WALL:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+void geomancy_dig(int oy, int ox, int dir, int length)
+{
+ int dy = ddy[dir];
+ int dx = ddx[dir];
+ int y = dy + oy;
+ int x = dx + ox;
+ int i;
+
+ for (i=0; i<length; i++)
+ {
+ /* stop at the end of tunnelable things */
+ if (!geomancy_can_tunnel(y, x)) {
+ break;
+ }
+
+ if (geomancy_can_tunnel(y - 1, x - 1)) { geomancy_random_wall(y - 1, x - 1); }
+ if (geomancy_can_tunnel(y - 1, x )) { geomancy_random_wall(y - 1, x ); }
+ if (geomancy_can_tunnel(y - 1, x + 1)) { geomancy_random_wall(y - 1, x + 1); }
+
+ if (geomancy_can_tunnel(y , x - 1)) { geomancy_random_wall(y , x - 1); }
+ if (geomancy_can_tunnel(y , x + 1)) { geomancy_random_wall(y , x + 1); }
+
+ if (geomancy_can_tunnel(y + 1, x - 1)) { geomancy_random_wall(y + 1, x - 1); }
+ if (geomancy_can_tunnel(y + 1, x )) { geomancy_random_wall(y + 1, x ); }
+ if (geomancy_can_tunnel(y + 1, x + 1)) { geomancy_random_wall(y + 1, x + 1); }
+
+ y = y + dy;
+ x = x + dx;
+ }
+
+ /* Step back towards origin */
+ y = y - dy;
+ x = x - dx;
+ while ((y != oy) || (x != ox))
+ {
+ geomancy_random_floor(y, x, TRUE);
+
+ /* Should we branch ? */
+ if (magik(20))
+ {
+ int rot = magik(50) ? -1 : 1;
+ geomancy_dig(y, x, rotate_dir(dir, rot), length / 3);
+ }
+
+ y = y - dy;
+ x = x - dx;
+ }
+}
+
+void channel_the_elements(int y, int x, int level)
+{
+ // Type of water to use (if any)
+ auto water_type = []() -> int {
+ return (get_skill(SKILL_WATER) >= 18) ? GF_WAVE : GF_WATER;
+ };
+ // Do we use hellfire?
+ auto use_hellfire = []() -> bool {
+ return get_skill(SKILL_FIRE) >= 15;
+ };
+ // Type of fire to use (if any)
+ auto fire_type = [&use_hellfire]() -> int {
+ return use_hellfire() ? GF_HELL_FIRE : GF_FIRE;
+ };
+
+ switch (cave[y][x].feat)
+ {
+ case FEAT_GRASS:
+ hp_player(p_ptr->mhp * (5 + get_skill_scale(SKILL_EARTH, 20)) / 100);
+ break;
+
+ case FEAT_FLOWER:
+ hp_player(p_ptr->mhp * (5 + get_skill_scale(SKILL_EARTH, 30)) / 100);
+ break;
+
+ case FEAT_DARK_PIT:
+ {
+ int dir, type;
+ if (!get_aim_dir(&dir)) break;
+
+ type = (get_skill(SKILL_EARTH) >= 18) ? GF_NETHER : GF_DARK;
+
+ fire_bolt(type, dir, damroll(10, get_skill(SKILL_EARTH)));
+
+ break;
+ }
+
+ case FEAT_SHAL_WATER:
+ {
+ int dir;
+ if (!get_aim_dir(&dir)) break;
+
+ if (get_skill(SKILL_WATER) >= 8)
+ {
+ fire_beam(water_type(), dir, damroll(3, get_skill(SKILL_WATER)));
+ }
+ else
+ {
+ fire_bolt(water_type(), dir, damroll(3, get_skill(SKILL_WATER)));
+ }
+
+ break;
+ }
+
+ case FEAT_DEEP_WATER:
+ {
+ int dir;
+ if (!get_aim_dir(&dir)) break;
+
+ if (get_skill(SKILL_WATER) >= 8)
+ {
+ fire_beam(water_type(), dir, damroll(5, get_skill(SKILL_WATER)));
+ }
+ else
+ {
+ fire_bolt(water_type(), dir, damroll(5, get_skill(SKILL_WATER)));
+ }
+
+ break;
+ }
+
+ case FEAT_ICE:
+ {
+ int dir;
+ if (!get_aim_dir(&dir)) break;
+
+ if (get_skill(SKILL_WATER) >= 12)
+ {
+ fire_ball(GF_ICE, dir, get_skill_scale(SKILL_WATER, 340), 3);
+ }
+ else
+ {
+ fire_bolt(GF_ICE, dir, damroll(3, get_skill(SKILL_WATER)));
+ }
+
+ break;
+ }
+
+ case FEAT_SAND:
+ {
+ int type, dur;
+
+ type = use_hellfire() ? SHIELD_GREAT_FIRE : SHIELD_FIRE;
+
+ 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);
+
+ break;
+ }
+
+ case FEAT_SHAL_LAVA:
+ {
+ int dir;
+ if (!get_aim_dir(&dir)) break;
+
+ fire_bolt(fire_type(), dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15));
+ break;
+ }
+
+ case FEAT_DEEP_LAVA:
+ {
+ int dir;
+ if (!get_aim_dir(&dir)) break;
+
+ fire_ball(fire_type(), dir, damroll(get_skill_scale(SKILL_FIRE, 30), 15), 3);
+ break;
+ }
+
+ default:
+ msg_print("You cannot channel this area.");
+ return;
+ }
+
+ /* Drain area? */
+ if (magik(100 - level))
+ {
+ if (cave[y][x].feat == FEAT_FLOWER)
+ {
+ cave_set_feat(y, x, FEAT_GRASS);
+ }
+ else
+ {
+ cave_set_feat(y, x, FEAT_FLOOR);
+ }
+ msg_print("The area is drained.");
+ }
+}
diff --git a/src/spells2.hpp b/src/spells2.hpp
new file mode 100644
index 00000000..bffc4a2c
--- /dev/null
+++ b/src/spells2.hpp
@@ -0,0 +1,115 @@
+#pragma once
+
+#include "h-basic.h"
+#include "identify_mode.hpp"
+#include "object_filter.hpp"
+#include "object_type_fwd.hpp"
+
+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_hooks(int i, object_type *o_ptr, identify_mode type);
+extern bool_ 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 detect_objects_gold(int rad);
+extern bool detect_objects_normal(int rad);
+extern bool_ detect_monsters_normal(int rad);
+extern bool_ detect_monsters_invis(int rad);
+extern bool_ detect_monsters_xxx(u32b match_flag, 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 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 void destroy_area(int y1, int x1, int r);
+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_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 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_ wall_to_mud(int dir);
+extern bool_ disarm_trap(int dir);
+extern bool_ wizard_lock(int dir);
+extern bool_ slow_monster(int dir);
+extern bool_ sleep_monster(int dir);
+extern bool_ confuse_monster(int dir, int plev);
+extern bool_ fear_monster(int dir, int plev);
+extern bool_ poly_monster(int dir);
+extern bool_ teleport_monster(int dir);
+extern bool_ trap_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 summon_cyber(void);
+extern bool_ confuse_monsters(int dam);
+extern bool_ charm_monsters(int dam);
+extern bool_ charm_animals(int dam);
+extern bool_ stun_monsters(int dam);
+extern bool_ banish_monsters(int dist);
+extern bool_ turn_monsters(int dam);
+extern bool_ 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 object_filter_t const &item_tester_hook_recharge();
+extern bool_ project_hack(int typ, int dam);
+extern void project_meteor(int radius, int typ, int dam, u32b flg);
+extern object_filter_t const &item_tester_hook_artifactable();
+extern bool_ passwall(int dir, bool_ safe);
+extern bool_ project_hook(int typ, int dir, int dam, int flg);
+extern bool_ reset_recall(bool_ no_trepas_max_depth);
+extern void geomancy_random_wall(int y, int x);
+extern void geomancy_random_floor(int y, int x, bool_ kill_wall);
+extern void geomancy_dig(int oy, int ox, int dir, int length);
+extern void channel_the_elements(int y, int x, int level);
+extern void random_resistance (object_type * o_ptr, bool_ is_scroll, int specific);
diff --git a/src/spells3.cc b/src/spells3.cc
new file mode 100644
index 00000000..35604f7d
--- /dev/null
+++ b/src/spells3.cc
@@ -0,0 +1,4606 @@
+#include "spells3.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd5.hpp"
+#include "feature_type.hpp"
+#include "lua_bind.hpp"
+#include "mimic.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "player_type.hpp"
+#include "school_book.hpp"
+#include "skills.hpp"
+#include "spell_type.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "spells4.hpp"
+#include "spells5.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "timer_type.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <algorithm>
+#include <cassert>
+#include <vector>
+
+s32b NOXIOUSCLOUD = -1; /* Identifier */
+s32b AIRWINGS = -1; /* Identifier */
+s32b INVISIBILITY;
+s32b POISONBLOOD;
+s32b THUNDERSTORM;
+s32b STERILIZE;
+
+s32b BLINK;
+s32b DISARM;
+s32b TELEPORT;
+s32b TELEAWAY;
+s32b RECALL;
+s32b PROBABILITY_TRAVEL;
+
+s32b DEMON_BLADE;
+s32b DEMON_MADNESS;
+s32b DEMON_FIELD;
+s32b DOOM_SHIELD;
+s32b UNHOLY_WORD;
+s32b DEMON_CLOAK;
+s32b DEMON_SUMMON;
+s32b DISCHARGE_MINION;
+s32b CONTROL_DEMON;
+
+s32b STARIDENTIFY;
+s32b IDENTIFY;
+s32b VISION;
+s32b SENSEHIDDEN;
+s32b REVEALWAYS;
+s32b SENSEMONSTERS;
+
+s32b STONESKIN;
+s32b DIG;
+s32b STONEPRISON;
+s32b STRIKE;
+s32b SHAKE;
+
+s32b ERU_SEE;
+s32b ERU_LISTEN;
+s32b ERU_UNDERSTAND;
+s32b ERU_PROT;
+
+s32b GLOBELIGHT;
+s32b FIREFLASH;
+s32b FIERYAURA;
+s32b FIREWALL;
+s32b FIREGOLEM;
+
+s32b CALL_THE_ELEMENTS;
+s32b CHANNEL_ELEMENTS;
+s32b ELEMENTAL_WAVE;
+s32b VAPORIZE;
+s32b GEOLYSIS;
+s32b DRIPPING_TREAD;
+s32b GROW_BARRIER;
+s32b ELEMENTAL_MINION;
+
+s32b MANATHRUST;
+s32b DELCURSES;
+s32b RESISTS;
+s32b MANASHIELD;
+
+s32b MANWE_SHIELD;
+s32b MANWE_AVATAR;
+s32b MANWE_BLESS;
+s32b MANWE_CALL;
+
+s32b MELKOR_CURSE;
+s32b MELKOR_CORPSE_EXPLOSION;
+s32b MELKOR_MIND_STEAL;
+
+s32b RECHARGE;
+s32b SPELLBINDER;
+s32b DISPERSEMAGIC;
+s32b TRACKER;
+s32b INERTIA_CONTROL;
+timer_type *TIMER_INERTIA_CONTROL = 0;
+
+s32b CHARM;
+s32b CONFUSE;
+s32b ARMOROFFEAR;
+s32b STUN;
+
+s32b MAGELOCK;
+s32b SLOWMONSTER;
+s32b ESSENCESPEED;
+s32b BANISHMENT;
+
+s32b TULKAS_AIM;
+s32b TULKAS_WAVE;
+s32b TULKAS_SPIN;
+
+s32b DRAIN;
+s32b GENOCIDE;
+s32b WRAITHFORM;
+s32b FLAMEOFUDUN;
+
+s32b TIDALWAVE;
+s32b ICESTORM;
+s32b ENTPOTION;
+s32b VAPOR;
+s32b GEYSER;
+
+s32b YAVANNA_CHARM_ANIMAL;
+s32b YAVANNA_GROW_GRASS;
+s32b YAVANNA_TREE_ROOTS;
+s32b YAVANNA_WATER_BITE;
+s32b YAVANNA_UPROOT;
+
+s32b GROWTREE;
+s32b HEALING;
+s32b RECOVERY;
+s32b REGENERATION;
+s32b SUMMONANNIMAL;
+s32b GROW_ATHELAS = -1;
+
+s32b DEVICE_HEAL_MONSTER;
+s32b DEVICE_SPEED_MONSTER;
+s32b DEVICE_WISH;
+s32b DEVICE_SUMMON;
+s32b DEVICE_MANA;
+s32b DEVICE_NOTHING;
+s32b DEVICE_HOLY_FIRE;
+s32b DEVICE_THUNDERLORDS;
+
+s32b MUSIC_STOP;
+s32b MUSIC_HOLD;
+s32b MUSIC_CONF;
+s32b MUSIC_STUN;
+s32b MUSIC_LITE;
+s32b MUSIC_HEAL;
+s32b MUSIC_HERO;
+s32b MUSIC_TIME;
+s32b MUSIC_MIND;
+s32b MUSIC_BLOW;
+s32b MUSIC_WIND;
+s32b MUSIC_YLMIR;
+s32b MUSIC_AMBARKANTA;
+
+s32b AULE_FIREBRAND;
+s32b AULE_ENCHANT_WEAPON;
+s32b AULE_ENCHANT_ARMOUR;
+s32b AULE_CHILD;
+
+s32b MANDOS_TEARS_LUTHIEN = -1;
+s32b MANDOS_SPIRIT_FEANTURI = -1;
+s32b MANDOS_TALE_DOOM = -1;
+s32b MANDOS_CALL_HALLS = -1;
+
+s32b ULMO_BELEGAER;
+s32b ULMO_DRAUGHT_ULMONAN;
+s32b ULMO_CALL_ULUMURI;
+s32b ULMO_WRATH;
+
+s32b VARDA_LIGHT_VALINOR;
+s32b VARDA_CALL_ALMAREN;
+s32b VARDA_EVENSTAR;
+s32b VARDA_STARKINDLER;
+
+static s32b get_level_s(int sp, int max)
+{
+ return get_level(sp, max, 1);
+}
+
+static void find_position(int y, int x, int *yy, int *xx)
+{
+ int attempts = 500;
+
+ do
+ {
+ scatter(yy, xx, y, x, 6);
+ }
+ while (!(in_bounds(*yy, *xx) && cave_floor_bold(*yy, *xx)) && --attempts);
+}
+
+static casting_result cast(bool_ effect)
+{
+ return effect ? CAST_OBVIOUS : CAST_HIDDEN;
+}
+
+static casting_result cplus(casting_result old, bool_ effect)
+{
+ if (old == NO_CAST)
+ {
+ return cast(effect);
+ }
+ else
+ {
+ if ((old == CAST_OBVIOUS) || (effect == TRUE)) {
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return CAST_HIDDEN;
+ }
+ }
+}
+
+GENERATE_MONSTER_LOOKUP_FN(get_fire_golem, "Fire golem")
+
+// -------------------------------------------------------------
+
+casting_result air_noxious_cloud()
+{
+ int dir, type;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (get_level_s(NOXIOUSCLOUD, 50) >= 30)
+ {
+ type = GF_UNBREATH;
+ }
+ else
+ {
+ type = GF_POIS;
+ }
+
+ fire_cloud(type, dir, 7 + get_level_s(NOXIOUSCLOUD, 150), 3, 5 + get_level_s(NOXIOUSCLOUD, 40));
+ return CAST_OBVIOUS;
+}
+
+const char *air_noxious_cloud_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " rad 3 dur " FMTs32b,
+ (7 + get_level_s(NOXIOUSCLOUD, 150)),
+ (5 + get_level_s(NOXIOUSCLOUD, 40)));
+ return buf;
+}
+
+casting_result air_wings_of_winds()
+{
+ if (get_level_s(AIRWINGS, 50) >= 16)
+ {
+ if (p_ptr->tim_fly == 0)
+ {
+ return cast(set_tim_fly(randint(10) + 5 + get_level_s(AIRWINGS, 25)));
+ }
+ }
+ else
+ {
+ if (p_ptr->tim_ffall == 0)
+ {
+ return cast(set_tim_ffall(randint(10) + 5 + get_level_s(AIRWINGS, 25)));
+ }
+ }
+
+ return CAST_HIDDEN;
+}
+
+const char *air_wings_of_winds_info()
+{
+ static char buf[128];
+ sprintf(buf, "dur " FMTs32b "+d10", (5 + get_level_s(AIRWINGS, 25)));
+ return buf;
+}
+
+casting_result air_invisibility()
+{
+ if (p_ptr->tim_invisible == 0)
+ {
+ return cast(set_invis(randint(20) + 15 + get_level_s(INVISIBILITY, 50), 20 + get_level_s(INVISIBILITY, 50)));
+ }
+
+ return CAST_HIDDEN;
+}
+
+const char *air_invisibility_info()
+{
+ static char buf[128];
+ sprintf(buf, "dur " FMTs32b "+d20 power " FMTs32b,
+ (15 + get_level_s(INVISIBILITY, 50)),
+ (20 + get_level_s(INVISIBILITY, 50)));
+ return buf;
+}
+
+casting_result air_poison_blood()
+{
+ casting_result result = NO_CAST;
+
+ if (p_ptr->oppose_pois == 0)
+ {
+ result = cplus(result, set_oppose_pois(randint(30) + 25 + get_level_s(POISONBLOOD, 25)));
+ }
+
+ if ((p_ptr->tim_poison == 0) &&
+ (get_level_s(POISONBLOOD, 50) >= 15))
+ {
+ result = cplus(result, set_poison(randint(30) + 25 + get_level_s(POISONBLOOD, 25)));
+ }
+
+ return result;
+}
+
+const char *air_poison_blood_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d30",
+ (25 + get_level_s(POISONBLOOD, 25)));
+ return buf;
+}
+
+casting_result air_thunderstorm()
+{
+ if (p_ptr->tim_thunder == 0)
+ {
+ return cast(set_tim_thunder(randint(10) + 10 + get_level_s(THUNDERSTORM, 25), 5 + get_level_s(THUNDERSTORM, 10), 10 + get_level_s(THUNDERSTORM, 25)));
+ }
+
+ return CAST_HIDDEN;
+}
+
+const char *air_thunderstorm_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "d" FMTs32b " dur " FMTs32b "+d10",
+ (5 + get_level_s(THUNDERSTORM, 10)),
+ (10 + get_level_s(THUNDERSTORM, 25)),
+ (10 + get_level_s(THUNDERSTORM, 25)));
+ return buf;
+}
+
+casting_result air_sterilize()
+{
+ set_no_breeders((30) + 20 + get_level_s(STERILIZE, 70));
+ return CAST_OBVIOUS;
+}
+
+const char *air_sterilize_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d30",
+ (20 + get_level_s(STERILIZE, 70)));
+ return buf;
+}
+
+casting_result convey_blink()
+{
+ if (get_level_s(BLINK, 50) >= 30)
+ {
+ int oy = p_ptr->py;
+ int ox = p_ptr->px;
+
+ teleport_player(10 + get_level_s(BLINK, 8));
+ create_between_gate(0, oy, ox);
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ teleport_player(10 + get_level_s(BLINK, 8));
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *convey_blink_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "distance " FMTs32b,
+ (10 + get_level_s(BLINK, 8)));
+ return buf;
+}
+
+casting_result convey_disarm()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, destroy_doors_touch());
+ if (get_level_s(DISARM, 50) >= 10)
+ {
+ result = cplus(result, destroy_traps_touch());
+ }
+
+ return result;
+}
+
+casting_result convey_teleport()
+{
+ p_ptr->energy -= (25 - get_level_s(TELEPORT, 50));
+ teleport_player(100 + get_level_s(TELEPORT, 100));
+ return CAST_OBVIOUS;
+}
+
+const char *convey_teleport_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "distance " FMTs32b,
+ (100 + get_level_s(TELEPORT, 100)));
+ return buf;
+}
+
+casting_result convey_teleport_away()
+{
+ if (get_level_s(TELEAWAY, 50) >= 20)
+ {
+ return cast(project_hack(GF_AWAY_ALL, 100));
+ }
+ else if (get_level_s(TELEAWAY, 50) >= 10)
+ {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_AWAY_ALL, dir, 100, 3 + get_level_s(TELEAWAY, 4)));
+ }
+ else
+ {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+ return cast(teleport_monster(dir));
+ }
+}
+
+static int recall_get_d()
+{
+ int d = 21 - get_level_s(RECALL, 15);
+ if (d < 0)
+ {
+ d = 0;
+ }
+ return d;
+}
+
+static int recall_get_f()
+{
+ int f = 15 - get_level_s(RECALL, 10);
+ if (f < 1)
+ {
+ f = 1;
+ }
+ return f;
+}
+
+casting_result convey_recall()
+{
+ int x,y;
+ cave_type *c_ptr;
+
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+
+ c_ptr = &cave[y][x];
+
+ if ((y == p_ptr->py) &&
+ (x == p_ptr->px))
+ {
+ int d = recall_get_d();
+ int f = recall_get_f();
+ recall_player(d, f);
+ return CAST_OBVIOUS;
+ }
+ else if (c_ptr->m_idx > 0)
+ {
+ swap_position(y, x);
+ return CAST_OBVIOUS;
+ }
+ else if (!c_ptr->o_idxs.empty())
+ {
+ // Set the target
+ target_who = -1;
+ target_col = x;
+ target_row = y;
+ // Fetch item
+ if (get_level_s(RECALL, 50) >= 15)
+ {
+ fetch(5, 10 + get_level_s(RECALL, 150), FALSE);
+ }
+ else
+ {
+ fetch(5, 10 + get_level_s(RECALL, 150), TRUE);
+ }
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *convey_recall_info()
+{
+ static char buf[128];
+ int d = recall_get_d();
+ int f = recall_get_f();
+
+ sprintf(buf,
+ "dur %d+d%d weight " FMTs32b "lb",
+ f, d, (1 + get_level_s(RECALL, 15)));
+ return buf;
+}
+
+casting_result convey_probability_travel()
+{
+ return cast(set_prob_travel(randint(20) + get_level_s(PROBABILITY_TRAVEL, 60)));
+}
+
+const char *convey_probability_travel_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d20",
+ get_level_s(PROBABILITY_TRAVEL, 60));
+ return buf;
+}
+
+casting_result demonology_demon_blade()
+{
+ int rad, type;
+
+ type = GF_FIRE;
+ if (get_level_s(DEMON_BLADE, 50) >= 30)
+ {
+ type = GF_HELL_FIRE;
+ }
+
+ rad = 0;
+ if (get_level_s(DEMON_BLADE, 50) >= 45)
+ {
+ rad = 1;
+ }
+
+ return cast(set_project(randint(20) + get_level_s(DEMON_BLADE, 80),
+ type,
+ 4 + get_level_s(DEMON_BLADE, 40),
+ rad,
+ PROJECT_STOP | PROJECT_KILL));
+}
+
+const char *demonology_demon_blade_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d20 dam " FMTs32b "/blow",
+ (get_level_s(DEMON_BLADE, 80)),
+ (4 + get_level_s(DEMON_BLADE, 40)));
+ return buf;
+}
+
+casting_result demonology_demon_madness()
+{
+ casting_result result = NO_CAST;
+ int dir, type, y1, x1, y2, x2;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ type = GF_CHAOS;
+ if (magik(33))
+ {
+ type = GF_CONFUSION;
+ }
+ if (magik(33))
+ {
+ type = GF_CHARM;
+ }
+
+ // Calculate the coordinates of arrival
+ {
+ // Use the given direction
+ int tx = p_ptr->px + (ddx[dir] * 100);
+ int ty = p_ptr->py + (ddy[dir] * 100);
+
+ // Hack -- Use an actual "target"
+ if ((dir == 5) && target_okay())
+ {
+ tx = target_col;
+ ty = target_row;
+ }
+
+ y1 = ty;
+ x1 = tx;
+ }
+
+ // Calculate the appropriate place
+ y2 = p_ptr->py - (y1 - p_ptr->py);
+ x2 = p_ptr->px - (x1 - p_ptr->px);
+
+ result = cplus(result,
+ project(0, 1 + get_level(DEMON_MADNESS, 4, 0),
+ y1, x1,
+ 20 + get_level_s(DEMON_MADNESS, 200),
+ type,
+ PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL));
+ result = cplus(result,
+ project(0, 1 + get_level(DEMON_MADNESS, 4, 0),
+ y2, x2,
+ 20 + get_level_s(DEMON_MADNESS, 200),
+ type,
+ PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL));
+
+ return result;
+}
+
+const char *demonology_demon_madness_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " rad " FMTs32b,
+ (20 + get_level_s(DEMON_MADNESS, 200)),
+ (1 + get_level(DEMON_MADNESS, 4, 0)));
+ return buf;
+}
+
+casting_result demonology_demon_field()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_cloud(GF_NEXUS,
+ dir,
+ 20 + get_level_s(DEMON_FIELD, 70),
+ 7,
+ 30 + get_level_s(DEMON_FIELD, 100)));
+}
+
+const char *demonology_demon_field_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " dur " FMTs32b,
+ (20 + get_level_s(DEMON_FIELD, 70)),
+ (30 + get_level_s(DEMON_FIELD, 100)));
+ return buf;
+}
+
+casting_result demonology_doom_shield()
+{
+ return cast(set_shield(randint(10) + 20 + get_level_s(DOOM_SHIELD, 100),
+ -300 + get_level_s(DOOM_SHIELD, 100),
+ SHIELD_COUNTER,
+ 1 + get_level_s(DOOM_SHIELD, 14),
+ 10 + get_level_s(DOOM_SHIELD, 15)));
+}
+
+const char *demonology_doom_shield_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10 dam " FMTs32b "d" FMTs32b,
+ (20 + get_level_s(DOOM_SHIELD, 100)),
+ (1 + get_level_s(DOOM_SHIELD, 14)),
+ (10 + get_level_s(DOOM_SHIELD, 15)));
+ return buf;
+}
+
+casting_result demonology_unholy_word()
+{
+ int x, y;
+ cave_type *c_ptr = NULL;
+
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+
+ c_ptr = &cave[y][x];
+ if (c_ptr->m_idx > 0)
+ {
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ if (m_ptr->status != MSTATUS_PET)
+ {
+ msg_print("You can only target a pet.");
+ return NO_CAST;
+ }
+
+ /* Oops he is angry now */
+ if (magik(30 - get_level(UNHOLY_WORD, 25, 0)))
+ {
+ char buf[128];
+ monster_desc(buf, m_ptr, 0);
+ if (buf[0] != '\0')
+ {
+ buf[0] = toupper(buf[0]);
+ }
+
+ msg_format("%s turns against you.", buf);
+ }
+ else
+ {
+ char buf[128];
+ s32b heal;
+
+ monster_desc(buf, m_ptr, 0);
+ msg_format("You consume %s.", buf);
+
+ 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);
+ }
+
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *demonology_unholy_word_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heal mhp%% of " FMTs32b "%%",
+ (30 + get_level(UNHOLY_WORD, 50, 0)));
+ return buf;
+}
+
+casting_result demonology_demon_cloak()
+{
+ return cast(set_tim_reflect(randint(5) + 5 + get_level(DEMON_CLOAK, 15, 0)));
+}
+
+const char *demonology_demon_cloak_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d5",
+ (5 + get_level(DEMON_CLOAK, 15, 0)));
+ return buf;
+}
+
+casting_result demonology_summon_demon()
+{
+ int type, level, minlevel;
+
+ level = dun_level;
+
+ minlevel = 4;
+ if (level < minlevel)
+ {
+ level = minlevel;
+ }
+
+ summon_specific_level = 5 + get_level_s(DEMON_SUMMON, 100);
+
+ type = SUMMON_DEMON;
+ if (get_level_s(DEMON_SUMMON, 50) >= 35)
+ {
+ type = SUMMON_HI_DEMON;
+ }
+
+ if (summon_specific_friendly(p_ptr->py, p_ptr->px, level, type, TRUE))
+ {
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ msg_print("Something blocks your summoning!");
+ return CAST_HIDDEN;
+ }
+}
+
+const char *demonology_summon_demon_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level " FMTs32b,
+ (5 + get_level_s(DEMON_SUMMON, 100)));
+ return buf;
+}
+
+casting_result demonology_discharge_minion()
+{
+ cave_type *c_ptr;
+ int x, y;
+
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+
+ c_ptr = &cave[y][x];
+ if (c_ptr->m_idx > 0)
+ {
+ s32b dam;
+ monster_type *m_ptr = &m_list[c_ptr->m_idx];
+
+ if (m_ptr->status != MSTATUS_PET)
+ {
+ msg_print("You can only target a pet.");
+ return NO_CAST;
+ }
+
+ delete_monster_idx(c_ptr->m_idx);
+
+ dam = m_ptr->hp;
+ dam = (dam * (20 + get_level(DISCHARGE_MINION, 60, 0))) / 100;
+ if (dam > 100 + get_level(DISCHARGE_MINION, 500, 0))
+ {
+ dam = 100 + get_level(DISCHARGE_MINION, 500, 0);
+ }
+
+ /* We use project instead of fire_ball because we must tell it exactly where to land */
+ return cast(project(0, 2, y, x, dam,
+ GF_GRAVITY,
+ PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL));
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *demonology_discharge_minion_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "%% max " FMTs32b,
+ (20 + get_level(DISCHARGE_MINION, 60, 0)),
+ (100 + get_level(DISCHARGE_MINION, 500, 0)));
+ return buf;
+}
+
+casting_result demonology_control_demon()
+{
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_CONTROL_DEMON, dir, 50 + get_level_s(CONTROL_DEMON, 250), 0));
+}
+
+const char *demonology_control_demon_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power " FMTs32b,
+ (50 + get_level_s(CONTROL_DEMON, 250)));
+ return buf;
+}
+
+casting_result divination_greater_identify()
+{
+ if (get_check("Cast on yourself?"))
+ {
+ self_knowledge(NULL);
+ }
+ else
+ {
+ identify_fully();
+ }
+ return CAST_OBVIOUS;
+}
+
+casting_result divination_identify()
+{
+ if (get_level_s(IDENTIFY, 50) >= 27)
+ {
+ casting_result result = NO_CAST;
+ result = cplus(result, identify_pack());
+ result = cplus(result, fire_ball(GF_IDENTIFY, 0, 1, get_level_s(IDENTIFY, 3)));
+ return result;
+ }
+ else if (get_level_s(IDENTIFY, 50) >= 17)
+ {
+ casting_result result = NO_CAST;
+ result = cplus(result, identify_pack());
+ result = cplus(result, fire_ball(GF_IDENTIFY, 0, 1, 0));
+ return result;
+ }
+ else if (ident_spell())
+ {
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *divination_identify_info()
+{
+ static char buf[128];
+
+ if (get_level_s(IDENTIFY, 50) >= 27)
+ {
+ sprintf(buf, "rad " FMTs32b, get_level_s(IDENTIFY, 3));
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+casting_result divination_vision()
+{
+ if (get_level_s(VISION, 50) >= 25)
+ {
+ wiz_lite_extra();
+ }
+ else
+ {
+ map_area();
+ }
+ return CAST_OBVIOUS;
+
+}
+
+casting_result divination_sense_hidden()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, detect_traps(15 + get_level(SENSEHIDDEN, 40, 0)));
+ if (get_level_s(SENSEHIDDEN, 50) >= 15)
+ {
+ result = cplus(result, set_tim_invis(10 + randint(20) + get_level_s(SENSEHIDDEN, 40)));
+ }
+
+ return result;
+}
+
+const char *divination_sense_hidden_info()
+{
+ static char buf[128];
+
+ if (get_level_s(SENSEHIDDEN, 50) >= 15)
+ {
+ sprintf(buf,
+ "rad " FMTs32b " dur " FMTs32b "+d20",
+ (15 + get_level_s(SENSEHIDDEN, 40)),
+ (10 + get_level_s(SENSEHIDDEN, 40)));
+ }
+ else
+ {
+ sprintf(buf,
+ "rad " FMTs32b,
+ (15 + get_level_s(SENSEHIDDEN, 40)));
+ }
+
+ return buf;
+}
+
+casting_result divination_reveal_ways()
+{
+ casting_result result = NO_CAST;
+ result = cplus(result, detect_doors(10 + get_level(REVEALWAYS, 40, 0)));
+ result = cplus(result, detect_stairs(10 + get_level(REVEALWAYS, 40, 0)));
+ return result;
+}
+
+const char *divination_reveal_ways_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b,
+ (10 + get_level_s(REVEALWAYS, 40)));
+ return buf;
+}
+
+casting_result divination_sense_monsters()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, detect_monsters_normal(10 + get_level(SENSEMONSTERS, 40, 0)));
+ if (get_level_s(SENSEMONSTERS, 50) >= 30)
+ {
+ result = cplus(result, set_tim_esp(10 + randint(10) + get_level_s(SENSEMONSTERS, 20)));
+ }
+ return result;
+}
+
+const char *divination_sense_monsters_info()
+{
+ static char buf[128];
+
+ if (get_level_s(SENSEMONSTERS, 50) >= 30)
+ {
+ sprintf(buf,
+ "rad " FMTs32b " dur " FMTs32b "+d10",
+ (10 + get_level_s(SENSEMONSTERS, 40)),
+ (10 + get_level_s(SENSEMONSTERS, 20)));
+ }
+ else
+ {
+ sprintf(buf,
+ "rad " FMTs32b,
+ (10 + get_level_s(SENSEMONSTERS, 40)));
+ }
+
+ return buf;
+}
+
+casting_result earth_stone_skin()
+{
+ int type;
+
+ type = 0;
+ if (get_level_s(STONESKIN, 50) >= 25)
+ {
+ type = SHIELD_COUNTER;
+ }
+
+ return cast(set_shield(randint(10) + 10 + get_level_s(STONESKIN, 100),
+ 10 + get_level_s(STONESKIN, 50),
+ type,
+ 2 + get_level_s(STONESKIN, 5),
+ 3 + get_level_s(STONESKIN, 5)));
+}
+
+const char *earth_stone_skin_info()
+{
+ static char buf[128];
+
+ if (get_level_s(STONESKIN, 50) >= 25)
+ {
+ sprintf(buf,
+ "dam " FMTs32b "d" FMTs32b " dur " FMTs32b "+d10 AC " FMTs32b,
+ (2 + get_level_s(STONESKIN, 5)),
+ (3 + get_level_s(STONESKIN, 5)),
+ (10 + get_level_s(STONESKIN, 100)),
+ (10 + get_level_s(STONESKIN, 50)));
+ }
+ else
+ {
+ sprintf(buf,
+ "dur " FMTs32b "+d10 AC " FMTs32b,
+ (10 + get_level_s(STONESKIN, 100)),
+ (10 + get_level_s(STONESKIN, 50)));
+ }
+
+ return buf;
+}
+
+casting_result earth_dig()
+{
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(wall_to_mud(dir));
+}
+
+casting_result earth_stone_prison()
+{
+ int x,y;
+
+ if (get_level_s(STONEPRISON, 50) >= 10)
+ {
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+ }
+ else
+ {
+ y = p_ptr->py;
+ x = p_ptr->px;
+ }
+
+ wall_stone(y, x);
+ return CAST_OBVIOUS;
+}
+
+casting_result earth_strike()
+{
+ int dir, dmg;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ dmg = 50 + get_level_s(STRIKE, 50);
+ if (get_level_s(STRIKE, 50) >= 12)
+ {
+ return cast(fire_ball(GF_FORCE, dir, dmg, 1));
+ }
+ else
+ {
+ return cast(fire_ball(GF_FORCE, dir, dmg, 0));
+ }
+}
+
+const char *earth_strike_info()
+{
+ static char buf[128];
+ int dmg = 50 + get_level_s(STRIKE, 50);
+
+ if (get_level_s(STRIKE, 50) >= 12)
+ {
+ sprintf(buf, "dam %d rad 1", dmg);
+ }
+ else
+ {
+ sprintf(buf, "dam %d", dmg);
+ }
+
+ return buf;
+}
+
+casting_result earth_shake()
+{
+ int x,y;
+
+ if (get_level_s(SHAKE, 50) >= 10)
+ {
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+ }
+ else
+ {
+ x = p_ptr->px;
+ y = p_ptr->py;
+ }
+ earthquake(y, x, 4 + get_level_s(SHAKE, 10));
+ return CAST_OBVIOUS;
+}
+
+const char *earth_shake_info()
+{
+ static char buf[128];
+ sprintf(buf, "rad " FMTs32b, (4 + get_level_s(SHAKE, 10)));
+ return buf;
+}
+
+casting_result eru_see_the_music()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, set_tim_invis(randint(20) + 10 + get_level_s(ERU_SEE, 100)));
+
+ if (get_level_s(ERU_SEE, 50) >= 30)
+ {
+ wiz_lite_extra();
+ result = CAST_OBVIOUS;
+ }
+ else if (get_level_s(ERU_SEE, 50) >= 10)
+ {
+ map_area();
+ result = CAST_OBVIOUS;
+ }
+
+ if (get_level_s(ERU_SEE, 50) >= 20)
+ {
+ result = cplus(result, set_blind(0));
+ }
+
+ return result;
+}
+
+const char *eru_see_the_music_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d20",
+ (10 + get_level_s(ERU_SEE, 100)));
+ return buf;
+}
+
+casting_result eru_listen_to_the_music()
+{
+ casting_result result = NO_CAST;
+
+ if (get_level_s(ERU_LISTEN, 50) >= 30)
+ {
+ result = cplus(result, ident_all());
+ result = cplus(result, identify_pack());
+ }
+ else if (get_level_s(ERU_LISTEN, 50) >= 14)
+ {
+ result = cplus(result, identify_pack());
+ }
+ else
+ {
+ result = cplus(result, ident_spell());
+ }
+
+ return result;
+}
+
+casting_result eru_know_the_music()
+{
+ if (get_level_s(ERU_UNDERSTAND, 50) >= 10)
+ {
+ identify_pack_fully();
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return cast(identify_fully());
+ }
+}
+
+casting_result eru_lay_of_protection()
+{
+ return cast(fire_ball(GF_MAKE_GLYPH, 0, 1, 1 + get_level(ERU_PROT, 2, 0)));
+}
+
+const char *eru_lay_of_protection_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b,
+ (1 + get_level(ERU_PROT, 2, 0)));
+ return buf;
+}
+
+casting_result fire_globe_of_light()
+{
+ casting_result result = NO_CAST;
+
+ if (get_level_s(GLOBELIGHT, 50) >= 3)
+ {
+ result = cplus(result, lite_area(10, 4));
+ }
+ else
+ {
+ lite_room(p_ptr->py, p_ptr->px);
+ result = CAST_OBVIOUS;
+ }
+
+ if (get_level_s(GLOBELIGHT, 50) >= 15)
+ {
+ result = cplus(result,
+ fire_ball(GF_LITE,
+ 0,
+ 10 + get_level_s(GLOBELIGHT, 100),
+ 5 + get_level_s(GLOBELIGHT, 6)));
+ p_ptr->update |= PU_VIEW;
+ }
+
+ return result;
+}
+
+const char *fire_globe_of_light_info()
+{
+ static char buf[128];
+
+ if (get_level_s(GLOBELIGHT, 50) >= 15)
+ {
+ sprintf(buf, "dam " FMTs32b " rad " FMTs32b,
+ (10 + get_level_s(GLOBELIGHT, 100)),
+ (5 + get_level_s(GLOBELIGHT, 6)));
+ }
+ else
+ {
+ buf[0] = '\0';
+ }
+
+ return buf;
+}
+
+casting_result fire_fireflash()
+{
+ int dir;
+ int type = GF_FIRE;
+
+ if (get_level_s(FIREFLASH, 50) >= 20)
+ {
+ type = GF_HOLY_FIRE;
+ }
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(type, dir,
+ 20 + get_level_s(FIREFLASH, 500),
+ 2 + get_level_s(FIREFLASH, 5)));
+}
+
+const char *fire_fireflash_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " rad " FMTs32b,
+ (20 + get_level_s(FIREFLASH, 500)),
+ (2 + get_level_s(FIREFLASH, 5)));
+ return buf;
+}
+
+casting_result fire_fiery_shield()
+{
+ int type = SHIELD_FIRE;
+ if (get_level_s(FIERYAURA, 50) >= 8)
+ {
+ type = SHIELD_GREAT_FIRE;
+ }
+
+ return cast(set_shield(randint(20) + 10 + get_level_s(FIERYAURA, 70),
+ 10,
+ type,
+ 5 + get_level_s(FIERYAURA, 10),
+ 5 + get_level_s(FIERYAURA, 7)));
+}
+
+const char *fire_fiery_shield_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "d" FMTs32b " dur " FMTs32b "+d20",
+ (5 + get_level_s(FIERYAURA, 15)),
+ (5 + get_level_s(FIERYAURA, 7)),
+ (10 + get_level_s(FIERYAURA, 70)));
+ return buf;
+}
+
+casting_result fire_firewall()
+{
+ int dir;
+ int type = GF_FIRE;
+ if (get_level_s(FIREWALL, 50) >= 6)
+ {
+ type = GF_HELL_FIRE;
+ }
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ fire_wall(type, dir,
+ 40 + get_level_s(FIREWALL, 150),
+ 10 + get_level_s(FIREWALL, 14));
+ return CAST_OBVIOUS;
+}
+
+const char *fire_firewall_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b " dur " FMTs32b,
+ (40 + get_level_s(FIREWALL, 150)),
+ (10 + get_level_s(FIREWALL, 14)));
+ return buf;
+}
+
+object_filter_t const &item_tester_hook_fire_golem()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ TVal(TV_LITE),
+ Or(
+ SVal(SV_LITE_TORCH),
+ SVal(SV_LITE_LANTERN)));
+ return instance;
+}
+
+casting_result fire_golem()
+{
+ /* Can we reconnect ? */
+ if (do_control_reconnect())
+ {
+ msg_print("Control re-established.");
+ return NO_CAST;
+ }
+
+ int item;
+ if (!get_item(&item,
+ "Which light source do you want to use to create the golem?",
+ "You have no light source for the golem",
+ USE_INVEN | USE_EQUIP,
+ item_tester_hook_fire_golem()))
+ {
+ return NO_CAST;
+ }
+
+ /* Destroy the source object */
+ inc_stack_size(item, -1);
+
+ /* Find a place for it */
+ int x, y;
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+
+ /* Summon it */
+ int r_idx = get_fire_golem();
+ m_allow_special[r_idx] = TRUE;
+ int m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_FRIEND);
+ m_allow_special[r_idx] = FALSE;
+
+ /* level it */
+ if (m_idx != 0)
+ {
+ monster_set_level(m_idx, 7 + get_level_s(FIREGOLEM, 70));
+ p_ptr->control = m_idx;
+ m_list[m_idx].mflag |= MFLAG_CONTROL;
+ }
+
+ return CAST_OBVIOUS;
+}
+
+const char *fire_golem_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "golem level " FMTs32b,
+ (7 + get_level_s(FIREGOLEM, 70)));
+ return buf;
+}
+
+casting_result geomancy_call_the_elements()
+{
+ int dir = 0;
+
+ if (get_level_s(CALL_THE_ELEMENTS, 50) >= 17)
+ {
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+ }
+
+ fire_ball(GF_ELEMENTAL_GROWTH,
+ dir,
+ 1,
+ 1 + get_level(CALL_THE_ELEMENTS, 5, 0));
+
+ return CAST_OBVIOUS;
+}
+
+const char *geomancy_call_the_elements_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b,
+ (1 + get_level(CALL_THE_ELEMENTS, 5, 0)));
+ return buf;
+}
+
+casting_result geomancy_channel_elements()
+{
+ channel_the_elements(p_ptr->py, p_ptr->px, get_level_s(CHANNEL_ELEMENTS, 50));
+ return CAST_OBVIOUS;
+}
+
+typedef struct eff_type eff_type;
+struct eff_type {
+ s16b feat;
+ s16b low_effect;
+ s16b high_effect;
+ int damage;
+};
+
+static eff_type *geomancy_find_effect(eff_type effs[], int feat)
+{
+ int i;
+ for (i = 0; effs[i].feat >= 0; i++)
+ {
+ eff_type *p = &effs[i];
+ if (p->feat == feat)
+ {
+ return p;
+ }
+ }
+ return NULL;
+}
+
+static u32b dir_to_eff_flags(int dir)
+{
+ assert(dir >= 1);
+ assert(dir <= 9);
+
+ switch (dir)
+ {
+ case 1: return EFF_DIR1;
+ case 2: return EFF_DIR2;
+ case 3: return EFF_DIR3;
+ case 4: return EFF_DIR4;
+ case 5: return 0;
+ case 6: return EFF_DIR6;
+ case 7: return EFF_DIR7;
+ case 8: return EFF_DIR8;
+ case 9: return EFF_DIR9;
+ default:
+ assert(FALSE);
+ }
+ /* Default */
+ return 0;
+}
+
+casting_result geomancy_elemental_wave()
+{
+ int dir = 0, y = 0, x = 0;
+ eff_type *eff_ptr = NULL;
+ eff_type t[] =
+ {
+ /* Earth */
+ { 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) },
+
+ /* Water */
+ { 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 */
+ { 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) },
+ { -1, -1, -1, -1 },
+ };
+
+ if (!get_rep_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ y = ddy[dir] + p_ptr->py;
+ x = ddx[dir] + p_ptr->px;
+
+ eff_ptr = geomancy_find_effect(t, cave[y][x].feat);
+
+ if (!eff_ptr)
+ {
+ msg_print("You cannot channel this area.");
+ return NO_CAST;
+ }
+ else
+ {
+ s16b typ = eff_ptr->low_effect;
+ u32b dir_flag = dir_to_eff_flags(dir);
+
+ if (get_level_s(ELEMENTAL_WAVE, 50) >= 20)
+ {
+ typ = eff_ptr->high_effect;
+ }
+
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ fire_wave(typ,
+ 0,
+ eff_ptr->damage,
+ 0,
+ 6 + get_level_s(ELEMENTAL_WAVE, 20),
+ EFF_WAVE + EFF_LAST + dir_flag);
+
+ return CAST_OBVIOUS;
+ }
+}
+
+casting_result geomancy_vaporize()
+{
+ eff_type *eff_ptr = NULL;
+ eff_type 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) },
+ { -1, -1, -1, -1 },
+ };
+
+ eff_ptr = geomancy_find_effect(t, cave[p_ptr->py][p_ptr->px].feat);
+
+ if (!eff_ptr)
+ {
+ msg_print("You cannot channel this area.");
+ return NO_CAST;
+ }
+ else
+ {
+ s16b typ = eff_ptr->low_effect;
+ if (get_level_s(VAPORIZE, 50) >= 20)
+ {
+ typ = eff_ptr->high_effect;
+ }
+
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_FLOOR);
+
+ fire_cloud(typ,
+ 0,
+ eff_ptr->damage,
+ 1 + get_level_s(VAPORIZE, 4),
+ 10 + get_level_s(VAPORIZE, 20));
+
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *geomancy_vaporize_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b " dur " FMTs32b,
+ (1 + get_level_s(VAPORIZE, 4)),
+ (10 + get_level_s(VAPORIZE, 20)));
+ return buf;
+}
+
+bool_ geomancy_vaporize_depends()
+{
+ return get_skill(SKILL_AIR) >= 4;
+}
+
+casting_result geomancy_geolysis()
+{
+ int dir = 0;
+
+ if (!get_rep_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ msg_print("Elements recombine before you, laying down an open path.");
+ geomancy_dig(p_ptr->py, p_ptr->px, dir, 5 + get_level_s(GEOLYSIS, 12));
+
+ return CAST_OBVIOUS;
+}
+
+const char *geomancy_geolysis_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "length " FMTs32b,
+ (5 + get_level_s(GEOLYSIS, 12)));
+ return buf;
+}
+
+bool_ geomancy_geolysis_depends()
+{
+ return get_skill(SKILL_EARTH) >= 7;
+}
+
+casting_result geomancy_dripping_tread()
+{
+ if (p_ptr->dripping_tread == 0)
+ {
+ p_ptr->dripping_tread = randint(15) + 10 + get_level_s(DRIPPING_TREAD, 50);
+ msg_print("You start dripping raw elemental energies.");
+ }
+ else
+ {
+ p_ptr->dripping_tread = 0;
+ msg_print("You stop dripping raw elemental energies.");
+ }
+
+ return CAST_OBVIOUS;
+}
+
+const char *geomancy_dripping_tread_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d15 movs",
+ (10 + get_level_s(DRIPPING_TREAD, 50)));
+ return buf;
+}
+
+bool_ geomancy_dripping_tread_depends()
+{
+ return get_skill(SKILL_WATER) >= 10;
+}
+
+casting_result geomancy_grow_barrier()
+{
+ int dir = 0;
+
+ if (get_level_s(GROW_BARRIER, 50) >= 20)
+ {
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+ }
+
+ fire_ball(GF_ELEMENTAL_WALL, dir, 1, 1);
+ return CAST_OBVIOUS;
+}
+
+bool_ geomancy_grow_barrier_depends()
+{
+ return get_skill(SKILL_EARTH) >= 12;
+}
+
+typedef struct geo_summon geo_summon;
+struct geo_summon {
+ s16b feat;
+ s16b skill_idx;
+ cptr *summon_names;
+};
+
+geo_summon *geomancy_find_summon(geo_summon summons[], int feat)
+{
+ int i;
+ for (i = 0; summons[i].feat >= 0; i++)
+ {
+ geo_summon *summon = &summons[i];
+ if (summon->feat == feat)
+ {
+ return summon;
+ }
+ }
+ return NULL;
+}
+
+int geomancy_count_elements(cptr *elements)
+{
+ int i;
+ for (i = 0; elements[i] != NULL; i++)
+ {
+ }
+ return i;
+}
+
+casting_result geomancy_elemental_minion()
+{
+ int dir = 0;
+ int x = 0, y = 0;
+ geo_summon *summon_ptr = NULL;
+ cptr earth_summons[] = {
+ "Earth elemental",
+ "Xorn",
+ "Xaren",
+ NULL
+ };
+ cptr air_summons[] = {
+ "Air elemental",
+ "Ancient blue dragon",
+ "Great Storm Wyrm",
+ "Sky Drake",
+ NULL
+ };
+ cptr fire_summons[] = {
+ "Fire elemental",
+ "Ancient red dragon",
+ NULL
+ };
+ cptr water_summons[] = {
+ "Water elemental",
+ "Water troll",
+ "Water demon",
+ NULL
+ };
+ geo_summon summons[] = {
+ { FEAT_WALL_EXTRA, SKILL_EARTH, earth_summons },
+ { FEAT_WALL_OUTER, SKILL_EARTH, earth_summons },
+ { FEAT_WALL_INNER, SKILL_EARTH, earth_summons },
+ { FEAT_WALL_SOLID, SKILL_EARTH, earth_summons },
+ { FEAT_MAGMA, SKILL_EARTH, earth_summons },
+ { FEAT_QUARTZ, SKILL_EARTH, earth_summons },
+ { FEAT_MAGMA_H, SKILL_EARTH, earth_summons },
+ { FEAT_QUARTZ_H, SKILL_EARTH, earth_summons },
+ { FEAT_MAGMA_K, SKILL_EARTH, earth_summons },
+ { FEAT_QUARTZ_K, SKILL_EARTH, earth_summons },
+
+ { FEAT_DARK_PIT, SKILL_AIR, air_summons },
+
+ { FEAT_SANDWALL, SKILL_FIRE, fire_summons },
+ { FEAT_SANDWALL_H, SKILL_FIRE, fire_summons },
+ { FEAT_SANDWALL_K, SKILL_FIRE, fire_summons },
+ { FEAT_SHAL_LAVA, SKILL_FIRE, fire_summons },
+ { FEAT_DEEP_LAVA, SKILL_FIRE, fire_summons },
+
+ { FEAT_ICE_WALL, SKILL_WATER, water_summons },
+ { FEAT_SHAL_WATER, SKILL_WATER, water_summons },
+ { FEAT_DEEP_WATER, SKILL_WATER, water_summons },
+
+ { -1, -1, NULL },
+ };
+
+ if (!get_rep_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ y = ddy[dir] + p_ptr->py;
+ x = ddx[dir] + p_ptr->px;
+
+ summon_ptr = geomancy_find_summon(summons, cave[y][x].feat);
+
+ if (!summon_ptr)
+ {
+ msg_print("You cannot summon from this area.");
+ return NO_CAST;
+ }
+ else
+ {
+ cptr *names = summon_ptr->summon_names;
+ int max = get_skill_scale(summon_ptr->skill_idx,
+ geomancy_count_elements(names));
+ int r_idx = test_monster_name(names[rand_int(max)]);
+ int mx, my, m_idx;
+
+ /* Summon it */
+ find_position(y, x, &my, &mx);
+ m_idx = place_monster_one(my, mx, r_idx, 0, FALSE, MSTATUS_FRIEND);
+
+ /* Level it */
+ if (m_idx)
+ {
+ monster_set_level(m_idx, 10 + get_level_s(ELEMENTAL_MINION, 120));
+ }
+
+ cave_set_feat(y, x, FEAT_FLOOR);
+
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *geomancy_elemental_minion_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "min level " FMTs32b,
+ (10 + get_level_s(ELEMENTAL_MINION, 120)));
+ return buf;
+}
+
+static void get_manathrust_dam(s16b *num, s16b *sides)
+{
+ *num = 3 + get_level_s(MANATHRUST, 50);
+ *sides = 1 + get_level_s(MANATHRUST, 20);
+}
+
+casting_result mana_manathrust()
+{
+ int dir;
+ s16b num = 0;
+ s16b sides = 0;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ get_manathrust_dam(&num, &sides);
+ return cast(fire_bolt(GF_MANA, dir, damroll(num, sides)));
+}
+
+const char *mana_manathrust_info()
+{
+ s16b num = 0;
+ s16b sides = 0;
+ static char buf[128];
+
+ get_manathrust_dam(&num, &sides);
+ sprintf(buf,
+ "dam " FMTs16b "d" FMTs16b,
+ num,
+ sides);
+ return buf;
+}
+
+casting_result mana_remove_curses()
+{
+ casting_result result = NO_CAST;
+
+ if (get_level_s(DELCURSES, 50) >= 20)
+ {
+ result = cplus(result, remove_all_curse());
+ }
+ else
+ {
+ result = cplus(result, remove_curse());
+ }
+
+ if (result == CAST_OBVIOUS)
+ {
+ msg_print("The curse is broken!");
+ }
+
+ return result;
+}
+
+casting_result mana_elemental_shield()
+{
+ casting_result res = NO_CAST;
+
+ if (p_ptr->oppose_fire == 0)
+ {
+ res = cplus(res, set_oppose_fire(randint(10) + 15 + get_level_s(RESISTS, 50)));
+ }
+
+ if (p_ptr->oppose_cold == 0)
+ {
+ res = cplus(res, set_oppose_cold(randint(10) + 15 + get_level_s(RESISTS, 50)));
+ }
+
+ if (p_ptr->oppose_elec == 0)
+ {
+ res = cplus(res, set_oppose_elec(randint(10) + 15 + get_level_s(RESISTS, 50)));
+ }
+
+ if (p_ptr->oppose_acid == 0)
+ {
+ res = cplus(res, set_oppose_acid(randint(10) + 15 + get_level_s(RESISTS, 50)));
+ }
+
+ return res;
+}
+
+const char *mana_elemental_shield_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10",
+ (15 + get_level_s(RESISTS, 50)));
+ return buf;
+}
+
+casting_result mana_disruption_shield()
+{
+ if (get_level_s(MANASHIELD, 50) >= 5)
+ {
+ if (p_ptr->invuln == 0)
+ {
+ return cast(set_invuln(randint(5) + 3 + get_level_s(MANASHIELD, 10)));
+ }
+ }
+ else if (p_ptr->disrupt_shield == 0)
+ {
+ return cast(set_disrupt_shield(randint(5) + 3 + get_level_s(MANASHIELD, 10)));
+ }
+
+ return NO_CAST;
+}
+
+const char *mana_disruption_shield_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d5",
+ (3 + get_level_s(MANASHIELD, 10)));
+ return buf;
+}
+
+casting_result manwe_wind_shield()
+{
+ casting_result res = NO_CAST;
+ s32b dur = get_level_s(MANWE_SHIELD, 50) + 10 + randint(20);
+
+ res = cplus(res, set_protevil(dur));
+
+ if (get_level_s(MANWE_SHIELD, 50) >= 10)
+ {
+ int type = 0;
+ if (get_level_s(MANWE_SHIELD, 50) >= 20)
+ {
+ type = SHIELD_COUNTER;
+ }
+
+ res = cplus(res,
+ set_shield(dur,
+ get_level_s(MANWE_SHIELD, 30),
+ type,
+ 1 + get_level_s(MANWE_SHIELD, 2),
+ 1 + get_level_s(MANWE_SHIELD, 6)));
+ }
+
+ return res;
+}
+
+const char *manwe_wind_shield_info()
+{
+ static char buf[128];
+
+ sprintf(buf,
+ "dur " FMTs32b "+d20",
+ (get_level_s(MANWE_SHIELD, 50) + 10));
+
+ if (get_level_s(MANWE_SHIELD, 50) >= 10)
+ {
+ char tmp[128];
+ sprintf(tmp, " AC " FMTs32b, get_level_s(MANWE_SHIELD, 30));
+ strcat(buf, tmp);
+ }
+
+ if (get_level_s(MANWE_SHIELD, 50) >= 20)
+ {
+ char tmp[128];
+ sprintf(tmp, " dam " FMTs32b "d" FMTs32b,
+ (1 + get_level_s(MANWE_SHIELD, 2)),
+ (1 + get_level_s(MANWE_SHIELD, 6)));
+ strcat(buf, tmp);
+ }
+
+ return buf;
+}
+
+casting_result manwe_avatar()
+{
+ s16b mimic_idx = resolve_mimic_name("Maia");
+ assert(mimic_idx >= 0);
+
+ return cast(set_mimic(get_level_s(MANWE_AVATAR, 20) + randint(10),
+ mimic_idx,
+ p_ptr->lev));
+}
+
+const char *manwe_avatar_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10",
+ get_level_s(MANWE_AVATAR, 20));
+ return buf;
+}
+
+casting_result manwe_blessing()
+{
+ casting_result res = NO_CAST;
+ s32b dur = get_level_s(MANWE_BLESS, 70) + 30 + randint(40);
+
+ res = cplus(res, set_blessed(dur));
+ res = cplus(res, set_afraid(0));
+ res = cplus(res, set_lite(0));
+
+ if (get_level_s(MANWE_BLESS, 50) >= 10)
+ {
+ res = cplus(res, set_hero(dur));
+ }
+ if (get_level_s(MANWE_BLESS, 50) >= 20)
+ {
+ res = cplus(res, set_shero(dur));
+ }
+ if (get_level_s(MANWE_BLESS, 50) >= 30)
+ {
+ res = cplus(res, set_holy(dur));
+ }
+
+ return res;
+}
+
+const char *manwe_blessing_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d40",
+ get_level_s(MANWE_BLESS, 70) + 30);
+ return buf;
+}
+
+casting_result manwe_call()
+{
+ int y = 0, x = 0, m_idx = -1, r_idx = -1;
+
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+
+ r_idx = test_monster_name("Great eagle");
+ assert(r_idx >= 1);
+
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_FRIEND);
+
+ if (m_idx > 0)
+ {
+ monster_set_level(m_idx, 20 + get_level(MANWE_CALL, 70, 0));
+ return CAST_OBVIOUS;
+ }
+
+ return NO_CAST;
+}
+
+const char *manwe_call_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level " FMTs32b,
+ get_level_s(MANWE_CALL, 70) + 20);
+ return buf;
+}
+
+void do_melkor_curse(int m_idx)
+{
+ assert(m_idx >= 0);
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ if (get_level_s(MELKOR_CURSE, 50) >= 35)
+ {
+ auto const r_ptr = m_ptr->race();
+
+ m_ptr->maxhp = m_ptr->maxhp - r_ptr->hside;
+ if (m_ptr->maxhp < 1)
+ {
+ m_ptr->maxhp = 1;
+ }
+ if (m_ptr->hp > m_ptr->maxhp)
+ {
+ m_ptr->hp = m_ptr->maxhp;
+ }
+
+ p_ptr->redraw |= PR_FRAME;
+ }
+
+ if (get_level_s(MELKOR_CURSE, 50) >= 25)
+ {
+ m_ptr->speed = m_ptr->speed - get_level_s(MELKOR_CURSE, 7);
+ m_ptr->mspeed = m_ptr->mspeed - get_level_s(MELKOR_CURSE, 7);
+
+ if (m_ptr->speed < 70)
+ {
+ m_ptr->speed = 70;
+ }
+
+ if (m_ptr->mspeed < 70)
+ {
+ m_ptr->mspeed = 70;
+ }
+ }
+
+ if (get_level_s(MELKOR_CURSE, 50) >= 15)
+ {
+ m_ptr->ac = m_ptr->ac - get_level_s(MELKOR_CURSE, 50);
+
+ if (m_ptr->ac < -70)
+ {
+ m_ptr->ac = -70;
+ }
+ }
+
+ /* Reduce melee too */
+ {
+ int i;
+ int pow = get_level_s(MELKOR_CURSE, 2);
+
+ for (i = 0; i < 4; i++)
+ {
+ if (m_ptr->blow[i].d_dice <= 0)
+ {
+ break;
+ }
+
+ if (m_ptr->blow[i].d_dice < pow)
+ {
+ pow = m_ptr->blow[i].d_dice;
+ }
+ if (m_ptr->blow[i].d_side < pow)
+ {
+ pow = m_ptr->blow[i].d_side;
+ }
+
+ m_ptr->blow[i].d_dice = m_ptr->blow[i].d_dice - pow;
+ }
+ }
+
+ /* Describe what happened */
+ {
+ char buf[128];
+
+ monster_desc(buf, m_ptr, 0);
+ buf[0] = toupper(buf[0]);
+
+ strcat(buf, " looks weaker.");
+ msg_print(buf);
+ }
+
+ /* wake it */
+ m_ptr->csleep = 0;
+}
+
+casting_result melkor_curse()
+{
+ int dir = 0;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (target_who < 0)
+ {
+ msg_print("You must target a monster.");
+ return NO_CAST;
+ }
+ else
+ {
+ do_melkor_curse(target_who);
+ return CAST_OBVIOUS;
+ }
+}
+
+casting_result melkor_corpse_explosion()
+{
+ return cast(fire_ball(GF_CORPSE_EXPL,
+ 0,
+ 20 + get_level_s(MELKOR_CORPSE_EXPLOSION, 70),
+ 2 + get_level_s(MELKOR_CORPSE_EXPLOSION, 5)));
+}
+
+const char *melkor_corpse_explosion_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "%%",
+ 20 + get_level_s(MELKOR_CORPSE_EXPLOSION, 70));
+ return buf;
+}
+
+casting_result melkor_mind_steal()
+{
+ int dir = 0;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (target_who < 0)
+ {
+ msg_print("You must target a monster.");
+ return NO_CAST;
+ }
+ else
+ {
+ monster_type *m_ptr = &m_list[target_who];
+ int chance = get_level_s(MELKOR_MIND_STEAL, 50);
+
+ char buf[128];
+ monster_desc(buf, m_ptr, 0);
+ buf[0] = toupper(buf[0]);
+
+ auto const r_ptr = m_ptr->race();
+ if ((randint(m_ptr->level) < chance) &&
+ ((r_ptr->flags1 & RF1_UNIQUE) == 0))
+ {
+ p_ptr->control = target_who;
+ m_ptr->mflag |= MFLAG_CONTROL;
+ strcat(buf, " falls under your control.");
+ }
+ else
+ {
+ strcat(buf, " resists.");
+ }
+
+ msg_print(buf);
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *melkor_mind_steal_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "chance 1d(mlvl)<" FMTs32b,
+ get_level_s(MELKOR_MIND_STEAL, 50));
+ return buf;
+}
+
+casting_result meta_recharge()
+{
+ return cast(recharge(60 + get_level_s(RECHARGE, 140)));
+}
+
+const char *meta_recharge_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power " FMTs32b,
+ 60 + get_level_s(RECHARGE, 140));
+ return buf;
+}
+
+static int get_spellbinder_max()
+{
+ int i = get_level_s(SPELLBINDER, 4);
+ if (i > 4)
+ {
+ i = 4;
+ }
+ return i;
+}
+
+casting_result meta_spellbinder()
+{
+ if (p_ptr->spellbinder_num != 0)
+ {
+ struct trigger {
+ int idx;
+ cptr desc;
+ };
+ struct trigger triggers[] = {
+ { SPELLBINDER_HP75, "75% HP", },
+ { SPELLBINDER_HP50, "50% HP", },
+ { SPELLBINDER_HP25, "25% HP", },
+ { -1, NULL, },
+ };
+ int trigger_idx = -1;
+ int i;
+
+ assert(p_ptr->spellbinder_trigger >= 0);
+
+ for (trigger_idx = 0; triggers[trigger_idx].idx >= 0; trigger_idx++)
+ {
+ if (triggers[trigger_idx].idx == p_ptr->spellbinder_trigger)
+ {
+ break;
+ }
+ }
+
+ msg_print("The spellbinder is already active.");
+ msg_format("It will trigger at %s.", triggers[trigger_idx].desc);
+ msg_print("With the spells: ");
+ for (i = 0; i < p_ptr->spellbinder_num; i++)
+ {
+ msg_print(spell_type_name(spell_at(p_ptr->spellbinder[i])));
+ }
+
+ /* Doesn't cost anything */
+ return NO_CAST;
+ }
+ else
+ {
+ char c;
+ int i;
+
+ if (!get_com("Trigger at [a]75% hp [b]50% hp [c]25% hp?", &c))
+ {
+ return NO_CAST;
+ }
+
+ switch (c)
+ {
+ case 'a':
+ p_ptr->spellbinder_trigger = SPELLBINDER_HP75;
+ break;
+ case 'b':
+ p_ptr->spellbinder_trigger = SPELLBINDER_HP50;
+ break;
+ case 'c':
+ p_ptr->spellbinder_trigger = SPELLBINDER_HP25;
+ break;
+ default:
+ return NO_CAST;
+
+ }
+
+ p_ptr->spellbinder_num = get_spellbinder_max();
+ i = p_ptr->spellbinder_num;
+ while (i > 0)
+ {
+ s32b s = get_school_spell("bind", 0);
+ if (s == -1)
+ {
+ p_ptr->spellbinder_trigger = 0;
+ p_ptr->spellbinder_num = 0;
+ return CAST_OBVIOUS;
+ } else {
+ if (spell_type_skill_level(spell_at(s)) > 7 + get_level_s(SPELLBINDER, 35))
+ {
+ msg_format("You are only allowed spells with a base level of " FMTs32b ".", (7 + get_level_s(SPELLBINDER, 35)));
+ return CAST_OBVIOUS;
+ }
+ }
+
+ p_ptr->spellbinder[i] = s;
+ i = i - 1;
+ }
+
+ p_ptr->energy = p_ptr->energy - 3100;
+ msg_print("Spellbinder ready.");
+ return CAST_OBVIOUS;
+ }
+}
+
+const char *meta_spellbinder_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "number %d max level " FMTs32b,
+ get_spellbinder_max(),
+ (7 + get_level_s(SPELLBINDER, 35)));
+ return buf;
+}
+
+casting_result meta_disperse_magic()
+{
+ casting_result res = NO_CAST;
+
+ res = cplus(res, set_blind(0));
+ res = cplus(res, set_lite(0));
+ if (get_level_s(DISPERSEMAGIC, 50) >= 5)
+ {
+ res = cplus(res, set_confused(0));
+ res = cplus(res, set_image(0));
+ }
+ if (get_level_s(DISPERSEMAGIC, 50) >= 10)
+ {
+ res = cplus(res, set_slow(0));
+ res = cplus(res, set_fast(0, 0));
+ res = cplus(res, set_light_speed(0));
+ }
+ if (get_level_s(DISPERSEMAGIC, 50) >= 15)
+ {
+ res = cplus(res, set_stun(0));
+ res = cplus(res, set_cut(0));
+ }
+ if (get_level_s(DISPERSEMAGIC, 50) >= 20)
+ {
+ res = cplus(res, set_hero(0));
+ res = cplus(res, set_shero(0));
+ res = cplus(res, set_blessed(0));
+ res = cplus(res, set_shield(0, 0, 0, 0, 0));
+ res = cplus(res, set_afraid(0));
+ res = cplus(res, set_parasite(0, 0));
+ res = cplus(res, set_mimic(0, 0, 0));
+ }
+ return res;
+}
+
+casting_result meta_tracker()
+{
+ if ((last_teleportation_y < 0) ||
+ (last_teleportation_x < 0))
+ {
+ msg_print("There has not been any teleporatation here.");
+ }
+ else
+ {
+ teleport_player_to(last_teleportation_y, last_teleportation_x);
+ }
+ return CAST_OBVIOUS;
+}
+
+static void stop_inertia_controlled_spell()
+{
+ assert(TIMER_INERTIA_CONTROL != NULL);
+
+ p_ptr->inertia_controlled_spell = -1;
+ TIMER_INERTIA_CONTROL->enabled = FALSE;
+ p_ptr->update = p_ptr->update | PU_MANA;
+}
+
+void meta_inertia_control_hook_birth_objects()
+{
+ stop_inertia_controlled_spell();
+}
+
+casting_result meta_inertia_control()
+{
+ s32b s, difficulty, delay;
+ spell_type *spell;
+
+ if (p_ptr->inertia_controlled_spell != -1)
+ {
+ msg_print("You cancel your inertia flow control.");
+ stop_inertia_controlled_spell();
+ return NO_CAST;
+ }
+
+ s = get_school_spell("control", 0);
+ if (s == -1)
+ {
+ stop_inertia_controlled_spell();
+ return NO_CAST;
+ }
+
+ spell = spell_at(s);
+
+ if (!spell_type_inertia(spell, &difficulty, &delay))
+ {
+ msg_print("This spell inertia flow can not be controlled.");
+ stop_inertia_controlled_spell();
+ return NO_CAST;
+ }
+
+ if (difficulty > get_level_s(INERTIA_CONTROL, 10))
+ {
+ msg_format("This spell inertia flow(" FMTs32b ") is too strong to be controlled by your current spell.", difficulty);
+ stop_inertia_controlled_spell();
+ return NO_CAST;
+ }
+
+ p_ptr->inertia_controlled_spell = s;
+ TIMER_INERTIA_CONTROL->enabled = TRUE;
+ TIMER_INERTIA_CONTROL->delay = delay;
+ TIMER_INERTIA_CONTROL->countdown = delay;
+ p_ptr->update |= PU_MANA;
+ msg_format("Inertia flow controlling spell %s.", spell_type_name(spell_at(s)));
+ return CAST_OBVIOUS;
+}
+
+const char *meta_inertia_control_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level " FMTs32b,
+ get_level_s(INERTIA_CONTROL, 10));
+ return buf;
+}
+
+void meta_inertia_control_timer_callback()
+{
+ /* Don't cast a controlled spell in wilderness mode */
+ if (p_ptr->antimagic)
+ {
+ msg_print("Your anti-magic field disrupts any magic attempts.");
+ }
+ else if (p_ptr->anti_magic)
+ {
+ msg_print("Your anti-magic shell disrupts any magic attempts.");
+ }
+ else if ((p_ptr->inertia_controlled_spell != -1) &&
+ (!p_ptr->wild_mode))
+ {
+ lua_cast_school_spell(p_ptr->inertia_controlled_spell, TRUE);
+ }
+}
+
+void meta_inertia_control_calc_mana(int *msp)
+{
+ if (p_ptr->inertia_controlled_spell != -1)
+ {
+ *msp = *msp - (get_mana(p_ptr->inertia_controlled_spell) * 4);
+ if (*msp < 0)
+ {
+ *msp = 0;
+ }
+ }
+}
+
+static int mind_charm_power()
+{
+ return 10 + get_level_s(CHARM, 150);
+}
+
+casting_result mind_charm()
+{
+ int pwr = mind_charm_power();
+ int level = get_level_s(CHARM, 50);
+
+ if (level >= 35)
+ {
+ return cast(project_hack(GF_CHARM, pwr));
+ }
+ else
+ {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (level >= 15)
+ {
+ return cast(fire_ball(GF_CHARM, dir, pwr, 3));
+ }
+ else
+ {
+ return cast(fire_bolt(GF_CHARM, dir, pwr));
+ }
+ }
+}
+
+const char *mind_charm_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ mind_charm_power());
+ return buf;
+}
+
+static int mind_confuse_power()
+{
+ return 10 + get_level_s(CONFUSE, 150);
+}
+
+casting_result mind_confuse()
+{
+ int pwr = mind_confuse_power();
+ int level = get_level_s(CONFUSE, 50);
+
+ if (level >= 35)
+ {
+ return cast(project_hack(GF_OLD_CONF, pwr));
+ }
+ else
+ {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (level >= 15)
+ {
+ return cast(fire_ball(GF_OLD_CONF, dir, pwr, 3));
+ }
+ else
+ {
+ return cast(fire_bolt(GF_OLD_CONF, dir, pwr));
+ }
+ }
+}
+
+const char *mind_confuse_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ mind_confuse_power());
+ return buf;
+}
+
+static int mind_armor_of_fear_base_duration()
+{
+ return 10 + get_level_s(ARMOROFFEAR, 100);
+}
+
+static int mind_armor_of_fear_power_sides()
+{
+ return 1 + get_level_s(ARMOROFFEAR, 7);
+}
+
+static int mind_armor_of_fear_power_dice()
+{
+ return 5 + get_level_s(ARMOROFFEAR, 20);
+}
+
+casting_result mind_armor_of_fear()
+{
+ return cast(set_shield(randint(10) + mind_armor_of_fear_base_duration(),
+ 10,
+ SHIELD_FEAR,
+ mind_armor_of_fear_power_sides(),
+ mind_armor_of_fear_power_dice()));
+}
+
+const char *mind_armor_of_fear_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d10 power %dd%d",
+ mind_armor_of_fear_base_duration(),
+ mind_armor_of_fear_power_sides(),
+ mind_armor_of_fear_power_dice());
+ return buf;
+}
+
+static int mind_stun_power()
+{
+ return 10 + get_level_s(STUN, 150);
+}
+
+casting_result mind_stun()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ if (get_level_s(STUN, 50) >= 20)
+ {
+ return cast(fire_ball(GF_STUN, dir, mind_stun_power(), 3));
+ }
+ else
+ {
+ return cast(fire_bolt(GF_STUN, dir, mind_stun_power()));
+ }
+}
+
+const char *mind_stun_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ mind_stun_power());
+ return buf;
+}
+
+casting_result tempo_magelock()
+{
+ if (get_level_s(MAGELOCK, 50) >= 30)
+ {
+ int x,y;
+
+ if (get_level_s(MAGELOCK, 50) >= 40)
+ {
+ cave_type *c_ptr = NULL;
+
+ if (!tgt_pt(&x, &y))
+ {
+ return NO_CAST;
+ }
+
+ c_ptr = &cave[y][x];
+
+ if ((!(f_info[c_ptr->feat].flags1 | FF1_FLOOR)) ||
+ (f_info[c_ptr->feat].flags1 | FF1_PERMANENT) ||
+ (!los(p_ptr->py, p_ptr->px, y, x)))
+ {
+ msg_print("You cannot place it there.");
+ return NO_CAST;
+ }
+ } else {
+ y = p_ptr->py;
+ x = p_ptr->px;
+ }
+ cave_set_feat(y, x, 3);
+ return CAST_OBVIOUS;
+ } else {
+ int dir;
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+ return cast(wizard_lock(dir));
+ }
+}
+
+static s32b tempo_slow_monster_power()
+{
+ return 40 + get_level_s(SLOWMONSTER, 160);
+}
+
+casting_result tempo_slow_monster()
+{
+ int dir;
+ s32b pwr;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ pwr = tempo_slow_monster_power();
+ if (get_level_s(SLOWMONSTER, 50) >= 20)
+ {
+ return cast(fire_ball(GF_OLD_SLOW, dir, pwr, 1));
+ }
+ else
+ {
+ return cast(fire_bolt(GF_OLD_SLOW, dir, pwr));
+ }
+}
+
+const char *tempo_slow_monster_info()
+{
+ static char buf[128];
+ s32b pwr = tempo_slow_monster_power();
+
+ if (get_level_s(SLOWMONSTER, 50) >= 20)
+ {
+ sprintf(buf, "power " FMTs32b " rad 1", pwr);
+ }
+ else
+ {
+ sprintf(buf, "power " FMTs32b, pwr);
+ }
+ return buf;
+}
+
+static s32b tempo_essence_of_speed_base_duration()
+{
+ return 10 + get_level_s(ESSENCESPEED, 50);
+}
+
+static s32b tempo_essence_of_speed_bonus()
+{
+ return 5 + get_level_s(ESSENCESPEED, 20);
+}
+
+casting_result tempo_essence_of_speed()
+{
+ if (p_ptr->fast == 0)
+ {
+ return cast(set_fast(randint(10) + tempo_essence_of_speed_base_duration(),
+ tempo_essence_of_speed_bonus()));
+ }
+ return NO_CAST;
+}
+
+const char *tempo_essence_of_speed_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10 speed " FMTs32b,
+ tempo_essence_of_speed_base_duration(),
+ tempo_essence_of_speed_bonus());
+ return buf;
+}
+
+static s32b tempo_banishment_power()
+{
+ return 40 + get_level_s(BANISHMENT, 160);
+}
+
+casting_result tempo_banishment()
+{
+ casting_result result = NO_CAST;
+ s32b pwr = tempo_banishment_power();
+
+ result = cplus(result, project_hack(GF_AWAY_ALL, pwr));
+
+ if (get_level_s(BANISHMENT, 50) >= 15)
+ {
+ result = cplus(result,
+ project_hack(GF_STASIS, 20 + get_level_s(BANISHMENT, 120)));
+ }
+
+ return result;
+}
+
+const char *tempo_banishment_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power " FMTs32b,
+ tempo_banishment_power());
+ return buf;
+}
+
+casting_result tulkas_divine_aim()
+{
+ casting_result result = NO_CAST;
+ s32b dur = get_level_s(TULKAS_AIM, 50) + randint(10);
+
+ result = cplus(result, set_strike(dur));
+ if (get_level_s(TULKAS_AIM, 50) >= 20)
+ {
+ result = cplus(result, set_tim_deadly(dur));
+ }
+
+ return result;
+}
+
+const char *tulkas_divine_aim_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur " FMTs32b "+d10",
+ get_level_s(TULKAS_AIM, 50));
+ return buf;
+}
+
+casting_result tulkas_wave_of_power()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_bolt(GF_ATTACK, dir, get_level_s(TULKAS_WAVE, p_ptr->num_blow)));
+}
+
+const char *tulkas_wave_of_power_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "blows " FMTs32b,
+ get_level_s(TULKAS_WAVE, p_ptr->num_blow));
+ return buf;
+}
+
+casting_result tulkas_whirlwind()
+{
+ return cast(fire_ball(GF_ATTACK, 0, 1, 1));
+}
+
+/* Return the number of Udun/Melkor spells in a given book */
+int udun_in_book(s32b sval, s32b pval)
+{
+ int count = 0;
+
+ random_book_setup(sval, pval);
+
+ /* Get the school book */
+ school_book *school_book = school_books_at(sval);
+
+ /* Go through spells */
+ for (auto spell_idx : school_book->spell_idxs) {
+ spell_type *spell = spell_at(spell_idx);
+ for (auto school_idx : spell_type_get_schools(spell))
+ {
+ if ((school_idx == SCHOOL_UDUN) ||
+ (school_idx == SCHOOL_MELKOR))
+ {
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+int levels_in_book(s32b sval, s32b pval)
+{
+ int levels = 0;
+
+ random_book_setup(sval, pval);
+
+ /* Get the school book */
+ school_book *school_book = school_books_at(sval);
+
+ /* Parse all spells */
+ for (auto spell_idx : school_book->spell_idxs)
+ {
+ spell_type *spell = spell_at(spell_idx);
+ levels += spell_type_skill_level(spell);
+ }
+
+ return levels;
+}
+
+static object_filter_t const &udun_object_is_drainable()
+{
+ using namespace object_filter;
+ static auto instance = Or(
+ TVal(TV_WAND),
+ TVal(TV_ROD_MAIN),
+ TVal(TV_STAFF));
+ return instance;
+}
+
+casting_result udun_drain()
+{
+ /* Ask for an item */
+ int item;
+ if (!get_item(&item,
+ "What item to drain?",
+ "You have nothing you can drain",
+ USE_INVEN,
+ udun_object_is_drainable()))
+ {
+ return NO_CAST;
+ }
+
+ /* Drain */
+ object_type *o_ptr = get_object(item);
+
+ switch (o_ptr->tval)
+ {
+ case TV_STAFF:
+ case TV_WAND:
+ {
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+
+ /* Generate mana */
+ increase_mana(o_ptr->pval * k_ptr->level * o_ptr->number);
+
+ /* Destroy it */
+ inc_stack_size(item, -99);
+
+ break;
+ }
+
+ case TV_ROD_MAIN:
+ {
+ /* Generate mana */
+ increase_mana(o_ptr->timeout);
+
+ /* Drain it */
+ o_ptr->timeout = 0;
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= PN_COMBINE | PN_REORDER;
+ p_ptr->window |= PW_INVEN | PW_EQUIP | PW_PLAYER;
+ break;
+ }
+
+ default:
+ assert(FALSE);
+ }
+
+ return CAST_OBVIOUS;
+}
+
+casting_result udun_genocide()
+{
+ if (get_level_s(GENOCIDE, 50) < 10)
+ {
+ genocide(TRUE);
+ }
+ else
+ {
+ if (get_check("Genocide all monsters near you? "))
+ {
+ mass_genocide(TRUE);
+ }
+ else
+ {
+ genocide(TRUE);
+ }
+ }
+
+ return CAST_OBVIOUS;
+}
+
+static int udun_wraithform_base_duration()
+{
+ return 20 + get_level_s(WRAITHFORM, 40);
+}
+
+casting_result udun_wraithform()
+{
+ return cast(set_shadow(randint(30) + udun_wraithform_base_duration()));
+}
+
+const char *udun_wraithform_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d30",
+ udun_wraithform_base_duration());
+ return buf;
+}
+
+static int udun_flame_of_udun_base_duration()
+{
+ return 5 + get_level_s(FLAMEOFUDUN, 30);
+}
+
+casting_result udun_flame_of_udun()
+{
+ return cast(set_mimic(randint(15) + udun_flame_of_udun_base_duration(),
+ resolve_mimic_name("Balrog"),
+ get_level_s(FLAMEOFUDUN, 50)));
+}
+
+const char *udun_flame_of_udun_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d15",
+ udun_flame_of_udun_base_duration());
+ return buf;
+}
+
+static int tidal_wave_damage()
+{
+ return 40 + get_level_s(TIDALWAVE, 200);
+}
+
+static int tidal_wave_duration()
+{
+ return 6 + get_level_s(TIDALWAVE, 10);
+}
+
+casting_result water_tidal_wave()
+{
+ fire_wave(GF_WAVE,
+ 0,
+ tidal_wave_damage(),
+ 0,
+ tidal_wave_duration(),
+ EFF_WAVE);
+ return CAST_OBVIOUS;
+}
+
+const char *water_tidal_wave_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d dur %d",
+ tidal_wave_damage(),
+ tidal_wave_duration());
+ return buf;
+}
+
+static int water_ice_storm_damage()
+{
+ return 80 + get_level_s(ICESTORM, 200);
+}
+
+static int water_ice_storm_radius()
+{
+ return 1 + get_level(ICESTORM, 3, 0);
+}
+
+static int water_ice_storm_duration()
+{
+ return 20 + get_level_s(ICESTORM, 70);
+}
+
+casting_result water_ice_storm()
+{
+ int type = GF_COLD;
+
+ if (get_level_s(ICESTORM, 50) >= 10)
+ {
+ type = GF_ICE;
+ }
+
+ fire_wave(type,
+ 0,
+ water_ice_storm_damage(),
+ water_ice_storm_radius(),
+ water_ice_storm_duration(),
+ EFF_STORM);
+
+ return CAST_OBVIOUS;
+}
+
+const char *water_ice_storm_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d rad %d dur %d",
+ water_ice_storm_damage(),
+ water_ice_storm_radius(),
+ water_ice_storm_duration());
+ return buf;
+}
+
+static int water_ent_potion_base_duration()
+{
+ return 25 + get_level_s(ENTPOTION, 40);;
+}
+
+casting_result water_ent_potion()
+{
+ set_food(PY_FOOD_MAX - 1);
+ msg_print("The Ent's Potion fills your stomach.");
+
+ if (get_level_s(ENTPOTION, 50) >= 5)
+ {
+ set_afraid(0);
+ }
+ if (get_level_s(ENTPOTION, 50) >= 12)
+ {
+ set_hero(p_ptr->hero + randint(25) + water_ent_potion_base_duration());
+ }
+
+ return CAST_OBVIOUS;
+}
+
+const char *water_ent_potion_info()
+{
+ if (get_level_s(ENTPOTION, 50) >= 12)
+ {
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d25",
+ water_ent_potion_base_duration());
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+static int water_vapor_damage()
+{
+ return 3 + get_level_s(VAPOR, 20);
+}
+
+static int water_vapor_radius()
+{
+ return 3 + get_level(VAPOR, 9, 0);
+}
+
+static int water_vapor_duration()
+{
+ return 5;
+}
+
+casting_result water_vapor()
+{
+ fire_cloud(GF_WATER,
+ 0,
+ water_vapor_damage(),
+ water_vapor_radius(),
+ water_vapor_duration());
+ return CAST_OBVIOUS;
+}
+
+const char *water_vapor_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d rad %d dur %d",
+ water_vapor_damage(),
+ water_vapor_radius(),
+ water_vapor_duration());
+ return buf;
+}
+
+static void get_geyser_damage(int *dice, int *sides)
+{
+ assert(dice != NULL);
+ assert(sides != NULL);
+
+ *dice = get_level_s(GEYSER, 10);
+ *sides = 3 + get_level_s(GEYSER, 35);
+}
+
+casting_result water_geyser()
+{
+ int dir, dice, sides;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ get_geyser_damage(&dice, &sides);
+ return cast(fire_bolt_or_beam(2 * get_level_s(GEYSER, 85),
+ GF_WATER,
+ dir,
+ damroll(dice, sides)));
+}
+
+const char *water_geyser_info()
+{
+ static char buf[128];
+ int dice, sides;
+
+ get_geyser_damage(&dice, &sides);
+
+ sprintf(buf,
+ "dam %dd%d",
+ dice,
+ sides);
+ return buf;
+}
+
+static int charm_animal_power()
+{
+ return 10 + get_level_s(YAVANNA_CHARM_ANIMAL, 170);
+}
+
+static int charm_animal_radius()
+{
+ return get_level_s(YAVANNA_CHARM_ANIMAL, 2);
+}
+
+casting_result yavanna_charm_animal()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_CONTROL_ANIMAL,
+ dir,
+ charm_animal_power(),
+ charm_animal_radius()));
+}
+
+const char *yavanna_charm_animal_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d rad %d",
+ charm_animal_power(),
+ charm_animal_radius());
+ return buf;
+}
+
+static int yavanna_grow_grass_radius()
+{
+ return get_level_s(YAVANNA_GROW_GRASS, 4);
+}
+
+casting_result yavanna_grow_grass()
+{
+ grow_grass(yavanna_grow_grass_radius());
+ return CAST_OBVIOUS;
+}
+
+const char *yavanna_grow_grass_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad %d",
+ yavanna_grow_grass_radius());
+ return buf;
+}
+
+static int tree_roots_duration()
+{
+ return 10 + get_level_s(YAVANNA_TREE_ROOTS, 30);
+}
+
+static int tree_roots_ac()
+{
+ return 10 + get_level_s(YAVANNA_TREE_ROOTS, 60);
+}
+
+static int tree_roots_damage()
+{
+ return 10 + get_level_s(YAVANNA_TREE_ROOTS, 20);
+}
+
+casting_result yavanna_tree_roots()
+{
+ return cast(set_roots(tree_roots_duration(),
+ tree_roots_ac(),
+ tree_roots_damage()));
+}
+
+const char *yavanna_tree_roots_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d AC %d dam %d",
+ tree_roots_duration(),
+ tree_roots_ac(),
+ tree_roots_damage());
+ return buf;
+}
+
+static int water_bite_base_duration()
+{
+ return 30 + get_level_s(YAVANNA_WATER_BITE, 150);
+}
+
+static int water_bite_damage()
+{
+ return 10 + get_level_s(YAVANNA_WATER_BITE, 50);
+}
+
+casting_result yavanna_water_bite()
+{
+ int rad = 0;
+
+ if (get_level_s(YAVANNA_WATER_BITE, 50) >= 25)
+ {
+ rad = 1;
+ }
+
+ return cast(set_project(randint(30) + water_bite_base_duration(),
+ GF_WATER,
+ water_bite_damage(),
+ rad,
+ PROJECT_STOP | PROJECT_KILL));
+}
+
+const char *yavanna_water_bite_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d30 dam %d/blow",
+ water_bite_base_duration(),
+ water_bite_damage());
+ return buf;
+}
+
+static int uproot_mlevel()
+{
+ return 30 + get_level_s(YAVANNA_UPROOT, 70);
+}
+
+casting_result yavanna_uproot()
+{
+ int dir, x, y;
+ cave_type *c_ptr;
+
+ if (!get_rep_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ y = ddy[dir];
+ x = ddx[dir];
+
+ y += p_ptr->py;
+ x += p_ptr->px;
+
+ c_ptr = &cave[y][x];
+
+ if (c_ptr->feat == FEAT_TREES)
+ {
+ s16b m_idx;
+
+ cave_set_feat(y, x, FEAT_GRASS);
+
+ /* Summon it */
+ find_position(y, x, &y, &x);
+ m_idx = place_monster_one(y, x, test_monster_name("Ent"), 0, FALSE, MSTATUS_FRIEND);
+
+ /* level it */
+ if (m_idx != 0)
+ {
+ monster_set_level(m_idx, uproot_mlevel());
+ }
+
+ msg_print("The tree awakes!");
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ msg_print("There is no tree there.");
+ return NO_CAST;
+ }
+}
+
+const char *yavanna_uproot_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "lev %d",
+ uproot_mlevel());
+ return buf;
+}
+
+static int nature_grow_trees_radius()
+{
+ return 2 + get_level_s(GROWTREE, 7);
+}
+
+casting_result nature_grow_trees()
+{
+ grow_trees(nature_grow_trees_radius());
+ return CAST_OBVIOUS;
+}
+
+const char *nature_grow_trees_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad %d",
+ nature_grow_trees_radius());
+ return buf;
+}
+
+static int nature_healing_percentage()
+{
+ return 15 + get_level_s(HEALING, 35);
+}
+
+static int nature_healing_hp()
+{
+ return p_ptr->mhp * nature_healing_percentage() / 100;
+}
+
+casting_result nature_healing()
+{
+ return cast(hp_player(nature_healing_hp()));
+}
+
+const char *nature_healing_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heal %d%% = %dhp",
+ nature_healing_percentage(),
+ nature_healing_hp());
+ return buf;
+}
+
+casting_result nature_recovery()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, set_poisoned(p_ptr->poisoned / 2));
+ if (get_level_s(RECOVERY, 50) >= 5)
+ {
+ result = cplus(result, set_poisoned(0));
+ result = cplus(result, set_cut(0));
+ }
+ if (get_level_s(RECOVERY, 50) >= 10)
+ {
+ result = cplus(result, do_res_stat(A_STR, TRUE));
+ result = cplus(result, do_res_stat(A_CON, TRUE));
+ result = cplus(result, do_res_stat(A_DEX, TRUE));
+ result = cplus(result, do_res_stat(A_WIS, TRUE));
+ result = cplus(result, do_res_stat(A_INT, TRUE));
+ result = cplus(result, do_res_stat(A_CHR, TRUE));
+ }
+ if (get_level_s(RECOVERY, 50) >= 15)
+ {
+ result = cplus(result, restore_level());
+ }
+
+ return result;
+}
+
+static int regeneration_base_duration()
+{
+ return 5 + get_level_s(REGENERATION, 50);
+}
+
+static int regeneration_power()
+{
+ return 300 + get_level_s(REGENERATION, 700);
+}
+
+casting_result nature_regeneration()
+{
+ if (p_ptr->tim_regen == 0)
+ {
+ return cast(set_tim_regen(randint(10) + regeneration_base_duration(),
+ regeneration_power()));
+ }
+ return NO_CAST;
+}
+
+const char *nature_regeneration_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d+d10 power %d",
+ regeneration_base_duration(),
+ regeneration_power());
+ return buf;
+}
+
+static int summon_animal_level()
+{
+ return 25 + get_level_s(SUMMONANNIMAL, 50);
+}
+
+casting_result nature_summon_animal()
+{
+ summon_specific_level = summon_animal_level();
+ return cast(summon_specific_friendly(p_ptr->py,
+ p_ptr->px,
+ dun_level,
+ SUMMON_ANIMAL,
+ TRUE));
+}
+
+const char *nature_summon_animal_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level %d",
+ summon_animal_level());
+ return buf;
+}
+
+casting_result nature_grow_athelas()
+{
+ if (p_ptr->black_breath)
+ {
+ msg_print("The hold of the Black Breath on you is broken!");
+ p_ptr->black_breath = FALSE;
+ return CAST_OBVIOUS;
+ }
+
+ return CAST_HIDDEN;
+}
+
+static int device_heal_monster_hp()
+{
+ return 20 + get_level_s(DEVICE_HEAL_MONSTER, 380);
+}
+
+casting_result device_heal_monster()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_OLD_HEAL, dir, device_heal_monster_hp(), 0));
+}
+
+const char *device_heal_monster_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heal %d",
+ device_heal_monster_hp());
+ return buf;
+}
+
+casting_result device_haste_monster()
+{
+ int dir;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ return cast(fire_ball(GF_OLD_SPEED, dir, 1, 0));
+}
+
+const char *device_haste_monster_info()
+{
+ return "speed +10";
+}
+
+casting_result device_wish()
+{
+ make_wish();
+ return CAST_OBVIOUS;
+}
+
+casting_result device_summon_monster()
+{
+ casting_result result = NO_CAST;
+ int i;
+
+ for (i = 0; i < 4 + get_level_s(DEVICE_SUMMON, 30); i++)
+ {
+ result = cplus(result, summon_specific(p_ptr->py, p_ptr->px, dun_level, 0));
+ }
+
+ return result;
+}
+
+static int device_mana_pct()
+{
+ return 20 + get_level_s(DEVICE_MANA, 50);
+}
+
+casting_result device_mana()
+{
+ increase_mana((p_ptr->msp * device_mana_pct()) / 100);
+ return CAST_OBVIOUS;
+}
+
+const char *device_mana_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "restore %d%%",
+ device_mana_pct());
+ return buf;
+}
+
+casting_result device_nothing()
+{
+ return CAST_HIDDEN;
+}
+
+static int holy_fire_damage()
+{
+ return 50 + get_level_s(DEVICE_HOLY_FIRE, 300);
+}
+
+casting_result device_holy_fire()
+{
+ return cast(project_hack(GF_HOLY_FIRE, holy_fire_damage()));
+}
+
+const char *device_holy_fire_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d",
+ holy_fire_damage());
+ return buf;
+}
+
+casting_result device_thunderlords()
+{
+ switch (game_module_idx)
+ {
+ case MODULE_TOME:
+ {
+ if (dun_level > 0)
+ {
+ 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.");
+ }
+ return CAST_OBVIOUS;
+ }
+
+ case MODULE_THEME:
+ {
+ if (dun_level > 0)
+ {
+ msg_print("As you blow the horn, an Eagle of Manwe appears overhead.");
+ recall_player(0, 1);
+ }
+ else
+ {
+ msg_print("You cannot use it there.");
+ }
+ return CAST_OBVIOUS;
+ }
+
+ default:
+ assert(FALSE);
+ return NO_CAST;
+ }
+}
+
+void static start_lasting_spell(int spl)
+{
+ p_ptr->music_extra = -spl;
+}
+
+casting_result music_stop_singing_spell()
+{
+ start_lasting_spell(0);
+ return CAST_OBVIOUS;
+}
+
+static int holding_pattern_power()
+{
+ return 10 + get_level_s(MUSIC_HOLD, 100);
+}
+
+int music_holding_pattern_lasting()
+{
+ project_hack(GF_OLD_SLOW, holding_pattern_power());
+ return get_mana(MUSIC_HOLD);
+}
+
+casting_result music_holding_pattern_spell()
+{
+ start_lasting_spell(MUSIC_HOLD);
+ return CAST_OBVIOUS;
+}
+
+const char *music_holding_pattern_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ holding_pattern_power());
+ return buf;
+}
+
+static int illusion_pattern_power()
+{
+ return 10 + get_level_s(MUSIC_CONF, 100);
+}
+
+int music_illusion_pattern_lasting()
+{
+ project_hack(GF_OLD_CONF, illusion_pattern_power());
+ return get_mana(MUSIC_CONF);
+}
+
+casting_result music_illusion_pattern_spell()
+{
+ start_lasting_spell(MUSIC_CONF);
+ return CAST_OBVIOUS;
+}
+
+const char *music_illusion_pattern_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ illusion_pattern_power());
+ return buf;
+}
+
+static int stun_pattern_power()
+{
+ return 10 + get_level_s(MUSIC_STUN, 90);
+}
+
+int music_stun_pattern_lasting()
+{
+ project_hack(GF_STUN, stun_pattern_power());
+ return get_mana(MUSIC_STUN);
+}
+
+casting_result music_stun_pattern_spell()
+{
+ start_lasting_spell(MUSIC_STUN);
+ return CAST_OBVIOUS;
+}
+
+const char *music_stun_pattern_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "power %d",
+ stun_pattern_power());
+ return buf;
+}
+
+int music_song_of_the_sun_lasting()
+{
+ set_lite(5);
+ return 1;
+}
+
+casting_result music_song_of_the_sun_spell()
+{
+ start_lasting_spell(MUSIC_LITE);
+ return CAST_OBVIOUS;
+}
+
+int flow_of_life_hp()
+{
+ return 7 + get_level_s(MUSIC_HEAL, 100);
+}
+
+int music_flow_of_life_lasting()
+{
+ hp_player(flow_of_life_hp());
+ return get_mana(MUSIC_HEAL);
+}
+
+casting_result music_flow_of_life_spell()
+{
+ start_lasting_spell(MUSIC_HEAL);
+ return CAST_OBVIOUS;
+}
+
+const char *music_flow_of_life_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heal %d/turn",
+ flow_of_life_hp());
+ return buf;
+}
+
+int music_heroic_ballad_lasting()
+{
+ set_hero(5);
+ if (get_level_s(MUSIC_HERO, 50) >= 10)
+ {
+ set_shero(5);
+ }
+ if (get_level_s(MUSIC_HERO, 50) >= 20)
+ {
+ set_strike(5);
+ }
+ if (get_level_s(MUSIC_HERO, 50) >= 25)
+ {
+ set_oppose_cc(5);
+ }
+ return get_mana(MUSIC_HERO);
+}
+
+casting_result music_heroic_ballad_spell()
+{
+ start_lasting_spell(MUSIC_HERO);
+ return CAST_OBVIOUS;
+}
+
+int music_hobbit_melodies_lasting()
+{
+ set_shield(5, 10 + get_level_s(MUSIC_TIME, 50), 0, 0, 0);
+ if (get_level_s(MUSIC_TIME, 50) >= 15)
+ {
+ set_fast(5, 7 + get_level_s(MUSIC_TIME, 10));
+ }
+ return get_mana(MUSIC_TIME);
+}
+
+casting_result music_hobbit_melodies_spell()
+{
+ start_lasting_spell(MUSIC_TIME);
+ return CAST_OBVIOUS;
+}
+
+const char *music_hobbit_melodies_info()
+{
+ static char buf[128];
+ if (get_level_s(MUSIC_TIME, 50) >= 15)
+ {
+ sprintf(buf, "AC " FMTs32b " speed " FMTs32b,
+ 10 + get_level_s(MUSIC_TIME, 50),
+ 7 + get_level_s(MUSIC_TIME, 10));
+ }
+ else
+ {
+ sprintf(buf, "AC " FMTs32b,
+ 10 + get_level_s(MUSIC_TIME, 50));
+ }
+ return buf;
+}
+
+int music_clairaudience_lasting()
+{
+ set_tim_esp(5);
+ if (get_level_s(MUSIC_MIND, 50) >= 10)
+ {
+ fire_ball(GF_IDENTIFY, 0, 1, 1 + get_level(MUSIC_MIND, 3, 0));
+ }
+ return get_mana(MUSIC_MIND);
+}
+
+casting_result music_clairaudience_spell()
+{
+ start_lasting_spell(MUSIC_MIND);
+ return CAST_OBVIOUS;
+}
+
+const char *music_clairaudience_info()
+{
+ static char buf[128];
+
+ if (get_level_s(MUSIC_MIND, 50) >= 10)
+ {
+ sprintf(buf, "rad " FMTs32b,
+ 1 + get_level(MUSIC_MIND, 3, 0));
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+casting_result music_blow_spell()
+{
+ 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 CAST_OBVIOUS;
+}
+
+const char *music_blow_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam " FMTs32b "d" FMTs32b " rad " FMTs32b,
+ 2 + get_level(MUSIC_BLOW, 10, 0),
+ 4 + get_level(MUSIC_BLOW, 40, 0),
+ 1 + get_level(MUSIC_BLOW, 12, 0));
+ return buf;
+}
+
+casting_result music_gush_of_wind_spell()
+{
+ fire_ball(GF_AWAY_ALL,
+ 0,
+ 10 + get_level(MUSIC_BLOW, 40, 0),
+ 1 + get_level(MUSIC_BLOW, 12, 0));
+ return CAST_OBVIOUS;
+}
+
+const char *music_gush_of_wind_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dist " FMTs32b " rad " FMTs32b,
+ 10 + get_level(MUSIC_BLOW, 40, 0),
+ 1 + get_level(MUSIC_BLOW, 12, 0));
+ return buf;
+}
+
+casting_result music_horns_of_ylmir_spell()
+{
+ earthquake(p_ptr->py, p_ptr->px, 2 + get_level_s(MUSIC_YLMIR, 10));
+ return CAST_OBVIOUS;
+}
+
+const char *music_horns_of_ylmir_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "rad " FMTs32b,
+ 2 + get_level_s(MUSIC_YLMIR, 10));
+ return buf;
+}
+
+casting_result music_ambarkanta_spell()
+{
+ alter_reality();
+ return CAST_OBVIOUS;
+}
+
+casting_result aule_firebrand_spell()
+{
+ int rad = 0;
+ int type = GF_FIRE;
+ s32b level = get_level_s(AULE_FIREBRAND, 50);
+
+ if (level > 30)
+ {
+ type = GF_HOLY_FIRE;
+ }
+
+ if (level >= 15)
+ {
+ rad = 1;
+ }
+
+ return cast(set_project(level + randint(20),
+ type,
+ 4 + level,
+ rad,
+ PROJECT_STOP | PROJECT_KILL));
+}
+
+const char *aule_firebrand_info()
+{
+ s32b level = get_level_s(AULE_FIREBRAND, 50);
+ static char buf[128];
+
+ sprintf(buf,
+ "dur " FMTs32b "+d20 dam " FMTs32b "/blow",
+ level,
+ 4 + level);
+ return buf;
+}
+
+static object_filter_t const &aule_enchant_weapon_item_tester()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ // Cannot enchant artifacts, spell is probably already too overpowered.
+ Not(IsArtifact()),
+ // Only weapons which Aule likes
+ Or(
+ TVal(TV_MSTAFF),
+ TVal(TV_BOW),
+ TVal(TV_HAFTED),
+ TVal(TV_POLEARM),
+ TVal(TV_SWORD),
+ TVal(TV_AXE)));
+ return instance;
+}
+
+casting_result aule_enchant_weapon_spell()
+{
+ s32b level = get_level_s(AULE_ENCHANT_WEAPON, 50);
+ s16b num_h, num_d, num_p;
+
+ num_h = 1 + randint(level/12);
+ num_d = 0;
+ num_p = 0;
+
+ if (level >= 5)
+ {
+ num_d = 1 + randint(level/12);
+ }
+ if (level >= 45)
+ {
+ num_p = 1;
+ }
+
+ int item;
+ if (!get_item(&item,
+ "Which object do you want to enchant?",
+ "You have no objects to enchant.",
+ USE_INVEN,
+ aule_enchant_weapon_item_tester()))
+ {
+ return NO_CAST;
+ }
+
+ object_type *o_ptr = get_object(item);
+
+ o_ptr->to_h = o_ptr->to_h + num_h;
+ o_ptr->to_d = o_ptr->to_d + num_d;
+ o_ptr->pval = o_ptr->pval + num_p;
+
+ return CAST_OBVIOUS;
+}
+
+const char *aule_enchant_weapon_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "tries " FMTs32b,
+ 1 + get_level_s(AULE_ENCHANT_WEAPON, 50)/12);
+ return buf;
+}
+
+static object_filter_t const &aule_enchant_armor_item_tester()
+{
+ using namespace object_filter;
+ static auto instance = And(
+ // No enchanting artifacts; the spell is already horribly
+ // overpowered.
+ Not(IsArtifact()),
+ // Only armor-like things can be enchanted
+ Or(
+ TVal(TV_BOOTS),
+ TVal(TV_GLOVES),
+ TVal(TV_HELM),
+ TVal(TV_CROWN),
+ TVal(TV_SHIELD),
+ TVal(TV_CLOAK),
+ TVal(TV_SOFT_ARMOR),
+ TVal(TV_HARD_ARMOR),
+ TVal(TV_DRAG_ARMOR)));
+ return instance;
+}
+
+casting_result aule_enchant_armour_spell()
+{
+ s32b level = get_level_s(AULE_ENCHANT_ARMOUR, 50);
+ s16b num_h, num_d, num_a, num_p;
+ int item;
+
+ num_a = 1 + randint(level/10);
+ num_h = 0;
+ num_d = 0;
+ num_p = 0;
+ if (level >= 20)
+ {
+ num_h = 1;
+ num_d = 1;
+ }
+ if (level >= 40)
+ {
+ num_p = 1;
+ }
+
+ if (!get_item(&item,
+ "Which object do you want to enchant?",
+ "You have no objects to enchant.",
+ USE_INVEN,
+ aule_enchant_armor_item_tester()))
+ {
+ return NO_CAST;
+ }
+
+ object_type *o_ptr = get_object(item);
+
+ o_ptr->to_h = o_ptr->to_h + num_h;
+ o_ptr->to_d = o_ptr->to_d + num_d;
+ o_ptr->pval = o_ptr->pval + num_p;
+ o_ptr->to_a = o_ptr->to_a + num_a;
+
+ return CAST_OBVIOUS;
+}
+
+const char *aule_enchant_armour_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "tries " FMTs32b,
+ 1 + get_level_s(AULE_ENCHANT_ARMOUR, 50)/10);
+ return buf;
+}
+
+casting_result aule_child_spell()
+{
+ int y, x;
+ s16b m_idx;
+
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+ m_idx = place_monster_one(y, x, test_monster_name("Dwarven warrior"),
+ 0, FALSE, MSTATUS_FRIEND);
+
+ if (m_idx)
+ {
+ monster_set_level(m_idx, 20 + get_level(AULE_CHILD, 70, 0));
+ return CAST_OBVIOUS;
+ }
+ else
+ {
+ return NO_CAST;
+ }
+}
+
+const char *aule_child_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level " FMTs32b,
+ 20 + get_level_s(AULE_CHILD, 70));
+ return buf;
+}
+
+static int tears_of_luthien_hp()
+{
+ return 10 * get_level_s(MANDOS_TEARS_LUTHIEN, 30);
+}
+
+casting_result mandos_tears_of_luthien_spell()
+{
+ casting_result result = NO_CAST;
+
+ result = cplus(result, hp_player(tears_of_luthien_hp()));
+ result = cplus(result, set_stun(0));
+ result = cplus(result, set_cut(0));
+ result = cplus(result, set_afraid(0));
+
+ return result;
+}
+
+const char *mandos_tears_of_luthien_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "heals %d",
+ tears_of_luthien_hp());
+ return buf;
+}
+
+casting_result mandos_spirit_of_the_feanturi_spell()
+{
+ casting_result result = NO_CAST;
+ s32b level = get_level_s(MANDOS_SPIRIT_FEANTURI, 50);
+
+ result = cplus(result, set_afraid(0));
+ result = cplus(result, set_confused(0));
+
+ if (level >= 20)
+ {
+ result = cplus(result, do_res_stat(A_WIS, TRUE));
+ result = cplus(result, do_res_stat(A_INT, TRUE));
+ }
+
+ if (level >= 30)
+ {
+ result = cplus(result, set_image(0));
+ result = cplus(result, heal_insanity(p_ptr->msane * level / 100));
+ }
+
+ return result;
+}
+
+const char *mandos_spirit_of_the_feanturi_info()
+{
+ static char buf[128];
+ s32b level = get_level_s(MANDOS_SPIRIT_FEANTURI, 50) ;
+ if (level >= 20)
+ {
+ sprintf(buf, "heals " FMTs32b "%%", level);
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+static int tale_of_doom_duration()
+{
+ return 5 + get_level_s(MANDOS_TALE_DOOM,10);
+}
+
+casting_result mandos_tale_of_doom_spell()
+{
+ return cast(set_tim_precognition(tale_of_doom_duration()));
+}
+
+const char *mandos_tale_of_doom_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dur %d",
+ tale_of_doom_duration());
+ return buf;
+}
+
+int call_to_the_halls_mlev()
+{
+ return 20 + get_level(MANDOS_CALL_HALLS, 70, 0);
+}
+
+casting_result mandos_call_to_the_halls_spell()
+{
+ int y, x;
+ s16b m_idx;
+ std::vector<int> summons {
+ test_monster_name("Experienced spirit"),
+ test_monster_name("Wise spirit")
+ };
+
+ int r_idx = summons[rand_int(summons.size())];
+ assert(r_idx >= 0);
+
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_FRIEND);
+ if (m_idx)
+ {
+ monster_set_level(m_idx, call_to_the_halls_mlev());
+ return CAST_OBVIOUS;
+ }
+ return NO_CAST;
+}
+
+const char *mandos_call_to_the_halls_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level %d",
+ call_to_the_halls_mlev());
+ return buf;
+}
+
+static void get_belegaer_damage(int *dice, int *sides)
+{
+ *dice = get_level_s(ULMO_BELEGAER, 10);
+ *sides = 3 + get_level_s(ULMO_BELEGAER, 35);
+}
+
+casting_result ulmo_song_of_belegaer_spell()
+{
+ int dir, dice, sides;
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ get_belegaer_damage(&dice, &sides);
+ return cast(fire_bolt_or_beam(2 * get_level_s(ULMO_BELEGAER, 85),
+ GF_WATER,
+ dir,
+ damroll(dice, sides)));
+}
+
+const char *ulmo_song_of_belegaer_info()
+{
+ static char buf[128];
+ int dice, sides;
+
+ get_belegaer_damage(&dice, &sides);
+ sprintf(buf,
+ "dam %dd%d",
+ dice,
+ sides);
+ return buf;
+}
+
+int draught_of_ulmonan_hp()
+{
+ return 5 * get_level_s(ULMO_DRAUGHT_ULMONAN, 50);
+}
+
+casting_result ulmo_draught_of_ulmonan_spell()
+{
+ casting_result result = NO_CAST;
+ s32b level = get_level_s(ULMO_DRAUGHT_ULMONAN, 50);
+
+ result = cplus(result, hp_player(draught_of_ulmonan_hp()));
+
+ result = cplus(result, set_poisoned(0));
+ result = cplus(result, set_cut(0));
+ result = cplus(result, set_stun(0));
+ result = cplus(result, set_blind(0));
+
+ if (level >= 10)
+ {
+ result = cplus(result, do_res_stat(A_STR, TRUE));
+ result = cplus(result, do_res_stat(A_CON, TRUE));
+ result = cplus(result, do_res_stat(A_DEX, TRUE));
+ }
+
+ if (level >= 20)
+ {
+ result = cplus(result, set_parasite(0, 0));
+ result = cplus(result, set_mimic(0, 0, 0));
+ }
+
+ return result;
+}
+
+const char *ulmo_draught_of_ulmonan_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "cure %d",
+ draught_of_ulmonan_hp());
+ return buf;
+}
+
+static int call_of_the_ulumuri_mlev()
+{
+ return 30 + get_level(ULMO_CALL_ULUMURI, 70, 0);
+}
+
+casting_result ulmo_call_of_the_ulumuri_spell()
+{
+ int x,y;
+ s16b m_idx;
+ std::vector<int> summons {
+ test_monster_name("Water spirit"),
+ test_monster_name("Water elemental")
+ };
+
+ int r_idx = summons[rand_int(summons.size())];
+ assert(r_idx >= 0);
+
+ find_position(p_ptr->py, p_ptr->px, &y, &x);
+
+ m_idx = place_monster_one(y, x, r_idx, 0, FALSE, MSTATUS_FRIEND);
+ if (m_idx)
+ {
+ monster_set_level(m_idx, call_of_the_ulumuri_mlev());
+ return CAST_OBVIOUS;
+ }
+
+ return NO_CAST;
+}
+
+const char *ulmo_call_of_the_ulumuri_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "level %d",
+ call_of_the_ulumuri_mlev());
+ return buf;
+}
+
+static int wrath_of_ulmo_damage()
+{
+ return 40 + get_level_s(ULMO_WRATH, 150);
+}
+
+static int wrath_of_ulmo_duration()
+{
+ return 10 + get_level_s(ULMO_WRATH, 14);
+}
+
+casting_result ulmo_wrath_of_ulmo_spell()
+{
+ int dir, type = GF_WATER;
+
+ if (get_level_s(ULMO_WRATH, 50) >= 30)
+ {
+ type = GF_WAVE;
+ }
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ fire_wall(type,
+ dir,
+ wrath_of_ulmo_damage(),
+ wrath_of_ulmo_duration());
+ return CAST_OBVIOUS;
+}
+
+const char *ulmo_wrath_of_ulmo_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d dur %d",
+ wrath_of_ulmo_damage(),
+ wrath_of_ulmo_duration());
+ return buf;
+}
+
+static int light_of_valinor_damage()
+{
+ return 10 + get_level_s(VARDA_LIGHT_VALINOR, 100);
+}
+
+static int light_of_valinor_radius()
+{
+ return 5 + get_level_s(VARDA_LIGHT_VALINOR, 6);
+}
+
+casting_result varda_light_of_valinor_spell()
+{
+ casting_result result = NO_CAST;
+
+ if (get_level_s(VARDA_LIGHT_VALINOR, 50) >= 3)
+ {
+ result = cplus(result, lite_area(10, 4));
+ }
+ else
+ {
+ lite_room(p_ptr->py, p_ptr->px);
+ result = CAST_OBVIOUS;
+ }
+
+ if (get_level_s(VARDA_LIGHT_VALINOR, 50) >= 15)
+ {
+ result = cplus(result,
+ fire_ball(GF_LITE,
+ 0,
+ light_of_valinor_damage(),
+ light_of_valinor_radius()));
+ }
+
+ return result;
+}
+
+const char *varda_light_of_valinor_info()
+{
+ static char buf[128];
+ if (get_level_s(VARDA_LIGHT_VALINOR, 50) >= 15)
+ {
+ sprintf(buf,
+ "dam %d rad %d",
+ light_of_valinor_damage(),
+ light_of_valinor_radius());
+ return buf;
+ }
+ else
+ {
+ return "";
+ }
+}
+
+casting_result varda_call_of_almaren_spell()
+{
+ int power = 5 * p_ptr->lev;
+ if (get_level_s(VARDA_CALL_ALMAREN, 50) >= 20)
+ {
+ dispel_evil(power);
+ }
+ else
+ {
+ banish_evil(power);
+ }
+ return CAST_OBVIOUS;
+}
+
+casting_result varda_evenstar_spell()
+{
+ wiz_lite_extra();
+ if (get_level_s(VARDA_EVENSTAR, 50) >= 40)
+ {
+ identify_pack();
+ self_knowledge(NULL);
+ }
+
+ return CAST_OBVIOUS;
+}
+
+static int star_kindler_bursts()
+{
+ return p_ptr->lev / 5;
+}
+
+static int star_kindler_damage()
+{
+ return 20 + get_level_s(VARDA_STARKINDLER, 100);
+}
+
+casting_result varda_star_kindler_spell()
+{
+ int dir, i, n = star_kindler_bursts();
+
+ if (!get_aim_dir(&dir))
+ {
+ return NO_CAST;
+ }
+
+ for (i = 0; i < n; i++)
+ {
+ fire_ball(GF_LITE,
+ dir,
+ star_kindler_damage(),
+ 10);
+ }
+
+ return CAST_OBVIOUS;
+}
+
+const char *varda_star_kindler_info()
+{
+ static char buf[128];
+ sprintf(buf,
+ "dam %d bursts %d rad 10",
+ star_kindler_damage(),
+ star_kindler_bursts());
+ return buf;
+}
+
diff --git a/src/spells3.hpp b/src/spells3.hpp
new file mode 100644
index 00000000..3380203a
--- /dev/null
+++ b/src/spells3.hpp
@@ -0,0 +1,445 @@
+#pragma once
+
+#include "spell_type_fwd.hpp"
+#include "h-basic.h"
+#include "timer_type_fwd.hpp"
+
+extern s32b NOXIOUSCLOUD;
+extern s32b AIRWINGS;
+extern s32b INVISIBILITY;
+extern s32b POISONBLOOD;
+extern s32b THUNDERSTORM;
+extern s32b STERILIZE;
+
+casting_result air_noxious_cloud();
+const char *air_noxious_cloud_info();
+casting_result air_wings_of_winds();
+const char *air_wings_of_winds_info();
+casting_result air_invisibility();
+const char *air_invisibility_info();
+casting_result air_poison_blood();
+const char *air_poison_blood_info();
+casting_result air_thunderstorm();
+const char *air_thunderstorm_info();
+casting_result air_sterilize();
+const char *air_sterilize_info();
+
+extern s32b BLINK;
+extern s32b DISARM;
+extern s32b TELEPORT;
+extern s32b TELEAWAY;
+extern s32b RECALL;
+extern s32b PROBABILITY_TRAVEL;
+
+casting_result convey_blink();
+const char *convey_blink_info();
+casting_result convey_disarm();
+casting_result convey_teleport();
+const char *convey_teleport_info();
+casting_result convey_teleport_away();
+casting_result convey_recall();
+const char *convey_recall_info();
+casting_result convey_probability_travel();
+const char *convey_probability_travel_info();
+
+extern s32b DEMON_BLADE;
+extern s32b DEMON_MADNESS;
+extern s32b DEMON_FIELD;
+extern s32b DOOM_SHIELD;
+extern s32b UNHOLY_WORD;
+extern s32b DEMON_CLOAK;
+extern s32b DEMON_SUMMON;
+extern s32b DISCHARGE_MINION;
+extern s32b CONTROL_DEMON;
+
+casting_result demonology_demon_blade();
+const char *demonology_demon_blade_info();
+casting_result demonology_demon_madness();
+const char *demonology_demon_madness_info();
+casting_result demonology_demon_field();
+const char *demonology_demon_field_info();
+casting_result demonology_doom_shield();
+const char *demonology_doom_shield_info();
+casting_result demonology_unholy_word();
+const char *demonology_unholy_word_info();
+casting_result demonology_demon_cloak();
+const char *demonology_demon_cloak_info();
+casting_result demonology_summon_demon();
+const char *demonology_summon_demon_info();
+casting_result demonology_discharge_minion();
+const char *demonology_discharge_minion_info();
+casting_result demonology_control_demon();
+const char *demonology_control_demon_info();
+
+extern s32b STARIDENTIFY;
+extern s32b IDENTIFY;
+extern s32b VISION;
+extern s32b SENSEHIDDEN;
+extern s32b REVEALWAYS;
+extern s32b SENSEMONSTERS;
+
+casting_result divination_greater_identify();
+casting_result divination_identify();
+const char *divination_identify_info();
+casting_result divination_vision();
+casting_result divination_sense_hidden();
+const char *divination_sense_hidden_info();
+casting_result divination_reveal_ways();
+const char *divination_reveal_ways_info();
+casting_result divination_sense_monsters();
+const char *divination_sense_monsters_info();
+
+extern s32b STONESKIN;
+extern s32b DIG;
+extern s32b STONEPRISON;
+extern s32b STRIKE;
+extern s32b SHAKE;
+
+casting_result earth_stone_skin();
+const char *earth_stone_skin_info();
+casting_result earth_dig();
+casting_result earth_stone_prison();
+casting_result earth_strike();
+const char *earth_strike_info();
+casting_result earth_shake();
+const char *earth_shake_info();
+
+extern s32b ERU_SEE;
+extern s32b ERU_LISTEN;
+extern s32b ERU_UNDERSTAND;
+extern s32b ERU_PROT;
+
+casting_result eru_see_the_music();
+const char *eru_see_the_music_info();
+casting_result eru_listen_to_the_music();
+casting_result eru_know_the_music();
+casting_result eru_lay_of_protection();
+const char *eru_lay_of_protection_info();
+
+extern s32b GLOBELIGHT;
+extern s32b FIREFLASH;
+extern s32b FIERYAURA;
+extern s32b FIREWALL;
+extern s32b FIREGOLEM;
+
+casting_result fire_globe_of_light();
+const char *fire_globe_of_light_info();
+casting_result fire_fireflash();
+const char *fire_fireflash_info();
+casting_result fire_fiery_shield();
+const char *fire_fiery_shield_info();
+casting_result fire_firewall();
+const char *fire_firewall_info();
+casting_result fire_golem();
+const char *fire_golem_info();
+
+extern s32b CALL_THE_ELEMENTS;
+extern s32b CHANNEL_ELEMENTS;
+extern s32b ELEMENTAL_WAVE;
+extern s32b VAPORIZE;
+extern s32b GEOLYSIS;
+extern s32b DRIPPING_TREAD;
+extern s32b GROW_BARRIER;
+extern s32b ELEMENTAL_MINION;
+
+casting_result geomancy_call_the_elements();
+const char *geomancy_call_the_elements_info();
+casting_result geomancy_channel_elements();
+casting_result geomancy_elemental_wave();
+casting_result geomancy_vaporize();
+const char *geomancy_vaporize_info();
+bool_ geomancy_vaporize_depends();
+casting_result geomancy_geolysis();
+const char *geomancy_geolysis_info();
+bool_ geomancy_geolysis_depends();
+casting_result geomancy_dripping_tread();
+const char *geomancy_dripping_tread_info();
+bool_ geomancy_dripping_tread_depends();
+casting_result geomancy_grow_barrier();
+bool_ geomancy_grow_barrier_depends();
+casting_result geomancy_elemental_minion();
+const char *geomancy_elemental_minion_info();
+
+extern s32b MANATHRUST;
+extern s32b DELCURSES;
+extern s32b RESISTS;
+extern s32b MANASHIELD;
+
+casting_result mana_manathrust();
+const char *mana_manathrust_info();
+casting_result mana_remove_curses();
+casting_result mana_elemental_shield();
+const char *mana_elemental_shield_info();
+casting_result mana_disruption_shield();
+const char *mana_disruption_shield_info();
+
+extern s32b MANWE_SHIELD;
+extern s32b MANWE_AVATAR;
+extern s32b MANWE_BLESS;
+extern s32b MANWE_CALL;
+
+casting_result manwe_wind_shield();
+const char *manwe_wind_shield_info();
+casting_result manwe_avatar();
+const char *manwe_avatar_info();
+casting_result manwe_blessing();
+const char *manwe_blessing_info();
+casting_result manwe_call();
+const char *manwe_call_info();
+
+extern s32b MELKOR_CURSE;
+extern s32b MELKOR_CORPSE_EXPLOSION;
+extern s32b MELKOR_MIND_STEAL;
+
+void do_melkor_curse(int m_idx);
+
+casting_result melkor_curse();
+casting_result melkor_corpse_explosion();
+const char *melkor_corpse_explosion_info();
+casting_result melkor_mind_steal();
+const char *melkor_mind_steal_info();
+
+extern s32b RECHARGE;
+extern s32b SPELLBINDER;
+extern s32b DISPERSEMAGIC;
+extern s32b TRACKER;
+extern s32b INERTIA_CONTROL;
+extern timer_type *TIMER_INERTIA_CONTROL;
+
+casting_result meta_recharge();
+const char *meta_recharge_info();
+casting_result meta_spellbinder();
+const char *meta_spellbinder_info();
+casting_result meta_disperse_magic();
+casting_result meta_tracker();
+casting_result meta_inertia_control();
+const char *meta_inertia_control_info();
+
+void meta_inertia_control_timer_callback();
+void meta_inertia_control_calc_mana(int *msp);
+void meta_inertia_control_hook_birth_objects();
+
+extern s32b CHARM;
+extern s32b CONFUSE;
+extern s32b ARMOROFFEAR;
+extern s32b STUN;
+
+casting_result mind_charm();
+const char *mind_charm_info();
+casting_result mind_confuse();
+const char *mind_confuse_info();
+casting_result mind_armor_of_fear();
+const char *mind_armor_of_fear_info();
+casting_result mind_stun();
+const char *mind_stun_info();
+
+extern s32b MAGELOCK;
+extern s32b SLOWMONSTER;
+extern s32b ESSENCESPEED;
+extern s32b BANISHMENT;
+
+casting_result tempo_magelock();
+casting_result tempo_slow_monster();
+const char *tempo_slow_monster_info();
+casting_result tempo_essence_of_speed();
+const char *tempo_essence_of_speed_info();
+casting_result tempo_banishment();
+const char *tempo_banishment_info();
+
+extern s32b TULKAS_AIM;
+extern s32b TULKAS_WAVE;
+extern s32b TULKAS_SPIN;
+
+casting_result tulkas_divine_aim();
+const char *tulkas_divine_aim_info();
+casting_result tulkas_wave_of_power();
+const char *tulkas_wave_of_power_info();
+casting_result tulkas_whirlwind();
+
+extern s32b DRAIN;
+extern s32b GENOCIDE;
+extern s32b WRAITHFORM;
+extern s32b FLAMEOFUDUN;
+
+int udun_in_book(s32b sval, s32b pval);
+int levels_in_book(s32b sval, s32b pval);
+
+casting_result udun_drain();
+casting_result udun_genocide();
+casting_result udun_wraithform();
+const char *udun_wraithform_info();
+casting_result udun_flame_of_udun();
+const char *udun_flame_of_udun_info();
+
+extern s32b TIDALWAVE;
+extern s32b ICESTORM;
+extern s32b ENTPOTION;
+extern s32b VAPOR;
+extern s32b GEYSER;
+
+casting_result water_tidal_wave();
+const char *water_tidal_wave_info();
+casting_result water_ice_storm();
+const char *water_ice_storm_info();
+casting_result water_ent_potion();
+const char *water_ent_potion_info();
+casting_result water_vapor();
+const char *water_vapor_info();
+casting_result water_geyser();
+const char *water_geyser_info();
+
+extern s32b YAVANNA_CHARM_ANIMAL;
+extern s32b YAVANNA_GROW_GRASS;
+extern s32b YAVANNA_TREE_ROOTS;
+extern s32b YAVANNA_WATER_BITE;
+extern s32b YAVANNA_UPROOT;
+
+casting_result yavanna_charm_animal();
+const char *yavanna_charm_animal_info();
+casting_result yavanna_grow_grass();
+const char *yavanna_grow_grass_info();
+casting_result yavanna_tree_roots();
+const char *yavanna_tree_roots_info();
+casting_result yavanna_water_bite();
+const char *yavanna_water_bite_info();
+casting_result yavanna_uproot();
+const char *yavanna_uproot_info();
+
+extern s32b GROWTREE;
+extern s32b HEALING;
+extern s32b RECOVERY;
+extern s32b REGENERATION;
+extern s32b SUMMONANNIMAL;
+extern s32b GROW_ATHELAS;
+
+casting_result nature_grow_trees();
+const char *nature_grow_trees_info();
+casting_result nature_healing();
+const char *nature_healing_info();
+casting_result nature_recovery();
+casting_result nature_regeneration();
+const char *nature_regeneration_info();
+casting_result nature_summon_animal();
+const char *nature_summon_animal_info();
+casting_result nature_grow_athelas();
+
+extern s32b DEVICE_HEAL_MONSTER;
+extern s32b DEVICE_SPEED_MONSTER;
+extern s32b DEVICE_WISH;
+extern s32b DEVICE_SUMMON;
+extern s32b DEVICE_MANA;
+extern s32b DEVICE_NOTHING;
+extern s32b DEVICE_HOLY_FIRE;
+extern s32b DEVICE_THUNDERLORDS;
+
+casting_result device_heal_monster();
+const char *device_heal_monster_info();
+casting_result device_haste_monster();
+const char *device_haste_monster_info();
+casting_result device_wish();
+casting_result device_summon_monster();
+casting_result device_mana();
+const char *device_mana_info();
+casting_result device_nothing();
+casting_result device_holy_fire();
+const char *device_holy_fire_info();
+casting_result device_thunderlords();
+
+extern s32b MUSIC_STOP;
+extern s32b MUSIC_HOLD;
+extern s32b MUSIC_CONF;
+extern s32b MUSIC_STUN;
+extern s32b MUSIC_LITE;
+extern s32b MUSIC_HEAL;
+extern s32b MUSIC_HERO;
+extern s32b MUSIC_TIME;
+extern s32b MUSIC_MIND;
+extern s32b MUSIC_BLOW;
+extern s32b MUSIC_WIND;
+extern s32b MUSIC_YLMIR;
+extern s32b MUSIC_AMBARKANTA;
+
+casting_result music_stop_singing_spell();
+int music_holding_pattern_lasting();
+casting_result music_holding_pattern_spell();
+const char *music_holding_pattern_info();
+int music_illusion_pattern_lasting();
+casting_result music_illusion_pattern_spell();
+const char *music_illusion_pattern_info();
+int music_stun_pattern_lasting();
+casting_result music_stun_pattern_spell();
+const char *music_stun_pattern_info();
+int music_song_of_the_sun_lasting();
+casting_result music_song_of_the_sun_spell();
+int music_flow_of_life_lasting();
+casting_result music_flow_of_life_spell();
+const char *music_flow_of_life_info();
+int music_heroic_ballad_lasting();
+casting_result music_heroic_ballad_spell();
+int music_hobbit_melodies_lasting();
+casting_result music_hobbit_melodies_spell();
+const char *music_hobbit_melodies_info();
+int music_clairaudience_lasting();
+casting_result music_clairaudience_spell();
+const char *music_clairaudience_info();
+casting_result music_blow_spell();
+const char *music_blow_info();
+casting_result music_gush_of_wind_spell();
+const char *music_gush_of_wind_info();
+casting_result music_horns_of_ylmir_spell();
+const char *music_horns_of_ylmir_info();
+casting_result music_ambarkanta_spell();
+
+extern s32b AULE_FIREBRAND;
+extern s32b AULE_ENCHANT_WEAPON;
+extern s32b AULE_ENCHANT_ARMOUR;
+extern s32b AULE_CHILD;
+
+casting_result aule_firebrand_spell();
+const char *aule_firebrand_info();
+casting_result aule_enchant_weapon_spell();
+const char *aule_enchant_weapon_info();
+casting_result aule_enchant_armour_spell();
+const char *aule_enchant_armour_info();
+casting_result aule_child_spell();
+const char *aule_child_info();
+
+extern s32b MANDOS_TEARS_LUTHIEN;
+extern s32b MANDOS_SPIRIT_FEANTURI;
+extern s32b MANDOS_TALE_DOOM;
+extern s32b MANDOS_CALL_HALLS;
+
+casting_result mandos_tears_of_luthien_spell();
+const char *mandos_tears_of_luthien_info();
+casting_result mandos_spirit_of_the_feanturi_spell();
+const char *mandos_spirit_of_the_feanturi_info();
+casting_result mandos_tale_of_doom_spell();
+const char *mandos_tale_of_doom_info();
+casting_result mandos_call_to_the_halls_spell();
+const char *mandos_call_to_the_halls_info();
+
+extern s32b ULMO_BELEGAER;
+extern s32b ULMO_DRAUGHT_ULMONAN;
+extern s32b ULMO_CALL_ULUMURI;
+extern s32b ULMO_WRATH;
+
+casting_result ulmo_song_of_belegaer_spell();
+const char *ulmo_song_of_belegaer_info();
+casting_result ulmo_draught_of_ulmonan_spell();
+const char *ulmo_draught_of_ulmonan_info();
+casting_result ulmo_call_of_the_ulumuri_spell();
+const char *ulmo_call_of_the_ulumuri_info();
+casting_result ulmo_wrath_of_ulmo_spell();
+const char *ulmo_wrath_of_ulmo_info();
+
+extern s32b VARDA_LIGHT_VALINOR;
+extern s32b VARDA_CALL_ALMAREN;
+extern s32b VARDA_EVENSTAR;
+extern s32b VARDA_STARKINDLER;
+
+casting_result varda_light_of_valinor_spell();
+const char *varda_light_of_valinor_info();
+casting_result varda_call_of_almaren_spell();
+casting_result varda_evenstar_spell();
+casting_result varda_star_kindler_spell();
+const char *varda_star_kindler_info();
diff --git a/src/spells4.cc b/src/spells4.cc
new file mode 100644
index 00000000..62586758
--- /dev/null
+++ b/src/spells4.cc
@@ -0,0 +1,541 @@
+#include "spells4.hpp"
+
+#include "cave.hpp"
+#include "cmd5.hpp"
+#include "gods.hpp"
+#include "lua_bind.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "school_book.hpp"
+#include "spell_type.hpp"
+#include "spells3.hpp"
+#include "spells5.hpp"
+#include "spells6.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <sstream>
+
+static std::array<school_book, SCHOOL_BOOKS_SIZE> &school_books() {
+ static std::array<school_book, SCHOOL_BOOKS_SIZE> *instance = new std::array<school_book, SCHOOL_BOOKS_SIZE>;
+ return *instance;
+}
+
+
+s32b SCHOOL_AIR;
+s32b SCHOOL_AULE;
+s32b SCHOOL_CONVEYANCE;
+s32b SCHOOL_DEMON;
+s32b SCHOOL_DEVICE;
+s32b SCHOOL_DIVINATION;
+s32b SCHOOL_EARTH;
+s32b SCHOOL_ERU;
+s32b SCHOOL_FIRE;
+s32b SCHOOL_GEOMANCY;
+s32b SCHOOL_MANA;
+s32b SCHOOL_MANDOS;
+s32b SCHOOL_MANWE;
+s32b SCHOOL_MELKOR;
+s32b SCHOOL_META;
+s32b SCHOOL_MIND;
+s32b SCHOOL_MUSIC;
+s32b SCHOOL_NATURE;
+s32b SCHOOL_TEMPORAL;
+s32b SCHOOL_TULKAS;
+s32b SCHOOL_UDUN;
+s32b SCHOOL_ULMO;
+s32b SCHOOL_VARDA;
+s32b SCHOOL_WATER;
+s32b SCHOOL_YAVANNA;
+
+static bool_ uses_piety_to_cast(int s)
+{
+ return spell_type_uses_piety_to_cast(spell_at(s));
+}
+
+/** Describe what type of energy the spell uses for casting */
+cptr get_power_name(s32b s)
+{
+ return uses_piety_to_cast(s) ? "piety" : "mana";
+}
+
+/* Changes the amount of power(mana, piety, whatever) for the spell */
+void adjust_power(s32b s, s32b amount)
+{
+ if (uses_piety_to_cast(s))
+ {
+ inc_piety(GOD_ALL, amount);
+ }
+ else
+ {
+ increase_mana(amount);
+ }
+}
+
+/* Return the amount of power available for casting spell */
+s32b get_power(s32b s)
+{
+ return uses_piety_to_cast(s) ? p_ptr->grace : p_ptr->csp;
+}
+
+/* Output the describtion when it is used as a spell */
+void print_spell_desc(int s, int y)
+{
+ spell_type *spell = spell_at(s);
+
+ spell_type_description_foreach(spell,
+ [&y] (std::string const &text) -> void {
+ c_prt(TERM_L_BLUE, text.c_str(), y, 0);
+ y += 1;
+ });
+
+ if (spell_type_uses_piety_to_cast(spell))
+ {
+ c_prt(TERM_L_WHITE, "It uses piety to cast.", y, 0);
+ y++;
+ }
+
+ if (spell_type_castable_while_blind(spell))
+ {
+ c_prt(TERM_ORANGE, "It is castable even while blinded.", y, 0);
+ y++;
+ }
+
+ if (spell_type_castable_while_confused(spell))
+ {
+ c_prt(TERM_ORANGE, "It is castable even while confused.", y, 0);
+ y++;
+ }
+}
+
+school_book *school_books_at(int i)
+{
+ assert(i >= 0);
+ assert(i < SCHOOL_BOOKS_SIZE);
+ return &school_books()[i];
+}
+
+void school_book_add_spell(school_book *school_book, s32b spell_idx)
+{
+ assert(school_book != nullptr);
+ school_book->spell_idxs.insert(std::begin(school_book->spell_idxs), spell_idx);
+}
+
+int school_book_length(int sval)
+{
+ school_book *school_book = school_books_at(sval);
+ return school_book->spell_idxs.size();
+}
+
+int spell_x(int sval, int spell_idx, int i)
+{
+ assert(i >= 0);
+
+ if (sval == BOOK_RANDOM)
+ {
+ return spell_idx;
+ }
+ else
+ {
+ school_book *school_book = school_books_at(sval);
+ return school_book->spell_idxs.at(i);
+ }
+}
+
+bool_ school_book_contains_spell(int sval, s32b spell_idx)
+{
+ random_book_setup(sval, spell_idx);
+ school_book *school_book = school_books_at(sval);
+ return (school_book->spell_idxs.end() !=
+ std::find(school_book->spell_idxs.begin(),
+ school_book->spell_idxs.end(),
+ spell_idx));
+}
+
+static void push_spell(int book_idx, s32b spell_idx)
+{
+ school_book *school_book = school_books_at(book_idx);
+ assert(school_book != NULL);
+ school_book_add_spell(school_book, spell_idx);
+}
+
+void init_school_books()
+{
+ /* Note: We're adding the spells in the reverse order that
+ they appear in each book. This is because the list
+ operations insert at the front. */
+
+ /* Create the crystal of mana */
+ push_spell(TOME_MANA, MANASHIELD);
+ push_spell(TOME_MANA, RESISTS);
+ push_spell(TOME_MANA, DELCURSES);
+ push_spell(TOME_MANA, MANATHRUST);
+
+ /* The book of the eternal flame */
+ push_spell(TOME_FIRE, FIERYAURA);
+ push_spell(TOME_FIRE, FIREWALL);
+ push_spell(TOME_FIRE, FIREFLASH);
+ push_spell(TOME_FIRE, FIREGOLEM);
+ push_spell(TOME_FIRE, GLOBELIGHT);
+
+ /* The book of the blowing winds */
+ push_spell(TOME_WINDS, THUNDERSTORM);
+ push_spell(TOME_WINDS, AIRWINGS);
+ push_spell(TOME_WINDS, STERILIZE);
+ push_spell(TOME_WINDS, INVISIBILITY);
+ push_spell(TOME_WINDS, POISONBLOOD);
+ push_spell(TOME_WINDS, NOXIOUSCLOUD);
+
+ /* The book of the impenetrable earth */
+ push_spell(TOME_EARTH, STRIKE);
+ push_spell(TOME_EARTH, SHAKE);
+ push_spell(TOME_EARTH, STONEPRISON);
+ push_spell(TOME_EARTH, DIG);
+ push_spell(TOME_EARTH, STONESKIN);
+
+ /* The book of the unstopable wave */
+ push_spell(TOME_WATER, ICESTORM);
+ push_spell(TOME_WATER, TIDALWAVE);
+ push_spell(TOME_WATER, ENTPOTION);
+ push_spell(TOME_WATER, VAPOR);
+ push_spell(TOME_WATER, GEYSER);
+
+ /* Create the book of translocation */
+ push_spell(TOME_TRANSLOCATION, PROBABILITY_TRAVEL);
+ push_spell(TOME_TRANSLOCATION, RECALL);
+ push_spell(TOME_TRANSLOCATION, TELEAWAY);
+ push_spell(TOME_TRANSLOCATION, TELEPORT);
+ push_spell(TOME_TRANSLOCATION, DISARM);
+ push_spell(TOME_TRANSLOCATION, BLINK);
+
+ /* Create the book of the tree */
+ if (game_module_idx == MODULE_THEME)
+ {
+ push_spell(TOME_NATURE, GROW_ATHELAS);
+ }
+ push_spell(TOME_NATURE, SUMMONANNIMAL);
+ push_spell(TOME_NATURE, REGENERATION);
+ push_spell(TOME_NATURE, RECOVERY);
+ push_spell(TOME_NATURE, HEALING);
+ push_spell(TOME_NATURE, GROWTREE);
+
+ /* Create the book of Knowledge */
+ push_spell(TOME_KNOWLEDGE, STARIDENTIFY);
+ push_spell(TOME_KNOWLEDGE, VISION);
+ push_spell(TOME_KNOWLEDGE, IDENTIFY);
+ push_spell(TOME_KNOWLEDGE, REVEALWAYS);
+ push_spell(TOME_KNOWLEDGE, SENSEHIDDEN);
+ push_spell(TOME_KNOWLEDGE, SENSEMONSTERS);
+
+ /* Create the book of the Time */
+ push_spell(TOME_TIME, BANISHMENT);
+ push_spell(TOME_TIME, ESSENCESPEED);
+ push_spell(TOME_TIME, SLOWMONSTER);
+ push_spell(TOME_TIME, MAGELOCK);
+
+ /* Create the book of meta spells */
+ push_spell(TOME_META, INERTIA_CONTROL);
+ push_spell(TOME_META, TRACKER);
+ push_spell(TOME_META, SPELLBINDER);
+ push_spell(TOME_META, DISPERSEMAGIC);
+ push_spell(TOME_META, RECHARGE);
+
+ /* Create the book of the mind */
+ push_spell(TOME_MIND, STUN);
+ push_spell(TOME_MIND, ARMOROFFEAR);
+ push_spell(TOME_MIND, CONFUSE);
+ push_spell(TOME_MIND, CHARM);
+
+ /* Create the book of hellflame */
+ push_spell(TOME_HELLFLAME, FLAMEOFUDUN);
+ push_spell(TOME_HELLFLAME, WRAITHFORM);
+ push_spell(TOME_HELLFLAME, GENOCIDE);
+ push_spell(TOME_HELLFLAME, DRAIN);
+
+ /* Create the book of eru */
+ push_spell(TOME_ERU, ERU_PROT);
+ push_spell(TOME_ERU, ERU_UNDERSTAND);
+ push_spell(TOME_ERU, ERU_LISTEN);
+ push_spell(TOME_ERU, ERU_SEE);
+
+ /* Create the book of manwe */
+ push_spell(TOME_MANWE, MANWE_AVATAR);
+ push_spell(TOME_MANWE, MANWE_CALL);
+ push_spell(TOME_MANWE, MANWE_SHIELD);
+ push_spell(TOME_MANWE, MANWE_BLESS);
+
+ /* Create the book of tulkas */
+ push_spell(TOME_TULKAS, TULKAS_WAVE);
+ push_spell(TOME_TULKAS, TULKAS_SPIN);
+ push_spell(TOME_TULKAS, TULKAS_AIM);
+
+ /* Create the book of melkor */
+ push_spell(TOME_MELKOR, MELKOR_MIND_STEAL);
+ push_spell(TOME_MELKOR, MELKOR_CORPSE_EXPLOSION);
+ push_spell(TOME_MELKOR, MELKOR_CURSE);
+
+ /* Create the book of yavanna */
+ push_spell(TOME_YAVANNA, YAVANNA_UPROOT);
+ push_spell(TOME_YAVANNA, YAVANNA_WATER_BITE);
+ push_spell(TOME_YAVANNA, YAVANNA_TREE_ROOTS);
+ push_spell(TOME_YAVANNA, YAVANNA_GROW_GRASS);
+ push_spell(TOME_YAVANNA, YAVANNA_CHARM_ANIMAL);
+
+ /* Create the book of beginner's cantrip */
+ push_spell(BOOK_CANTRIPS, SENSEHIDDEN);
+ push_spell(BOOK_CANTRIPS, SENSEMONSTERS);
+ push_spell(BOOK_CANTRIPS, BLINK);
+ push_spell(BOOK_CANTRIPS, ENTPOTION);
+ push_spell(BOOK_CANTRIPS, GLOBELIGHT);
+ push_spell(BOOK_CANTRIPS, MANATHRUST);
+
+ /* Create the book of teleporatation */
+ push_spell(BOOK_TELEPORTATION, TELEAWAY);
+ push_spell(BOOK_TELEPORTATION, TELEPORT);
+ push_spell(BOOK_TELEPORTATION, BLINK);
+
+ /* Create the book of summoning */
+ push_spell(BOOK_SUMMONING, SUMMONANNIMAL);
+ push_spell(BOOK_SUMMONING, FIREGOLEM);
+
+ /* Create the Armageddon Demonblade */
+ push_spell(BOOK_DEMON_SWORD, DEMON_FIELD);
+ push_spell(BOOK_DEMON_SWORD, DEMON_MADNESS);
+ push_spell(BOOK_DEMON_SWORD, DEMON_BLADE);
+
+ /* Create the Shield Demonblade */
+ push_spell(BOOK_DEMON_SHIELD, UNHOLY_WORD);
+ push_spell(BOOK_DEMON_SHIELD, DEMON_CLOAK);
+ push_spell(BOOK_DEMON_SHIELD, DOOM_SHIELD);
+
+ /* Create the Control Demonblade */
+ push_spell(BOOK_DEMON_HELM, CONTROL_DEMON);
+ push_spell(BOOK_DEMON_HELM, DISCHARGE_MINION);
+ push_spell(BOOK_DEMON_HELM, DEMON_SUMMON);
+
+ /* Create the Drums */
+ push_spell(BOOK_DRUMS, MUSIC_STUN);
+ push_spell(BOOK_DRUMS, MUSIC_CONF);
+ push_spell(BOOK_DRUMS, MUSIC_HOLD);
+ push_spell(BOOK_DRUMS, MUSIC_STOP);
+
+ /* Create the Harps */
+ push_spell(BOOK_HARPS, MUSIC_MIND);
+ push_spell(BOOK_HARPS, MUSIC_TIME);
+ push_spell(BOOK_HARPS, MUSIC_HEAL);
+ push_spell(BOOK_HARPS, MUSIC_HERO);
+ push_spell(BOOK_HARPS, MUSIC_LITE);
+ push_spell(BOOK_HARPS, MUSIC_STOP);
+
+ /* Create the Horns */
+ push_spell(BOOK_HORNS, MUSIC_AMBARKANTA);
+ push_spell(BOOK_HORNS, MUSIC_YLMIR);
+ push_spell(BOOK_HORNS, MUSIC_WIND);
+ push_spell(BOOK_HORNS, MUSIC_BLOW);
+ push_spell(BOOK_HORNS, MUSIC_STOP);
+
+ /* Book of the Player, filled in by the Library Quest */
+ push_spell(BOOK_PLAYER, -1);
+
+ /* Geomancy spells, not a real book */
+ push_spell(BOOK_GEOMANCY, ELEMENTAL_MINION);
+ push_spell(BOOK_GEOMANCY, GROW_BARRIER);
+ push_spell(BOOK_GEOMANCY, DRIPPING_TREAD);
+ push_spell(BOOK_GEOMANCY, GEOLYSIS);
+ push_spell(BOOK_GEOMANCY, VAPORIZE);
+ push_spell(BOOK_GEOMANCY, ELEMENTAL_WAVE);
+ push_spell(BOOK_GEOMANCY, CHANNEL_ELEMENTS);
+ push_spell(BOOK_GEOMANCY, CALL_THE_ELEMENTS);
+
+ if (game_module_idx == MODULE_THEME)
+ {
+ /* Aule */
+ push_spell(BOOK_AULE, AULE_CHILD);
+ push_spell(BOOK_AULE, AULE_ENCHANT_ARMOUR);
+ push_spell(BOOK_AULE, AULE_ENCHANT_WEAPON);
+ push_spell(BOOK_AULE, AULE_FIREBRAND);
+
+ /* Varda */
+ push_spell(BOOK_VARDA, VARDA_STARKINDLER);
+ push_spell(BOOK_VARDA, VARDA_EVENSTAR);
+ push_spell(BOOK_VARDA, VARDA_CALL_ALMAREN);
+ push_spell(BOOK_VARDA, VARDA_LIGHT_VALINOR);
+
+ /* Ulmo */
+ push_spell(BOOK_ULMO, ULMO_WRATH);
+ push_spell(BOOK_ULMO, ULMO_CALL_ULUMURI);
+ push_spell(BOOK_ULMO, ULMO_DRAUGHT_ULMONAN);
+ push_spell(BOOK_ULMO, ULMO_BELEGAER);
+
+ /* Mandos */
+ push_spell(BOOK_MANDOS, MANDOS_CALL_HALLS);
+ push_spell(BOOK_MANDOS, MANDOS_TALE_DOOM);
+ push_spell(BOOK_MANDOS, MANDOS_SPIRIT_FEANTURI);
+ push_spell(BOOK_MANDOS, MANDOS_TEARS_LUTHIEN);
+ }
+
+ /* Random spell book; just initialize to anything */
+ push_spell(BOOK_RANDOM, -1);
+}
+
+void random_book_setup(s16b sval, s32b spell_idx)
+{
+ if (sval == BOOK_RANDOM)
+ {
+ school_book *school_book = school_books_at(sval);
+ school_book->spell_idxs.clear();
+ school_book->spell_idxs.push_back(spell_idx);
+ }
+}
+
+static std::string spell_school_name(spell_type *spell)
+{
+ std::ostringstream buf;
+ bool first = true;
+
+ for (s32b school_idx : spell_type_get_schools(spell))
+ {
+ school_type *school = school_at(school_idx);
+ // Add separator?
+ if (first)
+ {
+ first = false; // Skip separator
+ }
+ else
+ {
+ buf << "/";
+ }
+ // Put in the school's name
+ buf << school->name;
+ }
+
+ return buf.str();
+}
+
+int print_spell(cptr label_, byte color, int y, s32b s)
+{
+ s32b level;
+ bool_ na;
+ spell_type *spell = spell_at(s);
+ cptr spell_info = spell_type_info(spell);
+ cptr label = (label_ == NULL) ? "" : label_;
+ char level_str[8] = "n/a";
+ char buf[128];
+
+ get_level_school(spell, 50, -50, &level, &na);
+
+ std::string sch_str(spell_school_name(spell));
+
+ if (!na)
+ {
+ sprintf(level_str, "%3d", (int) level);
+ }
+
+ sprintf(buf, "%s%-20s%-16s %s %4d %3d%% %s",
+ label,
+ spell_type_name(spell_at(s)),
+ sch_str.c_str(),
+ level_str,
+ get_mana(s),
+ (int) spell_chance_book(s),
+ spell_info);
+ c_prt(color, buf, y, 0);
+
+ return y + 1;
+}
+
+void lua_cast_school_spell(s32b s, bool_ no_cost)
+{
+ bool_ use = FALSE;
+ spell_type *spell = spell_at(s);
+
+ /* No magic? */
+ if (p_ptr->antimagic > 0)
+ {
+ 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;
+ }
+
+ /* if it costs something then some condition must be met */
+ if (!no_cost)
+ {
+ /* Require lite */
+ if (!spell_type_castable_while_blind(spell) &&
+ ((p_ptr->blind > 0) || no_lite()))
+ {
+ msg_print("You cannot see!");
+ return;
+ }
+
+ /* Not when confused */
+ if (!spell_type_castable_while_confused(spell) &&
+ (p_ptr->confused > 0))
+ {
+ msg_print("You are too confused!");
+ return;
+ }
+
+ /* Enough mana */
+ if (get_mana(s) > get_power(s))
+ {
+ char buf[128];
+ sprintf(buf,
+ "You do not have enough %s, do you want to try anyway?",
+ get_power_name(s));
+
+ if (!get_check(buf))
+ {
+ return;
+ }
+ }
+
+ /* Invoke the spell effect */
+ if (!magik(spell_chance_book(s)))
+ {
+ use = (spell_type_produce_effect(spell) != NO_CAST);
+ }
+ else
+ {
+ use = TRUE;
+
+ /* failures are dangerous; we'll flush the input buffer
+ so it isn't missed. */
+ if (flush_failure)
+ {
+ flush();
+ }
+
+ msg_print("You failed to get the spell off!");
+ }
+ }
+ else
+ {
+ spell_type_produce_effect(spell);
+ }
+
+ /* Use the mana/piety */
+ if (use == TRUE)
+ {
+ /* Reduce mana */
+ adjust_power(s, -get_mana(s));
+
+ /* Take a turn */
+ energy_use = is_magestaff() ? 80 : 100;
+ }
+
+ /* Refresh player */
+ p_ptr->redraw |= PR_FRAME;
+ p_ptr->window |= PW_PLAYER;
+}
diff --git a/src/spells4.hpp b/src/spells4.hpp
new file mode 100644
index 00000000..02cdc3ce
--- /dev/null
+++ b/src/spells4.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type_fwd.hpp"
+#include "school_book_fwd.hpp"
+
+extern s32b SCHOOL_AIR;
+extern s32b SCHOOL_AULE;
+extern s32b SCHOOL_CONVEYANCE;
+extern s32b SCHOOL_DEMON;
+extern s32b SCHOOL_DEVICE;
+extern s32b SCHOOL_DIVINATION;
+extern s32b SCHOOL_EARTH;
+extern s32b SCHOOL_ERU;
+extern s32b SCHOOL_FIRE;
+extern s32b SCHOOL_GEOMANCY;
+extern s32b SCHOOL_MANA;
+extern s32b SCHOOL_MANDOS;
+extern s32b SCHOOL_MANWE;
+extern s32b SCHOOL_MELKOR;
+extern s32b SCHOOL_META;
+extern s32b SCHOOL_MIND;
+extern s32b SCHOOL_MUSIC;
+extern s32b SCHOOL_NATURE;
+extern s32b SCHOOL_TEMPORAL;
+extern s32b SCHOOL_TULKAS;
+extern s32b SCHOOL_UDUN;
+extern s32b SCHOOL_ULMO;
+extern s32b SCHOOL_VARDA;
+extern s32b SCHOOL_WATER;
+extern s32b SCHOOL_YAVANNA;
+
+void print_spell_desc(int s, int y);
+void init_school_books();
+school_book *school_books_at(int sval);
+void school_book_add_spell(school_book *school_book, s32b spell_idx);
+void random_book_setup(s16b sval, s32b spell_idx);
+int print_spell(cptr label, byte color, int y, s32b s);
+int school_book_length(int sval);
+int spell_x(int sval, int spell_idx, int i);
+bool_ school_book_contains_spell(int sval, s32b spell_idx);
+void lua_cast_school_spell(s32b spell_idx, bool_ no_cost);
diff --git a/src/spells5.cc b/src/spells5.cc
new file mode 100644
index 00000000..f503c822
--- /dev/null
+++ b/src/spells5.cc
@@ -0,0 +1,2395 @@
+#include "spells5.hpp"
+
+#include "spell_type.hpp"
+#include "device_allocation.hpp"
+#include "spells3.hpp"
+#include "spells4.hpp"
+#include "variable.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+static s16b school_spells_count = 0;
+static struct spell_type *school_spells[SCHOOL_SPELLS_MAX];
+
+static spell_type *spell_new(s32b *index, cptr name)
+{
+ assert(school_spells_count < SCHOOL_SPELLS_MAX);
+
+ spell_type *spell = spell_type_new(name);
+ school_spells[school_spells_count] = spell;
+ *index = school_spells_count;
+ school_spells_count++;
+
+ return spell;
+}
+
+static cptr no_info()
+{
+ return "";
+}
+
+spell_type *spell_at(s32b index)
+{
+ assert(index >= 0);
+ assert(index < school_spells_count);
+
+ return school_spells[index];
+}
+
+int find_spell(cptr name)
+{
+ int i;
+
+ for (i = 0; i < school_spells_count; i++)
+ {
+ if (streq(spell_type_name(spell_at(i)), name))
+ {
+ return i;
+ }
+ }
+
+ /* Not found */
+ return -1;
+}
+
+s16b get_random_spell(s16b random_type, int level)
+{
+ int tries;
+
+ for (tries = 0; tries < 1000; tries++)
+ {
+ s16b spl = rand_int(school_spells_count);
+ spell_type *spell = spell_at(spl);
+
+ if ((spell_type_random_type(spell) == random_type) &&
+ (rand_int(spell_type_skill_level(spell) * 3) < level))
+ {
+ return spl;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Get a spell for a device of a given tval (wand or staff).
+ */
+s16b get_random_stick(byte tval, int level)
+{
+ int tries;
+
+ for (tries = 0; tries < 1000; tries++)
+ {
+ long spell_idx = rand_int(school_spells_count);
+ spell_type *spell = spell_at(spell_idx);
+ device_allocation *device_allocation = spell_type_device_allocation(spell, tval);
+
+ if ((device_allocation != NULL) &&
+ (rand_int(spell_type_skill_level(spell) * 3) < level) &&
+ (magik(100 - device_allocation->rarity)))
+ {
+ return spell_idx;
+ }
+ }
+
+ return -1;
+}
+
+static void spells_init_tome()
+{
+ {
+ spell_type *spell = spell_new(&DEVICE_THUNDERLORDS, "Artifact Thunderlords");
+ spell_type_describe(spell, "A thunderlord will appear to transport you quickly to the surface.");
+ spell_type_set_mana(spell, 1, 1);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_init_device(spell,
+ no_info,
+ device_thunderlords);
+
+ spell_type_set_device_charges(spell, "3+d3");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 999;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 1, 1);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+}
+
+static void spells_init_theme()
+{
+ {
+ spell_type *spell = spell_new(&GROW_ATHELAS, "Grow Athelas");
+ spell_type_describe(spell, "Cures the Black Breath");
+ spell_type_set_mana(spell, 60, 100);
+ spell_type_set_difficulty(spell, 30, 95);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_NATURE,
+ no_info,
+ nature_grow_athelas);
+
+ spell_type_set_device_charges(spell, "1+d3");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 85;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 15, 45);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&AULE_FIREBRAND, "Firebrand");
+ spell_type_describe(spell, "Imbues your melee weapon with fire to deal more damage");
+ spell_type_describe(spell, "At level 15 it spreads over a 1 radius zone around your target");
+ spell_type_describe(spell, "At level 30 it deals holy fire damage");
+ spell_type_set_mana(spell, 10, 100);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_AULE,
+ aule_firebrand_info,
+ aule_firebrand_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&AULE_ENCHANT_WEAPON, "Enchant Weapon");
+ spell_type_describe(spell, "Tries to enchant a weapon to-hit");
+ spell_type_describe(spell, "At level 5 it also enchants to-dam");
+ spell_type_describe(spell, "At level 45 it enhances the special powers of magical weapons");
+ spell_type_describe(spell, "The might of the enchantment increases with the level");
+ spell_type_set_mana(spell, 100, 200);
+ spell_type_set_difficulty(spell, 10, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_AULE,
+ aule_enchant_weapon_info,
+ aule_enchant_weapon_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&AULE_ENCHANT_ARMOUR, "Enchant Armour");
+ spell_type_describe(spell, "Tries to enchant a piece of armour");
+ spell_type_describe(spell, "At level 20 it also enchants to-hit and to-dam");
+ spell_type_describe(spell, "At level 40 it enhances the special powers of magical armour");
+ spell_type_describe(spell, "The might of the enchantment increases with the level");
+ spell_type_set_mana(spell, 100, 200);
+ spell_type_set_difficulty(spell, 15, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_AULE,
+ aule_enchant_armour_info,
+ aule_enchant_armour_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&AULE_CHILD, "Child of Aule");
+ spell_type_describe(spell, "Summons a levelled Dwarven warrior to help you battle the forces");
+ spell_type_describe(spell, "of Morgoth");
+ spell_type_set_mana(spell, 200, 500);
+ spell_type_set_difficulty(spell, 20, 40);
+ spell_type_init_priest(spell,
+ SCHOOL_AULE,
+ aule_child_info,
+ aule_child_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&VARDA_LIGHT_VALINOR, "Light of Valinor");
+ spell_type_describe(spell, "Lights a room");
+ spell_type_describe(spell, "At level 3 it starts damaging monsters");
+ spell_type_describe(spell, "At level 15 it starts creating a more powerful kind of light");
+ spell_type_set_mana(spell, 1, 100);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_VARDA,
+ varda_light_of_valinor_info,
+ varda_light_of_valinor_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&VARDA_CALL_ALMAREN, "Call of Almaren");
+ spell_type_describe(spell, "Banishes evil beings");
+ spell_type_describe(spell, "At level 20 it dispels evil beings");
+ spell_type_set_mana(spell, 5, 150);
+ spell_type_set_difficulty(spell, 10, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_VARDA,
+ no_info,
+ varda_call_of_almaren_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&VARDA_EVENSTAR, "Evenstar");
+ spell_type_describe(spell, "Maps and lights the whole level.");
+ spell_type_describe(spell, "At level 40 it maps and lights the whole level,");
+ spell_type_describe(spell, "in addition to letting you know yourself better");
+ spell_type_describe(spell, "and identifying your whole pack.");
+ spell_type_set_mana(spell, 20, 200);
+ spell_type_set_difficulty(spell, 20, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_VARDA,
+ no_info,
+ varda_evenstar_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&VARDA_STARKINDLER, "Star Kindler");
+ spell_type_describe(spell, "Does multiple bursts of light damage.");
+ spell_type_describe(spell, "The damage increases with level.");
+ spell_type_set_mana(spell, 50, 250);
+ spell_type_set_difficulty(spell, 30, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_VARDA,
+ varda_star_kindler_info,
+ varda_star_kindler_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&ULMO_BELEGAER, "Song of Belegaer");
+ spell_type_describe(spell, "Channels the power of the Great Sea into your fingertips.");
+ spell_type_describe(spell, "Sometimes it can blast through its first target.");
+ spell_type_set_mana(spell, 1, 100);
+ spell_type_set_difficulty(spell, 1, 25);
+ spell_type_init_priest(spell,
+ SCHOOL_ULMO,
+ ulmo_song_of_belegaer_info,
+ ulmo_song_of_belegaer_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&ULMO_DRAUGHT_ULMONAN, "Draught of Ulmonan");
+ spell_type_describe(spell, "Fills you with a draught with powerful curing effects,");
+ spell_type_describe(spell, "prepared by Ulmo himself.");
+ spell_type_describe(spell, "Level 1: blindness, poison, cuts and stunning");
+ spell_type_describe(spell, "Level 10: drained STR, DEX and CON");
+ spell_type_describe(spell, "Level 20: parasites and mimicry");
+ spell_type_set_mana(spell, 25, 200);
+ spell_type_set_difficulty(spell, 15, 50);
+ spell_type_init_priest(spell,
+ SCHOOL_ULMO,
+ ulmo_draught_of_ulmonan_info,
+ ulmo_draught_of_ulmonan_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&ULMO_CALL_ULUMURI, "Call of the Ulumuri");
+ spell_type_describe(spell, "Summons a leveled water spirit or elemental");
+ spell_type_describe(spell, "to fight for you");
+ spell_type_set_mana(spell, 50, 300);
+ spell_type_set_difficulty(spell, 20, 75);
+ spell_type_init_priest(spell,
+ SCHOOL_ULMO,
+ ulmo_call_of_the_ulumuri_info,
+ ulmo_call_of_the_ulumuri_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&ULMO_WRATH, "Wrath of Ulmo");
+ spell_type_describe(spell, "Conjures up a sea storm.");
+ spell_type_describe(spell, "At level 30 it turns into a more forceful storm.");
+ spell_type_set_mana(spell, 100, 400);
+ spell_type_set_difficulty(spell, 30, 95);
+ spell_type_init_priest(spell,
+ SCHOOL_ULMO,
+ ulmo_wrath_of_ulmo_info,
+ ulmo_wrath_of_ulmo_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANDOS_TEARS_LUTHIEN, "Tears of Luthien");
+ spell_type_describe(spell, "Calls upon the spirit of Luthien to ask Mandos for healing and succour.");
+ spell_type_set_mana(spell, 10, 100);
+ spell_type_set_difficulty(spell, 5, 25);
+ spell_type_init_priest(spell,
+ SCHOOL_MANDOS,
+ mandos_tears_of_luthien_info,
+ mandos_tears_of_luthien_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANDOS_SPIRIT_FEANTURI, "Feanturi");
+ spell_type_describe(spell, "Channels the power of Mandos to cure fear and confusion.");
+ spell_type_describe(spell, "At level 20 it restores lost INT and WIS");
+ spell_type_describe(spell, "At level 30 it cures hallucinations and restores a percentage of lost sanity");
+ spell_type_set_mana(spell, 40, 200);
+ spell_type_set_difficulty(spell, 10, 50);
+ spell_type_init_priest(spell,
+ SCHOOL_MANDOS,
+ mandos_spirit_of_the_feanturi_info,
+ mandos_spirit_of_the_feanturi_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANDOS_TALE_DOOM, "Tale of Doom");
+ spell_type_describe(spell, "Allows you to predict the future for a short time.");
+ spell_type_set_mana(spell, 60, 300);
+ spell_type_set_difficulty(spell, 25, 75);
+ spell_type_init_priest(spell,
+ SCHOOL_MANDOS,
+ mandos_tale_of_doom_info,
+ mandos_tale_of_doom_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANDOS_CALL_HALLS, "Call to the Halls");
+ spell_type_describe(spell, "Summons a leveled spirit from the Halls of Mandos");
+ spell_type_describe(spell, "to fight for you.");
+ spell_type_set_mana(spell, 80, 400);
+ spell_type_set_difficulty(spell, 30, 95);
+ spell_type_init_priest(spell,
+ SCHOOL_MANDOS,
+ mandos_call_to_the_halls_info,
+ mandos_call_to_the_halls_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&DEVICE_THUNDERLORDS, "Artifact Thunderlords");
+ spell_type_describe(spell, "An Eagle of Manwe will appear to transport you quickly to the town.");
+ spell_type_set_mana(spell, 1, 1);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_init_device(spell,
+ no_info,
+ device_thunderlords);
+
+ spell_type_set_device_charges(spell, "5+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 999;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 1, 1);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+}
+
+void school_spells_init()
+{
+ /* Zero out spell array */
+ {
+ int i = 0;
+ for (i = 0; i < SCHOOL_SPELLS_MAX; i++)
+ {
+ school_spells[i] = NULL;
+ }
+ }
+
+ /* Spells */
+ {
+ spell_type *spell = spell_new(&GLOBELIGHT, "Globe of Light");
+ spell_type_describe(spell, "Creates a globe of pure light");
+ spell_type_describe(spell, "At level 3 it starts damaging monsters");
+ spell_type_describe(spell, "At level 15 it starts creating a more powerful kind of light");
+ spell_type_set_mana(spell, 2, 15);
+ spell_type_set_inertia(spell, 1, 40);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_FIRE,
+ fire_globe_of_light_info,
+ fire_globe_of_light);
+
+ spell_type_set_device_charges(spell, "10+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 7;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 10, 45);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&FIREFLASH, "Fireflash");
+ spell_type_describe(spell, "Conjures a ball of fire to burn your foes to ashes");
+ spell_type_describe(spell, "At level 20 it turns into a ball of holy fire");
+ spell_type_set_mana(spell, 5, 70);
+ spell_type_set_difficulty(spell, 10, 35);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_FIRE,
+ fire_fireflash_info,
+ fire_fireflash);
+
+ spell_type_set_device_charges(spell, "5+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 35;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 15, 35);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&FIERYAURA, "Fiery Shield");
+ spell_type_describe(spell, "Creates a shield of fierce flames around you");
+ spell_type_describe(spell, "At level 8 it turns into a greater kind of flame that can not be resisted");
+ spell_type_set_mana(spell, 20, 60);
+ spell_type_set_inertia(spell, 2, 15);
+ spell_type_set_difficulty(spell, 20, 50);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_FIRE,
+ fire_fiery_shield_info,
+ fire_fiery_shield);
+
+ spell_type_set_device_charges(spell, "3+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 50;
+ range_init(&device_allocation->base_level, 1, 10);
+ range_init(&device_allocation->max_level, 5, 40);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&FIREWALL, "Firewall");
+ spell_type_describe(spell, "Creates a fiery wall to incinerate monsters stupid enough to attack you");
+ spell_type_describe(spell, "At level 6 it turns into a wall of hell fire");
+ spell_type_set_mana(spell, 25, 100);
+ spell_type_set_difficulty(spell, 15, 40);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_FIRE,
+ fire_firewall_info,
+ fire_firewall);
+
+ spell_type_set_device_charges(spell, "4+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 55;
+ range_init(&device_allocation->base_level, 1, 10);
+ range_init(&device_allocation->max_level, 5, 40);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&FIREGOLEM, "Fire Golem");
+ spell_type_describe(spell, "Creates a fiery golem and controls it");
+ spell_type_describe(spell, "During the control the available keylist is:");
+ spell_type_describe(spell, "Movement keys: movement of the golem(depending on its speed");
+ spell_type_describe(spell, " it can move more than one square)");
+ spell_type_describe(spell, ", : pickup all items on the floor");
+ spell_type_describe(spell, "d : drop all carried items");
+ spell_type_describe(spell, "i : list all carried items");
+ spell_type_describe(spell, "m : end the possession/use golem powers");
+ spell_type_describe(spell, "Most of the other keys are disabled, you cannot interact with your");
+ spell_type_describe(spell, "real body while controlling the golem");
+ spell_type_describe(spell, "But to cast the spell you will need a lantern or a wooden torch to");
+ spell_type_describe(spell, "Create the golem from");
+ spell_type_add_school(spell, SCHOOL_MIND);
+ spell_type_set_mana(spell, 16, 70);
+ spell_type_set_difficulty(spell, 7, 40);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_FIRE,
+ fire_golem_info,
+ fire_golem);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANATHRUST, "Manathrust");
+ spell_type_describe(spell, "Conjures up mana into a powerful bolt");
+ spell_type_describe(spell, "The damage is irresistible and will increase with level");
+ spell_type_set_mana(spell, 1, 25);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_MANA,
+ mana_manathrust_info,
+ mana_manathrust);
+
+ spell_type_set_device_charges(spell, "7+d10");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 5;
+ range_init(&device_allocation->base_level, 1, 20);
+ range_init(&device_allocation->max_level, 15, 33);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&DELCURSES, "Remove Curses");
+ spell_type_describe(spell, "Remove curses of worn objects");
+ spell_type_describe(spell, "At level 20 switches to *remove curses*");
+ spell_type_set_mana(spell, 20, 40);
+ spell_type_set_inertia(spell, 1, 10);
+ spell_type_set_difficulty(spell, 10, 30);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_MANA,
+ no_info,
+ mana_remove_curses);
+
+ spell_type_set_device_charges(spell, "3+d8");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 70;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 15, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&RESISTS, "Elemental Shield");
+ spell_type_describe(spell, "Provide resistances to the four basic elements");
+ spell_type_set_mana(spell, 17, 20);
+ spell_type_set_inertia(spell, 2, 25);
+ spell_type_set_difficulty(spell, 20, 40);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_MANA,
+ mana_elemental_shield_info,
+ mana_elemental_shield);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANASHIELD, "Disruption Shield");
+ spell_type_describe(spell, "Uses mana instead of hp to take damage");
+ spell_type_describe(spell, "At level 5 switches to Globe of Invulnerability.");
+ spell_type_describe(spell, "The spell breaks as soon as a melee, shooting, throwing or magical");
+ spell_type_describe(spell, "skill action is attempted, and lasts only a short time.");
+ spell_type_set_mana(spell, 50, 50);
+ spell_type_set_inertia(spell, 9, 10);
+ spell_type_set_difficulty(spell, 45, 90);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_MANA,
+ mana_disruption_shield_info,
+ mana_disruption_shield);
+ }
+
+ {
+ spell_type *spell = spell_new(&TIDALWAVE, "Tidal Wave");
+ spell_type_describe(spell, "Summons a monstrous tidal wave that will expand and crush the");
+ spell_type_describe(spell, "monsters under its mighty waves.");
+ spell_type_set_mana(spell, 16, 40);
+ spell_type_set_inertia(spell, 4, 100);
+ spell_type_set_difficulty(spell, 16, 65);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_WATER,
+ water_tidal_wave_info,
+ water_tidal_wave);
+
+ spell_type_set_device_charges(spell, "6+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 54;
+ range_init(&device_allocation->base_level, 1, 10);
+ range_init(&device_allocation->max_level, 20, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&ICESTORM, "Ice Storm");
+ spell_type_describe(spell, "Engulfs you in a storm of roaring cold that strikes your foes.");
+ spell_type_describe(spell, "At level 10 it turns into shards of ice.");
+ spell_type_set_mana(spell, 30, 60);
+ spell_type_set_inertia(spell, 3, 40);
+ spell_type_set_difficulty(spell, 22, 80);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_WATER,
+ water_ice_storm_info,
+ water_ice_storm);
+
+ spell_type_set_device_charges(spell, "3+d7");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 65;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 25, 45);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&ENTPOTION, "Ent's Potion");
+ spell_type_describe(spell, "Fills up your stomach.");
+ spell_type_describe(spell, "At level 5 it boldens your heart.");
+ spell_type_describe(spell, "At level 12 it makes you heroic.");
+ spell_type_set_mana(spell, 7, 15);
+ spell_type_set_inertia(spell, 1, 30);
+ spell_type_set_difficulty(spell, 6, 35);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_WATER,
+ water_ent_potion_info,
+ water_ent_potion);
+ }
+
+ {
+ spell_type *spell = spell_new(&VAPOR, "Vapor");
+ spell_type_describe(spell, "Fills the air with toxic moisture to eradicate annoying critters.");
+ spell_type_set_mana(spell, 2, 12);
+ spell_type_set_inertia(spell, 1, 30);
+ spell_type_set_difficulty(spell, 2, 20);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_WATER,
+ water_vapor_info,
+ water_vapor);
+ }
+
+ {
+ spell_type *spell = spell_new(&GEYSER, "Geyser");
+ spell_type_describe(spell, "Shoots a geyser of water from your fingertips.");
+ spell_type_describe(spell, "Sometimes it can blast through its first target.");
+ spell_type_set_mana(spell, 1, 35);
+ spell_type_set_difficulty(spell, 1, 5);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_WATER,
+ water_geyser_info,
+ water_geyser);
+ }
+
+ {
+ spell_type *spell = spell_new(&NOXIOUSCLOUD, "Noxious Cloud");
+ spell_type_describe(spell, "Creates a cloud of poison");
+ spell_type_describe(spell, "The cloud will persist for some turns, damaging all monsters passing by");
+ spell_type_describe(spell, "At spell level 30 it turns into a thick gas attacking all living beings");
+ spell_type_set_mana(spell, 3, 30);
+ spell_type_set_difficulty(spell, 3, 20);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_AIR,
+ air_noxious_cloud_info,
+ air_noxious_cloud);
+
+ spell_type_set_device_charges(spell, "5+d7");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 15;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 25, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&AIRWINGS, "Wings of Winds");
+ spell_type_describe(spell, "Grants the power of levitation");
+ spell_type_describe(spell, "At level 16 it grants the power of controlled flight");
+ spell_type_add_school(spell, SCHOOL_CONVEYANCE);
+ spell_type_set_mana(spell, 30, 40);
+ spell_type_set_inertia(spell, 1, 10);
+ spell_type_set_difficulty(spell, 22, 60);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_AIR,
+ air_wings_of_winds_info,
+ air_wings_of_winds);
+
+ spell_type_set_device_charges(spell, "7+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 27;
+ range_init(&device_allocation->base_level, 1, 10);
+ range_init(&device_allocation->max_level, 20, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&INVISIBILITY, "Invisibility");
+ spell_type_describe(spell, "Grants invisibility");
+ spell_type_set_mana(spell, 10, 20);
+ spell_type_set_inertia(spell, 1, 30);
+ spell_type_set_difficulty(spell, 16, 50);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_AIR,
+ air_invisibility_info,
+ air_invisibility);
+ }
+
+ {
+ spell_type *spell = spell_new(&POISONBLOOD, "Poison Blood");
+ spell_type_describe(spell, "Grants resist poison");
+ spell_type_describe(spell, "At level 15 it provides poison branding to wielded weapon");
+ spell_type_set_mana(spell, 10, 20);
+ spell_type_set_inertia(spell, 1, 35);
+ spell_type_set_difficulty(spell, 12, 30);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_AIR,
+ air_poison_blood_info,
+ air_poison_blood);
+
+ spell_type_set_device_charges(spell, "10+d15");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 45;
+ range_init(&device_allocation->base_level, 1, 25);
+ range_init(&device_allocation->max_level, 35, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&THUNDERSTORM, "Thunderstorm");
+ spell_type_describe(spell, "Charges up the air around you with electricity");
+ spell_type_describe(spell, "Each turn it will throw a thunder bolt at a random monster in sight");
+ spell_type_describe(spell, "The thunder does 3 types of damage, one third of lightning");
+ spell_type_describe(spell, "one third of sound and one third of light");
+ spell_type_add_school(spell, SCHOOL_NATURE);
+ spell_type_set_mana(spell, 40, 60);
+ spell_type_set_inertia(spell, 2, 15);
+ spell_type_set_difficulty(spell, 25, 60);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_AIR,
+ air_thunderstorm_info,
+ air_thunderstorm);
+
+ spell_type_set_device_charges(spell, "5+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 85;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 25, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&STERILIZE, "Sterilize");
+ spell_type_describe(spell, "Prevents explosive breeding for a while.");
+ spell_type_set_mana(spell, 10, 100);
+ spell_type_set_difficulty(spell, 20, 50);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_AIR,
+ air_sterilize_info,
+ air_sterilize);
+
+ spell_type_set_device_charges(spell, "7+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 20;
+ range_init(&device_allocation->base_level, 1, 10);
+ range_init(&device_allocation->max_level, 20, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&STONESKIN, "Stone Skin");
+ spell_type_describe(spell, "Creates a shield of earth around you to protect you");
+ spell_type_describe(spell, "At level 25 it starts dealing damage to attackers");
+ spell_type_set_mana(spell, 1, 50);
+ spell_type_set_inertia(spell, 2, 50);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_EARTH,
+ earth_stone_skin_info,
+ earth_stone_skin);
+ }
+
+ {
+ spell_type *spell = spell_new(&DIG, "Dig");
+ spell_type_describe(spell, "Digs a hole in a wall much faster than any shovels");
+ spell_type_set_mana(spell, 14, 14);
+ spell_type_set_difficulty(spell, 12, 20);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_EARTH,
+ no_info,
+ earth_dig);
+
+ spell_type_set_device_charges(spell, "15+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 25;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 1, 1);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&STONEPRISON, "Stone Prison");
+ spell_type_describe(spell, "Creates a prison of walls around you");
+ spell_type_describe(spell, "At level 10 it allows you to target a monster");
+ spell_type_set_mana(spell, 30, 50);
+ spell_type_set_difficulty(spell, 25, 65);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_EARTH,
+ no_info,
+ earth_stone_prison);
+
+ spell_type_set_device_charges(spell, "5+d3");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 57;
+ range_init(&device_allocation->base_level, 1, 3);
+ range_init(&device_allocation->max_level, 5, 20);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&STRIKE, "Strike");
+ spell_type_describe(spell, "Creates a micro-ball of force that will push monsters backwards");
+ spell_type_describe(spell, "If the monster is caught near a wall, it'll be crushed against it");
+ spell_type_describe(spell, "At level 12 it turns into a ball of radius 1");
+ spell_type_set_mana(spell, 30, 50);
+ spell_type_set_difficulty(spell, 30, 60);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_EARTH,
+ earth_strike_info,
+ earth_strike);
+
+ spell_type_set_device_charges(spell, "2+d6");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 635;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 10, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&SHAKE, "Shake");
+ spell_type_describe(spell, "Creates a localised earthquake");
+ spell_type_describe(spell, "At level 10 it can be targeted at any location");
+ spell_type_set_mana(spell, 25, 30);
+ spell_type_set_inertia(spell, 2, 50);
+ spell_type_set_difficulty(spell, 27, 60);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_EARTH,
+ earth_shake_info,
+ earth_shake);
+
+ spell_type_set_device_charges(spell, "5+d10");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 75;
+ range_init(&device_allocation->base_level, 1, 3);
+ range_init(&device_allocation->max_level, 9, 20);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&BLINK, "Phase Door");
+ spell_type_describe(spell, "Teleports you on a small scale range");
+ spell_type_describe(spell, "At level 30 it creates void jumpgates");
+ spell_type_set_mana(spell, 1, 3);
+ spell_type_set_inertia(spell, 1, 5);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_CONVEYANCE,
+ convey_blink_info,
+ convey_blink);
+ }
+
+ {
+ spell_type *spell = spell_new(&DISARM, "Disarm");
+ spell_type_describe(spell, "Destroys doors and traps");
+ spell_type_describe(spell, "At level 10 it destroys doors and traps, then reveals and unlocks any secret");
+ spell_type_describe(spell, "doors");
+ spell_type_set_mana(spell, 2, 4);
+ spell_type_set_difficulty(spell, 3, 15);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_CONVEYANCE,
+ no_info,
+ convey_disarm);
+
+ spell_type_set_device_charges(spell, "10+d15");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 4;
+ range_init(&device_allocation->base_level, 1, 10);
+ range_init(&device_allocation->max_level, 10, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&TELEPORT, "Teleportation");
+ spell_type_describe(spell, "Teleports you around the level. The casting time decreases with level");
+ spell_type_set_mana(spell, 8, 14);
+ spell_type_set_inertia(spell, 1, 10);
+ spell_type_set_difficulty(spell, 10, 30);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_CONVEYANCE,
+ convey_teleport_info,
+ convey_teleport);
+
+ spell_type_set_device_charges(spell, "7+d7");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 50;
+ range_init(&device_allocation->base_level, 1, 20);
+ range_init(&device_allocation->max_level, 20, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&TELEAWAY, "Teleport Away");
+ spell_type_describe(spell, "Teleports a line of monsters away");
+ spell_type_describe(spell, "At level 10 it turns into a ball");
+ spell_type_describe(spell, "At level 20 it teleports all monsters in sight");
+ spell_type_set_mana(spell, 15, 40);
+ spell_type_set_difficulty(spell, 23, 60);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_CONVEYANCE,
+ no_info,
+ convey_teleport_away);
+
+ spell_type_set_device_charges(spell, "3+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 75;
+ range_init(&device_allocation->base_level, 1, 20);
+ range_init(&device_allocation->max_level, 20, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&RECALL, "Recall");
+ spell_type_describe(spell, "Cast on yourself it will recall you to the surface/dungeon.");
+ spell_type_describe(spell, "Cast at a monster you will swap positions with the monster.");
+ spell_type_describe(spell, "Cast at an object it will fetch the object to you.");
+ spell_type_set_mana(spell, 25, 25);
+ spell_type_set_difficulty(spell, 30, 60);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_CONVEYANCE,
+ convey_recall_info,
+ convey_recall);
+ }
+
+ {
+ spell_type *spell = spell_new(&PROBABILITY_TRAVEL, "Probability Travel");
+ spell_type_describe(spell, "Renders you immaterial, when you hit a wall you travel through it and");
+ spell_type_describe(spell, "instantly appear on the other side of it. You can also float up and down");
+ spell_type_describe(spell, "at will");
+ spell_type_set_mana(spell, 30, 50);
+ spell_type_set_inertia(spell, 6, 40);
+ spell_type_set_difficulty(spell, 35, 90);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_CONVEYANCE,
+ convey_probability_travel_info,
+ convey_probability_travel);
+
+ spell_type_set_device_charges(spell, "1+d2");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 97;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 8, 25);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&GROWTREE, "Grow Trees");
+ spell_type_describe(spell, "Makes trees grow extremely quickly around you");
+ spell_type_add_school(spell, SCHOOL_TEMPORAL);
+ spell_type_set_mana(spell, 6, 30);
+ spell_type_set_inertia(spell, 5, 50);
+ spell_type_set_difficulty(spell, 6, 35);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_NATURE,
+ nature_grow_trees_info,
+ nature_grow_trees);
+ }
+
+ {
+ spell_type *spell = spell_new(&HEALING, "Healing");
+ spell_type_describe(spell, "Heals a percent of hitpoints");
+ spell_type_set_mana(spell, 15, 50);
+ spell_type_set_difficulty(spell, 10, 45);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_NATURE,
+ nature_healing_info,
+ nature_healing);
+
+ spell_type_set_device_charges(spell, "2+d3");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 90;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 20, 40);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&RECOVERY, "Recovery");
+ spell_type_describe(spell, "Reduces the length of time that you are poisoned");
+ spell_type_describe(spell, "At level 5 it cures poison and cuts");
+ spell_type_describe(spell, "At level 10 it restores drained stats");
+ spell_type_describe(spell, "At level 15 it restores lost experience");
+ spell_type_set_mana(spell, 10, 25);
+ spell_type_set_inertia(spell, 2, 100);
+ spell_type_set_difficulty(spell, 15, 60);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_NATURE,
+ no_info,
+ nature_recovery);
+
+ spell_type_set_device_charges(spell, "5+d10");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 50;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 10, 30);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&REGENERATION, "Regeneration");
+ spell_type_describe(spell, "Increases your body's regeneration rate");
+ spell_type_set_mana(spell, 30, 55);
+ spell_type_set_inertia(spell, 4, 40);
+ spell_type_set_difficulty(spell, 20, 70);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_NATURE,
+ nature_regeneration_info,
+ nature_regeneration);
+ }
+
+ {
+ spell_type *spell = spell_new(&SUMMONANNIMAL, "Summon Animal");
+ spell_type_describe(spell, "Summons a leveled animal to your aid");
+ spell_type_set_mana(spell, 25, 50);
+ spell_type_set_difficulty(spell, 25, 90);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_NATURE,
+ nature_summon_animal_info,
+ nature_summon_animal);
+
+ spell_type_set_device_charges(spell, "1+d3");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 85;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 15, 45);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&STARIDENTIFY, "Greater Identify");
+ spell_type_describe(spell, "Asks for an object and fully identify it, providing the full list of powers");
+ spell_type_describe(spell, "Cast at yourself it will reveal your powers");
+ spell_type_set_mana(spell, 30, 30);
+ spell_type_set_difficulty(spell, 35, 80);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_DIVINATION,
+ no_info,
+ divination_greater_identify);
+ }
+
+ {
+ spell_type *spell = spell_new(&IDENTIFY, "Identify");
+ spell_type_describe(spell, "Asks for an object and identifies it");
+ spell_type_describe(spell, "At level 17 it identifies all objects in the inventory");
+ spell_type_describe(spell, "At level 27 it identifies all objects in the inventory and in a");
+ spell_type_describe(spell, "radius on the floor, as well as probing monsters in that radius");
+ spell_type_set_mana(spell, 10, 50);
+ spell_type_set_difficulty(spell, 8, 40);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_DIVINATION,
+ divination_identify_info,
+ divination_identify);
+
+ spell_type_set_device_charges(spell, "7+d10");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 45;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 15, 40);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&VISION, "Vision");
+ spell_type_describe(spell, "Detects the layout of the surrounding area");
+ spell_type_describe(spell, "At level 25 it maps and lights the whole level");
+ spell_type_set_mana(spell, 7, 55);
+ spell_type_set_inertia(spell, 2, 200);
+ spell_type_set_difficulty(spell, 15, 45);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_DIVINATION,
+ no_info,
+ divination_vision);
+
+ spell_type_set_device_charges(spell, "4+d6");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 60;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 10, 30);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&SENSEHIDDEN, "Sense Hidden");
+ spell_type_describe(spell, "Detects the traps in a certain radius around you");
+ spell_type_describe(spell, "At level 15 it allows you to sense invisible for a while");
+ spell_type_set_mana(spell, 2, 10);
+ spell_type_set_inertia(spell, 1, 10);
+ spell_type_set_difficulty(spell, 5, 25);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_DIVINATION,
+ divination_sense_hidden_info,
+ divination_sense_hidden);
+
+ spell_type_set_device_charges(spell, "1+d15");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 20;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 10, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&REVEALWAYS, "Reveal Ways");
+ spell_type_describe(spell, "Detects the doors/stairs/ways in a certain radius around you");
+ spell_type_set_mana(spell, 3, 15);
+ spell_type_set_inertia(spell, 1, 10);
+ spell_type_set_difficulty(spell, 9, 20);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_DIVINATION,
+ divination_reveal_ways_info,
+ divination_reveal_ways);
+
+ spell_type_set_device_charges(spell, "6+d6");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 35;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 25, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&SENSEMONSTERS, "Sense Monsters");
+ spell_type_describe(spell, "Detects all monsters near you");
+ spell_type_describe(spell, "At level 30 it allows you to sense monster minds for a while");
+ spell_type_set_mana(spell, 1, 20);
+ spell_type_set_inertia(spell, 1, 10);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_DIVINATION,
+ divination_sense_monsters_info,
+ divination_sense_monsters);
+
+ spell_type_set_device_charges(spell, "5+d10");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 37;
+ range_init(&device_allocation->base_level, 1, 10);
+ range_init(&device_allocation->max_level, 15, 40);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&MAGELOCK, "Magelock");
+ spell_type_describe(spell, "Magically locks a door");
+ spell_type_describe(spell, "At level 30 it creates a glyph of warding");
+ spell_type_describe(spell, "At level 40 the glyph can be placed anywhere in the field of vision");
+ spell_type_set_mana(spell, 1, 35);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_TEMPORAL,
+ no_info,
+ tempo_magelock);
+
+ spell_type_set_device_charges(spell, "7+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 30;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 15, 45);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&SLOWMONSTER, "Slow Monster");
+ spell_type_describe(spell, "Magically slows down the passing of time around a monster");
+ spell_type_describe(spell, "At level 20 it affects a zone");
+ spell_type_set_mana(spell, 10, 15);
+ spell_type_set_difficulty(spell, 10, 35);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_TEMPORAL,
+ tempo_slow_monster_info,
+ tempo_slow_monster);
+
+ spell_type_set_device_charges(spell, "5+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 23;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 20, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&ESSENCESPEED, "Essence of Speed");
+ spell_type_describe(spell, "Magically decreases the passing of time around you, making you move faster with");
+ spell_type_describe(spell, "respect to the rest of the universe.");
+ spell_type_set_mana(spell, 20, 40);
+ spell_type_set_inertia(spell, 5, 20);
+ spell_type_set_difficulty(spell, 15, 50);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_TEMPORAL,
+ tempo_essence_of_speed_info,
+ tempo_essence_of_speed);
+
+ spell_type_set_device_charges(spell, "3+d3");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 80;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 10, 39);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&BANISHMENT, "Banishment");
+ spell_type_describe(spell, "Disrupts the space/time continuum in your area and teleports all monsters away.");
+ spell_type_describe(spell, "At level 15 it may also lock them in a time bubble for a while.");
+ spell_type_add_school(spell, SCHOOL_CONVEYANCE);
+ spell_type_set_mana(spell, 30, 40);
+ spell_type_set_inertia(spell, 5, 50);
+ spell_type_set_difficulty(spell, 30, 95);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_TEMPORAL,
+ tempo_banishment_info,
+ tempo_banishment);
+
+ spell_type_set_device_charges(spell, "1+d3");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 98;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 10, 36);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&RECHARGE, "Recharge");
+ spell_type_describe(spell, "Taps on the ambient mana to recharge an object's power (charges or mana)");
+ spell_type_set_mana(spell, 10, 100);
+ spell_type_set_difficulty(spell, 5, 20);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_META,
+ meta_recharge_info,
+ meta_recharge);
+ }
+
+ {
+ spell_type *spell = spell_new(&SPELLBINDER, "Spellbinder");
+ spell_type_describe(spell, "Stores spells in a trigger.");
+ spell_type_describe(spell, "When the condition is met all spells fire off at the same time");
+ spell_type_describe(spell, "This spell takes a long time to cast so you are advised to prepare it");
+ spell_type_describe(spell, "in a safe area.");
+ spell_type_describe(spell, "Also it will use the mana for the Spellbinder and the mana for the");
+ spell_type_describe(spell, "selected spells");
+ spell_type_set_mana(spell, 100, 300);
+ spell_type_set_difficulty(spell, 20, 85);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_META,
+ meta_spellbinder_info,
+ meta_spellbinder);
+ }
+
+ {
+ spell_type *spell = spell_new(&DISPERSEMAGIC, "Disperse Magic");
+ spell_type_describe(spell, "Dispels a lot of magic that can affect you, be it good or bad");
+ spell_type_describe(spell, "Level 1: blindness and light");
+ spell_type_describe(spell, "Level 5: confusion and hallucination");
+ spell_type_describe(spell, "Level 10: speed (both bad or good) and light speed");
+ spell_type_describe(spell, "Level 15: stunning, meditation, cuts");
+ spell_type_describe(spell, "Level 20: hero, super hero, bless, shields, afraid, parasites, mimicry");
+ spell_type_set_mana(spell, 30, 60);
+ spell_type_set_inertia(spell, 1, 5);
+ spell_type_set_difficulty(spell, 15, 40);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_set_castable_while_confused(spell, TRUE);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_META,
+ no_info,
+ meta_disperse_magic);
+
+ spell_type_set_device_charges(spell, "5+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 25;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 5, 40);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&TRACKER, "Tracker");
+ spell_type_describe(spell, "Tracks down the last teleportation that happened on the level and teleports");
+ spell_type_describe(spell, "you to it");
+ spell_type_add_school(spell, SCHOOL_CONVEYANCE);
+ spell_type_set_mana(spell, 50, 50);
+ spell_type_set_difficulty(spell, 30, 95);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_META,
+ no_info,
+ meta_tracker);
+ }
+
+ {
+ spell_type *spell = spell_new(&INERTIA_CONTROL, "Inertia Control");
+ spell_type_describe(spell, "Changes the energy flow of a spell to be continuously recasted");
+ spell_type_describe(spell, "at a given interval. The inertia controlled spell reduces your");
+ spell_type_describe(spell, "maximum mana by four times its cost.");
+ spell_type_set_mana(spell, 300, 700);
+ spell_type_set_difficulty(spell, 37, 95);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_META,
+ meta_inertia_control_info,
+ meta_inertia_control);
+ }
+
+ {
+ spell_type *spell = spell_new(&CHARM, "Charm");
+ spell_type_describe(spell, "Tries to manipulate the mind of a monster to make it friendly");
+ spell_type_describe(spell, "At level 15 it turns into a ball");
+ spell_type_describe(spell, "At level 35 it affects all monsters in sight");
+ spell_type_set_mana(spell, 1, 20);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_MIND,
+ mind_charm_info,
+ mind_charm);
+
+ spell_type_set_device_charges(spell, "7+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 35;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 20, 40);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&CONFUSE, "Confuse");
+ spell_type_describe(spell, "Tries to manipulate the mind of a monster to confuse it");
+ spell_type_describe(spell, "At level 15 it turns into a ball");
+ spell_type_describe(spell, "At level 35 it affects all monsters in sight");
+ spell_type_set_mana(spell, 5, 30);
+ spell_type_set_difficulty(spell, 5, 20);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_MIND,
+ mind_confuse_info,
+ mind_confuse);
+
+ spell_type_set_device_charges(spell, "3+d4");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 45;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 20, 40);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&ARMOROFFEAR, "Armor of Fear");
+ spell_type_describe(spell, "Creates a shield of pure fear around you. Any monster attempting to hit you");
+ spell_type_describe(spell, "must save or flee");
+ spell_type_set_mana(spell, 10, 50);
+ spell_type_set_inertia(spell, 2, 20);
+ spell_type_set_difficulty(spell, 10, 35);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_MIND,
+ mind_armor_of_fear_info,
+ mind_armor_of_fear);
+ }
+
+ {
+ spell_type *spell = spell_new(&STUN, "Stun");
+ spell_type_describe(spell, "Tries to manipulate the mind of a monster to stun it");
+ spell_type_describe(spell, "At level 20 it turns into a ball");
+ spell_type_set_mana(spell, 10, 90);
+ spell_type_set_difficulty(spell, 15, 45);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_MIND,
+ mind_stun_info,
+ mind_stun);
+ }
+
+ {
+ spell_type *spell = spell_new(&DRAIN, "Drain");
+ spell_type_describe(spell, "Drains the mana contained in wands, staves and rods to increase yours");
+ spell_type_add_school(spell, SCHOOL_MANA);
+ spell_type_set_mana(spell, 0, 0);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_UDUN,
+ no_info,
+ udun_drain);
+ }
+
+ {
+ spell_type *spell = spell_new(&GENOCIDE, "Genocide");
+ spell_type_describe(spell, "Genocides all monsters of a race on the level");
+ spell_type_describe(spell, "At level 10 it can genocide all monsters near you");
+ spell_type_add_school(spell, SCHOOL_NATURE);
+ spell_type_set_mana(spell, 50, 50);
+ spell_type_set_difficulty(spell, 25, 90);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_UDUN,
+ no_info,
+ udun_genocide);
+
+ spell_type_set_device_charges(spell, "2+d2");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 85;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 5, 15);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&WRAITHFORM, "Wraithform");
+ spell_type_describe(spell, "Turns you into an immaterial being");
+ spell_type_add_school(spell, SCHOOL_CONVEYANCE);
+ spell_type_set_mana(spell, 20, 40);
+ spell_type_set_inertia(spell, 4, 30);
+ spell_type_set_difficulty(spell, 30, 95);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_UDUN,
+ udun_wraithform_info,
+ udun_wraithform);
+ }
+
+ {
+ spell_type *spell = spell_new(&FLAMEOFUDUN, "Flame of Udun");
+ spell_type_describe(spell, "Turns you into a powerful Balrog");
+ spell_type_add_school(spell, SCHOOL_FIRE);
+ spell_type_set_mana(spell, 70, 100);
+ spell_type_set_inertia(spell, 7, 15);
+ spell_type_set_difficulty(spell, 35, 95);
+ spell_type_init_mage(spell,
+ RANDOM,
+ SCHOOL_UDUN,
+ udun_flame_of_udun_info,
+ udun_flame_of_udun);
+ }
+
+ {
+ spell_type *spell = spell_new(&CALL_THE_ELEMENTS, "Call the Elements");
+ spell_type_describe(spell, "Randomly creates various elements around you");
+ spell_type_describe(spell, "Each type of element chance is controlled by your level");
+ spell_type_describe(spell, "in the corresponding skill");
+ spell_type_describe(spell, "At level 17 it can be targeted");
+ spell_type_set_mana(spell, 2, 20);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_mage(spell,
+ NO_RANDOM,
+ SCHOOL_GEOMANCY,
+ geomancy_call_the_elements_info,
+ geomancy_call_the_elements);
+ }
+
+ {
+ spell_type *spell = spell_new(&CHANNEL_ELEMENTS, "Channel Elements");
+ spell_type_describe(spell, "Draws on the caster's immediate environs to form an attack or other effect.");
+ spell_type_describe(spell, "Grass/Flower heals.");
+ spell_type_describe(spell, "Water creates water bolt attacks.");
+ spell_type_describe(spell, "Ice creates ice bolt attacks.");
+ spell_type_describe(spell, "Sand creates a wall of thick, blinding, burning sand around you.");
+ spell_type_describe(spell, "Lava creates fire bolt attacks.");
+ spell_type_describe(spell, "Deep lava creates fire ball attacks.");
+ spell_type_describe(spell, "Chasm creates darkness bolt attacks.");
+ spell_type_describe(spell, "At Earth level 18, darkness becomes nether.");
+ spell_type_describe(spell, "At Water level 8, water attacks become beams with a striking effect.");
+ spell_type_describe(spell, "At Water level 12, ice attacks become balls of ice shards.");
+ spell_type_describe(spell, "At Water level 18, water attacks push monsters back.");
+ spell_type_describe(spell, "At Fire level 15, fire become hellfire.");
+ spell_type_set_mana(spell, 3, 30);
+ spell_type_set_difficulty(spell, 3, 20);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_mage(spell,
+ NO_RANDOM,
+ SCHOOL_GEOMANCY,
+ no_info,
+ geomancy_channel_elements);
+ }
+
+ {
+ spell_type *spell = spell_new(&ELEMENTAL_WAVE, "Elemental Wave");
+ spell_type_describe(spell, "Draws on an adjacent special square to project a slow-moving");
+ spell_type_describe(spell, "wave of that element in that direction");
+ spell_type_describe(spell, "Abyss squares cannot be channeled into a wave.");
+ spell_type_set_mana(spell, 15, 50);
+ spell_type_set_difficulty(spell, 15, 20);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_mage(spell,
+ NO_RANDOM,
+ SCHOOL_GEOMANCY,
+ no_info,
+ geomancy_elemental_wave);
+ }
+
+ {
+ spell_type *spell = spell_new(&VAPORIZE, "Vaporize");
+ spell_type_describe(spell, "Draws upon your immediate environs to form a cloud of damaging vapors");
+ spell_type_set_mana(spell, 3, 30);
+ spell_type_set_difficulty(spell, 4, 15);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_geomancy(
+ spell,
+ geomancy_vaporize_info,
+ geomancy_vaporize,
+ geomancy_vaporize_depends);
+ }
+
+ {
+ spell_type *spell = spell_new(&GEOLYSIS, "Geolysis");
+ spell_type_describe(spell, "Burrows deeply and slightly at random into a wall,");
+ spell_type_describe(spell, "leaving behind tailings of various different sorts of walls in the passage.");
+ spell_type_set_mana(spell, 15, 40);
+ spell_type_set_difficulty(spell, 7, 15);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_geomancy(
+ spell,
+ geomancy_geolysis_info,
+ geomancy_geolysis,
+ geomancy_geolysis_depends);
+ }
+
+ {
+ spell_type *spell = spell_new(&DRIPPING_TREAD, "Dripping Tread");
+ spell_type_describe(spell, "Causes you to leave random elemental forms behind as you walk");
+ spell_type_set_mana(spell, 15, 25);
+ spell_type_set_difficulty(spell, 10, 15);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_geomancy(
+ spell,
+ geomancy_dripping_tread_info,
+ geomancy_dripping_tread,
+ geomancy_dripping_tread_depends);
+ }
+
+ {
+ spell_type *spell = spell_new(&GROW_BARRIER, "Grow Barrier");
+ spell_type_describe(spell, "Creates impassable terrain (walls, trees, etc.) around you.");
+ spell_type_describe(spell, "At level 20 it can be projected around another area.");
+ spell_type_set_mana(spell, 30, 40);
+ spell_type_set_difficulty(spell, 12, 15);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_geomancy(
+ spell,
+ no_info,
+ geomancy_grow_barrier,
+ geomancy_grow_barrier_depends);
+ }
+
+ {
+ spell_type *spell = spell_new(&ELEMENTAL_MINION, "Elemental Minion");
+ spell_type_describe(spell, "Summons a minion from a nearby element.");
+ spell_type_describe(spell, "Walls can summon Earth elmentals, Xorns and Xarens");
+ spell_type_describe(spell, "Dark Pits can summon Air elementals, Ancient blue dragons, Great Storm Wyrms");
+ spell_type_describe(spell, "and Sky Drakes");
+ spell_type_describe(spell, "Sandwalls and lava can summon Fire elementals and Ancient red dragons");
+ spell_type_describe(spell, "Icewall, and water can summon Water elementals, Water trolls and Water demons");
+ spell_type_set_mana(spell, 40, 80);
+ spell_type_set_difficulty(spell, 20, 25);
+ spell_type_init_geomancy(
+ spell,
+ geomancy_elemental_minion_info,
+ geomancy_elemental_minion,
+ NULL);
+ }
+
+ {
+ spell_type *spell = spell_new(&ERU_SEE, "See the Music");
+ spell_type_describe(spell, "Allows you to 'see' the Great Music from which the world");
+ spell_type_describe(spell, "originates, allowing you to see unseen things");
+ spell_type_describe(spell, "At level 10 it allows you to see your surroundings");
+ spell_type_describe(spell, "At level 20 it allows you to cure blindness");
+ spell_type_describe(spell, "At level 30 it allows you to fully see all the level");
+ spell_type_set_mana(spell, 1, 50);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_ERU,
+ eru_see_the_music_info,
+ eru_see_the_music);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ }
+
+ {
+ spell_type *spell = spell_new(&ERU_LISTEN, "Listen to the Music");
+ spell_type_describe(spell, "Allows you to listen to the Great Music from which the world");
+ spell_type_describe(spell, "originates, allowing you to understand the meaning of things");
+ spell_type_describe(spell, "At level 14 it allows you to identify all your pack");
+ spell_type_describe(spell, "At level 30 it allows you to identify all items on the level");
+ spell_type_set_mana(spell, 15, 200);
+ spell_type_set_difficulty(spell, 7, 25);
+ spell_type_init_priest(spell,
+ SCHOOL_ERU,
+ no_info,
+ eru_listen_to_the_music);
+ }
+
+ {
+ spell_type *spell = spell_new(&ERU_UNDERSTAND, "Know the Music");
+ spell_type_describe(spell, "Allows you to understand the Great Music from which the world");
+ spell_type_describe(spell, "originates, allowing you to know the full abilities of things");
+ spell_type_describe(spell, "At level 10 it allows you to *identify* all your pack");
+ spell_type_set_mana(spell, 200, 600);
+ spell_type_set_difficulty(spell, 30, 50);
+ spell_type_init_priest(spell,
+ SCHOOL_ERU,
+ no_info,
+ eru_know_the_music);
+ }
+
+ {
+ spell_type *spell = spell_new(&ERU_PROT, "Lay of Protection");
+ spell_type_describe(spell, "Creates a circle of safety around you");
+ spell_type_set_mana(spell, 400, 400);
+ spell_type_set_difficulty(spell, 35, 80);
+ spell_type_init_priest(spell,
+ SCHOOL_ERU,
+ eru_lay_of_protection_info,
+ eru_lay_of_protection);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANWE_SHIELD, "Wind Shield");
+ spell_type_describe(spell, "It surrounds you with a shield of wind that deflects blows from evil monsters");
+ spell_type_describe(spell, "At level 10 it increases your armour rating");
+ spell_type_describe(spell, "At level 20 it retaliates against monsters that melee you");
+ spell_type_set_mana(spell, 100, 500);
+ spell_type_set_difficulty(spell, 10, 30);
+ spell_type_init_priest(spell,
+ SCHOOL_MANWE,
+ manwe_wind_shield_info,
+ manwe_wind_shield);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANWE_AVATAR, "Avatar");
+ spell_type_describe(spell, "It turns you into a full grown Maia");
+ spell_type_set_mana(spell, 1000, 1000);
+ spell_type_set_difficulty(spell, 35, 80);
+ spell_type_init_priest(spell,
+ SCHOOL_MANWE,
+ manwe_avatar_info,
+ manwe_avatar);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANWE_BLESS, "Manwe's Blessing");
+ spell_type_describe(spell, "Manwe's Blessing removes your fears, blesses you and surrounds you with");
+ spell_type_describe(spell, "holy light");
+ spell_type_describe(spell, "At level 10 it also grants heroism");
+ spell_type_describe(spell, "At level 20 it also grants super heroism");
+ spell_type_describe(spell, "At level 30 it also grants holy luck and life protection");
+ spell_type_set_mana(spell, 10, 100);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_MANWE,
+ manwe_blessing_info,
+ manwe_blessing);
+ }
+
+ {
+ spell_type *spell = spell_new(&MANWE_CALL, "Manwe's Call");
+ spell_type_describe(spell, "Manwe's Call summons a Great Eagle to help you battle the forces");
+ spell_type_describe(spell, "of Morgoth");
+ spell_type_set_mana(spell, 200, 500);
+ spell_type_set_difficulty(spell, 20, 40);
+ spell_type_init_priest(spell,
+ SCHOOL_MANWE,
+ manwe_call_info,
+ manwe_call);
+ }
+
+ {
+ spell_type *spell = spell_new(&TULKAS_AIM, "Divine Aim");
+ spell_type_describe(spell, "It makes you more accurate in combat");
+ spell_type_describe(spell, "At level 20 all your blows are critical hits");
+ spell_type_set_mana(spell, 30, 500);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_TULKAS,
+ tulkas_divine_aim_info,
+ tulkas_divine_aim);
+ }
+
+ {
+ spell_type *spell = spell_new(&TULKAS_WAVE, "Wave of Power");
+ spell_type_describe(spell, "It allows you to project a number of melee blows across a distance");
+ spell_type_set_mana(spell, 200, 200);
+ spell_type_set_difficulty(spell, 20, 75);
+ spell_type_init_priest(spell,
+ SCHOOL_TULKAS,
+ tulkas_wave_of_power_info,
+ tulkas_wave_of_power);
+ }
+
+ {
+ spell_type *spell = spell_new(&TULKAS_SPIN, "Whirlwind");
+ spell_type_describe(spell, "It allows you to spin around and hit all monsters nearby");
+ spell_type_set_mana(spell, 100, 100);
+ spell_type_set_difficulty(spell, 10, 45);
+ spell_type_init_priest(spell,
+ SCHOOL_TULKAS,
+ no_info,
+ tulkas_whirlwind);
+ }
+
+ {
+ spell_type *spell = spell_new(&MELKOR_CURSE, "Curse");
+ spell_type_describe(spell, "It curses a monster, reducing its melee power");
+ spell_type_describe(spell, "At level 5 it can be auto-casted (with no piety cost) while fighting");
+ spell_type_describe(spell, "At level 15 it also reduces armor");
+ spell_type_describe(spell, "At level 25 it also reduces speed");
+ spell_type_describe(spell, "At level 35 it also reduces max life (but it is never fatal)");
+ spell_type_set_mana(spell, 50, 300);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_init_priest(spell,
+ SCHOOL_MELKOR,
+ no_info,
+ melkor_curse);
+ }
+
+ {
+ spell_type *spell = spell_new(&MELKOR_CORPSE_EXPLOSION, "Corpse Explosion");
+ spell_type_describe(spell, "It makes corpses in an area around you explode for a percent of their");
+ spell_type_describe(spell, "hit points as damage");
+ spell_type_set_mana(spell, 100, 500);
+ spell_type_set_difficulty(spell, 10, 45);
+ spell_type_init_priest(spell,
+ SCHOOL_MELKOR,
+ melkor_corpse_explosion_info,
+ melkor_corpse_explosion);
+ }
+
+ {
+ spell_type *spell = spell_new(&MELKOR_MIND_STEAL, "Mind Steal");
+ spell_type_describe(spell, "It allows your spirit to temporarily leave your own body, which will");
+ spell_type_describe(spell, "be vulnerable, to control one of your enemies body.");
+ spell_type_set_mana(spell, 1000, 3000);
+ spell_type_set_difficulty(spell, 20, 90);
+ spell_type_init_priest(spell,
+ SCHOOL_MELKOR,
+ melkor_mind_steal_info,
+ melkor_mind_steal);
+ }
+
+ {
+ spell_type *spell = spell_new(&YAVANNA_CHARM_ANIMAL, "Charm Animal");
+ spell_type_describe(spell, "It tries to tame an animal");
+ spell_type_set_mana(spell, 10, 100);
+ spell_type_set_difficulty(spell, 1, 30);
+ spell_type_init_priest(spell,
+ SCHOOL_YAVANNA,
+ yavanna_charm_animal_info,
+ yavanna_charm_animal);
+ }
+
+ {
+ spell_type *spell = spell_new(&YAVANNA_GROW_GRASS, "Grow Grass");
+ spell_type_describe(spell, "Create a floor of grass around you. While on grass and praying");
+ spell_type_describe(spell, "a worshipper of Yavanna will know a greater regeneration rate");
+ spell_type_set_mana(spell, 70, 150);
+ spell_type_set_difficulty(spell, 10, 65);
+ spell_type_init_priest(spell,
+ SCHOOL_YAVANNA,
+ yavanna_grow_grass_info,
+ yavanna_grow_grass);
+ }
+
+ {
+ spell_type *spell = spell_new(&YAVANNA_TREE_ROOTS, "Tree Roots");
+ spell_type_describe(spell, "Creates roots deep in the floor from your feet, making you more stable and able");
+ spell_type_describe(spell, "to make stronger attacks, but prevents any movement (even teleportation).");
+ spell_type_describe(spell, "It also makes you recover from stunning almost immediately.");
+ spell_type_set_mana(spell, 50, 1000);
+ spell_type_set_difficulty(spell, 15, 70);
+ spell_type_init_priest(spell,
+ SCHOOL_YAVANNA,
+ yavanna_tree_roots_info,
+ yavanna_tree_roots);
+ }
+
+ {
+ spell_type *spell = spell_new(&YAVANNA_WATER_BITE, "Water Bite");
+ spell_type_describe(spell, "Imbues your melee weapon with a natural stream of water");
+ spell_type_describe(spell, "At level 25, it spreads over a 1 radius zone around your target");
+ spell_type_set_mana(spell, 150, 300);
+ spell_type_set_difficulty(spell, 20, 90);
+ spell_type_init_priest(spell,
+ SCHOOL_YAVANNA,
+ yavanna_water_bite_info,
+ yavanna_water_bite);
+ }
+
+ {
+ spell_type *spell = spell_new(&YAVANNA_UPROOT, "Uproot");
+ spell_type_describe(spell, "Awakes a tree to help you battle the forces of Morgoth");
+ spell_type_set_mana(spell, 250, 350);
+ spell_type_set_difficulty(spell, 35, 95);
+ spell_type_init_priest(spell,
+ SCHOOL_YAVANNA,
+ yavanna_uproot_info,
+ yavanna_uproot);
+ }
+
+ {
+ spell_type *spell = spell_new(&DEMON_BLADE, "Demon Blade");
+ spell_type_describe(spell, "Imbues your blade with fire to deal more damage");
+ spell_type_describe(spell, "At level 30 it deals hellfire damage");
+ spell_type_describe(spell, "At level 45 it spreads over a 1 radius zone around your target");
+ spell_type_set_mana(spell, 4, 44);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_init_demonology(spell,
+ demonology_demon_blade_info,
+ demonology_demon_blade);
+
+ spell_type_set_device_charges(spell, "3+d7");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 75;
+ range_init(&device_allocation->base_level, 1, 17);
+ range_init(&device_allocation->max_level, 20, 40);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&DEMON_MADNESS, "Demon Madness");
+ spell_type_describe(spell, "Fire 2 balls in opposite directions of randomly chaos, confusion or charm");
+ spell_type_set_mana(spell, 5, 20);
+ spell_type_set_difficulty(spell, 10, 25);
+ spell_type_init_demonology(spell,
+ demonology_demon_madness_info,
+ demonology_demon_madness);
+ }
+
+ {
+ spell_type *spell = spell_new(&DEMON_FIELD, "Demon Field");
+ spell_type_describe(spell, "Fires a cloud of deadly nexus over a radius of 7");
+ spell_type_set_mana(spell, 20, 60);
+ spell_type_set_difficulty(spell, 20, 60);
+ spell_type_init_demonology(spell,
+ demonology_demon_field_info,
+ demonology_demon_field);
+ }
+
+ {
+ spell_type *spell = spell_new(&DOOM_SHIELD, "Doom Shield");
+ spell_type_describe(spell, "Raises a mirror of pain around you, doing very high damage to your foes");
+ spell_type_describe(spell, "that dare hit you, but greatly reduces your armour class");
+ spell_type_set_mana(spell, 2, 30);
+ spell_type_set_difficulty(spell, 1, 10);
+ spell_type_init_demonology(spell,
+ demonology_doom_shield_info,
+ demonology_doom_shield);
+ }
+
+ {
+ spell_type *spell = spell_new(&UNHOLY_WORD, "Unholy Word");
+ spell_type_describe(spell, "Kills a pet to heal you");
+ spell_type_describe(spell, "There is a chance that the pet won't die but will turn against you");
+ spell_type_describe(spell, "it will decrease with higher level");
+ spell_type_set_mana(spell, 15, 45);
+ spell_type_set_difficulty(spell, 25, 55);
+ spell_type_init_demonology(spell,
+ demonology_unholy_word_info,
+ demonology_unholy_word);
+ }
+
+ {
+ spell_type *spell = spell_new(&DEMON_CLOAK, "Demon Cloak");
+ spell_type_describe(spell, "Raises a mirror that can reflect bolts and arrows for a time");
+ spell_type_set_mana(spell, 10, 40);
+ spell_type_set_difficulty(spell, 20, 70);
+ spell_type_init_demonology(spell,
+ demonology_demon_cloak_info,
+ demonology_demon_cloak);
+ }
+
+ {
+ spell_type *spell = spell_new(&DEMON_SUMMON, "Summon Demon");
+ spell_type_describe(spell, "Summons a leveled demon to your side");
+ spell_type_describe(spell, "At level 35 it summons a high demon");
+ spell_type_set_mana(spell, 10, 50);
+ spell_type_set_difficulty(spell, 5, 30);
+ spell_type_init_demonology(spell,
+ demonology_summon_demon_info,
+ demonology_summon_demon);
+ }
+
+ {
+ spell_type *spell = spell_new(&DISCHARGE_MINION, "Discharge Minion");
+ spell_type_describe(spell, "The targeted pet will explode in a burst of gravity");
+ spell_type_set_mana(spell, 20, 50);
+ spell_type_set_difficulty(spell, 10, 30);
+ spell_type_init_demonology(spell,
+ demonology_discharge_minion_info,
+ demonology_discharge_minion);
+ }
+
+ {
+ spell_type *spell = spell_new(&CONTROL_DEMON, "Control Demon");
+ spell_type_describe(spell, "Attempts to control a demon");
+ spell_type_set_mana(spell, 30, 70);
+ spell_type_set_difficulty(spell, 25, 55);
+ spell_type_init_demonology(spell,
+ demonology_control_demon_info,
+ demonology_control_demon);
+ }
+
+ {
+ spell_type *spell = spell_new(&DEVICE_HEAL_MONSTER, "Heal Monster");
+ spell_type_describe(spell, "Heals a monster");
+ spell_type_set_mana(spell, 5, 20);
+ spell_type_set_difficulty(spell, 3, 15);
+ spell_type_init_device(spell,
+ device_heal_monster_info,
+ device_heal_monster);
+
+ spell_type_set_device_charges(spell, "10+d10");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 17;
+ range_init(&device_allocation->base_level, 1, 15);
+ range_init(&device_allocation->max_level, 20, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&DEVICE_SPEED_MONSTER, "Haste Monster");
+ spell_type_describe(spell, "Haste a monster");
+ spell_type_set_mana(spell, 10, 10);
+ spell_type_set_difficulty(spell, 10, 30);
+ spell_type_init_device(spell,
+ device_haste_monster_info,
+ device_haste_monster);
+
+ spell_type_set_device_charges(spell, "10+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 7;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 20, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&DEVICE_WISH, "Wish");
+ spell_type_describe(spell, "This grants you a wish, beware of what you ask for!");
+ spell_type_set_mana(spell, 400, 400);
+ spell_type_set_difficulty(spell, 50, 99);
+ spell_type_init_device(spell,
+ no_info,
+ device_wish);
+
+ spell_type_set_device_charges(spell, "1+d2");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 98;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 1, 1);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&DEVICE_SUMMON, "Summon");
+ spell_type_describe(spell, "Summons hostile monsters near you");
+ spell_type_set_mana(spell, 5, 25);
+ spell_type_set_difficulty(spell, 5, 20);
+ spell_type_init_device(spell,
+ no_info,
+ device_summon_monster);
+
+ spell_type_set_device_charges(spell, "1+d20");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 13;
+ range_init(&device_allocation->base_level, 1, 40);
+ range_init(&device_allocation->max_level, 25, 50);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&DEVICE_MANA, "Mana");
+ spell_type_describe(spell, "Restores a part(or all) of your mana");
+ spell_type_set_mana(spell, 1, 1);
+ spell_type_set_difficulty(spell, 30, 80);
+ spell_type_init_device(spell,
+ device_mana_info,
+ device_mana);
+
+ spell_type_set_device_charges(spell, "2+d3");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 78;
+ range_init(&device_allocation->base_level, 1, 5);
+ range_init(&device_allocation->max_level, 20, 35);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&DEVICE_NOTHING, "Nothing");
+ spell_type_describe(spell, "It does nothing.");
+ spell_type_set_mana(spell, 0, 0);
+ spell_type_set_difficulty(spell, 1, 0);
+ spell_type_init_device(spell,
+ no_info,
+ device_nothing);
+
+ spell_type_set_device_charges(spell, "0+d0");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 3;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 1, 1);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_WAND);
+ device_allocation->rarity = 3;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 1, 1);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&DEVICE_HOLY_FIRE, "Holy Fire of Mithrandir");
+ spell_type_describe(spell, "The Holy Fire created by this staff will deeply(double damage) burn");
+ spell_type_describe(spell, "all that is evil.");
+ spell_type_set_mana(spell, 50, 150);
+ spell_type_set_difficulty(spell, 30, 75);
+ spell_type_init_device(spell,
+ device_holy_fire_info,
+ device_holy_fire);
+
+ spell_type_set_device_charges(spell, "2+d5");
+
+ {
+ device_allocation *device_allocation = device_allocation_new(TV_STAFF);
+ device_allocation->rarity = 999;
+ range_init(&device_allocation->base_level, 1, 1);
+ range_init(&device_allocation->max_level, 35, 35);
+ spell_type_add_device_allocation(spell, device_allocation);
+ }
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_STOP, "Stop singing(I)");
+ spell_type_describe(spell, "Stops the current song, if any.");
+ spell_type_set_mana(spell, 0, 0);
+ spell_type_set_difficulty(spell, 1, -400);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_music(spell,
+ 1,
+ no_info,
+ music_stop_singing_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_HOLD, "Holding Pattern(I)");
+ spell_type_describe(spell, "Slows down all monsters listening the song.");
+ spell_type_describe(spell, "Consumes the amount of mana each turn.");
+ spell_type_set_mana(spell, 1, 10);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_music_lasting(
+ spell,
+ 1,
+ music_holding_pattern_info,
+ music_holding_pattern_spell,
+ music_holding_pattern_lasting);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_CONF, "Illusion Pattern(II)");
+ spell_type_describe(spell, "Tries to confuse all monsters listening the song.");
+ spell_type_describe(spell, "Consumes the amount of mana each turn.");
+ spell_type_set_mana(spell, 2, 15);
+ spell_type_set_difficulty(spell, 5, 30);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_music_lasting(
+ spell,
+ 2,
+ music_illusion_pattern_info,
+ music_illusion_pattern_spell,
+ music_illusion_pattern_lasting);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_STUN, "Stun Pattern(IV)");
+ spell_type_describe(spell, "Stuns all monsters listening the song.");
+ spell_type_describe(spell, "Consumes the amount of mana each turn.");
+ spell_type_set_mana(spell, 3, 25);
+ spell_type_set_difficulty(spell, 10, 45);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_music_lasting(
+ spell,
+ 4,
+ music_stun_pattern_info,
+ music_stun_pattern_spell,
+ music_stun_pattern_lasting);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_LITE, "Song of the Sun(I)");
+ spell_type_describe(spell, "Provides light as long as you sing.");
+ spell_type_describe(spell, "Consumes the amount of mana each turn.");
+ spell_type_set_mana(spell, 1, 1);
+ spell_type_set_difficulty(spell, 1, 20);
+ spell_type_set_castable_while_blind(spell, TRUE);
+ spell_type_init_music_lasting(
+ spell,
+ 1,
+ no_info,
+ music_song_of_the_sun_spell,
+ music_song_of_the_sun_lasting);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_HEAL, "Flow of Life(II)");
+ spell_type_describe(spell, "Heals you as long as you sing.");
+ spell_type_describe(spell, "Consumes the amount of mana each turn.");
+ spell_type_set_mana(spell, 5, 30);
+ spell_type_set_difficulty(spell, 7, 35);
+ spell_type_init_music_lasting(
+ spell,
+ 2,
+ music_flow_of_life_info,
+ music_flow_of_life_spell,
+ music_flow_of_life_lasting);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_HERO, "Heroic Ballad(II)");
+ spell_type_describe(spell, "Increases melee accuracy");
+ spell_type_describe(spell, "At level 10 it increases it even more and reduces armour a bit");
+ spell_type_describe(spell, "At level 20 it increases it again");
+ spell_type_describe(spell, "At level 25 it grants protection against chaos and confusion");
+ spell_type_describe(spell, "Consumes the amount of mana each turn.");
+ spell_type_set_mana(spell, 4, 14);
+ spell_type_set_difficulty(spell, 10, 45);
+ spell_type_init_music_lasting(
+ spell,
+ 2,
+ no_info,
+ music_heroic_ballad_spell,
+ music_heroic_ballad_lasting);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_TIME, "Hobbit Melodies(III)");
+ spell_type_describe(spell, "Greatly increases your reflexes allowing you to block more melee blows.");
+ spell_type_describe(spell, "At level 15 it also makes you faster.");
+ spell_type_describe(spell, "Consumes the amount of mana each turn.");
+ spell_type_set_mana(spell, 10, 30);
+ spell_type_set_difficulty(spell, 20, 70);
+ spell_type_init_music_lasting(
+ spell,
+ 3,
+ music_hobbit_melodies_info,
+ music_hobbit_melodies_spell,
+ music_hobbit_melodies_lasting);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_MIND, "Clairaudience(IV)");
+ spell_type_describe(spell, "Allows you to sense monster minds as long as you sing.");
+ spell_type_describe(spell, "At level 10 it identifies all objects in a radius on the floor,");
+ spell_type_describe(spell, "as well as probing monsters in that radius.");
+ spell_type_describe(spell, "Consumes the amount of mana each turn.");
+ spell_type_set_mana(spell, 15, 30);
+ spell_type_set_difficulty(spell, 25, 75);
+ spell_type_init_music_lasting(
+ spell,
+ 4,
+ music_clairaudience_info,
+ music_clairaudience_spell,
+ music_clairaudience_lasting);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_BLOW, "Blow(I)");
+ spell_type_describe(spell, "Produces a powerful, blowing, sound all around you.");
+ spell_type_set_mana(spell, 3, 30);
+ spell_type_set_difficulty(spell, 4, 20);
+ spell_type_init_music(spell,
+ 1,
+ music_blow_info,
+ music_blow_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_WIND, "Gush of Wind(II)");
+ spell_type_describe(spell, "Produces a outgoing gush of wind that sends monsters away.");
+ spell_type_set_mana(spell, 15, 45);
+ spell_type_set_difficulty(spell, 14, 30);
+ spell_type_init_music(spell,
+ 2,
+ music_gush_of_wind_info,
+ music_gush_of_wind_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_YLMIR, "Horns of Ylmir(III)");
+ spell_type_describe(spell, "Produces an earth shaking sound.");
+ spell_type_set_mana(spell, 25, 30);
+ spell_type_set_difficulty(spell, 20, 20);
+ spell_type_init_music(spell,
+ 3,
+ music_horns_of_ylmir_info,
+ music_horns_of_ylmir_spell);
+ }
+
+ {
+ spell_type *spell = spell_new(&MUSIC_AMBARKANTA, "Ambarkanta(IV)");
+ spell_type_describe(spell, "Produces a reality shaking sound that transports you to a nearly");
+ spell_type_describe(spell, "identical reality.");
+ spell_type_set_mana(spell, 70, 70);
+ spell_type_set_difficulty(spell, 25, 60);
+ spell_type_init_music(spell,
+ 4,
+ no_info,
+ music_ambarkanta_spell);
+ }
+
+ /* Module-specific spells */
+ switch (game_module_idx)
+ {
+ case MODULE_TOME:
+ spells_init_tome();
+ break;
+ case MODULE_THEME:
+ spells_init_theme();
+ break;
+ default:
+ assert(FALSE);
+ }
+
+}
diff --git a/src/spells5.hpp b/src/spells5.hpp
new file mode 100644
index 00000000..7359fa16
--- /dev/null
+++ b/src/spells5.hpp
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "h-basic.h"
+
+void school_spells_init();
+struct spell_type *spell_at(s32b index);
+s16b get_random_spell(s16b random_type, int lev);
+s16b get_random_stick(byte tval, int level);
+int find_spell(cptr name);
diff --git a/src/spells6.cc b/src/spells6.cc
new file mode 100644
index 00000000..a4564ae3
--- /dev/null
+++ b/src/spells6.cc
@@ -0,0 +1,402 @@
+#include "spells6.hpp"
+
+#include "gods.hpp"
+#include "lua_bind.hpp"
+#include "object2.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "skills.hpp"
+#include "skill_type.hpp"
+#include "spell_type.hpp"
+#include "spells4.hpp"
+#include "variable.hpp"
+
+#include <cassert>
+#include <vector>
+#include <type_traits>
+
+struct school_provider
+{
+ byte deity_idx; /* Deity which provides school levels */
+
+ s16b skill_idx; /* Skill used for determining the boost */
+
+ long mul; /* Multiplier */
+
+ long div; /* Divisor */
+};
+
+struct school_provider_list {
+public:
+ std::vector<school_provider> v;
+};
+
+static school_provider school_provider_new(byte deity_idx, long mul, long div)
+{
+ school_provider p;
+ p.deity_idx = deity_idx;
+ p.skill_idx = SKILL_PRAY;
+ p.mul = mul;
+ p.div = div;
+ return p;
+}
+
+school_type *school_at(int index)
+{
+ assert(index >= 0);
+ assert(index < schools_count);
+
+ return &schools[index];
+}
+
+static void school_init(school_type *school, cptr name, s16b skill)
+{
+ assert(school != NULL);
+
+ static_assert(std::is_pod<school_type>::value, "Cannot initialize non-POD using memset!");
+ memset(school, 0, sizeof(school_type));
+
+ school->providers = new school_provider_list();
+ school->name = name;
+ school->skill = skill;
+
+ school->deity_idx = -1;
+}
+
+static school_type *school_new(s32b *school_idx, cptr name, s16b skill)
+{
+ assert(schools_count < SCHOOLS_MAX);
+
+ *school_idx = schools_count;
+ schools_count++;
+
+ school_type *school = &schools[*school_idx];
+ school_init(school, name, skill);
+
+ return school;
+}
+
+static school_type *sorcery_school_new(s32b *school_idx, cptr name, s16b skill)
+{
+ school_type *school = school_new(school_idx, name, skill);
+ school->spell_power = TRUE;
+ school->sorcery = TRUE;
+ return school;
+}
+
+static school_type *god_school_new(s32b *school_idx, byte god)
+{
+ school_type *school = NULL;
+ deity_type *deity = NULL;
+
+ /* Get the god */
+ deity = god_at(god);
+ assert(deity != NULL);
+
+ /* Ignore gods which aren't enabled for this module. */
+ if (god_enabled(deity))
+ {
+ school = school_new(school_idx, deity->name, SKILL_PRAY);
+ school->spell_power = TRUE;
+ school->deity_idx = god;
+ school->deity = deity;
+ return school;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+static void school_god(school_type *school, byte god, int mul, int div)
+{
+ assert(school->providers != nullptr);
+
+ deity_type *deity = god_at(god);
+ assert(deity != NULL);
+
+ /* Ignore gods which aren't enabled for this module. */
+ if (god_enabled(deity))
+ {
+ school->providers->v.push_back(school_provider_new(god, mul, div));
+ }
+}
+
+static int udun_bonus_levels()
+{
+ return (p_ptr->lev * 2) / 3;
+}
+
+static bool_ geomancy_depends_satisfied()
+{
+ object_type *o_ptr = NULL;
+
+ /* Require at least one point in each school */
+ if ((get_skill(SKILL_FIRE) <= 0) ||
+ (get_skill(SKILL_AIR) <= 0) ||
+ (get_skill(SKILL_EARTH) <= 0) ||
+ (get_skill(SKILL_WATER) <= 0))
+ {
+ return FALSE;
+ }
+
+ /* Require to wield a Mage Staff, as the spells requries the
+ * caster to stomp the floor with it. */
+ o_ptr = get_object(INVEN_WIELD);
+
+ return ((o_ptr != NULL) &&
+ (o_ptr->k_idx > 0) &&
+ (o_ptr->tval == TV_MSTAFF));
+}
+
+long get_provided_levels(school_type *school)
+{
+ for (auto school_provider: school->providers->v)
+ {
+ if (school_provider.deity_idx == p_ptr->pgod)
+ {
+ return (s_info[school_provider.skill_idx].value * school_provider.mul) / school_provider.div;
+ }
+ }
+
+ return 0;
+}
+
+struct get_level_school_callback_data {
+ bool_ allow_spell_power;
+ long bonus;
+ long lvl;
+ long num;
+};
+
+static bool get_level_school_callback(struct get_level_school_callback_data *data, int school_idx)
+{
+ school_type *school = school_at(school_idx);
+ long r = 0, s = 0, p = 0, ok = 0;
+
+ /* Does it require we worship a specific god? */
+ if ((school->deity_idx > 0) &&
+ (school->deity_idx != p_ptr->pgod))
+ {
+ return false;
+ }
+
+ /* Take the basic skill value */
+ r = s_info[school->skill].value;
+
+ /* Do we pass tests? */
+ if ((school->depends_satisfied != NULL) &&
+ (!school->depends_satisfied()))
+ {
+ return false;
+ }
+
+ /* Include effects of Sorcery (if applicable) */
+ if (school->sorcery)
+ {
+ s = s_info[SKILL_SORCERY].value;
+ }
+
+ /* Include effects of Spell Power? Every school must
+ * allow use of Spell Power for it to apply. */
+ if (!school->spell_power)
+ {
+ data->allow_spell_power = FALSE;
+ }
+
+ /* Calculate effects of provided levels */
+ p = get_provided_levels(school);
+
+ /* Find the highest of Skill, Sorcery and Provided levels. */
+ ok = r;
+ if (ok < s)
+ {
+ ok = s;
+ }
+ if (ok < p)
+ {
+ ok = p;
+ }
+
+ /* Do we need to add a special bonus? */
+ if (school->bonus_levels != NULL)
+ {
+ data->bonus += (school->bonus_levels() * (SKILL_STEP / 10));
+ }
+
+ /* All schools must be non-zero to be able to use it. */
+ if (ok <= 0)
+ {
+ return false;
+ }
+
+ /* Apply it */
+ data->lvl += ok;
+ data->num += 1;
+
+ /* Keep going */
+ return true;
+}
+
+void get_level_school(spell_type *spell, s32b max, s32b min, s32b *level, bool_ *na)
+{
+ assert(level != NULL);
+ assert(na != NULL);
+
+ /* Do we pass tests? */
+ if (!spell_type_dependencies_satisfied(spell))
+ {
+ *level = min;
+ *na = TRUE;
+ return;
+ }
+
+ /* Set up initial state */
+ get_level_school_callback_data data;
+ data.allow_spell_power = TRUE;
+ data.bonus = 0;
+ data.lvl = 0;
+ data.num = 0;
+
+ // Go through all the spell's schools and count up all the
+ // levels and make sure we can actually cast the spell.
+ for (auto school_idx : spell_type_get_schools(spell))
+ {
+ if (!get_level_school_callback(&data, school_idx))
+ {
+ *level = min;
+ *na = TRUE;
+ return;
+ }
+ }
+
+ /* Add the Spellpower skill as a bonus on top */
+ if (data.allow_spell_power)
+ {
+ data.bonus += (get_skill_scale(SKILL_SPELL, 20) * (SKILL_STEP / 10));
+ }
+
+ /* Add bonus from objects */
+ data.bonus += (p_ptr->to_s * (SKILL_STEP / 10));
+
+ /* We divide by 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
+ * point is 1000 internally. */
+ data.lvl = (data.lvl / data.num) / 10;
+ data.lvl = lua_get_level(spell, data.lvl, max, min, data.bonus);
+
+ /* Result */
+ *level = data.lvl;
+ *na = FALSE;
+}
+
+void schools_init()
+{
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_MANA, "Mana", SKILL_MANA);
+ school_god(school, GOD_ERU, 1, 2);
+ school_god(school, GOD_VARDA, 1, 4);
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_FIRE, "Fire", SKILL_FIRE);
+ school_god(school, GOD_AULE, 3, 5);
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_AIR, "Air", SKILL_AIR);
+ school_god(school, GOD_MANWE, 2, 3);
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_WATER, "Water", SKILL_WATER);
+ school_god(school, GOD_YAVANNA, 1, 2);
+ school_god(school, GOD_ULMO, 3, 5);
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_EARTH, "Earth", SKILL_EARTH);
+ school_god(school, GOD_TULKAS, 4, 5);
+ school_god(school, GOD_YAVANNA, 1, 2);
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_CONVEYANCE, "Conveyance", SKILL_CONVEYANCE);
+ school_god(school, GOD_MANWE, 1, 2);
+ }
+
+ {
+ school_type *school = school_new(&SCHOOL_GEOMANCY, "Geomancy", SKILL_GEOMANCY);
+ school->spell_power = TRUE;
+ school->depends_satisfied = geomancy_depends_satisfied;
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_DIVINATION, "Divination", SKILL_DIVINATION);
+ school_god(school, GOD_ERU, 2, 3);
+ school_god(school, GOD_MANDOS, 1, 3);
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_TEMPORAL, "Temporal", SKILL_TEMPORAL);
+ school_god(school, GOD_YAVANNA, 1, 6);
+ school_god(school, GOD_MANDOS, 1, 4);
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_NATURE, "Nature", SKILL_NATURE);
+ school_god(school, GOD_YAVANNA, 1, 2);
+ school_god(school, GOD_ULMO, 1, 2);
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_META, "Meta", SKILL_META);
+ school_god(school, GOD_MANWE, 1, 3);
+ school_god(school, GOD_VARDA, 1, 2);
+ }
+
+ {
+ school_type *school = sorcery_school_new(&SCHOOL_MIND, "Mind", SKILL_MIND);
+ school_god(school, GOD_ERU, 1, 3);
+ school_god(school, GOD_MELKOR, 1, 3);
+ }
+
+ {
+ school_type *school = school_new(&SCHOOL_UDUN, "Udun", SKILL_UDUN);
+ school->bonus_levels = udun_bonus_levels;
+ }
+
+ {
+ school_new(&SCHOOL_DEMON, "Demon", SKILL_DAEMON);
+ }
+
+ /* God-specific schools; all with a standard setup */
+ {
+ god_school_new(&SCHOOL_ERU, GOD_ERU);
+ god_school_new(&SCHOOL_MANWE, GOD_MANWE);
+ god_school_new(&SCHOOL_TULKAS, GOD_TULKAS);
+ god_school_new(&SCHOOL_MELKOR, GOD_MELKOR);
+ god_school_new(&SCHOOL_YAVANNA, GOD_YAVANNA);
+
+ god_school_new(&SCHOOL_AULE, GOD_AULE);
+ god_school_new(&SCHOOL_VARDA, GOD_VARDA);
+ god_school_new(&SCHOOL_ULMO, GOD_ULMO);
+ god_school_new(&SCHOOL_MANDOS, GOD_MANDOS);
+ }
+
+ /* Placeholder schools */
+ {
+ school_new(&SCHOOL_DEVICE, "Device", SKILL_DEVICE);
+ school_new(&SCHOOL_MUSIC, "Music", SKILL_MUSIC);
+ }
+
+}
+
+void mana_school_calc_mana(int *msp)
+{
+ if (get_skill(SKILL_MANA) >= 35)
+ {
+ *msp = *msp + (*msp * ((get_skill(SKILL_MANA) - 34)) / 100);
+ }
+}
diff --git a/src/spells6.hpp b/src/spells6.hpp
new file mode 100644
index 00000000..bbd32d9b
--- /dev/null
+++ b/src/spells6.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "school_type_fwd.hpp"
+
+void schools_init();
+school_type *school_at(int index);
+void mana_school_calc_mana(int *msp);
diff --git a/src/squelch/CMakeLists.txt b/src/squelch/CMakeLists.txt
new file mode 100644
index 00000000..7b1495ba
--- /dev/null
+++ b/src/squelch/CMakeLists.txt
@@ -0,0 +1,9 @@
+ADD_LIBRARY(squelch
+ automatizer.cc
+ condition.cc
+ condition_metadata.cc
+ cursor.cc
+ object_status.cc
+ rule.cc
+ tree_printer.cc
+)
diff --git a/src/squelch/automatizer.cc b/src/squelch/automatizer.cc
new file mode 100644
index 00000000..c3c52b1b
--- /dev/null
+++ b/src/squelch/automatizer.cc
@@ -0,0 +1,278 @@
+#include "tome/squelch/automatizer_fwd.hpp"
+#include "tome/squelch/automatizer.hpp"
+
+#include "tome/squelch/rule.hpp"
+#include "tome/squelch/cursor.hpp"
+#include "tome/squelch/tree_printer.hpp"
+#include "util.hpp"
+#include "z-term.h"
+
+namespace squelch {
+
+/**
+ * Parse rules from JSON array
+ */
+static std::vector< std::shared_ptr < Rule > > parse_rules(json_t *rules_j)
+{
+ std::vector< std::shared_ptr < Rule > > rules;
+
+ if (!json_is_array(rules_j))
+ {
+ msg_format("Error 'rules' is not an array");
+ return rules;
+ }
+
+ for (size_t i = 0; i < json_array_size(rules_j); i++)
+ {
+ json_t *rule_j = json_array_get(rules_j, i);
+ auto rule = Rule::parse_rule(rule_j);
+ if (rule)
+ {
+ rules.push_back(rule);
+ }
+ }
+
+ return rules;
+}
+
+//----------------------------------------------------------
+// Automatizer
+//----------------------------------------------------------
+
+int Automatizer::append_rule(std::shared_ptr< Rule > rule)
+{
+ m_rules.push_back(rule);
+ return m_rules.size() - 1;
+}
+
+void Automatizer::swap_rules(int i, int j)
+{
+ swap(m_rules.at(i), m_rules.at(j));
+}
+
+bool Automatizer::apply_rules(object_type *o_ptr, int item_idx) const
+{
+ for (auto rule : m_rules)
+ {
+ if (rule->apply_rule(o_ptr, item_idx))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::shared_ptr<json_t> Automatizer::to_json() const
+{
+ auto rules_json = std::shared_ptr<json_t>(json_array(), &json_decref);
+
+ for (auto rule : m_rules)
+ {
+ json_array_append_new(rules_json.get(), rule->to_json());
+ }
+
+ return rules_json;
+}
+
+void Automatizer::load_json(json_t *json)
+{
+ // Go through all the found rules
+ auto rules = parse_rules(json);
+
+ // Load rule
+ for (auto rule : rules)
+ {
+ append_rule(rule);
+ }
+}
+
+int Automatizer::remove_current_selection()
+{
+ assert(!m_rules.empty());
+
+ // Previously selected rule
+ int prev_selected_rule = m_selected_rule;
+ int new_selected_rule;
+
+ // If the cursor is at the top level then we want to delete
+ // the rule itself
+ if (m_cursor->size() < 1)
+ {
+ // Remove rule
+ m_rules.erase(m_rules.begin() + m_selected_rule);
+ // Select previous
+ new_selected_rule = prev_selected_rule - 1;
+ }
+ else
+ {
+ // Delete the currently selected condition in rule.
+ m_rules.at(m_selected_rule)->delete_selected_condition(m_cursor.get());
+ // Keep selection
+ new_selected_rule = m_selected_rule;
+ }
+
+ // Do we need to adjust to select a different rule?
+ if ((prev_selected_rule != new_selected_rule) && (new_selected_rule >= 0))
+ {
+ select_rule(new_selected_rule);
+ }
+
+ // Return the selected rule.
+ return m_selected_rule;
+}
+
+void Automatizer::reset_view()
+{
+ // Clear cursor
+ m_cursor->clear();
+
+ // Empty rules?
+ if (m_rules.empty())
+ {
+ return;
+ }
+
+ // Reset scroll position
+ m_tree_printer->reset_scroll();
+
+ // Put the top-level condition into cursor
+ auto condition = m_rules.at(m_selected_rule)->get_condition();
+ if (condition)
+ {
+ m_cursor->push(condition.get());
+ }
+}
+
+void Automatizer::show_current() const
+{
+ if (m_rules.empty())
+ {
+ return;
+ }
+
+ m_tree_printer->reset();
+ m_rules.at(m_selected_rule)->write_tree(
+ m_tree_printer.get(),
+ m_cursor.get());
+}
+
+void Automatizer::scroll_up()
+{
+ m_tree_printer->scroll_up();
+}
+
+void Automatizer::scroll_down()
+{
+ m_tree_printer->scroll_down();
+}
+
+void Automatizer::scroll_left()
+{
+ m_tree_printer->scroll_left();
+}
+
+void Automatizer::scroll_right()
+{
+ m_tree_printer->scroll_right();
+}
+
+void Automatizer::move_up()
+{
+ m_cursor->move_up();
+}
+
+void Automatizer::move_down()
+{
+ m_cursor->move_down();
+}
+
+void Automatizer::move_left()
+{
+ m_cursor->move_left();
+}
+
+void Automatizer::move_right()
+{
+ m_cursor->move_right();
+}
+
+void Automatizer::add_new_condition(std::function<std::shared_ptr<Condition> ()> factory)
+{
+ m_rules.at(m_selected_rule)->add_new_condition(
+ m_cursor.get(),
+ factory);
+}
+
+void Automatizer::get_rule_names(std::vector<const char *> *names) const
+{
+ names->resize(m_rules.size());
+ for (size_t i = 0; i < m_rules.size(); i++)
+ {
+ (*names)[i] = m_rules.at(i)->get_name();
+ }
+}
+
+int Automatizer::rules_count() const
+{
+ return m_rules.size();
+}
+
+int Automatizer::rules_begin() const
+{
+ return m_begin;
+}
+
+void Automatizer::select_rule(int selected_rule)
+{
+ m_selected_rule = selected_rule;
+
+ int wid, hgt;
+ Term_get_size(&wid, &hgt);
+
+ // Adjust selection to conform to bounds.
+ {
+ int rules_size = m_rules.size(); // Convert to int to avoid warnings
+
+ if (m_selected_rule < 0)
+ {
+ m_selected_rule = rules_size - 1;
+ m_begin = m_selected_rule - hgt + 3;
+ if (m_begin < 0)
+ {
+ m_begin = 0;
+ }
+ }
+
+ if (m_selected_rule < m_begin)
+ {
+ m_begin = m_selected_rule;
+ }
+
+ if (m_selected_rule >= rules_size)
+ {
+ m_selected_rule = 0;
+ m_begin = 0;
+ }
+
+ if (m_selected_rule >= m_begin + hgt - 2)
+ {
+ m_begin++;
+ }
+ }
+
+ // Adjust tree printer and cursor.
+ reset_view();
+}
+
+int Automatizer::selected_rule() const
+{
+ return m_selected_rule;
+}
+
+std::shared_ptr<Rule> Automatizer::current_rule()
+{
+ return m_rules.at(m_selected_rule);
+}
+
+} // namespace
diff --git a/src/squelch/condition.cc b/src/squelch/condition.cc
new file mode 100644
index 00000000..c3b8c3f5
--- /dev/null
+++ b/src/squelch/condition.cc
@@ -0,0 +1,1078 @@
+#include "tome/squelch/condition_fwd.hpp"
+#include "tome/squelch/condition.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+
+#include "tome/squelch/cursor.hpp"
+#include "tome/squelch/tree_printer.hpp"
+#include "../ability_type.hpp"
+#include "../object1.hpp"
+#include "../object2.hpp"
+#include "../object_kind.hpp"
+#include "../object_type.hpp"
+#include "../player_race.hpp"
+#include "../player_race_mod.hpp"
+#include "../player_spec.hpp"
+#include "../player_type.hpp"
+#include "../skills.hpp"
+#include "../skill_type.hpp"
+#include "../quark.hpp"
+#include "../util.hpp"
+#include "../variable.hpp"
+
+namespace squelch {
+
+EnumStringMap<match_type> &match_mapping()
+{
+ // TODO: This is quite ugly and leads to valgrind complaints
+ static auto m = new EnumStringMap<match_type> {
+ { match_type::AND, "and" },
+ { match_type::OR, "or" },
+ { match_type::NOT, "not" },
+ { match_type::NAME, "name" },
+ { match_type::CONTAIN, "contain" },
+ { match_type::INSCRIBED, "inscribed" },
+ { match_type::DISCOUNT, "discount" },
+ { match_type::SYMBOL, "symbol" },
+ { match_type::STATE, "state" },
+ { match_type::STATUS, "status" },
+ { match_type::TVAL, "tval" },
+ { match_type::SVAL, "sval" },
+ { match_type::RACE, "race" },
+ { match_type::SUBRACE, "subrace" },
+ { match_type::CLASS, "class" },
+ { match_type::LEVEL, "level" },
+ { match_type::SKILL, "skill" },
+ { match_type::ABILITY, "ability" },
+ { match_type::INVENTORY, "inventory" },
+ { match_type::EQUIPMENT, "equipment" } };
+ return *m;
+};
+
+EnumStringMap<identification_state> &identification_state_mapping()
+{
+ // TODO: This is quite ugly and leads to valgrind complaints
+ static auto m = new EnumStringMap<identification_state> {
+ { identification_state::IDENTIFIED, "identified" },
+ { identification_state::NOT_IDENTIFIED, "not identified" } };
+ return *m;
+}
+
+json_t *Condition::to_json() const
+{
+ json_t *j = json_object();
+ json_object_set_new(j, "type",
+ json_string(match_mapping().stringify(match)));
+ to_json(j);
+ return j;
+}
+
+void Condition::display(TreePrinter *tree_printer, Cursor *cursor) const
+{
+ assert(tree_printer);
+
+ // Use normal or "selected" colours?
+ uint8_t bcol = TERM_L_GREEN;
+ uint8_t ecol = TERM_GREEN;
+ if (cursor->is_selected(this))
+ {
+ bcol = TERM_VIOLET;
+ ecol = TERM_VIOLET;
+ }
+
+ // Indent a level and display tree.
+ tree_printer->indent();
+ write_tree(tree_printer, cursor, ecol, bcol);
+ tree_printer->dedent();
+}
+
+std::shared_ptr<Condition> Condition::parse_condition(json_t *condition_json)
+{
+ // Parsers for concrete types of conditions.
+ static std::map< match_type,
+ std::function< std::shared_ptr< Condition > ( json_t * ) > > parsers {
+ { match_type::AND, &AndCondition::from_json },
+ { match_type::OR, &OrCondition::from_json },
+ { match_type::NOT, &NotCondition::from_json },
+ { match_type::INVENTORY, &InventoryCondition::from_json },
+ { match_type::EQUIPMENT, &EquipmentCondition::from_json },
+ { match_type::NAME, &NameCondition::from_json },
+ { match_type::CONTAIN, &ContainCondition::from_json },
+ { match_type::SYMBOL, &SymbolCondition::from_json },
+ { match_type::INSCRIBED, &InscriptionCondition::from_json },
+ { match_type::DISCOUNT, &DiscountCondition::from_json },
+ { match_type::TVAL, &TvalCondition::from_json },
+ { match_type::SVAL, &SvalCondition::from_json },
+ { match_type::STATUS, &StatusCondition::from_json },
+ { match_type::STATE, &StateCondition::from_json },
+ { match_type::RACE, &RaceCondition::from_json },
+ { match_type::SUBRACE, &SubraceCondition::from_json },
+ { match_type::CLASS, &ClassCondition::from_json },
+ { match_type::LEVEL, &LevelCondition::from_json },
+ { match_type::SKILL, &SkillCondition::from_json },
+ { match_type::ABILITY, &AbilityCondition::from_json } };
+
+ if ((condition_json == nullptr) || json_is_null(condition_json))
+ {
+ return nullptr;
+ }
+
+ cptr type_s = nullptr;
+ if (json_unpack(condition_json,
+ "{s:s}",
+ "type", &type_s) < 0)
+ {
+ msg_print("Missing/invalid 'type' in condition");
+ return nullptr;
+ }
+
+ match_type match;
+ if (!match_mapping().parse(type_s, &match))
+ {
+ msg_format("Invalid 'type' in condition: %s", type_s);
+ return nullptr;
+ }
+
+ // Look up parser and... parse
+ auto parser_i = parsers.find(match);
+ if (parser_i != parsers.end())
+ {
+ return parser_i->second(condition_json);
+ }
+
+ assert(false && "Missing parser");
+ return nullptr;
+}
+
+json_t *Condition::optional_to_json(std::shared_ptr<Condition> condition)
+{
+ return condition
+ ? condition->to_json()
+ : json_null();
+}
+
+bool TvalCondition::is_match(object_type *o_ptr) const
+{
+ return (o_ptr->tval == m_tval);
+}
+
+std::shared_ptr<Condition> TvalCondition::from_json(json_t *j)
+{
+ int tval;
+
+ if (json_unpack(j, "{s:i}", "tval", &tval) < 0)
+ {
+ msg_print("Missing/invalid 'tval' property");
+ return nullptr;
+ }
+
+ return std::make_shared<TvalCondition>(tval);
+}
+
+void TvalCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "tval", json_integer(m_tval));
+}
+
+void TvalCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Its ");
+ p->write(bcol, "tval");
+ p->write(ecol, " is ");
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, format("%d", (int) m_tval));
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+bool NameCondition::is_match(object_type *o_ptr) const
+{
+ char buf1[128];
+ object_desc(buf1, o_ptr, -1, 0);
+
+ return boost::algorithm::iequals(m_name, buf1);
+}
+
+std::shared_ptr<Condition> NameCondition::from_json(json_t *j)
+{
+ cptr s = nullptr;
+ if (json_unpack(j, "{s:s}", "name", &s) < 0)
+ {
+ msg_print("Missing/invalid 'name' property");
+ return nullptr;
+ }
+ return std::make_shared<NameCondition>(s);
+}
+
+void NameCondition::write_tree(TreePrinter *p, Cursor *cursor, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Its ");
+ p->write(bcol, "name");
+ p->write(ecol, " is \"");
+ p->write(TERM_WHITE, m_name.c_str());
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+void NameCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "name", json_string(m_name.c_str()));
+}
+
+bool ContainCondition::is_match(object_type *o_ptr) const
+{
+ char buf1[128];
+ object_desc(buf1, o_ptr, -1, 0);
+ return boost::algorithm::icontains(buf1, m_contain);
+}
+
+std::shared_ptr<Condition> ContainCondition::from_json(json_t *j)
+{
+ cptr s = nullptr;
+ if (json_unpack(j, "{s:s}", "contain", &s) < 0)
+ {
+ msg_print("Missing/invalid 'contain' property");
+ return nullptr;
+ }
+ return std::make_shared<ContainCondition>(s);
+}
+
+void ContainCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Its ");
+ p->write(bcol, "name");
+ p->write(ecol, " contains \"");
+ p->write(TERM_WHITE, m_contain.c_str());
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+void ContainCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "contain", json_string(m_contain.c_str()));
+}
+
+bool SvalCondition::is_match(object_type *o_ptr) const
+{
+ return (object_aware_p(o_ptr) &&
+ (o_ptr->sval >= m_min) &&
+ (o_ptr->sval <= m_max));
+}
+
+std::shared_ptr<Condition> SvalCondition::from_json(json_t *j)
+{
+ int min, max;
+
+ if (json_unpack(j, "{s:i,s:i}",
+ "min", &min,
+ "max", &max) < 0)
+ {
+ msg_print("Missing/invalid 'min'/'max' properties");
+ return nullptr;
+ }
+
+ return std::make_shared<SvalCondition>(min, max);
+}
+
+void SvalCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Its ");
+ p->write(bcol, "sval");
+ p->write(ecol, " is from ");
+ p->write(TERM_WHITE, format("%d", m_min));
+ p->write(ecol, " to ");
+ p->write(TERM_WHITE, format("%d", m_max));
+ p->write(TERM_WHITE, "\n");
+}
+
+void SvalCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "min", json_integer(m_min));
+ json_object_set_new(j, "max", json_integer(m_max));
+}
+
+void GroupingCondition::add_child(ConditionFactory const &factory)
+{
+ auto c_ptr = factory();
+ if (c_ptr)
+ {
+ m_conditions.push_back(c_ptr);
+ }
+}
+
+void GroupingCondition::remove_child(Condition *condition)
+{
+ m_conditions.erase(
+ std::remove_if(
+ std::begin(m_conditions),
+ std::end(m_conditions),
+ [&] (std::shared_ptr<Condition> p) {
+ return p.get() == condition;
+ }),
+ std::end(m_conditions));
+}
+
+std::shared_ptr<Condition> GroupingCondition::first_child()
+{
+ if (!m_conditions.empty())
+ {
+ return m_conditions.front();
+ }
+ return nullptr;
+}
+
+std::shared_ptr<Condition> GroupingCondition::previous_child(Condition *current)
+{
+ std::shared_ptr<Condition> prev_condition;
+
+ for (auto condition_p : m_conditions)
+ {
+ if (condition_p.get() == current)
+ {
+ // Do we have a previous child?
+ if (prev_condition)
+ {
+ return prev_condition;
+ }
+ else
+ {
+ // No predecessor
+ return nullptr;
+ }
+ }
+ // Keep track of predecessor
+ prev_condition = condition_p;
+ }
+
+ return nullptr;
+}
+
+std::shared_ptr<Condition> GroupingCondition::next_child(Condition *current)
+{
+ for (auto it = m_conditions.begin();
+ it != m_conditions.end();
+ it++)
+ {
+ if (it->get() == current)
+ {
+ it++;
+ // Move to next child (if any)
+ if (it == m_conditions.end())
+ {
+ // No successor
+ return nullptr;
+ }
+
+ return *it;
+ }
+ }
+
+ return nullptr;
+}
+
+std::vector< std::shared_ptr<Condition> > GroupingCondition::parse_conditions(json_t *j)
+{
+ json_t *conditions_j = json_object_get(j, "conditions");
+
+ if ((conditions_j == nullptr) ||
+ (json_is_null(conditions_j)))
+ {
+ return std::vector< std::shared_ptr<Condition> >();
+ }
+ else if (!json_is_array(conditions_j))
+ {
+ msg_print("'conditions' property has invalid type");
+ return std::vector< std::shared_ptr<Condition> >();
+ }
+ else
+ {
+ std::vector< std::shared_ptr<Condition> > subconditions;
+ for (size_t i = 0; i < json_array_size(conditions_j); i++)
+ {
+ json_t *subcondition_j =
+ json_array_get(conditions_j, i);
+
+ std::shared_ptr<Condition> subcondition =
+ parse_condition(subcondition_j);
+
+ if (subcondition != nullptr)
+ {
+ subconditions.push_back(subcondition);
+ }
+ }
+ return subconditions;
+ }
+}
+
+void GroupingCondition::to_json(json_t *j) const
+{
+ json_t *ja = json_array();
+ for (auto condition_p : m_conditions)
+ {
+ json_array_append_new(ja, optional_to_json(condition_p));
+ }
+ json_object_set_new(j, "conditions", ja);
+}
+
+bool AndCondition::is_match(object_type *o_ptr) const
+{
+ for (auto condition_p : m_conditions)
+ {
+ if (!condition_p->is_match(o_ptr))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+std::shared_ptr<Condition> AndCondition::from_json(json_t *j)
+{
+ auto condition = std::make_shared<AndCondition>();
+ for (auto subcondition : parse_conditions(j))
+ {
+ condition->add_condition(subcondition);
+ }
+ return condition;
+}
+
+void AndCondition::write_tree(TreePrinter *p, Cursor *c, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "All of the following are true:");
+ p->write(TERM_WHITE, "\n");
+
+ for (auto condition_p : m_conditions)
+ {
+ condition_p->display(p, c);
+ }
+}
+
+bool OrCondition::is_match(object_type *o_ptr) const
+{
+ for (auto condition_p : m_conditions)
+ {
+ if (condition_p->is_match(o_ptr))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+std::shared_ptr<Condition> OrCondition::from_json(json_t *j)
+{
+ std::shared_ptr<OrCondition> condition =
+ std::make_shared<OrCondition>();
+
+ for (auto subcondition : parse_conditions(j))
+ {
+ condition->add_condition(subcondition);
+ }
+
+ return condition;
+}
+
+void OrCondition::write_tree(TreePrinter *p, Cursor *c, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "At least one of the following are true:");
+ p->write(TERM_WHITE, "\n");
+
+ for (auto condition_p : m_conditions)
+ {
+ condition_p->display(p, c);
+ }
+}
+
+bool StatusCondition::is_match(object_type *o_ptr) const
+{
+ return m_status == object_status(o_ptr);
+}
+
+std::shared_ptr<Condition> StatusCondition::from_json(json_t *j)
+{
+ cptr s;
+ if (json_unpack(j, "{s:s}", "status", &s) < 0)
+ {
+ msg_print("Missing/invalid 'status' property");
+ return nullptr;
+ }
+
+ status_type status;
+ if (!status_mapping().parse(s, &status))
+ {
+ msg_format("Invalid 'status' property: %s", s);
+ return nullptr;
+ }
+
+ return std::make_shared<StatusCondition>(status);
+}
+
+void StatusCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Its ");
+ p->write(bcol, "status");
+ p->write(ecol, " is ");
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, status_mapping().stringify(m_status));
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+void StatusCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "status", json_string(status_mapping().stringify(m_status)));
+}
+
+bool RaceCondition::is_match(object_type *o_ptr) const
+{
+ return boost::algorithm::iequals(m_race, rp_ptr->title);
+}
+
+std::shared_ptr<Condition> RaceCondition::from_json(json_t *j)
+{
+ cptr s;
+
+ if (json_unpack(j, "{s:s}", "race", &s) < 0)
+ {
+ msg_print("Missing/invalid 'race' property");
+ return nullptr;
+ }
+
+ return std::make_shared<RaceCondition>(s);
+}
+
+void RaceCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Player ");
+ p->write(bcol, "race");
+ p->write(ecol, " is ");
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, m_race.c_str());
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+void RaceCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "race", json_string(m_race.c_str()));
+}
+
+bool SubraceCondition::is_match(object_type *o_ptr) const
+{
+ return boost::algorithm::iequals(m_subrace, rmp_ptr->title);
+}
+
+std::shared_ptr<Condition> SubraceCondition::from_json(json_t *j)
+{
+ cptr s;
+
+ if (json_unpack(j, "{s:s}", "subrace", &s) < 0)
+ {
+ msg_print("Missing/invalid 'subrace' property");
+ return nullptr;
+ }
+
+ return std::make_shared<SubraceCondition>(s);
+}
+
+void SubraceCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Player ");
+ p->write(bcol, "subrace");
+ p->write(ecol, " is ");
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, m_subrace.c_str());
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+void SubraceCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "subrace", json_string(m_subrace.c_str()));
+}
+
+bool ClassCondition::is_match(object_type *o_ptr) const
+{
+ return boost::algorithm::iequals(m_class, spp_ptr->title);
+}
+
+std::shared_ptr<Condition> ClassCondition::from_json(json_t *j)
+{
+ cptr s;
+
+ if (json_unpack(j, "{s:s}", "class", &s) < 0)
+ {
+ msg_print("Missing/invalid 'class' property");
+ return nullptr;
+ }
+
+ return std::make_shared<ClassCondition>(s);
+}
+
+void ClassCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Player ");
+ p->write(bcol, "class");
+ p->write(ecol, " is ");
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, m_class.c_str());
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+void ClassCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "class", json_string(m_class.c_str()));
+}
+
+bool InscriptionCondition::is_match(object_type *o_ptr) const
+{
+ if (o_ptr->note == 0)
+ {
+ return false;
+ }
+ return boost::algorithm::icontains(
+ quark_str(o_ptr->note),
+ m_inscription);
+}
+
+std::shared_ptr<Condition> InscriptionCondition::from_json(json_t *j)
+{
+ cptr s = nullptr;
+ if (json_unpack(j, "{s:s}", "inscription", &s) < 0)
+ {
+ msg_print("Missing/invalid 'inscription' property");
+ return nullptr;
+ }
+ return std::make_shared<InscriptionCondition>(s);
+}
+
+void InscriptionCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "It is ");
+ p->write(bcol, "inscribed");
+ p->write(ecol, " with ");
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, m_inscription.c_str());
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+void InscriptionCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "inscription", json_string(m_inscription.c_str()));
+}
+
+bool DiscountCondition::is_match(object_type *o_ptr) const
+{
+ return (object_aware_p(o_ptr) &&
+ (o_ptr->discount >= m_min) &&
+ (o_ptr->discount <= m_max));
+}
+
+std::shared_ptr<Condition> DiscountCondition::from_json(json_t *j)
+{
+ int min, max;
+
+ if (json_unpack(j, "{s:i,s:i}",
+ "min", &min,
+ "max", &max) < 0)
+ {
+ msg_print("Missing/invalid 'min'/'max' properties");
+ return nullptr;
+ }
+
+ return std::make_shared<DiscountCondition>(min, max);
+}
+
+void DiscountCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Its ");
+ p->write(bcol, "discount");
+ p->write(ecol, " is from ");
+ p->write(TERM_WHITE, format("%d", m_min));
+ p->write(ecol, " to ");
+ p->write(TERM_WHITE, format("%d", m_max));
+ p->write(TERM_WHITE, "\n");
+}
+
+void DiscountCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "min", json_integer(m_min));
+ json_object_set_new(j, "max", json_integer(m_max));
+}
+
+bool LevelCondition::is_match(object_type *) const
+{
+ return ((p_ptr->lev >= m_min) &&
+ (p_ptr->lev <= m_max));
+}
+
+std::shared_ptr<Condition> LevelCondition::from_json(json_t *j)
+{
+ int min, max;
+ if (json_unpack(j, "{s:i,s:i}",
+ "min", &min,
+ "max", &max) < 0)
+ {
+ msg_print("Missing/invalid 'min'/'max' properties");
+ return nullptr;
+ }
+
+ return std::make_shared<LevelCondition>(min, max);
+}
+
+void LevelCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Your ");
+ p->write(bcol, "level");
+ p->write(ecol, " is from ");
+
+ p->write(TERM_WHITE, format("%d", m_min));
+ p->write(ecol, " to ");
+ p->write(TERM_WHITE, format("%d", m_max));
+ p->write(TERM_WHITE, "\n");
+}
+
+void LevelCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "min", json_integer(m_min));
+ json_object_set_new(j, "max", json_integer(m_max));
+}
+
+bool SkillCondition::is_match(object_type *) const
+{
+ uint16_t sk = get_skill(m_skill_idx);
+ return ((sk >= m_min) &&
+ (sk <= m_max));
+}
+
+std::shared_ptr<Condition> SkillCondition::from_json(json_t *j)
+{
+ cptr s;
+ int min, max;
+ if (json_unpack(j, "{s:i,s:i,s:s}",
+ "min", &min,
+ "max", &max,
+ "name", &s) < 0)
+ {
+ msg_print("Missing/invalid 'min'/'max'/'name' properties");
+ return nullptr;
+ }
+
+ auto si = find_skill_i(s);
+ if (si < 0)
+ {
+ msg_print("Invalid 'name' property");
+ return nullptr;
+ }
+
+ return std::make_shared<SkillCondition>(si, min, max);
+}
+
+void SkillCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Your skill in ");
+ p->write(bcol, s_info[m_skill_idx].name);
+ p->write(ecol, " is from ");
+ p->write(TERM_WHITE, format("%d", (int) m_min));
+ p->write(ecol, " to ");
+ p->write(TERM_WHITE, format("%d", (int) m_max));
+ p->write(TERM_WHITE, "\n");
+}
+
+void SkillCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "name",
+ json_string(s_info[m_skill_idx].name));
+ json_object_set_new(j, "min",
+ json_integer(m_min));
+ json_object_set_new(j, "max",
+ json_integer(m_max));
+}
+
+bool StateCondition::is_match(object_type *o_ptr) const
+{
+ switch (m_state)
+ {
+ case identification_state::IDENTIFIED:
+ return object_known_p(o_ptr);
+ case identification_state::NOT_IDENTIFIED:
+ return !object_known_p(o_ptr);
+ }
+
+ assert(false);
+ return false;
+}
+
+std::shared_ptr<Condition> StateCondition::from_json(json_t *j)
+{
+ cptr s;
+ if (json_unpack(j, "{s:s}", "state", &s) < 0)
+ {
+ msg_print("Missing/invalid 'state' property");
+ return nullptr;
+ }
+
+ identification_state state;
+ if (!identification_state_mapping().parse(s, &state))
+ {
+ msg_format("Invalid 'state' property: %s", s);
+ return nullptr;
+ }
+
+ return std::make_shared<StateCondition>(state);
+}
+
+void StateCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Its ");
+ p->write(bcol, "state");
+ p->write(ecol, " is ");
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, identification_state_mapping().stringify(m_state));
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+void StateCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "state",
+ json_string(identification_state_mapping().
+ stringify(m_state)));
+}
+
+bool SymbolCondition::is_match(object_type *o_ptr) const
+{
+ object_kind *k_ptr = &k_info[o_ptr->k_idx];
+ return k_ptr->d_char == m_symbol;
+}
+
+std::shared_ptr<Condition> SymbolCondition::from_json(json_t *j)
+{
+ cptr s_ = nullptr;
+ if (json_unpack(j, "{s:s}", "symbol", &s_) < 0)
+ {
+ msg_print("Missing/invalid 'symbol' property");
+ return nullptr;
+ }
+
+ std::string s(s_);
+ if (s.empty())
+ {
+ msg_print("Invalid 'symbol' property: Too short");
+ return nullptr;
+ }
+ if (s.size() > 1)
+ {
+ msg_print("Invalid 'symbol' property: Too long");
+ return nullptr;
+ }
+
+ return std::make_shared<SymbolCondition>(s[0]);
+}
+
+void SymbolCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ p->write(ecol, "Its ");
+ p->write(bcol, "symbol");
+ p->write(ecol, " is ");
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, format("%c", m_symbol));
+ p->write(ecol, "\"");
+ p->write(TERM_WHITE, "\n");
+}
+
+void SymbolCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "symbol",
+ json_string(format("%c", m_symbol)));
+}
+
+bool AbilityCondition::is_match(object_type *) const
+{
+ return has_ability(m_ability_idx);
+}
+
+std::shared_ptr<Condition> AbilityCondition::from_json(json_t *j)
+{
+ cptr a;
+ if (json_unpack(j, "{s:s}", "ability", &a) < 0)
+ {
+ msg_print("Missing/invalid 'ability' property");
+ return nullptr;
+ }
+
+ auto ai = find_ability(a);
+ if (ai < 0)
+ {
+ msg_print("Invalid 'ability' property");
+ return nullptr;
+ }
+
+ return std::make_shared<AbilityCondition>(ai);
+}
+
+void AbilityCondition::write_tree(TreePrinter *p, Cursor *, uint8_t ecol, uint8_t bcol) const
+{
+ cptr ability_s = ab_info[m_ability_idx].name;
+
+ p->write(ecol, "You have the ");
+ p->write(bcol, ability_s);
+ p->write(ecol, " ability");
+ p->write(TERM_WHITE, "\n");
+}
+
+void AbilityCondition::to_json(json_t *j) const
+{
+ cptr ability_s = ab_info[m_ability_idx].name;
+ json_object_set_new(j, "ability", json_string(ability_s));
+}
+
+void SingleSubconditionCondition::add_child(std::function< std::shared_ptr<Condition> () > const &factory)
+{
+ // If we already have a subcondition then we cannot
+ // add one.
+ if (!m_subcondition)
+ {
+ m_subcondition = factory();
+ }
+}
+
+void SingleSubconditionCondition::remove_child(Condition *c)
+{
+ if (m_subcondition.get() == c) {
+ m_subcondition.reset();
+ }
+}
+
+std::shared_ptr<Condition> SingleSubconditionCondition::first_child()
+{
+ return m_subcondition;
+}
+
+void SingleSubconditionCondition::to_json(json_t *j) const
+{
+ json_object_set_new(j, "condition",
+ optional_to_json(m_subcondition));
+}
+
+std::shared_ptr<Condition> SingleSubconditionCondition::parse_single_subcondition(json_t *in_json)
+{
+ json_t *condition_j =
+ json_object_get(in_json, "condition");
+
+ if ((condition_j == nullptr) ||
+ (json_is_null(condition_j)))
+ {
+ return nullptr;
+ }
+ else if (!json_is_object(condition_j))
+ {
+ msg_format("Invalid 'condition' property");
+ return nullptr;
+ }
+ else
+ {
+ return parse_condition(condition_j);
+ }
+}
+
+bool NotCondition::is_match(object_type *o_ptr) const
+{
+ if (!m_subcondition)
+ {
+ return true;
+ }
+
+ return !m_subcondition->is_match(o_ptr);
+}
+
+std::shared_ptr<Condition> NotCondition::from_json(json_t *j)
+{
+ return std::make_shared<NotCondition>(parse_single_subcondition(j));
+}
+
+void NotCondition::write_tree(TreePrinter *p, Cursor *c, byte ecol, byte bcol) const
+{
+ p->write(ecol, "Negate the following:");
+ p->write(TERM_WHITE, "\n");
+ if (m_subcondition)
+ {
+ m_subcondition->display(p, c);
+ }
+}
+
+bool InventoryCondition::is_match(object_type *) const
+{
+ if (!m_subcondition)
+ {
+ return false;
+ }
+
+ for (int i = 0; i < INVEN_WIELD; i++)
+ {
+ if (m_subcondition->is_match(&p_ptr->inventory[i]))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::shared_ptr<Condition> InventoryCondition::from_json(json_t *j)
+{
+ return std::make_shared<InventoryCondition>(
+ parse_single_subcondition(j));
+}
+
+void InventoryCondition::write_tree(TreePrinter *p, Cursor *c, byte ecol, byte bcol) const
+{
+ p->write(ecol, "Something in your ");
+ p->write(bcol, "inventory");
+ p->write(ecol, " matches the following:");
+ p->write(TERM_WHITE, "\n");
+ if (m_subcondition)
+ {
+ m_subcondition->display(p, c);
+ }
+}
+
+bool EquipmentCondition::is_match(object_type *) const
+{
+ if (!m_subcondition)
+ {
+ return false;
+ }
+
+ for (int i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (m_subcondition->is_match(&p_ptr->inventory[i]))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::shared_ptr<Condition> EquipmentCondition::from_json(json_t *j)
+{
+ return std::make_shared<EquipmentCondition>(
+ parse_single_subcondition(j));
+}
+
+void EquipmentCondition::write_tree(TreePrinter *p, Cursor *c, byte ecol, byte bcol) const
+{
+ p->write(ecol, "Something in your ");
+ p->write(bcol, "equipment");
+ p->write(ecol, " matches the following:");
+ p->write(TERM_WHITE, "\n");
+ if (m_subcondition)
+ {
+ m_subcondition->display(p, c);
+ }
+}
+
+} // namespace
diff --git a/src/squelch/condition_metadata.cc b/src/squelch/condition_metadata.cc
new file mode 100644
index 00000000..62a90e58
--- /dev/null
+++ b/src/squelch/condition_metadata.cc
@@ -0,0 +1,496 @@
+#include "tome/squelch/condition_metadata.hpp"
+#include "tome/squelch/condition.hpp"
+
+#include <vector>
+
+#include "tome/squelch/object_status.hpp"
+#include "lua_bind.hpp"
+#include "skills.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "z-term.h"
+
+namespace squelch {
+
+static std::shared_ptr<Condition> create_condition_name()
+{
+ cptr s = lua_input_box("Object name to match?", 79);
+ if (strlen(s) == 0)
+ {
+ return nullptr;
+ }
+
+ return std::make_shared<NameCondition>(s);
+}
+
+static std::shared_ptr<Condition> create_condition_contain()
+{
+ cptr s = lua_input_box("Word to find in object name?", 79);
+ if (strlen(s) == 0)
+ {
+ return nullptr;
+ }
+
+ return std::make_shared<ContainCondition>(s);
+}
+
+static std::shared_ptr<Condition> create_condition_inscribed()
+{
+ cptr s = lua_input_box("Word to find in object inscription?", 79);
+ if (strlen(s) == 0)
+ {
+ return nullptr;
+ }
+
+ return std::make_shared<InscriptionCondition>(s);
+}
+
+static std::shared_ptr<Condition> create_condition_discount()
+{
+ int min, max;
+
+ {
+ cptr s = lua_input_box("Min discount?", 79);
+ if (sscanf(s, "%d", &min) < 1)
+ {
+ return nullptr;
+ }
+ }
+
+ {
+ cptr s = lua_input_box("Max discount?", 79);
+ if (sscanf(s, "%d", &max) < 1)
+ {
+ return nullptr;
+ }
+ }
+
+ return std::make_shared<DiscountCondition>(min, max);
+}
+
+static std::shared_ptr<Condition> create_condition_symbol()
+{
+ char c;
+ cptr s = lua_input_box("Symbol to match?", 1);
+ if (sscanf(s, "%c", &c) < 1)
+ {
+ return nullptr;
+ }
+
+ return std::make_shared<SymbolCondition>(c);
+}
+
+static std::shared_ptr<Condition> create_condition_status()
+{
+ status_type status;
+ char c;
+
+ c = lua_msg_box("[t]errible, [v]ery bad, [b]ad, "
+ "[a]verage, [G]ood, [V]ery good, [S]pecial?");
+
+ switch (c)
+ {
+ case 't': status = status_type::TERRIBLE; break;
+ case 'v': status = status_type::VERY_BAD; break;
+ case 'b': status = status_type::BAD; break;
+ case 'a': status = status_type::AVERAGE; break;
+ case 'G': status = status_type::GOOD; break;
+ case 'V': status = status_type::VERY_GOOD; break;
+ case 'S': status = status_type::SPECIAL; break;
+ default: return nullptr;
+ }
+
+ return std::make_shared<StatusCondition>(status);
+}
+
+static std::shared_ptr<Condition> create_condition_state()
+{
+ char c = lua_msg_box("[i]dentified, [n]on identified?");
+
+ identification_state s;
+ switch (c)
+ {
+ case 'i': s = identification_state::IDENTIFIED; break;
+ case 'n': s = identification_state::NOT_IDENTIFIED; break;
+ default: return nullptr;
+ }
+
+ return std::make_shared<StateCondition>(s);
+}
+
+static bool in_byte_range(int x)
+{
+ return (x >= 0) && (x < 256);
+}
+
+static std::shared_ptr<Condition> create_condition_tval()
+{
+ cptr s = lua_input_box("Tval to match?", 79);
+ int tval;
+ if (sscanf(s, "%d", &tval) < 1)
+ {
+ return nullptr;
+ }
+
+ if (!in_byte_range(tval))
+ {
+ return nullptr;
+ }
+
+ return std::make_shared<TvalCondition>(tval);
+}
+
+static std::shared_ptr<Condition> create_condition_sval()
+{
+ int sval_min, sval_max;
+
+ {
+ cptr s = lua_input_box("Min sval?", 79);
+ if ((sscanf(s, "%d", &sval_min) < 1) ||
+ (!in_byte_range(sval_min)))
+ {
+ return nullptr;
+ }
+ }
+
+ {
+ cptr s = lua_input_box("Max sval?", 79);
+ if ((sscanf(s, "%d", &sval_max) < 1) ||
+ (!in_byte_range(sval_max)))
+ {
+ return nullptr;
+ }
+ }
+
+ return std::make_shared<SvalCondition>(sval_min, sval_max);
+}
+
+static std::shared_ptr<Condition> create_condition_race()
+{
+ cptr s = lua_input_box("Player race to match?", 79);
+ if (strlen(s) == 0)
+ {
+ return nullptr;
+ }
+
+ return std::make_shared<RaceCondition>(s);
+}
+
+static std::shared_ptr<Condition> create_condition_subrace()
+{
+ cptr s = lua_input_box("Player subrace to match?", 79);
+ if (strlen(s) == 0)
+ {
+ return nullptr;
+ }
+
+ return std::make_shared<SubraceCondition>(s);
+}
+
+static std::shared_ptr<Condition> create_condition_class()
+{
+ cptr s = lua_input_box("Player class to match?", 79);
+ if (strlen(s) == 0)
+ {
+ return nullptr;
+ }
+
+ return std::make_shared<ClassCondition>(s);
+}
+
+static std::shared_ptr<Condition> create_condition_level()
+{
+ int min, max;
+
+ {
+ cptr s = lua_input_box("Min player level?", 79);
+ if (sscanf(s, "%d", &min) < 1)
+ {
+ return nullptr;
+ }
+ }
+
+ {
+ cptr s = lua_input_box("Max player level?", 79);
+ if (sscanf(s, "%d", &max) < 1)
+ {
+ return nullptr;
+ }
+ }
+
+ return std::make_shared<LevelCondition>(min, max);
+}
+
+static std::shared_ptr<Condition> create_condition_skill()
+{
+ int min, max;
+
+ {
+ cptr s = lua_input_box("Min skill level?", 79);
+ if (sscanf(s, "%d", &min) < 1)
+ {
+ return nullptr;
+ }
+ }
+
+ {
+ cptr s = lua_input_box("Max skill level?", 79);
+ if (sscanf(s, "%d", &max) < 1)
+ {
+ return nullptr;
+ }
+ }
+
+ s16b skill_idx;
+ {
+ cptr s = lua_input_box("Skill name?", 79);
+ if (strlen(s) == 0)
+ {
+ return nullptr;
+ }
+
+ skill_idx = find_skill_i(s);
+ if (skill_idx < 0)
+ {
+ return nullptr;
+ }
+ }
+
+ return std::make_shared<SkillCondition>(skill_idx, min, max);
+}
+
+static std::shared_ptr<Condition> create_condition_ability()
+{
+ cptr s = lua_input_box("Ability name?", 79);
+ if (strlen(s) == 0)
+ {
+ return nullptr;
+ }
+
+ s16b ai = find_ability(s);
+ if (ai < 0)
+ {
+ return nullptr;
+ }
+
+ return std::make_shared<AbilityCondition>(ai);
+}
+
+static void display_desc(match_type match_type_)
+{
+ int i = 0;
+ auto line = [&i] (const char *s) {
+ c_prt(TERM_WHITE, s, i + 1, 17);
+ i++;
+ };
+
+ switch (match_type_)
+ {
+ case match_type::AND:
+ line("Check is true if all rules within it are true");
+ break;
+
+ case match_type::OR:
+ line("Check is true if at least one rule within it is true");
+ break;
+
+ case match_type::NOT:
+ line("Invert the result of its child rule");
+ break;
+
+ case match_type::NAME:
+ line("Check is true if object name matches name");
+ break;
+
+ case match_type::CONTAIN:
+ line("Check is true if object name contains word");
+ break;
+
+ case match_type::INSCRIBED:
+ line("Check is true if object inscription contains word");
+ break;
+
+ case match_type::DISCOUNT:
+ line("Check is true if object discount is between two values");
+ break;
+
+ case match_type::SYMBOL:
+ line("Check is true if object symbol is ok");
+ break;
+
+ case match_type::STATE:
+ line("Check is true if object is identified/unidentified");
+ break;
+
+ case match_type::STATUS:
+ line("Check is true if object status is ok");
+ break;
+
+ case match_type::TVAL:
+ line("Check is true if object tval(from k_info.txt) is ok");
+
+ case match_type::SVAL:
+ line("Check is true if object sval(from k_info.txt) is between");
+ line("two values");
+ break;
+
+ case match_type::RACE:
+ line("Check is true if player race is ok");
+ break;
+
+ case match_type::SUBRACE:
+ line("Check is true if player subrace is ok");
+ break;
+
+ case match_type::CLASS:
+ line("Check is true if player class is ok");
+ break;
+
+ case match_type::LEVEL:
+ line("Check is true if player level is between 2 values");
+ break;
+
+ case match_type::SKILL:
+ line("Check is true if player skill level is between 2 values");
+ break;
+
+ case match_type::ABILITY:
+ line("Check is true if player has the ability");
+ break;
+
+ case match_type::INVENTORY:
+ line("Check is true if something in player's inventory matches");
+ line("the contained rule");
+ break;
+
+ case match_type::EQUIPMENT:
+ line("Check is true if something in player's equipment matches");
+ line("the contained rule");
+ break;
+ }
+}
+
+std::shared_ptr<Condition> new_condition_interactive()
+{
+ static std::vector<match_type> condition_types = {
+ match_type::AND,
+ match_type::OR,
+ match_type::NOT,
+ match_type::NAME,
+ match_type::CONTAIN,
+ match_type::INSCRIBED,
+ match_type::DISCOUNT,
+ match_type::SYMBOL,
+ match_type::STATE,
+ match_type::STATUS,
+ match_type::TVAL,
+ match_type::SVAL,
+ match_type::RACE,
+ match_type::SUBRACE,
+ match_type::CLASS,
+ match_type::LEVEL,
+ match_type::SKILL,
+ match_type::ABILITY,
+ match_type::INVENTORY,
+ match_type::EQUIPMENT
+ };
+ static std::vector<const char *> condition_type_names;
+
+ // Fill in types names?
+ if (condition_type_names.empty())
+ {
+ for (auto condition_type : condition_types)
+ {
+ condition_type_names.push_back(
+ match_mapping().stringify(condition_type));
+ }
+ }
+
+ // Choose
+ int begin = 0, sel = 0;
+ while (1)
+ {
+ int wid, hgt;
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ display_list(0, 0, hgt - 1, 15, "Rule types", condition_type_names.data(), condition_types.size(), begin, sel, TERM_L_GREEN);
+
+ display_desc(condition_types[sel]);
+
+ char c = inkey();
+
+ if (c == ESCAPE) break;
+ else if (c == '8')
+ {
+ sel--;
+ if (sel < 0)
+ {
+ sel = condition_types.size() - 1;
+ begin = condition_types.size() - hgt;
+ if (begin < 0) begin = 0;
+ }
+ if (sel < begin) begin = sel;
+ }
+ else if (c == '2')
+ {
+ sel++;
+ if (sel >= static_cast<int>(condition_types.size()))
+ {
+ sel = 0;
+ begin = 0;
+ }
+ if (sel >= begin + hgt - 1) begin++;
+ }
+ else if (c == '\r')
+ {
+ switch (condition_types[sel])
+ {
+ case match_type::AND:
+ return std::make_shared<AndCondition>();
+ case match_type::OR:
+ return std::make_shared<OrCondition>();
+ case match_type::NOT:
+ return std::make_shared<NotCondition>();
+ case match_type::NAME:
+ return create_condition_name();
+ case match_type::CONTAIN:
+ return create_condition_contain();
+ case match_type::INSCRIBED:
+ return create_condition_inscribed();
+ case match_type::DISCOUNT:
+ return create_condition_discount();
+ case match_type::SYMBOL:
+ return create_condition_symbol();
+ case match_type::STATE:
+ return create_condition_state();
+ case match_type::STATUS:
+ return create_condition_status();
+ case match_type::TVAL:
+ return create_condition_tval();
+ case match_type::SVAL:
+ return create_condition_sval();
+ case match_type::RACE:
+ return create_condition_race();
+ case match_type::SUBRACE:
+ return create_condition_subrace();
+ case match_type::CLASS:
+ return create_condition_class();
+ case match_type::LEVEL:
+ return create_condition_level();
+ case match_type::SKILL:
+ return create_condition_skill();
+ case match_type::ABILITY:
+ return create_condition_ability();
+ case match_type::INVENTORY:
+ return std::make_shared<InventoryCondition>();
+ case match_type::EQUIPMENT:
+ return std::make_shared<EquipmentCondition>();
+
+ }
+ }
+ }
+ return nullptr;
+}
+
+} // namespace
diff --git a/src/squelch/cursor.cc b/src/squelch/cursor.cc
new file mode 100644
index 00000000..3a3bec46
--- /dev/null
+++ b/src/squelch/cursor.cc
@@ -0,0 +1,96 @@
+#include "tome/squelch/cursor_fwd.hpp"
+#include "tome/squelch/cursor.hpp"
+
+#include <algorithm>
+#include <cassert>
+
+#include "tome/squelch/condition.hpp"
+
+namespace squelch {
+
+bool Cursor::is_selected(Condition const *condition) const
+{
+ return std::end(m_conditions) !=
+ std::find(std::begin(m_conditions),
+ std::end(m_conditions),
+ condition);
+}
+
+Condition *Cursor::pop()
+{
+ assert(!m_conditions.empty());
+ Condition *c = m_conditions.back();
+ m_conditions.pop_back();
+ return c;
+}
+
+Condition *Cursor::current()
+{
+ assert(!m_conditions.empty());
+ return m_conditions.back();
+}
+
+void Cursor::move_right()
+{
+ if (m_conditions.empty()) {
+ return;
+ }
+ // Go right if the currently selected condition has children.
+ std::shared_ptr<Condition> c = current()->first_child();
+ if (c)
+ {
+ push(c.get());
+ }
+}
+
+void Cursor::move_left()
+{
+ if (size() > 1)
+ {
+ pop();
+ }
+}
+
+void Cursor::move_up()
+{
+ if (size() > 1)
+ {
+ Condition *prev_top = pop();
+
+ // Find previous child
+ std::shared_ptr<Condition> prev_condition =
+ current()->previous_child(prev_top);
+
+ // Do we have a previous child?
+ if (prev_condition)
+ {
+ push(prev_condition.get());
+ }
+ else
+ {
+ push(prev_top);
+ }
+ }
+}
+
+void Cursor::move_down()
+{
+ if (size() > 1)
+ {
+ Condition *prev_top = pop();
+
+ std::shared_ptr<Condition> next_condition =
+ current()->next_child(prev_top);
+
+ if (next_condition)
+ {
+ push(next_condition.get());
+ }
+ else
+ {
+ push(prev_top);
+ }
+ }
+}
+
+} // namespace
diff --git a/src/squelch/object_status.cc b/src/squelch/object_status.cc
new file mode 100644
index 00000000..075c0529
--- /dev/null
+++ b/src/squelch/object_status.cc
@@ -0,0 +1,153 @@
+#include "tome/squelch/object_status_fwd.hpp"
+#include "tome/squelch/object_status.hpp"
+
+#include "../inventory.hpp"
+#include "../object1.hpp"
+#include "../object2.hpp"
+#include "../object_type.hpp"
+#include "../variable.hpp"
+
+namespace squelch {
+
+EnumStringMap<status_type> &status_mapping()
+{
+ // TODO: This is quite ugly and leads to valgrind complaints
+ static auto m = new EnumStringMap<status_type> {
+ { status_type::BAD, "bad" },
+ { status_type::VERY_BAD, "very bad" },
+ { status_type::AVERAGE, "average" },
+ { status_type::GOOD, "good" },
+ { status_type::VERY_GOOD, "very good" },
+ { status_type::SPECIAL, "special" },
+ { status_type::TERRIBLE, "terrible" },
+ { status_type::NONE, "none" },
+ { status_type::CHEST_EMPTY, "(empty chest)" },
+ { status_type::CHEST_DISARMED, "(disarmed chest)" } };
+ return *m;
+}
+
+status_type object_status(object_type *o_ptr)
+{
+ if (!object_known_p(o_ptr))
+ {
+ switch (o_ptr->sense)
+ {
+ case SENSE_CURSED: return status_type::BAD;
+ case SENSE_WORTHLESS: return status_type::VERY_BAD;
+ case SENSE_AVERAGE: return status_type::AVERAGE;
+ case SENSE_GOOD_LIGHT: return status_type::GOOD;
+ case SENSE_GOOD_HEAVY: return status_type::GOOD;
+ case SENSE_EXCELLENT: return status_type::VERY_GOOD;
+ case SENSE_SPECIAL: return status_type::SPECIAL;
+ case SENSE_TERRIBLE: return status_type::TERRIBLE;
+ default: return status_type::NONE;
+ }
+ }
+ else
+ {
+ s16b slot = wield_slot_ideal(o_ptr, TRUE);
+
+ if (artifact_p(o_ptr))
+ {
+ if (!(o_ptr->ident & IDENT_CURSED))
+ {
+ return status_type::SPECIAL;
+ }
+ else
+ {
+ return status_type::TERRIBLE;
+ }
+ }
+ else if ((o_ptr->name2 > 0) ||
+ (o_ptr->name2b > 0))
+ {
+ if (!(o_ptr->ident & IDENT_CURSED))
+ {
+ return status_type::VERY_GOOD;
+ }
+ else
+ {
+ return status_type::VERY_BAD;
+ }
+ }
+ else if ((slot == INVEN_WIELD) ||
+ (slot == INVEN_BOW) ||
+ (slot == INVEN_AMMO) ||
+ (slot == INVEN_TOOL))
+ {
+ if (o_ptr->to_h + o_ptr->to_d < 0)
+ {
+ return status_type::BAD;
+ }
+ else if (o_ptr->to_h + o_ptr->to_d > 0)
+ {
+ return status_type::GOOD;
+ }
+ else
+ {
+ return status_type::AVERAGE;
+ }
+ }
+ else if ((slot >= INVEN_BODY) &&
+ (slot <= INVEN_FEET))
+ {
+ if (o_ptr->to_a < 0)
+ {
+ return status_type::BAD;
+ }
+ else if (o_ptr->to_a > 0)
+ {
+ return status_type::GOOD;
+ }
+ else
+ {
+ return status_type::AVERAGE;
+ }
+ }
+ else if (slot == INVEN_RING)
+ {
+ if ((o_ptr->to_d + o_ptr->to_h < 0) ||
+ (o_ptr->to_a < 0) ||
+ (o_ptr->pval < 0))
+ {
+ return status_type::BAD;
+ }
+ else
+ {
+ return status_type::AVERAGE;
+ }
+ }
+ else if (slot == INVEN_NECK)
+ {
+ if (o_ptr->pval < 0)
+ {
+ return status_type::BAD;
+ }
+ else
+ {
+ return status_type::AVERAGE;
+ }
+ }
+ else if (o_ptr->tval == TV_CHEST)
+ {
+ if (o_ptr->pval == 0)
+ {
+ return status_type::CHEST_EMPTY;
+ }
+ else if (o_ptr->pval < 0)
+ {
+ return status_type::CHEST_DISARMED;
+ }
+ else
+ {
+ return status_type::AVERAGE;
+ }
+ }
+ else
+ {
+ return status_type::AVERAGE;
+ }
+ }
+}
+
+} // namespace
diff --git a/src/squelch/rule.cc b/src/squelch/rule.cc
new file mode 100644
index 00000000..1c17d2fd
--- /dev/null
+++ b/src/squelch/rule.cc
@@ -0,0 +1,332 @@
+#include "tome/squelch/rule_fwd.hpp"
+#include "tome/squelch/rule.hpp"
+
+#include "tome/squelch/cursor.hpp"
+#include "tome/squelch/condition.hpp"
+#include "tome/squelch/tree_printer.hpp"
+#include "../angband.h"
+#include "../modules.hpp"
+#include "../object1.hpp"
+#include "../object2.hpp"
+#include "../object_type.hpp"
+#include "../quark.hpp"
+#include "../tables.hpp"
+#include "../util.hpp"
+#include "../variable.hpp"
+
+namespace squelch {
+
+EnumStringMap<action_type> &action_mapping()
+{
+ static auto m = new EnumStringMap<action_type> {
+ { action_type::AUTO_DESTROY, "destroy" },
+ { action_type::AUTO_PICKUP, "pickup" },
+ { action_type::AUTO_INSCRIBE, "inscribe" } };
+ return *m;
+}
+
+void Rule::set_name(const char *new_name)
+{
+ assert(new_name != nullptr);
+ m_name = new_name;
+}
+
+const char *Rule::get_name() const
+{
+ return m_name.c_str();
+}
+
+std::shared_ptr<Condition> Rule::get_condition() const
+{
+ return m_condition;
+}
+
+json_t *Rule::to_json() const
+{
+ json_t *rule_json = json_object();
+ json_object_set_new(rule_json,
+ "name",
+ json_string(m_name.c_str()));
+ json_object_set_new(rule_json,
+ "action",
+ json_string(action_mapping().stringify(m_action)));
+ json_object_set_new(rule_json,
+ "module",
+ json_string(modules[m_module_idx].meta.name));
+ json_object_set_new(rule_json,
+ "condition",
+ Condition::optional_to_json(m_condition));
+ return rule_json;
+}
+
+void Rule::add_new_condition(Cursor *cursor,
+ ConditionFactory const &factory)
+{
+ // Top-level condition?
+ if (!m_condition)
+ {
+ // Sanity check for navigation stack
+ assert(cursor->empty());
+
+ // Create new top-level condition
+ m_condition = factory();
+
+ // Select the condition
+ if (m_condition)
+ {
+ cursor->push(m_condition.get());
+ }
+ }
+ else
+ {
+ cursor->current()->add_child(factory);
+ }
+}
+
+void Rule::delete_selected_condition(Cursor *cursor)
+{
+ assert(cursor->size() >= 1);
+
+ if (cursor->size() == 1)
+ {
+ cursor->pop();
+ m_condition.reset();
+ }
+ else
+ {
+ Condition *prev_top = cursor->pop();
+ Condition *top = cursor->current();
+
+ // Jump up a level; this is a simple way to ensure a
+ // valid cursor. We could be a little cleverer here by
+ // trying to move inside the current level, but it
+ // gets a little complicated.
+ cursor->move_left();
+
+ // Now we can remove the condition from its parent
+ top->remove_child(prev_top);
+ }
+}
+
+void Rule::write_tree(TreePrinter *tree_printer, Cursor *cursor) const
+{
+ // Write out the main rule
+ do_write_tree(tree_printer);
+
+ // Write out the condition
+ if (m_condition)
+ {
+ m_condition->display(tree_printer, cursor);
+ }
+}
+
+bool Rule::apply_rule(object_type *o_ptr, int item_idx) const
+{
+ // Check module
+ if (m_module_idx != game_module_idx)
+ {
+ return false;
+ }
+
+ // Check condition
+ if (m_condition && m_condition->is_match(o_ptr))
+ {
+ return do_apply_rule(o_ptr, item_idx);
+ }
+
+ // Doesn't apply
+ return false;
+}
+
+std::shared_ptr<Rule> Rule::parse_rule(json_t *rule_json)
+{
+ if (!json_is_object(rule_json))
+ {
+ msg_print("Rule is not an object");
+ return nullptr;
+ }
+
+ // Retrieve the attributes
+ char *rule_name_s = nullptr;
+ char *rule_action_s = nullptr;
+ char *rule_module_s = nullptr;
+ if (json_unpack(rule_json,
+ "{s:s,s:s,s:s}",
+ "name", &rule_name_s,
+ "action", &rule_action_s,
+ "module", &rule_module_s) < 0)
+ {
+ msg_print("Rule missing required field(s)");
+ return nullptr;
+ }
+
+ // Convert attributes
+ action_type action;
+ if (!action_mapping().parse((cptr) rule_action_s, &action))
+ {
+ msg_format("Invalid rule action '%s'", rule_action_s);
+ return nullptr;
+ }
+
+ int module_idx = find_module((cptr) rule_module_s);
+ if (module_idx < 0)
+ {
+ msg_format("Skipping rule for unrecognized module '%s'",
+ (cptr) rule_module_s);
+ return nullptr;
+ }
+
+ // Parse condition
+ std::shared_ptr<Condition> condition =
+ Condition::parse_condition(json_object_get(rule_json, "condition"));
+
+ // Parse rule
+ switch (action)
+ {
+ case action_type::AUTO_INSCRIBE:
+ {
+ json_t *rule_inscription_j = json_object_get(rule_json, "inscription");
+
+ if (rule_inscription_j == nullptr)
+ {
+ msg_print("Inscription rule missing 'inscription' attribute");
+ return nullptr;
+ }
+ if (!json_is_string(rule_inscription_j))
+ {
+ msg_print("Inscription rule 'inscription' attribute wrong type");
+ return nullptr;
+ }
+
+ std::string inscription =
+ json_string_value(rule_inscription_j);
+ return std::make_shared<InscribeRule>(
+ rule_name_s, module_idx, condition, inscription);
+ }
+
+ case action_type::AUTO_PICKUP:
+ return std::make_shared<PickUpRule>(
+ rule_name_s, module_idx, condition);
+
+ case action_type::AUTO_DESTROY:
+ return std::make_shared<DestroyRule>(
+ rule_name_s, module_idx, condition);
+ }
+
+ assert(false);
+ return nullptr;
+}
+
+
+void DestroyRule::do_write_tree(TreePrinter *p) const
+{
+ p->write(TERM_GREEN, "A rule named \"");
+ p->write(TERM_WHITE, m_name.c_str());
+ p->write(TERM_GREEN, "\" to ");
+ p->write(TERM_L_GREEN, "destroy");
+ p->write(TERM_GREEN, " when");
+ p->write(TERM_WHITE, "\n");
+}
+
+bool DestroyRule::do_apply_rule(object_type *o_ptr, int item_idx) const
+{
+ // Must be identified
+ if (object_aware_p(o_ptr) == FALSE)
+ {
+ return false;
+ }
+
+ // Never destroy inscribed items
+ if (o_ptr->note)
+ {
+ return false;
+ }
+
+ // Ignore artifacts; cannot be destroyed anyway
+ if (artifact_p(o_ptr))
+ {
+ return false;
+ }
+
+ // Cannot destroy CURSE_NO_DROP objects.
+ {
+ u32b f1, f2, f3, f4, f5, esp;
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f4 & TR4_CURSE_NO_DROP) != 0)
+ {
+ return false;
+ }
+ }
+
+ // Destroy
+ msg_print("<Auto-destroy>");
+ inc_stack_size(item_idx, -o_ptr->number);
+ return true;
+}
+
+void PickUpRule::do_write_tree(TreePrinter *p) const
+{
+ p->write(TERM_GREEN, "A rule named \"");
+ p->write(TERM_WHITE, m_name.c_str());
+ p->write(TERM_GREEN, "\" to ");
+ p->write(TERM_L_GREEN, "pick up");
+ p->write(TERM_GREEN, " when");
+ p->write(TERM_WHITE, "\n");
+}
+
+bool PickUpRule::do_apply_rule(object_type *o_ptr, int item_idx) const
+{
+ if (item_idx >= 0)
+ {
+ return false;
+ }
+
+ if (!inven_carry_okay(o_ptr))
+ {
+ return false;
+ }
+
+ msg_print("<Auto-pickup>");
+ object_pickup(-item_idx);
+ return true;
+}
+
+json_t *InscribeRule::to_json() const
+{
+ json_t *j = Rule::to_json();
+
+ json_object_set_new(j,
+ "inscription",
+ json_string(m_inscription.c_str()));
+
+ return j;
+}
+
+void InscribeRule::do_write_tree(TreePrinter *p) const
+{
+ p->write(TERM_GREEN, "A rule named \"");
+ p->write(TERM_WHITE, m_name.c_str());
+ p->write(TERM_GREEN, "\" to ");
+ p->write(TERM_L_GREEN, "inscribe");
+ p->write(TERM_GREEN, " an item with \"");
+ p->write(TERM_WHITE, m_inscription.c_str());
+ p->write(TERM_GREEN, "\" when");
+ p->write(TERM_WHITE, "\n");
+}
+
+bool InscribeRule::do_apply_rule(object_type *o_ptr, int) const
+{
+ // Already inscribed?
+ if (o_ptr->note != 0)
+ {
+ return false;
+ }
+
+ // Inscribe
+ msg_format("<Auto-Inscribe {%s}>", m_inscription.c_str());
+ o_ptr->note = quark_add(m_inscription.c_str());
+ return true;
+}
+
+} // namespace
diff --git a/src/squelch/tree_printer.cc b/src/squelch/tree_printer.cc
new file mode 100644
index 00000000..2be098dc
--- /dev/null
+++ b/src/squelch/tree_printer.cc
@@ -0,0 +1,89 @@
+#include "tome/squelch/tree_printer_fwd.hpp"
+#include "tome/squelch/tree_printer.hpp"
+
+#include "../z-term.h"
+
+namespace squelch {
+
+TreePrinter::TreePrinter() : m_indent(0)
+{
+ int wid, hgt;
+ // Output window
+ Term_get_size(&wid, &hgt);
+ m_write_out_y = 1;
+ m_write_out_x = 16;
+ m_write_out_h = hgt - 4 - 1;
+ m_write_out_w = wid - 1 - 15 - 1;
+ // Set position
+ reset();
+ reset_scroll();
+}
+
+void TreePrinter::indent() {
+ m_indent++;
+}
+
+void TreePrinter::dedent() {
+ m_indent--;
+}
+
+void TreePrinter::reset() {
+ m_write_x = 0;
+ m_write_y = 0;
+}
+
+void TreePrinter::reset_scroll() {
+ m_write_off_y = 0;
+ m_write_off_x = 0;
+}
+
+void TreePrinter::scroll_up() {
+ m_write_off_y--;
+}
+
+void TreePrinter::scroll_down() {
+ m_write_off_y++;
+}
+
+void TreePrinter::scroll_left() {
+ m_write_off_x++;
+}
+
+void TreePrinter::scroll_right() {
+ m_write_off_x--;
+}
+
+void TreePrinter::write(uint8_t color, cptr line)
+{
+ cptr p = line;
+
+ for (p = line; *p != '\0'; p++)
+ {
+ char c = *p;
+ int x = m_write_x - m_write_off_x + 3*m_indent;
+ int y = m_write_y - m_write_off_y;
+
+ if (c != '\n')
+ {
+ if ((y >= 0) &&
+ (y < m_write_out_h) &&
+ (x >= 0) &&
+ (x < m_write_out_w))
+ {
+ Term_putch(x + m_write_out_x,
+ y + m_write_out_y,
+ color,
+ c);
+ }
+
+ m_write_x += 1;
+ }
+ else
+ {
+ m_write_x = 0;
+ m_write_y += 1;
+ }
+ }
+}
+
+} // namespace
diff --git a/src/squeltch.cc b/src/squeltch.cc
new file mode 100644
index 00000000..bab1e5c0
--- /dev/null
+++ b/src/squeltch.cc
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 2002 DarkGod
+ * Copyright (c) 2012 Bardur Arantsson
+ *
+ * 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 "squeltch.hpp"
+
+#include "cave_type.hpp"
+#include "files.hpp"
+#include "loadsave.hpp"
+#include "lua_bind.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "player_type.hpp"
+#include "tome/squelch/tree_printer.hpp"
+#include "tome/squelch/condition.hpp"
+#include "tome/squelch/condition_metadata.hpp"
+#include "tome/squelch/rule.hpp"
+#include "tome/squelch/cursor.hpp"
+#include "tome/squelch/object_status.hpp"
+#include "tome/squelch/automatizer.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+
+#include <jansson.h>
+#include <algorithm>
+#include <memory>
+#include <deque>
+#include <list>
+#include <string>
+#include <vector>
+
+using squelch::action_type;
+using squelch::action_mapping;
+
+using squelch::status_type;
+
+using squelch::Rule;
+using squelch::DestroyRule;
+using squelch::PickUpRule;
+using squelch::InscribeRule;
+
+using squelch::Condition;
+using squelch::AndCondition;
+using squelch::TvalCondition;
+using squelch::SvalCondition;
+using squelch::NameCondition;
+using squelch::StatusCondition;
+
+static squelch::Automatizer *automatizer = nullptr;
+
+void squeltch_grid(void)
+{
+ if (!automatizer_enabled)
+ {
+ return;
+ }
+
+ // Copy list of objects since we may modify it
+ auto const object_idxs(cave[p_ptr->py][p_ptr->px].o_idxs);
+
+ // Scan the pile of objects
+ for (auto const this_o_idx: object_idxs)
+ {
+ // 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);
+ }
+
+ // Apply rules
+ automatizer->apply_rules(o_ptr, -this_o_idx);
+ }
+}
+
+void squeltch_inventory(void)
+{
+ if (!automatizer_enabled)
+ {
+ return;
+ }
+
+ bool changed = true;
+ for (int num_iter = 0; changed && (num_iter < 100); num_iter++)
+ {
+ // No changes on this iteration.
+ changed = false;
+ // Traverse inventory
+ for (int i = 0; i < INVEN_PACK; i++)
+ {
+ object_type *o_ptr = &p_ptr->inventory[i];
+ if ((o_ptr->k_idx > 0) && automatizer->apply_rules(o_ptr, i))
+ {
+ // We have changes
+ changed = true;
+ // Re-traverse inventory
+ break;
+ }
+ }
+ }
+ // If we reached the iteration limit, "changed" will be true
+ if (changed)
+ {
+ cmsg_format(TERM_VIOLET, "'apply_rules' ran too often.");
+ }
+}
+
+static int create_new_rule()
+{
+ char name[20] = { '\0' };
+ int wid = 0, hgt = 0;
+
+ Term_get_size(&wid, &hgt);
+
+ sprintf(name, "%s", "No name");
+ if (!input_box("Name?", hgt / 2, wid / 2, name, sizeof(name)))
+ {
+ return -1;
+ }
+
+ char typ = lua_msg_box("[D]estroy, [P]ickup, [I]nscribe?");
+
+ std::shared_ptr<Rule> rule;
+ switch (typ)
+ {
+ case 'd':
+ case 'D':
+ rule = std::make_shared<DestroyRule>(name, game_module_idx, nullptr);
+ break;
+
+ case 'p':
+ case 'P':
+ rule = std::make_shared<PickUpRule>(name, game_module_idx, nullptr);
+ break;
+
+ case 'i':
+ case 'I':
+ {
+ cptr i = lua_input_box("Inscription?", 79);
+ if ((i == nullptr) || (strlen(i) == 0))
+ {
+ return -1;
+ }
+
+ rule = std::make_shared<InscribeRule>(
+ name, game_module_idx, nullptr, std::string(i));
+
+ break;
+ }
+
+ default:
+ return -1;
+ }
+
+ return automatizer->append_rule(rule);
+}
+
+static void automatizer_save_rules()
+{
+ char name[30] = { '\0' };
+ char buf[1025];
+ char ch;
+ int hgt, wid;
+
+ Term_get_size(&wid, &hgt);
+
+ sprintf(name, "%s.atm", player_name);
+
+ if (!input_box("Save name?", hgt / 2, wid / 2, name, sizeof(name)))
+ {
+ return;
+ }
+
+ // Build the filename
+ path_build(buf, 1024, ANGBAND_DIR_USER, name);
+
+ 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'))
+ {
+ return;
+ }
+ }
+
+ // Write to file
+ {
+ auto rules_json = automatizer->to_json();
+
+ int status = json_dump_file(rules_json.get(), buf,
+ JSON_INDENT(2) |
+ JSON_SORT_KEYS);
+ if (status == 0)
+ {
+ c_put_str(TERM_WHITE, "Saved rules in file ",
+ hgt / 2,
+ wid / 2 - 14);
+ }
+ else
+ {
+ c_put_str(TERM_WHITE, "Saving rules failed! ",
+ hgt / 2,
+ wid / 2 - 14);
+ }
+
+ // Wait for keypress
+ inkey();
+ }
+}
+
+static void rename_rule(Rule *rule)
+{
+ char name[16];
+ int wid, hgt;
+
+ assert(rule != nullptr);
+
+ Term_get_size(&wid, &hgt);
+
+ sprintf(name, "%s", rule->get_name());
+ if (input_box("New name?", hgt / 2, wid / 2, name, sizeof(name)))
+ {
+ rule->set_name(name);
+ }
+}
+
+#define ACTIVE_LIST 0
+#define ACTIVE_RULE 1
+void do_cmd_automatizer()
+{
+ int wid = 0, hgt = 0;
+ int active = ACTIVE_LIST;
+ cptr keys;
+ cptr keys2;
+ cptr keys3;
+ std::vector<cptr> rule_names;
+
+ 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();
+
+ automatizer->reset_view();
+
+ while (1)
+ {
+ Term_clear();
+ Term_get_size(&wid, &hgt);
+
+ automatizer->get_rule_names(&rule_names);
+
+ display_list(0, 0, hgt - 1, 15, "Rules", rule_names.data(), automatizer->rules_count(), automatizer->rules_begin(), automatizer->selected_rule(), (active == ACTIVE_LIST) ? TERM_L_GREEN : TERM_GREEN);
+
+ 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 = "#G?#W for Automatizer help";
+ }
+ 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";
+ }
+ 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);
+
+ automatizer->show_current();
+
+ char 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 (!automatizer->rules_count()) continue;
+
+ automatizer->select_rule(
+ automatizer->selected_rule() - 1);
+ }
+ else if (c == '2')
+ {
+ if (!automatizer->rules_count()) continue;
+
+ automatizer->select_rule(
+ automatizer->selected_rule() + 1);
+ }
+ else if (c == 'u')
+ {
+ int sel = automatizer->selected_rule();
+ if (sel > 0)
+ {
+ automatizer->swap_rules(sel-1, sel);
+ automatizer->select_rule(sel-1);
+ }
+ }
+ else if (c == 'd')
+ {
+ if (!automatizer->rules_count()) continue;
+
+ int sel = automatizer->selected_rule();
+ if (sel < automatizer->rules_count() - 1)
+ {
+ automatizer->swap_rules(sel, sel+1);
+ automatizer->select_rule(sel+1);
+ }
+ }
+ else if (c == 'n')
+ {
+ int i = create_new_rule();
+ if (i >= 0)
+ {
+ automatizer->select_rule(i);
+ active = ACTIVE_RULE;
+ }
+ }
+ else if (c == 's')
+ {
+ automatizer_save_rules();
+ }
+ else if (c == 'r')
+ {
+ if (!automatizer->rules_count()) continue;
+
+ rename_rule(automatizer->current_rule().get());
+ continue;
+ }
+ else if (c == 'k')
+ {
+ automatizer_enabled = FALSE;
+ break;
+ }
+ else if (c == '\t')
+ {
+ if (!automatizer->rules_count()) continue;
+
+ active = ACTIVE_RULE;
+ }
+ }
+ else if (active == ACTIVE_RULE)
+ {
+ if (c == '?')
+ {
+ screen_save();
+ show_file("automat.txt", "Automatizer help", 0, 0);
+ screen_load();
+ }
+ else if (c == '8')
+ {
+ automatizer->move_up();
+ }
+ else if (c == '2')
+ {
+ automatizer->move_down();
+ }
+ else if (c == '6')
+ {
+ automatizer->move_right();
+ }
+ else if (c == '4')
+ {
+ automatizer->move_left();
+ }
+ else if (c == '9')
+ {
+ automatizer->scroll_up();
+ }
+ else if (c == '3')
+ {
+ automatizer->scroll_down();
+ }
+ else if (c == '7')
+ {
+ automatizer->scroll_left();
+ }
+ else if (c == '1')
+ {
+ automatizer->scroll_right();
+ }
+ else if (c == 'a')
+ {
+ automatizer->add_new_condition(
+ squelch::new_condition_interactive);
+ }
+ else if (c == 'd')
+ {
+ if (!automatizer->rules_count())
+ {
+ continue;
+ }
+
+ int new_sel =
+ automatizer->remove_current_selection();
+
+ if (new_sel == -1)
+ {
+ active = ACTIVE_LIST;
+ }
+ }
+ else if (c == '\t')
+ {
+ active = ACTIVE_LIST;
+ }
+ }
+ }
+
+ screen_load();
+}
+
+enum class add_rule_mode { TVAL, TSVAL, NAME };
+
+static void easy_add_rule(add_rule_mode mode, bool do_status, object_type *o_ptr)
+{
+ std::shared_ptr<Condition> condition;
+
+ switch (mode)
+ {
+
+ case add_rule_mode::TVAL:
+ condition = std::make_shared<TvalCondition>(o_ptr->tval);
+ break;
+
+ case add_rule_mode::TSVAL:
+ {
+ auto andCondition = std::make_shared<AndCondition>();
+
+ andCondition->add_condition(
+ std::make_shared<TvalCondition>(o_ptr->tval));
+ andCondition->add_condition(
+ std::make_shared<SvalCondition>(o_ptr->sval, o_ptr->sval));
+
+ condition = andCondition;
+ break;
+ }
+
+ case add_rule_mode::NAME:
+ {
+ char buf[128];
+ object_desc(buf, o_ptr, -1, 0);
+
+ condition = std::make_shared<NameCondition>(buf);
+ break;
+ }
+
+ }
+
+ // Use object status?
+ if (do_status)
+ {
+ status_type status = squelch::object_status(o_ptr);
+
+ auto andCondition = std::make_shared<AndCondition>();
+
+ andCondition->add_condition(
+ std::shared_ptr<Condition>(condition)); // cycle
+ andCondition->add_condition(
+ std::make_shared<StatusCondition>(status));
+
+ // Replace condition; breaks cycle
+ condition = andCondition;
+ }
+
+ // Rule name
+ auto rule_name = action_mapping().stringify(action_type::AUTO_DESTROY);
+
+ // Append to list of rules
+ automatizer->append_rule(
+ std::make_shared<DestroyRule>(
+ rule_name, game_module_idx, condition));
+
+ msg_print("Rule added. Please go to the Automatizer screen (press = then T)");
+ msg_print("to save the modified ruleset.");
+}
+
+void automatizer_add_rule(object_type *o_ptr)
+{
+ bool do_status = false;
+
+ while (true)
+ {
+ char ch;
+
+ if (!get_com(format("Destroy all of the same [T]ype, [F]amily or [N]ame, also use [S]tatus (%s)? ", (do_status) ? "Yes" : "No"), &ch))
+ {
+ break;
+ }
+
+ if (ch == 'S' || ch == 's')
+ {
+ do_status = !do_status;
+ continue;
+ }
+
+ if (ch == 'T' || ch == 't')
+ {
+ easy_add_rule(add_rule_mode::TSVAL, do_status, o_ptr);
+ break;
+ }
+
+ if (ch == 'F' || ch == 'f')
+ {
+ easy_add_rule(add_rule_mode::TVAL, do_status, o_ptr);
+ break;
+ }
+
+ if (ch == 'N' || ch == 'n')
+ {
+ easy_add_rule(add_rule_mode::NAME, do_status, o_ptr);
+ break;
+ }
+ }
+}
+
+/**
+ * Initialize the automatizer.
+ */
+void automatizer_init()
+{
+ // Only permit single initialization.
+ assert(automatizer == nullptr);
+
+ // Set up dependencies
+ auto tree_printer(std::make_shared<squelch::TreePrinter>());
+ auto cursor(std::make_shared<squelch::Cursor>());
+
+ // Initialize
+ automatizer = new squelch::Automatizer(tree_printer, cursor);
+}
+
+/**
+ * Load automatizer file. Returns true iff automatizer
+ * rules were loaded successfully.
+ */
+bool automatizer_load(boost::filesystem::path const &path)
+{
+ assert(automatizer != NULL);
+
+ // Does the path exist?
+ if (!boost::filesystem::exists(path))
+ {
+ return false; // Not fatal; just skip
+ }
+
+ // Parse file
+ json_error_t error;
+ std::shared_ptr<json_t> rules_json(
+ json_load_file(path.c_str(), 0, &error),
+ &json_decref);
+ if (rules_json == nullptr)
+ {
+ msg_format("Error parsing automatizer rules from '%s'.", path.c_str());
+ msg_format("Line %d, Column %d", error.line, error.column);
+ msg_print(nullptr);
+ return false;
+ }
+
+ // Load rules
+ automatizer->load_json(rules_json.get());
+ return true;
+}
diff --git a/src/squeltch.hpp b/src/squeltch.hpp
new file mode 100644
index 00000000..65ddfb51
--- /dev/null
+++ b/src/squeltch.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type_fwd.hpp"
+#include <boost/filesystem.hpp>
+
+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);
+extern bool_ automatizer_create;
+extern void automatizer_init();
+extern bool automatizer_load(boost::filesystem::path const &path);
diff --git a/src/stairs_direction.hpp b/src/stairs_direction.hpp
new file mode 100644
index 00000000..b0b5f25d
--- /dev/null
+++ b/src/stairs_direction.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+typedef enum { STAIRS_UP, STAIRS_DOWN } stairs_direction;
diff --git a/src/stats.hpp b/src/stats.hpp
new file mode 100644
index 00000000..e682446c
--- /dev/null
+++ b/src/stats.hpp
@@ -0,0 +1,11 @@
+#pragma once
+
+/*
+ * 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
diff --git a/src/status.cc b/src/status.cc
new file mode 100644
index 00000000..0a3977c7
--- /dev/null
+++ b/src/status.cc
@@ -0,0 +1,783 @@
+/* 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 "files.hpp"
+#include "monster2.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "player_type.hpp"
+#include "stats.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra1.hpp"
+
+static void row_trival(const char*, s16b, u32b, s16b, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_bival(const char*, s16b, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void row_npval(const char*, s16b, u32b, int, u32b[INVEN_TOTAL - INVEN_WIELD + 2][7]);
+static void statline(const 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(const 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);
+static void status_sight(void);
+static void status_attr(void);
+static void status_combat(void);
+static void status_move(void);
+static void status_item(void);
+static 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)
+
+static 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;
+ }
+ }
+}
+
+static 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;
+ }
+ }
+}
+
+static 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;
+ }
+ }
+}
+
+static 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;
+ }
+ }
+}
+
+static 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_bival("Antimagic", 4, TR4_ANTIMAGIC_50, 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;
+ }
+ }
+}
+
+static 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()
+{
+ 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_FRAME | PR_MAP);
+ handle_stuff();
+}
+
+static void az_line(int xo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+{
+ int index = xo; /* Leave room for description */
+ int i;
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ if (p_ptr->inventory[i].k_idx) /* Wearing anything here? */
+ {
+ char cstrng[2];
+ cstrng[0] = (i - INVEN_WIELD) + 'a'; /* Assumes ASCII */
+ cstrng[1] = '\0'; /* terminate it */
+ c_put_str(TERM_WHITE, cstrng, 2, index++); /* Assumes ASCII */
+
+ /* DGDGDGDG */
+ /* object_flags_known(&inventory[i],*/
+ object_flags(&p_ptr->inventory[i], /* Help me debug */
+ &flag_arr[i - INVEN_WIELD][1], /* f1 */
+ &flag_arr[i - INVEN_WIELD][2], /* f2 */
+ &flag_arr[i - INVEN_WIELD][3], /* f3 */
+ &flag_arr[i - INVEN_WIELD][4], /* f4 */
+ &flag_arr[i - INVEN_WIELD][5], /* f5 */
+ &flag_arr[i - INVEN_WIELD][0]); /* esp */
+ flag_arr[i - INVEN_WIELD][6] = 1; /* And mark it to display */
+ }
+ else flag_arr[i - INVEN_WIELD][6] = 0; /* Otherwise don't display it */
+ }
+ c_put_str(TERM_WHITE, "@", 2, index++);
+ player_flags(
+ &flag_arr[INVEN_PLAYER][1], /* f1 */
+ &flag_arr[INVEN_PLAYER][2], /* f2 */
+ &flag_arr[INVEN_PLAYER][3], /* f3 */
+ &flag_arr[INVEN_PLAYER][4], /* f4 */
+ &flag_arr[INVEN_PLAYER][5], /* f5 */
+ &flag_arr[INVEN_PLAYER][0] /* esp */
+ );
+ flag_arr[INVEN_PLAYER][6] = 1;
+}
+
+static void status_trival(s32b val1, s32b val2, byte ypos, byte xpos)
+{
+ if (val1 != 0)
+ c_put_str(TERM_L_BLUE, "*", ypos, xpos);
+ else if (val2 != 0)
+ c_put_str(TERM_L_BLUE, "+", ypos, xpos);
+ else
+ c_put_str(TERM_WHITE, ".", ypos, xpos);
+}
+
+static void status_bival(s32b val, byte ypos, byte xpos)
+{
+ if (val != 0)
+ c_put_str(TERM_L_BLUE, "+", ypos, xpos);
+ else
+ c_put_str(TERM_WHITE, ".", ypos, xpos);
+}
+
+static void status_numeric(s32b val, byte ypos, byte xpos)
+{
+ u32b magnitude = ABS(val);
+ int color = TERM_WHITE; /* default */
+ char strnum[2];
+
+ if (val<0) {
+ color = TERM_RED;
+ };
+ if (val>0) {
+ color = TERM_GREEN;
+ };
+
+ if (magnitude == 0) {
+ sprintf(strnum, ".");
+ } if (magnitude > 9) {
+ sprintf(strnum, "*");
+ } else {
+ sprintf(strnum, "%lu", (unsigned long int) magnitude);
+ }
+
+ c_put_str(color, strnum, ypos, xpos);
+}
+
+static void status_count(s32b val1, int v1, s32b val2, int v2, s32b val3, int v3, s32b val4, int v4, byte ypos, byte xpos)
+{
+ int v = 0;
+
+ if (val1 != 0) v += v1;
+ if (val2 != 0) v += v2;
+ if (val3 != 0) v += v3;
+ if (val4 != 0) v += v4;
+
+ status_numeric(v, ypos, xpos);
+}
+
+static void row_count(const 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(const 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(const 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(const 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(const char* statname, int statidx, u32b flag, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+/* Displays a status row for a primary stat */
+{
+ int i;
+ int x = row_x_start;
+ char statstr[8];
+ byte stat_color = TERM_L_RED;
+
+ cnv_stat(p_ptr->stat_use[statidx], statstr);
+
+ c_put_str(TERM_L_GREEN, statstr, yo, 4 + row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ byte color = TERM_L_RED;
+
+ if (flag_arr[i][6] == 1)
+ {
+ switch (statidx)
+ {
+ case A_STR:
+ if (flag_arr[i][2] & TR2_SUST_STR)
+ color = TERM_L_BLUE;
+ break;
+ case A_INT:
+ if (flag_arr[i][2] & TR2_SUST_INT)
+ color = TERM_L_BLUE;
+ break;
+ case A_WIS:
+ if (flag_arr[i][2] & TR2_SUST_WIS)
+ color = TERM_L_BLUE;
+ break;
+ case A_DEX:
+ if (flag_arr[i][2] & TR2_SUST_DEX)
+ color = TERM_L_BLUE;
+ break;
+ case A_CON:
+ if (flag_arr[i][2] & TR2_SUST_CON)
+ color = TERM_L_BLUE;
+ break;
+ case A_CHR:
+ if (flag_arr[i][2] & TR2_SUST_CHR)
+ color = TERM_L_BLUE;
+ break;
+ }
+
+ if (i == INVEN_PLAYER ) /* Player flags */
+ {
+ if (flag_arr[i][1] & flag)
+ c_put_str((color == TERM_L_RED) ? TERM_YELLOW : color, "*", yo, x + SL_LENGTH);
+ else c_put_str((color == TERM_L_RED) ? TERM_WHITE : color, ".", yo, x + SL_LENGTH);
+ x++;
+ continue;
+ }
+ if (flag_arr[i][1] & flag)
+ status_numeric(p_ptr->inventory[i + INVEN_WIELD].pval, yo, x + SL_LENGTH);
+ else
+ c_put_str((color == TERM_L_RED) ? TERM_WHITE : color, ".", yo, x + SL_LENGTH);
+
+ if (color != TERM_L_RED)
+ stat_color = color;
+
+ x++;
+ }
+ }
+
+ c_put_str(stat_color, statname, yo, row_x_start);
+}
+
+static void row_hd_bon(int which, int yo, u32b flag_arr[INVEN_TOTAL - INVEN_WIELD + 2][7])
+/* To-hit/dmg modifiers, selected by 1st argument */
+{
+ int i;
+ int x = row_x_start;
+ if ((which != 0) && (which != 1)) return;
+ c_put_str(TERM_L_GREEN, ((which == 0) ? "To-Hit" : "To-Dmg"), yo, row_x_start);
+ for (i = 0; i < (INVEN_TOTAL - INVEN_WIELD + 2); i++)
+ {
+ if (flag_arr[i][6] == 1)
+ {
+ if (i == INVEN_PLAYER) /* Player? */
+ {
+ c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ if ( (which == 0) && (p_ptr->inventory[INVEN_WIELD + i].to_h != 0))
+ {
+ status_numeric(p_ptr->inventory[INVEN_WIELD + i].to_h, yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ if ( (which == 1) && (p_ptr->inventory[INVEN_WIELD + i].to_d != 0))
+ {
+ status_numeric(p_ptr->inventory[INVEN_WIELD + i].to_d, yo, x + STATNM_LENGTH);
+ x++;
+ continue;
+ }
+ c_put_str(TERM_WHITE, ".", yo, x + STATNM_LENGTH);
+ x++;
+ }
+ }
+}
+
+static void status_companion(void)
+{
+ int i;
+
+ FILE *fff;
+
+ char file_name[1024];
+
+ Term_clear();
+
+ /* Temporary file */
+ if (path_temp(file_name, 1024)) return;
+
+ /* Open a new file */
+ fff = my_fopen(file_name, "w");
+
+ /* Calculate companions */
+ /* Process the monsters (backwards) */
+ for (i = m_max - 1; i >= 1; i--)
+ {
+ /* Access the monster */
+ monster_type *m_ptr = &m_list[i];
+
+ if (m_ptr->status == MSTATUS_COMPANION)
+ {
+ char m_name[80];
+ int b, y = 0;
+
+ /* Extract monster name */
+ monster_desc(m_name, m_ptr, 0x80);
+
+ fprintf(fff, "#####BCompanion: %s\n", m_name);
+
+ fprintf(fff, " Lev/Exp : [[[[[G%d / %ld]\n", m_ptr->level, (long int) m_ptr->exp);
+ if (m_ptr->level < MONSTER_LEVEL_MAX) fprintf(fff, " Next lvl: [[[[[G%ld]\n", (long int) monster_exp(m_ptr->level + 1));
+ else fprintf(fff, " Next lvl: [[[[[G****]\n");
+
+ fprintf(fff, " HP : [[[[[G%ld / %ld]\n", (long int) m_ptr->hp, (long int) m_ptr->maxhp);
+ fprintf(fff, " AC : [[[[[G%d]\n", m_ptr->ac);
+ fprintf(fff, " Speed : [[[[[G%d]\n", m_ptr->mspeed - 110);
+
+ for (b = 0; b < 4; b++)
+ {
+ if (!m_ptr->blow[b].d_dice) continue;
+ if (!m_ptr->blow[b].d_side) continue;
+
+ fprintf(fff, " Blow %1d : [[[[[G%dd%d]\n", y + 1, m_ptr->blow[b].d_dice, m_ptr->blow[b].d_side);
+ y++;
+ }
+
+ fprintf(fff, "\n");
+ }
+ }
+
+ /* Close the file */
+ my_fclose(fff);
+
+ /* Display the file contents */
+ show_file(file_name, "Companion List", 0, 0);
+
+ /* Remove the file */
+ fd_kill(file_name);
+}
diff --git a/src/status.hpp b/src/status.hpp
new file mode 100644
index 00000000..74624446
--- /dev/null
+++ b/src/status.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+void status_main();
diff --git a/src/store.cc b/src/store.cc
new file mode 100644
index 00000000..0fbe2e9b
--- /dev/null
+++ b/src/store.cc
@@ -0,0 +1,3878 @@
+/*
+ * 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 "store.hpp"
+
+#include "bldg.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd3.hpp"
+#include "cmd4.hpp"
+#include "cmd5.hpp"
+#include "files.hpp"
+#include "hooks.hpp"
+#include "obj_theme.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "owner_type.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "spell_type.hpp"
+#include "skills.hpp"
+#include "spells5.hpp"
+#include "stats.hpp"
+#include "store_action_type.hpp"
+#include "store_type.hpp"
+#include "store_info_type.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+#define STORE_GENERAL_STORE "General Store"
+#define STORE_ARMOURY "Armoury"
+#define STORE_WEAPONSMITH "Weaponsmith"
+#define STORE_TEMPLE "Temple"
+#define STORE_ALCHEMY "Alchemy shop"
+#define STORE_MAGIC "Magic shop"
+#define STORE_BLACK_MARKET "Black Market"
+#define STORE_BOOKS "Book Store"
+#define STORE_PETS "Pet Shop"
+#define STORE_HUNTING_SUPPLIES "Hunting Supply Store"
+#define STORE_RUNIC_MAGIC "Runic Magic Shop"
+#define STORE_CONSTRUCTION_SUPPLIES "Construction Supply Store"
+#define STORE_MUSIC "Music Store"
+
+#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_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!"
+};
+
+
+/*
+ * 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)
+ {
+ msg_print("The shopkeeper whispers something into your ear:");
+ get_rnd_line("rumors.txt", rumour);
+ msg_print(rumour);
+ }
+}
+
+
+/*
+ * 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)]);
+}
+
+
+
+/*
+ * Messages for reacting to purchase prices.
+ */
+
+#define MAX_COMMENT_7A 4
+
+static cptr comment_7a[MAX_COMMENT_7A] =
+{
+ "Arrgghh!",
+ "You moron!",
+ "You hear someone sobbing...",
+ "The shopkeeper howls in agony!"
+};
+
+#define MAX_COMMENT_7B 4
+
+static cptr comment_7b[MAX_COMMENT_7B] =
+{
+ "Darn!",
+ "You fiend!",
+ "The shopkeeper yells at you.",
+ "The shopkeeper glares at you."
+};
+
+#define MAX_COMMENT_7C 4
+
+static cptr comment_7c[MAX_COMMENT_7C] =
+{
+ "Cool!",
+ "You've made my day!",
+ "The shopkeeper giggles.",
+ "The shopkeeper laughs loudly."
+};
+
+#define MAX_COMMENT_7D 4
+
+static cptr comment_7d[MAX_COMMENT_7D] =
+{
+ "Yippee!",
+ "I think I'll retire!",
+ "The shopkeeper jumps for joy.",
+ "The shopkeeper smiles gleefully."
+};
+
+/*
+ * Let a shop-keeper React to a purchase
+ *
+ * We paid "price", it was worth "value", and we thought it was worth "guess"
+ */
+static void purchase_analyze(s32b price, s32b value, s32b guess)
+{
+ /* Item was worthless, but we bought it */
+ if ((value <= 0) && (price > value))
+ {
+ /* Comment */
+ msg_print(comment_7a[rand_int(MAX_COMMENT_7A)]);
+
+ /* Sound */
+ sound(SOUND_STORE1);
+ }
+
+ /* Item was cheaper than we thought, and we paid more than necessary */
+ else if ((value < guess) && (price > value))
+ {
+ /* Comment */
+ msg_print(comment_7b[rand_int(MAX_COMMENT_7B)]);
+
+ /* Sound */
+ sound(SOUND_STORE2);
+ }
+
+ /* Item was a good bargain, and we got away with it */
+ else if ((value > guess) && (value < (4 * guess)) && (price < value))
+ {
+ /* Comment */
+ msg_print(comment_7c[rand_int(MAX_COMMENT_7C)]);
+
+ /* Sound */
+ sound(SOUND_STORE3);
+ }
+
+ /* Item was a great bargain, and we got away with it */
+ else if ((value > guess) && (price < value))
+ {
+ /* Comment */
+ msg_print(comment_7d[rand_int(MAX_COMMENT_7D)]);
+
+ /* Sound */
+ sound(SOUND_STORE4);
+ }
+}
+
+
+
+
+
+/*
+ * We store the current "store number" here so everyone can access it
+ */
+static int cur_store_num = 7;
+
+/*
+ * We store the current "store page" here so everyone can access it
+ */
+static int store_top = 0;
+
+/*
+ * We store the current "store pointer" here so everyone can access it
+ */
+static store_type *st_ptr = NULL;
+
+/*
+ * We store the current "owner type" here so everyone can access it
+ */
+static owner_type *ot_ptr = NULL;
+
+
+
+/*
+ * Determine the price of an item (qty one) in a store.
+ *
+ * This function takes into account the player's charisma, and the
+ * shop-keepers friendliness, and the shop-keeper's base greed, but
+ * never lets a shop-keeper lose money in a transaction.
+ *
+ * The "greed" value should exceed 100 when the player is "buying" the
+ * item, and should be less than 100 when the player is "selling" it.
+ *
+ * Hack -- the black market always charges twice as much as it should.
+ *
+ * Charisma adjustment runs from 80 to 130
+ * Racial adjustment runs from 95 to 130
+ *
+ * Since greed/charisma/racial adjustments are centered at 100, we need
+ * to adjust (by 200) to extract a usable multiplier. Note that the
+ * "greed" value is always something (?).
+ */
+static s32b price_item(object_type *o_ptr, int greed, bool_ flip)
+{
+ int factor;
+ int adjust;
+ s32b price;
+
+
+ /* Get the value of one of the items */
+ price = object_value(o_ptr);
+
+ /* Worthless items */
+ if (price <= 0) return (0L);
+
+ /* Compute the racial factor */
+ if (is_state(st_ptr, STORE_LIKED))
+ {
+ factor = ot_ptr->costs[STORE_LIKED];
+ }
+ else if (is_state(st_ptr, STORE_HATED))
+ {
+ factor = ot_ptr->costs[STORE_HATED];
+ }
+ else
+ {
+ factor = ot_ptr->costs[STORE_NORMAL];
+ }
+
+ /* Add in the charisma factor */
+ factor += adj_chr_gold[p_ptr->stat_ind[A_CHR]];
+
+ /* Shop is buying */
+ if (flip)
+ {
+ /* Mega Hack^3 */
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ price /= 5;
+ break;
+ }
+
+ /* Adjust for greed */
+ adjust = 100 + (300 - (greed + factor));
+
+ /* Never get "silly" */
+ if (adjust > 100) adjust = 100;
+
+ /* Mega-Hack -- Black market sucks */
+ if (st_info[st_ptr->st_idx].flags1 & SF1_ALL_ITEM) price = price / 2;
+
+ /* No selling means you get no money */
+ if (no_selling) price = 0;
+ }
+
+ /* 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;
+
+ /* Never give items away for free */
+ if (price <= 0L) price = 1L;
+ }
+
+ /* Compute the final price (with rounding) */
+ price = (price * adjust + 50L) / 100L;
+
+ /* 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);
+}
+
+
+static bool_ is_blessed(object_type const *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 const *o_ptr)
+{
+ cptr store_name = st_info[st_ptr->st_idx].name;
+
+ /* 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;
+ }
+
+ /* What do stores buy? */
+ if (streq(store_name, STORE_GENERAL_STORE))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_CORPSE:
+ case TV_FOOD:
+ case TV_LITE:
+ case TV_FLASK:
+ case TV_SPIKE:
+ case TV_SHOT:
+ case TV_ARROW:
+ case TV_BOLT:
+ case TV_DIGGING:
+ case TV_CLOAK:
+ case TV_BOTTLE:
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_ARMOURY))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_SHIELD:
+ case TV_CLOAK:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_WEAPONSMITH))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_SHOT:
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_BOOMERANG:
+ case TV_BOW:
+ case TV_DIGGING:
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_MSTAFF:
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_TEMPLE))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_DRUID_BOOK:
+ case TV_SCROLL:
+ case TV_POTION2:
+ case TV_POTION:
+ case TV_HAFTED:
+ return true;
+ }
+
+ if ((o_ptr->tval == TV_BOOK) &&
+ (o_ptr->sval == BOOK_RANDOM) &&
+ (spell_type_random_type(spell_at(o_ptr->pval)) == SKILL_SPIRITUALITY))
+ {
+ return true;
+ }
+ else if ((o_ptr->tval == TV_POLEARM) &&
+ is_blessed(o_ptr))
+ {
+ return true;
+ }
+ else if ((o_ptr->tval == TV_SWORD) &&
+ is_blessed(o_ptr))
+ {
+ return true;
+ }
+ else if ((o_ptr->tval == TV_AXE) &&
+ is_blessed(o_ptr))
+ {
+ return true;
+ }
+ else if ((o_ptr->tval == TV_BOOMERANG) &&
+ is_blessed(o_ptr))
+ {
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_ALCHEMY))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_SCROLL:
+ case TV_POTION2:
+ case TV_POTION:
+ case TV_BOTTLE:
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_MAGIC))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_SYMBIOTIC_BOOK:
+ case TV_AMULET:
+ case TV_RING:
+ case TV_STAFF:
+ case TV_WAND:
+ case TV_ROD:
+ case TV_ROD_MAIN:
+ case TV_SCROLL:
+ case TV_POTION2:
+ case TV_POTION:
+ case TV_MSTAFF:
+ case TV_RANDART:
+ return true;
+ }
+
+ if ((o_ptr->tval == TV_BOOK) &&
+ (o_ptr->sval == BOOK_RANDOM) &&
+ (spell_type_random_type(spell_at(o_ptr->pval)) == SKILL_MAGIC))
+ {
+ return true;
+ }
+ else if ((o_ptr->tval == TV_BOOK) &&
+ (o_ptr->sval != BOOK_RANDOM))
+ {
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_BLACK_MARKET))
+ {
+ return true;
+ }
+ else if (streq(store_name, STORE_BOOKS))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_BOOK:
+ case TV_SYMBIOTIC_BOOK:
+ case TV_MUSIC_BOOK:
+ case TV_DAEMON_BOOK:
+ case TV_DRUID_BOOK:
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_PETS))
+ {
+ return (o_ptr->tval == TV_EGG);
+ }
+ else if (streq(store_name, STORE_HUNTING_SUPPLIES))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_TRAPKIT:
+ case TV_BOOMERANG:
+ case TV_SHOT:
+ case TV_BOLT:
+ case TV_ARROW:
+ case TV_BOW:
+ case TV_POTION2:
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_RUNIC_MAGIC))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_RUNE1:
+ case TV_RUNE2:
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_CONSTRUCTION_SUPPLIES))
+ {
+ switch (o_ptr->tval)
+ {
+ case TV_LITE:
+ case TV_DIGGING:
+ return true;
+ }
+ }
+ else if (streq(store_name, STORE_MUSIC))
+ {
+ return (o_ptr->tval == TV_INSTRUMENT);
+ }
+
+ /* 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;
+
+ /* Magic Shop */
+ if (streq(st_info[st_ptr->st_idx].name, STORE_MAGIC) &&
+ magik(20))
+ {
+ s16b spell;
+
+ object_prep(&forge, lookup_kind(TV_BOOK, BOOK_RANDOM));
+ spell = get_random_spell(SKILL_MAGIC, 20);
+ assert (spell > -1);
+ forge.pval = spell;
+
+ /* Use the forged object */
+ q_ptr = &forge;
+ obj_all_done = TRUE;
+ }
+
+ /* Temple */
+ else if (streq(st_info[st_ptr->st_idx].name, STORE_TEMPLE) &&
+ magik(20))
+ {
+ s16b spell;
+
+ object_prep(&forge, lookup_kind(TV_BOOK, BOOK_RANDOM));
+ spell = get_random_spell(SKILL_SPIRITUALITY, 20);
+ assert(spell > -1);
+ forge.pval = spell;
+
+ /* Use the forged object */
+ q_ptr = &forge;
+ obj_all_done = TRUE;
+ }
+
+ /* 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;
+ }
+}
+
+
+
+/*
+ * 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;
+ {
+ byte a = object_attr(o_ptr);
+ char c = object_char(o_ptr);
+
+ if (!o_ptr->k_idx) c = ' ';
+
+ Term_draw(cur_col, i + 6, a, c);
+ 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 */
+ 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 */
+ {
+ /* 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 */
+ 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 */
+ {
+ /* 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);
+ }
+
+ /* Extract the "minimum" price */
+ x = price_item(o_ptr, ot_ptr->inflation, FALSE);
+
+ /* Can we buy one ? */
+ if (x > p_ptr->au) color = TERM_L_DARK;
+
+ /* Actually draw the price */
+ strnfmt(out_val, 160, "%9ld ", static_cast<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", static_cast<long>(p_ptr->au));
+ prt(out_val, 19, 68);
+}
+
+
+/*
+ * Displays store (after clearing screen) -RAK-
+ */
+void display_store(void)
+{
+ char buf[80];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* The "Home" is special */
+ if (cur_store_num == 7)
+ {
+ put_str("Your Home", 3, 30);
+
+ /* Label the item descriptions */
+ put_str("Item Description", 5, 3);
+
+ /* If showing weights, show label */
+ put_str("Weight", 5, 70);
+ }
+
+ else if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM)
+ {
+ /* Show the name of the store */
+ strnfmt(buf, 80, "%s", st_info[cur_store_num].name);
+ prt(buf, 3, 30);
+
+ /* Label the item descriptions */
+ put_str("Item Description", 5, 3);
+
+ /* If showing weights, show label */
+ put_str("Weight", 5, 70);
+ }
+
+ /* Normal stores */
+ else
+ {
+ /* Put the owner name and race */
+ strnfmt(buf, 80, "%s", ot_ptr->name);
+ put_str(buf, 3, 10);
+
+ /* Show the max price in the store (above prices) */
+ strnfmt(buf, 80, "%s (" FMTs16b ")",
+ st_info[cur_store_num].name,
+ ot_ptr->max_cost);
+ prt(buf, 3, 50);
+
+ /* Label the item descriptions */
+ put_str("Item Description", 5, 3);
+
+ /* If showing weights, show label */
+ put_str("Weight", 5, 60);
+
+ /* Label the asking price (in stores) */
+ put_str("Price", 5, 72);
+ }
+
+ /* Display the current gold */
+ store_prt_gold();
+
+ /* Draw in the inventory */
+ display_inventory();
+}
+
+
+
+/*
+ * Get the ID of a store item and return its value -RAK-
+ */
+static int get_stock(int *com_val, cptr pmt, int i, int j)
+{
+ char command;
+
+ char out_val[160];
+
+ /* Get the item index */
+ if (repeat_pull(com_val))
+ {
+
+ /* Verify the item */
+ if ((*com_val >= i) && (*com_val <= j))
+ {
+ /* Success */
+ return (TRUE);
+ }
+ }
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Assume failure */
+ *com_val = ( -1);
+
+ /* Build the prompt */
+ strnfmt(out_val, 160, "(Items %c-%c, ESC to exit) %s",
+ I2A(i), I2A(j), pmt);
+
+ /* Ask until done */
+ while (TRUE)
+ {
+ int k;
+
+ /* Escape */
+ if (!get_com(out_val, &command)) break;
+
+ /* Convert */
+ k = (islower(command) ? A2I(command) : -1);
+
+ /* Legal responses */
+ if ((k >= i) && (k <= j))
+ {
+ *com_val = k;
+ break;
+ }
+
+ /* Oops */
+ bell();
+ }
+
+ /* Clear the prompt */
+ prt("", 0, 0);
+
+ /* Cancel */
+ if (command == ESCAPE) return (FALSE);
+
+ repeat_push(*com_val);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/**
+ * Prompt for a yes/no during selling/buying
+ *
+ * @return TRUE if 'yes' was selected, otherwise returns FALSE.
+ */
+static bool_ prompt_yesno(cptr prompt)
+{
+ cptr allowed = "yn\r\n";
+ cptr yes = "y\r\n";
+ char buf[128];
+ bool_ ret;
+
+ /* Build prompt */
+ snprintf(buf, sizeof(buf), "%s [y/n/RET/ESC] ", prompt);
+
+ /* Prompt for it */
+ msg_print(NULL);
+ prt(buf, 0, 0);
+
+ /* Get answer */
+ while (TRUE)
+ {
+ int key = inkey();
+
+ /* ESC means no. */
+ if (key == ESCAPE) {
+ ret = FALSE;
+ break;
+ }
+
+ /* Any other key must be in the allowed set to break the loop. */
+ if ((strchr(allowed, key) != NULL) || quick_messages) {
+ /* Check for presence in the 'yes' set */
+ ret = (strchr(yes, key) != NULL);
+ break;
+ }
+
+ /* Retry */
+ bell();
+ }
+
+ /* Erase the prompt */
+ prt("", 0, 0);
+
+ /* Success */
+ return ret;
+}
+
+
+
+/*
+ * Haggling routine -RAK-
+ *
+ * Return TRUE if purchase is NOT successful
+ */
+static bool_ purchase_haggle(object_type *o_ptr, s32b *price)
+{
+ s32b cur_ask;
+ bool_ cancel = FALSE;
+ char out_val[160];
+ char prompt[128];
+ char o_name[80];
+
+
+ *price = 0;
+
+ /* Extract the price */
+ cur_ask = price_item(o_ptr, ot_ptr->inflation, FALSE);
+
+ /* Buy for the whole pile */
+ cur_ask *= o_ptr->number;
+
+ /* Describe the object (fully) */
+ object_desc_store(o_name, o_ptr, TRUE, 3);
+
+ /* Prompt */
+ strnfmt(out_val, sizeof(out_val), "%s: " FMTs32b, "Price", cur_ask);
+ put_str(out_val, 1, 0);
+ strnfmt(prompt, sizeof(prompt), "Buy %s?", o_name);
+ cancel = !prompt_yesno(prompt);
+
+ /* Handle result */
+ if (cancel)
+ {
+ /* Cancel */
+ return (TRUE);
+ }
+ else
+ {
+ *price = cur_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 cur_ask;
+ bool_ cancel = FALSE;
+ char out_val[160];
+ char prompt[128];
+ char o_name[80];
+
+
+ *price = 0;
+
+ /* Extract price */
+ cur_ask = price_item(o_ptr, ot_ptr->inflation, TRUE);
+
+ /* Limit to shopkeeper's purse */
+ if (cur_ask > ot_ptr->max_cost) {
+ cur_ask = ot_ptr->max_cost;
+ }
+
+ /* Sell the whole pile */
+ cur_ask *= o_ptr->number;
+
+ /* Describe the object */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Prompt */
+ strnfmt(out_val, sizeof(out_val), "%s: " FMTs32b, "Price", cur_ask);
+ put_str(out_val, 1, 0);
+ strnfmt(prompt, sizeof(prompt), "Sell %s?", o_name);
+ cancel = !prompt_yesno(prompt);
+
+ /* Handle result */
+ if (cancel)
+ {
+ /* Cancel */
+ return (TRUE);
+ }
+ else
+ {
+ *price = cur_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;
+
+ /* "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();
+
+ /* 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->inflation, FALSE);
+
+ /* Find out how many the player wants */
+ if (o_ptr->number > 1)
+ {
+ s32b q;
+
+
+ /* 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
+ {
+ 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)
+ {
+ /* 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)
+ {
+ /* Player can afford it */
+ if (p_ptr->au >= price)
+ {
+ /* Say "okay" */
+ say_comment_1();
+
+ /* Make a sound */
+ sound(SOUND_BUY);
+
+ /* 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;
+
+ /* Describe the transaction */
+ object_desc(o_name, j_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You bought %s for " FMTs32b " gold.", o_name, 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;
+
+ 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 prompt */
+ cptr q, s;
+ if (cur_store_num == STORE_HOME)
+ {
+ q = "Drop which item? ";
+ s = "You have nothing to drop.";
+ }
+ else if (museum)
+ {
+ q = "Donate which item?";
+ s = "You have nothing to donate.";
+ }
+ else
+ {
+ q = "Sell which item? ";
+ s = "You have nothing that I want.";
+ }
+
+ /* Get an item */
+ if (!get_item(&item, q, s, (USE_EQUIP | USE_INVEN), store_will_buy))
+ {
+ return;
+ }
+
+ /* Get the item */
+ o_ptr = get_object(item);
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Hack -- Cannot remove cursed items */
+ if (cursed_p(o_ptr))
+ {
+ if (item >= INVEN_WIELD)
+ {
+ /* Oops */
+ msg_print("Hmmm, it seems to be cursed.");
+
+ /* Nope */
+ return;
+ }
+ else
+ {
+ if (f4 & TR4_CURSE_NO_DROP)
+ {
+ /* Oops */
+ msg_print("Hmmm, you seem to be unable to drop it.");
+
+ /* Nope */
+ return;
+ }
+ }
+ }
+
+
+ /* Assume one item */
+ amt = 1;
+
+ /* Find out how many the player wants (letter means "all") */
+ if (o_ptr->number > 1)
+ {
+ /* Get a quantity */
+ amt = get_quantity(NULL, o_ptr->number);
+
+ /* Allow user abort */
+ if (amt <= 0) return;
+ }
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Get a copy of the object */
+ object_copy(q_ptr, o_ptr);
+
+ /* Modify quantity */
+ q_ptr->number = amt;
+
+ /* Hack -- If a rod or wand, allocate total maximum
+ * timeouts or charges to those being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+ }
+
+ /* Get a full description */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Remove any inscription for stores */
+ if ((cur_store_num != 7) && !museum)
+ {
+ q_ptr->note = 0;
+ }
+
+ /* Is there room in the store (or the home?) */
+ if (!store_check_num(q_ptr))
+ {
+ if (cur_store_num == 7) msg_print("Your home is full.");
+ else if (museum) msg_print("The museum is full.");
+ else msg_print("I have not the room in my store to keep it.");
+ return;
+ }
+
+
+ /* Real store */
+ if ((cur_store_num != 7) && !museum)
+ {
+ /* 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);
+
+ /* 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 " FMTs32b " gold.", o_name, price);
+
+ /* Analyze the prices (and comment verbally) */
+ purchase_analyze(price, value, dummy);
+
+ /*
+ * Hack -- Allocate charges between those wands or rods sold
+ * and retained, unless all are being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+
+ if (o_ptr->number > amt) o_ptr->pval -= q_ptr->pval;
+ }
+
+ /* Take the item from the player, describe the result */
+ inc_stack_size(item, -amt);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* The store gets that (known) item */
+ item_pos = store_carry(q_ptr);
+
+ /* Re-display if item is now in store */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+ }
+
+ /* Player is at museum */
+ else if (museum)
+ {
+ char o2_name[80];
+ object_desc(o2_name, q_ptr, TRUE, 0);
+
+ msg_print("Once you donate something, you cannot take it back.");
+ if (!get_check(format("Do you really want to donate %s?", o2_name))) return;
+
+ /* Identify it */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+ q_ptr->ident |= IDENT_MENTAL;
+
+ /*
+ * Hack -- Allocate charges between those wands or rods sold
+ * and retained, unless all are being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+
+ if (o_ptr->number > amt) o_ptr->pval -= q_ptr->pval;
+ }
+
+
+ /* Describe */
+ msg_format("You donate %s (%c).", o_name, index_to_label(item));
+
+ choice = 0;
+
+ /* Take it from the players inventory */
+ inc_stack_size(item, -amt);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Update store display */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+
+ /* Player is at home */
+ else
+ {
+ /* Describe */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /*
+ * Hack -- Allocate charges between those wands or rods sold
+ * and retained, unless all are being sold. -LM-
+ */
+ if (o_ptr->tval == TV_WAND)
+ {
+ q_ptr->pval = o_ptr->pval * amt / o_ptr->number;
+
+ if (o_ptr->number > amt) o_ptr->pval -= q_ptr->pval;
+ }
+
+ /* Take it from the players inventory */
+ inc_stack_size(item, -amt);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Update store display */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+}
+
+
+
+/*
+ * Examine an item in a store -JDL-
+ */
+void store_examine(void)
+{
+ int i;
+ int item;
+
+ object_type *o_ptr;
+
+ char o_name[80];
+
+ char out_val[160];
+
+
+ /* Empty? */
+ if (st_ptr->stock_num <= 0)
+ {
+ if (cur_store_num == 7) msg_print("Your home is empty.");
+ else if (st_info[st_ptr->st_idx].flags1 & SF1_MUSEUM) msg_print("The museum is empty.");
+ else msg_print("I am currently out of stock.");
+ return;
+ }
+
+
+ /* Find the number of objects on this and following pages */
+ i = (st_ptr->stock_num - store_top);
+
+ /* And then restrict it to the current page */
+ if (i > 12) i = 12;
+
+ /* Prompt */
+ strnfmt(out_val, 160, "Which item do you want to examine? ");
+
+ /* Get the item number to be examined */
+ if (!get_stock(&item, out_val, 0, i - 1)) return;
+
+ /* Get the actual index */
+ item = item + store_top;
+
+ /* Get the actual item */
+ o_ptr = &st_ptr->stock[item];
+
+ /* Debug hack */
+ if (wizard)
+ {
+ drop_near(o_ptr, -1, p_ptr->py, p_ptr->px);
+ }
+
+ /* Require full knowledge */
+ if (!(o_ptr->ident & (IDENT_MENTAL)))
+ {
+ /* This can only happen in the home */
+ msg_print("You have no special knowledge about that item.");
+ return;
+ }
+
+ /* Description */
+ object_desc(o_name, o_ptr, TRUE, 3);
+
+ /* Describe */
+ msg_format("Examining %s...", o_name);
+
+ /* Show the object's powers. */
+ if (!object_out_desc(o_ptr, NULL, FALSE, TRUE))
+ {
+ msg_print("You see nothing special.");
+ }
+
+ /* Show spell listing for instruments, daemonwear and spellbooks. */
+ if ((o_ptr->tval == TV_INSTRUMENT) || (o_ptr->tval == TV_DAEMON_BOOK)
+ || (o_ptr->tval == TV_BOOK))
+ {
+ do_cmd_browse_aux(o_ptr);
+ }
+
+ return;
+}
+
+
+
+
+
+/*
+ * Hack -- set this to leave the store
+ */
+static bool_ leave_store = FALSE;
+
+
+/*
+ * Process a command in a store
+ *
+ * Note that we must allow the use of a few "special" commands
+ * in the stores which are not allowed in the dungeon, and we
+ * must disable some commands which are allowed in the dungeon
+ * but not in the stores, to prevent chaos.
+ */
+static bool_ store_process_command(void)
+{
+ bool_ validcmd = FALSE;
+ int i;
+ store_action_type *ba_ptr;
+ bool_ recreate = FALSE;
+
+ /* Handle repeating the last command */
+ repeat_check();
+
+ for (i = 0; i < 6; i++)
+ {
+ ba_ptr = &ba_info[st_info[st_ptr->st_idx].actions[i]];
+
+ if (ba_ptr->letter)
+ {
+ if (ba_ptr->letter == command_cmd)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ if (ba_ptr->letter_aux)
+ {
+ if (ba_ptr->letter_aux == command_cmd)
+ {
+ validcmd = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (validcmd)
+ {
+ recreate = bldg_process_command(st_ptr, i);
+ }
+ else
+ {
+ /* Parse the command */
+ switch (command_cmd)
+ {
+ /* Leave */
+ case ESCAPE:
+ {
+ leave_store = TRUE;
+ break;
+ }
+
+ /* Browse */
+ case ' ':
+ {
+ if (st_ptr->stock_num <= 12)
+ {
+ msg_print("Entire inventory is shown.");
+ }
+ else
+ {
+ store_top += 12;
+ if (store_top >= st_ptr->stock_num) store_top = 0;
+ display_inventory();
+ }
+ break;
+ }
+
+ /* Browse backwards */
+ case '-':
+ {
+ if (st_ptr->stock_num <= 12)
+ {
+ msg_print("Entire inventory is shown.");
+ }
+ else
+ {
+ store_top -= 12;
+ if (store_top < 0)
+ {
+ store_top = ((st_ptr->stock_num - 1) / 12) * 12;
+ }
+ display_inventory();
+ }
+ }
+
+ /* Redraw */
+ case KTRL('R'):
+ {
+ do_cmd_redraw();
+ display_store();
+ break;
+ }
+
+ /* Ignore return */
+ case '\r':
+ {
+ break;
+ }
+
+
+
+ /*** Inventory Commands ***/
+
+ /* Wear/wield equipment */
+ case 'w':
+ {
+ do_cmd_wield();
+ break;
+ }
+
+ /* Take off equipment */
+ case 't':
+ {
+ do_cmd_takeoff();
+ break;
+ }
+
+ /* Destroy an item */
+ case 'k':
+ {
+ do_cmd_destroy();
+ break;
+ }
+
+ /* Equipment list */
+ case 'e':
+ {
+ do_cmd_equip();
+ break;
+ }
+
+ /* Inventory list */
+ case 'i':
+ {
+ do_cmd_inven();
+ break;
+ }
+
+
+ /*** Various commands ***/
+
+ /* Identify an object */
+ case 'I':
+ {
+ do_cmd_observe();
+ break;
+ }
+
+ /* Hack -- toggle windows */
+ case KTRL('I'):
+ {
+ toggle_inven_equip();
+ break;
+ }
+
+
+
+ /*** Use various objects ***/
+
+ /* Browse a book */
+ case 'b':
+ {
+ do_cmd_browse();
+ break;
+ }
+
+ /* Inscribe an object */
+ case '{':
+ {
+ do_cmd_inscribe();
+ break;
+ }
+
+ /* Uninscribe an object */
+ case '}':
+ {
+ do_cmd_uninscribe();
+ break;
+ }
+
+
+
+ /*** Help and Such ***/
+
+ /* Help */
+ case '?':
+ {
+ do_cmd_help();
+ break;
+ }
+
+ /* Identify symbol */
+ case '/':
+ {
+ do_cmd_query_symbol();
+ break;
+ }
+
+ /* Character description */
+ case 'C':
+ {
+ do_cmd_change_name();
+ display_store();
+ break;
+ }
+
+
+ /*** System Commands ***/
+
+ /* Single line from a pref file */
+ case '"':
+ {
+ do_cmd_pref();
+ break;
+ }
+
+ /* Interact with macros */
+ case '@':
+ {
+ do_cmd_macros();
+ break;
+ }
+
+ /* Interact with visuals */
+ case '%':
+ {
+ do_cmd_visuals();
+ break;
+ }
+
+ /* Interact with colors */
+ case '&':
+ {
+ do_cmd_colors();
+ break;
+ }
+
+ /* Interact with options */
+ case '=':
+ {
+ do_cmd_options();
+ break;
+ }
+
+
+ /*** Misc Commands ***/
+
+ /* Take notes */
+ case ':':
+ {
+ do_cmd_note();
+ break;
+ }
+
+ /* Version info */
+ case 'V':
+ {
+ do_cmd_version();
+ break;
+ }
+
+ /* Repeat level feeling */
+ case KTRL('F'):
+ {
+ do_cmd_feeling();
+ break;
+ }
+
+ /* Show previous message */
+ case KTRL('O'):
+ {
+ do_cmd_message_one();
+ break;
+ }
+
+ /* Show previous messages */
+ case KTRL('P'):
+ {
+ do_cmd_messages();
+ break;
+ }
+
+ /* Check artifacts, uniques etc. */
+ case '~':
+ case '|':
+ {
+ do_cmd_knowledge();
+ break;
+ }
+
+ /* Load "screen dump" */
+ case '(':
+ {
+ do_cmd_load_screen();
+ break;
+ }
+
+ /* Save "screen dump" */
+ case ')':
+ {
+ do_cmd_save_screen();
+ break;
+ }
+
+
+ /* Hack -- Unknown command */
+ default:
+ {
+ if (st_ptr->st_idx == STORE_HOME)
+ msg_print("That command does not work in this home.");
+ else
+ msg_print("That command does not work in this store.");
+ break;
+ }
+ }
+ }
+
+ return recreate;
+}
+
+
+/*
+ * Enter a store, and interact with it.
+ *
+ * Note that we use the standard "request_command()" function
+ * to get a command, allowing us to use "command_arg" and all
+ * command macros and other nifty stuff, but we use the special
+ * "shopping" argument, to force certain commands to be converted
+ * into other commands, normally, we convert "p" (pray) and "m"
+ * (cast magic) into "g" (get), and "s" (search) into "d" (drop).
+ */
+void do_cmd_store(void)
+{
+ int which;
+ int maintain_num;
+ int tmp_chr;
+ int i;
+ bool_ recreate = FALSE;
+
+ cave_type *c_ptr;
+
+
+ /* Access the player grid */
+ c_ptr = &cave[p_ptr->py][p_ptr->px];
+
+ /* Verify a store */
+ if (c_ptr->feat != FEAT_SHOP)
+ {
+ msg_print("You see no store here.");
+ return;
+ }
+
+ /* Extract the store code */
+ which = c_ptr->special;
+
+ /* Hack -- Check the "locked doors" */
+ if (town_info[p_ptr->town_num].store[which].store_open >= turn)
+ {
+ msg_print("The doors are locked.");
+ return;
+ }
+
+ /* Calculate the number of store maintainances since the last visit */
+ maintain_num = (turn - town_info[p_ptr->town_num].store[which].last_visit) / (10L * STORE_TURNS);
+
+ /* Maintain the store max. 10 times */
+ if (maintain_num > 10) maintain_num = 10;
+
+ if (maintain_num)
+ {
+ /* Maintain the store */
+ for (i = 0; i < maintain_num; i++)
+ store_maint(p_ptr->town_num, which);
+
+ /* Save the visit */
+ town_info[p_ptr->town_num].store[which].last_visit = turn;
+ }
+
+ /* Forget the lite */
+ /* forget_lite(); */
+
+ /* Forget the view */
+ forget_view();
+
+
+ /* Hack -- Character is in "icky" mode */
+ character_icky = TRUE;
+
+
+ /* No command argument */
+ command_arg = 0;
+
+ /* No repeated command */
+ command_rep = 0;
+
+ /* No automatic command */
+ command_new = 0;
+
+
+ /* Save the store number */
+ cur_store_num = which;
+
+ /* Save the store and owner pointers */
+ st_ptr = &town_info[p_ptr->town_num].store[cur_store_num];
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Start at the beginning */
+ store_top = 0;
+
+ /* Display the store */
+ display_store();
+
+ /* Mega-Hack -- Ignore keymaps on store action letters */
+ for (i = 0; i < 6; i++)
+ {
+ store_action_type *ba_ptr =
+ &ba_info[st_info[st_ptr->st_idx].actions[i]];
+ request_command_ignore_keymaps[2*i] = ba_ptr->letter;
+ request_command_ignore_keymaps[2*i+1] = ba_ptr->letter_aux;
+
+ }
+
+ /* Do not leave */
+ leave_store = FALSE;
+
+ /* Interact with player */
+ while (!leave_store)
+ {
+ /* Hack -- Clear line 1 */
+ prt("", 1, 0);
+
+ /* Hack -- Check the charisma */
+ tmp_chr = p_ptr->stat_use[A_CHR];
+
+ /* Clear */
+ clear_from(21);
+
+
+ /* Basic commands */
+ c_prt(TERM_YELLOW, " ESC.", 22, 0);
+ prt(") Exit.", 22, 4);
+
+ /* Browse if necessary */
+ if (st_ptr->stock_num > 12)
+ {
+ c_prt(TERM_YELLOW, " SPACE", 23, 0);
+ prt(") Next page", 23, 6);
+ }
+
+ /* Prompt */
+ prt("You may: ", 21, 0);
+
+ /* Show the commands */
+ show_building(st_ptr);
+
+ /* Get a command */
+ request_command(TRUE);
+
+ /* Process the command */
+ if (store_process_command()) recreate = TRUE;
+
+ /* Hack -- Character is still in "icky" mode */
+ character_icky = TRUE;
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* XXX XXX XXX Pack Overflow */
+ if (p_ptr->inventory[INVEN_PACK].k_idx)
+ {
+ int item = INVEN_PACK;
+
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Hack -- Flee from the store */
+ if (cur_store_num != 7)
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee the store...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Flee from the home */
+ else if (!store_check_num(o_ptr))
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee your home...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Drop items into the home */
+ else
+ {
+ int item_pos;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char o_name[80];
+
+
+ /* Give a message */
+ msg_print("Your pack overflows!");
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Grab a copy of the item */
+ object_copy(q_ptr, o_ptr);
+
+ /* Describe it */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Remove it from the players inventory */
+ inc_stack_size(item, -255);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Redraw the home */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+ }
+
+ /* Hack -- Redisplay store prices if charisma changes */
+ if (tmp_chr != p_ptr->stat_use[A_CHR]) display_inventory();
+
+ /* Hack -- get kicked out of the store */
+ if (st_ptr->store_open >= turn) leave_store = TRUE;
+ }
+
+ /* Free turn XXX XXX XXX */
+ energy_use = 0;
+
+ /* Recreate the level only when needed */
+ if (recreate)
+ {
+ /* Reinit wilderness to activate quests ... */
+ p_ptr->oldpx = p_ptr->px;
+ p_ptr->oldpy = p_ptr->py;
+
+ p_ptr->leaving = TRUE;
+ }
+
+ /* Hack -- Character is no longer in "icky" mode */
+ character_icky = FALSE;
+
+
+ /* Hack -- Cancel automatic command */
+ command_new = 0;
+
+ /* Mega-Hack -- Clear the 'ignore-keymaps' list */
+ memset(request_command_ignore_keymaps, 0, 12);
+
+ /* Flush messages XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Clear the screen */
+ Term_clear();
+
+
+ /* Update everything */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE);
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw entire screen */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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->store_open = 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;
+
+ /* 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];
+
+ /* 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;
+
+ /* 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]);
+ }
+}
+
+
+/*
+ * Enter the home, and interact with it from the dungeon (trump magic).
+ *
+ * Note that we use the standard "request_command()" function
+ * to get a command, allowing us to use "command_arg" and all
+ * command macros and other nifty stuff, but we use the special
+ * "shopping" argument, to force certain commands to be converted
+ * into other commands, normally, we convert "p" (pray) and "m"
+ * (cast magic) into "g" (get), and "s" (search) into "d" (drop).
+ */
+void do_cmd_home_trump(void)
+{
+ int which;
+ int maintain_num;
+ int tmp_chr;
+ int i;
+ int town_num;
+
+ /* Extract the store code */
+ which = 7;
+
+ if (p_ptr->town_num) town_num = p_ptr->town_num;
+ else town_num = 1;
+
+ /* Hack -- Check the "locked doors" */
+ if (town_info[town_num].store[which].store_open >= turn)
+ {
+ msg_print("The doors are locked.");
+ return;
+ }
+
+ /* Calculate the number of store maintainances since the last visit */
+ maintain_num = (turn - town_info[town_num].store[which].last_visit) / (10L * STORE_TURNS);
+
+ /* Maintain the store max. 10 times */
+ if (maintain_num > 10) maintain_num = 10;
+
+ if (maintain_num)
+ {
+ /* Maintain the store */
+ for (i = 0; i < maintain_num; i++)
+ store_maint(town_num, which);
+
+ /* Save the visit */
+ town_info[town_num].store[which].last_visit = turn;
+ }
+
+ /* Forget the lite */
+ /* forget_lite(); */
+
+ /* Forget the view */
+ forget_view();
+
+
+ /* Hack -- Character is in "icky" mode */
+ character_icky = TRUE;
+
+
+ /* No command argument */
+ command_arg = 0;
+
+ /* No repeated command */
+ command_rep = 0;
+
+ /* No automatic command */
+ command_new = 0;
+
+
+ /* Save the store number */
+ cur_store_num = which;
+
+ /* Save the store and owner pointers */
+ st_ptr = &town_info[town_num].store[cur_store_num];
+ ot_ptr = &ow_info[st_ptr->owner];
+
+
+ /* Start at the beginning */
+ store_top = 0;
+
+ /* Display the store */
+ display_store();
+
+ /* Mega-Hack -- Ignore keymaps on store action letters */
+ for (i = 0; i < 6; i++)
+ {
+ store_action_type *ba_ptr =
+ &ba_info[st_info[st_ptr->st_idx].actions[i]];
+ request_command_ignore_keymaps[2*i] = ba_ptr->letter;
+ request_command_ignore_keymaps[2*i+1] = ba_ptr->letter_aux;
+ }
+
+ /* Do not leave */
+ leave_store = FALSE;
+
+ /* Interact with player */
+ while (!leave_store)
+ {
+ /* Hack -- Clear line 1 */
+ prt("", 1, 0);
+
+ /* Hack -- Check the charisma */
+ tmp_chr = p_ptr->stat_use[A_CHR];
+
+ /* Clear */
+ clear_from(21);
+
+
+ /* Basic commands */
+ prt(" ESC) Exit from Building.", 22, 0);
+
+ /* Browse if necessary */
+ if (st_ptr->stock_num > 12)
+ {
+ prt(" SPACE) Next page of stock", 23, 0);
+ }
+
+ /* Home commands */
+ if (cur_store_num == 7)
+ {
+ prt(" g) Get an item.", 22, 31);
+ prt(" d) Drop an item.", 23, 31);
+ }
+
+ /* Shop commands XXX XXX XXX */
+ else
+ {
+ prt(" p) Purchase an item.", 22, 31);
+ prt(" s) Sell an item.", 23, 31);
+ }
+
+ /* Add in the eXamine option */
+ prt(" x) eXamine an item.", 22, 56);
+
+ /* Prompt */
+ prt("You may: ", 21, 0);
+
+ /* Get a command */
+ request_command(TRUE);
+
+ /* Process the command */
+ store_process_command();
+
+ /* Hack -- Character is still in "icky" mode */
+ character_icky = TRUE;
+
+ /* Notice stuff */
+ notice_stuff();
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* XXX XXX XXX Pack Overflow */
+ if (p_ptr->inventory[INVEN_PACK].k_idx)
+ {
+ int item = INVEN_PACK;
+
+ object_type *o_ptr = &p_ptr->inventory[item];
+
+ /* Hack -- Flee from the store */
+ if (cur_store_num != 7)
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee the store...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Flee from the home */
+ else if (!store_check_num(o_ptr))
+ {
+ /* Message */
+ msg_print("Your pack is so full that you flee your home...");
+
+ /* Leave */
+ leave_store = TRUE;
+ }
+
+ /* Hack -- Drop items into the home */
+ else
+ {
+ int item_pos;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ char o_name[80];
+
+
+ /* Give a message */
+ msg_print("Your pack overflows!");
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Grab a copy of the item */
+ object_copy(q_ptr, o_ptr);
+
+ /* Describe it */
+ object_desc(o_name, q_ptr, TRUE, 3);
+
+ /* Message */
+ msg_format("You drop %s (%c).", o_name, index_to_label(item));
+
+ /* Remove it from the players inventory */
+ inc_stack_size(item, -255);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Let the home carry it */
+ item_pos = home_carry(q_ptr);
+
+ /* Redraw the home */
+ if (item_pos >= 0)
+ {
+ store_top = (item_pos / 12) * 12;
+ display_inventory();
+ }
+ }
+ }
+
+ /* Hack -- Redisplay store prices if charisma changes */
+ if (tmp_chr != p_ptr->stat_use[A_CHR]) display_inventory();
+
+ /* Hack -- get kicked out of the store */
+ if (st_ptr->store_open >= turn) leave_store = TRUE;
+ }
+
+
+ /* Hack -- Character is no longer in "icky" mode */
+ character_icky = FALSE;
+
+
+ /* Hack -- Cancel automatic command */
+ command_new = 0;
+
+ /* Mega-Hack -- Clear the 'ignore-keymaps' list */
+ memset(request_command_ignore_keymaps, 0, 12);
+
+ /* Flush messages XXX XXX XXX */
+ msg_print(NULL);
+
+
+ /* Clear the screen */
+ Term_clear();
+
+
+ /* Update everything */
+ p_ptr->update |= (PU_VIEW | PU_MON_LITE);
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw entire screen */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
diff --git a/src/store.hpp b/src/store.hpp
new file mode 100644
index 00000000..f67d94eb
--- /dev/null
+++ b/src/store.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+extern void do_cmd_store();
+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 do_cmd_home_trump();
+extern void store_sell();
+extern void store_purchase();
+extern void store_examine();
+extern void store_stole();
+extern void store_prt_gold();
diff --git a/src/store_action_type.hpp b/src/store_action_type.hpp
new file mode 100644
index 00000000..048e13a0
--- /dev/null
+++ b/src/store_action_type.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Store/building actions.
+ */
+struct store_action_type
+{
+ const char *name; /* Name */
+
+ 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 */
+};
diff --git a/src/store_action_type_fwd.hpp b/src/store_action_type_fwd.hpp
new file mode 100644
index 00000000..e1746dad
--- /dev/null
+++ b/src/store_action_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct store_action_type;
diff --git a/src/store_info_type.hpp b/src/store_info_type.hpp
new file mode 100644
index 00000000..030afe91
--- /dev/null
+++ b/src/store_info_type.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Number of items to choose stock from
+ */
+constexpr int STORE_CHOICES = 56;
+
+/**
+ * Store descriptor.
+ */
+struct store_info_type
+{
+ const char *name; /* Name */
+
+ 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 */
+};
diff --git a/src/store_info_type_fwd.hpp b/src/store_info_type_fwd.hpp
new file mode 100644
index 00000000..a0dace90
--- /dev/null
+++ b/src/store_info_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct store_info_type;
diff --git a/src/store_type.hpp b/src/store_type.hpp
new file mode 100644
index 00000000..e3f917ac
--- /dev/null
+++ b/src/store_type.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type.hpp"
+
+/**
+ * A store, with an owner, various state flags, a current stock
+ * of items, and a table of items that are often purchased.
+ */
+struct store_type
+{
+ u16b st_idx;
+
+ /**
+ * Owner index
+ */
+ u16b owner;
+
+ /**
+ * Closed until this turn.
+ */
+ s32b store_open;
+
+ /**
+ * Last visited on this turn.
+ */
+ s32b last_visit;
+
+ /**
+ * Stock: Number of entries.
+ */
+ byte stock_num;
+
+ /**
+ * Stock: Total size of array
+ */
+ s16b stock_size;
+
+ /**
+ * Stock: Actual stock items
+ */
+ object_type *stock;
+};
diff --git a/src/store_type_fwd.hpp b/src/store_type_fwd.hpp
new file mode 100644
index 00000000..15410563
--- /dev/null
+++ b/src/store_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct store_type;
diff --git a/src/tables.cc b/src/tables.cc
new file mode 100644
index 00000000..09e6c18c
--- /dev/null
+++ b/src/tables.cc
@@ -0,0 +1,4506 @@
+/*
+ * 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 "tables.hpp"
+#include "tables.h"
+
+#include "modules.hpp"
+#include "options.hpp"
+#include "q_library.hpp"
+#include "q_fireprof.hpp"
+#include "q_bounty.hpp"
+#include "q_thrain.hpp"
+#include "q_narsil.hpp"
+#include "q_evil.hpp"
+#include "q_betwen.hpp"
+#include "q_haunted.hpp"
+#include "q_invas.hpp"
+#include "q_nirna.hpp"
+#include "q_eol.hpp"
+#include "q_god.hpp"
+#include "q_dragons.hpp"
+#include "q_poison.hpp"
+#include "q_spider.hpp"
+#include "q_wolves.hpp"
+#include "q_shroom.hpp"
+#include "q_nazgul.hpp"
+#include "q_wight.hpp"
+#include "q_troll.hpp"
+#include "q_hobbit.hpp"
+#include "q_thief.hpp"
+#include "q_ultrae.hpp"
+#include "q_ultrag.hpp"
+#include "q_one.hpp"
+#include "q_main.hpp"
+#include "q_rand.hpp"
+#include "stats.hpp"
+#include "variable.hpp"
+
+
+
+/*
+ * 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) -- 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 (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 },
+};
+
+
+
+/*
+ * This table allows quick conversion from "speed" to "energy"
+ * The basic function WAS ((S>=110) ? (S-110) : (100 / (120-S)))
+ * Note that table access is *much* quicker than computation.
+ *
+ * Note that the table has been changed at high speeds. From
+ * "Slow (-40)" to "Fast (+30)" is pretty much unchanged, but
+ * at speeds above "Fast (+30)", one approaches an asymptotic
+ * effective limit of 50 energy per turn. This means that it
+ * is relatively easy to reach "Fast (+30)" and get about 40
+ * energy per turn, but then speed becomes very "expensive",
+ * and you must get all the way to "Fast (+50)" to reach the
+ * point of getting 45 energy per turn. After that point,
+ * further increases in speed are more or less pointless,
+ * except to balance out heavy inventory.
+ *
+ * Note that currently the fastest monster is "Fast (+30)".
+ *
+ * It should be possible to lower the energy threshold from
+ * 100 units to 50 units, though this may interact badly with
+ * the (compiled out) small random energy boost code. It may
+ * also tend to cause more "clumping" at high speeds.
+ */
+byte extract_energy[300] =
+{
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* Slow */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* S-50 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* S-40 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ /* S-30 */ 2, 2, 2, 2, 2, 2, 2, 3, 3, 3,
+ /* S-20 */ 3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+ /* S-10 */ 5, 5, 5, 5, 6, 6, 7, 7, 8, 9,
+ /* Norm */ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ /* F+10 */ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ /* F+20 */ 30, 31, 32, 33, 34, 35, 36, 36, 37, 37,
+ /* F+30 */ 38, 38, 39, 39, 40, 40, 40, 41, 41, 41,
+ /* F+40 */ 42, 42, 42, 43, 43, 43, 44, 44, 44, 44,
+ /* F+50 */ 45, 45, 45, 45, 45, 46, 46, 46, 46, 46,
+ /* F+60 */ 47, 47, 47, 47, 47, 48, 48, 48, 48, 48,
+ /* F+70 */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Fast */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+ /* Virtual */ 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
+};
+
+
+
+
+/*
+ * Base experience levels, may be adjusted up for race and/or class
+ */
+s32b player_exp[PY_MAX_LEVEL] =
+{
+ 10,
+ 25,
+ 45,
+ 70,
+ 100,
+ 140,
+ 200,
+ 280,
+ 380,
+ 500,
+ 650,
+ 850,
+ 1100,
+ 1400,
+ 1800,
+ 2300,
+ 2900,
+ 3600,
+ 4400,
+ 5400,
+ 6800,
+ 8400,
+ 10200,
+ 12500,
+ 17500,
+ 25000,
+ 35000L,
+ 50000L,
+ 75000L,
+ 100000L,
+ 150000L,
+ 200000L,
+ 275000L,
+ 350000L,
+ 450000L,
+ 550000L,
+ 700000L,
+ 850000L,
+ 1000000L,
+ 1250000L,
+ 1500000L,
+ 1800000L,
+ 2100000L,
+ 2400000L,
+ 2700000L,
+ 3000000L,
+ 3500000L,
+ 4000000L,
+ 4500000L,
+ 5000000L
+};
+
+
+/*
+ * Player Sexes
+ *
+ * Title,
+ * Winner
+ */
+player_sex sex_info[MAX_SEXES] =
+{
+ {
+ "Female",
+ "Queen"
+ },
+
+{
+"Male",
+"King"
+},
+{
+"Neuter",
+"Ruler"
+}
+};
+
+/*
+ * Hack -- the "basic" color names (see "TERM_xxx")
+ */
+cptr color_names[16] =
+{
+ "Dark",
+ "White",
+ "Slate",
+ "Orange",
+ "Red",
+ "Green",
+ "Blue",
+ "Umber",
+ "Light Dark",
+ "Light Slate",
+ "Violet",
+ "Yellow",
+ "Light Red",
+ "Light Green",
+ "Light Blue",
+ "Light Umber",
+};
+
+
+/*
+ * Abbreviations of healthy stats
+ */
+cptr stat_names[6] =
+{
+ "STR", "INT", "WIS", "DEX", "CON", "CHR"
+};
+
+/*
+ * Abbreviations of damaged stats
+ */
+cptr stat_names_reduced[6] =
+{
+ "Str", "Int", "Wis", "Dex", "Con", "Chr"
+};
+
+
+/*
+ * Certain "screens" always use the main screen, including News, Birth,
+ * Dungeon, Tomb-stone, High-scores, Macros, Colors, Visuals, Options.
+ *
+ * Later, special flags may allow sub-windows to "steal" stuff from the
+ * main window, including File dump (help), File dump (artifacts, uniques),
+ * Character screen, Small scale map, Previous Messages, Store screen, etc.
+ *
+ * The "ctrl-i" (tab) command flips the "Display inven/equip" and "Display
+ * equip/inven" flags for all windows.
+ *
+ * The "ctrl-g" command (or pseudo-command) should perhaps grab a snapshot
+ * of the main screen into any interested windows.
+ */
+cptr window_flag_desc[32] =
+{
+ "Display inven/equip",
+ "Display equip/inven",
+ NULL,
+ "Display character",
+ "Show visible monsters",
+ NULL,
+ "Display messages",
+ "Display overhead view",
+ "Display monster recall",
+ "Display object recall",
+ NULL,
+ "Display snap-shot",
+ NULL,
+ NULL,
+ "Display borg messages",
+ "Display borg status",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+/*
+ * Available Options
+ *
+ * Option Screen Sets:
+ *
+ * Set 1: User Interface
+ * Set 2: Disturbance
+ * Set 3: Inventory
+ * Set 4: Game Play
+ * Set 5: ToME
+ * Set 6: Birth
+ *
+ * Note that bits 28-31 of set 0 are currently unused.
+ */
+option_type option_info[] =
+{
+ /*** User-Interface ***/
+
+ { &rogue_like_commands, FALSE, 1, 0,
+ "rogue_like_commands", "Rogue-like commands" },
+
+ { &quick_messages, TRUE, 1, 1,
+ "quick_messages", "Activate quick messages" },
+
+ { &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" },
+
+ { &always_repeat, TRUE, 1, 7,
+ "always_repeat", "Repeat obvious commands" },
+
+ { &ring_bell, FALSE, 1, 18,
+ "ring_bell", "Audible bell (on errors, etc)" },
+ /* Changed to default to FALSE -- it's so extremely annoying!!! -TY */
+
+ /*** Disturbance ***/
+
+ { &find_ignore_stairs, FALSE, 2, 0,
+ "find_ignore_stairs", "Run past stairs" },
+
+ { &find_ignore_doors, TRUE, 2, 1,
+ "find_ignore_doors", "Run through open doors" },
+
+ { &find_cut, FALSE, 2, 2,
+ "find_cut", "Run past known corners" },
+
+ { &find_examine, TRUE, 2, 3,
+ "find_examine", "Run into potential corners" },
+
+ { &disturb_move, FALSE, 2, 4,
+ "disturb_move", "Disturb whenever any monster moves" },
+
+ { &disturb_near, TRUE, 2, 5,
+ "disturb_near", "Disturb whenever viewable monster moves" },
+
+ { &disturb_panel, TRUE, 2, 6,
+ "disturb_panel", "Disturb whenever map panel changes" },
+
+ { &disturb_detect, TRUE, 2, 21,
+ "disturb_detect", "Disturb whenever leaving trap-detected area" },
+
+ { &disturb_state, TRUE, 2, 7,
+ "disturb_state", "Disturb whenever player state changes" },
+
+ { &disturb_minor, TRUE, 2, 8,
+ "disturb_minor", "Disturb whenever boring things happen" },
+
+ { &disturb_other, FALSE, 2, 9,
+ "disturb_other", "Disturb whenever random things happen" },
+
+ { &alert_hitpoint, FALSE, 2, 10,
+ "alert_hitpoint", "Alert user to critical hitpoints" },
+
+ { &alert_failure, FALSE, 2, 11,
+ "alert_failure", "Alert user to various failures" },
+
+ { &last_words, TRUE, 2, 12,
+ "last_words", "Get last words when the character dies" },
+
+ { &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" },
+
+ /*** Game-Play ***/
+
+ { &auto_scum, TRUE, 3, 1,
+ "auto_scum", "Auto-scum for good levels" },
+
+ { &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" },
+
+ { &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)" },
+
+ { &smart_learn, FALSE, 3, 14,
+ "smart_learn", "Monsters learn from their mistakes" },
+
+ { &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" },
+
+ { &avoid_abort, FALSE, 4, 2,
+ "avoid_abort", "Avoid checking for user abort" },
+
+ { &avoid_shimmer, FALSE, 4, 17,
+ "avoid_shimmer", "Avoid extra shimmering (fast)" },
+
+ { &avoid_other, FALSE, 4, 3,
+ "avoid_other", "Avoid processing special colors (fast)" },
+
+ { &flush_failure, TRUE, 4, 4,
+ "flush_failure", "Flush input on various failures" },
+
+ { &flush_disturb, FALSE, 4, 5,
+ "flush_disturb", "Flush input whenever disturbed" },
+
+ { &flush_command, FALSE, 4, 6,
+ "flush_command", "Flush input before every command" },
+
+ { &fresh_before, TRUE, 4, 7,
+ "fresh_before", "Flush output before every command" },
+
+ { &fresh_after, FALSE, 4, 8,
+ "fresh_after", "Flush output after every command" },
+
+ { &fresh_message, FALSE, 4, 9,
+ "fresh_message", "Flush output after every message" },
+
+ { &hilite_player, FALSE, 4, 11,
+ "hilite_player", "Hilite the player with the cursor" },
+
+ { &view_yellow_lite, FALSE, 4, 12,
+ "view_yellow_lite", "Use special colors for torch-lit grids" },
+
+ { &view_bright_lite, FALSE, 4, 13,
+ "view_bright_lite", "Use special colors for 'viewable' grids" },
+
+ { &view_granite_lite, FALSE, 4, 14,
+ "view_granite_lite", "Use special colors for wall grids (slow)" },
+
+ { &view_special_lite, FALSE, 4, 15,
+ "view_special_lite", "Use special colors for floor grids (slow)" },
+
+ { &center_player, FALSE, 4, 16,
+ "center_player", "Center the view on the player (very slow)" },
+
+ /*** ToME options ***/
+
+ { &option_ingame_help, TRUE, 5, 1,
+ "ingame_help", "Ingame contextual help" },
+
+ { &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 ***/
+
+ { &preserve, TRUE, 6, 2,
+ "preserve", "Preserve artifacts" },
+
+ { &autoroll, TRUE, 6, 3,
+ "autoroll", "Specify 'minimal' stats" },
+
+ { &point_based, FALSE, 6, 17,
+ "point_based", "Generate character using a point system" },
+
+ { &ironman_rooms, FALSE, 6, 6,
+ "ironman_rooms", "Always generate very unusual rooms" },
+
+ { &joke_monsters, FALSE, 6, 14,
+ "joke_monsters", "Allow use of some 'joke' monsters" },
+
+ { &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" },
+
+ { &no_selling, FALSE, 6, 20,
+ "no_selling", "Items always sell for 0 gold" },
+
+ /*** End of Table ***/
+
+ { NULL, 0, 0, 0,
+ NULL, NULL }
+};
+
+
+
+/* Names used for random artifact name generation */
+cptr artifact_names_list =
+ "adanedhel\n"
+ "adurant\n"
+ "aeglos\n"
+ "aegnor\n"
+ "aelin\n"
+ "aeluin\n"
+ "aerandir\n"
+ "aerin\n"
+ "agarwaen\n"
+ "aglareb\n"
+ "aglarond\n"
+ "aglon\n"
+ "ainulindale\n"
+ "ainur\n"
+ "alcarinque\n"
+ "aldaron\n"
+ "aldudenie\n"
+ "almaren\n"
+ "alqualonde\n"
+ "aman\n"
+ "amandil\n"
+ "amarie\n"
+ "amarth\n"
+ "amlach\n"
+ "amon\n"
+ "amras\n"
+ "amrod\n"
+ "anach\n"
+ "anar\n"
+ "anarion\n"
+ "ancalagon\n"
+ "ancalimon\n"
+ "anarrima\n"
+ "andor\n"
+ "andram\n"
+ "androth\n"
+ "anduin\n"
+ "andunie\n"
+ "anfauglir\n"
+ "anfauglith\n"
+ "angainor\n"
+ "angband\n"
+ "anghabar\n"
+ "anglachel\n"
+ "angrenost\n"
+ "angrim\n"
+ "angrist\n"
+ "angrod\n"
+ "anguirel\n"
+ "annael\n"
+ "annatar\n"
+ "annon\n"
+ "annuminas\n"
+ "apanonar\n"
+ "aradan\n"
+ "aragorn\n"
+ "araman\n"
+ "aranel\n"
+ "aranruth\n"
+ "aranwe\n"
+ "aras\n"
+ "aratan\n"
+ "aratar\n"
+ "arathorn\n"
+ "arda\n"
+ "ard-galen\n"
+ "aredhel\n"
+ "ar-feiniel\n"
+ "argonath\n"
+ "arien\n"
+ "armenelos\n"
+ "arminas\n"
+ "arnor\n"
+ "aros\n"
+ "arossiach\n"
+ "arthad\n"
+ "arvernien\n"
+ "arwen\n"
+ "ascar\n"
+ "astaldo\n"
+ "atalante\n"
+ "atanamir\n"
+ "atanatari\n"
+ "atani\n"
+ "aule\n"
+ "avallone\n"
+ "avari\n"
+ "avathar\n"
+ "balan\n"
+ "balar\n"
+ "balrog\n"
+ "barad\n"
+ "baragund\n"
+ "barahir\n"
+ "baran\n"
+ "baranduin\n"
+ "bar\n"
+ "bauglir\n"
+ "beleg\n"
+ "belegaer\n"
+ "belegost\n"
+ "belegund\n"
+ "beleriand\n"
+ "belfalas\n"
+ "belthil\n"
+ "belthronding\n"
+ "beor\n"
+ "beraid\n"
+ "bereg\n"
+ "beren\n"
+ "boromir\n"
+ "boron\n"
+ "bragollach\n"
+ "brandir\n"
+ "bregolas\n"
+ "bregor\n"
+ "brethil\n"
+ "brilthor\n"
+ "brithiach\n"
+ "brithombar\n"
+ "brithon\n"
+ "cabed\n"
+ "calacirya\n"
+ "calaquendi\n"
+ "calenardhon\n"
+ "calion\n"
+ "camlost\n"
+ "caragdur\n"
+ "caranthir\n"
+ "carcharoth\n"
+ "cardolan\n"
+ "carnil\n"
+ "celeborn\n"
+ "celebrant\n"
+ "celebrimbor\n"
+ "celebrindal\n"
+ "celebros\n"
+ "celegorm\n"
+ "celon\n"
+ "cirdan\n"
+ "cirith\n"
+ "cirth\n"
+ "ciryatan\n"
+ "ciryon\n"
+ "coimas\n"
+ "corollaire\n"
+ "crissaegrim\n"
+ "cuarthal\n"
+ "cuivienen\n"
+ "culurien\n"
+ "curufin\n"
+ "curufinwe\n"
+ "curunir\n"
+ "cuthalion\n"
+ "daedeloth\n"
+ "daeron\n"
+ "dagnir\n"
+ "dagor\n"
+ "dagorlad\n"
+ "dairuin\n"
+ "danwedh\n"
+ "delduwath\n"
+ "denethor\n"
+ "dimbar\n"
+ "dimrost\n"
+ "dinen\n"
+ "dior\n"
+ "dirnen\n"
+ "dolmed\n"
+ "doriath\n"
+ "dorlas\n"
+ "dorthonion\n"
+ "draugluin\n"
+ "drengist\n"
+ "duath\n"
+ "duinath\n"
+ "duilwen\n"
+ "dunedain\n"
+ "dungortheb\n"
+ "earendil\n"
+ "earendur\n"
+ "earnil\n"
+ "earnur\n"
+ "earrame\n"
+ "earwen\n"
+ "echor\n"
+ "echoriath\n"
+ "ecthelion\n"
+ "edain\n"
+ "edrahil\n"
+ "eglador\n"
+ "eglarest\n"
+ "eglath\n"
+ "eilinel\n"
+ "eithel\n"
+ "ekkaia\n"
+ "elbereth\n"
+ "eldalie\n"
+ "eldalieva\n"
+ "eldamar\n"
+ "eldar\n"
+ "eledhwen\n"
+ "elemmire\n"
+ "elende\n"
+ "elendil\n"
+ "elendur\n"
+ "elenna\n"
+ "elentari\n"
+ "elenwe\n"
+ "elerrina\n"
+ "elleth\n"
+ "elmoth\n"
+ "elostirion\n"
+ "elrond\n"
+ "elros\n"
+ "elu\n"
+ "eluchil\n"
+ "elured\n"
+ "elurin\n"
+ "elwe\n"
+ "elwing\n"
+ "emeldir\n"
+ "endor\n"
+ "engrin\n"
+ "engwar\n"
+ "eol\n"
+ "eonwe\n"
+ "ephel\n"
+ "erchamion\n"
+ "ereb\n"
+ "ered\n"
+ "erech\n"
+ "eregion\n"
+ "ereinion\n"
+ "erellont\n"
+ "eressea\n"
+ "eriador\n"
+ "eru\n"
+ "esgalduin\n"
+ "este\n"
+ "estel\n"
+ "estolad\n"
+ "ethir\n"
+ "ezellohar\n"
+ "faelivrin\n"
+ "falas\n"
+ "falathar\n"
+ "falathrim\n"
+ "falmari\n"
+ "faroth\n"
+ "fauglith\n"
+ "feanor\n"
+ "feanturi\n"
+ "felagund\n"
+ "finarfin\n"
+ "finduilas\n"
+ "fingolfin\n"
+ "fingon\n"
+ "finwe\n"
+ "firimar\n"
+ "formenos\n"
+ "fornost\n"
+ "frodo\n"
+ "fuin\n"
+ "fuinur\n"
+ "gabilgathol\n"
+ "galad\n"
+ "galadriel\n"
+ "galathilion\n"
+ "galdor\n"
+ "galen\n"
+ "galvorn\n"
+ "gandalf\n"
+ "gaurhoth\n"
+ "gelion\n"
+ "gelmir\n"
+ "gelydh\n"
+ "gil\n"
+ "gildor\n"
+ "giliath\n"
+ "ginglith\n"
+ "girith\n"
+ "glaurung\n"
+ "glingal\n"
+ "glirhuin\n"
+ "gloredhel\n"
+ "glorfindel\n"
+ "golodhrim\n"
+ "gondolin\n"
+ "gondor\n"
+ "gonnhirrim\n"
+ "gorgoroth\n"
+ "gorlim\n"
+ "gorthaur\n"
+ "gorthol\n"
+ "gothmog\n"
+ "guilin\n"
+ "guinar\n"
+ "guldur\n"
+ "gundor\n"
+ "gurthang\n"
+ "gwaith\n"
+ "gwareth\n"
+ "gwindor\n"
+ "hadhodrond\n"
+ "hador\n"
+ "haladin\n"
+ "haldad\n"
+ "haldan\n"
+ "haldar\n"
+ "haldir\n"
+ "haleth\n"
+ "halmir\n"
+ "handir\n"
+ "harad\n"
+ "hareth\n"
+ "hathaldir\n"
+ "hathol\n"
+ "haudh\n"
+ "helcar\n"
+ "helcaraxe\n"
+ "helevorn\n"
+ "helluin\n"
+ "herumor\n"
+ "herunumen\n"
+ "hildorien\n"
+ "himlad\n"
+ "himring\n"
+ "hirilorn\n"
+ "hisilome\n"
+ "hithaeglir\n"
+ "hithlum\n"
+ "hollin\n"
+ "huan\n"
+ "hunthor\n"
+ "huor\n"
+ "hurin\n"
+ "hyarmendacil\n"
+ "hyarmentir\n"
+ "iant\n"
+ "iaur\n"
+ "ibun\n"
+ "idril\n"
+ "illuin\n"
+ "ilmare\n"
+ "ilmen\n"
+ "iluvatar\n"
+ "imlach\n"
+ "imladris\n"
+ "indis\n"
+ "ingwe\n"
+ "irmo\n"
+ "isil\n"
+ "isildur\n"
+ "istari\n"
+ "ithil\n"
+ "ivrin\n"
+ "kelvar\n"
+ "kementari\n"
+ "ladros\n"
+ "laiquendi\n"
+ "lalaith\n"
+ "lamath\n"
+ "lammoth\n"
+ "lanthir\n"
+ "laurelin\n"
+ "leithian\n"
+ "legolin\n"
+ "lembas\n"
+ "lenwe\n"
+ "linaewen\n"
+ "lindon\n"
+ "lindorie\n"
+ "loeg\n"
+ "lomelindi\n"
+ "lomin\n"
+ "lomion\n"
+ "lorellin\n"
+ "lorien\n"
+ "lorindol\n"
+ "losgar\n"
+ "lothlann\n"
+ "lothlorien\n"
+ "luin\n"
+ "luinil\n"
+ "lumbar\n"
+ "luthien\n"
+ "mablung\n"
+ "maedhros\n"
+ "maeglin\n"
+ "maglor\n"
+ "magor\n"
+ "mahanaxar\n"
+ "mahtan\n"
+ "maiar\n"
+ "malduin\n"
+ "malinalda\n"
+ "mandos\n"
+ "manwe\n"
+ "mardil\n"
+ "melian\n"
+ "melkor\n"
+ "menegroth\n"
+ "meneldil\n"
+ "menelmacar\n"
+ "meneltarma\n"
+ "minas\n"
+ "minastir\n"
+ "mindeb\n"
+ "mindolluin\n"
+ "mindon\n"
+ "minyatur\n"
+ "mirdain\n"
+ "miriel\n"
+ "mithlond\n"
+ "mithrandir\n"
+ "mithrim\n"
+ "mordor\n"
+ "morgoth\n"
+ "morgul\n"
+ "moria\n"
+ "moriquendi\n"
+ "mormegil\n"
+ "morwen\n"
+ "nahar\n"
+ "naeramarth\n"
+ "namo\n"
+ "nandor\n"
+ "nargothrond\n"
+ "narog\n"
+ "narsil\n"
+ "narsilion\n"
+ "narya\n"
+ "nauglamir\n"
+ "naugrim\n"
+ "ndengin\n"
+ "neithan\n"
+ "neldoreth\n"
+ "nenar\n"
+ "nenning\n"
+ "nenuial\n"
+ "nenya\n"
+ "nerdanel\n"
+ "nessa\n"
+ "nevrast\n"
+ "nibin\n"
+ "nienna\n"
+ "nienor\n"
+ "nimbrethil\n"
+ "nimloth\n"
+ "nimphelos\n"
+ "nimrais\n"
+ "nimras\n"
+ "ningloron\n"
+ "niniel\n"
+ "ninniach\n"
+ "ninquelote\n"
+ "niphredil\n"
+ "nirnaeth\n"
+ "nivrim\n"
+ "noegyth\n"
+ "nogrod\n"
+ "noldolante\n"
+ "noldor\n"
+ "numenor\n"
+ "nurtale\n"
+ "obel\n"
+ "ohtar\n"
+ "oiolosse\n"
+ "oiomure\n"
+ "olorin\n"
+ "olvar\n"
+ "olwe\n"
+ "ondolinde\n"
+ "orfalch\n"
+ "ormal\n"
+ "orocarni\n"
+ "orodreth\n"
+ "orodruin\n"
+ "orome\n"
+ "oromet\n"
+ "orthanc\n"
+ "osgiliath\n"
+ "osse\n"
+ "ossiriand\n"
+ "palantir\n"
+ "pelargir\n"
+ "pelori\n"
+ "periannath\n"
+ "quendi\n"
+ "quenta\n"
+ "quenya\n"
+ "radagast\n"
+ "radhruin\n"
+ "ragnor\n"
+ "ramdal\n"
+ "rana\n"
+ "rathloriel\n"
+ "rauros\n"
+ "region\n"
+ "rerir\n"
+ "rhovanion\n"
+ "rhudaur\n"
+ "rhun\n"
+ "rhunen\n"
+ "rian\n"
+ "ringil\n"
+ "ringwil\n"
+ "romenna\n"
+ "rudh\n"
+ "rumil\n"
+ "saeros\n"
+ "salmar\n"
+ "saruman\n"
+ "sauron\n"
+ "serech\n"
+ "seregon\n"
+ "serinde\n"
+ "shelob\n"
+ "silmarien\n"
+ "silmaril\n"
+ "silpion\n"
+ "sindar\n"
+ "singollo\n"
+ "sirion\n"
+ "soronume\n"
+ "sul\n"
+ "sulimo\n"
+ "talath\n"
+ "taniquetil\n"
+ "tar\n"
+ "taras\n"
+ "tarn\n"
+ "tathren\n"
+ "taur\n"
+ "tauron\n"
+ "teiglin\n"
+ "telchar\n"
+ "telemnar\n"
+ "teleri\n"
+ "telperion\n"
+ "telumendil\n"
+ "thalion\n"
+ "thalos\n"
+ "thangorodrim\n"
+ "thargelion\n"
+ "thingol\n"
+ "thoronath\n"
+ "thorondor\n"
+ "thranduil\n"
+ "thuringwethil\n"
+ "tilion\n"
+ "tintalle\n"
+ "tinuviel\n"
+ "tirion\n"
+ "tirith\n"
+ "tol\n"
+ "tulkas\n"
+ "tumhalad\n"
+ "tumladen\n"
+ "tuna\n"
+ "tuor\n"
+ "turambar\n"
+ "turgon\n"
+ "turin\n"
+ "uial\n"
+ "uilos\n"
+ "uinen\n"
+ "ulairi\n"
+ "ulmo\n"
+ "ulumuri\n"
+ "umanyar\n"
+ "umarth\n"
+ "umbar\n"
+ "ungoliant\n"
+ "urthel\n"
+ "uruloki\n"
+ "utumno\n"
+ "vaire\n"
+ "valacirca\n"
+ "valandil\n"
+ "valaquenta\n"
+ "valar\n"
+ "valaraukar\n"
+ "valaroma\n"
+ "valier\n"
+ "valimar\n"
+ "valinor\n"
+ "valinoreva\n"
+ "valmar\n"
+ "vana\n"
+ "vanyar\n"
+ "varda\n"
+ "vasa\n"
+ "vilya\n"
+ "vingilot\n"
+ "vinyamar\n"
+ "voronwe\n"
+ "wethrin\n"
+ "wilwarin\n"
+ "yavanna\n"
+ ;
+
+
+martial_arts ma_blows[MAX_MA] =
+{
+ { "You punch %s.", 1, 0, 2, 4, 0, 0 },
+ { "You kick %s.", 2, 0, 2, 6, 0, 0 },
+ { "You strike %s.", 3, 0, 2, 7, 0, 0 },
+ { "You hit %s with your knee.", 5, 5, 4, 3, MA_KNEE, 0 },
+ { "You hit %s with your elbow.", 7, 5, 2, 8, 0, 0 },
+ { "You butt %s.", 9, 10, 4, 5, 0, 0 },
+ { "You kick %s.", 11, 10, 6, 4, MA_SLOW, 0 },
+ { "You uppercut %s.", 13, 12, 8, 4, MA_STUN, 6 },
+ { "You double-kick %s.", 16, 15, 10, 4, MA_STUN, 8 },
+ { "You hit %s with a Cat's Claw.", 20, 20, 10, 5, 0, 0 },
+ { "You hit %s with a jump kick.", 25, 25, 10, 6, MA_STUN, 10 },
+ { "You hit %s with an Eagle's Claw.", 29, 25, 12, 6, 0, 0 },
+ { "You hit %s with a circle kick.", 33, 30, 12, 8, MA_STUN, 10 },
+ { "You hit %s with an Iron Fist.", 37, 35, 16, 8, MA_STUN, 10 },
+ { "You hit %s with a flying kick.", 41, 35, 16, 10, MA_STUN, 12 },
+ { "You hit %s with a Dragon Fist.", 45, 35, 20, 10, MA_STUN, 16 },
+ { "You hit %s with a Crushing Blow.", 48, 35, 20, 12, MA_STUN, 18 },
+};
+
+/*
+ * cptr desc; A verbose attack description
+ * int min_level; Minimum level to use
+ * int chance; Chance of 'success
+ * int dd; Damage dice
+ * int ds; Damage sides
+ * s16b effect; Special effects
+ * s16b power; Special effects power
+ */
+martial_arts bear_blows[MAX_BEAR] =
+{
+ { "You claw %s.", 1, 0, 3, 4, MA_STUN, 4 },
+ { "You swat %s.", 4, 0, 4, 4, MA_WOUND, 20 },
+ { "You bite %s.", 9, 2, 4, 4, MA_WOUND, 30 },
+ { "You hug %s.", 15, 5, 6, 4, MA_FULL_SLOW, 0 },
+ { "You swat and rake %s.", 25, 10, 6, 5, MA_STUN | MA_WOUND, 10 },
+ { "You hug and claw %s.", 30, 15, 6, 6, MA_FULL_SLOW | MA_WOUND, 60 },
+ { "You double swat %s.", 35, 20, 9, 7, MA_STUN | MA_WOUND, 20 },
+ { "You double swat and rake %s.", 40, 25, 10, 10, MA_STUN | MA_WOUND, 25 },
+};
+
+
+magic_power mindcraft_powers[MAX_MINDCRAFT_POWERS] =
+{
+ /* Level gained, cost, %fail, name, desc */
+ {
+ /* Det. monsters/traps */
+ 1, 1, 15,
+ "Precognition",
+ "Detect monsters, traps and level layout and lights up at higher levels."
+ },
+ {
+ /* ~MM */
+ 2, 1, 20,
+ "Neural Blast",
+ "Blast the minds of your foes."
+ },
+ {
+ /* Phase/Between gate */
+ 3, 2, 25,
+ "Minor Displacement",
+ "Short distance teleportation"
+ },
+ {
+ /* Tele. Self / All */
+ 7, 6, 35,
+ "Major Displacement",
+ "Teleport you and others at high levels."
+ },
+ {
+ 9, 7, 50,
+ "Domination",
+ "Charm monsters"
+ },
+ {
+ /* Telekinetic "bolt" */
+ 11, 7, 30,
+ "Pulverise",
+ "Fires a bolt of pure sound."
+ },
+ {
+ /* Psychic/physical defenses */
+ 13, 12, 50,
+ "Character Armour",
+ "Sets up physical/elemental shield."
+ },
+ {
+ 15, 12, 60,
+ "Psychometry",
+ "Identifies objects."
+ },
+ {
+ /* Ball -> LOS */
+ 18, 10, 45,
+ "Mind Wave",
+ "Projects psi waves to crush the minds of your foes."
+ },
+ {
+ 23, 15, 50,
+ "Adrenaline Channeling",
+ "Heals you, cures you and speeds you."
+ },
+ {
+ /* Convert enemy HP to mana */
+ 25, 10, 40,
+ "Psychic Drain",
+ "Drain your foes' life into your mana reserves"
+ },
+ {
+ /* Ball -> LOS */
+ 28, 20, 45,
+ "Telekinetic Wave",
+ "Powerful wave of pure telekinetic forces."
+ },
+};
+
+magic_power necro_powers[MAX_NECRO_POWERS] =
+ {
+ /* Level gained, cost, %fail, name, desc */
+ {
+ /* Bolt/beam/ball/LOS of stun/scare */
+ 1, 2, 10,
+ "Horrify",
+ "Calls upon the darkness to stun and scare your foes."
+ },
+ {
+ /* Ball */
+ 5, 6, 20,
+ "Raise Dead",
+ "Brings back your foes in the form of various undead. Also, can heal monsters."
+ },
+ {
+ /* Summons weapon */
+ 12, 20, 25,
+ "Necromantic Teeth",
+ "Conjures a temporary vampiric weapon."
+ },
+ {
+ /* Heals when killing a monster */
+ 20, 10, 25,
+ "Absorb Soul",
+ "Gives back some life for each kill."
+ },
+ {
+ /* Bolt */
+ 30, 15, 20,
+ "Vampirism",
+ "Drain the life of your foes into your own."
+ },
+ {
+ /* The Death word, always bolt put your HP to 1 */
+ 35, 100, 25,
+ "Death",
+ "Instantly kills your opponent and you, turning yourself into an undead."
+ },
+};
+
+magic_power mimic_powers[MAX_MIMIC_POWERS] =
+{
+ /* Level gained, cost, %fail, name */
+ {
+ /* Use a book of lore */
+ 1, 2, 0,
+ "Mimic",
+ "Lets you use the powers of a Cloak of Mimicry."
+ },
+ {
+ /* Invisibility */
+ 10, 6, 20,
+ "Invisibility",
+ "Hides you from the sight of mortals."
+ },
+ {
+ /* +1 pair of legs */
+ 25, 20, 25,
+ "Legs Mimicry",
+ "Temporarily provides a new pair of legs."
+ },
+ {
+ /* wall form */
+ 30, 40, 30,
+ "Wall Mimicry",
+ "Temporarily lets you walk in walls, and ONLY in walls."
+ },
+ {
+ /* +1 pair of arms, +1 weapon */
+ 35, 100, 40,
+ "Arms Mimicry",
+ "Temporarily provides a new pair of arms."
+ },
+};
+
+magic_power symbiotic_powers[MAX_SYMBIOTIC_POWERS] =
+{
+ /* Level gained, cost, %fail, name */
+ {
+ 1, 1, 0,
+ "Hypnotise",
+ "Hypnotise a non-moving pet to allow you to enter symbiosis(wear) with it."
+ },
+ {
+ 1, 1, 0,
+ "Release",
+ "Release an hypnotised pet."
+ },
+ {
+ 3, 2, 10,
+ "Charm Never-Moving",
+ "Tries to charm a never-moving monster."
+ },
+ {
+ 5, 5, 20,
+ "Life Share",
+ "Evens out your life with your symbiote."
+ },
+ {
+ 10, 10, 20,
+ "Use Minor Powers",
+ "Allows you to use some of the powers of your symbiote."
+ },
+ {
+ 15, 14, 25,
+ "Heal Symbiote",
+ "Heals your symbiotic monster."
+ },
+ {
+ 25, 30, 40,
+ "Use major powers",
+ "Allows you to use all the powers of your symbiote."
+ },
+ {
+ 30, 35, 40,
+ "Summon Never-Moving Pet",
+ "Summons a never-moving pet."
+ },
+ {
+ 40, 60, 70,
+ "Force Symbiosis",
+ "Allows you to use all the powers of a monster in your line of sight."
+ },
+};
+
+
+/*
+ * Name and description (max. 10 lines) of the gods.
+ * Only the first four lines are printed at birth.
+ */
+
+deity_type deity_info[MAX_GODS] =
+{
+ {
+ { MODULE_TOME, MODULE_THEME, -1, },
+ "Nobody",
+ {
+ "Atheist",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ { MODULE_TOME, MODULE_THEME, -1, },
+ "Eru Iluvatar",
+ {
+ "He is the supreme god, he created the world, and most of its inhabitants.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ { MODULE_TOME, MODULE_THEME, -1, },
+ "Manwe Sulimo",
+ {
+ "He is the king of the Valar, most powerful of them after Melkor.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ { MODULE_TOME, MODULE_THEME, -1, },
+ "Tulkas",
+ {
+ "He is the last of the Valar that came to the world, and the fiercest fighter.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ { MODULE_TOME, MODULE_THEME, -1, },
+ "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.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ { MODULE_TOME, MODULE_THEME, -1, },
+ "Yavanna Kementari",
+ {
+ "She is the Vala of nature, protectress of the great forests of Middle-earth.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ { MODULE_THEME, -1, },
+ "Aule the Smith",
+ {
+ "Aule is a smith, and the creator of the Dwarves.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ { MODULE_THEME, -1, },
+ "Varda Elentari",
+ {
+ "The Queen of the Stars. In light is her power and joy.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ { MODULE_THEME, -1, },
+ "Ulmo",
+ {
+ "Ulmo is called Lord of Waters, he rules all that is water on Arda.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+ {
+ { MODULE_THEME, -1, },
+ "Mandos",
+ {
+ "The Doomsman of the Valar and keeper of the slain.",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ },
+};
+
+/* jk - to hit, to dam, to ac, to stealth, to disarm, to saving throw */
+/* this concept is taken from Adom, where Thomas Biskup thought it out, */
+/* as far as I know. */
+tactic_info_type tactic_info[9] =
+{
+ /* hit dam ac stl dis sav */
+ { -10, -10, + 15, + 3, + 15, + 14, "coward"}, /* 4-4 */
+ { -8, -8, + 10, + 2, + 9, + 9, "meek"}, /* 4-3 */
+ { -4, -4, + 5, + 1, + 5, + 5, "wary"}, /* 4-2 */
+ { -2, -2, + 2, + 1, + 2, + 2, "careful"}, /* 4-1 */
+ { 0, 0, 0, 0, 0, 0, "normal"}, /* 4+0 */
+ { 2, 2, -2, -1, -2, -3, "confident"}, /* 4+1 */
+ { 4, 4, -5, -2, -5, -7, "aggressive"}, /* 4+2 */
+ { 6, 6, -10, -3, -11, -12, "furious"}, /* 4+3 */
+ { 8, 12, -25, -5, -18, -18, "berserker"} /* 4+4 */
+};
+
+/*
+ * Random artifact activations.
+ */
+activation activation_info[MAX_T_ACT] =
+{
+ { "death", 0, ACT_DEATH },
+ { "ruination", 0, ACT_RUINATION },
+ { "destruction", 1000, ACT_DESTRUC },
+ { "stupidity", 0, ACT_UNINT },
+ { "weakness", 0, ACT_UNSTR },
+ { "unhealth", 0, ACT_UNCON },
+ { "ugliness", 0, ACT_UNCHR },
+ { "clumsiness", 0, ACT_UNDEX },
+ { "naivete", 0, ACT_UNWIS },
+ { "stat loss", 0, ACT_STATLOSS },
+ { "huge stat loss", 0, ACT_HISTATLOSS },
+ { "experience loss", 0, ACT_EXPLOSS },
+ { "huge experience loss", 0, ACT_HIEXPLOSS },
+ { "teleportation", 1000, ACT_TELEPORT },
+ { "monster summoning", 5, ACT_SUMMON_MONST },
+ { "paralyzation", 0, ACT_PARALYZE },
+ { "hallucination", 100, ACT_HALLU },
+ { "poisoning", 0, ACT_POISON },
+ { "hunger", 0, ACT_HUNGER },
+ { "stun", 0, ACT_STUN },
+ { "cuts", 0, ACT_CUTS },
+ { "paranoia", 0, ACT_PARANO },
+ { "confusion", 0, ACT_CONFUSION },
+ { "blindness", 0, ACT_BLIND },
+ { "pet summoning", 1010, ACT_PET_SUMMON },
+ { "cure paralyzation", 5000, ACT_CURE_PARA },
+ { "cure hallucination", 1000, ACT_CURE_HALLU },
+ { "cure poison", 1000, ACT_CURE_POIS },
+ { "cure hunger", 1000, ACT_CURE_HUNGER },
+ { "cure stun", 1000, ACT_CURE_STUN },
+ { "cure cut", 1000, ACT_CURE_CUTS },
+ { "cure fear", 1000, ACT_CURE_FEAR },
+ { "cure confusion", 1000, ACT_CURE_CONF },
+ { "cure blindness", 1000, ACT_CURE_BLIND },
+ { "cure light wounds", 500, ACT_CURE_LW },
+ { "cure serious wounds", 750, ACT_CURE_MW },
+ { "cure critical wounds", 1000, ACT_CURE_700 },
+ { "curing", 1100, ACT_CURING },
+ { "genocide", 5000, ACT_GENOCIDE },
+ { "mass genocide", 10000, ACT_MASS_GENO },
+ { "restoration", 2000, ACT_REST_ALL },
+ { "light", 1000, ACT_LIGHT },
+ { "darkness", 0, ACT_DARKNESS },
+ { "teleportation", 1000, ACT_TELEPORT },
+ { "level teleportation", 500, ACT_LEV_TELE },
+ { "acquirement", 30000, ACT_ACQUIREMENT },
+ { "something weird", 50, ACT_WEIRD },
+ { "aggravation", 0, ACT_AGGRAVATE },
+ { "corruption", 100, ACT_MUT },
+ { "cure insanity", 2000, ACT_CURE_INSANITY },
+ { "light absortion", 800, ACT_LIGHT_ABSORBTION },
+};
+
+/*
+ * Possible movement type.
+ */
+move_info_type move_info[9] =
+{
+ /* speed, searching, stealth, perception */
+ { -10, 17, 4, 20, "slug-like"},
+ { -8, 12, 4, 16, "very slow"},
+ { -6, 8, 3, 10, "slow"},
+ { -3, 4, 2, 6, "leisurely"},
+ { 0, 0, 0, 0, "normal"},
+ { 1, -4, -1, -4, "brisk"},
+ { 2, -6, -4, -8, "fast"},
+ { 3, -10, -7, -14, "very fast"},
+ { 4, -16, -10, -20, "running"}
+};
+
+/*
+ * Possible inscriptions type.
+ */
+inscription_info_type inscription_info[MAX_INSCRIPTIONS] =
+{
+ { /* Nothing */
+ "",
+ 0,
+ TRUE,
+ 0,
+ },
+ { /* Light up the room(Adunaic) */
+ "ure nimir", /* sun shine */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 30,
+ },
+ { /* Darkness in room(Adunaic) */
+ "lomi gimli", /* night stars */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 10,
+ },
+ { /* Storm(Adunaic) */
+ "dulgi bawiba", /* black winds */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 40,
+ },
+ { /* Protection(Sindarin) */
+ "pedo mellon a minno", /* say friend and enter */
+ INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 8,
+ },
+ { /* Dwarves summoning(Khuzdul) */
+ "Baruk Khazad! Khazad aimenu!", /* Axes of the Dwarves, the Dwarves are upon you! */
+ INSCRIP_EXEC_ENGRAVE,
+ FALSE,
+ 100,
+ },
+ { /* Open Chasm(Nandorin) */
+ "dunna hrassa", /* black precipice */
+ INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 50,
+ },
+ { /* Blast of Black Fire(Orcish) */
+ "burz ghash ronk", /* black fire pool */
+ INSCRIP_EXEC_ENGRAVE | INSCRIP_EXEC_WALK | INSCRIP_EXEC_MONST_WALK,
+ FALSE,
+ 60,
+ },
+};
+
+/*
+ * Inscriptions for pseudo-id
+ */
+cptr sense_desc[] =
+{
+ "whoops",
+ "cursed",
+ "average",
+ "good",
+ "good",
+ "excellent",
+ "worthless",
+ "terrible",
+ "special",
+ "broken",
+ ""
+};
+
+/*
+ * Flag groups used for art creation, level gaining weapons, ...
+ * -----
+ * Name,
+ * Price,
+ * Flags 1,
+ * Flags 2,
+ * Flags 3,
+ * Flags 4,
+ * ESP,
+ */
+flags_group flags_groups[MAX_FLAG_GROUP] =
+{
+ {
+ "Fire",
+ TERM_L_RED,
+ 1,
+ TR1_SLAY_UNDEAD | TR1_BRAND_FIRE,
+ TR2_RES_FIRE,
+ TR3_SH_FIRE | TR3_LITE1 | TR3_IGNORE_FIRE,
+ 0,
+ 0,
+ },
+ {
+ "Cold",
+ TERM_WHITE,
+ 1,
+ TR1_SLAY_DRAGON | TR1_SLAY_DEMON | TR1_BRAND_COLD,
+ TR2_RES_COLD | TR2_INVIS,
+ TR3_SLOW_DIGEST | TR3_IGNORE_COLD,
+ 0,
+ 0,
+ },
+ {
+ "Acid",
+ TERM_GREEN,
+ 3,
+ TR1_SLAY_ANIMAL | TR1_IMPACT | TR1_TUNNEL | TR1_BRAND_ACID,
+ TR2_RES_ACID,
+ TR3_IGNORE_ACID,
+ 0,
+ 0,
+ },
+ {
+ "Lightning",
+ TERM_L_BLUE,
+ 1,
+ TR1_SLAY_EVIL | TR1_BRAND_ELEC,
+ TR2_RES_ELEC,
+ TR3_IGNORE_ELEC | TR3_SH_ELEC | TR3_TELEPORT,
+ 0,
+ 0,
+ },
+ {
+ "Poison",
+ TERM_L_GREEN,
+ 2,
+ TR1_CHR | TR1_VAMPIRIC | TR1_SLAY_ANIMAL | TR1_BRAND_POIS,
+ TR2_SUST_CHR | TR2_RES_POIS,
+ TR3_DRAIN_EXP,
+ 0,
+ ESP_TROLL | ESP_GIANT,
+ },
+ {
+ "Air",
+ TERM_BLUE,
+ 5,
+ TR1_WIS | TR1_STEALTH | TR1_INFRA | TR1_SPEED,
+ TR2_RES_LITE | TR2_RES_DARK | TR2_RES_BLIND | TR2_SUST_WIS,
+ TR3_FEATHER | TR3_SEE_INVIS | TR3_BLESSED,
+ 0,
+ ESP_GOOD,
+ },
+ {
+ "Earth",
+ TERM_L_UMBER,
+ 5,
+ TR1_STR | TR1_CON | TR1_TUNNEL | TR1_BLOWS | TR1_SLAY_TROLL | TR1_SLAY_GIANT | TR1_IMPACT,
+ TR2_SUST_STR | TR2_SUST_CON | TR2_FREE_ACT | TR2_RES_FEAR | TR2_RES_SHARDS,
+ TR3_REGEN,
+ 0,
+ ESP_TROLL | ESP_GIANT,
+ },
+ {
+ "Mind",
+ TERM_YELLOW,
+ 7,
+ TR1_INT | TR1_SEARCH,
+ TR2_SUST_INT | TR2_RES_CONF | TR2_RES_FEAR,
+ 0,
+ 0,
+ ESP_ORC | ESP_TROLL | ESP_GIANT | ESP_ANIMAL | ESP_UNIQUE | ESP_SPIDER | ESP_DEMON,
+ },
+ {
+ "Shield",
+ TERM_RED,
+ 7,
+ TR1_DEX,
+ TR2_SUST_DEX | TR2_INVIS | TR2_REFLECT | TR2_HOLD_LIFE | TR2_RES_SOUND | TR2_RES_NEXUS,
+ TR3_REGEN,
+ 0,
+ 0,
+ },
+ {
+ "Chaos",
+ TERM_VIOLET,
+ 7,
+ TR1_CHAOTIC | TR1_IMPACT,
+ TR2_RES_CHAOS | TR2_RES_DISEN,
+ TR3_REGEN,
+ 0,
+ ESP_ALL,
+ },
+ {
+ "Magic",
+ TERM_L_BLUE,
+ 10,
+ TR1_MANA | TR1_SPELL,
+ TR2_RES_CHAOS | TR2_RES_DISEN,
+ TR3_WRAITH,
+ TR4_PRECOGNITION | TR4_FLY | TR4_CLONE,
+ 0,
+ },
+ {
+ "Antimagic",
+ TERM_L_DARK,
+ 10,
+ TR1_VAMPIRIC | TR1_CHAOTIC | TR1_BLOWS | TR1_SPEED,
+ TR2_LIFE | TR2_REFLECT | TR2_FREE_ACT | TR2_HOLD_LIFE,
+ TR3_NO_MAGIC | TR3_NO_TELE | TR3_SEE_INVIS,
+ TR4_ANTIMAGIC_50,
+ 0,
+ },
+};
+
+/* Powers */
+power_type powers_type[POWER_MAX] =
+{
+ {
+ "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,
+ },
+ {
+ "notused", /* Merchant abilities; no longer used, but want to
+ * avoid having to move all potential places where
+ * we're indexing into this table. */
+ "notused",
+ "notused",
+ "notused",
+ 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,
+ },
+ {
+ "invisibility",
+ "You are able melt into the shadows to become invisible.",
+ "You suddenly become able to melt into the shadows.",
+ "You lose your shadow-melting ability.",
+ 30, 10, A_DEX, 20,
+ },
+ {
+ "web",
+ "You are able throw a thick and very resistant spider web.",
+ "You suddenly become able to weave webs.",
+ "You lose your web-weaving capability.",
+ 25, 30, A_DEX, 20,
+ },
+ {
+ "control space/time continuum",
+ "You are able to control the space/time continuum.",
+ "You become able to control the space/time continuum.",
+ "You are no more able to control the space/time continuum.",
+ 1, 10, A_WIS, 10,
+ },
+};
+
+/*
+ * The Quests
+ */
+quest_type quest[MAX_Q_IDX] =
+{
+ {
+ FALSE,
+ "",
+ {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 0,
+
+ NULL,
+ NULL,
+ {0, 0},
+ NULL,
+ },
+ {
+ 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],
+ quest_necro_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ 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],
+ quest_sauron_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ 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],
+ quest_morgoth_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ /* Bree plot */
+ {
+ 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],
+ quest_thieves_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ FALSE,
+ "Random Quest",
+ {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ },
+ QUEST_STATUS_UNTAKEN,
+ 5,
+
+ NULL,
+ quest_random_init_hook,
+ {0, 0},
+ quest_random_describe,
+ },
+
+ {
+ 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],
+ quest_hobbit_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ 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],
+ quest_nazgul_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ 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],
+ quest_troll_init_hook,
+ {FALSE, 0},
+ NULL,
+ },
+
+ {
+ 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],
+ quest_wight_init_hook,
+ {FALSE, 0},
+ NULL,
+ },
+
+ /* Lorien plot */
+ {
+ 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],
+ quest_spider_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ 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],
+ quest_poison_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* Other quests */
+ {
+ 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],
+ quest_narsil_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* Gondolin plot */
+ {
+ 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],
+ quest_eol_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ 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],
+ quest_nirnaeth_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ 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],
+ quest_invasion_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* Minas Anor Plot*/
+ {
+ 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],
+ quest_between_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ 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],
+ quest_one_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ 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],
+ quest_shroom_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ {
+ 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],
+ quest_thrain_init_hook,
+ {0, 0},
+ NULL,
+ },
+
+ /* The 2 ultra endings go here */
+ {
+ 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],
+ quest_ultra_good_init_hook,
+ {0, 0},
+ NULL,
+ },
+ {
+ 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],
+ quest_ultra_evil_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* More Lorien */
+ {
+ 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],
+ quest_wolves_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* More Gondolin */
+ {
+ 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],
+ quest_dragons_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* More Minas Anor */
+ {
+ 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],
+ quest_haunted_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* Khazad-Dum Plot*/
+ {
+ 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],
+ quest_evil_init_hook,
+ {0, 0},
+ NULL,
+ },
+ /* Bounty */
+ {
+ FALSE,
+ "Bounty quest",
+ {
+ "", /* dynamic desc */
+ },
+ QUEST_STATUS_UNTAKEN,
+ -1,
+ NULL,
+ quest_bounty_init_hook,
+ {0, 0, 0, 0},
+ quest_bounty_describe,
+ },
+ /* Fireproofing */
+ {
+ FALSE,
+ "Old Mages quest",
+ {
+ "", /* dynamic desc */
+ },
+ QUEST_STATUS_UNTAKEN,
+ 20,
+ NULL,
+ quest_fireproof_init_hook,
+ {0, 0, 0, 0},
+ quest_fireproof_describe,
+ },
+ /* Library */
+ {
+ FALSE,
+ "Library quest",
+ {
+ "", /* dynamic desc */
+ },
+ QUEST_STATUS_UNTAKEN,
+ 35,
+ NULL,
+ quest_library_init_hook,
+ { -1, -1, -1, -1 },
+ quest_library_describe,
+ },
+ /* God quest */
+ {
+ FALSE,
+ "God quest",
+ {
+ "", /* dynamic desc */
+ },
+ QUEST_STATUS_UNTAKEN,
+ -1,
+ NULL,
+ quest_god_init_hook,
+ { 0 /* quests_given */,
+ 0 /* relics_found */,
+ 1 /* dun_mindepth */,
+ 4 /* dun_maxdepth */,
+ 0 /* dun_minplev */,
+ 0 /* relic_gen_tries */,
+ FALSE /* relic_generated */,
+ 1 /* dung_x */,
+ 1 /* dung_y */,
+ },
+ quest_god_describe,
+ },
+};
+
+
+/* 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_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."
+ },
+ {
+ 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."
+ },
+ {
+ 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."
+ },
+ {
+ 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
+ },
+ /* Theme: Minas Tirith -> Gondolin link */
+ {
+ 0,
+ FALSE,
+ 3, 11,
+ 119, 25,
+ 0, 0
+ },
+};
+
+/*
+ * 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 },
+};
+
+/**
+ * Modules
+ */
+module_type modules[MAX_MODULES] =
+{
+ {
+ { "ToME",
+ { 2, 4, 0 },
+ { "DarkGod", "darkgod@t-o-m-e.net" },
+ "The Tales of Middle-earth, the standard and official game.\n"
+ "You are set on a quest to investigate the old tower of Dol Guldur.\n"
+ "But who knows what will happen...",
+ "ToME",
+ NULL /* default dir */,
+ },
+ /* Randarts: */
+ { 30, 20, 20 },
+ /* Max player level: */
+ 50,
+ /* Skills: */
+ { 6, 4, },
+ /* Intro function */
+ tome_intro,
+ /* Race status function: ToME requires no special handling */
+ NULL
+ },
+
+ {
+ { "Theme",
+ { 1, 2, 0 },
+ { "furiosity", "furiosity@gmail.com" },
+ "A module that goes back to Tolkien roots, though by no means canonical.\n"
+ "A new wilderness map, new monsters, objects, artifacts, uniques, ego items,\n"
+ "terrain features, gods, races, subraces, and classes. Have fun. :-)",
+ "Theme",
+ "theme",
+ },
+ /* Randarts: */
+ { 30, 30, 30 },
+ /* Max player level: */
+ 50,
+ /* Skill overage: */
+ { 6, 5, },
+ /* Intro function */
+ theme_intro,
+ /* Race status function */
+ theme_race_status
+ }
+
+};
+
diff --git a/src/tables.h b/src/tables.h
new file mode 100644
index 00000000..9a5cfb58
--- /dev/null
+++ b/src/tables.h
@@ -0,0 +1,12 @@
+#pragma once
+
+// C linkage required for these functions since main-* code uses them.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char hexsym[16];
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/tables.hpp b/src/tables.hpp
new file mode 100644
index 00000000..4a3e33d6
--- /dev/null
+++ b/src/tables.hpp
@@ -0,0 +1,82 @@
+#pragma once
+
+#include "angband.h"
+#include "activation.hpp"
+#include "between_exit.hpp"
+#include "body.hpp"
+#include "cli_comm_fwd.hpp"
+#include "flags_group.hpp"
+#include "gf_name_type.hpp"
+#include "inscription_info_type.hpp"
+#include "magic_power.hpp"
+#include "martial_arts.hpp"
+#include "module_type.hpp"
+#include "monster_power.hpp"
+#include "move_info_type.hpp"
+#include "option_type.hpp"
+#include "player_defs.hpp"
+#include "player_sex.hpp"
+#include "power_type.hpp"
+#include "powers.hpp"
+#include "quest_type.hpp"
+#include "tactic_info_type.hpp"
+#include "tval_desc.hpp"
+
+extern s16b ddd[9];
+extern s16b ddx[10];
+extern s16b ddy[10];
+extern s16b ddx_ddd[9];
+extern s16b ddy_ddd[9];
+extern byte adj_mag_mana[];
+extern byte adj_mag_fail[];
+extern byte adj_mag_stat[];
+extern byte adj_chr_gold[];
+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 byte extract_energy[300];
+extern s32b player_exp[PY_MAX_LEVEL];
+extern player_sex sex_info[MAX_SEXES];
+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 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 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[POWER_MAX];
+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[MAX_Q_IDX];
+extern int max_body_part[BODY_MAX];
+extern gf_name_type gf_names[];
+extern module_type modules[MAX_MODULES];
diff --git a/src/tactic_info_type.hpp b/src/tactic_info_type.hpp
new file mode 100644
index 00000000..da94767d
--- /dev/null
+++ b/src/tactic_info_type.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Tactics descriptor.
+ */
+struct tactic_info_type
+{
+ s16b to_hit;
+ s16b to_dam;
+ s16b to_ac;
+ s16b to_stealth;
+ s16b to_disarm;
+ s16b to_saving;
+ cptr name;
+};
diff --git a/src/terrain.hpp b/src/terrain.hpp
new file mode 100644
index 00000000..118a877a
--- /dev/null
+++ b/src/terrain.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+/*
+ * 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
diff --git a/src/timer_type.hpp b/src/timer_type.hpp
new file mode 100644
index 00000000..0ce6b095
--- /dev/null
+++ b/src/timer_type.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "h-basic.h"
+
+/*
+ * Timer descriptor and runtime data.
+ */
+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 */
+
+ void (*callback)(); /* The C function to call upon firing */
+};
diff --git a/src/timer_type_fwd.hpp b/src/timer_type_fwd.hpp
new file mode 100644
index 00000000..bda03716
--- /dev/null
+++ b/src/timer_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct timer_type;
diff --git a/src/town_type.hpp b/src/town_type.hpp
new file mode 100644
index 00000000..f8458c60
--- /dev/null
+++ b/src/town_type.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "h-basic.h"
+#include "store_type_fwd.hpp"
+
+/**
+ * Town descriptor.
+ */
+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? */
+};
diff --git a/src/town_type_fwd.hpp b/src/town_type_fwd.hpp
new file mode 100644
index 00000000..9de8c448
--- /dev/null
+++ b/src/town_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct town_type;
diff --git a/src/trap_type.hpp b/src/trap_type.hpp
new file mode 100644
index 00000000..d82c925b
--- /dev/null
+++ b/src/trap_type.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Trap descriptor.
+ */
+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 */
+ const char *name; /* normal name like weakness */
+ s16b dd, ds; /* base damage */
+ char *text; /* longer description once you've met this trap */
+ byte g_attr; /* Overlay graphic attribute */
+ char g_char; /* Overlay graphic character */
+};
diff --git a/src/trap_type_fwd.hpp b/src/trap_type_fwd.hpp
new file mode 100644
index 00000000..480edfef
--- /dev/null
+++ b/src/trap_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct trap_type;
diff --git a/src/traps.cc b/src/traps.cc
new file mode 100644
index 00000000..99428cf9
--- /dev/null
+++ b/src/traps.cc
@@ -0,0 +1,3174 @@
+/* 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 "traps.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd1.hpp"
+#include "cmd2.hpp"
+#include "dungeon_info_type.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "gods.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_spec.hpp"
+#include "player_type.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+bool_ do_player_trap_call_out(void)
+{
+ s16b i, sn, cx, cy;
+ s16b h_index = 0;
+ s16b h_level = 0;
+ monster_type *m_ptr;
+ char m_name[80];
+ bool_ ident = FALSE;
+
+ for (i = 1; i < m_max; i++)
+ {
+ m_ptr = &m_list[i];
+
+ /* Paranoia -- Skip dead monsters */
+ if (!m_ptr->r_idx) continue;
+
+ if (m_ptr->level >= h_level)
+ {
+ h_level = m_ptr->level;
+ h_index = i;
+ }
+ }
+
+ /* if the level is empty of monsters, h_index will be 0 */
+ if (!h_index) return (FALSE);
+
+ m_ptr = &m_list[h_index];
+
+ sn = 0;
+ for (i = 0; i < 8; i++)
+ {
+ cx = p_ptr->px + ddx[i];
+ cy = p_ptr->py + ddy[i];
+
+ /* Skip non-empty grids */
+ if (!cave_valid_bold(cy, cx)) continue;
+ if (cave[cy][cx].feat == FEAT_GLYPH) continue;
+ if ((cx == p_ptr->px) && (cy == p_ptr->py)) continue;
+ sn++;
+
+ /* Randomize choice */
+ if (rand_int(sn) > 0) continue;
+ cave[cy][cx].m_idx = h_index;
+ cave[m_ptr->fy][m_ptr->fx].m_idx = 0;
+ m_ptr->fx = cx;
+ m_ptr->fy = cy;
+
+ /* we do not change the sublevel! */
+ ident = TRUE;
+ update_mon(h_index, TRUE);
+ monster_desc(m_name, m_ptr, 0x08);
+ msg_format("You hear a rapid-shifting wail, and %s appears!", m_name);
+ break;
+ }
+
+ return (ident);
+}
+
+static bool_ do_trap_teleport_away(object_type *i_ptr, s16b y, s16b x)
+{
+ bool_ ident = FALSE;
+ char o_name[80];
+
+ s16b o_idx = 0;
+ object_type *o_ptr;
+ cave_type *c_ptr;
+
+ s16b x1;
+ s16b y1;
+
+ if (i_ptr == NULL) return (FALSE);
+
+ if (i_ptr->name1 == ART_POWER) return (FALSE);
+
+ while (o_idx == 0)
+ {
+ x1 = rand_int(cur_wid);
+ y1 = rand_int(cur_hgt);
+
+ /* Obtain grid */
+ c_ptr = &cave[y1][x1];
+
+ /* Require floor space (or shallow terrain) -KMW- */
+ if (!(f_info[c_ptr->feat].flags1 & FF1_FLOOR)) continue;
+
+ o_idx = drop_near(i_ptr, 0, y1, x1);
+ }
+
+ o_ptr = &o_list[o_idx];
+
+ x1 = o_ptr->ix;
+ y1 = o_ptr->iy;
+
+ if (!p_ptr->blind)
+ {
+ note_spot(y, x);
+ lite_spot(y, x);
+ ident = TRUE;
+ object_desc(o_name, i_ptr, FALSE, 0);
+ if (player_has_los_bold(y1, x1))
+ {
+ lite_spot(y1, x1);
+ msg_format("The %s suddenly stands elsewhere.", o_name);
+
+ }
+ else
+ {
+ msg_format("You suddenly don't see the %s any more!", o_name);
+ }
+ }
+ else
+ {
+ msg_print("You hear something move.");
+ }
+ return (ident);
+}
+
+/*
+ * this handles a trap that places walls around the player
+ */
+static bool_ player_handle_trap_of_walls(void)
+{
+ bool_ ident;
+
+ s16b dx, dy, cx, cy;
+ s16b sx = 0, sy = 0, sn, i;
+ cave_type *cv_ptr;
+ bool_ map[5][5] =
+ {
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE},
+ {FALSE, FALSE, FALSE, FALSE, FALSE}
+ };
+
+ for (dy = -2; dy <= 2; dy++)
+ for (dx = -2; dx <= 2; dx++)
+ {
+ /* Extract the location */
+ cx = p_ptr->px + dx;
+ cy = p_ptr->py + dy;
+
+ if (!in_bounds(cy, cx)) continue;
+
+ cv_ptr = &cave[cy][cx];
+
+ if (cv_ptr->m_idx) continue;
+
+ /* Lose room and vault */
+ cv_ptr->info &= ~(CAVE_ROOM | CAVE_ICKY);
+ /* Lose light and knowledge */
+ cv_ptr->info &= ~(CAVE_GLOW | CAVE_MARK);
+
+ /* Skip the center */
+ if (!dx && !dy) continue;
+
+ /* test for dungeon level */
+ if (randint(100) > 10 + max_dlv[dungeon_type]) continue;
+
+ /* Damage this grid */
+ map[2 + dx][2 + dy] = TRUE;
+ }
+
+ for (dy = -2; dy <= 2; dy++)
+ for (dx = -2; dx <= 2; dx++)
+ {
+ /* Extract the location */
+ cx = p_ptr->px + dx;
+ cy = p_ptr->py + dy;
+
+ /* Skip unaffected grids */
+ if (!map[2 + dx][2 + dy]) continue;
+
+ cv_ptr = &cave[cy][cx];
+
+ if (cv_ptr->m_idx)
+ {
+ monster_type *m_ptr = &m_list[cv_ptr->m_idx];
+ auto const r_ptr = m_ptr->race();
+
+ /* 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_FRAME);
+
+ /* 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)
+{
+ trap_type *t_ptr = &t_info[trap];
+ s16b dam = damroll(t_ptr->dd, t_ptr->ds);
+ take_hit(dam, 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;
+ }
+
+ 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(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:
+ {
+ /* teleport away all items */
+ while (!cave[y][x].o_idxs.empty())
+ {
+ auto item = cave[y][x].o_idxs.front();
+
+ object_type *o_ptr = &o_list[item];
+
+ int 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(rand_int(dun_level) + 6);
+ }
+ ident = TRUE;
+ break;
+ }
+
+ /* Blindness/Confusion Trap */
+ case TRAP_OF_BLINDNESS_CONFUSION:
+ {
+ msg_print("A powerful magic protected this.");
+
+ if (!p_ptr->resist_blind)
+ {
+ ident |= set_blind(p_ptr->blind + rand_int(100) + 100);
+ }
+ if (!p_ptr->resist_conf)
+ {
+ ident |= set_confused(p_ptr->confused + rand_int(20) + 15);
+ }
+ break;
+ }
+
+ /* Aggravation Trap */
+ case TRAP_OF_AGGRAVATION:
+ {
+ msg_print("You hear a hollow noise echoing through the dungeons.");
+ aggravate_monsters(1);
+ break;
+ }
+
+ /* Multiplication Trap */
+ case TRAP_OF_MULTIPLICATION:
+ {
+ msg_print("You hear a loud click.");
+ for (k = -1; k <= 1; k++)
+ for (l = -1; l <= 1; l++)
+ {
+ if ((in_bounds(p_ptr->py + l, p_ptr->px + k)) &&
+ (!cave[p_ptr->py + l][p_ptr->px + k].t_idx))
+ {
+ place_trap(p_ptr->py + l, p_ptr->px + k);
+ }
+ }
+ ident = TRUE;
+ break;
+ }
+
+ /* Steal Item Trap */
+ case TRAP_OF_STEAL_ITEM:
+ {
+ /*
+ * please note that magical stealing is not so
+ * easily circumvented
+ */
+ if (!p_ptr->paralyzed &&
+ (rand_int(160) < (adj_dex_safe[p_ptr->stat_ind[A_DEX]] +
+ p_ptr->lev)))
+ {
+ /* Saving throw message */
+ msg_print("Your backpack seems to vibrate strangely!");
+ break;
+ }
+
+ /* Find an item */
+ for (k = 0; k < rand_int(10); k++)
+ {
+ char i_name[80];
+ object_type *j_ptr, *q_ptr, forge;
+
+ /* Pick an item */
+ s16b i = rand_int(INVEN_PACK);
+
+ /* Obtain the item */
+ j_ptr = &p_ptr->inventory[i];
+
+ /* Accept real items */
+ if (!j_ptr->k_idx) continue;
+
+ /* Don't steal artifacts -CFT */
+ if (artifact_p(j_ptr)) continue;
+
+ /* Get a description */
+ object_desc(i_name, j_ptr, FALSE, 3);
+
+ /* Message */
+ msg_format("%sour %s (%c) was stolen!",
+ ((j_ptr->number > 1) ? "One of y" : "Y"),
+ i_name, index_to_label(i));
+
+ /* Create the item */
+ q_ptr = &forge;
+ object_copy(q_ptr, j_ptr);
+ q_ptr->number = 1;
+
+ /* Drop it somewhere */
+ do_trap_teleport_away(q_ptr, y, x);
+
+ inc_stack_size_ex(i, -1, OPTIMIZE, NO_DESCRIBE);
+
+ ident = TRUE;
+ }
+ break;
+ }
+
+ /* Summon Fast Quylthulgs Trap */
+ case TRAP_OF_SUMMON_FAST_QUYLTHULGS:
+ {
+ for (k = 0; k < randint(3); k++)
+ {
+ ident |= summon_specific(y, x, max_dlv[dungeon_type], SUMMON_QUYLTHULG);
+ }
+
+ if (ident)
+ {
+ msg_print("You suddenly have company.");
+ (void)set_slow(p_ptr->slow + randint(25) + 15);
+ }
+ break;
+ }
+
+ /* Trap of Sinking */
+ case TRAP_OF_SINKING:
+ {
+ msg_print("You fell through a trap door!");
+
+ if (p_ptr->ffall)
+ {
+ if (dungeon_flags1 & DF1_TOWER)
+ {
+ msg_print("You float gently down to the previous level.");
+ }
+ else
+ {
+ msg_print("You float gently down to the next level.");
+ }
+ }
+ else
+ {
+ take_hit(damroll(2, 8), "a trap door");
+ }
+
+ /* Still alive and autosave enabled */
+ if (p_ptr->chp >= 0)
+ {
+ autosave_checkpoint();
+ }
+
+ if (dungeon_flags1 & DF1_TOWER) dun_level--;
+ else dun_level++;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+ break;
+ }
+
+ /* Trap of Mana Drain */
+ case TRAP_OF_MANA_DRAIN:
+ {
+ if (p_ptr->csp > 0)
+ {
+ p_ptr->csp = 0;
+ p_ptr->csp_frac = 0;
+ p_ptr->redraw |= (PR_FRAME);
+ 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);
+ }
+ 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_FRAME);
+ break;
+ }
+
+ /* Trap of No Return */
+ case TRAP_OF_NO_RETURN:
+ {
+ object_type *j_ptr;
+ s16b j;
+
+ for (j = 0; j < INVEN_WIELD; j++)
+ {
+ if (!p_ptr->inventory[j].k_idx) continue;
+
+ j_ptr = &p_ptr->inventory[j];
+
+ if ((j_ptr->tval == TV_SCROLL) &&
+ (j_ptr->sval == SV_SCROLL_WORD_OF_RECALL))
+ {
+ inc_stack_size_ex(j, -j_ptr->number, OPTIMIZE, NO_DESCRIBE);
+
+ combine_pack();
+ reorder_pack();
+
+ if (!ident)
+ {
+ msg_print("A small fire works its way through your backpack. "
+ "Some scrolls are burnt.");
+ }
+ else
+ {
+ msg_print("The fire hasn't finished.");
+ }
+ ident = TRUE;
+ }
+ else if ((j_ptr->tval == TV_ROD_MAIN) &&
+ (j_ptr->pval == SV_ROD_RECALL))
+ {
+ j_ptr->timeout = 0; /* a long time */
+ if (!ident) msg_print("You feel the air stabilise around you.");
+ ident = TRUE;
+ }
+ }
+ if ((!ident) && (p_ptr->word_recall))
+ {
+ msg_print("You feel like staying around.");
+ p_ptr->word_recall = 0;
+ ident = TRUE;
+ }
+ break;
+ }
+
+ /* Trap of Silent Switching */
+ case TRAP_OF_SILENT_SWITCHING:
+ {
+ s16b i, j, slot1, slot2;
+ object_type *j_ptr, *k_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ j_ptr = &p_ptr->inventory[i];
+
+ if (!j_ptr->k_idx) continue;
+
+ /* Do not allow this trap to touch the One Ring */
+ object_flags(j_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if(f3 & TR3_PERMA_CURSE) continue;
+
+ slot1 = wield_slot(j_ptr);
+
+ for (j = 0; j < INVEN_WIELD; j++)
+ {
+ k_ptr = &p_ptr->inventory[j];
+
+ if (!k_ptr->k_idx) continue;
+
+ /* Do not allow this trap to touch the One Ring */
+ object_flags(k_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+ if(f3 & TR3_PERMA_CURSE) continue;
+
+ /* this is a crude hack, but it prevent wielding 6 torches... */
+ if (k_ptr->number > 1) continue;
+
+ slot2 = wield_slot(k_ptr);
+
+ /* a chance of 4 in 5 of switching something, then 2 in 5 to do it again */
+ if ((slot1 == slot2) &&
+ (rand_int(100) < (80 - (ident * 40))))
+ {
+ object_type tmp_obj;
+
+ if (p_ptr->inventory[j].name1)
+ wield_set(p_ptr->inventory[j].name1, a_info[p_ptr->inventory[j].name1].set, FALSE);
+ if (p_ptr->inventory[i].name1)
+ takeoff_set(p_ptr->inventory[i].name1, a_info[p_ptr->inventory[i].name1].set);
+
+ tmp_obj = p_ptr->inventory[j];
+ p_ptr->inventory[j] = p_ptr->inventory[i];
+ p_ptr->inventory[i] = tmp_obj;
+ ident = TRUE;
+ }
+ }
+ }
+
+ if (ident)
+ {
+ p_ptr->update |= (PU_BONUS);
+ p_ptr->update |= (PU_TORCH);
+ p_ptr->update |= (PU_MANA);
+ msg_print("You somehow feel like another person.");
+ }
+ else
+ {
+ msg_print("You feel a lack of useful items.");
+ }
+ break;
+ }
+
+ /* Trap of Walls */
+ case TRAP_OF_WALLS:
+ {
+ ident = player_handle_trap_of_walls();
+ break;
+ }
+
+ /* Trap of Calling Out */
+ case TRAP_OF_CALLING_OUT:
+ {
+ ident = do_player_trap_call_out();
+
+ if (!ident)
+ {
+ /* Increase "afraid" */
+ if (p_ptr->resist_fear)
+ {
+ msg_print("You feel as if you had a nightmare!");
+ }
+ else if (rand_int(100) < p_ptr->skill_sav)
+ {
+ msg_print("You remember having a nightmare!");
+ }
+ else
+ {
+ if (set_afraid(p_ptr->afraid + 3 + randint(40)))
+ {
+ msg_print("You have a vision of a powerful enemy.");
+ }
+ }
+ }
+ break;
+ }
+
+ /* Trap of Sliding */
+ case TRAP_OF_SLIDING:
+ break;
+
+ /* Trap of Charges Drain */
+ case TRAP_OF_CHARGES_DRAIN:
+ {
+ /* Find an item */
+ for (k = 0; k < 10; k++)
+ {
+ s16b i = rand_int(INVEN_PACK);
+
+ object_type *j_ptr = &p_ptr->inventory[i];
+
+ /* Drain charged wands/staffs
+ Hack -- don't let artifacts get drained */
+ if (((j_ptr->tval == TV_STAFF) ||
+ (j_ptr->tval == TV_WAND)) &&
+ (j_ptr->pval) &&
+ !artifact_p(j_ptr))
+ {
+ ident = TRUE;
+ j_ptr->pval = j_ptr->pval / (randint(4) + 1);
+
+ /* 60% chance of only 1 */
+ if (randint(10) > 3) break;
+ }
+ }
+
+ if (ident)
+ {
+ /* Window stuff */
+ p_ptr->window |= PW_INVEN;
+ /* Combine / Reorder the pack */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ msg_print("Your backpack seems to be turned upside down.");
+ }
+ else
+ {
+ msg_print("You hear a wail of great disappointment.");
+ }
+ break;
+ }
+
+ /* Trap of Stair Movement */
+ case TRAP_OF_STAIR_MOVEMENT:
+ {
+ s16b cx, cy, i, j;
+ s16b cnt = 0;
+ s16b cnt_seen = 0;
+ s16b tmps, tmpx;
+ s16b tmpspecial, tmpspecial2;
+ u32b tmpf;
+ bool_ seen = FALSE;
+ s16b index_x[20], index_y[20]; /* 20 stairs per level is enough? */
+ cave_type *cv_ptr;
+
+ if (max_dlv[dungeon_type] == 99)
+ {
+ /* no sense in relocating that stair! */
+ msg_print("You have a feeling that this trap could be dangerous.");
+ break;
+ }
+
+ for (cx = 0; cx < cur_wid; cx++)
+ for (cy = 0; cy < cur_hgt; cy++)
+ {
+ cv_ptr = &cave[cy][cx];
+
+ if ((cv_ptr->feat != FEAT_LESS) &&
+ (cv_ptr->feat != FEAT_MORE) &&
+ (cv_ptr->feat != FEAT_SHAFT_UP) &&
+ (cv_ptr->feat != FEAT_SHAFT_DOWN)) continue;
+
+ index_x[cnt] = cx;
+ index_y[cnt] = cy;
+ cnt++;
+ }
+
+ if (cnt == 0)
+ {
+ if (wizard) msg_print("Executing moving stairs trap on level with no stairs!");
+ break;
+ }
+
+ for (i = 0; i < cnt; i++)
+ {
+ seen = FALSE;
+
+ for (j = 0; j < 10; j++) /* try 10 times to relocate */
+ {
+ cave_type *cv_ptr = &cave[index_y[i]][index_x[i]];
+ cave_type *cv_ptr2;
+
+ cx = rand_int(cur_wid);
+ cy = rand_int(cur_hgt);
+
+ if ((cx == index_x[i]) || (cy == index_y[i])) continue;
+
+ cv_ptr2 = &cave[cy][cx];
+
+ if (!cave_valid_bold(cy, cx) || (!cv_ptr2->o_idxs.empty())) continue;
+
+ /* don't put anything in vaults */
+ if (cv_ptr2->info & CAVE_ICKY) continue;
+
+ tmpx = cv_ptr2->mimic;
+ tmps = cv_ptr2->info;
+ tmpf = cv_ptr2->feat;
+ tmpspecial = cv_ptr2->special;
+ tmpspecial2 = cv_ptr2->special2;
+ cave[cy][cx].mimic = cv_ptr->mimic;
+ cave[cy][cx].info = cv_ptr->info;
+ cave[cy][cx].special = cv_ptr->special;
+ cave[cy][cx].special2 = cv_ptr->special2;
+ cave_set_feat(cy, cx, cv_ptr->feat);
+ cv_ptr->mimic = tmpx;
+ cv_ptr->info = tmps;
+ cv_ptr->special = tmpspecial;
+ cv_ptr->special2 = tmpspecial2;
+ cave_set_feat(index_y[i], index_x[i], tmpf);
+
+ /* if we are placing walls in rooms, make them rubble instead */
+ if ((cv_ptr->info & CAVE_ROOM) &&
+ (cv_ptr->feat >= FEAT_WALL_EXTRA) &&
+ (cv_ptr->feat <= FEAT_PERM_SOLID))
+ {
+ cave_set_feat(index_y[i], index_x[i], FEAT_RUBBLE);
+ }
+
+ if (player_has_los_bold(cy, cx))
+ {
+ note_spot(cy, cx);
+ lite_spot(cy, cx);
+ seen = TRUE;
+ }
+ else
+ {
+ cv_ptr2->info &= ~CAVE_MARK;
+ }
+
+ if (player_has_los_bold(index_y[i], index_x[i]))
+ {
+ note_spot(index_y[i], index_x[i]);
+ lite_spot(index_y[i], index_x[i]);
+ seen = TRUE;
+ }
+ else
+ {
+ cv_ptr->info &= ~CAVE_MARK;
+ }
+ break;
+ }
+
+ if (seen) cnt_seen++;
+ }
+
+ ident = (cnt_seen > 0);
+
+ if ((ident) && (cnt_seen > 1))
+ {
+ msg_print("You see some stairs move.");
+ }
+ else if (ident)
+ {
+ msg_print("You see a stair move.");
+ }
+ else
+ {
+ msg_print("You hear distant scraping noises.");
+ }
+ p_ptr->redraw |= PR_MAP;
+ break;
+ }
+
+ /* Trap of New Trap */
+ case TRAP_OF_NEW:
+ {
+ /* if we're on a floor or on a door, place a new trap */
+ if ((item == -1) || (item == -2))
+ {
+ place_trap(y, x);
+ if (player_has_los_bold(y, x))
+ {
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+ }
+ else
+ {
+ /* re-trap the chest */
+ place_trap(y, x);
+ }
+ msg_print("You hear a noise, and then its echo.");
+ ident = FALSE;
+ break;
+ }
+
+ /* Trap of Acquirement */
+ case TRAP_OF_ACQUIREMENT:
+ {
+ /* Get a nice thing */
+ msg_print("You notice something falling off the trap.");
+ acquirement(y, x, 1, TRUE, FALSE);
+
+ /* If we're on a floor or on a door, place a new trap */
+ if ((item == -1) || (item == -2))
+ {
+ place_trap(y, x);
+ if (player_has_los_bold(y, x))
+ {
+ note_spot(y, x);
+ lite_spot(y, x);
+ }
+ }
+ else
+ {
+ /* Re-trap the chest */
+ place_trap(y, x);
+ }
+ msg_print("You hear a noise, and then its echo.");
+
+ /* Never known */
+ ident = FALSE;
+ }
+ break;
+
+ /* Trap of Scatter Items */
+ case TRAP_OF_SCATTER_ITEMS:
+ {
+ s16b i, j;
+ bool_ message = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+
+ if (!p_ptr->inventory[i].k_idx) continue;
+
+ if (rand_int(10) < 3) continue;
+
+ for (j = 0; j < 10; j++)
+ {
+ object_type tmp_obj, *j_ptr = &tmp_obj;
+ s16b cx = x + 15 - rand_int(30);
+ s16b cy = y + 15 - rand_int(30);
+
+ if (!in_bounds(cy, cx)) continue;
+
+ if (!cave_floor_bold(cy, cx)) continue;
+
+ object_copy(j_ptr, &p_ptr->inventory[i]);
+
+ inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE);
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ (void)floor_carry(cy, cx, j_ptr);
+
+ if (!message)
+ {
+ msg_print("You feel light-footed.");
+ message = TRUE;
+ }
+
+ if (player_has_los_bold(cy, cx))
+ {
+ char i_name[80];
+
+ object_desc(i_name, &tmp_obj, TRUE, 3);
+ note_spot(cy, cx);
+ lite_spot(cy, cx);
+ ident = TRUE;
+ msg_format("Suddenly %s appear%s!", i_name,
+ (j_ptr->number > 1) ? "" : "s");
+ }
+ break;
+ }
+ }
+ ident = message;
+ break;
+ }
+
+ /* Trap of Decay */
+ case TRAP_OF_DECAY:
+ break;
+
+ /* Trap of Wasting Wands */
+ case TRAP_OF_WASTING_WANDS:
+ {
+ s16b i;
+ object_type *j_ptr;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ if (!p_ptr->inventory[i].k_idx) continue;
+
+ j_ptr = &p_ptr->inventory[i];
+
+ if ((j_ptr->tval == TV_WAND) && (rand_int(5) == 1))
+ {
+ if (object_known_p(j_ptr)) ident = TRUE;
+
+ /* Create a Wand of Nothing */
+ object_prep(j_ptr, lookup_kind(TV_WAND, SV_WAND_NOTHING));
+ apply_magic(j_ptr, 0, FALSE, FALSE, FALSE, boost::make_optional(0));
+ 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));
+ apply_magic(j_ptr, 0, FALSE, FALSE, FALSE, boost::make_optional(0));
+ j_ptr->ident &= ~IDENT_KNOWN;
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+ }
+ }
+ if (ident)
+ {
+ msg_print("You have lost trust in your backpack!");
+ }
+ else
+ {
+ msg_print("You hear an echoing cry of rage.");
+ }
+ break;
+ }
+
+ /* Trap of Filling */
+ case TRAP_OF_FILLING:
+ {
+ s16b nx, ny;
+
+ for (nx = x - 8; nx <= x + 8; nx++)
+ for (ny = y - 8; ny <= y + 8; ny++)
+ {
+ if (!in_bounds (ny, nx)) continue;
+
+ if (rand_int(distance(ny, nx, y, x)) > 3)
+ {
+ place_trap(ny, nx);
+ }
+ }
+
+ msg_print("The floor vibrates in a strange way.");
+ ident = FALSE;
+ break;
+ }
+
+ case TRAP_OF_DRAIN_SPEED:
+ {
+ object_type *j_ptr;
+ s16b j, chance = 75;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ for (j = 0; j < INVEN_TOTAL; j++)
+ {
+ /* don't bother the overflow slot */
+ if (j == INVEN_PACK) continue;
+
+ if (!p_ptr->inventory[j].k_idx) continue;
+
+ j_ptr = &p_ptr->inventory[j];
+ object_flags(j_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* is it a non-artifact speed item? */
+ if ((!j_ptr->name1) && (f1 & TR1_SPEED))
+ {
+ if (randint(100) < chance)
+ {
+ j_ptr->pval = j_ptr->pval / 2;
+ if (j_ptr->pval == 0)
+ {
+ j_ptr->pval--;
+ }
+ chance /= 2;
+ ident = TRUE;
+ }
+ inven_item_optimize(j);
+ }
+ }
+ if (!ident)
+ {
+ msg_print("You feel some things in your pack vibrating.");
+ }
+ else
+ {
+ combine_pack();
+ reorder_pack();
+ msg_print("You suddenly feel you have time for self-reflection.");
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Recalculate mana */
+ p_ptr->update |= (PU_MANA);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+ break;
+ }
+
+ /*
+ * single missile traps
+ */
+ case TRAP_OF_ARROW_I:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_NORMAL, 4, 8, 0, "Arrow Trap");
+ break;
+ case TRAP_OF_ARROW_II:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_NORMAL, 5, 8, 0, "Bolt Trap");
+ break;
+ case TRAP_OF_ARROW_III:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_HEAVY, 6, 8, 0, "Seeker Arrow Trap");
+ break;
+ case TRAP_OF_ARROW_IV:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_HEAVY, 8, 10, 0, "Seeker Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_I:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_NORMAL, 4, 8, 10 + randint(20), "Poison Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_II:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_NORMAL, 5, 8, 15 + randint(30), "Poison Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_III:
+ ident = player_handle_missile_trap(1, TV_ARROW, SV_AMMO_HEAVY, 6, 8, 30 + randint(50), "Poison Seeker Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROW_IV:
+ ident = player_handle_missile_trap(1, TV_BOLT, SV_AMMO_HEAVY, 8, 10, 40 + randint(70), "Poison Seeker Bolt Trap");
+ break;
+ case TRAP_OF_DAGGER_I:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_DAGGER_II:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_DAGGER, 3, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGER_I:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 15 + randint(20), "Poison Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGER_II:
+ ident = player_handle_missile_trap(1, TV_SWORD, SV_DAGGER, 3, 8, 20 + randint(30), "Poison Dagger Trap");
+ break;
+
+ /*
+ * multiple missile traps
+ * numbers range from 2 (level 0 to 14) to 10 (level 120 and up)
+ */
+ case TRAP_OF_ARROWS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_NORMAL, 4, 8, 0, "Arrow Trap");
+ break;
+ case TRAP_OF_ARROWS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_NORMAL, 5, 8, 0, "Bolt Trap");
+ break;
+ case TRAP_OF_ARROWS_III:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_HEAVY, 6, 8, 0, "Seeker Arrow Trap");
+ break;
+ case TRAP_OF_ARROWS_IV:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_HEAVY, 8, 10, 0, "Seeker Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_NORMAL, 4, 8, 10 + randint(20), "Poison Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_NORMAL, 5, 8, 15 + randint(30), "Poison Bolt Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_III:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_ARROW, SV_AMMO_HEAVY, 6, 8, 30 + randint(50), "Poison Seeker Arrow Trap");
+ break;
+ case TRAP_OF_POISON_ARROWS_IV:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_BOLT, SV_AMMO_HEAVY, 8, 10, 40 + randint(70), "Poison Seeker Bolt Trap");
+ break;
+ case TRAP_OF_DAGGERS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_DAGGERS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_DAGGER, 3, 8, 0, "Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGERS_I:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_BROKEN_DAGGER, 2, 8, 15 + randint(20), "Poison Dagger Trap");
+ break;
+ case TRAP_OF_POISON_DAGGERS_II:
+ ident = player_handle_missile_trap(2 + (max_dlv[dungeon_type] / 15), TV_SWORD, SV_DAGGER, 3, 8, 20 + randint(30), "Poison Dagger Trap");
+ break;
+
+ case TRAP_OF_DROP_ITEMS:
+ {
+ s16b i;
+ bool_ message = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type tmp_obj;
+
+ if (!p_ptr->inventory[i].k_idx) continue;
+ if (randint(100) < 80) continue;
+ if (p_ptr->inventory[i].name1 == ART_POWER) continue;
+
+ tmp_obj = p_ptr->inventory[i];
+
+ /* drop carefully */
+ drop_near(&tmp_obj, 0, y, x);
+
+ inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE);
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ if (!message)
+ {
+ msg_print("You are startled by a sudden sound.");
+ message = TRUE;
+ }
+ ident = TRUE;
+ }
+ if (!ident)
+ {
+ msg_print("You hear a sudden, strange sound.");
+ }
+ break;
+ }
+
+ case TRAP_OF_DROP_ALL_ITEMS:
+ {
+ s16b i;
+ bool_ message = FALSE;
+
+ for (i = 0; i < INVEN_PACK; i++)
+ {
+ object_type tmp_obj;
+
+ if (!p_ptr->inventory[i].k_idx) continue;
+ if (randint(100) < 10) continue;
+ if (p_ptr->inventory[i].name1 == ART_POWER) continue;
+
+ tmp_obj = p_ptr->inventory[i];
+
+ /* drop carefully */
+ drop_near(&tmp_obj, 0, y, x);
+
+ inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE);
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ if (!message)
+ {
+ msg_print("You are greatly startled by a sudden sound.");
+ message = TRUE;
+ }
+ ident = TRUE;
+ }
+ if (!ident)
+ {
+ msg_print("You hear a sudden, strange sound.");
+ }
+ break;
+ }
+
+ case TRAP_OF_DROP_EVERYTHING:
+ {
+ s16b i;
+ bool_ message = FALSE;
+
+ for (i = 0; i < INVEN_TOTAL; i++)
+ {
+ object_type tmp_obj;
+ if (!p_ptr->inventory[i].k_idx) continue;
+ if (randint(100) < 30) continue;
+ if (p_ptr->inventory[i].name1 == ART_POWER) continue;
+
+ tmp_obj = p_ptr->inventory[i];
+ /* drop carefully */
+
+ drop_near(&tmp_obj, 0, y, x);
+
+ inc_stack_size_ex(i, -999, OPTIMIZE, NO_DESCRIBE);
+
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ if (!message)
+ {
+ msg_print("You are completely startled by a sudden sound.");
+ message = TRUE;
+ }
+ ident = TRUE;
+ }
+ if (!ident)
+ {
+ msg_print("You hear a sudden, strange sound.");
+ }
+ break;
+ }
+
+ /* Bolt Trap */
+ case TRAP_G_ELEC_BOLT:
+ ident = player_handle_breath_trap(1, GF_ELEC, TRAP_G_ELEC_BOLT);
+ break;
+ case TRAP_G_POIS_BOLT:
+ ident = player_handle_breath_trap(1, GF_POIS, TRAP_G_POIS_BOLT);
+ break;
+ case TRAP_G_ACID_BOLT:
+ ident = player_handle_breath_trap(1, GF_ACID, TRAP_G_ACID_BOLT);
+ break;
+ case TRAP_G_COLD_BOLT:
+ ident = player_handle_breath_trap(1, GF_COLD, TRAP_G_COLD_BOLT);
+ break;
+ case TRAP_G_FIRE_BOLT:
+ ident = player_handle_breath_trap(1, GF_FIRE, TRAP_G_FIRE_BOLT);
+ break;
+ case TRAP_OF_ELEC_BOLT:
+ ident = player_handle_breath_trap(1, GF_ELEC, TRAP_OF_ELEC_BOLT);
+ break;
+ case TRAP_OF_POIS_BOLT:
+ ident = player_handle_breath_trap(1, GF_POIS, TRAP_OF_POIS_BOLT);
+ break;
+ case TRAP_OF_ACID_BOLT:
+ ident = player_handle_breath_trap(1, GF_ACID, TRAP_OF_ACID_BOLT);
+ break;
+ case TRAP_OF_COLD_BOLT:
+ ident = player_handle_breath_trap(1, GF_COLD, TRAP_OF_COLD_BOLT);
+ break;
+ case TRAP_OF_FIRE_BOLT:
+ ident = player_handle_breath_trap(1, GF_FIRE, TRAP_OF_FIRE_BOLT);
+ break;
+ case TRAP_OF_PLASMA_BOLT:
+ ident = player_handle_breath_trap(1, GF_PLASMA, TRAP_OF_PLASMA_BOLT);
+ break;
+ case TRAP_OF_WATER_BOLT:
+ ident = player_handle_breath_trap(1, GF_WATER, TRAP_OF_WATER_BOLT);
+ break;
+ case TRAP_OF_LITE_BOLT:
+ ident = player_handle_breath_trap(1, GF_LITE, TRAP_OF_LITE_BOLT);
+ break;
+ case TRAP_OF_DARK_BOLT:
+ ident = player_handle_breath_trap(1, GF_DARK, TRAP_OF_DARK_BOLT);
+ break;
+ case TRAP_OF_SHARDS_BOLT:
+ ident = player_handle_breath_trap(1, GF_SHARDS, TRAP_OF_SHARDS_BOLT);
+ break;
+ case TRAP_OF_SOUND_BOLT:
+ ident = player_handle_breath_trap(1, GF_SOUND, TRAP_OF_SOUND_BOLT);
+ break;
+ case TRAP_OF_CONFUSION_BOLT:
+ ident = player_handle_breath_trap(1, GF_CONFUSION, TRAP_OF_CONFUSION_BOLT);
+ break;
+ case TRAP_OF_FORCE_BOLT:
+ ident = player_handle_breath_trap(1, GF_FORCE, TRAP_OF_FORCE_BOLT);
+ break;
+ case TRAP_OF_INERTIA_BOLT:
+ ident = player_handle_breath_trap(1, GF_INERTIA, TRAP_OF_INERTIA_BOLT);
+ break;
+ case TRAP_OF_MANA_BOLT:
+ ident = player_handle_breath_trap(1, GF_MANA, TRAP_OF_MANA_BOLT);
+ break;
+ case TRAP_OF_ICE_BOLT:
+ ident = player_handle_breath_trap(1, GF_ICE, TRAP_OF_ICE_BOLT);
+ break;
+ case TRAP_OF_CHAOS_BOLT:
+ ident = player_handle_breath_trap(1, GF_CHAOS, TRAP_OF_CHAOS_BOLT);
+ break;
+ case TRAP_OF_NETHER_BOLT:
+ ident = player_handle_breath_trap(1, GF_NETHER, TRAP_OF_NETHER_BOLT);
+ break;
+ case TRAP_OF_DISENCHANT_BOLT:
+ ident = player_handle_breath_trap(1, GF_DISENCHANT, TRAP_OF_DISENCHANT_BOLT);
+ break;
+ case TRAP_OF_NEXUS_BOLT:
+ ident = player_handle_breath_trap(1, GF_NEXUS, TRAP_OF_NEXUS_BOLT);
+ break;
+ case TRAP_OF_TIME_BOLT:
+ ident = player_handle_breath_trap(1, GF_TIME, TRAP_OF_TIME_BOLT);
+ break;
+ case TRAP_OF_GRAVITY_BOLT:
+ ident = player_handle_breath_trap(1, GF_GRAVITY, TRAP_OF_GRAVITY_BOLT);
+ break;
+
+ /* Ball Trap */
+ case TRAP_OF_ELEC_BALL:
+ ident = player_handle_breath_trap(3, GF_ELEC, TRAP_OF_ELEC_BALL);
+ break;
+ case TRAP_OF_POIS_BALL:
+ ident = player_handle_breath_trap(3, GF_POIS, TRAP_OF_POIS_BALL);
+ break;
+ case TRAP_OF_ACID_BALL:
+ ident = player_handle_breath_trap(3, GF_ACID, TRAP_OF_ACID_BALL);
+ break;
+ case TRAP_OF_COLD_BALL:
+ ident = player_handle_breath_trap(3, GF_COLD, TRAP_OF_COLD_BALL);
+ break;
+ case TRAP_OF_FIRE_BALL:
+ ident = player_handle_breath_trap(3, GF_FIRE, TRAP_OF_FIRE_BALL);
+ break;
+ case TRAP_OF_PLASMA_BALL:
+ ident = player_handle_breath_trap(3, GF_PLASMA, TRAP_OF_PLASMA_BALL);
+ break;
+ case TRAP_OF_WATER_BALL:
+ ident = player_handle_breath_trap(3, GF_WATER, TRAP_OF_WATER_BALL);
+ break;
+ case TRAP_OF_LITE_BALL:
+ ident = player_handle_breath_trap(3, GF_LITE, TRAP_OF_LITE_BALL);
+ break;
+ case TRAP_OF_DARK_BALL:
+ ident = player_handle_breath_trap(3, GF_DARK, TRAP_OF_DARK_BALL);
+ break;
+ case TRAP_OF_SHARDS_BALL:
+ ident = player_handle_breath_trap(3, GF_SHARDS, TRAP_OF_SHARDS_BALL);
+ break;
+ case TRAP_OF_SOUND_BALL:
+ ident = player_handle_breath_trap(3, GF_SOUND, TRAP_OF_SOUND_BALL);
+ break;
+ case TRAP_OF_CONFUSION_BALL:
+ ident = player_handle_breath_trap(3, GF_CONFUSION, TRAP_OF_CONFUSION_BALL);
+ break;
+ case TRAP_OF_FORCE_BALL:
+ ident = player_handle_breath_trap(3, GF_FORCE, TRAP_OF_FORCE_BALL);
+ break;
+ case TRAP_OF_INERTIA_BALL:
+ ident = player_handle_breath_trap(3, GF_INERTIA, TRAP_OF_INERTIA_BALL);
+ break;
+ case TRAP_OF_MANA_BALL:
+ ident = player_handle_breath_trap(3, GF_MANA, TRAP_OF_MANA_BALL);
+ break;
+ case TRAP_OF_ICE_BALL:
+ ident = player_handle_breath_trap(3, GF_ICE, TRAP_OF_ICE_BALL);
+ break;
+ case TRAP_OF_CHAOS_BALL:
+ ident = player_handle_breath_trap(3, GF_CHAOS, TRAP_OF_CHAOS_BALL);
+ break;
+ case TRAP_OF_NETHER_BALL:
+ ident = player_handle_breath_trap(3, GF_NETHER, TRAP_OF_NETHER_BALL);
+ break;
+ case TRAP_OF_DISENCHANT_BALL:
+ ident = player_handle_breath_trap(3, GF_DISENCHANT, TRAP_OF_DISENCHANT_BALL);
+ break;
+ case TRAP_OF_NEXUS_BALL:
+ ident = player_handle_breath_trap(3, GF_NEXUS, TRAP_OF_NEXUS_BALL);
+ break;
+ case TRAP_OF_TIME_BALL:
+ ident = player_handle_breath_trap(3, GF_TIME, TRAP_OF_TIME_BALL);
+ break;
+ case TRAP_OF_GRAVITY_BALL:
+ ident = player_handle_breath_trap(3, GF_GRAVITY, TRAP_OF_GRAVITY_BALL);
+ break;
+
+ /* -SC- */
+ case TRAP_OF_FEMINITY:
+ {
+ msg_print("Gas sprouts out... you feel yourself transmute.");
+ p_ptr->psex = SEX_FEMALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_MASCULINITY:
+ {
+ msg_print("Gas sprouts out... you feel yourself transmute.");
+ p_ptr->psex = SEX_MALE;
+ sp_ptr = &sex_info[p_ptr->psex];
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_NEUTRALITY:
+ {
+ msg_print("Gas sprouts out... you feel yourself transmute.");
+ p_ptr->psex = SEX_NEUTER;
+ sp_ptr = &sex_info[p_ptr->psex];
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_AGING:
+ {
+ msg_print("Colors are scintillating around you. "
+ "You see your past running before your eyes.");
+ p_ptr->age += randint((rp_ptr->b_age + rmp_ptr->b_age) / 2);
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_GROWING:
+ {
+ s16b tmp;
+
+ msg_print("Heavy fumes sprout out... you feel yourself transmute.");
+ if (p_ptr->psex == SEX_FEMALE) tmp = rp_ptr->f_b_ht + rmp_ptr->f_b_ht;
+ else tmp = rp_ptr->m_b_ht + rmp_ptr->m_b_ht;
+
+ p_ptr->ht += randint(tmp / 4);
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ case TRAP_OF_SHRINKING:
+ {
+ s16b tmp;
+
+ msg_print("Heavy fumes sprout out... you feel yourself transmute.");
+ if (p_ptr->psex == SEX_FEMALE) tmp = rp_ptr->f_b_ht + rmp_ptr->f_b_ht;
+ else tmp = rp_ptr->m_b_ht + rmp_ptr->m_b_ht;
+
+ p_ptr->ht -= randint(tmp / 4);
+ if (p_ptr->ht <= tmp / 4) p_ptr->ht = tmp / 4;
+ ident = TRUE;
+ trap_hit(trap);
+ break;
+ }
+
+ /* Trap of Divine Anger */
+ case TRAP_OF_DIVINE_ANGER:
+ {
+ if (p_ptr->pgod == 0)
+ {
+ msg_format("Suddenly you feel glad you're a mere %s", spp_ptr->title);
+ }
+ 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);
+ }
+ 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);
+
+ /* 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_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;
+}
+
+
+/*
+ * Place a leveled trap at given position
+ */
+void place_trap_leveled(int y, int x, int lev)
+{
+ int prev_dun_level = dun_level;
+ dun_level = lev;
+ place_trap(y,x);
+ dun_level = prev_dun_level;
+}
+
+/*
+ * 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 const *o_ptr)
+{
+ return (((o_ptr->tval == TV_ROD_MAIN) && (o_ptr->pval != 0)) ||
+ (o_ptr->tval == TV_STAFF) ||
+ (o_ptr->tval == TV_WAND));
+}
+
+/*
+ * 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;
+ }
+
+ /* Get an item */
+ q = "Use which trapping kit? ";
+ s = "You have no trapping kits.";
+ if (!get_item(&item_kit,
+ q, s,
+ USE_INVEN,
+ object_filter::TVal(TV_TRAPKIT)))
+ {
+ return;
+ }
+
+ o_ptr = &p_ptr->inventory[item_kit];
+
+ /* Trap kits need a second object */
+ object_filter_t object_filter = object_filter::Or(
+ object_filter::And(
+ object_filter::SVal(SV_TRAPKIT_BOW),
+ object_filter::TVal(TV_ARROW)),
+ object_filter::And(
+ object_filter::SVal(SV_TRAPKIT_XBOW),
+ object_filter::TVal(TV_BOLT)),
+ object_filter::And(
+ object_filter::SVal(SV_TRAPKIT_SLING),
+ object_filter::TVal(TV_SHOT)),
+ object_filter::And(
+ object_filter::SVal(SV_TRAPKIT_POTION),
+ object_filter::Or(
+ object_filter::TVal(TV_POTION),
+ object_filter::TVal(TV_POTION2))),
+ object_filter::And(
+ object_filter::SVal(SV_TRAPKIT_SCROLL),
+ object_filter::TVal(TV_SCROLL)),
+ object_filter::And(
+ object_filter::SVal(SV_TRAPKIT_DEVICE),
+ &item_tester_hook_device));
+
+ /* 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, object_filter)) return;
+
+ /* Get the second object */
+ j_ptr = &p_ptr->inventory[item_load];
+
+ /* Assume a single object */
+ num = 1;
+
+ /* In some cases, take multiple objects to load */
+ if (o_ptr->sval != SV_TRAPKIT_DEVICE)
+ {
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ if ((f3 & TR3_XTRA_SHOTS) && (o_ptr->pval > 0)) num += o_ptr->pval;
+
+ if (f2 & (TRAP2_AUTOMATIC_5 | TRAP2_AUTOMATIC_99)) num = 99;
+
+ if (num > j_ptr->number) num = j_ptr->number;
+
+ c = format("How many (1-%d)? ", num);
+
+ /* Ask for number of items to use */
+ num = get_quantity(c, num);
+ }
+
+ /* Canceled */
+ if (!num) return;
+
+ /* Take a turn */
+ energy_use = 100;
+
+ /* Get local object */
+ i_ptr = &object_type_body;
+
+ /* Obtain local object for trap content */
+ object_copy(i_ptr, j_ptr);
+
+ /* Set number */
+ i_ptr->number = num;
+
+ /* Drop it here */
+ cave[p_ptr->py][p_ptr->px].special = floor_carry(p_ptr->py, p_ptr->px, i_ptr);
+
+ /* Obtain local object for trap trigger kit */
+ object_copy(i_ptr, o_ptr);
+
+ /* Set number */
+ i_ptr->number = 1;
+
+ /* Drop it here */
+ cave[p_ptr->py][p_ptr->px].special2 = floor_carry(p_ptr->py, p_ptr->px, i_ptr);
+
+ /* Modify, Describe, Optimize */
+ inc_stack_size_ex(item_kit, -1, NO_OPTIMIZE, DESCRIBE);
+ inc_stack_size_ex(item_load, -num, NO_OPTIMIZE, DESCRIBE);
+
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ if (inven_item_optimize(i)) break;
+ }
+ for (i = 0; i < INVEN_WIELD; i++)
+ {
+ inven_item_optimize(i);
+ }
+
+ /* Actually set the trap */
+ cave_set_feat(p_ptr->py, p_ptr->px, FEAT_MON_TRAP);
+}
+
+/*
+ * Monster hitting a rod trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_rod(int m_idx, object_type *o_ptr)
+{
+ int dam = 0, typ = 0;
+ int rad = 0;
+ monster_type *m_ptr = &m_list[m_idx];
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+
+ /* Depend on rod type */
+ switch (o_ptr->pval)
+ {
+ case SV_ROD_DETECT_TRAP:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ break;
+ case SV_ROD_DETECTION:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ break;
+ case SV_ROD_ILLUMINATION:
+ typ = GF_LITE_WEAK;
+ dam = damroll(2, 15);
+ rad = 3;
+ lite_room(y, x);
+ break;
+ case SV_ROD_CURING:
+ typ = GF_OLD_HEAL;
+ dam = damroll(3, 4); /* and heal conf? */
+ break;
+ case SV_ROD_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = 300;
+ break;
+ case SV_ROD_SPEED:
+ typ = GF_OLD_SPEED;
+ dam = 50;
+ break;
+ case SV_ROD_TELEPORT_AWAY:
+ typ = GF_AWAY_ALL;
+ dam = MAX_SIGHT * 5;
+ break;
+ case SV_ROD_DISARMING:
+ break;
+ case SV_ROD_LITE:
+ typ = GF_LITE_WEAK;
+ dam = damroll(6, 8);
+ break;
+ case SV_ROD_SLEEP_MONSTER:
+ typ = GF_OLD_SLEEP;
+ dam = 50;
+ break;
+ case SV_ROD_SLOW_MONSTER:
+ typ = GF_OLD_SLOW;
+ dam = 50;
+ break;
+ case SV_ROD_DRAIN_LIFE:
+ typ = GF_OLD_DRAIN;
+ dam = 75;
+ break;
+ case SV_ROD_POLYMORPH:
+ typ = GF_OLD_POLY;
+ dam = 50;
+ break;
+ case SV_ROD_ACID_BOLT:
+ typ = GF_ACID;
+ dam = damroll(6, 8);
+ break;
+ case SV_ROD_ELEC_BOLT:
+ typ = GF_ELEC;
+ dam = damroll(3, 8);
+ break;
+ case SV_ROD_FIRE_BOLT:
+ typ = GF_FIRE;
+ dam = damroll(8, 8);
+ break;
+ case SV_ROD_COLD_BOLT:
+ typ = GF_COLD;
+ dam = damroll(5, 8);
+ break;
+ case SV_ROD_ACID_BALL:
+ typ = GF_ACID;
+ dam = 60;
+ rad = 2;
+ break;
+ case SV_ROD_ELEC_BALL:
+ typ = GF_ELEC;
+ dam = 32;
+ rad = 2;
+ break;
+ case SV_ROD_FIRE_BALL:
+ typ = GF_FIRE;
+ dam = 72;
+ rad = 2;
+ break;
+ case SV_ROD_COLD_BALL:
+ typ = GF_COLD;
+ dam = 48;
+ rad = 2;
+ break;
+ default:
+ return (FALSE);
+ }
+
+ /* Actually hit the monster */
+ if (typ) (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a staff trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_staff(int m_idx, object_type *o_ptr)
+{
+ return (FALSE);
+}
+
+/*
+ * Monster hitting a scroll trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_scroll(int m_idx, int sval)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int dam = 0, typ = 0;
+ int rad = 0;
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+ int k;
+
+ /* Depend on scroll type */
+ switch (sval)
+ {
+ case SV_SCROLL_CURSE_ARMOR:
+ case SV_SCROLL_CURSE_WEAPON:
+ case SV_SCROLL_TRAP_CREATION: /* these don't work :-( */
+ case SV_SCROLL_WORD_OF_RECALL: /* should these? */
+ case SV_SCROLL_IDENTIFY:
+ case SV_SCROLL_STAR_IDENTIFY:
+ case SV_SCROLL_MAPPING:
+ case SV_SCROLL_DETECT_GOLD:
+ case SV_SCROLL_DETECT_ITEM:
+ case SV_SCROLL_REMOVE_CURSE:
+ case SV_SCROLL_STAR_REMOVE_CURSE:
+ case SV_SCROLL_ENCHANT_ARMOR:
+ case SV_SCROLL_ENCHANT_WEAPON_TO_HIT:
+ case SV_SCROLL_ENCHANT_WEAPON_TO_DAM:
+ case SV_SCROLL_STAR_ENCHANT_ARMOR:
+ case SV_SCROLL_STAR_ENCHANT_WEAPON:
+ case SV_SCROLL_RECHARGING:
+ case SV_SCROLL_DETECT_DOOR:
+ case SV_SCROLL_DETECT_INVIS:
+ case SV_SCROLL_SATISFY_HUNGER:
+ case SV_SCROLL_RUNE_OF_PROTECTION:
+ case SV_SCROLL_TRAP_DOOR_DESTRUCTION:
+ case SV_SCROLL_PROTECTION_FROM_EVIL:
+ return (FALSE);
+ case SV_SCROLL_DARKNESS:
+ unlite_room(y, x);
+ typ = GF_DARK_WEAK;
+ dam = 10;
+ rad = 3;
+ break;
+ case SV_SCROLL_AGGRAVATE_MONSTER:
+ aggravate_monsters(m_idx);
+ return (FALSE);
+ case SV_SCROLL_SUMMON_MONSTER:
+ for (k = 0; k < randint(3) ; k++) summon_specific(y, x, dun_level, 0);
+ return (FALSE);
+ case SV_SCROLL_SUMMON_UNDEAD:
+ for (k = 0; k < randint(3) ; k++) summon_specific(y, x, dun_level, SUMMON_UNDEAD);
+ return (FALSE);
+ case SV_SCROLL_PHASE_DOOR:
+ typ = GF_AWAY_ALL;
+ dam = 10;
+ break;
+ case SV_SCROLL_TELEPORT:
+ typ = GF_AWAY_ALL;
+ dam = 100;
+ break;
+ case SV_SCROLL_TELEPORT_LEVEL:
+ delete_monster(y, x);
+ return (TRUE);
+ case SV_SCROLL_LIGHT:
+ lite_room(y, x);
+ typ = GF_LITE_WEAK;
+ dam = damroll(2, 8);
+ rad = 2;
+ break;
+ case SV_SCROLL_DETECT_TRAP:
+ m_ptr->smart |= SM_NOTE_TRAP;
+ return (FALSE);
+ case SV_SCROLL_BLESSING:
+ typ = GF_HOLY_FIRE;
+ dam = damroll(1, 4);
+ break;
+ case SV_SCROLL_HOLY_CHANT:
+ typ = GF_HOLY_FIRE;
+ dam = damroll(2, 4);
+ break;
+ case SV_SCROLL_HOLY_PRAYER:
+ typ = GF_HOLY_FIRE;
+ dam = damroll(4, 4);
+ break;
+ case SV_SCROLL_MONSTER_CONFUSION:
+ typ = GF_OLD_CONF;
+ dam = damroll(5, 10);
+ break;
+ case SV_SCROLL_STAR_DESTRUCTION:
+ destroy_area(y, x, 15);
+ return (FALSE);
+ case SV_SCROLL_DISPEL_UNDEAD:
+ typ = GF_DISP_UNDEAD;
+ rad = 5;
+ dam = 60;
+ break;
+ case SV_SCROLL_GENOCIDE:
+ {
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ genocide_aux(FALSE, r_ptr->d_char);
+ /* although there's no point in a multiple genocide trap... */
+ return (!(r_ptr->flags1 & RF1_UNIQUE));
+ }
+ case SV_SCROLL_MASS_GENOCIDE:
+ for (k = 0; k < 8; k++)
+ delete_monster(y + ddy[k], x + ddx[k]);
+ delete_monster(y, x);
+ return (TRUE);
+ case SV_SCROLL_ACQUIREMENT:
+ acquirement(y, x, 1, TRUE, FALSE);
+ return (FALSE);
+ case SV_SCROLL_STAR_ACQUIREMENT:
+ acquirement(y, x, randint(2) + 1, TRUE, FALSE);
+ return (FALSE);
+ default:
+ return (FALSE);
+ }
+
+ /* Actually hit the monster */
+ (void) project( -2, rad, y, x, dam, typ, PROJECT_KILL | PROJECT_ITEM | PROJECT_JUMP);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a wand trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_wand(int m_idx, object_type *o_ptr)
+{
+ return (FALSE);
+}
+
+/*
+ * Monster hitting a potions trap -MWK-
+ *
+ * Return TRUE if the monster died
+ */
+bool_ mon_hit_trap_aux_potion(int m_idx, object_type *o_ptr)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ int dam = 0, typ = 0;
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+ int sval = o_ptr->sval;
+
+ /* Depend on potion type */
+ if (o_ptr->tval == TV_POTION)
+ {
+ switch (sval)
+ {
+ /* Nothing happens */
+ case SV_POTION_WATER:
+ case SV_POTION_APPLE_JUICE:
+ case SV_POTION_SLIME_MOLD:
+ case SV_POTION_SALT_WATER:
+ case SV_POTION_DEC_STR:
+ case SV_POTION_DEC_INT:
+ case SV_POTION_DEC_WIS:
+ case SV_POTION_DEC_DEX:
+ case SV_POTION_DEC_CON:
+ case SV_POTION_DEC_CHR:
+ case SV_POTION_INFRAVISION:
+ case SV_POTION_DETECT_INVIS:
+ case SV_POTION_SLOW_POISON:
+ case SV_POTION_CURE_POISON:
+ case SV_POTION_RESIST_HEAT:
+ case SV_POTION_RESIST_COLD:
+ case SV_POTION_RESTORE_MANA:
+ case SV_POTION_RESTORE_EXP:
+ case SV_POTION_RES_STR:
+ case SV_POTION_RES_INT:
+ case SV_POTION_RES_WIS:
+ case SV_POTION_RES_DEX:
+ case SV_POTION_RES_CON:
+ case SV_POTION_RES_CHR:
+ case SV_POTION_INC_STR:
+ case SV_POTION_INC_INT:
+ case SV_POTION_INC_WIS:
+ case SV_POTION_INC_DEX:
+ case SV_POTION_INC_CON:
+ case SV_POTION_INC_CHR:
+ case SV_POTION_AUGMENTATION:
+ case SV_POTION_RUINATION: /* ??? */
+ case SV_POTION_ENLIGHTENMENT:
+ case SV_POTION_STAR_ENLIGHTENMENT:
+ case SV_POTION_SELF_KNOWLEDGE:
+ return (FALSE);
+
+ case SV_POTION_EXPERIENCE:
+ if (m_ptr->level < MONSTER_LEVEL_MAX)
+ {
+ m_ptr->exp = monster_exp(m_ptr->level + 1);
+ monster_check_experience(m_idx, FALSE);
+ }
+ return (FALSE);
+ case SV_POTION_SLOWNESS:
+ typ = GF_OLD_SLOW;
+ dam = damroll(4, 6);
+ break;
+ case SV_POTION_POISON:
+ typ = GF_POIS;
+ dam = damroll(8, 6);
+ break;
+ case SV_POTION_CONFUSION:
+ typ = GF_CONFUSION;
+ dam = damroll(4, 6);
+ break;
+ case SV_POTION_BLINDNESS:
+ typ = GF_DARK;
+ dam = 10;
+ break;
+ case SV_POTION_SLEEP:
+ typ = GF_OLD_SLEEP;
+ dam = damroll (4, 6);
+ break;
+ case SV_POTION_LOSE_MEMORIES:
+ typ = GF_OLD_CONF;
+ dam = damroll(10, 10);
+ break;
+ case SV_POTION_DETONATIONS:
+ typ = GF_DISINTEGRATE;
+ dam = damroll(20, 20);
+ break;
+ case SV_POTION_DEATH:
+ typ = GF_NETHER;
+ dam = damroll(100, 20);
+ break;
+ case SV_POTION_BOLDNESS:
+ m_ptr->monfear = 0;
+ return (FALSE);
+ case SV_POTION_SPEED:
+ typ = GF_OLD_SPEED;
+ dam = damroll(5, 10);
+ break;
+ case SV_POTION_HEROISM:
+ case SV_POTION_BESERK_STRENGTH:
+ m_ptr->monfear = 0;
+ typ = GF_OLD_HEAL;
+ dam = damroll(2, 10);
+ break;
+ case SV_POTION_CURE_LIGHT:
+ typ = GF_OLD_HEAL;
+ dam = damroll(3, 4);
+ break;
+ case SV_POTION_CURE_SERIOUS:
+ typ = GF_OLD_HEAL;
+ dam = damroll(4, 6);
+ break;
+ case SV_POTION_CURE_CRITICAL:
+ typ = GF_OLD_HEAL;
+ dam = damroll(6, 8);
+ break;
+ case SV_POTION_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = 300;
+ break;
+ case SV_POTION_STAR_HEALING:
+ typ = GF_OLD_HEAL;
+ dam = 1000;
+ break;
+ case SV_POTION_LIFE:
+ {
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+ if (r_ptr->flags3 & RF3_UNDEAD)
+ {
+ typ = GF_HOLY_FIRE;
+ dam = damroll(20, 20);
+ }
+ else
+ {
+ typ = GF_OLD_HEAL;
+ dam = 5000;
+ }
+ break;
+ }
+ default:
+ return (FALSE);
+
+ }
+ }
+ else
+ {}
+
+ /* Actually hit the monster */
+ (void) project_m( -2, 0, y, x, dam, typ);
+ return (cave[y][x].m_idx == 0 ? TRUE : FALSE);
+}
+
+/*
+ * Monster hitting a monster trap -MWK-
+ * Returns True if the monster died, false otherwise
+ */
+bool_ mon_hit_trap(int m_idx)
+{
+ monster_type *m_ptr = &m_list[m_idx];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ 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 */
+ auto kit_o_idx = cave[my][mx].special2;
+ auto kit_o_ptr = &o_list[kit_o_idx];
+ auto load_o_ptr = &o_list[cave[my][mx].special];
+ auto j_ptr = &object_type_body;
+
+ /* Get trap properties */
+ object_flags(kit_o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Can set off check */
+ /* Ghosts only set off Ghost traps */
+ if ((r_ptr->flags2 & RF2_PASS_WALL) && !(f2 & TRAP2_KILL_GHOST)) return (FALSE);
+
+ /* Some traps are specialized to some creatures */
+ if (f2 & TRAP2_ONLY_MASK)
+ {
+ bool_ affect = FALSE;
+ if ((f2 & TRAP2_ONLY_DRAGON) && (r_ptr->flags3 & RF3_DRAGON)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_DEMON) && (r_ptr->flags3 & RF3_DEMON)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_UNDEAD) && (r_ptr->flags3 & RF3_UNDEAD)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_EVIL) && (r_ptr->flags3 & RF3_EVIL)) affect = TRUE;
+ if ((f2 & TRAP2_ONLY_ANIMAL) && (r_ptr->flags3 & RF3_ANIMAL)) affect = TRUE;
+
+ /* Don't set it off if forbidden */
+ if (!affect) return (FALSE);
+ }
+
+ /* Get detection difficulty */
+ difficulty = 25;
+
+ /* Some traps are well-hidden */
+ if (f1 & TR1_STEALTH)
+ {
+ difficulty += 10 * (kit_o_ptr->pval);
+ }
+
+ /* Get monster smartness for trap detection */
+ /* Higher level monsters are smarter */
+ smartness = r_ptr->level;
+
+ /* Smart monsters are better at detecting traps */
+ if (r_ptr->flags2 & RF2_SMART) smartness += 10;
+
+ /* Some monsters have already noticed one of out traps */
+ if (m_ptr->smart & SM_NOTE_TRAP) smartness += 20;
+
+ /* Stupid monsters are no good at detecting traps */
+ if (r_ptr->flags2 & (RF2_STUPID | RF2_EMPTY_MIND)) smartness = -150;
+
+ /* Check if the monster notices the trap */
+ if (randint(300) > (difficulty - smartness + 150)) notice = TRUE;
+
+ /* Disarm check */
+ if (notice)
+ {
+ /* The next traps will be easier to spot! */
+ m_ptr->smart |= SM_NOTE_TRAP;
+
+ /* Get trap disarming difficulty */
+ difficulty = (kit_o_ptr->ac + kit_o_ptr->to_a);
+
+ /* Get monster disarming ability */
+ /* Higher level monsters are better */
+ smartness = r_ptr->level / 5;
+
+ /* Smart monsters are better at disarming */
+ if (r_ptr->flags2 & RF2_SMART) smartness *= 2;
+
+ /* Stupid monsters never disarm traps */
+ if (r_ptr->flags2 & RF2_STUPID) smartness = -150;
+
+ /* Nonsmart animals never disarm traps */
+ if ((r_ptr->flags3 & RF3_ANIMAL) && !(r_ptr->flags2 & RF2_SMART)) smartness = -150;
+
+ /* Check if the monster disarms the trap */
+ if (randint(120) > (difficulty - smartness + 80)) disarm = TRUE;
+ }
+
+ /* If disarmed, remove the trap and print a message */
+ if (disarm)
+ {
+ remove = TRUE;
+
+ if (m_ptr->ml)
+ {
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s disarms a trap!", m_name);
+ }
+ }
+
+ /* Otherwise, activate the trap! */
+ else
+ {
+ /* Message for visible monster */
+ if (m_ptr->ml)
+ {
+ /* Get the name */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s sets off a trap!", m_name);
+ }
+ else
+ {
+ /* No message if monster isn't visible ? */
+ }
+
+ /* Next time be more careful */
+ if (randint(100) < 80) m_ptr->smart |= SM_NOTE_TRAP;
+
+ /* Actually activate the trap */
+ switch (kit_o_ptr->sval)
+ {
+ case SV_TRAPKIT_BOW:
+ case SV_TRAPKIT_XBOW:
+ case SV_TRAPKIT_SLING:
+ {
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+ if (shots > load_o_ptr->number) shots = load_o_ptr->number;
+
+ while (shots-- && !dead)
+ {
+ /* Total base damage */
+ dam = damroll(load_o_ptr->dd, load_o_ptr->ds) + load_o_ptr->to_d + kit_o_ptr->to_d;
+
+ /* Total hit probability */
+ chance = (kit_o_ptr->to_h + load_o_ptr->to_h + 20) * BTH_PLUS_ADJ;
+
+ /* Damage multiplier */
+ if (kit_o_ptr->sval == SV_TRAPKIT_BOW) mul = 3;
+ if (kit_o_ptr->sval == SV_TRAPKIT_XBOW) mul = 4;
+ if (kit_o_ptr->sval == SV_TRAPKIT_SLING) mul = 2;
+ if (f3 & TR3_XTRA_MIGHT) mul += kit_o_ptr->pval;
+ if (mul < 0) mul = 0;
+
+ /* Multiply damage */
+ dam *= mul;
+
+ /* Check if we hit the monster */
+ if (test_hit_fire(chance, r_ptr->ac, TRUE))
+ {
+ /* Assume a default death */
+ cptr note_dies = " dies.";
+
+ /* Some monsters get "destroyed" */
+ if ((r_ptr->flags3 & (RF3_DEMON)) ||
+ (r_ptr->flags3 & (RF3_UNDEAD)) ||
+ (r_ptr->flags2 & (RF2_STUPID)) ||
+ (strchr("Evg", r_ptr->d_char)))
+ {
+ /* Special note at death */
+ note_dies = " is destroyed.";
+ }
+
+ /* Message if visible */
+ if (m_ptr->ml)
+ {
+ /* describe the monster (again, just in case :-) */
+ monster_desc(m_name, m_ptr, 0);
+
+ /* Print a message */
+ msg_format("%^s is hit by a missile.", m_name);
+ }
+
+ /* Apply slays, brand, critical hits */
+ dam = tot_dam_aux(load_o_ptr, dam, m_ptr, &special);
+ dam = critical_shot(load_o_ptr->weight, load_o_ptr->to_h, dam, SKILL_ARCHERY);
+
+ /* No negative damage */
+ if (dam < 0) dam = 0;
+
+ /* Hit the monster, check for death */
+ if (mon_take_hit(m_idx, dam, &fear, note_dies))
+ {
+ /* Dead monster */
+ dead = TRUE;
+ }
+
+ /* No death */
+ else
+ {
+ /* Message */
+ message_pain(m_idx, dam);
+
+ if (special) attack_special(m_ptr, special, dam);
+
+ /* Take note */
+ if (fear && m_ptr->ml)
+ {
+ /* Message */
+ msg_format("%^s flees in terror!", m_name);
+ }
+ }
+
+ }
+
+ /* Exploding ammo */
+ if (load_o_ptr->pval2 != 0)
+ {
+ int rad = 0;
+ int dam = (damroll(load_o_ptr->dd, load_o_ptr->ds) + load_o_ptr->to_d)*2;
+ int flag = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL |
+ PROJECT_JUMP;
+
+ switch (load_o_ptr->sval)
+ {
+ case SV_AMMO_LIGHT:
+ rad = 2;
+ dam /= 2;
+ break;
+ case SV_AMMO_NORMAL:
+ rad = 3;
+ break;
+ case SV_AMMO_HEAVY:
+ rad = 4;
+ dam *= 2;
+ break;
+ }
+
+ project(0, rad, my, mx, dam, load_o_ptr->pval2, flag);
+
+ breakage = 100;
+ }
+ else
+ {
+ breakage = breakage_chance(load_o_ptr);
+ }
+
+ /* Copy and decrease ammo */
+ object_copy(j_ptr, load_o_ptr);
+
+ j_ptr->number = 1;
+
+ load_o_ptr->number--;
+
+ if (load_o_ptr->number <= 0)
+ {
+ remove = TRUE;
+ delete_object_idx(kit_o_idx);
+ }
+
+ /* 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_idx);
+ }
+ }
+
+ 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_idx);
+ }
+ }
+
+ break;
+ }
+
+ case SV_TRAPKIT_DEVICE:
+ {
+ if (load_o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* Extract mana cost of the rod tip */
+ u32b tf1, tf2, tf3, tf4, tf5, tesp;
+ object_kind *tip_o_ptr = &k_info[lookup_kind(TV_ROD, load_o_ptr->pval)];
+ object_flags(load_o_ptr, &tf1, &tf2, &tf3, &tf4, &tf5, &tesp);
+ cost = (tf4 & TR4_CHEAPNESS) ? tip_o_ptr->pval / 2 : tip_o_ptr->pval;
+ if (cost <= 0) cost = 1;
+ }
+
+ /* Get number of shots */
+ shots = 1;
+ if (f3 & TR3_XTRA_SHOTS) shots += kit_o_ptr->pval;
+ if (shots <= 0) shots = 1;
+
+ if (load_o_ptr->tval == TV_ROD_MAIN)
+ {
+ if (shots > load_o_ptr->timeout / cost) shots = load_o_ptr->timeout / cost;
+ }
+ else
+ {
+ if (shots > load_o_ptr->pval) shots = load_o_ptr->pval;
+ }
+
+ while (shots-- && !dead)
+ {
+ /* Get the effect effect */
+ switch (load_o_ptr->tval)
+ {
+ case TV_ROD_MAIN:
+ dead = mon_hit_trap_aux_rod(m_idx, load_o_ptr);
+ break;
+ case TV_WAND:
+ dead = mon_hit_trap_aux_wand(m_idx, load_o_ptr);
+ break;
+ case TV_STAFF:
+ dead = mon_hit_trap_aux_staff(m_idx, load_o_ptr);
+ break;
+ }
+
+ if (load_o_ptr->tval == TV_ROD_MAIN)
+ {
+ /* decrease stored mana (timeout) for rods */
+ load_o_ptr->timeout -= cost;
+ }
+ else
+ {
+ /* decrease charges for wands and staves */
+ load_o_ptr->pval--;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ msg_print("oops! nonexistant trap!");
+
+ }
+
+ /* Non-automatic traps are removed */
+ if (!(f2 & (TRAP2_AUTOMATIC_5 | TRAP2_AUTOMATIC_99)))
+ {
+ remove = TRUE;
+ }
+ else if (f2 & TRAP2_AUTOMATIC_5) remove = (randint(5) == 1);
+
+ }
+
+ /* Special trap effect -- teleport to */
+ if ((f2 & TRAP2_TELEPORT_TO) && (!disarm) && (!dead))
+ {
+ teleport_monster_to(m_idx, p_ptr->py, p_ptr->px);
+ }
+
+ /* Remove the trap if inactive now */
+ if (remove) place_floor_convert_glass(my, mx);
+
+ /* did it die? */
+ return (dead);
+}
+
diff --git a/src/traps.hpp b/src/traps.hpp
new file mode 100644
index 00000000..3df1e430
--- /dev/null
+++ b/src/traps.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "h-basic.h"
+#include "object_type_fwd.hpp"
+
+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_leveled(int y, int x, int lev);
+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);
diff --git a/src/tval_desc.hpp b/src/tval_desc.hpp
new file mode 100644
index 00000000..abf0b5e2
--- /dev/null
+++ b/src/tval_desc.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+/**
+ * TVal description entry.
+ */
+struct tval_desc
+{
+ /**
+ * TVal
+ */
+ int tval;
+
+ /**
+ * Description
+ */
+ char const *desc;
+};
diff --git a/src/util.cc b/src/util.cc
new file mode 100644
index 00000000..08af0658
--- /dev/null
+++ b/src/util.cc
@@ -0,0 +1,3685 @@
+/* File: util.c */
+
+/* Purpose: Angband utilities -BEN- */
+
+#include "util.hpp"
+#include "util.h"
+
+#include "cli_comm.hpp"
+#include "cmd3.hpp"
+#include "cmd4.hpp"
+#include "init1.hpp"
+#include "messages.hpp"
+#include "monster_ego.hpp"
+#include "monster_race.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "tables.h"
+#include "tables.hpp"
+#include "timer_type.hpp"
+#include "variable.h"
+#include "variable.hpp"
+#include "xtra1.hpp"
+
+#include <boost/algorithm/string/predicate.hpp>
+#include <chrono>
+#include <thread>
+
+using boost::algorithm::iequals;
+using std::this_thread::sleep_for;
+using std::chrono::milliseconds;
+
+/*
+* Find a default user name from the system.
+*/
+void user_name(char *buf, int id)
+{
+#ifdef SET_UID
+ struct passwd *pw;
+
+ /* Look up the user name */
+ if ((pw = getpwuid(id)))
+ {
+ (void)strcpy(buf, pw->pw_name);
+ buf[16] = '\0';
+
+ return;
+ }
+#endif /* SET_UID */
+
+ /* Oops. Hack -- default to "PLAYER" */
+ strcpy(buf, "PLAYER");
+}
+
+
+
+/*
+* The concept of the "file" routines below (and elsewhere) is that all
+* file handling should be done using as few routines as possible, since
+* every machine is slightly different, but these routines always have the
+* same semantics.
+*
+* In fact, perhaps we should use the "path_parse()" routine below to convert
+* from "canonical" filenames (optional leading tilde's, internal wildcards,
+* slash as the path seperator, etc) to "system" filenames (no special symbols,
+* system-specific path seperator, etc). This would allow the program itself
+* to assume that all filenames are "Unix" filenames, and explicitly "extract"
+* such filenames if needed (by "path_parse()", or perhaps "path_canon()").
+*
+* Note that "path_temp" should probably return a "canonical" filename.
+*
+* Note that "my_fopen()" and "my_open()" and "my_make()" and "my_kill()"
+* and "my_move()" and "my_copy()" should all take "canonical" filenames.
+*
+* Note that "canonical" filenames use a leading "slash" to indicate an absolute
+* path, and a leading "tilde" to indicate a special directory, and default to a
+* relative path, but MSDOS uses a leading "drivename plus colon" to indicate the
+* use of a "special drive", and then the rest of the path is parsed "normally",
+* and MACINTOSH uses a leading colon to indicate a relative path, and an embedded
+* colon to indicate a "drive plus absolute path", and finally defaults to a file
+* in the current working directory, which may or may not be defined.
+*
+* We should probably parse a leading "~~/" as referring to "ANGBAND_DIR". (?)
+*/
+
+
+#ifdef SET_UID
+
+/*
+* Extract a "parsed" path from an initial filename
+* Normally, we simply copy the filename into the buffer
+* But leading tilde symbols must be handled in a special way
+* Replace "~/" by the home directory of the current user
+*/
+errr path_parse(char *buf, int max, cptr file)
+{
+ cptr u, s;
+ struct passwd *pw;
+
+
+ /* Assume no result */
+ buf[0] = '\0';
+
+ /* No file? */
+ if (!file) return ( -1);
+
+ /* File needs no parsing */
+ if (file[0] != '~')
+ {
+ strcpy(buf, file);
+ return (0);
+ }
+
+ /* Point at the user */
+ u = file + 1;
+
+ /* Look for non-user portion of the file */
+ s = strstr(u, PATH_SEP);
+
+#ifdef GETLOGIN_BROKEN
+ /* Ask the environment for the home directory */
+ u = getenv("HOME");
+
+ if (!u) return (1);
+
+ (void)strcpy(buf, u);
+#else
+ /* Look up password data for user */
+ pw = getpwuid(getuid());
+
+ /* Nothing found? */
+ if (!pw) return (1);
+
+ /* Make use of the info */
+ (void)strcpy(buf, pw->pw_dir);
+#endif
+
+ /* Append the rest of the filename, if any */
+ if (s) (void)strcat(buf, s);
+
+ /* Success */
+ return (0);
+}
+
+
+#else /* SET_UID */
+
+
+/*
+* Extract a "parsed" path from an initial filename
+*
+* This requires no special processing on simple machines,
+* except for verifying the size of the filename.
+*/
+errr path_parse(char *buf, int max, cptr file)
+{
+ /* Accept the filename */
+ strnfmt(buf, max, "%s", file);
+
+ /* Success */
+ return (0);
+}
+
+
+#endif /* SET_UID */
+
+
+/*
+* Hack -- acquire a "temporary" file name if possible
+*
+* This filename is always in "system-specific" form.
+*/
+errr path_temp(char *buf, int max)
+{
+#ifdef WINDOWS
+ static u32b tmp_counter;
+ static char valid_characters[] =
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ char rand_ext[4];
+
+ rand_ext[0] = valid_characters[rand_int(sizeof (valid_characters))];
+ rand_ext[1] = valid_characters[rand_int(sizeof (valid_characters))];
+ rand_ext[2] = valid_characters[rand_int(sizeof (valid_characters))];
+ rand_ext[3] = '\0';
+ strnfmt(buf, max, "%s/t_%ud.%s", ANGBAND_DIR_XTRA, tmp_counter, rand_ext);
+ tmp_counter++;
+#else
+ cptr s;
+
+ /* Temp file */
+ s = tmpnam(NULL);
+
+ /* Oops */
+ if (!s) return ( -1);
+
+ /* Format to length */
+ strnfmt(buf, max, "%s", s);
+#endif
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Create a new path by appending a file (or directory) to a path
+*
+* This requires no special processing on simple machines, except
+* for verifying the size of the filename, but note the ability to
+* bypass the given "path" with certain special file-names.
+*
+* Note that the "file" may actually be a "sub-path", including
+* a path and a file.
+*
+* Note that this function yields a path which must be "parsed"
+* using the "parse" function above.
+*/
+errr path_build(char *buf, int max, cptr path, cptr file)
+{
+ /* Special file */
+ if (file[0] == '~')
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Absolute file, on "normal" systems */
+ else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, ""))
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* No path given */
+ else if (!path[0])
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Path and File */
+ else
+ {
+ /* Build the new path */
+ strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- replacement for "fopen()"
+*/
+FILE *my_fopen(cptr file, cptr mode)
+{
+
+ 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));
+
+}
+
+
+/*
+* Hack -- replacement for "fclose()"
+*/
+errr my_fclose(FILE *fff)
+{
+ /* Require a file */
+ if (!fff) return ( -1);
+
+ /* Close, check for error */
+ if (fclose(fff) == EOF) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- replacement for "fgets()"
+*
+* Read a string, without a newline, to a file
+*
+* Process tabs, strip internal non-printables
+*/
+errr my_fgets(FILE *fff, char *buf, huge n)
+{
+ huge i = 0;
+
+ while (TRUE)
+ {
+ int c = fgetc(fff);
+
+ if (c == EOF)
+ {
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success (0) if some characters were read */
+ return (i == 0);
+ }
+
+ /* Handle newline -- DOS (\015\012), Mac (\015), UNIX (\012) */
+ else if (c == '\r')
+ {
+ c = fgetc(fff);
+ if (c != '\n') ungetc(c, fff);
+
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success */
+ return (0);
+ }
+ else if (c == '\n')
+ {
+ c = fgetc(fff);
+ if (c != '\r') ungetc(c, fff);
+
+ /* Terminate */
+ buf[i] = '\0';
+
+ /* Success */
+ return (0);
+ }
+
+ /* Handle tabs */
+ else if (c == '\t')
+ {
+ /* Hack -- require room */
+ if (i + 8 >= n) break;
+
+ /* Append 1-8 spaces */
+ do { buf[i++] = ' '; } while (i % 8);
+ }
+
+ /* Handle printables */
+ else if (isprint(c))
+ {
+ /* Copy */
+ buf[i++] = c;
+
+ /* Check length */
+ if (i >= n) break;
+ }
+ }
+
+ /* Nothing */
+ buf[0] = '\0';
+
+ /* Failure */
+ return (1);
+}
+
+
+/*
+* 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 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"
+*
+*/
+int fd_make(cptr file, int mode)
+{
+ char buf[1024];
+
+ /* Hack -- Try to parse the path */
+ if (path_parse(buf, 1024, file)) return ( -1);
+
+
+/* Create the file, fail if exists, write-only, binary */
+return (open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode));
+
+
+}
+
+
+/*
+* 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);
+
+
+/* Attempt to open the file */
+return (open(buf, flags | O_BINARY, 0));
+
+}
+
+
+/*
+* Hack -- attempt to seek on a file descriptor
+*/
+errr fd_seek(int fd, huge n)
+{
+ s32b p;
+
+ /* Verify fd */
+ if (fd < 0) return ( -1);
+
+ /* Seek to the given position */
+ p = lseek(fd, n, SEEK_SET);
+
+ /* Failure */
+ if (p < 0) return (1);
+
+ /* Failure */
+ if ((huge)p != n) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to read data from a file descriptor
+*/
+errr fd_read(int fd, char *buf, huge n)
+{
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+#ifndef SET_UID
+
+ /* Read pieces */
+ while (n >= 16384)
+ {
+ /* Read a piece */
+ if (read(fd, buf, 16384) != 16384) return (1);
+
+ /* Shorten the task */
+ buf += 16384;
+
+ /* Shorten the task */
+ n -= 16384;
+ }
+
+#endif
+
+ /* Read the final piece */
+ if ((huge)read(fd, buf, n) != n) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- Attempt to write data to a file descriptor
+*/
+errr fd_write(int fd, cptr buf, huge n)
+{
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+#ifndef SET_UID
+
+ /* Write pieces */
+ while (n >= 16384)
+ {
+ /* Write a piece */
+ if (write(fd, buf, 16384) != 16384) return (1);
+
+ /* Shorten the task */
+ buf += 16384;
+
+ /* Shorten the task */
+ n -= 16384;
+ }
+
+#endif
+
+ /* Write the final piece */
+ if ((huge)write(fd, buf, n) != n) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+* Hack -- attempt to close a file descriptor
+*/
+errr fd_close(int fd)
+{
+ /* Verify the fd */
+ if (fd < 0) return ( -1);
+
+ /* Close */
+ (void)close(fd);
+
+ /* XXX XXX XXX */
+ return (0);
+}
+
+
+/*
+* XXX XXX XXX Important note about "colors" XXX XXX XXX
+*
+* The "TERM_*" color definitions list the "composition" of each
+* "Angband color" in terms of "quarters" of each of the three color
+* components (Red, Green, Blue), for example, TERM_UMBER is defined
+* as 2/4 Red, 1/4 Green, 0/4 Blue.
+*
+* The following info is from "Torbjorn Lindgren" (see "main-xaw.c").
+*
+* These values are NOT gamma-corrected. On most machines (with the
+* Macintosh being an important exception), you must "gamma-correct"
+* the given values, that is, "correct for the intrinsic non-linearity
+* of the phosphor", by converting the given intensity levels based
+* on the "gamma" of the target screen, which is usually 1.7 (or 1.5).
+*
+* The actual formula for conversion is unknown to me at this time,
+* but you can use the table below for the most common gamma values.
+*
+* So, on most machines, simply convert the values based on the "gamma"
+* of the target screen, which is usually in the range 1.5 to 1.7, and
+* usually is closest to 1.7. The converted value for each of the five
+* different "quarter" values is given below:
+*
+* Given Gamma 1.0 Gamma 1.5 Gamma 1.7 Hex 1.7
+* ----- ---- ---- ---- ---
+* 0/4 0.00 0.00 0.00 #00
+* 1/4 0.25 0.27 0.28 #47
+* 2/4 0.50 0.55 0.56 #8f
+* 3/4 0.75 0.82 0.84 #d7
+* 4/4 1.00 1.00 1.00 #ff
+*
+* Note that some machines (i.e. most IBM machines) are limited to a
+* hard-coded set of colors, and so the information above is useless.
+*
+* Also, some machines are limited to a pre-determined set of colors,
+* for example, the IBM can only display 16 colors, and only 14 of
+* those colors resemble colors used by Angband, and then only when
+* you ignore the fact that "Slate" and "cyan" are not really matches,
+* so on the IBM, we use "orange" for both "Umber", and "Light Umber"
+* in addition to the obvious "Orange", since by combining all of the
+* "indeterminate" colors into a single color, the rest of the colors
+* are left with "meaningful" values.
+*/
+
+
+/*
+* Move the cursor
+*/
+void move_cursor(int row, int col)
+{
+ Term_gotoxy(col, row);
+}
+
+
+
+/*
+* Convert a decimal to a single digit octal number
+*/
+static char octify(uint i)
+{
+ return (hexsym[i % 8]);
+}
+
+/*
+* Convert a decimal to a single digit hex number
+*/
+static char hexify(uint i)
+{
+ return (hexsym[i % 16]);
+}
+
+
+/*
+* Convert a octal-digit into a decimal
+*/
+static int deoct(char c)
+{
+ if (isdigit(c)) return (D2I(c));
+ return (0);
+}
+
+/*
+* Convert a hexidecimal-digit into a decimal
+*/
+static int dehex(char c)
+{
+ if (isdigit(c)) return (D2I(c));
+ if (islower(c)) return (A2I(c) + 10);
+ if (isupper(c)) return (A2I(tolower(c)) + 10);
+ return (0);
+}
+
+
+static 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 (iequals(str, macro_modifier_name[i]))
+ 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 (iequals(str, macro_trigger_name[i]) && ']' == 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 (iequals(key_code, macro_trigger_keycode[0][i])
+ || iequals(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 */
+ free(macro__act[n]);
+ }
+
+ /* Create a new macro */
+ else
+ {
+ /* Acquire a new index */
+ n = macro__num++;
+
+ /* Save the pattern */
+ macro__pat[n] = strdup(pat);
+ }
+
+ /* Save the action */
+ macro__act[n] = strdup(act);
+
+ /* Efficiency */
+ macro__use[(byte)(pat[0])] = TRUE;
+
+ /* 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_bell();
+ }
+
+ /* Flush the input (later!) */
+ flush();
+}
+
+
+/*
+* Hack -- Make a (relevant?) sound
+*/
+void sound(int val)
+{
+ /* Ignore; sound not currently supported. */
+ return;
+}
+
+
+
+/*
+* 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 */
+ sleep_for(milliseconds(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;
+
+bool_ inkey_flag = FALSE;
+
+
+/*
+* 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_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.
+*/
+static char inkey_real(bool_ inkey_scan)
+{
+ int v;
+
+ char kk;
+
+ char ch = 0;
+
+ bool_ done = FALSE;
+
+ term *old = Term;
+
+ /* Hack -- Use the "inkey_next" pointer */
+ if (inkey_next && *inkey_next)
+ {
+ /* Get next character, and advance */
+ ch = *inkey_next++;
+
+ /* Cancel the various "global parameters" */
+ inkey_base = inkey_flag = inkey_scan = FALSE;
+
+ /* Accept result */
+ macro_recorder_add(ch);
+ return (ch);
+ }
+
+ /* Forget pointer */
+ inkey_next = NULL;
+
+
+ /* 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]);
+
+ /* 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 */
+ sleep_for(milliseconds(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;
+
+ /* Do an html dump */
+ do_cmd_html_dump();
+
+ /* 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_flag = FALSE;
+
+
+ /* Return the keypress */
+ macro_recorder_add(ch);
+ return (ch);
+}
+
+char inkey(void) {
+ return inkey_real(FALSE);
+}
+
+char inkey_scan() {
+ return inkey_real(TRUE);
+}
+
+/*
+* Hack -- flush
+*/
+static void msg_flush(int x)
+{
+ byte a = TERM_L_BLUE;
+
+ /* Pause for response */
+ Term_putstr(x, 0, -1, a, "-more-");
+
+ /* Get an acceptable keypress */
+ while (1)
+ {
+ int cmd = inkey();
+ if (quick_messages) break;
+ if ((cmd == ESCAPE) || (cmd == ' ')) break;
+ if ((cmd == '\n') || (cmd == '\r')) break;
+ bell();
+ }
+
+ /* Clear the line */
+ Term_erase(0, 0, 255);
+}
+
+/* Display a message */
+void display_message(int x, int y, int split, byte color, cptr t)
+{
+ int i = 0, j = 0;
+
+ while (i < split)
+ {
+ if (t[i] == '#')
+ {
+ if (t[i + 1] == '#')
+ {
+ Term_putstr(x + j, y, 1, color, "#");
+ i += 2;
+ j++;
+ }
+ else
+ {
+ color = color_char_to_attr(t[i + 1]);
+ i += 2;
+ }
+ }
+ else
+ {
+ Term_putstr(x + j, y, 1, color, t + i);
+ i++;
+ j++;
+ }
+ }
+}
+
+/*
+* Output a message to the top line of the screen.
+*
+* Break long messages into multiple pieces (40-72 chars).
+*
+* Allow multiple short messages to "share" the top line.
+*
+* Prompt the user to make sure he has a chance to read them.
+*
+* These messages are memorized for later reference (see above).
+*
+* We could do "Term_fresh()" to provide "flicker" if needed.
+*
+* The global "msg_flag" variable can be cleared to tell us to
+* "erase" any "pending" messages still on the screen.
+*
+* XXX XXX XXX Note that we must be very careful about using the
+* "msg_print()" functions without explicitly calling the special
+* "msg_print(NULL)" function, since this may result in the loss
+* of information if the screen is cleared, or if anything is
+* displayed on the top line.
+*
+* XXX XXX XXX Note that "msg_print(NULL)" will clear the top line
+* even if no messages are pending. This is probably a hack.
+*/
+void cmsg_print(byte color, cptr msg)
+{
+ static int p = 0;
+
+ int n;
+ int wid;
+
+ char *t;
+
+ char buf[1024];
+
+ Term_get_size(&wid, nullptr);
+ int lim = 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(msg, color);
+
+ /* Handle "auto_more" */
+ if (auto_more)
+ {
+ /* Window stuff */
+ p_ptr->window |= (PW_MESSAGE);
+
+ /* Force window update */
+ window_stuff();
+
+ /* Done */
+ return;
+ }
+
+
+ /* Copy it */
+ strcpy(buf, msg);
+
+ /* Analyze the buffer */
+ t = buf;
+
+ /* Split message */
+ while (n > lim)
+ {
+ char oops;
+
+ int check, split;
+
+ /* Default split */
+ split = lim;
+
+ /* Find the "best" split point */
+ for (check = 40; check < lim; check++)
+ {
+ /* Found a valid split point */
+ if (t[check] == ' ') split = check;
+ }
+
+ /* Save the split character */
+ oops = t[split];
+
+ /* Split the message */
+ t[split] = '\0';
+
+ /* Display part of the message */
+ display_message(0, 0, split, color, t);
+
+ /* Flush it */
+ msg_flush(split + 1);
+
+ /* Memorize the piece */
+ /* if (character_generated) message_add(t); */
+
+ /* Restore the split character */
+ t[split] = oops;
+
+ /* Insert a space */
+ t[--split] = ' ';
+
+ /* Prepare to recurse on the rest of "buf" */
+ t += split;
+ n -= split;
+ }
+
+
+ /* Display the tail of the message */
+ display_message(p, 0, n, color, t);
+
+ /* Memorize the tail */
+ /* if (character_generated) message_add(t); */
+
+ /* Window stuff */
+ p_ptr->window |= (PW_MESSAGE);
+
+ /* Remember the message */
+ msg_flag = TRUE;
+
+ /* Remember the position */
+ p += n + 1;
+
+ /* Optional refresh */
+ if (fresh_message) Term_fresh();
+}
+
+/* Hack -- for compatibility and easy sake */
+void msg_print(cptr msg)
+{
+ cmsg_print(TERM_WHITE, msg);
+}
+
+
+/*
+ * Hack -- prevent "accidents" in "screen_save()" or "screen_load()"
+ */
+static int screen_depth = 0;
+
+
+/*
+ * Save the screen, and increase the "icky" depth.
+ *
+ * This function must match exactly one call to "screen_load()".
+ */
+void screen_save(void)
+{
+ /* Hack -- Flush messages */
+ msg_print(NULL);
+
+ /* Save the screen (if legal) */
+ if (screen_depth++ == 0) Term_save();
+
+ /* Increase "icky" depth */
+ character_icky++;
+}
+
+
+/*
+ * Load the screen, and decrease the "icky" depth.
+ *
+ * This function must match exactly one call to "screen_save()".
+ */
+void screen_load(void)
+{
+ /* Hack -- Flush messages */
+ msg_print(NULL);
+
+ /* Load the screen (if legal) */
+ if (--screen_depth == 0) Term_load();
+
+ /* Decrease "icky" depth */
+ character_icky--;
+}
+
+
+/*
+* Display a formatted message, using "vstrnfmt()" and "msg_print()".
+*/
+void msg_format(cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ cmsg_print(TERM_WHITE, buf);
+}
+
+void cmsg_format(byte color, cptr fmt, ...)
+{
+ va_list vp;
+
+ char buf[1024];
+
+ /* Begin the Varargs Stuff */
+ va_start(vp, fmt);
+
+ /* Format the args, save the length */
+ (void)vstrnfmt(buf, 1024, fmt, vp);
+
+ /* End the Varargs Stuff */
+ va_end(vp);
+
+ /* Display */
+ cmsg_print(color, buf);
+}
+
+
+
+/*
+* Display a string on the screen using an attribute.
+*
+* At the given location, using the given attribute, if allowed,
+* add the given string. Do not clear the line.
+*/
+void c_put_str(byte attr, cptr str, int row, int col)
+{
+ /* Position cursor, Dump the attr/text */
+ Term_putstr(col, row, -1, attr, str);
+}
+
+/*
+* As above, but in "white"
+*/
+void put_str(cptr str, int row, int col)
+{
+ /* Spawn */
+ Term_putstr(col, row, -1, TERM_WHITE, str);
+}
+
+
+
+/*
+* Display a string on the screen using an attribute, and clear
+* to the end of the line.
+*/
+void c_prt(byte attr, cptr str, int row, int col)
+{
+ /* Clear line, position cursor */
+ Term_erase(col, row, 255);
+
+ /* Dump the attr/text */
+ Term_addstr( -1, attr, str);
+}
+
+/*
+* As above, but in "white"
+*/
+void prt(cptr str, int row, int col)
+{
+ /* Spawn */
+ c_prt(TERM_WHITE, str, row, col);
+}
+
+
+
+/*
+ * Print some (colored) text to the screen at the current cursor position,
+ * automatically "wrapping" existing text (at spaces) when necessary to
+ * avoid placing any text into the last column, and clearing every line
+ * before placing any text in that line. Also, allow "newline" to force
+ * a "wrap" to the next line. Advance the cursor as needed so sequential
+ * calls to this function will work correctly.
+ *
+ * Once this function has been called, the cursor should not be moved
+ * until all the related "text_out()" calls to the window are complete.
+ *
+ * This function will correctly handle any width up to the maximum legal
+ * value of 256, though it works best for a standard 80 character width.
+ */
+void text_out_to_screen(byte a, cptr str)
+{
+ int x, y;
+
+ int wid, h;
+
+ int wrap;
+
+ cptr s;
+
+
+ /* Obtain the size */
+ (void)Term_get_size(&wid, &h);
+
+ /* Obtain the cursor */
+ (void)Term_locate(&x, &y);
+
+ /* Wrapping boundary */
+ 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 column 75 ; 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 = 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;
+ int hgt;
+ Term_get_size(nullptr, &hgt);
+
+ /* Erase requested rows */
+ for (y = row; y < 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.
+*/
+static bool_ askfor_aux_complete = FALSE;
+
+bool_ askfor_aux(char *buf, int len)
+{
+ int y, x;
+
+ int i = 0;
+
+ int k = 0;
+
+ int wid, hgt;
+
+ bool_ done = FALSE;
+
+
+ /* Locate the cursor */
+ Term_locate(&x, &y);
+
+ /* Get terminal size */
+ Term_get_size(&wid, &hgt);
+
+ /* Paranoia -- check column */
+ if ((x < 0) || (x >= wid)) x = 0;
+
+ /* Restrict the length */
+ if (x + len > wid) len = wid - x;
+
+
+ /* Paranoia -- Clip the default entry */
+ buf[len - 1] = '\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);
+}
+
+bool_ askfor_aux_with_completion(char *buf, int len)
+{
+ askfor_aux_complete = TRUE;
+ bool_ res = askfor_aux(buf, len);
+ askfor_aux_complete = FALSE;
+ return res;
+}
+
+/*
+* Get a string from the user
+*
+* The "prompt" should take the form "Prompt: "
+*
+* Note that the initial contents of the string is used as
+* the default response, so be sure to "clear" it if needed.
+*
+* We clear the input, and return FALSE, on "ESCAPE".
+*/
+bool_ get_string(cptr prompt, char *buf, int len)
+{
+ bool_ res;
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Display prompt */
+ prt(prompt, 0, 0);
+
+ /* Ask the user for a string */
+ res = askfor_aux(buf, len);
+
+ /* Clear prompt */
+ prt("", 0, 0);
+
+ /* Result */
+ return (res);
+}
+
+
+/*
+* Verify something with the user
+*
+* The "prompt" should take the form "Query? "
+*
+* Note that "[y/n]" is appended to the prompt.
+*/
+bool_ get_check(cptr prompt)
+{
+ int i;
+
+ char buf[80];
+
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Hack -- Build a "useful" prompt */
+ strnfmt(buf, 78, "%.70s[y/n] ", prompt);
+
+ /* Prompt for it */
+ prt(buf, 0, 0);
+
+ /* Get an acceptable answer */
+ while (TRUE)
+ {
+ i = inkey();
+ if (quick_messages) break;
+ if (i == ESCAPE) break;
+ if (strchr("YyNn", i)) break;
+ bell();
+ }
+
+ /* Erase the prompt */
+ prt("", 0, 0);
+
+ /* Normal negation */
+ if ((i != 'Y') && (i != 'y')) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Prompts for a keypress
+*
+* The "prompt" should take the form "Command: "
+*
+* Returns TRUE unless the character is "Escape"
+*/
+bool_ get_com(cptr prompt, char *command)
+{
+ /* Paranoia XXX XXX XXX */
+ msg_print(NULL);
+
+ /* Display a prompt */
+ prt(prompt, 0, 0);
+
+ /* Get a key */
+ *command = inkey();
+
+ /* Clear the prompt */
+ prt("", 0, 0);
+
+ /* Handle "cancel" */
+ if (*command == ESCAPE) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+* Request a "quantity" from the user
+*
+* Hack -- allow "command_arg" to specify a quantity
+*/
+s32b get_quantity(cptr prompt, s32b max)
+{
+ s32b amt;
+ int aamt;
+
+ char tmp[80];
+
+ char buf[80];
+
+
+ /* Use "command_arg" */
+ if (command_arg)
+ {
+ /* Extract a number */
+ amt = command_arg;
+
+ /* Clear "command_arg" */
+ command_arg = 0;
+
+ /* Enforce the maximum */
+ if (amt > max) amt = max;
+
+ /* Use it */
+ return (amt);
+ }
+
+ /* Get the item index */
+ if ((max != 1) && repeat_pull(&aamt))
+ {
+ amt = aamt;
+
+ /* Enforce the maximum */
+ if (amt > max) amt = max;
+
+ /* Enforce the minimum */
+ if (amt < 0) amt = 0;
+
+ /* Use it */
+ return (amt);
+ }
+
+ /* Build a prompt if needed */
+ if (!prompt)
+ {
+ /* Build a prompt */
+ sprintf(tmp, "Quantity (1-%ld): ", (long int) max);
+
+ /* Use that prompt */
+ prompt = tmp;
+ }
+
+
+ /* Default to one */
+ amt = 1;
+
+ /* Build the default */
+ sprintf(buf, "%ld", (long int) amt);
+
+ /* Ask for a quantity */
+ if (!get_string(prompt, buf, 9)) return (0);
+
+ /* Extract a number */
+ amt = atoi(buf);
+
+ /* A letter means "all" */
+ if (isalpha(buf[0])) amt = max;
+
+ /* Enforce the maximum */
+ if (amt > max) amt = max;
+
+ /* Enforce the minimum */
+ if (amt < 0) amt = 0;
+
+
+ if (amt) repeat_push(amt);
+
+ /* Return the result */
+ return (amt);
+}
+
+
+/*
+* Pause for user response XXX XXX XXX
+*/
+void pause_line(int row)
+{
+ prt("", row, 0);
+ put_str("[Press any key to continue]", row, 23);
+ inkey();
+ prt("", row, 0);
+}
+
+
+/*
+* Hack -- special buffer to hold the action of the current keymap
+*/
+static char request_command_buffer[256];
+
+/*
+* Mega-Hack -- characters for which keymaps should be ignored in
+* request_command(). This MUST have at least twice as many characters as
+* there are building actions in the actions[] array in store_info_type.
+*/
+#define MAX_IGNORE_KEYMAPS 12
+char request_command_ignore_keymaps[MAX_IGNORE_KEYMAPS];
+
+/*
+* Mega-Hack -- flag set by do_cmd_{inven,equip}() to allow keymaps in
+* auto-command mode.
+*/
+bool_ request_command_inven_mode = FALSE;
+
+
+/*
+* Request a command from the user.
+*
+* Sets p_ptr->command_cmd, p_ptr->command_dir, p_ptr->command_rep,
+* p_ptr->command_arg. May modify p_ptr->command_new.
+*
+* Note that "caret" ("^") is treated specially, and is used to
+* allow manual input of control characters. This can be used
+* on many machines to request repeated tunneling (Ctrl-H) and
+* on the Macintosh to request "Control-Caret".
+*
+* Note that "backslash" is treated specially, and is used to bypass any
+* keymap entry for the following character. This is useful for macros.
+*
+* Note that this command is used both in the dungeon and in
+* stores, and must be careful to work in both situations.
+*
+* Note that "p_ptr->command_new" may not work any more. XXX XXX XXX
+*/
+void request_command(int shopping)
+{
+ int i;
+
+ s16b cmd;
+ char cmd_char;
+
+ int mode;
+
+ cptr act;
+
+
+ /* Keymap mode */
+ mode = get_keymap_mode();
+
+ /* No command yet */
+ command_cmd = 0;
+
+ /* No "argument" yet */
+ command_arg = 0;
+
+ /* No "direction" yet */
+ command_dir = 0;
+
+
+ /* Get command */
+ while (1)
+ {
+ /* Hack -- auto-commands */
+ if (command_new)
+ {
+ /* Flush messages */
+ msg_print(NULL);
+
+ /* Use auto-command */
+ cmd = command_new;
+
+ /* Forget it */
+ command_new = 0;
+
+ /* Hack - bypass keymaps, unless asked not to */
+ if (!inkey_next && !request_command_inven_mode)
+ {
+ inkey_next = "";
+ }
+
+ /* Mega-Hack -- turn off this flag immediately */
+ request_command_inven_mode = FALSE;
+ }
+
+ /* Get a keypress in "command" mode */
+ else
+ {
+ /* Hack -- no flush needed */
+ msg_flag = FALSE;
+
+ /* Activate "command mode" */
+ inkey_flag = TRUE;
+
+ /* Get a command */
+ cmd = inkey();
+ }
+
+ /* Clear top line */
+ prt("", 0, 0);
+
+
+ /* Command Count */
+ if (cmd == '0')
+ {
+ int old_arg = command_arg;
+
+ /* Reset */
+ command_arg = 0;
+
+ /* Begin the input */
+ prt("Count: ", 0, 0);
+
+ /* Get a command count */
+ while (1)
+ {
+ /* Get a new keypress */
+ cmd = inkey();
+
+ /* Simple editing (delete or backspace) */
+ if ((cmd == 0x7F) || (cmd == KTRL('H')))
+ {
+ /* Delete a digit */
+ command_arg = command_arg / 10;
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Actual numeric data */
+ else if (cmd >= '0' && cmd <= '9')
+ {
+ /* Stop count at 9999 */
+ if (command_arg >= 1000)
+ {
+ /* Warn */
+ bell();
+
+ /* Limit */
+ command_arg = 9999;
+ }
+
+ /* Increase count */
+ else
+ {
+ /* Incorporate that digit */
+ command_arg = command_arg * 10 + D2I(cmd);
+ }
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Exit on "unusable" input */
+ else
+ {
+ break;
+ }
+ }
+
+ /* Hack -- Handle "zero" */
+ if (command_arg == 0)
+ {
+ /* Default to 99 */
+ command_arg = 99;
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Hack -- Handle "old_arg" */
+ if (old_arg != 0)
+ {
+ /* Restore old_arg */
+ command_arg = old_arg;
+
+ /* Show current count */
+ prt(format("Count: %d", command_arg), 0, 0);
+ }
+
+ /* Hack -- white-space means "enter command now" */
+ if ((cmd == ' ') || (cmd == '\n') || (cmd == '\r'))
+ {
+ /* Get a real command */
+ bool_ temp = get_com("Command: ", &cmd_char);
+ cmd = cmd_char;
+
+ if (!temp)
+ {
+ /* Clear count */
+ command_arg = 0;
+
+ /* Continue */
+ continue;
+ }
+ }
+ }
+
+
+ /* Allow "keymaps" to be bypassed */
+ if (cmd == '\\')
+ {
+ /* Get a real command */
+ (void)get_com("Command: ", &cmd_char);
+
+ cmd = cmd_char;
+
+ /* Hack -- bypass keymaps */
+ if (!inkey_next) inkey_next = "";
+ }
+
+
+ /* Allow "control chars" to be entered */
+ if (cmd == '^')
+ {
+ /* Get a new command and controlify it */
+ if (get_com("Control: ", &cmd_char)) cmd = KTRL(cmd_char);
+ else cmd = 0;
+ }
+
+
+ /* Look up applicable keymap */
+ act = keymap_act[mode][(byte)(cmd)];
+
+ /* Mega-Hack -- Ignore certain keymaps */
+ if (shopping && cmd > 0)
+ {
+ for (i = 0; i < MAX_IGNORE_KEYMAPS; i++)
+ if (cmd == request_command_ignore_keymaps[i])
+ {
+ act = NULL;
+ break;
+ }
+ }
+
+ /* Apply keymap if not inside a keymap already */
+ if (act && !inkey_next)
+ {
+ /* Install the keymap (limited buffer size) */
+ strnfmt(request_command_buffer, 256, "%s", act);
+
+ /* Start using the buffer */
+ inkey_next = request_command_buffer;
+
+ /* Continue */
+ continue;
+ }
+
+
+ /* Paranoia */
+ if (!cmd) continue;
+
+
+ /* Use command */
+ command_cmd = cmd;
+
+ /* Done */
+ break;
+ }
+
+ /* Hack -- Auto-repeat certain commands */
+ if (always_repeat && (command_arg <= 0))
+ {
+ /* Hack -- auto repeat certain commands */
+ if (strchr("TBDoc+", command_cmd))
+ {
+ /* Repeat 99 times */
+ command_arg = 99;
+ }
+ }
+
+ /* Hack -- Scan equipment */
+ for (i = INVEN_WIELD; i < INVEN_TOTAL; i++)
+ {
+ cptr s;
+
+ object_type *o_ptr = &p_ptr->inventory[i];
+
+ /* Skip non-objects */
+ if (!o_ptr->k_idx) continue;
+
+ /* No inscription */
+ if (!o_ptr->note) continue;
+
+ /* Obtain the inscription */
+ s = quark_str(o_ptr->note);
+
+ /* Find a '^' */
+ s = strchr(s, '^');
+
+ /* Process preventions */
+ while (s)
+ {
+ /* Check the "restriction" character */
+ if ((s[1] == command_cmd) || (s[1] == '*'))
+ {
+ /* Hack -- Verify command */
+ if (!get_check("Are you sure? "))
+ {
+ /* Hack -- Use space */
+ command_cmd = ' ';
+ }
+ }
+
+ /* Find another '^' */
+ s = strchr(s + 1, '^');
+ }
+ }
+
+
+ /* Hack -- erase the message line. */
+ prt("", 0, 0);
+}
+
+
+
+
+/*
+ * Check a char for "vowel-hood"
+ */
+bool_ is_a_vowel(int ch)
+{
+ switch (ch)
+ {
+ case 'a':
+ case 'e':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'A':
+ case 'E':
+ case 'I':
+ case 'O':
+ case 'U':
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+
+/*
+ * GH
+ * Called from cmd4.c and a few other places. Just extracts
+ * a direction from the keymap for ch (the last direction,
+ * in fact) byte or char here? I'm thinking that keymaps should
+ * generally only apply to single keys, which makes it no more
+ * than 128, so a char should suffice... but keymap_act is 256...
+ */
+int get_keymap_dir(char ch)
+{
+ int d = 0;
+
+ int mode;
+
+ cptr act;
+
+ cptr s;
+
+
+ /* Already a direction? */
+ if (isdigit(ch))
+ {
+ d = D2I(ch);
+ }
+ else
+ {
+ /* Keymap mode */
+ mode = get_keymap_mode();
+
+ /* Extract the action (if any) */
+ act = keymap_act[mode][(byte)(ch)];
+
+ /* Analyze */
+ if (act)
+ {
+ /* Convert to a direction */
+ for (s = act; *s; ++s)
+ {
+ /* Use any digits in keymap */
+ if (isdigit(*s)) d = D2I(*s);
+ }
+ }
+ }
+
+ /* Paranoia */
+ if (d == 5) d = 0;
+
+ /* Return direction */
+ return (d);
+}
+
+
+#define REPEAT_MAX 20
+
+/* Number of chars saved */
+static int repeat__cnt = 0;
+
+/* Current index */
+static int repeat__idx = 0;
+
+/* Saved "stuff" */
+static int repeat__key[REPEAT_MAX];
+
+
+void repeat_push(int what)
+{
+ /* Too many keys */
+ if (repeat__cnt == REPEAT_MAX) return;
+
+ /* Push the "stuff" */
+ repeat__key[repeat__cnt++] = what;
+
+ /* Prevents us from pulling keys */
+ ++repeat__idx;
+}
+
+
+bool_ repeat_pull(int *what)
+{
+ /* All out of keys */
+ if (repeat__idx == repeat__cnt) return (FALSE);
+
+ /* Grab the next key, advance */
+ *what = repeat__key[repeat__idx++];
+
+ /* Success */
+ return (TRUE);
+}
+
+void repeat_check(void)
+{
+ int what;
+
+ /* Ignore some commands */
+ if (command_cmd == ESCAPE) return;
+ if (command_cmd == ' ') return;
+ if (command_cmd == '\r') return;
+ if (command_cmd == '\n') return;
+
+ /* Repeat Last Command */
+ if (command_cmd == 'n')
+ {
+ /* Reset */
+ repeat__idx = 0;
+
+ /* Get the command */
+ if (repeat_pull(&what))
+ {
+ /* Save the command */
+ command_cmd = what;
+ }
+ }
+
+ /* Start saving new command */
+ else
+ {
+ /* Reset */
+ repeat__cnt = 0;
+ repeat__idx = 0;
+
+ what = command_cmd;
+
+ /* Save this command */
+ repeat_push(what);
+ }
+}
+
+
+/*
+ * Read a number at a specific location on the screen
+ *
+ * Allow numbers of any size and save the last keypress.
+ */
+u32b get_number(u32b def, u32b max, int y, int x, char *cmd)
+{
+ u32b res = def;
+
+ /* Player has not typed anything yet */
+ bool_ no_keys = TRUE;
+
+ /* Begin the input with default */
+ prt(format("%lu", def), y, x);
+
+ /* Get a command count */
+ while (1)
+ {
+ /* Get a new keypress */
+ *cmd = inkey();
+
+ /* Simple editing (delete or backspace) */
+ if ((*cmd == 0x7F) || (*cmd == KTRL('H')))
+ {
+ /* Override the default */
+ no_keys = FALSE;
+
+ /* Delete a digit */
+ res = res / 10;
+
+ prt(format("%lu", res), y, x);
+ }
+
+ /* Actual numeric data */
+ else if (*cmd >= '0' && *cmd <= '9')
+ {
+ /* Override the default */
+ if (no_keys)
+ {
+ no_keys = FALSE;
+ res = 0;
+ }
+
+ /* Don't overflow */
+ if (((u32b)(0 - 1) - D2I(*cmd)) / 10 < res)
+ {
+ /* Warn */
+ bell();
+
+ /* Limit */
+ res = (max + 1 == 0) ? (u32b)(0 - 1) : max;
+ }
+
+ /* Stop count at maximum */
+ else if (res * 10 + D2I(*cmd) > max)
+ {
+ /* Warn */
+ bell();
+
+ /* Limit */
+ res = max;
+ }
+
+ /* Increase count */
+ else
+ {
+ /* Incorporate that digit */
+ res = res * 10 + D2I(*cmd);
+ }
+
+ /* Show current count */
+ prt(format("%lu", res), y, x);
+ }
+
+ /* Escape cancels */
+ else if (*cmd == ESCAPE)
+ {
+ res = 0;
+ break;
+ }
+
+ /* Exit on "unusable" input */
+ else
+ {
+ break;
+ }
+ }
+
+ return res;
+}
+
+/*
+ * Allow the user to select multiple items without pressing '0'
+ */
+void get_count(int number, int max)
+{
+ char cmd;
+
+ /* Use the default */
+ command_arg = number;
+
+ /* Hack -- Optional flush */
+ if (flush_command) flush();
+
+ /* Clear top line */
+ prt("", 0, 0);
+
+ /* Begin the input */
+ prt("How many?", 0, 0);
+
+ /* Actually get a number */
+ command_arg = get_number(command_arg, max, 0, 10, &cmd);
+
+ prt("", 0, 0);
+}
+
+byte count_bits(u32b array)
+{
+ byte k = 0, i;
+
+ if (array)
+ for (i = 0; i < 32; i++)
+ if (array & (1 << i)) k++;
+
+ return k;
+}
+
+/* Return the lowered string */
+void strlower(char *buf)
+{
+ u16b i;
+
+ for (i = 0; (buf[i] != 0) && (i < 256) ;i++)
+ {
+ if (isupper(buf[i])) buf[i] = tolower(buf[i]);
+ }
+}
+
+/*
+ * Given monster name as string, return the index in r_info array. Name
+ * must exactly match (look out for commas and the like!), or else 0 is
+ * returned. Case doesn't matter. -GSN-
+ */
+int test_monster_name(cptr name)
+{
+ int i;
+
+ /* Scan the monsters */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+ if (r_ptr->name && iequals(name, r_ptr->name)) 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];
+ if (re_ptr->name && iequals(name, re_ptr->name)) 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];
+ /* If name matches, give us the number */
+ if (k_ptr->name && iequals(name, k_ptr->name)) 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_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, race_mod_info[ps].title);
+ }
+ else
+ {
+ sprintf(buf, "%s %s", race_mod_info[ps].title, race_info[pr].title);
+ }
+ }
+ else
+ {
+ sprintf(buf, "%s", race_info[pr].title);
+ }
+
+ return (buf);
+}
+
+/*
+ * Ask to select an item in a list
+ */
+int ask_menu(cptr ask, const std::vector<std::string> &items)
+{
+ int ret = -1, i, start = 0;
+ char c;
+ int size = items.size(); // Convert to int to avoid warnings
+
+ /* 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 < size) && (i < start + 20); i++)
+ {
+ prt(format("%c) %s", I2A(i - start), items[i].c_str()), i - start + 1, 0);
+ }
+
+ /* Wait for user input */
+ c = inkey();
+
+ /* Leave the screen */
+ if (c == ESCAPE) break;
+
+ /* Scroll */
+ else if (c == '+')
+ {
+ if (start + 20 < size)
+ 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 >= size)
+ {
+ 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("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);
+}
+
+/*
+ * 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();
+}
+
+/*
+ * Timers
+ */
+timer_type *new_timer(void (*callback)(), s32b delay)
+{
+ timer_type *t_ptr = new timer_type();
+
+ static_assert(std::is_pod<timer_type>::value, "Cannot memset a non-POD type");
+ memset(t_ptr, 0, sizeof(timer_type));
+
+ t_ptr->next = gl_timers;
+ gl_timers = t_ptr;
+
+ t_ptr->callback = callback;
+ t_ptr->delay = delay;
+ t_ptr->countdown = delay;
+ t_ptr->enabled = FALSE;
+
+ return t_ptr;
+}
+
+int get_keymap_mode()
+{
+ if (rogue_like_commands)
+ {
+ return KEYMAP_MODE_ROGUE;
+ }
+ else
+ {
+ return KEYMAP_MODE_ORIG;
+ }
+}
+
+/**
+ * Determines if a map location is fully inside the outer walls
+ */
+bool in_bounds(int y, int x)
+{
+ return (y > 0) && (x > 0) && (y < cur_hgt-1) && (x < cur_wid-1);
+}
+
+/**
+ * Determines if a map location is on or inside the outer walls
+ */
+bool in_bounds2(int y, int x)
+{
+ return (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)".
+ */
+bool panel_contains(int y, int x)
+{
+ return (y >= panel_row_min) && (y <= panel_row_max) && (x >= panel_col_min) && (x <= panel_col_max);
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 00000000..4ae797b9
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "h-basic.h"
+
+// C linkage required for these functions since main-* code uses them.
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern errr path_parse(char *buf, int max, cptr file);
+extern errr path_build(char *buf, int max, cptr path, cptr file);
+extern void bell(void);
+extern errr macro_add(cptr pat, cptr act);
+extern sint macro_find_exact(cptr pat);
+extern char inkey(void);
+extern void prt(cptr str, int row, int col);
+extern void pause_line(int row);
+extern void user_name(char *buf, int id);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/util.hpp b/src/util.hpp
new file mode 100644
index 00000000..bb8a64f4
--- /dev/null
+++ b/src/util.hpp
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "h-basic.h"
+#include "timer_type_fwd.hpp"
+
+#include <vector>
+#include <string>
+
+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 cptr get_player_race_name(int pr, int ps);
+extern cptr get_day(int day);
+extern s32b bst(s32b what, s32b t);
+extern errr path_temp(char *buf, int max);
+extern FILE *my_fopen(cptr file, cptr mode);
+extern errr my_fgets(FILE *fff, char *buf, huge n);
+extern errr my_fclose(FILE *fff);
+extern errr fd_kill(cptr file);
+extern errr fd_move(cptr file, cptr what);
+extern int fd_make(cptr file, int mode);
+extern int fd_open(cptr file, int flags);
+extern errr fd_seek(int fd, huge n);
+extern errr fd_read(int fd, char *buf, huge n);
+extern errr fd_write(int fd, cptr buf, huge n);
+extern errr fd_close(int fd);
+extern void flush(void);
+extern void 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 char inkey_scan(void);
+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 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_from(int row);
+extern int ask_menu(cptr ask, const std::vector<std::string> &items);
+extern bool_ askfor_aux(char *buf, int len);
+extern bool_ askfor_aux_with_completion(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 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(void (*callback)(), s32b delay);
+extern int get_keymap_mode();
+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);
+extern bool in_bounds(int y, int x);
+extern bool in_bounds2(int y, int x);
+extern bool panel_contains(int y, int x);
diff --git a/src/variable.cc b/src/variable.cc
new file mode 100644
index 00000000..f0d18111
--- /dev/null
+++ b/src/variable.cc
@@ -0,0 +1,1065 @@
+/*
+ * 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 "variable.hpp"
+#include "variable.h"
+
+#include "cli_comm_fwd.hpp"
+#include "player_type.hpp"
+#include "randart_gen_type.hpp"
+#include "util.hpp"
+
+
+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;
+
+/*
+ * Savefile version
+ */
+byte sf_major; /* Savefile's "version_major" */
+byte sf_minor; /* Savefile's "version_minor" */
+byte sf_patch; /* Savefile's "version_patch" */
+
+/*
+ * Savefile information
+ */
+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 aruments
+ */
+bool_ arg_wizard; /* Command arg -- Request wizard mode */
+bool_ arg_force_original; /* Command arg -- Request original keyset */
+bool_ arg_force_roguelike; /* Command arg -- Request roguelike keyset */
+
+/*
+ * 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_icky; /* The game is in an icky full screen mode */
+bool_ character_xtra; /* The game is in an icky startup mode */
+
+u32b seed_flavor; /* Hack -- consistent object colors */
+
+s16b command_cmd; /* Current "Angband Command" */
+
+s16b command_arg; /* Gives argument of current command */
+s16b command_rep; /* Gives repetition of current command */
+s16b command_dir; /* Gives direction of current command */
+
+s16b command_wrk; /* See "cmd1.c" */
+
+s16b command_new; /* Command chaining from inven/equip view */
+
+s32b energy_use; /* Energy use this turn */
+
+bool_ create_up_stair; /* Auto-create "up stairs" */
+bool_ create_down_stair; /* Auto-create "down stairs" */
+
+bool_ create_up_shaft; /* Auto-create "up shaft" */
+bool_ create_down_shaft; /* Auto-create "down shaft" */
+
+bool_ msg_flag; /* Used in msg_print() for "buffering" */
+
+bool_ alive; /* True if game is running */
+
+bool_ death; /* True if player has died */
+
+s16b running; /* Current counter for running, if any */
+s16b resting; /* Current counter for resting, if any */
+
+s16b cur_hgt; /* Current dungeon height */
+s16b cur_wid; /* Current dungeon width */
+s16b dun_level; /* Current dungeon level */
+s16b old_dun_level; /* Old dungeon level */
+s16b num_repro; /* Current reproducer count */
+s16b object_level; /* Current object creation level */
+s16b monster_level; /* Current monster creation level */
+
+s32b turn; /* Current game turn */
+s32b old_turn; /* Turn when level began (feelings) */
+
+bool_ wizard; /* Is the player currently in Wizard mode? */
+
+u16b total_winner; /* Semi-Hack -- Game has been won */
+u16b has_won; /* Semi-Hack -- Game has been won */
+
+u16b noscore; /* Track various "cheating" conditions */
+
+bool_ inkey_base; /* 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_ hack_mind;
+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()" */
+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 -- Indentation for the text when using text_out().
+ */
+int text_out_indent = 0;
+
+
+/*
+ * Software options (set via the '=' command). See "tables.c"
+ */
+
+
+
+
+/* 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 */
+
+/*
+ * 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;
+
+
+
+/*
+ * Current player's character name
+ */
+char player_name[32];
+
+/*
+ * Stripped version of "player_name"
+ */
+char player_base[32];
+
+/*
+ * What killed the player
+ */
+char died_from[80];
+
+/*
+ * Hack -- Textual "history" for the Player
+ */
+char history[4][60];
+
+/*
+ * Buffer to hold the current savefile name
+ */
+char savefile[1024];
+
+
+/*
+ * Array of grids lit by player lite (see "cave.c")
+ */
+s16b lite_n;
+s16b lite_y[LITE_MAX];
+s16b lite_x[LITE_MAX];
+
+/*
+ * Array of grids viewable to the player (see "cave.c")
+ */
+s16b view_n;
+byte view_y[VIEW_MAX];
+byte view_x[VIEW_MAX];
+
+/*
+ * Array of grids for use by various functions (see "cave.c")
+ */
+s16b temp_n;
+byte temp_y[TEMP_MAX];
+byte temp_x[TEMP_MAX];
+
+
+/*
+ * Number of active macros.
+ */
+s16b macro__num;
+
+/*
+ * Array of macro patterns [MACRO_MAX]
+ */
+char **macro__pat;
+
+/*
+ * Array of macro actions [MACRO_MAX]
+ */
+char **macro__act;
+
+/*
+ * Array of macro types [MACRO_MAX]
+ */
+bool_ *macro__cmd;
+
+/*
+ * Current macro action [1024]
+ */
+char *macro__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 */
+};
+
+
+/*
+ * The array of "cave grids" [MAX_WID][MAX_HGT].
+ */
+cave_type **cave = nullptr;
+
+/*
+ * 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 * ALLOCATIONS_MAX)
+ */
+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.
+ */
+char *keymap_act[KEYMAP_MODES][256];
+
+
+
+/*** Player information ***/
+
+/*
+ * Pointer to the player info
+ */
+player_type *p_ptr = nullptr;
+
+/*
+ * 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;
+
+
+/*
+ * 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 vault generation arrays
+ */
+vault_type *v_info;
+
+/*
+ * The terrain feature arrays
+ */
+feature_type *f_info;
+
+/*
+ * The object kind arrays
+ */
+object_kind *k_info;
+
+/*
+ * The artifact arrays
+ */
+artifact_type *a_info;
+
+/*
+ * The item set arrays
+ */
+set_type *set_info;
+
+/*
+ * The ego-item arrays
+ */
+ego_item_type *e_info;
+
+/*
+ * The randart arrays
+ */
+randart_part_type *ra_info;
+randart_gen_type ra_gen[30];
+
+/* jk */
+/* the trap-arrays */
+trap_type *t_info;
+
+/*
+ * The monster race arrays
+ */
+monster_race *r_info;
+
+/*
+ * The monster ego race arrays
+ */
+monster_ego *re_info;
+
+/*
+ * The dungeon types arrays
+ */
+dungeon_info_type *d_info;
+
+/*
+ * Player abilities arrays
+ */
+ability_type *ab_info;
+
+/*
+ * Player skills arrays
+ */
+skill_type *s_info;
+
+/*
+ * Player race arrays
+ */
+player_race *race_info;
+
+/*
+ * Player mod race arrays
+ */
+player_race_mod *race_mod_info;
+
+/*
+ * Player class arrays
+ */
+player_class *class_info;
+meta_class_type *meta_class_info;
+
+/*
+ * The wilderness features arrays
+ */
+wilderness_type_info *wf_info;
+int wildc2i[256];
+
+/*
+ * The store/building types arrays
+ */
+store_info_type *st_info;
+
+/*
+ * The building actions types arrays
+ */
+store_action_type *ba_info;
+
+/*
+ * The owner types arrays
+ */
+owner_type *ow_info;
+
+/*
+ * Default texts for feature information.
+ */
+cptr DEFAULT_FEAT_TEXT = "a wall blocking your way";
+cptr DEFAULT_FEAT_TUNNEL = "You cannot tunnel through that.";
+cptr DEFAULT_FEAT_BLOCK = DEFAULT_FEAT_TEXT;
+
+/*
+ * Hack -- The special Angband "System Suffix"
+ * This variable is used to choose an appropriate "pref-xxx" file
+ */
+cptr ANGBAND_SYS = "xxx";
+
+/*
+ * Path name: The main "lib" directory
+ * This variable is not actually used anywhere in the code
+ */
+char *ANGBAND_DIR;
+
+/*
+ * Core lua system
+ * These files are portable between platforms
+ */
+char *ANGBAND_DIR_CORE;
+
+/*
+ * Textual dungeon level definition files
+ * These files are portable between platforms
+ */
+char *ANGBAND_DIR_DNGN;
+
+/*
+ * Binary image files for the "*_info" arrays (binary)
+ * These files are not portable between platforms
+ */
+char *ANGBAND_DIR_DATA;
+
+/*
+ * Textual template files for the "*_info" arrays (ascii)
+ * These files are portable between platforms
+ */
+char *ANGBAND_DIR_EDIT;
+
+/*
+ * Various extra files (ascii)
+ * These files may be portable between platforms
+ */
+char *ANGBAND_DIR_FILE;
+
+/*
+ * Help files (normal) for the online help (ascii)
+ * These files are portable between platforms
+ */
+char *ANGBAND_DIR_HELP;
+
+/*
+ * Help files (spoilers) for the online help (ascii)
+ * These files are portable between platforms
+ */
+char *ANGBAND_DIR_INFO;
+
+/*
+ * Modules, those subdirectories are half-mirrors of lib/
+ */
+char *ANGBAND_DIR_MODULES;
+
+/*
+ * Textual template files for the plot files (ascii)
+ * These files are portable between platforms
+ */
+char *ANGBAND_DIR_NOTE;
+
+/*
+ * Savefiles for current characters (binary)
+ * These files are portable between platforms
+ */
+char *ANGBAND_DIR_SAVE;
+
+/*
+ * Default "preference" files (ascii)
+ * These files are rarely portable between platforms
+ */
+char *ANGBAND_DIR_PREF;
+
+/*
+ * User "preference" files (ascii)
+ * These files are rarely portable between platforms
+ */
+char *ANGBAND_DIR_USER;
+
+/*
+ * Various extra files (binary)
+ * These files are rarely portable between platforms
+ */
+char *ANGBAND_DIR_XTRA;
+
+
+
+/*
+ * 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);
+
+/*
+ * Devices
+ */
+s32b get_level_max_stick = -1;
+s32b get_level_use_stick = -1;
+
+/*
+ * 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 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
+ */
+u16b max_set_idx = 1;
+
+/*
+ * Maximum number of players info in p_info.txt
+ */
+u16b max_rp_idx;
+u16b max_rmp_idx;
+u16b max_c_idx;
+u16b max_mc_idx;
+
+/*
+ * Maximum number of actions types in ba_info.txt
+ */
+u16b max_ba_idx;
+
+/*
+ * Maximum number of owner types in ow_info.txt
+ */
+u16b max_ow_idx;
+
+/*
+ * Maximum number of objects in the level
+ */
+u16b max_o_idx;
+
+/*
+ * Maximum number of monsters in the level
+ */
+u16b max_m_idx;
+
+/*
+ * Maximum number of traps in tr_info.txt
+ */
+u16b max_t_idx;
+
+/*
+ * Maximum number of wilderness features in wf_info.txt
+ */
+u16b max_wf_idx;
+
+/*
+ * Flags for initialization
+ */
+int init_flags;
+
+/* True if on an ambush */
+bool_ ambush_flag;
+
+/* True if on fated level */
+bool_ fate_flag;
+
+/* No breeders */
+s16b no_breeds;
+
+/* Carried monsters can't take the damage if this is them which attack the player */
+bool_ carried_monster_hit = FALSE;
+
+/*
+ * Random artifacts.
+ */
+random_artifact random_artifacts[MAX_RANDARTS];
+/* These three used to be constants but now are set by modules */
+s32b RANDART_WEAPON;
+s32b RANDART_ARMOR;
+s32b RANDART_JEWEL;
+
+/*
+ * Random spells.
+ */
+random_spell random_spells[MAX_SPELLS];
+s16b spell_num;
+
+/*
+ * Runecrafter's selfmade spells.
+ */
+rune_spell rune_spells[MAX_RUNES];
+s16b rune_num;
+
+/*
+ * Fate.
+ */
+fate fates[MAX_FATES];
+
+/*
+ * Which dungeon ?
+ * 0 = Wilderness
+ * 1 = Mirkwood
+ * 2 = Mordor
+ * 3 = Angband
+ * 4 = Barrow Downs
+ * 5 = Mount Doom
+ * 6 = Nether Realm
+ * etc. (see d_info.txt)
+ */
+byte dungeon_type;
+s16b *max_dlv;
+
+/* The Doppleganger index in m_list */
+s16b doppleganger;
+
+/* To allow wilderness encounters */
+bool_ generate_encounter;
+
+/* Special levels */
+bool_ special_lvls;
+
+/*
+ * Such an ugly hack ...
+ */
+bool_ *m_allow_special;
+bool_ *k_allow_special;
+bool_ *a_allow_special;
+
+/*
+ * Plots
+ */
+s16b plots[MAX_PLOTS];
+
+/*
+ * Random quest
+ */
+random_quest random_quests[MAX_RANDOM_QUEST];
+
+/*
+ * Special levels
+ */
+bool_ *special_lvl[MAX_DUNGEON_DEPTH];
+bool_ generate_special_feeling = FALSE;
+
+/*
+ * Dungeon flags
+ */
+u32b dungeon_flags1;
+u32b dungeon_flags2;
+
+/*
+ * The last character displayed
+ */
+birther previous_char;
+
+/*
+ * Race histories
+ */
+hist_type *bg;
+int max_bg_idx;
+
+/*
+ * The spell list of schools
+ */
+s16b schools_count = 0;
+school_type schools[SCHOOLS_MAX];
+
+/*
+ * 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];
+
+/*
+ * 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 = 127;
+
+/*
+ * Automatizer enabled status
+ */
+bool_ automatizer_enabled = FALSE;
+bool_ automatizer_create = 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 game_module_idx;
+s32b VERSION_MAJOR;
+s32b VERSION_MINOR;
+s32b VERSION_PATCH;
+
+/*
+ * Some module info
+ */
+s32b max_plev = 50;
+s32b DUNGEON_BASE = 4;
+s32b DUNGEON_DEATH = 28;
+s32b DUNGEON_ASTRAL = 8;
+s32b DUNGEON_ASTRAL_WILD_X = 45;
+s32b DUNGEON_ASTRAL_WILD_Y = 19;
+
+/*
+ * Timers
+ */
+timer_type *gl_timers = NULL;
+
+
+/**
+ * Get the version string.
+ */
+const char *get_version_string()
+{
+ static char version_str[80];
+ static bool_ initialized = 0;
+ if (!initialized) {
+ sprintf(version_str, "%s %ld.%ld.%ld%s",
+ game_module,
+ (long int) VERSION_MAJOR,
+ (long int) VERSION_MINOR,
+ (long int) VERSION_PATCH, IS_CVS);
+ initialized = TRUE;
+ }
+ return version_str;
+}
+
+/*
+ * A list of tvals and their textual names
+ */
+tval_desc 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_PARCHMENT, "Parchment" },
+ { TV_INSTRUMENT, "Musical Instrument" },
+ { TV_RUNE1, "Rune 1" },
+ { TV_RUNE2, "Rune 2" },
+ { TV_JUNK, "Junk" },
+ { TV_TRAPKIT, "Trapping Kit" },
+ { 0, NULL }
+};
diff --git a/src/variable.h b/src/variable.h
new file mode 100644
index 00000000..7621149a
--- /dev/null
+++ b/src/variable.h
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "angband.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern cptr ANGBAND_SYS;
+extern char *ANGBAND_DIR_MODULES;
+extern char *ANGBAND_DIR_SAVE;
+extern char *ANGBAND_DIR_CORE;
+extern char *ANGBAND_DIR_DNGN;
+extern char *ANGBAND_DIR_DATA;
+extern char *ANGBAND_DIR_EDIT;
+extern char *ANGBAND_DIR_FILE;
+extern char *ANGBAND_DIR_HELP;
+extern char *ANGBAND_DIR_INFO;
+extern char *ANGBAND_DIR_NOTE;
+extern char *ANGBAND_DIR_PREF;
+extern char *ANGBAND_DIR_USER;
+extern char *ANGBAND_DIR_XTRA;
+extern term *angband_term[ANGBAND_TERM_MAX];
+extern char angband_term_name[ANGBAND_TERM_MAX][80];
+extern byte angband_color_table[256][4];
+extern bool_ arg_wizard;
+extern bool_ arg_force_original;
+extern bool_ arg_force_roguelike;
+extern bool_ character_generated;
+extern bool_ character_icky;
+extern bool_ inkey_flag;
+extern bool_ msg_flag;
+extern char player_name[32];
+extern char player_base[32];
+extern char savefile[1024];
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/src/variable.hpp b/src/variable.hpp
new file mode 100644
index 00000000..ab52f5b6
--- /dev/null
+++ b/src/variable.hpp
@@ -0,0 +1,308 @@
+#pragma once
+
+#include "angband.h"
+#include "ability_type_fwd.hpp"
+#include "alloc_entry_fwd.hpp"
+#include "artifact_type_fwd.hpp"
+#include "birther.hpp"
+#include "cave_type_fwd.hpp"
+#include "deity_type.hpp"
+#include "dungeon_info_type_fwd.hpp"
+#include "effect_type.hpp"
+#include "ego_item_type_fwd.hpp"
+#include "fate.hpp"
+#include "feature_type_fwd.hpp"
+#include "hist_type_fwd.hpp"
+#include "meta_class_type_fwd.hpp"
+#include "monster_ego_fwd.hpp"
+#include "monster_race_fwd.hpp"
+#include "monster_type_fwd.hpp"
+#include "object_kind_fwd.hpp"
+#include "object_type_fwd.hpp"
+#include "owner_type_fwd.hpp"
+#include "player_class_fwd.hpp"
+#include "player_defs.hpp"
+#include "player_race_fwd.hpp"
+#include "player_race_mod_fwd.hpp"
+#include "player_sex_fwd.hpp"
+#include "player_spec_fwd.hpp"
+#include "player_type_fwd.hpp"
+#include "randart_gen_type_fwd.hpp"
+#include "randart_part_type_fwd.hpp"
+#include "random_artifact.hpp"
+#include "random_quest.hpp"
+#include "random_spell.hpp"
+#include "rune_spell.hpp"
+#include "school_type.hpp"
+#include "set_type_fwd.hpp"
+#include "skill_type_fwd.hpp"
+#include "skills_defs.hpp"
+#include "store_action_type_fwd.hpp"
+#include "store_info_type_fwd.hpp"
+#include "timer_type_fwd.hpp"
+#include "town_type_fwd.hpp"
+#include "trap_type_fwd.hpp"
+#include "tval_desc.hpp"
+#include "vault_type_fwd.hpp"
+#include "wilderness_map_fwd.hpp"
+#include "wilderness_type_info_fwd.hpp"
+
+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];
+extern byte version_major;
+extern byte version_minor;
+extern byte version_patch;
+extern byte sf_major;
+extern byte sf_minor;
+extern byte sf_patch;
+extern u32b sf_when;
+extern u16b sf_lives;
+extern u16b sf_saves;
+extern bool_ character_dungeon;
+extern bool_ character_loaded;
+extern bool_ character_xtra;
+extern u32b seed_flavor;
+extern s16b command_cmd;
+extern s16b command_arg;
+extern s16b command_rep;
+extern s16b command_dir;
+extern s16b command_wrk;
+extern s16b command_new;
+extern s32b energy_use;
+extern bool_ create_up_stair;
+extern bool_ create_down_stair;
+extern bool_ create_up_shaft;
+extern bool_ create_down_shaft;
+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 u16b total_winner;
+extern u16b has_won;
+extern u16b noscore;
+extern bool_ inkey_base;
+extern s16b coin_type;
+extern bool_ opening_chest;
+extern bool_ shimmer_monsters;
+extern bool_ shimmer_objects;
+extern bool_ repair_monsters;
+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 int total_friends;
+extern s32b total_friend_levels;
+extern int leaving_quest;
+extern char summon_kin_type;
+extern bool_ hack_mind;
+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_indent;
+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 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 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 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 char died_from[80];
+extern char history[4][60];
+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 char **macro__pat;
+extern char **macro__act;
+extern bool_ *macro__cmd;
+extern char *macro__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 cave_type **cave;
+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 char *keymap_act[KEYMAP_MODES][256];
+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 s16b player_hp[PY_MAX_LEVEL];
+extern ability_type *ab_info;
+extern skill_type *s_info;
+extern vault_type *v_info;
+extern feature_type *f_info;
+extern object_kind *k_info;
+extern artifact_type *a_info;
+extern ego_item_type *e_info;
+extern randart_part_type *ra_info;
+extern randart_gen_type ra_gen[30];
+extern monster_race *r_info;
+extern monster_ego *re_info;
+extern dungeon_info_type *d_info;
+extern player_class *class_info;
+extern meta_class_type *meta_class_info;
+extern player_race *race_info;
+extern player_race_mod *race_mod_info;
+extern trap_type *t_info;
+extern wilderness_type_info *wf_info;
+extern int wildc2i[256];
+extern store_info_type *st_info;
+extern store_action_type *ba_info;
+extern owner_type *ow_info;
+extern set_type *set_info;
+extern cptr DEFAULT_FEAT_TEXT;
+extern cptr DEFAULT_FEAT_TUNNEL;
+extern cptr DEFAULT_FEAT_BLOCK;
+extern char *ANGBAND_DIR;
+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 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_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 u16b max_set_idx;
+extern int init_flags;
+extern bool_ ambush_flag;
+extern bool_ fate_flag;
+extern s16b no_breeds;
+extern bool_ carried_monster_hit;
+extern random_artifact random_artifacts[MAX_RANDARTS];
+extern s32b RANDART_WEAPON;
+extern s32b RANDART_ARMOR;
+extern s32b RANDART_JEWEL;
+extern random_spell random_spells[MAX_SPELLS];
+extern s16b spell_num;
+extern rune_spell rune_spells[MAX_RUNES];
+extern s16b rune_num;
+extern fate fates[MAX_FATES];
+extern byte dungeon_type;
+extern s16b *max_dlv;
+extern s16b doppleganger;
+extern bool_ generate_encounter;
+extern bool_ special_lvls;
+extern bool_ *m_allow_special;
+extern bool_ *k_allow_special;
+extern bool_ *a_allow_special;
+extern s16b plots[MAX_PLOTS];
+extern random_quest random_quests[MAX_RANDOM_QUEST];
+extern bool_ *special_lvl[MAX_DUNGEON_DEPTH];
+extern bool_ generate_special_feeling;
+extern u32b dungeon_flags1;
+extern u32b dungeon_flags2;
+extern birther previous_char;
+extern int max_bg_idx;
+extern s16b schools_count;
+extern school_type schools[SCHOOLS_MAX];
+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 int max_bact;
+extern bool_ automatizer_enabled;
+extern s16b last_teleportation_y;
+extern s16b last_teleportation_x;
+extern cptr game_module;
+extern s32b game_module_idx;
+extern s32b VERSION_MAJOR;
+extern s32b VERSION_MINOR;
+extern s32b VERSION_PATCH;
+extern s32b max_plev;
+extern s32b DUNGEON_BASE;
+extern s32b DUNGEON_DEATH;
+extern s32b DUNGEON_ASTRAL;
+extern s32b DUNGEON_ASTRAL_WILD_X;
+extern s32b DUNGEON_ASTRAL_WILD_Y;
+extern deity_type deity_info[MAX_GODS];
+extern timer_type *gl_timers;
+extern const char *get_version_string();
+extern tval_desc tvals[];
+extern hist_type *bg;
diff --git a/src/vault_type.hpp b/src/vault_type.hpp
new file mode 100644
index 00000000..650599cb
--- /dev/null
+++ b/src/vault_type.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * Vault descriptors.
+ */
+struct vault_type
+{
+ char *data; /* Vault data */
+
+ 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) */
+};
diff --git a/src/vault_type_fwd.hpp b/src/vault_type_fwd.hpp
new file mode 100644
index 00000000..25ffd8cc
--- /dev/null
+++ b/src/vault_type_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct vault_type;
diff --git a/src/wild.cc b/src/wild.cc
new file mode 100644
index 00000000..7724176f
--- /dev/null
+++ b/src/wild.cc
@@ -0,0 +1,1301 @@
+/*
+ * 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 "wild.hpp"
+
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "feature_type.hpp"
+#include "hook_wild_gen_in.hpp"
+#include "hooks.hpp"
+#include "init1.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "options.hpp"
+#include "player_type.hpp"
+#include "store_info_type.hpp"
+#include "tables.hpp"
+#include "town_type.hpp"
+#include "util.hpp"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+#include "z-rand.hpp"
+
+#include <memory>
+
+
+/*
+ * 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 = static_cast<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 = static_cast<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
+ */
+static int generate_area(int y, int x, bool_ border, bool_ corner)
+{
+ 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;
+
+ /* 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, rand_int(MAX_WILD_TERRAIN));
+ cave_set_feat(MAX_HGT - 2, 1, rand_int(MAX_WILD_TERRAIN));
+ cave_set_feat(1, MAX_WID - 2, rand_int(MAX_WILD_TERRAIN));
+ cave_set_feat(MAX_HGT - 2, MAX_WID - 2, rand_int(MAX_WILD_TERRAIN));
+
+ if (!corner)
+ {
+ /* x1, y1, x2, y2, num_depths, roughness */
+ plasma_recursive(1, 1, MAX_WID - 2, MAX_HGT - 2, MAX_WILD_TERRAIN - 1, roughness);
+ }
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+
+ for (y1 = 1; y1 < MAX_HGT - 1; y1++)
+ {
+ for (x1 = 1; x1 < MAX_WID - 1; x1++)
+ {
+ cave_set_feat(y1, x1,
+ wf_info[terrain[1][1]].terrain[cave[y1][x1].feat]);
+ }
+ }
+
+ }
+
+ /* Should we create a town ? */
+ if ((p_ptr->town_num > 0) && (p_ptr->town_num < 1000))
+ {
+ /* Create the town */
+ int xstart = 0;
+ int ystart = 0;
+
+ /* Initialize the town */
+ init_flags = INIT_CREATE_DUNGEON;
+ process_dungeon_file("t_info.txt", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+ }
+ else
+ {
+ /* Reset the town flag */
+ p_ptr->town_num = 0;
+ }
+
+ if (!corner)
+ {
+ /*
+ * Place roads in the wilderness
+ * ToDo: make the road a bit more interresting
+ */
+ road = wf_info[wild_map[y][x].feat].road;
+
+ if (road & ROAD_NORTH)
+ {
+ /* North road */
+ for (y1 = 1; y1 < MAX_HGT / 2; y1++)
+ {
+ x1 = MAX_WID / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+
+ if (road & ROAD_SOUTH)
+ {
+ /* North road */
+ for (y1 = MAX_HGT / 2; y1 < MAX_HGT - 1; y1++)
+ {
+ x1 = MAX_WID / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+
+ if (road & ROAD_EAST)
+ {
+ /* East road */
+ for (x1 = MAX_WID / 2; x1 < MAX_WID - 1; x1++)
+ {
+ y1 = MAX_HGT / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+
+ if (road & ROAD_WEST)
+ {
+ /* West road */
+ for (x1 = 1; x1 < MAX_WID / 2; x1++)
+ {
+ y1 = MAX_HGT / 2;
+ cave_set_feat(y1, x1, FEAT_FLOOR);
+ }
+ }
+ }
+
+ /* Hack -- Use the "simple" RNG */
+ Rand_quick = TRUE;
+
+ /* Hack -- Induce consistant town layout */
+ Rand_value = wild_map[y][x].seed;
+
+ entrance = wf_info[wild_map[y][x].feat].entrance;
+ if (!entrance) entrance = wild_map[y][x].entrance;
+
+ /* Create the dungeon if requested on the map */
+ if (entrance >= 1000)
+ {
+ int dy, dx;
+
+ dy = rand_range(6, cur_hgt - 6);
+ dx = rand_range(6, cur_wid - 6);
+
+ cave_set_feat(dy, dx, FEAT_MORE);
+ cave[dy][dx].special = entrance - 1000;
+ cave[dy][dx].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+
+ /* Use the complex RNG */
+ Rand_quick = FALSE;
+
+ /* MEGA HACK -- set at least one floor grid */
+ for (y1 = 1; y1 < cur_hgt - 1; y1++)
+ {
+ for (x1 = 1; x1 < cur_wid - 1; x1++)
+ {
+ if (cave_floor_bold(y1, x1)) hack_floor++;
+ }
+ }
+
+ /* NO floor ? put one */
+ if (!hack_floor)
+ {
+ cave_set_feat(cur_hgt / 2, cur_wid / 2, FEAT_GRASS);
+ cave[cur_hgt / 2][cur_wid / 2].special = 0;
+ hack_floor = 1;
+ }
+
+ /* Set the monster generation level to the wilderness level */
+ monster_level = wf_info[wild_map[y][x].feat].level;
+
+ /* Set the object generation level to the wilderness level */
+ object_level = wf_info[wild_map[y][x].feat].level;
+
+ return hack_floor;
+}
+
+/*
+ * Border of the wilderness area
+ */
+namespace {
+
+ 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;
+ };
+
+ border_type border;
+
+}
+
+/*
+ * Build the wilderness area outside of the town.
+ * -KMW-
+ */
+void wilderness_gen()
+{
+ int i, y, x, hack_floor;
+ bool_ daytime;
+ int xstart = 0;
+ int ystart = 0;
+ cave_type *c_ptr;
+
+ /* Init the wilderness */
+ process_dungeon_file("w_info.txt", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+
+ x = p_ptr->wilderness_x;
+ y = p_ptr->wilderness_y;
+
+ /* Set the correct monster hook */
+ set_mon_num_hook();
+
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ /* North border */
+ generate_area(y - 1, x, TRUE, FALSE);
+
+ 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);
+
+ for (i = 1; i < MAX_WID - 1; i++)
+ {
+ border.south[i] = cave[1][i].feat;
+ }
+
+ /* West border */
+ generate_area(y, x - 1, TRUE, FALSE);
+
+ 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);
+
+ 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);
+ border.north_west = cave[MAX_HGT - 2][MAX_WID - 2].feat;
+
+ /* North east corner */
+ generate_area(y - 1, x + 1, FALSE, TRUE);
+ border.north_east = cave[MAX_HGT - 2][1].feat;
+
+ /* South west corner */
+ generate_area(y + 1, x - 1, FALSE, TRUE);
+ border.south_west = cave[1][MAX_WID - 2].feat;
+
+ /* South east corner */
+ generate_area(y + 1, x + 1, FALSE, TRUE);
+ border.south_east = cave[1][1].feat;
+
+
+ /* Create terrain of the current area */
+ hack_floor = generate_area(y, x, FALSE, FALSE);
+
+
+ /* 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);
+
+ {
+ 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;
+ }
+ }
+
+ struct hook_wild_gen_in in = { FALSE };
+ process_hooks_new(HOOK_WILD_GEN, &in, NULL);
+}
+
+/*
+ * Build the wilderness area.
+ * -DG-
+ */
+void wilderness_gen_small()
+{
+ int i, j, entrance;
+ int xstart = 0;
+ int ystart = 0;
+
+ /* To prevent stupid things */
+ for (i = 0; i < MAX_WID; i++)
+ {
+ for (j = 0; j < MAX_HGT; j++)
+ {
+ cave_set_feat(j, i, FEAT_EKKAIA);
+ }
+ }
+
+ /* Init the wilderness */
+ process_dungeon_file("w_info.txt", &ystart, &xstart, cur_hgt, cur_wid, TRUE, FALSE);
+
+ /* Fill the map */
+ for (i = 0; i < max_wild_x; i++)
+ {
+ for (j = 0; j < max_wild_y; j++)
+ {
+ entrance = wf_info[wild_map[j][i].feat].entrance;
+ if (!entrance) entrance = wild_map[j][i].entrance;
+
+ if (wild_map[j][i].entrance)
+ {
+ cave_set_feat(j, i, FEAT_MORE);
+ }
+ else
+ {
+ cave_set_feat(j, i, wf_info[wild_map[j][i].feat].feat);
+ }
+
+ if ((cave[j][i].feat == FEAT_MORE) && (entrance >= 1000))
+ {
+ cave[j][i].special = entrance - 1000;
+ }
+
+ /* Show it if we know it */
+ if (wild_map[j][i].known)
+ {
+ cave[j][i].info |= (CAVE_GLOW | CAVE_MARK);
+ }
+ }
+ }
+
+ /* Place the player */
+ p_ptr->px = p_ptr->wilderness_x;
+ p_ptr->py = p_ptr->wilderness_y;
+
+ /* Set rewarded quests to finished */
+ for (i = 0; i < MAX_Q_IDX; i++)
+ {
+ if (quest[i].status == QUEST_STATUS_REWARDED)
+ {
+ quest[i].status = QUEST_STATUS_FINISHED;
+ }
+ }
+
+ struct hook_wild_gen_in in = { TRUE };
+ process_hooks_new(HOOK_WILD_GEN, &in, NULL);
+}
+
+/* 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);
+
+ /* 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 */
+ std::unique_ptr<int[]> rooms(new int[max_st_idx]);
+ num = get_shops(rooms.get());
+
+ /* 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);
+ }
+ }
+ }
+
+ /* 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);
+
+ /* 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 */
+ std::unique_ptr<int[]> rooms(new int[max_st_idx]);
+ num = get_shops(rooms.get());
+
+ /* 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);
+ }
+ }
+ }
+
+ /* 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;
+
+ /* Prepare an Array of "remaining stores", and count them */
+ std::unique_ptr<int[]> rooms(new int[max_st_idx]);
+ num = get_shops(rooms.get());
+
+ /* 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);
+ }
+ }
+}
+
+
+
+/*
+ * 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/wild.hpp b/src/wild.hpp
new file mode 100644
index 00000000..4cd9f0e7
--- /dev/null
+++ b/src/wild.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+extern void wilderness_gen();
+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);
diff --git a/src/wilderness_map.hpp b/src/wilderness_map.hpp
new file mode 100644
index 00000000..41e873bd
--- /dev/null
+++ b/src/wilderness_map.hpp
@@ -0,0 +1,15 @@
+#pragma once
+
+#include "h-basic.h"
+
+/**
+ * A structure describing a 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 ? */
+};
diff --git a/src/wilderness_map_fwd.hpp b/src/wilderness_map_fwd.hpp
new file mode 100644
index 00000000..806efb1b
--- /dev/null
+++ b/src/wilderness_map_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct wilderness_map;
diff --git a/src/wilderness_type_info.hpp b/src/wilderness_type_info.hpp
new file mode 100644
index 00000000..bc23c03e
--- /dev/null
+++ b/src/wilderness_type_info.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "h-basic.h"
+#include "terrain.hpp"
+
+/**
+ * A structure describing a wilderness area
+ * with a terrain, a town or a dungeon entrance
+ */
+struct wilderness_type_info
+{
+ const char *name; /* Name */
+ const char *text; /* Text */
+
+ 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 */
+};
diff --git a/src/wilderness_type_info_fwd.hpp b/src/wilderness_type_info_fwd.hpp
new file mode 100644
index 00000000..a206c9e3
--- /dev/null
+++ b/src/wilderness_type_info_fwd.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+struct wilderness_type_info;
diff --git a/src/wizard1.cc b/src/wizard1.cc
new file mode 100644
index 00000000..616a46cd
--- /dev/null
+++ b/src/wizard1.cc
@@ -0,0 +1,2499 @@
+#include "wizard1.hpp"
+
+#include "artifact_type.hpp"
+#include "cmd7.hpp"
+#include "ego_item_type.hpp"
+#include "monster_race.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "object_type.hpp"
+#include "skill_type.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+
+#include <vector>
+
+/*
+ * 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_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))
+ {
+ apply_magic(q_ptr, 0, FALSE, FALSE, FALSE, boost::make_optional(0));
+ }
+
+ /* Level */
+ (*lev) = k_ptr->level;
+
+ /* Value */
+ (*val) = object_value(q_ptr);
+
+
+ /* Hack */
+ if (!buf || !dam || !wgt) return;
+
+
+ /* Description (too brief) */
+ object_desc_store(buf, q_ptr, FALSE, 0);
+
+
+ /* Misc info */
+ strcpy(dam, "");
+
+ /* Damage */
+ switch (q_ptr->tval)
+ {
+ /* Bows */
+ case TV_BOW:
+ {
+ break;
+ }
+
+ /* Ammo */
+ case TV_SHOT:
+ case TV_BOLT:
+ case TV_ARROW:
+
+ /* Boomerangs */
+ case TV_BOOMERANG:
+
+ /* Weapons */
+ case TV_HAFTED:
+ case TV_POLEARM:
+ case TV_SWORD:
+ case TV_AXE:
+ case TV_MSTAFF:
+
+ /* Tools */
+ case TV_DIGGING:
+ {
+ sprintf(dam, "%dd%d", q_ptr->dd, q_ptr->ds);
+ break;
+ }
+
+ /* Armour */
+ case TV_BOOTS:
+ case TV_GLOVES:
+ case TV_CLOAK:
+ case TV_CROWN:
+ case TV_HELM:
+ case TV_SHIELD:
+ case TV_SOFT_ARMOR:
+ case TV_HARD_ARMOR:
+ case TV_DRAG_ARMOR:
+ {
+ sprintf(dam, "%d", q_ptr->ac);
+ break;
+ }
+ }
+
+
+ /* Weight */
+ sprintf(wgt, "%3ld.%ld", (long int) (q_ptr->weight / 10), (long int) (q_ptr->weight % 10));
+}
+
+
+/*
+ * Create a spoiler file for items
+ */
+static void spoil_obj_desc(cptr fname)
+{
+ int i, k, s, t, n = 0;
+
+ u16b who[200];
+
+ char buf[1024];
+
+ char wgt[80];
+ char dam[80];
+
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+
+ /* Header */
+ sprintf(buf, "Basic Items Spoilers for %s", get_version_string());
+ spoiler_underline(buf);
+ spoiler_blanklines(2);
+
+ /* More Header */
+ fprintf(fff, "%-45s %8s%7s%5s%9s\n",
+ "Description", "Dam/AC", "Wgt", "Lev", "Cost");
+ fprintf(fff, "%-45s %8s%7s%5s%9s\n",
+ "----------------------------------------",
+ "------", "---", "---", "----");
+
+ /* List the groups */
+ for (i = 0; TRUE; i++)
+ {
+ /* Write out the group title */
+ if (group_item[i].name)
+ {
+ /* Hack -- bubble-sort by cost and then level */
+ for (s = 0; s < n - 1; s++)
+ {
+ for (t = 0; t < n - 1; t++)
+ {
+ int i1 = t;
+ int i2 = t + 1;
+
+ int e1;
+ int e2;
+
+ s32b t1;
+ s32b t2;
+
+ kind_info(NULL, NULL, NULL, &e1, &t1, who[i1]);
+ kind_info(NULL, NULL, NULL, &e2, &t2, who[i2]);
+
+ if ((t1 > t2) || ((t1 == t2) && (e1 > e2)))
+ {
+ int tmp = who[i1];
+ who[i1] = who[i2];
+ who[i2] = tmp;
+ }
+ }
+ }
+
+ /* Spoil each item */
+ for (s = 0; s < n; s++)
+ {
+ int e;
+ s32b v;
+
+ /* Describe the kind */
+ kind_info(buf, dam, wgt, &e, &v, who[s]);
+
+ /* Dump it */
+ fprintf(fff, " %-45s%8s%7s%5d%9ld\n",
+ buf, dam, wgt, e, (long)(v));
+ }
+
+ /* Start a new set */
+ n = 0;
+
+ /* Notice the end */
+ if (!group_item[i].tval) break;
+
+ /* Start a new set */
+ fprintf(fff, "\n\n%s\n\n", group_item[i].name);
+ }
+
+ /* Acquire legal item types */
+ for (k = 1; k < max_k_idx; k++)
+ {
+ object_kind *k_ptr = &k_info[k];
+
+ /* Skip wrong tval's */
+ if (k_ptr->tval != group_item[i].tval) continue;
+
+ /* Hack -- Skip artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART | TR3_NORM_ART)) continue;
+
+ /* Hack -- Skip Ring of Powers */
+ if (k == 785) continue;
+
+ /* Save the index */
+ who[n++] = k;
+ }
+ }
+
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+/*
+ * Artifact Spoilers by: randy@PICARD.tamu.edu (Randy Hutson)
+ */
+
+
+/*
+ * Returns a "+" string if a number is non-negative and an empty
+ * string if negative
+ */
+#define POSITIZE(v) (((v) >= 0) ? "+" : "")
+
+/*
+ * These are used to format the artifact spoiler file. INDENT1 is used
+ * to indent all but the first line of an artifact spoiler. INDENT2 is
+ * used when a line "wraps". (Bladeturner's resistances cause this.)
+ */
+#define INDENT1 " "
+#define INDENT2 " "
+
+/*
+ * MAX_LINE_LEN specifies when a line should wrap.
+ */
+#define MAX_LINE_LEN 75
+
+/*
+ * Given an array, determine how many elements are in the array
+ */
+#define N_ELEMENTS(a) (sizeof (a) / sizeof ((a)[0]))
+
+/*
+ * The artifacts categorized by type
+ */
+static grouper group_artifact[] =
+{
+ { TV_SWORD, "Edged Weapons" },
+ { TV_POLEARM, "Polearms" },
+ { TV_HAFTED, "Hafted Weapons" },
+ { TV_AXE, "Axes" },
+
+ { TV_MSTAFF, "Mage Staffs" },
+
+ { TV_BOW, "Bows" },
+
+ { TV_SHOT, "Ammo" },
+ { TV_ARROW, NULL },
+ { TV_BOLT, NULL },
+
+ { TV_BOOMERANG, "Boomerangs" },
+
+ { TV_INSTRUMENT, "Instruments" },
+
+ { TV_SOFT_ARMOR, "Body Armor" },
+ { TV_HARD_ARMOR, NULL },
+ { TV_DRAG_ARMOR, NULL },
+
+ { TV_CLOAK, "Cloaks" },
+ { TV_SHIELD, "Shields" },
+ { TV_HELM, "Helms/Crowns" },
+ { TV_CROWN, NULL },
+ { TV_GLOVES, "Gloves" },
+ { TV_BOOTS, "Boots" },
+
+ { TV_DAEMON_BOOK, "Demonic Equipment" },
+
+ { TV_LITE, "Light Sources" },
+ { TV_AMULET, "Amulets" },
+ { TV_RING, "Rings" },
+
+ { TV_TOOL, "Tools" },
+ { TV_DIGGING, NULL },
+ { TV_TRAPKIT, "Trapping Kits" },
+
+ { 0, NULL }
+};
+
+
+
+/*
+ * Pair together a constant flag with a textual description.
+ *
+ * Used by both "init.c" and "wiz-spo.c".
+ *
+ * Note that it sometimes more efficient to actually make an array
+ * of textual names, where entry 'N' is assumed to be paired with
+ * the flag whose value is "1L << N", but that requires hard-coding.
+ */
+
+typedef struct flag_desc flag_desc;
+
+struct flag_desc
+{
+ const u32b flag;
+ const char *const desc;
+};
+
+
+
+/*
+ * These are used for "+3 to STR, DEX", etc. These are separate from
+ * the other pval affected traits to simplify the case where an object
+ * affects all stats. In this case, "All stats" is used instead of
+ * listing each stat individually.
+ */
+
+static flag_desc stat_flags_desc[] =
+{
+ { TR1_STR, "STR" },
+ { TR1_INT, "INT" },
+ { TR1_WIS, "WIS" },
+ { TR1_DEX, "DEX" },
+ { TR1_CON, "CON" },
+ { TR1_CHR, "CHR" }
+};
+
+/*
+ * Besides stats, these are the other player traits
+ * which may be affected by an object's pval
+ */
+
+static flag_desc pval_flags1_desc[] =
+{
+ { TR1_STEALTH, "Stealth" },
+ { TR1_SEARCH, "Searching" },
+ { TR1_INFRA, "Infravision" },
+ { TR1_TUNNEL, "Tunnelling" },
+ { TR1_BLOWS, "Attacks" },
+ { TR1_SPEED, "Speed" }
+};
+
+/*
+ * Slaying preferences for weapons
+ */
+
+static flag_desc slay_flags_desc[] =
+{
+ { TR1_SLAY_ANIMAL, "Animal" },
+ { TR1_SLAY_EVIL, "Evil" },
+ { TR1_SLAY_UNDEAD, "Undead" },
+ { TR1_SLAY_DEMON, "Demon" },
+ { TR1_SLAY_ORC, "Orc" },
+ { TR1_SLAY_TROLL, "Troll" },
+ { TR1_SLAY_GIANT, "Giant" },
+ { TR1_SLAY_DRAGON, "Dragon" },
+ { TR1_KILL_DRAGON, "Xdragon" }
+};
+
+/*
+ * Elemental brands for weapons
+ *
+ * Clearly, TR1_IMPACT is a bit out of place here. To simplify
+ * coding, it has been included here along with the elemental
+ * brands. It does seem to fit in with the brands and slaying
+ * more than the miscellaneous section.
+ */
+static flag_desc brand_flags_desc[] =
+{
+ { TR1_BRAND_ACID, "Acid Brand" },
+ { TR1_BRAND_ELEC, "Lightning Brand" },
+ { TR1_BRAND_FIRE, "Flame Tongue" },
+ { TR1_BRAND_COLD, "Frost Brand" },
+ { TR1_BRAND_POIS, "Poisoned" },
+
+ { TR1_CHAOTIC, "Mark of Chaos" },
+ { TR1_VAMPIRIC, "Vampiric" },
+ { TR1_IMPACT, "Earthquake impact on hit" },
+ { TR1_VORPAL, "Very sharp" },
+};
+
+
+/*
+ * The 15 resistables
+ */
+static const flag_desc resist_flags_desc[] =
+{
+ { TR2_RES_ACID, "Acid" },
+ { TR2_RES_ELEC, "Lightning" },
+ { TR2_RES_FIRE, "Fire" },
+ { TR2_RES_COLD, "Cold" },
+ { TR2_RES_POIS, "Poison" },
+ { TR2_RES_FEAR, "Fear"},
+ { TR2_RES_LITE, "Light" },
+ { TR2_RES_DARK, "Dark" },
+ { TR2_RES_BLIND, "Blindness" },
+ { TR2_RES_CONF, "Confusion" },
+ { TR2_RES_SOUND, "Sound" },
+ { TR2_RES_SHARDS, "Shards" },
+ { TR2_RES_NETHER, "Nether" },
+ { TR2_RES_NEXUS, "Nexus" },
+ { TR2_RES_CHAOS, "Chaos" },
+ { TR2_RES_DISEN, "Disenchantment" },
+};
+
+/*
+ * Elemental immunities (along with poison)
+ */
+
+static const flag_desc immune_flags_desc[] =
+{
+ { TR2_IM_ACID, "Acid" },
+ { TR2_IM_ELEC, "Lightning" },
+ { TR2_IM_FIRE, "Fire" },
+ { TR2_IM_COLD, "Cold" },
+};
+
+/*
+ * Sustain stats - these are given their "own" line in the
+ * spoiler file, mainly for simplicity
+ */
+static const flag_desc sustain_flags_desc[] =
+{
+ { TR2_SUST_STR, "STR" },
+ { TR2_SUST_INT, "INT" },
+ { TR2_SUST_WIS, "WIS" },
+ { TR2_SUST_DEX, "DEX" },
+ { TR2_SUST_CON, "CON" },
+ { TR2_SUST_CHR, "CHR" },
+};
+
+/*
+ * Miscellaneous magic given by an object's "flags2" field
+ */
+
+static const flag_desc misc_flags2_desc[] =
+{
+ { TR2_REFLECT, "Reflection" },
+ { TR2_FREE_ACT, "Free Action" },
+ { TR2_HOLD_LIFE, "Hold Life" },
+};
+
+/*
+ * Miscellaneous magic given by an object's "flags3" field
+ *
+ * Note that cursed artifacts and objects with permanent light
+ * are handled "directly" -- see analyze_misc_magic()
+ */
+
+static const flag_desc misc_flags3_desc[] =
+{
+ { TR3_SH_FIRE, "Fiery Aura" },
+ { TR3_SH_ELEC, "Electric Aura" },
+ { TR3_NO_TELE, "Prevent Teleportation" },
+ { TR3_NO_MAGIC, "Anti-Magic" },
+ { TR3_WRAITH, "Wraith Form" },
+ { TR3_FEATHER, "Levitation" },
+ { TR3_SEE_INVIS, "See Invisible" },
+ { TR3_SLOW_DIGEST, "Slow Digestion" },
+ { TR3_REGEN, "Regeneration" },
+ { TR3_XTRA_SHOTS, "+1 Extra Shot" }, /* always +1? */
+ { TR3_DRAIN_EXP, "Drains Experience" },
+ { TR3_AGGRAVATE, "Aggravates" },
+ { TR3_BLESSED, "Blessed Blade" },
+};
+
+
+/*
+ * A special type used just for dealing with pvals
+ */
+typedef struct
+{
+ /*
+ * This will contain a string such as "+2", "-10", etc.
+ */
+ char pval_desc[12];
+
+ /*
+ * A list of various player traits affected by an object's pval such
+ * as stats, speed, stealth, etc. "Extra attacks" is NOT included in
+ * this list since it will probably be desirable to format its
+ * description differently.
+ *
+ * Note that room need only be reserved for the number of stats - 1
+ * since the description "All stats" is used if an object affects all
+ * all stats. Also, room must be reserved for a sentinel NULL pointer.
+ *
+ * This will be a list such as ["STR", "DEX", "Stealth", NULL] etc.
+ *
+ * This list includes extra attacks, for simplicity.
+ */
+ cptr pval_affects[N_ELEMENTS(stat_flags_desc) - 1 +
+ N_ELEMENTS(pval_flags1_desc) + 1];
+
+}
+pval_info_type;
+
+
+/*
+ * An "object analysis structure"
+ *
+ * It will be filled with descriptive strings detailing an object's
+ * various magical powers. The "ignore X" traits are not noted since
+ * all artifacts ignore "normal" destruction.
+ */
+
+typedef struct
+{
+ /* "The Longsword Dragonsmiter (6d4) (+20, +25)" */
+ char description[160];
+
+ /* Description of what is affected by an object's pval */
+ pval_info_type pval_info;
+
+ /* A list of an object's slaying preferences */
+ cptr slays[N_ELEMENTS(slay_flags_desc) + 1];
+
+ /* A list if an object's elemental brands */
+ cptr brands[N_ELEMENTS(brand_flags_desc) + 1];
+
+ /* A list of immunities granted by an object */
+ cptr immunities[N_ELEMENTS(immune_flags_desc) + 1];
+
+ /* A list of resistances granted by an object */
+ cptr resistances[N_ELEMENTS(resist_flags_desc) + 1];
+
+ /* A list of stats sustained by an object */
+ cptr sustains[N_ELEMENTS(sustain_flags_desc) - 1 + 1];
+
+ /* A list of various magical qualities an object may have */
+ cptr misc_magic[N_ELEMENTS(misc_flags2_desc) + N_ELEMENTS(misc_flags3_desc)
+ + 1 /* Permanent Light */
+ + 1 /* type of curse */
+ + 1]; /* sentinel NULL */
+
+ /* A string describing an artifact's activation */
+ cptr activation;
+
+ /* "Level 20, Rarity 30, 3.0 lbs, 20000 Gold" */
+ char misc_desc[80];
+}
+obj_desc_list;
+
+
+
+
+
+
+/*
+ * This function does most of the actual "analysis". Given a set of bit flags
+ * (which will be from one of the flags fields from the object in question),
+ * a "flag description structure", a "description list", and the number of
+ * elements in the "flag description structure", this function sets the
+ * "description list" members to the appropriate descriptions contained in
+ * the "flag description structure".
+ *
+ * The possibly updated description pointer is returned.
+ */
+static cptr *spoiler_flag_aux(const u32b art_flags, const flag_desc *flag_ptr,
+ cptr *desc_ptr, const int n_elmnts)
+{
+ int i;
+
+ for (i = 0; i < n_elmnts; ++i)
+ {
+ if (art_flags & flag_ptr[i].flag)
+ {
+ *desc_ptr++ = flag_ptr[i].desc;
+ }
+ }
+
+ return desc_ptr;
+}
+
+
+/*
+ * Acquire a "basic" description "The Cloak of Death [1,+10]"
+ */
+static void analyze_general (object_type *o_ptr, char *desc_ptr)
+{
+ /* Get a "useful" description of the object */
+ object_desc_store(desc_ptr, o_ptr, TRUE, 1);
+}
+
+
+/*
+ * List "player traits" altered by an artifact's pval. These include stats,
+ * speed, infravision, tunnelling, stealth, searching, and extra attacks.
+ */
+static void analyze_pval (object_type *o_ptr, pval_info_type *p_ptr)
+{
+ const u32b all_stats = (TR1_STR | TR1_INT | TR1_WIS |
+ TR1_DEX | TR1_CON | TR1_CHR);
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ cptr *affects_list;
+
+ /* If pval == 0, there is nothing to do. */
+ if (!o_ptr->pval)
+ {
+ /* An "empty" pval description indicates that pval == 0 */
+ p_ptr->pval_desc[0] = '\0';
+ return;
+ }
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ affects_list = p_ptr->pval_affects;
+
+ /* Create the "+N" string */
+ sprintf(p_ptr->pval_desc, "%s%ld", POSITIZE(o_ptr->pval), (long int) o_ptr->pval);
+
+ /* First, check to see if the pval affects all stats */
+ if ((f1 & all_stats) == all_stats)
+ {
+ *affects_list++ = "All stats";
+ }
+
+ /* Are any stats affected? */
+ else if (f1 & all_stats)
+ {
+ affects_list = spoiler_flag_aux(f1, stat_flags_desc,
+ affects_list,
+ N_ELEMENTS(stat_flags_desc));
+ }
+
+ /* And now the "rest" */
+ affects_list = spoiler_flag_aux(f1, pval_flags1_desc,
+ affects_list,
+ N_ELEMENTS(pval_flags1_desc));
+
+ /* Terminate the description list */
+ *affects_list = NULL;
+}
+
+
+/* Note the slaying specialties of a weapon */
+static void analyze_slay (object_type *o_ptr, cptr *slay_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ slay_list = spoiler_flag_aux(f1, slay_flags_desc, slay_list,
+ N_ELEMENTS(slay_flags_desc));
+
+ /* Terminate the description list */
+ *slay_list = NULL;
+}
+
+/* Note an object's elemental brands */
+static void analyze_brand (object_type *o_ptr, cptr *brand_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ brand_list = spoiler_flag_aux(f1, brand_flags_desc, brand_list,
+ N_ELEMENTS(brand_flags_desc));
+
+ /* Terminate the description list */
+ *brand_list = NULL;
+}
+
+
+/* Note the resistances granted by an object */
+static void analyze_resist (object_type *o_ptr, cptr *resist_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ resist_list = spoiler_flag_aux(f2, resist_flags_desc,
+ resist_list, N_ELEMENTS(resist_flags_desc));
+
+ /* Terminate the description list */
+ *resist_list = NULL;
+}
+
+
+/* Note the immunities granted by an object */
+static void analyze_immune (object_type *o_ptr, cptr *immune_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ immune_list = spoiler_flag_aux(f2, immune_flags_desc,
+ immune_list, N_ELEMENTS(immune_flags_desc));
+
+ /* Terminate the description list */
+ *immune_list = NULL;
+}
+
+/* Note which stats an object sustains */
+
+static void analyze_sustains (object_type *o_ptr, cptr *sustain_list)
+{
+ const u32b all_sustains = (TR2_SUST_STR | TR2_SUST_INT | TR2_SUST_WIS |
+ TR2_SUST_DEX | TR2_SUST_CON | TR2_SUST_CHR);
+
+ u32b f1, f2, f3, f4, f5, esp;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Simplify things if an item sustains all stats */
+ if ((f2 & all_sustains) == all_sustains)
+ {
+ *sustain_list++ = "All stats";
+ }
+
+ /* Should we bother? */
+ else if ((f2 & all_sustains))
+ {
+ sustain_list = spoiler_flag_aux(f2, sustain_flags_desc,
+ sustain_list,
+ N_ELEMENTS(sustain_flags_desc));
+ }
+
+ /* Terminate the description list */
+ *sustain_list = NULL;
+}
+
+
+/*
+ * Note miscellaneous powers bestowed by an artifact such as see invisible,
+ * free action, permanent light, etc.
+ */
+static void analyze_misc_magic (object_type *o_ptr, cptr *misc_list)
+{
+ u32b f1, f2, f3, f4, f5, esp;
+ int radius = 0;
+
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ misc_list = spoiler_flag_aux(f2, misc_flags2_desc, misc_list,
+ N_ELEMENTS(misc_flags2_desc));
+
+ misc_list = spoiler_flag_aux(f3, misc_flags3_desc, misc_list,
+ N_ELEMENTS(misc_flags3_desc));
+
+ /*
+ * Glowing artifacts -- small radius light.
+ */
+
+ if (f3 & TR3_LITE1) radius++;
+ if (f4 & TR4_LITE2) radius += 2;
+ if (f4 & TR4_LITE3) radius += 3;
+
+ if (f4 & TR4_FUEL_LITE)
+ {
+ *misc_list++ = format("It provides light (radius %d) forever.", radius);
+ }
+ else
+ {
+ *misc_list++ = format("It provides light (radius %d) when fueled.", radius);
+ }
+
+ /*
+ * Handle cursed objects here to avoid redundancies such as noting
+ * that a permanently cursed object is heavily cursed as well as
+ * being "lightly cursed".
+ */
+
+ if (cursed_p(o_ptr))
+ {
+ if (f3 & (TR3_TY_CURSE))
+ {
+ *misc_list++ = "Ancient Curse";
+ }
+ if (f3 & (TR3_PERMA_CURSE))
+ {
+ *misc_list++ = "Permanently Cursed";
+ }
+ else if (f3 & (TR3_HEAVY_CURSE))
+ {
+ *misc_list++ = "Heavily Cursed";
+ }
+ else
+ {
+ *misc_list++ = "Cursed";
+ }
+ }
+
+ /* Terminate the description list */
+ *misc_list = NULL;
+}
+
+
+
+
+/*
+ * Determine the minimum depth an artifact can appear, its rarity, its weight,
+ * and its value in gold pieces
+ */
+static void analyze_misc (object_type *o_ptr, char *misc_desc)
+{
+ artifact_type *a_ptr = &a_info[o_ptr->name1];
+
+ sprintf(misc_desc, "Level %u, Rarity %u, %d.%d lbs, %ld Gold",
+ a_ptr->level, a_ptr->rarity,
+ a_ptr->weight / 10, a_ptr->weight % 10, (long int) a_ptr->cost);
+}
+
+
+/*
+ * Fill in an object description structure for a given object
+ */
+static void object_analyze(object_type *o_ptr, obj_desc_list *desc_ptr)
+{
+ analyze_general(o_ptr, desc_ptr->description);
+
+ analyze_pval(o_ptr, &desc_ptr->pval_info);
+
+ analyze_brand(o_ptr, desc_ptr->brands);
+
+ analyze_slay(o_ptr, desc_ptr->slays);
+
+ analyze_immune(o_ptr, desc_ptr->immunities);
+
+ analyze_resist(o_ptr, desc_ptr->resistances);
+
+ analyze_sustains(o_ptr, desc_ptr->sustains);
+
+ analyze_misc_magic(o_ptr, desc_ptr->misc_magic);
+
+ analyze_misc(o_ptr, desc_ptr->misc_desc);
+
+ desc_ptr->activation = item_activation(o_ptr, 0);
+}
+
+
+static void print_header(void)
+{
+ char buf[80];
+
+ sprintf(buf, "Artifact Spoilers for %s", get_version_string());
+ spoiler_underline(buf);
+}
+
+/*
+ * This is somewhat ugly.
+ *
+ * Given a header ("Resist", e.g.), a list ("Fire", "Cold", Acid", e.g.),
+ * and a separator character (',', e.g.), write the list to the spoiler file
+ * in a "nice" format, such as:
+ *
+ * Resist Fire, Cold, Acid
+ *
+ * That was a simple example, but when the list is long, a line wrap
+ * should occur, and this should induce a new level of indention if
+ * a list is being spread across lines. So for example, Bladeturner's
+ * list of resistances should look something like this
+ *
+ * Resist Acid, Lightning, Fire, Cold, Poison, Light, Dark, Blindness,
+ * Confusion, Sound, Shards, Nether, Nexus, Chaos, Disenchantment
+ *
+ * However, the code distinguishes between a single list of many items vs.
+ * many lists. (The separator is used to make this determination.) A single
+ * list of many items will not cause line wrapping (since there is no
+ * apparent reason to do so). So the lists of Ulmo's miscellaneous traits
+ * might look like this:
+ *
+ * Free Action; Hold Life; See Invisible; Slow Digestion; Regeneration
+ * Blessed Blade
+ *
+ * So comparing the two, "Regeneration" has no trailing separator and
+ * "Blessed Blade" was not indented. (Also, Ulmo's lists have no headers,
+ * but that's not relevant to line wrapping and indention.)
+ */
+
+/* ITEM_SEP separates items within a list */
+#define ITEM_SEP ','
+
+
+/* LIST_SEP separates lists */
+#define LIST_SEP ';'
+
+
+/* Create a spoiler file entry for an artifact */
+
+static void spoiler_print_art(obj_desc_list *art_ptr, int name1, int set, object_type *o_ptr)
+{
+ /* Don't indent the first line */
+ fprintf(fff, "%s\n ", art_ptr->description);
+ text_out_indent = 4;
+ object_out_desc(o_ptr, fff, FALSE, TRUE);
+ text_out_indent = 0;
+
+ /* End with the miscellaneous facts */
+ fprintf(fff, "%s%s\n\n", INDENT1, art_ptr->misc_desc);
+}
+
+
+/*
+ * Hack -- Create a "forged" artifact
+ */
+static bool_ make_fake_artifact(object_type *o_ptr, int name1)
+{
+ int i;
+ int cur;
+
+ artifact_type *a_ptr = &a_info[name1];
+
+
+ /* Ignore "empty" artifacts */
+ if (!a_ptr->name) return FALSE;
+
+ /* Acquire the "kind" index */
+ i = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Oops */
+ if (!i) return (FALSE);
+
+ /* Create the artifact */
+ object_prep(o_ptr, i);
+
+ /* Save the name */
+ o_ptr->name1 = name1;
+
+ /* Keep the One Ring untouched by apply_magic */
+ if (name1 != ART_POWER)
+ {
+ cur = a_ptr->cur_num;
+ apply_magic(o_ptr, -1, TRUE, TRUE, TRUE);
+ a_ptr->cur_num = cur;
+ }
+ else
+ {
+ o_ptr->pval = a_ptr->pval;
+ }
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * Create a spoiler file for artifacts
+ */
+static void spoil_artifact(cptr fname)
+{
+ int i, j;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ obj_desc_list artifact;
+
+ char buf[1024];
+
+
+ /* Build the filename */
+ path_build(buf, sizeof(buf), ANGBAND_DIR_USER, fname);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Dump the header */
+ print_header();
+
+ /* List the artifacts by tval */
+ for (i = 0; group_artifact[i].tval; i++)
+ {
+ /* Write out the group title */
+ if (group_artifact[i].name)
+ {
+ spoiler_blanklines(2);
+ spoiler_underline(group_artifact[i].name);
+ spoiler_blanklines(1);
+ }
+
+ /* Now search through all of the artifacts */
+ for (j = 1; j < max_a_idx; ++j)
+ {
+ artifact_type *a_ptr = &a_info[j];
+
+ /* We only want objects in the current group */
+ if (a_ptr->tval != group_artifact[i].tval) continue;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Attempt to "forge" the artifact */
+ if (!make_fake_artifact(q_ptr, j)) continue;
+
+ /* Aware and Known */
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+
+ /* Analyze the artifact */
+ object_analyze(q_ptr, &artifact);
+
+ /* Write out the artifact description to the spoiler file */
+ spoiler_print_art(&artifact, j, a_ptr->set, q_ptr);
+ }
+ }
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+
+
+
+/*
+ * Create a spoiler file for monsters -BEN-
+ */
+static void spoil_mon_desc(cptr fname)
+{
+ 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);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Allocate the "who" array */
+ std::vector<s16b> who;
+
+ /* Dump the header */
+ sprintf(buf, "Monster Spoilers for %s", get_version_string());
+ spoiler_underline(buf);
+ spoiler_blanklines(2);
+
+ /* Dump the header */
+ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n",
+ "Name", "Lev", "Rar", "Spd", "Hp", "Ac", "Visual Info");
+ fprintf(fff, "%-40.40s%4s%4s%6s%8s%4s %11.11s\n",
+ "----", "---", "---", "---", "--", "--", "-----------");
+
+
+ /* Scan the monsters */
+ for (size_t i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ /* Use that monster */
+ if (r_ptr->name) {
+ who.push_back(i);
+ }
+ }
+
+
+ /* Scan again */
+ for (auto const who_i : who)
+ {
+ monster_race *r_ptr = &r_info[who_i];
+
+ /* Get the "name" */
+ if (r_ptr->flags1 & (RF1_UNIQUE))
+ {
+ sprintf(nam, "[U] %s", r_ptr->name);
+ }
+ else
+ {
+ sprintf(nam, "The %s", r_ptr->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");
+
+ /* 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);
+
+ /* Open the file */
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+
+ /* Dump the header */
+ sprintf(buf, "Monster Spoilers for %s", get_version_string());
+ spoiler_underline(buf);
+ spoiler_blanklines(2);
+
+ /*
+ * List all monsters in order.
+ */
+ for (n = 1; n < max_r_idx; n++)
+ {
+ monster_race *r_ptr = &r_info[n];
+
+ /* Extract the flags */
+ flags1 = r_ptr->flags1;
+ flags2 = r_ptr->flags2;
+ flags3 = r_ptr->flags3;
+ flags4 = r_ptr->flags4;
+ flags5 = r_ptr->flags5;
+ flags6 = r_ptr->flags6;
+ flags9 = r_ptr->flags9;
+ breath = FALSE;
+ magic = FALSE;
+
+ /* Extract a gender (if applicable) */
+ if (flags1 & (RF1_FEMALE)) msex = 2;
+ else if (flags1 & (RF1_MALE)) msex = 1;
+ else msex = 0;
+
+
+ /* Prefix */
+ if (flags1 & (RF1_UNIQUE))
+ {
+ spoil_out("[U] ");
+ }
+ else
+ {
+ spoil_out("The ");
+ }
+
+ /* Name */
+ sprintf(buf, "%s (", r_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_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.");
+}
+
+/*
+ * 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);
+ 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);
+
+ fff = my_fopen(buf, "w");
+
+ /* Oops */
+ if (!fff)
+ {
+ msg_print("Cannot create spoiler file.");
+ return;
+ }
+
+ /* Dump the header */
+ sprintf(buf, "Spell Spoiler (Skill Level 50) for %s", get_version_string());
+ spoiler_underline(buf);
+
+ /* Dump the bookless magic powers in alphabetical order */
+
+ /* Mimicry */
+ print_magic_powers(mimic_powers, MAX_MIMIC_POWERS, mimic_info, SKILL_MIMICRY);
+
+ /* Mindcraft */
+ print_magic_powers(mindcraft_powers, MAX_MINDCRAFT_POWERS, mindcraft_info, SKILL_MINDCRAFT);
+
+ /* Necromancy */
+ print_magic_powers(necro_powers, MAX_NECRO_POWERS, necro_info, SKILL_NECROMANCY);
+
+ /* Symbiosis */
+ print_magic_powers(symbiotic_powers, MAX_SYMBIOTIC_POWERS, symbiotic_info, SKILL_SYMBIOTIC);
+
+ /* Check for errors */
+ if (ferror(fff) || my_fclose(fff))
+ {
+ msg_print("Cannot close spoiler file.");
+ return;
+ }
+
+ /* Message */
+ msg_print("Successfully created a spoiler file.");
+}
+
+
+/*
+ * Create Spoiler files -BEN-
+ */
+void do_cmd_spoilers()
+{
+ 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) 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_spells("spell.spo");
+ }
+
+ /* Oops */
+ else
+ {
+ bell();
+ }
+
+ /* Flush messages */
+ msg_print(NULL);
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Leave "icky" mode */
+ character_icky = FALSE;
+}
diff --git a/src/wizard1.hpp b/src/wizard1.hpp
new file mode 100644
index 00000000..0429aa70
--- /dev/null
+++ b/src/wizard1.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+void do_cmd_spoilers();
diff --git a/src/wizard2.cc b/src/wizard2.cc
new file mode 100644
index 00000000..837d778b
--- /dev/null
+++ b/src/wizard2.cc
@@ -0,0 +1,1868 @@
+/*
+ * 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 "wizard2.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "cmd4.hpp"
+#include "corrupt.hpp"
+#include "dungeon_info_type.hpp"
+#include "files.hpp"
+#include "hooks.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "player_type.hpp"
+#include "randart.hpp"
+#include "status.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "traps.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+#include "wizard1.hpp"
+#include "xtra1.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+/*
+ * Adds a lvl to a monster
+ */
+static 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);
+ }
+}
+
+static 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
+ */
+static void teleport_player_town(int town)
+{
+ int x = 0, y = 0;
+
+ autosave_checkpoint();
+
+ /* Change town */
+ dun_level = 0;
+ p_ptr->town_num = town;
+
+ for (x = 0; x < max_wild_x; x++)
+ for (y = 0; y < max_wild_y; y++)
+ if (p_ptr->town_num == wf_info[wild_map[y][x].feat].entrance) goto finteletown;
+finteletown:
+ p_ptr->wilderness_y = y;
+ p_ptr->wilderness_x = x;
+
+ leaving_quest = p_ptr->inside_quest;
+ p_ptr->inside_quest = 0;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Hack -- Rerate Hitpoints
+ */
+void do_cmd_rerate(void)
+{
+ int min_value, max_value, i, percent;
+
+ min_value = (PY_MAX_LEVEL * 3 * (p_ptr->hitdie - 1)) / 8;
+ min_value += PY_MAX_LEVEL;
+
+ max_value = (PY_MAX_LEVEL * 5 * (p_ptr->hitdie - 1)) / 8;
+ max_value += PY_MAX_LEVEL;
+
+ player_hp[0] = p_ptr->hitdie;
+
+ /* Rerate */
+ while (1)
+ {
+ /* Collect values */
+ for (i = 1; i < PY_MAX_LEVEL; i++)
+ {
+ player_hp[i] = randint(p_ptr->hitdie);
+ player_hp[i] += player_hp[i - 1];
+ }
+
+ /* Legal values */
+ if ((player_hp[PY_MAX_LEVEL - 1] >= min_value) &&
+ (player_hp[PY_MAX_LEVEL - 1] <= max_value)) break;
+ }
+
+ percent = (int)(((long)player_hp[PY_MAX_LEVEL - 1] * 200L) /
+ (p_ptr->hitdie + ((PY_MAX_LEVEL - 1) * p_ptr->hitdie)));
+
+ /* Update and redraw hitpoints */
+ p_ptr->update |= (PU_HP);
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Message */
+ msg_format("Current Life Rating is %d/100.", percent);
+}
+
+
+/*
+ * Create the artifact of the specified number -- DAN
+ *
+ */
+static void wiz_create_named_art()
+{
+ object_type forge;
+ object_type *q_ptr;
+ int i, a_idx;
+ cptr p = "Number of the artifact :";
+ char out_val[80] = "";
+ artifact_type *a_ptr;
+
+ if (!get_string(p, out_val, 4)) return;
+ a_idx = atoi(out_val);
+
+ /* Return if out-of-bounds */
+ if ((a_idx <= 0) || (a_idx >= max_a_idx)) return;
+
+ a_ptr = &a_info[a_idx];
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Ignore "empty" artifacts */
+ if (!a_ptr->name) return;
+
+ /* Acquire the "kind" index */
+ i = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Oops */
+ if (!i) return;
+
+ /* Create the artifact */
+ object_prep(q_ptr, i);
+
+ /* Save the name */
+ q_ptr->name1 = a_idx;
+
+ apply_magic(q_ptr, -1, TRUE, TRUE, TRUE);
+
+ /* Identify it fully */
+ object_aware(q_ptr);
+ object_known(q_ptr);
+
+ /* Mark the item as fully known */
+ q_ptr->ident |= (IDENT_MENTAL);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+
+ /* All done */
+ msg_print("Allocated.");
+}
+
+/* Summon a horde of monsters */
+static void do_cmd_summon_horde()
+{
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 1000;
+
+ while (--attempts)
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 3);
+ if (cave_naked_bold(wy, wx)) break;
+ }
+
+ (void)alloc_horde(wy, wx);
+}
+
+
+/*
+ * 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, "%ld", (long) (p_ptr->grace));
+
+ /* Query */
+ if (!get_string("Piety: ", tmp_val, 9)) return;
+
+ /* Extract */
+ tmp_long = atol(tmp_val);
+
+ /* Verify */
+ if (tmp_long < 0) tmp_long = 0L;
+
+ /* Save */
+ p_ptr->grace = tmp_long;
+
+
+ /* 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);
+}
+
+
+
+/*
+ * 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_ptr->name;
+
+
+ /* Skip past leading characters */
+ while ((*str == ' ') || (*str == '&')) str++;
+
+ /* Copy useful chars */
+ for (t = buf; *str; str++)
+ {
+ if (*str != '~') *t++ = *str;
+ }
+
+ /* Terminate the new name */
+ *t = '\0';
+}
+
+
+/*
+ * Hack -- title for each column
+ *
+ * XXX XXX XXX This will not work with "EBCDIC", I would think.
+ */
+static char head[4] = { 'a', 'A', '0', ':' };
+
+
+/*
+ * Print a string as required by wiz_create_itemtype().
+ * Trims characters from the beginning until it fits in the space
+ * before the next row or the edge of the screen.
+ */
+static void wci_string(cptr string, int num)
+{
+ int row = 2 + (num % 20), col = 30 * (num / 20);
+ int ch = head[num / 20] + (num % 20), max_len = 0;
+
+ if (76-col < (signed)max_len)
+ max_len = 76-col;
+ else
+ max_len = 30-6;
+
+ if (strlen(string) > (unsigned)max_len)
+ string = string + (strlen(string) - max_len);
+
+ prt(format("[%c] %s", ch, string), row, col);
+}
+
+/*
+ * Specify tval and sval (type and subtype of object) originally
+ * by RAK, heavily modified by -Bernd-
+ *
+ * This function returns the k_idx of an object type, or zero if failed
+ *
+ * List up to 50 choices in three columns
+ */
+static int wiz_create_itemtype(void)
+{
+ int i, num, max_num;
+ int tval;
+
+ cptr tval_desc2;
+ char ch;
+
+ int choice[60];
+
+ char buf[160];
+
+
+ /* Clear screen */
+ Term_clear();
+
+ /* Print all tval's and their descriptions */
+ for (num = 0; (num < 60) && tvals[num].tval; num++)
+ {
+ wci_string(tvals[num].desc, num);
+ }
+
+ /* Me need to know the maximal possible tval_index */
+ max_num = num;
+
+ /* Choose! */
+ if (!get_com("Get what type of object? ", &ch)) return (0);
+
+ /* Analyze choice */
+ num = -1;
+ if ((ch >= head[0]) && (ch < head[0] + 20)) num = ch - head[0];
+ if ((ch >= head[1]) && (ch < head[1] + 20)) num = ch - head[1] + 20;
+ if ((ch >= head[2]) && (ch < head[2] + 17)) num = ch - head[2] + 40;
+
+ /* Bail out if choice is illegal */
+ if ((num < 0) || (num >= max_num)) return (0);
+
+ /* Base object type chosen, fill in tval */
+ tval = tvals[num].tval;
+ tval_desc2 = tvals[num].desc;
+
+
+ /*** And now we go for k_idx ***/
+
+ /* Clear screen */
+ Term_clear();
+
+ /* We have to search the whole itemlist. */
+ for (num = 0, i = 1; (num < 60) && (i < max_k_idx); i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Analyze matching items */
+ if (k_ptr->tval == tval)
+ {
+ /* Hack -- Skip instant artifacts */
+ if (k_ptr->flags3 & (TR3_INSTA_ART)) continue;
+
+ /* Acquire the "name" of object "i" */
+ strip_name(buf, i);
+
+ /* Print it */
+ wci_string(buf, num);
+
+ /* Remember the object index */
+ choice[num++] = i;
+ }
+ }
+
+ /* Me need to know the maximal possible remembered object_index */
+ max_num = num;
+
+ /* Choose! */
+ if (!get_com(format("What Kind of %s? ", tval_desc2), &ch)) return (0);
+
+ /* Analyze choice */
+ num = -1;
+ if ((ch >= head[0]) && (ch < head[0] + 20)) num = ch - head[0];
+ if ((ch >= head[1]) && (ch < head[1] + 20)) num = ch - head[1] + 20;
+ if ((ch >= head[2]) && (ch < head[2] + 17)) num = ch - head[2] + 40;
+
+ /* Bail out if choice is "illegal" */
+ if ((num < 0) || (num >= max_num)) return (0);
+
+ /* And return successful */
+ return (choice[num]);
+}
+
+
+/*
+ * Tweak an item
+ */
+static void wiz_tweak_item(object_type *o_ptr)
+{
+ cptr p;
+ char tmp_val[80];
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Extract the flags */
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+
+ p = "Enter new 'pval' setting: ";
+ sprintf(tmp_val, "%ld", (long int) o_ptr->pval);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->pval = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'pval2' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->pval2);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->pval2 = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'pval3' setting: ";
+ sprintf(tmp_val, "%ld", (long int) o_ptr->pval3);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->pval3 = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'to_a' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->to_a);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->to_a = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'to_h' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->to_h);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->to_h = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'to_d' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->to_d);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->to_d = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'name2' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->name2);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->name2 = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'name2b' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->name2b);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->name2b = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'sval' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->sval);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->sval = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+
+ p = "Enter new 'obj exp' setting: ";
+ sprintf(tmp_val, "%ld", (long int) o_ptr->exp);
+ if (!get_string(p, tmp_val, 9)) return;
+ wiz_display_item(o_ptr);
+ o_ptr->exp = atoi(tmp_val);
+ if (f4 & TR4_LEVELS) check_experience_obj(o_ptr);
+
+ p = "Enter new 'timeout' setting: ";
+ sprintf(tmp_val, "%d", o_ptr->timeout);
+ if (!get_string(p, tmp_val, 5)) return;
+ o_ptr->timeout = atoi(tmp_val);
+ wiz_display_item(o_ptr);
+}
+
+
+/*
+ * Apply magic to an item or turn it into an artifact. -Bernd-
+ */
+static void wiz_reroll_item(object_type *o_ptr)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ char ch;
+
+ bool_ changed = FALSE;
+
+
+ /* Hack -- leave artifacts alone */
+ if (artifact_p(o_ptr) || o_ptr->art_name) return;
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Copy the object */
+ object_copy(q_ptr, o_ptr);
+
+
+ /* Main loop. Ask for magification and artifactification */
+ while (TRUE)
+ {
+ /* Display full item debug information */
+ wiz_display_item(q_ptr);
+
+ /* Ask wizard what to do. */
+ if (!get_com("[a]ccept, [b]ad, [n]ormal, [g]ood, [e]xcellent, [r]andart? ", &ch))
+ {
+ changed = FALSE;
+ break;
+ }
+
+ /* Create/change it! */
+ if (ch == 'A' || ch == 'a')
+ {
+ changed = TRUE;
+ break;
+ }
+
+ /* Apply bad magic, but first clear object */
+ else if (ch == 'b' || ch == 'B')
+ {
+ object_prep(q_ptr, o_ptr->k_idx);
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE, boost::make_optional(-2));
+ }
+
+ /* 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;
+ const 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))
+ {
+ /* Allow interupt */
+ if (inkey_scan())
+ {
+ /* Flush */
+ flush();
+
+ /* Stop rolling */
+ break;
+ }
+
+ /* Dump the stats */
+ prt(format(q, i, matches, better, worse, other), 0, 0);
+ Term_fresh();
+ }
+
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Create an object */
+ make_object(q_ptr, good, great, theme);
+
+
+ /* XXX XXX XXX Mega-Hack -- allow multiple artifacts */
+ if (artifact_p(q_ptr))
+ {
+ if (q_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[q_ptr->sval].generated = FALSE;
+ }
+ else
+ {
+ a_info[q_ptr->name1].cur_num = 0;
+ }
+ }
+
+
+ /* Test for the same tval and sval. */
+ if ((o_ptr->tval) != (q_ptr->tval)) continue;
+ if ((o_ptr->sval) != (q_ptr->sval)) continue;
+
+ /* Check for match */
+ if ((q_ptr->pval == o_ptr->pval) &&
+ (q_ptr->to_a == o_ptr->to_a) &&
+ (q_ptr->to_h == o_ptr->to_h) &&
+ (q_ptr->to_d == o_ptr->to_d))
+ {
+ matches++;
+ }
+
+ /* Check for better */
+ else if ((q_ptr->pval >= o_ptr->pval) &&
+ (q_ptr->to_a >= o_ptr->to_a) &&
+ (q_ptr->to_h >= o_ptr->to_h) &&
+ (q_ptr->to_d >= o_ptr->to_d))
+ {
+ better++;
+ }
+
+ /* Check for worse */
+ else if ((q_ptr->pval <= o_ptr->pval) &&
+ (q_ptr->to_a <= o_ptr->to_a) &&
+ (q_ptr->to_h <= o_ptr->to_h) &&
+ (q_ptr->to_d <= o_ptr->to_d))
+ {
+ worse++;
+ }
+
+ /* Assume different */
+ else
+ {
+ other++;
+ }
+ }
+
+ /* Final dump */
+ msg_format(q, i, matches, better, worse, other);
+ msg_print(NULL);
+ }
+
+
+ /* Hack -- Normally only make a single artifact */
+ if (artifact_p(o_ptr))
+ {
+ if (o_ptr->tval == TV_RANDART)
+ {
+ random_artifacts[o_ptr->sval].generated = TRUE;
+ }
+ else
+ {
+ a_info[o_ptr->name1].cur_num = 1;
+ }
+ }
+}
+
+
+/*
+ * Change the quantity of a the item
+ */
+static void wiz_quantity_item(object_type *o_ptr)
+{
+ int tmp_int;
+ char tmp_val[100];
+
+ /* Default */
+ sprintf(tmp_val, "%d", o_ptr->number);
+
+ /* Query */
+ if (get_string("Quantity: ", tmp_val, 2))
+ {
+ /* Extract */
+ tmp_int = atoi(tmp_val);
+
+ /* Paranoia */
+ if (tmp_int < 1) tmp_int = 1;
+ if (tmp_int > 99) tmp_int = 99;
+
+ /* Accept modifications */
+ o_ptr->number = tmp_int;
+ }
+}
+
+
+
+/*
+ * Play with an item. Options include:
+ * - Output statistics (via wiz_roll_item)
+ * - Reroll item (via wiz_reroll_item)
+ * - Change properties (via wiz_tweak_item)
+ * - Change the number of items (via wiz_quantity_item)
+ */
+static void do_cmd_wiz_play(void)
+{
+ /* Get an item */
+ int item;
+ if (!get_item(&item,
+ "Play with which object? ",
+ "You have nothing to play with.",
+ (USE_EQUIP | USE_INVEN | USE_FLOOR)))
+ {
+ return;
+ }
+
+ /* Get the item */
+ object_type *o_ptr = get_object(item);
+
+
+ /* The item was not changed */
+ bool_ changed = FALSE;
+
+
+ /* Icky */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+
+ /* Get local object */
+ object_type forge;
+ object_type *q_ptr = &forge;
+
+ /* Copy object */
+ object_copy(q_ptr, o_ptr);
+
+
+ /* The main loop */
+ while (TRUE)
+ {
+ char ch;
+
+ /* Display the item */
+ wiz_display_item(q_ptr);
+
+ /* Get choice */
+ if (!get_com("[a]ccept [s]tatistics [r]eroll [t]weak [q]uantity apply[m]agic? ", &ch))
+ {
+ changed = FALSE;
+ break;
+ }
+
+ if (ch == 'A' || ch == 'a')
+ {
+ changed = TRUE;
+ break;
+ }
+
+ if (ch == 's' || ch == 'S')
+ {
+ wiz_statistics(q_ptr);
+ }
+
+ if (ch == 'r' || ch == 'r')
+ {
+ wiz_reroll_item(q_ptr);
+ }
+
+ if (ch == 't' || ch == 'T')
+ {
+ wiz_tweak_item(q_ptr);
+ }
+
+ if (ch == 'q' || ch == 'Q')
+ {
+ wiz_quantity_item(q_ptr);
+ }
+
+ if (ch == 'm' || ch == 'M')
+ {
+ int e = q_ptr->name2, eb = q_ptr->name2b;
+
+ object_prep(q_ptr, q_ptr->k_idx);
+ q_ptr->name2 = e;
+ q_ptr->name2b = eb;
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+ }
+ }
+
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Not Icky */
+ character_icky = FALSE;
+
+
+ /* Accept change */
+ if (changed)
+ {
+ /* Message */
+ msg_print("Changes accepted.");
+
+ /* Change */
+ object_copy(o_ptr, q_ptr);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Combine / Reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+ }
+
+ /* Ignore change */
+ else
+ {
+ msg_print("Changes ignored.");
+ }
+}
+
+
+/*
+ * Wizard routine for creating objects -RAK-
+ * Heavily modified to allow magification and artifactification -Bernd-
+ *
+ * Note that wizards cannot create objects on top of other objects.
+ *
+ * Hack -- this routine always makes a "dungeon object", and applies
+ * magic to it, and attempts to decline cursed items.
+ */
+static void wiz_create_item(void)
+{
+ object_type forge;
+ object_type *q_ptr;
+
+ int k_idx;
+
+
+ /* Icky */
+ character_icky = TRUE;
+
+ /* Save the screen */
+ Term_save();
+
+ /* Get object base type */
+ k_idx = wiz_create_itemtype();
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Not Icky */
+ character_icky = FALSE;
+
+
+ /* Return if failed */
+ if (!k_idx) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create the item */
+ object_prep(q_ptr, k_idx);
+
+ /* Apply magic (no messages, no artifacts) */
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Drop the object from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+
+ /* All done */
+ msg_print("Allocated.");
+}
+
+/*
+ * As above, but takes the k_idx as a parameter instead of using menus.
+ */
+static void wiz_create_item_2(void)
+{
+ object_type forge;
+ object_type *q_ptr;
+ int a_idx;
+ cptr p = "Number of the object :";
+ char out_val[80] = "";
+
+ if (!get_string(p, out_val, 4)) return;
+ a_idx = atoi(out_val);
+
+ /* Return if failed or out-of-bounds */
+ if ((a_idx <= 0) || (a_idx >= max_k_idx)) return;
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Create the item */
+ object_prep(q_ptr, a_idx);
+
+ /* Apply magic (no messages, no artifacts) */
+ apply_magic(q_ptr, dun_level, FALSE, FALSE, FALSE);
+
+ /* Drop the object from heaven */
+ drop_near(q_ptr, -1, p_ptr->py, p_ptr->px);
+
+ /* All done */
+ msg_print("Allocated.");
+}
+
+
+/*
+ * Cure everything instantly
+ */
+void do_cmd_wiz_cure_all(void)
+{
+ object_type *o_ptr;
+
+ /* Remove curses */
+ (void)remove_all_curse();
+
+ /* Restore stats */
+ (void)res_stat(A_STR, TRUE);
+ (void)res_stat(A_INT, TRUE);
+ (void)res_stat(A_WIS, TRUE);
+ (void)res_stat(A_CON, TRUE);
+ (void)res_stat(A_DEX, TRUE);
+ (void)res_stat(A_CHR, TRUE);
+
+ /* Restore the level */
+ (void)restore_level();
+
+ /* Heal the player */
+ p_ptr->chp = p_ptr->mhp;
+ p_ptr->chp_frac = 0;
+
+ /* Cure insanity of player */
+ p_ptr->csane = p_ptr->msane;
+ p_ptr->csane_frac = 0;
+
+ /* Heal the player monster */
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+ if (o_ptr->k_idx)
+ {
+ o_ptr->pval2 = o_ptr->pval3;
+ }
+
+ /* Restore mana */
+ p_ptr->csp = p_ptr->msp;
+ p_ptr->csp_frac = 0;
+
+ /* Cure stuff */
+ (void)set_blind(0);
+ (void)set_confused(0);
+ (void)set_poisoned(0);
+ (void)set_afraid(0);
+ (void)set_paralyzed(0);
+ (void)set_image(0);
+ (void)set_stun(0);
+ (void)set_cut(0);
+ (void)set_slow(0);
+ p_ptr->black_breath = FALSE;
+
+ /* No longer hungry */
+ (void)set_food(PY_FOOD_MAX - 1);
+
+ /* Redraw everything */
+ do_cmd_redraw();
+}
+
+
+/*
+ * Go to any level
+ */
+static void do_cmd_wiz_jump(void)
+{
+ /* Ask for level */
+ if (command_arg <= 0)
+ {
+ char ppp[80];
+
+ char tmp_val[160];
+
+ /* Prompt */
+ msg_format("dungeon_type : %d", dungeon_type);
+ sprintf(ppp, "Jump to level (0-%d): ", d_info[dungeon_type].maxdepth);
+
+ /* Default */
+ sprintf(tmp_val, "%d", dun_level);
+
+ /* Ask for a level */
+ if (!get_string(ppp, tmp_val, 10)) return;
+
+ /* Extract request */
+ command_arg = atoi(tmp_val);
+ }
+
+ /* Paranoia */
+ if (command_arg < 0) command_arg = 0;
+
+ /* Paranoia */
+ if (command_arg > d_info[dungeon_type].maxdepth) command_arg = d_info[dungeon_type].maxdepth;
+
+ /* Accept request */
+ msg_format("You jump to dungeon level %d.", command_arg);
+
+ autosave_checkpoint();
+
+ /* Change level */
+ dun_level = command_arg;
+
+ leaving_quest = p_ptr->inside_quest;
+
+ p_ptr->inside_quest = 0;
+
+ /* Leaving */
+ p_ptr->leaving = TRUE;
+}
+
+
+/*
+ * Become aware of a lot of objects
+ */
+static void do_cmd_wiz_learn(void)
+{
+ int i;
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Scan every object */
+ for (i = 1; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ /* Induce awareness */
+ if (k_ptr->level <= command_arg)
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Prepare object */
+ object_prep(q_ptr, i);
+
+ /* Awareness */
+ object_aware(q_ptr);
+ }
+ }
+}
+
+
+/*
+ * Summon some creatures
+ */
+static void do_cmd_wiz_summon(int num)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ {
+ (void)summon_specific(p_ptr->py, p_ptr->px, dun_level, 0);
+ }
+}
+
+
+/*
+ * Summon a creature of the specified type
+ *
+ * XXX XXX XXX This function is rather dangerous
+ */
+static void do_cmd_wiz_named(int r_idx, bool_ slp)
+{
+ int i, x, y;
+
+ /* Paranoia */
+ /* if (!r_idx) return; */
+
+ /* Prevent illegal monsters */
+ if (r_idx >= max_r_idx) return;
+
+ /* Try 10 times */
+ for (i = 0; i < 10; i++)
+ {
+ int d = 1;
+
+ /* Pick a location */
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ /* Require empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Place it (allow groups) */
+ m_allow_special[r_idx] = TRUE;
+ int m_idx = place_monster_aux(y, x, r_idx, slp, TRUE, MSTATUS_ENEMY);
+ m_allow_special[r_idx] = FALSE;
+
+ // If summoning succeeded, we stop.
+ if (m_idx)
+ {
+ break;
+ }
+ }
+}
+
+
+/*
+ * 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 */
+ for (i = 0; i < 10; i++)
+ {
+ int d = 1;
+
+ /* Pick a location */
+ scatter(&y, &x, p_ptr->py, p_ptr->px, d);
+
+ /* Require empty grids */
+ if (!cave_empty_bold(y, x)) continue;
+
+ /* Place it (allow groups) */
+ m_allow_special[r_idx] = TRUE;
+ int m_idx = place_monster_aux(y, x, r_idx, slp, TRUE, MSTATUS_PET);
+ m_allow_special[r_idx] = FALSE;
+
+ // Stop if we succeeded
+ if (m_idx)
+ {
+ break;
+ }
+ }
+}
+
+
+
+/*
+ * 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);
+ }
+}
+
+
+static 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();
+}
+
+
+/*
+ * Ask for and parse a "debug command"
+ * The "command_arg" may have been set.
+ */
+void do_cmd_debug()
+{
+ int x, y;
+ char cmd;
+
+
+ /* Get a "debug command" */
+ get_com("Debug Command: ", &cmd);
+
+ /* Analyze the command */
+ switch (cmd)
+ {
+ /* Nothing */
+ case ESCAPE:
+ case ' ':
+ case '\n':
+ case '\r':
+ break;
+
+
+ /* Hack -- Generate Spoilers */
+ case '"':
+ do_cmd_spoilers();
+ break;
+
+ case 'A':
+ status_main();
+ break;
+
+ /* Hack -- Help */
+ case '?':
+ do_cmd_help();
+ break;
+
+
+ /* Cure all maladies */
+ case 'a':
+ do_cmd_wiz_cure_all();
+ break;
+
+ /* Teleport to target */
+ case 'b':
+ do_cmd_wiz_bamf();
+ break;
+
+ case 'B':
+ do_cmd_wiz_body(command_arg);
+ break;
+
+ /* Create any object */
+ case '-':
+ wiz_create_item_2();
+ break;
+
+ /* Create any object */
+ case 'c':
+ wiz_create_item();
+ break;
+
+ /* Create a named artifact */
+ case 'C':
+ wiz_create_named_art();
+ break;
+
+ /* Detect everything */
+ case 'd':
+ detect_all(DEFAULT_RADIUS);
+ break;
+
+ /* Change of Dungeon type */
+ case 'D':
+ if ((command_arg >= 0) && (command_arg < max_d_idx))
+ {
+ dungeon_type = command_arg;
+ dun_level = d_info[dungeon_type].mindepth;
+ msg_format("You go into %s", d_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;
+
+ case 'H':
+ do_cmd_summon_horde(); break;
+
+ /* 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':
+ gain_random_corruption();
+ break;
+
+ /* Create a trap */
+ case 'R':
+ wiz_place_trap(p_ptr->py, p_ptr->px, command_arg);
+ break;
+
+ /* Summon _friendly_ named monster */
+ case 'N':
+ do_cmd_wiz_named_friendly(command_arg, TRUE);
+ break;
+
+ /* Summon Named Monster */
+ case 'n':
+ do_cmd_wiz_named(command_arg, TRUE);
+ break;
+
+ /* Object playing routines */
+ case 'o':
+ do_cmd_wiz_play();
+ break;
+
+ /* Phase Door */
+ case 'p':
+ teleport_player(10);
+ break;
+
+ /* get a Quest */
+ case 'q':
+ {
+ /* if (quest[command_arg].status == QUEST_STATUS_UNTAKEN)*/
+ if ((command_arg >= 1) && (command_arg < MAX_Q_IDX) && (command_arg != QUEST_RANDOM))
+ {
+ quest[command_arg].status = QUEST_STATUS_TAKEN;
+ *(quest[command_arg].plot) = command_arg;
+ 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_FRAME);
+
+ /* 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;
+
+ /* Mimic shape changing */
+ case '*':
+ p_ptr->tim_mimic = 100;
+ p_ptr->mimic_form = command_arg;
+ /* Redraw title */
+ p_ptr->redraw |= (PR_FRAME);
+ /* 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;
+
+ /* Not a Wizard Command */
+ default:
+ msg_print("That is not a valid debug command.");
+ break;
+ }
+}
diff --git a/src/wizard2.hpp b/src/wizard2.hpp
new file mode 100644
index 00000000..cec515c8
--- /dev/null
+++ b/src/wizard2.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "h-basic.h"
+
+extern void do_cmd_rerate(void);
+extern void do_cmd_wiz_cure_all(void);
+extern void do_cmd_wiz_named_friendly(int r_idx, bool_ slp);
+extern void do_cmd_debug();
diff --git a/src/xtra1.cc b/src/xtra1.cc
new file mode 100644
index 00000000..fd4a11fd
--- /dev/null
+++ b/src/xtra1.cc
@@ -0,0 +1,4684 @@
+/*
+ * 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 "xtra1.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "corrupt.hpp"
+#include "cmd7.hpp"
+#include "dungeon_info_type.hpp"
+#include "files.hpp"
+#include "gods.hpp"
+#include "hook_calculate_hp_in.hpp"
+#include "hook_calculate_hp_out.hpp"
+#include "hooks.hpp"
+#include "levels.hpp"
+#include "messages.hpp"
+#include "mimic.hpp"
+#include "monster1.hpp"
+#include "monster2.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_class.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_type.hpp"
+#include "skill_type.hpp"
+#include "skills.hpp"
+#include "spells3.hpp"
+#include "spells6.hpp"
+#include "stats.hpp"
+#include "tables.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+#include "xtra2.hpp"
+#include "z-rand.hpp"
+
+#include <cassert>
+
+/*
+ * 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--;
+ }
+ }
+
+ /* Clip to permissible range */
+ if (value < 3)
+ {
+ value = 3;
+ }
+
+ /* Return new value */
+ return (value);
+}
+
+
+
+/*
+ * Print character info at given row, column in a 13 char field
+ */
+static void prt_field(cptr info, int row, int col)
+{
+ /* Dump 13 spaces to clear */
+ c_put_str(TERM_WHITE, " ", row, col);
+
+ /* Dump the info itself */
+ c_put_str(TERM_L_BLUE, info, row, col);
+}
+
+/*
+ * Prints players max/cur piety
+ */
+static void prt_piety(void)
+{
+ char tmp[32];
+
+ /* Do not show piety unless it matters */
+ if (!p_ptr->pgod) return;
+
+ c_put_str(TERM_L_WHITE, "Pt ", ROW_PIETY, COL_PIETY);
+
+ sprintf(tmp, "%9ld", (long) p_ptr->grace);
+
+ c_put_str((p_ptr->praying) ? TERM_L_BLUE : TERM_GREEN, tmp, ROW_PIETY,
+ COL_PIETY + 3);
+}
+
+
+/*
+ * Prints the player's current sanity.
+ */
+static void prt_sane(void)
+{
+ char tmp[32];
+ byte color;
+ int perc;
+
+ if (p_ptr->msane == 0)
+ {
+ perc = 100;
+ }
+ else
+ {
+ perc = (100 * p_ptr->csane) / p_ptr->msane;
+ }
+
+ c_put_str(TERM_ORANGE, "SN ", ROW_SANITY, COL_SANITY);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->csane, p_ptr->msane);
+
+ if (perc >= 100)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (perc > (10 * hitpoint_warn))
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+
+ c_put_str(color, tmp, ROW_SANITY, COL_SANITY + 3);
+}
+
+
+/*
+ * Print character stat in given row, column
+ */
+static void prt_stat(int stat)
+{
+ char tmp[32];
+
+ cnv_stat(p_ptr->stat_use[stat], tmp);
+
+ /* Display "injured" stat */
+ if (p_ptr->stat_cur[stat] < p_ptr->stat_max[stat])
+ {
+ int colour;
+
+ if (p_ptr->stat_cnt[stat])
+ colour = TERM_ORANGE;
+ else
+ colour = TERM_YELLOW;
+
+ put_str(format("%s: ", stat_names_reduced[stat]), ROW_STAT + stat, 0);
+ c_put_str(colour, tmp, ROW_STAT + stat, COL_STAT + 6);
+ }
+
+ /* Display "healthy" stat */
+ else
+ {
+ put_str(format("%s: ", stat_names[stat]), ROW_STAT + stat, 0);
+ c_put_str(TERM_L_GREEN, tmp, ROW_STAT + stat, COL_STAT + 6);
+ }
+
+ /* Display "boosted" stat */
+ if (p_ptr->stat_cur[stat] > p_ptr->stat_max[stat])
+ {
+ put_str(format("%s: ", stat_names[stat]), ROW_STAT + stat, 0);
+ c_put_str(TERM_VIOLET, tmp, ROW_STAT + stat, COL_STAT + 6);
+ }
+
+ /* Indicate natural maximum */
+ if (p_ptr->stat_max[stat] == 18 + 100)
+ {
+ put_str("!", ROW_STAT + stat, 3);
+ }
+}
+
+
+
+
+/*
+ * Prints "title", including "wizard" or "winner" as needed.
+ */
+static void prt_title(void)
+{
+ cptr p = "";
+
+ /* Mimic shape */
+ if (p_ptr->mimic_form)
+ {
+ p = get_mimic_name(p_ptr->mimic_form);
+ }
+
+ /* 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];
+
+ }
+
+ 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 ((p_ptr->lev >= PY_MAX_LEVEL) || (p_ptr->lev >= max_plev))
+ {
+ (void)sprintf(out_val, "********");
+ }
+ else
+ {
+ (void)sprintf(out_val, "%8ld", (long)(player_exp[p_ptr->lev - 1] * p_ptr->expfact / 100L) - p_ptr->exp);
+ }
+
+ if (p_ptr->exp >= p_ptr->max_exp)
+ {
+ put_str("EXP ", ROW_EXP, 0);
+ c_put_str(TERM_L_GREEN, out_val, ROW_EXP, COL_EXP + 4);
+ }
+ else
+ {
+ put_str("Exp ", ROW_EXP, 0);
+ c_put_str(TERM_YELLOW, out_val, ROW_EXP, COL_EXP + 4);
+ }
+}
+
+
+/*
+ * Prints current gold
+ */
+static void prt_gold(void)
+{
+ char tmp[32];
+
+ put_str("AU ", ROW_GOLD, COL_GOLD);
+ sprintf(tmp, "%9ld", (long)p_ptr->au);
+ c_put_str(TERM_L_GREEN, tmp, ROW_GOLD, COL_GOLD + 3);
+}
+
+
+
+/*
+ * Prints current AC
+ */
+static void prt_ac(void)
+{
+ char tmp[32];
+
+ put_str("Cur AC ", ROW_AC, COL_AC);
+ sprintf(tmp, "%5d", p_ptr->dis_ac + p_ptr->dis_to_a);
+ c_put_str(TERM_L_GREEN, tmp, ROW_AC, COL_AC + 7);
+}
+
+
+/*
+ * Prints Cur/Max hit points
+ */
+static void prt_hp(void)
+{
+ char tmp[32];
+
+ byte color;
+
+ if (player_char_health) lite_spot(p_ptr->py, p_ptr->px);
+
+ if (p_ptr->necro_extra & CLASS_UNDEAD)
+ {
+ c_put_str(TERM_L_DARK, "DP ", ROW_HP, COL_HP);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->chp, p_ptr->mhp);
+
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_BLUE;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_VIOLET;
+ }
+ else
+ {
+ color = TERM_L_RED;
+ }
+ c_put_str(color, tmp, ROW_HP, COL_HP + 3);
+ }
+ else
+ {
+ c_put_str(TERM_RED, "HP ", ROW_HP, COL_HP);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->chp, p_ptr->mhp);
+
+ if (p_ptr->chp >= p_ptr->mhp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->chp > (p_ptr->mhp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ c_put_str(color, tmp, ROW_HP, COL_HP + 3);
+ }
+}
+
+/*
+ * Prints Cur/Max monster hit points
+ */
+static void prt_mh(void)
+{
+ char tmp[32];
+
+ byte color;
+
+ object_type *o_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (!o_ptr->pval2)
+ {
+ put_str(" ", ROW_MH, COL_MH);
+ return;
+ }
+
+ put_str("MH ", ROW_MH, COL_MH);
+
+ sprintf(tmp, "%4d/%4d", o_ptr->pval2, (int)o_ptr->pval3);
+ if (o_ptr->pval2 >= o_ptr->pval3)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (o_ptr->pval2 > (o_ptr->pval3 * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ c_put_str(color, tmp, ROW_MH, COL_MH + 3);
+}
+
+
+/*
+ * Prints players max/cur spell points
+ */
+static void prt_sp(void)
+{
+ char tmp[32];
+ byte color;
+
+
+ c_put_str(TERM_L_GREEN, "SP ", ROW_SP, COL_SP);
+
+ sprintf(tmp, "%4d/%4d", p_ptr->csp, p_ptr->msp);
+ if (p_ptr->csp >= p_ptr->msp)
+ {
+ color = TERM_L_GREEN;
+ }
+ else if (p_ptr->csp > (p_ptr->msp * hitpoint_warn) / 10)
+ {
+ color = TERM_YELLOW;
+ }
+ else
+ {
+ color = TERM_RED;
+ }
+ c_put_str(color, tmp, ROW_SP, COL_SP + 3);
+}
+
+
+/*
+ * Prints depth in stat area
+ */
+static void prt_depth(int row, int col)
+{
+ char depths[32];
+ dungeon_info_type *d_ptr = &d_info[dungeon_type];
+
+ if (p_ptr->wild_mode)
+ {
+ strcpy(depths, " ");
+ }
+ 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)
+ {
+ strcpy(depths, wf_info[wild_map[p_ptr->wilderness_y][p_ptr->wilderness_x].feat].name);
+ }
+ else
+ {
+ strcpy(depths, "Town/Wild");
+ }
+ }
+ 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, col);
+ else
+ prt(format("%13s", depths), row, col);
+}
+
+
+/*
+ * 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(int row, int col)
+{
+ 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, col);
+}
+
+
+/*
+ * Prints the speed of a character. -CJS-
+ */
+static void prt_speed(int row, int col)
+{
+ 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, col);
+}
+
+
+
+/*
+ * Prints status line
+ */
+static void prt_status_line(void)
+{
+ int wid, hgt;
+ Term_get_size(&wid, &hgt);
+ int row = hgt - 1;
+
+ /* Fainting / Starving */
+ int col = 0;
+ if (p_ptr->food < PY_FOOD_FAINT)
+ {
+ c_put_str(TERM_RED, "Weak ", row, col);
+ }
+ else if (p_ptr->food < PY_FOOD_WEAK)
+ {
+ c_put_str(TERM_ORANGE, "Weak ", row, col);
+ }
+ else if (p_ptr->food < PY_FOOD_ALERT)
+ {
+ c_put_str(TERM_YELLOW, "Hungry", row, col);
+ }
+ else if (p_ptr->food < PY_FOOD_FULL)
+ {
+ c_put_str(TERM_L_GREEN, " ", row, col);
+ }
+ else if (p_ptr->food < PY_FOOD_MAX)
+ {
+ c_put_str(TERM_L_GREEN, "Full ", row, col);
+ }
+ else
+ {
+ c_put_str(TERM_GREEN, "Gorged", row, col);
+ }
+
+ /* Blind */
+ col = 7;
+ if (p_ptr->blind)
+ {
+ c_put_str(TERM_ORANGE, "Blind", row, col);
+ }
+ else
+ {
+ put_str(" ", row, col);
+ }
+
+ /* Confusion */
+ col = 13;
+ if (p_ptr->confused)
+ {
+ c_put_str(TERM_ORANGE, "Conf", row, col);
+ }
+ else
+ {
+ put_str(" ", row, col);
+ }
+
+ /* Fear */
+ col = 18;
+ if (p_ptr->afraid)
+ {
+ c_put_str(TERM_ORANGE, "Afraid", row, col);
+ }
+ else
+ {
+ put_str(" ", row, col);
+ }
+
+ /* Poison */
+ col = 25;
+ if (p_ptr->poisoned)
+ {
+ c_put_str(TERM_ORANGE, "Poison", row, col);
+ }
+ else
+ {
+ put_str(" ", row, col);
+ }
+
+ /* Dtrap */
+ col = 32;
+ if (cave[p_ptr->py][p_ptr->px].info & CAVE_DETECT)
+ {
+ c_put_str(TERM_L_GREEN, "DTrap", row, col);
+ }
+ else
+ {
+ put_str(" ", row, col);
+ }
+
+ /* State */
+ col = 38;
+ prt_state(row, col);
+
+ /* Speed */
+ col = 49;
+ prt_speed(row, col);
+
+ /* "Study" */
+ col = 60;
+ if (p_ptr->skill_points)
+ {
+ put_str("Skill", row, col);
+ }
+ else
+ {
+ put_str(" ", row, col);
+ }
+
+ /* Depth */
+ col = wid - 14;
+ prt_depth(row, col);
+}
+
+
+
+static void prt_cut(void)
+{
+ int c = p_ptr->cut;
+ int hgt;
+ Term_get_size(nullptr, &hgt);
+ int row = hgt - 3;
+ int col = 0;
+
+ if (c > 1000)
+ {
+ c_put_str(TERM_L_RED, "Mortal wound", row, col);
+ }
+ else if (c > 200)
+ {
+ c_put_str(TERM_RED, "Deep gash ", row, col);
+ }
+ else if (c > 100)
+ {
+ c_put_str(TERM_RED, "Severe cut ", row, col);
+ }
+ else if (c > 50)
+ {
+ c_put_str(TERM_ORANGE, "Nasty cut ", row, col);
+ }
+ else if (c > 25)
+ {
+ c_put_str(TERM_ORANGE, "Bad cut ", row, col);
+ }
+ else if (c > 10)
+ {
+ c_put_str(TERM_YELLOW, "Light cut ", row, col);
+ }
+ else if (c)
+ {
+ c_put_str(TERM_YELLOW, "Graze ", row, col);
+ }
+ else
+ {
+ put_str(" ", row, col);
+ }
+}
+
+
+
+static void prt_stun(void)
+{
+ int s = p_ptr->stun;
+ int hgt;
+ Term_get_size(nullptr, &hgt);
+ int row = hgt - 2;
+ int col = 0;
+
+ if (s > 100)
+ {
+ c_put_str(TERM_RED, "Knocked out ", row, col);
+ }
+ else if (s > 50)
+ {
+ c_put_str(TERM_ORANGE, "Heavy stun ", row, col);
+ }
+ else if (s)
+ {
+ c_put_str(TERM_ORANGE, "Stun ", row, col);
+ }
+ else
+ {
+ put_str(" ", row, col);
+ }
+}
+
+
+
+/*
+ * 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)
+{
+ int hgt;
+ Term_get_size(nullptr, &hgt);
+ int col = 0;
+ int row = hgt - 4;
+
+ /* Not tracking */
+ if (!health_who)
+ {
+ /* Erase the health bar */
+ Term_erase(col, row, 12);
+ }
+
+ /* Tracking an unseen monster */
+ else if (!m_list[health_who].ml)
+ {
+ /* Indicate that the monster health is "unknown" */
+ Term_putstr(col, row, 12, TERM_WHITE, "[----------]");
+ }
+
+ /* Tracking a hallucinatory monster */
+ else if (p_ptr->image)
+ {
+ /* Indicate that the monster health is "unknown" */
+ Term_putstr(col, row, 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, row, 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, row, 12, TERM_WHITE, "[----------]");
+
+ /* Dump the current "health" (use '*' symbols) */
+ if (m_ptr->stunned) {
+ Term_putstr(col + 1, row, len, attr, "ssssssssss");
+ } else if (m_ptr->confused) {
+ Term_putstr(col + 1, row, len, attr, "cccccccccc");
+ } else {
+ Term_putstr(col + 1, row, len, attr, "**********");
+ }
+ }
+}
+
+
+
+/*
+ * Display basic info (mostly left of map)
+ */
+static void prt_frame(void)
+{
+ int i;
+
+ /* Race and Class */
+ prt_field(rp_ptr->title, ROW_RACE, COL_RACE);
+ prt_field(spp_ptr->title, 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();
+
+ /* Cut/Stun */
+ prt_cut();
+ prt_stun();
+
+ /* Current depth */
+ prt_status_line();
+
+ /* Special */
+ health_redraw();
+}
+
+
+/*
+ * Hack -- display inventory in sub-windows
+ */
+static void fix_inven(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_INVEN))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display inventory */
+ display_inven();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+
+/*
+ * Hack -- display equipment in sub-windows
+ */
+static void fix_equip(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_EQUIP))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display equipment */
+ display_equip();
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+/*
+ * Hack -- display character in sub-windows
+ */
+static void fix_player(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_PLAYER))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display player */
+ display_player(0);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+
+/*
+ * Hack -- display recent messages in sub-windows
+ *
+ * XXX XXX XXX Adjust for width and split messages
+ */
+void fix_message(void)
+{
+ int j, i;
+ int w, h;
+ int x, y;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_MESSAGE))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Get size */
+ Term_get_size(&w, &h);
+
+ /* Dump messages */
+ for (i = 0; i < h; i++)
+ {
+ /* Dump the message on the appropriate line */
+ display_message(0, (h - 1) - i, strlen(message_str((s16b)i)), message_color((s16b)i), message_str((s16b)i));
+
+ /* Cursor */
+ Term_locate(&x, &y);
+
+ /* Clear to end of line */
+ Term_erase(x, y, 255);
+ }
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display overhead view in sub-windows
+ *
+ * Note that the "player" symbol does NOT appear on the map.
+ */
+static void fix_overhead(void)
+{
+ int j;
+
+ int cy, cx;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_OVERHEAD))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Redraw map */
+ display_map(&cy, &cx);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display monster recall in sub-windows
+ */
+static void fix_monster(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_MONSTER))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Display monster race info */
+ if (monster_race_idx)
+ {
+ display_roff(monster_race_idx, monster_ego_idx);
+ }
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+
+/*
+ * Hack -- display object recall in sub-windows
+ */
+static void fix_object(void)
+{
+ int j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_OBJECT))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Clear */
+ Term_clear();
+
+ /* Display object info */
+ if (tracked_object)
+ if (!object_out_desc(tracked_object, NULL, FALSE, FALSE)) text_out("You see nothing special.");
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+ }
+}
+
+/* Show the monster list in a window */
+
+static void fix_m_list(void)
+{
+ int i, j;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ term *old = Term;
+
+ int c = 0;
+
+ /* No window */
+ if (!angband_term[j]) continue;
+
+ /* No relevant flags */
+ if (!(window_flag[j] & (PW_M_LIST))) continue;
+
+ /* Activate */
+ Term_activate(angband_term[j]);
+
+ /* Clear */
+ Term_clear();
+
+ /* Hallucination */
+ if (p_ptr->image)
+ {
+ c_prt(TERM_WHITE, "You can not see clearly", 0, 0);
+
+ /* Fresh */
+ Term_fresh();
+
+ /* Restore */
+ Term_activate(old);
+
+ return;
+ }
+
+ /* reset visible count */
+ for (i = 1; i < max_r_idx; i++)
+ {
+ monster_race *r_ptr = &r_info[i];
+
+ r_ptr->total_visible = 0;
+ }
+
+ /* Count up the number visible in each race */
+ for (i = 1; i < m_max; i++)
+ {
+ monster_type *m_ptr = &m_list[i];
+ monster_race *r_ptr = &r_info[m_ptr->r_idx];
+
+ /* Skip dead monsters */
+ if (m_ptr->hp < 0) continue;
+
+ /* Skip unseen monsters */
+ if (r_ptr->flags9 & RF9_MIMIC)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[m_ptr->mimic_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_ptr->name, (num % (h - 1)) + 1, (num / (h - 1) * 26));
+ }
+ else
+ {
+ c_prt(attr, format("%s (x%d)", 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 powers of player given the current set of corruptions.
+ */
+static void calc_powers_corruption()
+{
+ /* Map of corruptions to a power */
+ int i;
+
+ /* Grant powers according to whatever corruptions the player has */
+ for (i = 0; i < CORRUPTIONS_MAX; i++)
+ {
+ if (player_has_corruption(i))
+ {
+ int p = get_corruption_power(i);
+ if (p >= 0)
+ {
+ p_ptr->powers[p] = TRUE;
+ }
+ }
+ }
+}
+
+
+/* Ugly hack */
+bool_ calc_powers_silent = FALSE;
+
+/* Calc the player powers */
+static void calc_powers(void)
+{
+ int i, p = 0;
+ bool_ old_powers[POWER_MAX];
+
+ /* Hack -- wait for creation */
+ if (!character_generated) return;
+
+ /* Hack -- handle "xtra" mode */
+ if (character_xtra) return;
+
+ /* 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; i++) p_ptr->powers[i] = p_ptr->powers_mod[i];
+ for (; i < POWER_MAX; i++) p_ptr->powers[i] = 0;
+
+ /* Calculate powers granted by corruptions */
+ calc_powers_corruption();
+
+ /* 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)
+ {
+ calc_mimic_power();
+ }
+
+ /* 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;
+}
+
+
+/*
+ * Calculate the player's sanity
+ */
+static void calc_sanity()
+{
+ 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_FRAME);
+ 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 */
+ if (p_ptr->pgod == 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 (p_ptr->to_m)
+ {
+ msp += msp * p_ptr->to_m / 5;
+ }
+
+ /* 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);
+ }
+
+ /* Sp mods? */
+ mana_school_calc_mana(&msp);
+ meta_inertia_control_calc_mana(&msp);
+
+ /* 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_FRAME);
+
+ /* 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 */
+ assert(p_ptr->lev - 1 >= 0);
+ assert(p_ptr->lev - 1 < PY_MAX_LEVEL);
+ 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 */
+ if (p_ptr->pgod == 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 */
+ mhp += mhp * p_ptr->to_l / 5;
+
+ 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? */
+ {
+ struct hook_calculate_hp_in in = { mhp };
+ struct hook_calculate_hp_out out = { 0 };
+ if (process_hooks_new(HOOK_CALC_HP, &in, &out))
+ {
+ mhp = out.mhp;
+ }
+ }
+
+ /* 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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+ }
+}
+
+
+/*
+ * God hooks for light
+ */
+static void calc_torch_gods()
+{
+ if (p_ptr->pgod == GOD_VARDA)
+ {
+ /* increase lite radius */
+ p_ptr->cur_lite += 1;
+ }
+}
+
+
+/*
+ * 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;
+
+ /* gods */
+ calc_torch_gods();
+
+ /* 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;
+
+ /* Return the result */
+ return (i);
+}
+
+void calc_wield_monster()
+{
+ object_type *o_ptr;
+ monster_race *r_ptr;
+
+ /* Get the carried monster */
+ o_ptr = &p_ptr->inventory[INVEN_CARRY];
+
+ if (o_ptr->k_idx)
+ {
+ r_ptr = &r_info[o_ptr->pval];
+
+ if (r_ptr->flags2 & RF2_INVISIBLE)
+ p_ptr->invis += 20;
+ if (r_ptr->flags2 & RF2_REFLECTING)
+ p_ptr->reflect = TRUE;
+ if (r_ptr->flags7 & RF7_CAN_FLY)
+ p_ptr->ffall = TRUE;
+ if (r_ptr->flags7 & RF7_AQUATIC)
+ p_ptr->water_breath = TRUE;
+ }
+}
+
+/*
+ * Calc which body parts the player have, based on the
+ * monster he incarnate, note that that's bnot a hack
+ * since body parts of the player when in it's own body
+ * are also defined in r_info(monster 0)
+ */
+void calc_body()
+{
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+ int i, b_weapon, b_legs, b_arms;
+ byte *body_parts, bp[BODY_MAX];
+
+ if (!p_ptr->body_monster)
+ {
+ body_parts = bp;
+ for (i = 0; i < BODY_MAX; i++)
+ {
+ int b;
+
+ b = rp_ptr->body_parts[i] + rmp_ptr->body_parts[i];
+ if (b < 0) b = 0;
+
+ if (p_ptr->mimic_form == resolve_mimic_name("Bear"))
+ {
+ if (i == BODY_ARMS) b = 0;
+ else if (i == BODY_LEGS) b = 0;
+ }
+
+ bp[i] = b;
+ }
+ }
+ else
+ {
+ body_parts = bp;
+ for (i = 0; i < BODY_MAX; i++)
+ {
+ int b;
+
+ b = r_ptr->body_parts[i];
+ if (b < 0) b = 0;
+
+ bp[i] = b;
+ }
+ }
+
+ for (i = 0; i < BODY_MAX; i++)
+ {
+ int b;
+
+ b = bp[i] + cp_ptr->body_parts[i];
+ if (b < 0) b = 0;
+ if (b > max_body_part[i]) b = max_body_part[i];
+
+ bp[i] = b;
+ }
+
+ b_weapon = body_parts[BODY_WEAPON];
+ b_arms = body_parts[BODY_ARMS];
+ b_legs = body_parts[BODY_LEGS];
+
+ if (p_ptr->mimic_extra & CLASS_ARMS)
+ {
+ b_weapon++;
+ b_arms++;
+
+ if (b_weapon > 3) b_weapon = 3;
+ if (b_arms > 3) b_arms = 3;
+ }
+
+ if (p_ptr->mimic_extra & CLASS_LEGS)
+ {
+ b_legs++;
+
+ if (b_legs > 2) b_legs = 2;
+ }
+
+ for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++)
+ p_ptr->body_parts[i] = 0;
+
+ for (i = 0; i < b_weapon; i++)
+ p_ptr->body_parts[INVEN_WIELD - INVEN_WIELD + i] = INVEN_WIELD;
+ if (body_parts[BODY_WEAPON])
+ p_ptr->body_parts[INVEN_BOW - INVEN_WIELD] = INVEN_BOW;
+
+ for (i = 0; i < body_parts[BODY_TORSO]; i++)
+ {
+ p_ptr->body_parts[INVEN_BODY - INVEN_WIELD + i] = INVEN_BODY;
+ p_ptr->body_parts[INVEN_OUTER - INVEN_WIELD + i] = INVEN_OUTER;
+ p_ptr->body_parts[INVEN_LITE - INVEN_WIELD + i] = INVEN_LITE;
+ p_ptr->body_parts[INVEN_AMMO - INVEN_WIELD + i] = INVEN_AMMO;
+ p_ptr->body_parts[INVEN_CARRY - INVEN_WIELD + i] = INVEN_CARRY;
+ }
+
+ for (i = 0; i < body_parts[BODY_FINGER]; i++)
+ p_ptr->body_parts[INVEN_RING - INVEN_WIELD + i] = INVEN_RING;
+
+ for (i = 0; i < body_parts[BODY_HEAD]; i++)
+ {
+ p_ptr->body_parts[INVEN_HEAD - INVEN_WIELD + i] = INVEN_HEAD;
+ p_ptr->body_parts[INVEN_NECK - INVEN_WIELD + i] = INVEN_NECK;
+ }
+
+ for (i = 0; i < b_arms; i++)
+ {
+ p_ptr->body_parts[INVEN_ARM - INVEN_WIELD + i] = INVEN_ARM;
+ p_ptr->body_parts[INVEN_HANDS - INVEN_WIELD + i] = INVEN_HANDS;
+ }
+ if (body_parts[BODY_ARMS])
+ p_ptr->body_parts[INVEN_TOOL - INVEN_WIELD] = INVEN_TOOL;
+
+ for (i = 0; i < b_legs; i++)
+ p_ptr->body_parts[INVEN_FEET - INVEN_WIELD + i] = INVEN_FEET;
+
+ /* Ok now if the player lost a body part, he must drop the object he had on it */
+ for (i = 0; i < INVEN_TOTAL - INVEN_WIELD; i++)
+ {
+ if ((!p_ptr->body_parts[i]) && (p_ptr->inventory[i + INVEN_WIELD].k_idx))
+ {
+ /* Drop it NOW ! */
+ inven_takeoff(i + INVEN_WIELD, 255, TRUE);
+ }
+ }
+}
+
+/* Should be called by every calc_bonus call */
+void calc_body_bonus()
+{
+ monster_race *r_ptr = &r_info[p_ptr->body_monster];
+
+ /* If in the player body nothing have to be done */
+ if (!p_ptr->body_monster) return;
+
+ if (p_ptr->disembodied)
+ {
+ p_ptr->wraith_form = TRUE;
+ return;
+ }
+
+ p_ptr->ac += r_ptr->ac;
+ p_ptr->pspeed = r_ptr->speed;
+
+ if (r_ptr->flags1 & RF1_NEVER_MOVE) p_ptr->immovable = TRUE;
+ if (r_ptr->flags2 & RF2_STUPID) p_ptr->stat_add[A_INT] -= 1;
+ if (r_ptr->flags2 & RF2_SMART) p_ptr->stat_add[A_INT] += 1;
+ if (r_ptr->flags2 & RF2_REFLECTING) p_ptr->reflect = TRUE;
+ if (r_ptr->flags2 & RF2_INVISIBLE) p_ptr->invis += 20;
+ if (r_ptr->flags2 & RF2_REGENERATE) p_ptr->regenerate = TRUE;
+ if (r_ptr->flags2 & RF2_AURA_FIRE) p_ptr->sh_fire = TRUE;
+ if (r_ptr->flags2 & RF2_AURA_ELEC) p_ptr->sh_elec = TRUE;
+ if (r_ptr->flags2 & RF2_PASS_WALL) p_ptr->wraith_form = TRUE;
+ if (r_ptr->flags3 & RF3_SUSCEP_FIRE) p_ptr->sensible_fire = TRUE;
+ if (r_ptr->flags3 & RF3_IM_ACID) p_ptr->resist_acid = TRUE;
+ if (r_ptr->flags3 & RF3_IM_ELEC) p_ptr->resist_elec = TRUE;
+ if (r_ptr->flags3 & RF3_IM_FIRE) p_ptr->resist_fire = TRUE;
+ if (r_ptr->flags3 & RF3_IM_POIS) p_ptr->resist_pois = TRUE;
+ if (r_ptr->flags3 & RF3_IM_COLD) p_ptr->resist_cold = TRUE;
+ if (r_ptr->flags3 & RF3_RES_NETH) p_ptr->resist_neth = TRUE;
+ if (r_ptr->flags3 & RF3_RES_NEXU) p_ptr->resist_nexus = TRUE;
+ if (r_ptr->flags3 & RF3_RES_DISE) p_ptr->resist_disen = TRUE;
+ if (r_ptr->flags3 & RF3_NO_FEAR) p_ptr->resist_fear = TRUE;
+ if (r_ptr->flags3 & RF3_NO_SLEEP) p_ptr->free_act = TRUE;
+ if (r_ptr->flags3 & RF3_NO_CONF) p_ptr->resist_conf = TRUE;
+ if (r_ptr->flags7 & RF7_CAN_FLY) p_ptr->ffall = TRUE;
+ if (r_ptr->flags7 & RF7_AQUATIC) p_ptr->water_breath = TRUE;
+}
+
+
+/* Returns the number of extra blows based on abilities. */
+static int get_extra_blows_ability() {
+ /* Count bonus abilities */
+ int num = 0;
+ if (has_ability(AB_MAX_BLOW1)) num++;
+ if (has_ability(AB_MAX_BLOW2)) num++;
+ return num;
+}
+
+/* Returns the blow information based on class */
+void analyze_blow(int *num, int *wgt, int *mul)
+{
+ *num = cp_ptr->blow_num;
+ *wgt = cp_ptr->blow_wgt;
+ *mul = cp_ptr->blow_mul;
+
+ /* Count bonus abilities */
+ (*num) += get_extra_blows_ability();
+}
+
+/* Are all the weapons wielded of the right type ? */
+int get_weaponmastery_skill()
+{
+ int i, skill = 0;
+ object_type *o_ptr;
+
+ i = 0;
+ /* All weapons must be of the same type */
+ while ((p_ptr->body_parts[i] == INVEN_WIELD) && (i < INVEN_TOTAL))
+ {
+ o_ptr = &p_ptr->inventory[INVEN_WIELD + i];
+
+ if (!o_ptr->k_idx)
+ {
+ i++;
+ continue;
+ }
+ switch (o_ptr->tval)
+ {
+ case TV_DAEMON_BOOK:
+ case TV_SWORD:
+ if ((!skill) || (skill == SKILL_SWORD)) skill = SKILL_SWORD;
+ else skill = -1;
+ break;
+ case TV_AXE:
+ if ((!skill) || (skill == SKILL_AXE)) skill = SKILL_AXE;
+ else skill = -1;
+ break;
+ case TV_HAFTED:
+ if ((!skill) || (skill == SKILL_HAFTED)) skill = SKILL_HAFTED;
+ else skill = -1;
+ break;
+ case TV_POLEARM:
+ if ((!skill) || (skill == SKILL_POLEARM)) skill = SKILL_POLEARM;
+ else skill = -1;
+ break;
+ }
+ i++;
+ }
+
+ /* Everything is ok */
+ return skill;
+}
+
+/* Are all the ranged weapons wielded of the right type ? */
+int get_archery_skill()
+{
+ int i, skill = 0;
+
+ i = INVEN_BOW - INVEN_WIELD;
+ /* All weapons must be of the same type */
+ while (p_ptr->body_parts[i] == INVEN_BOW)
+ {
+ if (p_ptr->inventory[INVEN_WIELD + i].tval == TV_BOW)
+ {
+ switch (p_ptr->inventory[INVEN_WIELD + i].sval / 10)
+ {
+ case 0:
+ if ((!skill) || (skill == SKILL_SLING)) skill = SKILL_SLING;
+ else skill = -1;
+ break;
+ case 1:
+ if ((!skill) || (skill == SKILL_BOW)) skill = SKILL_BOW;
+ else skill = -1;
+ break;
+ case 2:
+ if ((!skill) || (skill == SKILL_XBOW)) skill = SKILL_XBOW;
+ else skill = -1;
+ break;
+ }
+ }
+ else
+ {
+ if ((!skill) || (skill == SKILL_BOOMERANG)) skill = SKILL_BOOMERANG;
+ else skill = -1;
+ }
+
+ i++;
+ }
+
+ /* Everything is ok */
+ return skill;
+}
+
+/* Apply gods */
+static void calc_gods()
+{
+ /* Boost WIS if the player follows Eru */
+ if (p_ptr->pgod == 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 */
+ if (p_ptr->pgod == 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;
+
+ if (praying_to(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 */
+ if (praying_to(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 */
+ if (p_ptr->pgod == GOD_MANWE)
+ {
+ if (p_ptr->grace >= 2000) p_ptr->ffall = TRUE;
+ }
+
+ /* Boost Str and Con if the player is following Tulkas */
+ if (p_ptr->pgod == 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;
+ }
+
+ /* Aule provides to-hit/damage bonuses and fire resistance */
+ if (p_ptr->pgod == GOD_AULE)
+ {
+ if (p_ptr->grace > 0)
+ {
+ int bonus;
+ /* Resist fire*/
+ if (p_ptr->grace > 5000)
+ {
+ p_ptr->resist_fire = TRUE;
+ }
+
+ bonus = p_ptr->grace / 5000;
+ if (bonus > 5)
+ {
+ bonus = 5;
+ }
+
+ p_ptr->to_h = p_ptr->to_h + bonus;
+ p_ptr->dis_to_h = p_ptr->dis_to_h + bonus;
+ p_ptr->to_d = p_ptr->to_d + bonus;
+ p_ptr->dis_to_d = p_ptr->dis_to_d + bonus;
+ }
+ }
+
+ /* Mandos provides nether resistance and, while praying,
+ nether immunity and prevents teleportation. */
+ if (p_ptr->pgod == GOD_MANDOS)
+ {
+ p_ptr->resist_neth = TRUE;
+
+ if ((p_ptr->grace > 10000) &&
+ (p_ptr->praying == TRUE))
+ {
+ p_ptr->resist_continuum = TRUE;
+ }
+
+ if ((p_ptr->grace > 20000) &&
+ (p_ptr->praying == TRUE))
+ {
+ p_ptr->immune_neth = TRUE;
+ }
+ }
+
+ /* Ulmo provides water breath and, while praying can
+ provide poison resistance and magic breath. */
+ if (p_ptr->pgod == GOD_ULMO)
+ {
+ p_ptr->water_breath = TRUE;
+
+ if ((p_ptr->grace > 1000) &&
+ (p_ptr->praying == TRUE))
+ {
+ p_ptr->resist_pois = TRUE;
+ }
+
+ if ((p_ptr->grace > 15000) &&
+ (p_ptr->praying == TRUE))
+ {
+ p_ptr->magical_breath = TRUE;
+ }
+ }
+}
+
+/* Apply spell schools */
+static void calc_schools()
+{
+ if (get_skill(SKILL_AIR) >= 50)
+ {
+ p_ptr->magical_breath = TRUE;
+ }
+
+ if (get_skill(SKILL_WATER) >= 30)
+ {
+ p_ptr->water_breath = TRUE;
+ }
+}
+
+/* Apply corruptions */
+static void calc_corruptions()
+{
+ if (player_has_corruption(CORRUPT_BALROG_AURA))
+ {
+ p_ptr->xtra_f3 |= TR3_SH_FIRE;
+ p_ptr->xtra_f3 |= TR3_LITE1;
+ }
+
+ if (player_has_corruption(CORRUPT_BALROG_WINGS))
+ {
+ p_ptr->xtra_f4 |= TR4_FLY;
+ p_ptr->stat_add[A_CHR] -= 4;
+ p_ptr->stat_add[A_DEX] -= 2;
+ }
+
+ if (player_has_corruption(CORRUPT_BALROG_STRENGTH))
+ {
+ p_ptr->stat_add[A_STR] += 3;
+ p_ptr->stat_add[A_CON] += 1;
+ p_ptr->stat_add[A_DEX] -= 3;
+ p_ptr->stat_add[A_CHR] -= 1;
+ }
+
+ if (player_has_corruption(CORRUPT_DEMON_SPIRIT))
+ {
+ p_ptr->stat_add[A_INT] += 1;
+ p_ptr->stat_add[A_CHR] -= 2;
+ }
+
+ if (player_has_corruption(CORRUPT_DEMON_HIDE))
+ {
+ p_ptr->to_a = p_ptr->to_a + p_ptr->lev;
+ p_ptr->dis_to_a = p_ptr->dis_to_a + p_ptr->lev;
+ p_ptr->pspeed = p_ptr->pspeed - (p_ptr->lev / 7);
+ if (p_ptr->lev >= 40)
+ {
+ p_ptr->xtra_f2 |= TR2_IM_FIRE;
+ }
+ }
+
+ if (player_has_corruption(CORRUPT_DEMON_REALM))
+ {
+ /* 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].mod == 0)
+ {
+ s_info[SKILL_DAEMON].mod = 1500;
+ }
+ s_info[SKILL_DAEMON].hidden = FALSE;
+ }
+
+ if (player_has_corruption(CORRUPT_RANDOM_TELEPORT))
+ {
+ p_ptr->xtra_f3 |= TR3_TELEPORT;
+ }
+
+ if (player_has_corruption(CORRUPT_ANTI_TELEPORT))
+ {
+ if (p_ptr->corrupt_anti_teleport_stopped == FALSE)
+ {
+ p_ptr->resist_continuum = TRUE;
+ }
+ }
+
+ if (player_has_corruption(CORRUPT_TROLL_BLOOD))
+ {
+ p_ptr->xtra_f3 |= (TR3_REGEN | TR3_AGGRAVATE);
+ p_ptr->xtra_esp |= ESP_TROLL;
+ }
+}
+
+/* Apply flags */
+static int extra_blows;
+static int extra_shots;
+void apply_flags(u32b f1, u32b f2, u32b f3, u32b f4, u32b f5, u32b esp, s16b pval, s16b tval, s16b to_h, s16b to_d, s16b to_a)
+{
+ s16b antimagic_mod;
+
+ /* Affect stats */
+ if (f1 & (TR1_STR)) p_ptr->stat_add[A_STR] += pval;
+ if (f1 & (TR1_INT)) p_ptr->stat_add[A_INT] += pval;
+ if (f1 & (TR1_WIS)) p_ptr->stat_add[A_WIS] += pval;
+ if (f1 & (TR1_DEX)) p_ptr->stat_add[A_DEX] += pval;
+ if (f1 & (TR1_CON)) p_ptr->stat_add[A_CON] += pval;
+ if (f1 & (TR1_CHR)) p_ptr->stat_add[A_CHR] += pval;
+ if (f5 & (TR5_LUCK)) p_ptr->luck_cur += pval;
+
+ /* Affect spell power */
+ if (f1 & (TR1_SPELL)) p_ptr->to_s += pval;
+
+ /* Affect mana capacity */
+ if (f1 & (TR1_MANA)) p_ptr->to_m += pval;
+
+ /* Affect life capacity */
+ if (f2 & (TR2_LIFE)) p_ptr->to_l += pval;
+
+ /* Affect stealth */
+ if (f1 & (TR1_STEALTH)) p_ptr->skill_stl += pval;
+
+ /* Affect searching ability (factor of five) */
+ if (f1 & (TR1_SEARCH)) p_ptr->skill_srh += (pval * 5);
+
+ /* Affect searching frequency (factor of five) */
+ if (f1 & (TR1_SEARCH)) p_ptr->skill_fos += (pval * 5);
+
+ /* Affect infravision */
+ if (f1 & (TR1_INFRA)) p_ptr->see_infra += pval;
+
+ /* Affect digging (factor of 20) */
+ if (f1 & (TR1_TUNNEL)) p_ptr->skill_dig += (pval * 20);
+
+ /* Affect speed */
+ if (f1 & (TR1_SPEED)) p_ptr->pspeed += pval;
+
+ /* Affect blows */
+ if (f1 & (TR1_BLOWS)) extra_blows += pval;
+ if (f5 & (TR5_CRIT)) p_ptr->xtra_crit += pval;
+
+ /* Hack -- Sensible fire */
+ if (f2 & (TR2_SENS_FIRE)) p_ptr->sensible_fire = TRUE;
+
+ /* Hack -- cause earthquakes */
+ if (f1 & (TR1_IMPACT)) p_ptr->impact = TRUE;
+
+ /* Affect invisibility */
+ if (f2 & (TR2_INVIS)) p_ptr->invis += (pval * 10);
+
+ /* Boost shots */
+ if (f3 & (TR3_XTRA_SHOTS)) extra_shots++;
+
+ /* Various flags */
+ if (f3 & (TR3_AGGRAVATE)) p_ptr->aggravate = TRUE;
+ if (f3 & (TR3_TELEPORT)) p_ptr->teleport = TRUE;
+ if (f5 & (TR5_DRAIN_MANA)) p_ptr->drain_mana++;
+ if (f5 & (TR5_DRAIN_HP)) p_ptr->drain_life++;
+ if (f3 & (TR3_DRAIN_EXP)) p_ptr->exp_drain = TRUE;
+ if (f3 & (TR3_BLESSED)) p_ptr->bless_blade = TRUE;
+ if (f3 & (TR3_XTRA_MIGHT)) p_ptr->xtra_might += pval;
+ if (f3 & (TR3_SLOW_DIGEST)) p_ptr->slow_digest = TRUE;
+ if (f3 & (TR3_REGEN)) p_ptr->regenerate = TRUE;
+ if (esp) p_ptr->telepathy |= esp;
+ if ((tval != TV_LITE) && (f3 & (TR3_LITE1))) p_ptr->lite = TRUE;
+ if ((tval != TV_LITE) && (f4 & (TR4_LITE2))) p_ptr->lite = TRUE;
+ if ((tval != TV_LITE) && (f4 & (TR4_LITE3))) p_ptr->lite = TRUE;
+ if (f3 & (TR3_SEE_INVIS)) p_ptr->see_inv = TRUE;
+ if (f2 & (TR2_FREE_ACT)) p_ptr->free_act = TRUE;
+ if (f2 & (TR2_HOLD_LIFE)) p_ptr->hold_life = TRUE;
+ if (f3 & (TR3_WRAITH)) p_ptr->wraith_form = TRUE;
+ if (f3 & (TR3_FEATHER)) p_ptr->ffall = TRUE;
+ if (f4 & (TR4_FLY)) p_ptr->fly = TRUE;
+ if (f4 & (TR4_CLIMB)) p_ptr->climb = TRUE;
+
+ /* Immunity flags */
+ if (f2 & (TR2_IM_FIRE)) p_ptr->immune_fire = TRUE;
+ if (f2 & (TR2_IM_ACID)) p_ptr->immune_acid = TRUE;
+ if (f2 & (TR2_IM_COLD)) p_ptr->immune_cold = TRUE;
+ if (f2 & (TR2_IM_ELEC)) p_ptr->immune_elec = TRUE;
+
+ /* Resistance flags */
+ if (f2 & (TR2_RES_ACID)) p_ptr->resist_acid = TRUE;
+ if (f2 & (TR2_RES_ELEC)) p_ptr->resist_elec = TRUE;
+ if (f2 & (TR2_RES_FIRE)) p_ptr->resist_fire = TRUE;
+ if (f2 & (TR2_RES_COLD)) p_ptr->resist_cold = TRUE;
+ if (f2 & (TR2_RES_POIS)) p_ptr->resist_pois = TRUE;
+ if (f2 & (TR2_RES_FEAR)) p_ptr->resist_fear = TRUE;
+ if (f2 & (TR2_RES_CONF)) p_ptr->resist_conf = TRUE;
+ if (f2 & (TR2_RES_SOUND)) p_ptr->resist_sound = TRUE;
+ if (f2 & (TR2_RES_LITE)) p_ptr->resist_lite = TRUE;
+ if (f2 & (TR2_RES_DARK)) p_ptr->resist_dark = TRUE;
+ if (f2 & (TR2_RES_CHAOS)) p_ptr->resist_chaos = TRUE;
+ if (f2 & (TR2_RES_DISEN)) p_ptr->resist_disen = TRUE;
+ if (f2 & (TR2_RES_SHARDS)) p_ptr->resist_shard = TRUE;
+ if (f2 & (TR2_RES_NEXUS)) p_ptr->resist_nexus = TRUE;
+ if (f2 & (TR2_RES_BLIND)) p_ptr->resist_blind = TRUE;
+ if (f2 & (TR2_RES_NETHER)) p_ptr->resist_neth = TRUE;
+ if (f4 & (TR4_IM_NETHER)) p_ptr->immune_neth = TRUE;
+
+ if (f2 & (TR2_REFLECT)) p_ptr->reflect = TRUE;
+ if (f3 & (TR3_SH_FIRE)) p_ptr->sh_fire = TRUE;
+ if (f3 & (TR3_SH_ELEC)) p_ptr->sh_elec = TRUE;
+ if (f3 & (TR3_NO_MAGIC)) p_ptr->anti_magic = TRUE;
+ if (f3 & (TR3_NO_TELE)) p_ptr->anti_tele = TRUE;
+
+ /* Sustain flags */
+ if (f2 & (TR2_SUST_STR)) p_ptr->sustain_str = TRUE;
+ if (f2 & (TR2_SUST_INT)) p_ptr->sustain_int = TRUE;
+ if (f2 & (TR2_SUST_WIS)) p_ptr->sustain_wis = TRUE;
+ if (f2 & (TR2_SUST_DEX)) p_ptr->sustain_dex = TRUE;
+ if (f2 & (TR2_SUST_CON)) p_ptr->sustain_con = TRUE;
+ if (f2 & (TR2_SUST_CHR)) p_ptr->sustain_chr = TRUE;
+
+ if (f4 & (TR4_PRECOGNITION)) p_ptr->precognition = TRUE;
+
+ antimagic_mod = to_h + to_d + to_a;
+
+ if (f4 & (TR4_ANTIMAGIC_50))
+ {
+ s32b tmp;
+
+ tmp = 10 + get_skill_scale(SKILL_ANTIMAGIC, 40) - antimagic_mod;
+ if (tmp > 0) p_ptr->antimagic += tmp;
+
+ tmp = 1 + get_skill_scale(SKILL_ANTIMAGIC, 4) - antimagic_mod / 15;
+ if (tmp > 0) p_ptr->antimagic_dis += tmp;
+ }
+
+ if (f4 & (TR4_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;
+ }
+}
+
+
+
+/**
+ * Are barehand fighter's hands empty?
+ */
+static 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;
+}
+
+
+
+/*
+ * 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)
+{
+ static bool_ monk_notify_aux = FALSE;
+ int i, j, hold;
+ int old_speed;
+ u32b old_telepathy;
+ int old_see_inv;
+ int old_dis_ac;
+ int old_dis_to_a;
+ object_type *o_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+ bool_ monk_armour_aux;
+
+
+ /* Save the old speed */
+ old_speed = p_ptr->pspeed;
+
+ /* Save the old vision stuff */
+ old_telepathy = p_ptr->telepathy;
+ old_see_inv = p_ptr->see_inv;
+
+ /* Save the old armor class */
+ old_dis_ac = p_ptr->dis_ac;
+ old_dis_to_a = p_ptr->dis_to_a;
+
+ /* Clear extra blows/shots */
+ extra_blows = extra_shots = 0;
+
+ /* Clear the stat modifiers */
+ for (i = 0; i < 6; i++) p_ptr->stat_add[i] = 0;
+
+ /* Mana multiplier */
+ p_ptr->to_m = 0;
+
+ /* Life multiplier */
+ p_ptr->to_l = 0;
+
+ /* Spell power */
+ p_ptr->to_s = 0;
+
+ /* Clear the Displayed/Real armor class */
+ p_ptr->dis_ac = p_ptr->ac = 0;
+
+ /* Clear the Displayed/Real Bonuses */
+ p_ptr->dis_to_h = p_ptr->to_h = p_ptr->to_h_melee = p_ptr->to_h_ranged = 0;
+ p_ptr->dis_to_d = p_ptr->to_d = p_ptr->to_d_melee = p_ptr->to_d_ranged = 0;
+ p_ptr->dis_to_a = p_ptr->to_a = 0;
+
+ /* Start with "normal" speed */
+ p_ptr->pspeed = 110;
+
+ /* Start with 0% additionnal crits */
+ p_ptr->xtra_crit = 0;
+
+ /* Start with a single blow per turn */
+ p_ptr->num_blow = 1;
+
+ /* Start with a single shot per turn */
+ p_ptr->num_fire = 1;
+
+ /* Starts with single throwing damage */
+ p_ptr->throw_mult = 1;
+
+ /* Reset the "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();
+ }
+
+ /* Take care of spell schools */
+ calc_schools();
+
+ /* Take care of corruptions */
+ calc_corruptions();
+
+ /* 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 (race_flags1_p(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);
+
+ /* 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 (race_flags1_p(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_FRAME);
+
+ /* 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_FRAME);
+
+ /* 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 */
+ if (p_ptr->pgod == 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;
+ }
+
+ /* Temporary precognition */
+ if (p_ptr->tim_precognition > 0)
+ {
+ apply_flags(0, 0, 0, TR4_PRECOGNITION, 0, 0, 0, 0, 0, 0, 0);
+ }
+
+ /* 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 "Reflection" */
+ if (p_ptr->tim_reflect)
+ {
+ p_ptr->reflect = TRUE;
+ }
+
+ /* Temporary "Levitation" and "Flying" */
+ if (p_ptr->tim_ffall)
+ {
+ p_ptr->ffall = TRUE;
+ }
+ if (p_ptr->tim_fly)
+ {
+ p_ptr->fly = 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;
+ }
+
+ /* 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;
+
+ /* Display the speed (if needed) */
+ if (p_ptr->pspeed != old_speed) p_ptr->redraw |= (PR_FRAME);
+
+
+ /* 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_FRAME);
+
+ /* 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 (race_flags1_p(PR1_XTRA_MIGHT_BOW) && p_ptr->tval_ammo == TV_ARROW)
+ p_ptr->xtra_might += 1;
+
+ if (race_flags1_p(PR1_XTRA_MIGHT_SLING) && p_ptr->tval_ammo == TV_SHOT)
+ p_ptr->xtra_might += 1;
+
+ if (race_flags1_p(PR1_XTRA_MIGHT_XBOW) && p_ptr->tval_ammo == TV_BOLT)
+ p_ptr->xtra_might += 1;
+
+ /* Examine the "current tool" */
+ o_ptr = &p_ptr->inventory[INVEN_TOOL];
+
+ /* Boost digging skill by tool weight */
+ if (o_ptr->k_idx && (o_ptr->tval == TV_DIGGING))
+ {
+ p_ptr->skill_dig += (o_ptr->weight / 10);
+ }
+
+ /* Examine the main weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+
+ /* Assume not heavy */
+ p_ptr->heavy_wield = FALSE;
+
+ /* Normal weapons */
+ if (o_ptr->k_idx && !p_ptr->heavy_wield)
+ {
+ int str_index, dex_index;
+
+ int num = 0, wgt = 0, mul = 0, div = 0;
+
+ analyze_blow(&num, &wgt, &mul);
+
+ /* Enforce a minimum "weight" (tenth pounds) */
+ div = ((o_ptr->weight < wgt) ? wgt : o_ptr->weight);
+
+ /* Access the strength vs weight */
+ str_index = (adj_str_blow[p_ptr->stat_ind[A_STR]] * mul / div);
+
+ /* Maximal value */
+ if (str_index > 11) str_index = 11;
+
+ /* Index by dexterity */
+ dex_index = (adj_dex_blow[p_ptr->stat_ind[A_DEX]]);
+
+ /* Maximal value */
+ if (dex_index > 11) dex_index = 11;
+
+ /* Use the blows table */
+ p_ptr->num_blow = blows_table[str_index][dex_index];
+
+ /* Maximal value */
+ if (p_ptr->num_blow > num) p_ptr->num_blow = num;
+
+ /* Add in the "bonus blows" */
+ p_ptr->num_blow += extra_blows;
+
+ /* Special class bonus blows */
+ p_ptr->num_blow += p_ptr->lev * cp_ptr->extra_blows / 50;
+
+ /* Weapon specialization bonus blows */
+ if (get_weaponmastery_skill() != -1)
+ p_ptr->num_blow += get_skill_scale(get_weaponmastery_skill(), 2);
+
+ /* Bonus blows for plain weaponmastery skill */
+ p_ptr->num_blow += get_skill_scale(SKILL_MASTERY, 3);
+
+ /* Require at least one blow */
+ if (p_ptr->num_blow < 1) p_ptr->num_blow = 1;
+ }
+ /* Different calculation for bear form with empty hands */
+ else if ((p_ptr->melee_style == SKILL_HAND) && monk_empty_hands())
+ {
+ int plev = get_skill(SKILL_HAND);
+
+ p_ptr->num_blow = get_extra_blows_ability();
+
+ if (plev > 9) p_ptr->num_blow++;
+ if (plev > 19) p_ptr->num_blow++;
+ if (plev > 29) p_ptr->num_blow++;
+ if (plev > 34) p_ptr->num_blow++;
+ if (plev > 39) p_ptr->num_blow++;
+ if (plev > 44) p_ptr->num_blow++;
+ if (plev > 49) p_ptr->num_blow++;
+
+ if (monk_heavy_armor()) p_ptr->num_blow /= 2;
+
+ p_ptr->num_blow += 1 + extra_blows;
+
+ if (!monk_heavy_armor())
+ {
+ p_ptr->to_h += (plev / 3);
+ p_ptr->to_d += (plev / 3);
+
+ p_ptr->dis_to_h += (plev / 3);
+ p_ptr->dis_to_d += (plev / 3);
+ }
+ }
+
+ /* Monsters that only have their "natural" attacks */
+ else if (!r_info[p_ptr->body_monster].body_parts[BODY_WEAPON])
+ {
+ int num = 0;
+ int wgt = 0;
+ int mul = 0;
+ analyze_blow(&num, &wgt, &mul);
+
+ /* Access the strength vs weight */
+ int 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 */
+ int 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 */
+ auto r_ptr = race_info_idx(p_ptr->body_monster, 0);
+ 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;
+}
+
+
+
+/*
+ * 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);
+ }
+
+ 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);
+ 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;
+
+
+ /* 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_FRAME))
+ {
+ p_ptr->redraw &= ~(PR_FRAME);
+ prt_frame();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->window"
+ */
+void window_stuff(void)
+{
+ int j;
+
+ u32b mask = 0L;
+
+
+ /* Nothing to do */
+ if (!p_ptr->window) return;
+
+ /* Scan windows */
+ for (j = 0; j < 8; j++)
+ {
+ /* Save usable flags */
+ if (angband_term[j]) mask |= window_flag[j];
+ }
+
+ /* Apply usable flags */
+ p_ptr->window &= mask;
+
+ /* Nothing to do */
+ if (!p_ptr->window) return;
+
+
+ /* Display p_ptr->inventory */
+ if (p_ptr->window & (PW_INVEN))
+ {
+ p_ptr->window &= ~(PW_INVEN);
+ fix_inven();
+ }
+
+ /* Display equipment */
+ if (p_ptr->window & (PW_EQUIP))
+ {
+ p_ptr->window &= ~(PW_EQUIP);
+ fix_equip();
+ }
+
+ /* Display player */
+ if (p_ptr->window & (PW_PLAYER))
+ {
+ p_ptr->window &= ~(PW_PLAYER);
+ fix_player();
+ }
+
+ /* Display monster list */
+ if (p_ptr->window & (PW_M_LIST))
+ {
+ p_ptr->window &= ~(PW_M_LIST);
+ fix_m_list();
+ }
+
+ /* Display overhead view */
+ if (p_ptr->window & (PW_MESSAGE))
+ {
+ p_ptr->window &= ~(PW_MESSAGE);
+ fix_message();
+ }
+
+ /* Display overhead view */
+ if (p_ptr->window & (PW_OVERHEAD))
+ {
+ p_ptr->window &= ~(PW_OVERHEAD);
+ fix_overhead();
+ }
+
+ /* Display monster recall */
+ if (p_ptr->window & (PW_MONSTER))
+ {
+ p_ptr->window &= ~(PW_MONSTER);
+ fix_monster();
+ }
+
+ /* Display object recall */
+ if (p_ptr->window & (PW_OBJECT))
+ {
+ p_ptr->window &= ~(PW_OBJECT);
+ fix_object();
+ }
+}
+
+
+/*
+ * Handle "p_ptr->update" and "p_ptr->redraw" and "p_ptr->window"
+ */
+void handle_stuff(void)
+{
+ /* Update stuff */
+ if (p_ptr->update) update_stuff();
+
+ /* Redraw stuff */
+ if (p_ptr->redraw) redraw_stuff();
+
+ /* Window stuff */
+ if (p_ptr->window) window_stuff();
+}
+
+
+bool_ monk_heavy_armor(void)
+{
+ u16b monk_arm_wgt = 0;
+
+ if (p_ptr->melee_style != SKILL_HAND) return FALSE;
+
+ /* Weight the armor */
+ monk_arm_wgt += p_ptr->inventory[INVEN_BODY].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_HEAD].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_ARM].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_OUTER].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_HANDS].weight;
+ monk_arm_wgt += p_ptr->inventory[INVEN_FEET].weight;
+
+ return (monk_arm_wgt > (100 + (get_skill(SKILL_HAND) * 4))) ;
+}
+
+static int get_artifact_idx(int level)
+{
+ int count = 0, i;
+
+ while (count < 1000)
+ {
+ artifact_type *a_ptr;
+
+ count++;
+ i = randint(max_a_idx - 1);
+ a_ptr = &a_info[i];
+ if (!a_ptr->tval) continue;
+
+ /* It is found/lost */
+ if (a_ptr->cur_num) continue;
+
+ /* OoD */
+ if (a_ptr->level > level) continue;
+
+ /* Avoid granting SPECIAL_GENE artifacts */
+ if (a_ptr->flags4 & TR4_SPECIAL_GENE) continue;
+
+ return i;
+ }
+
+ /* No matches found */
+ /* Grant a randart */
+ return 0;
+}
+
+/* Chose a fate */
+void gain_fate(byte fate)
+{
+ int i;
+ int level;
+
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if (!fates[i].fate)
+ {
+ fates[i].level = 0;
+
+ cmsg_print(TERM_VIOLET, "More of your prophecy has been unearthed!");
+ cmsg_print(TERM_VIOLET, "You should see a soothsayer quickly.");
+
+ if (fate)
+ fates[i].fate = fate;
+ else
+ /* If lucky (current luck > 0) avoid death fate */
+ switch (rand_int(p_ptr->luck_cur > 0 ? 17 : 18))
+ {
+ case 6:
+ case 2:
+ case 3:
+ case 7:
+ case 8:
+ case 9:
+ case 13:
+ fates[i].fate = FATE_FIND_O;
+ break;
+ case 1:
+ case 4:
+ case 5:
+ case 10:
+ case 11:
+ case 12:
+ case 14:
+ fates[i].fate = FATE_FIND_R;
+ break;
+ case 15:
+ case 16:
+ fates[i].fate = FATE_FIND_A;
+ break;
+ case 17:
+ fates[i].fate = FATE_DIE;
+ break;
+ case 0:
+ {
+ /* The deepest the better */
+ int chance = dun_level / 4;
+
+ /* No more than 1/2 chances */
+ if (chance > 50) chance = 50;
+
+ /* It's HARD to get now */
+ if (magik(chance))
+ {
+ fates[i].fate = FATE_NO_DIE_MORTAL;
+ }
+ else
+ {
+ fates[i].fate = FATE_FIND_O;
+ }
+ break;
+ }
+ }
+
+ switch (fates[i].fate)
+ {
+ case FATE_FIND_O:
+ {
+ while (TRUE)
+ {
+ object_kind *k_ptr;
+ obj_theme theme;
+
+ /* No themes */
+ theme.treasure = 100;
+ theme.combat = 100;
+ theme.magic = 100;
+ theme.tools = 100;
+ init_match_theme(theme);
+
+ /* Apply restriction */
+ get_obj_num_hook = kind_is_legal;
+
+ /* Rebuild allocation table */
+ get_obj_num_prep();
+
+ fates[i].o_idx = get_obj_num(max_dlv[dungeon_type] + randint(10));
+
+ /* Invalidate the cached allocation table */
+ alloc_kind_table_valid = FALSE;
+
+ k_ptr = &k_info[fates[i].o_idx];
+
+ if (!(k_ptr->flags3 & TR3_INSTA_ART) && !(k_ptr->flags3 & TR3_NORM_ART)) break;
+ }
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = rand_int(2);
+ fates[i].know = FALSE;
+ if (wizard) msg_format("New fate : Find object %d on level %d", fates[i].o_idx, fates[i].level);
+ break;
+ }
+ case FATE_FIND_R:
+ /* Prepare allocation table */
+ get_mon_num_prep();
+
+ fates[i].r_idx = get_mon_num(max_dlv[dungeon_type] + randint(10));
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = rand_int(2);
+ fates[i].know = FALSE;
+ if (wizard) msg_format("New fate : Meet monster %d on level %d", fates[i].r_idx, fates[i].level);
+ break;
+
+ case FATE_FIND_A:
+ fates[i].a_idx = get_artifact_idx(max_dlv[dungeon_type] + randint(10));
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = TRUE;
+ fates[i].know = FALSE;
+ if (wizard) msg_format("New fate : Find artifact %d on level %d", fates[i].a_idx, fates[i].level);
+ break;
+
+ case FATE_DIE:
+ level = rand_range(max_dlv[dungeon_type] - 20, max_dlv[dungeon_type] + 20);
+ fates[i].level = (level < 1) ? 1 : (level > 98) ? 98 : level;
+ fates[i].serious = TRUE;
+ fates[i].know = FALSE;
+ if ((wizard) || (p_ptr->precognition)) msg_format("New fate : Death on level %d", fates[i].level);
+ break;
+
+ case FATE_NO_DIE_MORTAL:
+ fates[i].serious = TRUE;
+ p_ptr->no_mortal = TRUE;
+ if ((wizard) || (p_ptr->precognition)) msg_format("New fate : Never to die by the hand of a mortal being.");
+ break;
+ }
+
+ break;
+ }
+ }
+}
+
+void fate_desc(char *desc, int fate)
+{
+ char buf[120];
+
+ if (fates[fate].serious)
+ {
+ strcpy(desc, "You are fated to ");
+ }
+ else
+ {
+ strcpy(desc, "You may ");
+ }
+ switch (fates[fate].fate)
+ {
+ case FATE_FIND_O:
+ {
+ object_type *o_ptr, forge;
+ char o_name[80];
+
+ o_ptr = &forge;
+ object_prep(o_ptr, fates[fate].o_idx);
+ object_desc_store(o_name, o_ptr, 1, 0);
+
+ sprintf(buf, "find %s on level %d.", o_name, fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_FIND_A:
+ {
+ object_type *q_ptr, forge;
+ char o_name[80];
+ artifact_type *a_ptr = &a_info[fates[fate].a_idx];
+ int I_kind;
+
+ /* Failed artefact allocation XXX XXX XXX */
+ if (fates[fate].a_idx == 0)
+ {
+ strcpy(o_name, "something special");
+ }
+
+ /* Legal artefacts */
+ else
+ {
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = fates[fate].a_idx;
+
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+
+ /* Hack -- acquire "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) q_ptr->ident |= (IDENT_CURSED);
+
+ random_artifact_resistance(q_ptr);
+
+ object_desc_store(o_name, q_ptr, 1, 0);
+ }
+
+ sprintf(buf, "find %s on level %d.", o_name, fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_FIND_R:
+ {
+ char m_name[80];
+
+ monster_race_desc(m_name, fates[fate].r_idx, 0);
+ sprintf(buf, "meet %s on level %d.", m_name, fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_DIE:
+ {
+ sprintf(buf, "die on level %d.", fates[fate].level);
+ strcat(desc, buf);
+ break;
+ }
+ case FATE_NO_DIE_MORTAL:
+ {
+ strcat(desc, "never to die by the hand of a mortal being.");
+ break;
+ }
+ }
+}
+
+void dump_fates(FILE *outfile)
+{
+ int i;
+ char buf[120];
+ bool_ pending = FALSE;
+
+ if (!outfile) return;
+
+ for (i = 0; i < MAX_FATES; i++)
+ {
+ if ((fates[i].fate) && (fates[i].know))
+ {
+ fate_desc(buf, i);
+ fprintf(outfile, "%s\n", buf);
+ }
+ if ((fates[i].fate) && !(fates[i].know)) pending = TRUE;
+ }
+ if (pending)
+ {
+ fprintf(outfile, "You do not know all of your fate.\n");
+ }
+}
+
+/*
+ * Return a luck number between a certain range
+ */
+int luck(int min, int max)
+{
+ int luck = p_ptr->luck_cur;
+ int range = max - min;
+
+ if (luck < -30) luck = -30;
+ if (luck > 30) luck = 30;
+ luck += 30;
+
+ luck *= range;
+ luck /= 60;
+
+ return (luck + min);
+}
+
+bool race_flags1_p(u32b flags1_mask)
+{
+ return (rp_ptr->flags1 | rmp_ptr->flags1 | cp_ptr->flags1 | spp_ptr->flags1) & flags1_mask;
+}
+
+bool race_flags2_p(u32b flags2_mask)
+{
+ return (rp_ptr->flags2 | rmp_ptr->flags2 | cp_ptr->flags2 | spp_ptr->flags2) & flags2_mask;
+}
diff --git a/src/xtra1.hpp b/src/xtra1.hpp
new file mode 100644
index 00000000..df2592ac
--- /dev/null
+++ b/src/xtra1.hpp
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "h-basic.h"
+
+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_heavy_armor(void);
+extern void calc_bonuses(bool_ silent);
+extern void gain_fate(byte fate);
+extern void fate_desc(char *desc, int fate);
+extern void dump_fates(FILE *OutFile);
+extern bool race_flags1_p(u32b flags1_mask);
+extern bool race_flags2_p(u32b flags2_mask);
diff --git a/src/xtra2.cc b/src/xtra2.cc
new file mode 100644
index 00000000..096f8966
--- /dev/null
+++ b/src/xtra2.cc
@@ -0,0 +1,5626 @@
+/*
+ * 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 "xtra2.hpp"
+
+#include "artifact_type.hpp"
+#include "cave.hpp"
+#include "cave_type.hpp"
+#include "corrupt.hpp"
+#include "dungeon_info_type.hpp"
+#include "ego_item_type.hpp"
+#include "feature_type.hpp"
+#include "files.hpp"
+#include "gods.hpp"
+#include "hook_player_level_in.hpp"
+#include "hook_monster_death_in.hpp"
+#include "hooks.hpp"
+#include "melee2.hpp"
+#include "mimic.hpp"
+#include "monster1.hpp"
+#include "monster2.hpp"
+#include "monster3.hpp"
+#include "monster_ego.hpp"
+#include "monster_race.hpp"
+#include "monster_type.hpp"
+#include "notes.hpp"
+#include "object1.hpp"
+#include "object2.hpp"
+#include "object_kind.hpp"
+#include "options.hpp"
+#include "player_class.hpp"
+#include "player_race.hpp"
+#include "player_race_mod.hpp"
+#include "player_type.hpp"
+#include "quark.hpp"
+#include "randart.hpp"
+#include "skill_type.hpp"
+#include "skills.hpp"
+#include "spells1.hpp"
+#include "spells2.hpp"
+#include "stats.hpp"
+#include "store_info_type.hpp"
+#include "tables.hpp"
+#include "trap_type.hpp"
+#include "util.hpp"
+#include "util.h"
+#include "variable.h"
+#include "variable.hpp"
+#include "wilderness_map.hpp"
+#include "wilderness_type_info.hpp"
+#include "wizard2.hpp"
+#include "xtra1.hpp"
+#include "z-rand.hpp"
+
+#include <type_traits>
+#include <cassert>
+
+#include <boost/algorithm/string/predicate.hpp>
+
+using boost::algorithm::iequals;
+
+static void corrupt_corrupted(void);
+
+/*
+ * Set "p_ptr->parasite" and "p_ptr->parasite_r_idx"
+ * notice observable changes
+ */
+bool_ set_parasite(int v, int r)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->parasite)
+ {
+ msg_print("You feel something growing in you.");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->parasite)
+ {
+ if (magik(80))
+ {
+ char r_name[80];
+ int wx, wy;
+ int attempts = 500;
+
+ monster_race_desc(r_name, p_ptr->parasite_r_idx, 0);
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 10);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (place_monster_one(wy, wx, p_ptr->parasite_r_idx, 0, FALSE, MSTATUS_ENEMY))
+ {
+ cmsg_format(TERM_L_BLUE, "Your body convulses and spawns %s.", r_name);
+ p_ptr->food -= 750;
+ if (p_ptr->food < 100) p_ptr->food = 100;
+ }
+ }
+ else
+ {
+ cmsg_print(TERM_L_BLUE, "The hideous thing growing in you seems to die.");
+ }
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->parasite = v;
+ p_ptr->parasite_r_idx = r;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set a simple player field.
+ */
+static bool_ set_simple_field(
+ s16b *p_field,
+ s16b v,
+ byte activate_color,
+ cptr activate_msg,
+ byte deactivate_color,
+ cptr deactivate_msg)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!*p_field)
+ {
+ cmsg_print(activate_color, activate_msg);
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (*p_field)
+ {
+ cmsg_print(deactivate_color, deactivate_msg);
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ *p_field = v;
+
+ /* Nothing to notice */
+ if (!notice)
+ return (FALSE);
+
+ /* Disturb */
+ if (disturb_state)
+ disturb(0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_project" and others
+ * notice observable changes
+ */
+bool_ set_project(int v, s16b gf, s16b dam, s16b rad, s16b flag)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_project, v,
+ TERM_WHITE, "Your weapon starts glowing.",
+ TERM_WHITE, "Your weapon stops glowing.");
+
+ /* Use the values */
+ p_ptr->tim_project_gf = gf;
+ p_ptr->tim_project_dam = dam;
+ p_ptr->tim_project_rad = rad;
+ p_ptr->tim_project_flag = flag;
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_roots" and others
+ * notice observable changes
+ */
+bool_ set_roots(int v, s16b ac, s16b dam)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_roots, v,
+ TERM_WHITE, "Roots dive into the floor from your feet.",
+ TERM_WHITE, "The roots of your feet suddenly vanish.");
+
+ /* Use the values */
+ p_ptr->tim_roots_dam = dam;
+ p_ptr->tim_roots_ac = ac;
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_(magic|water)_breath" and others
+ * notice observable changes
+ */
+bool_ set_tim_breath(int v, bool_ magical)
+{
+ if (magical)
+ {
+ return set_simple_field(
+ &p_ptr->tim_magic_breath, v,
+ TERM_WHITE, "Air seems to fill your lungs without breathing.",
+ TERM_WHITE, "You need to breathe again.");
+ }
+ else
+ {
+ return set_simple_field(
+ &p_ptr->tim_water_breath, v,
+ TERM_WHITE, "Water seems to fill your lungs.",
+ TERM_WHITE, "The water filling your lungs evaporates.");
+ }
+}
+
+/*
+ * Set timered precognition
+ */
+bool_ set_tim_precognition(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_precognition, v,
+ TERM_WHITE, "You feel able to predict the future.",
+ TERM_WHITE, "You feel less able to predict the future.");
+}
+
+/*
+ * Set "p_ptr->absorb_soul"
+ * notice observable changes
+ */
+bool_ set_absorb_soul(int v)
+{
+ return set_simple_field(
+ &p_ptr->absorb_soul, v,
+ TERM_L_DARK, "You start absorbing the souls of your foes.",
+ TERM_L_DARK, "You stop absorbing the souls of dead foes.");
+}
+
+/*
+ * Set "p_ptr->disrupt_shield"
+ * notice observable changes
+ */
+bool_ set_disrupt_shield(int v)
+{
+ return set_simple_field(
+ &p_ptr->disrupt_shield, v,
+ TERM_L_BLUE, "You feel invulnerable.",
+ TERM_L_RED, "You are more vulnerable.");
+}
+
+/*
+ * Set "p_ptr->prob_travel"
+ * notice observable changes
+ */
+bool_ set_prob_travel(int v)
+{
+ return set_simple_field(
+ &p_ptr->prob_travel, v,
+ TERM_WHITE, "You feel instable.",
+ TERM_WHITE, "You are more stable.");
+}
+
+/*
+ * Set "p_ptr->tim_invis", and "p_ptr->tim_inv_pow",
+ * notice observable changes
+ */
+bool_ set_invis(int v, int p)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_invisible, v,
+ TERM_WHITE, "You feel your body fade away.",
+ TERM_WHITE, "You are no longer invisible.");
+
+ /* Use the power value */
+ p_ptr->tim_inv_pow = p;
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_poison",
+ * notice observable changes
+ */
+bool_ set_poison(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_poison, v,
+ TERM_WHITE, "Your hands are dripping with venom.",
+ TERM_WHITE, "The venom source dries out.");
+}
+
+/*
+ * Set "no_breeds"
+ */
+bool_ set_no_breeders(int v)
+{
+ return set_simple_field(
+ &no_breeds, v,
+ TERM_WHITE, "You feel an anti-sexual aura.",
+ TERM_WHITE, "You no longer feel an anti-sexual aura.");
+}
+
+/*
+ * Set "p_ptr->tim_deadly"
+ */
+bool_ set_tim_deadly(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_deadly, v,
+ TERM_WHITE, "You feel extremely accurate.",
+ TERM_WHITE, "You are suddenly much less accurate.");
+}
+
+/*
+ * Set "p_ptr->tim_ffall"
+ */
+bool_ set_tim_ffall(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_ffall, v,
+ TERM_WHITE, "You feel very light.",
+ TERM_WHITE, "You are suddenly heavier.");
+}
+
+/*
+ * Set "p_ptr->tim_fly"
+ */
+bool_ set_tim_fly(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_fly, v,
+ TERM_WHITE, "You feel able to reach the clouds.",
+ TERM_WHITE, "You are suddenly a lot heavier.");
+}
+
+/*
+ * Set "p_ptr->tim_reflect"
+ */
+bool_ set_tim_reflect(int v)
+{
+ return set_simple_field(
+ &p_ptr->tim_reflect, v,
+ TERM_WHITE, "You start reflecting the world around you.",
+ TERM_WHITE, "You stop reflecting.");
+}
+
+/*
+ * Set "p_ptr->strike"
+ */
+bool_ set_strike(int v)
+{
+ return set_simple_field(
+ &p_ptr->strike, v,
+ TERM_WHITE, "You feel very accurate.",
+ TERM_WHITE, "You are no longer very accurate.");
+}
+
+/*
+ * Set "p_ptr->oppose_ld"
+ */
+bool_ set_oppose_ld(int v)
+{
+ return set_simple_field(
+ &p_ptr->oppose_ld, v,
+ TERM_WHITE, "You feel protected against light's fluctuation.",
+ TERM_WHITE, "You are no longer protected against light's fluctuation.");
+}
+
+/*
+ * Set "p_ptr->oppose_cc"
+ */
+bool_ set_oppose_cc(int v)
+{
+ return set_simple_field(
+ &p_ptr->oppose_cc, v,
+ TERM_WHITE, "You feel protected against raw chaos.",
+ TERM_WHITE, "You are no longer protected against chaos.");
+}
+
+/*
+ * Set "p_ptr->oppose_ss"
+ */
+bool_ set_oppose_ss(int v)
+{
+ return set_simple_field(
+ &p_ptr->oppose_ss, v,
+ TERM_WHITE, "You feel protected against the ravages of sound and shards.",
+ TERM_WHITE, "You are no longer protected against the ravages of sound and shards.");
+}
+
+/*
+ * Set "p_ptr->oppose_nex"
+ */
+bool_ set_oppose_nex(int v)
+{
+ return set_simple_field(
+ &p_ptr->oppose_nex, v,
+ TERM_WHITE, "You feel protected against the strange forces of nexus.",
+ TERM_WHITE, "You are no longer protected against the strange forces of nexus.");
+}
+
+/*
+ * Set "p_ptr->tim_mimic", and "p_ptr->mimic_form",
+ * notice observable changes
+ */
+bool_ set_mimic(int v, int p, int level)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_mimic)
+ {
+ msg_print("You feel your body change.");
+ p_ptr->mimic_form = p;
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_mimic)
+ {
+ msg_print("You are no longer transformed.");
+ p_ptr->mimic_form = 0;
+ notice = TRUE;
+ if (p == resolve_mimic_name("Bear"))
+ {
+ s_info[SKILL_BEAR].hidden = TRUE;
+ select_default_melee();
+ }
+ p = 0;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_mimic = v;
+ p_ptr->mimic_level = level;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0);
+
+ /* Redraw title */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BODY | PU_BONUS | PU_SANITY);
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->blind", notice observable changes
+ *
+ * Note the use of "PU_UN_VIEW", which is needed to memorize any terrain
+ * features which suddenly become "visible".
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ */
+bool_ set_blind(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->blind, v,
+ TERM_WHITE, "You are blind!",
+ TERM_WHITE, "You can see again.");
+
+ if (notice)
+ {
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Redraw the "blind" */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_lite", notice observable changes
+ *
+ * Note the use of "PU_VIEW", which is needed to
+ * memorize any terrain features which suddenly become "visible".
+ * Note that blindness is currently the only thing which can affect
+ * "player_can_see_bold()".
+ */
+bool_ set_lite(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_lite, v,
+ TERM_WHITE, "You suddenly seem brighter!",
+ TERM_WHITE, "You are no longer bright.");
+
+ if (notice)
+ {
+ /* Fully update the visuals */
+ p_ptr->update |= (PU_VIEW | PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->confused", notice observable changes
+ */
+bool_ set_confused(int v)
+{
+ bool_ notice =
+ set_simple_field(
+ &p_ptr->confused, v,
+ TERM_WHITE, "You are confused!",
+ TERM_WHITE, "You feel less confused now.");
+
+ if (notice)
+ {
+ /* Redraw the "confused" */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->poisoned", notice observable changes
+ */
+bool_ set_poisoned(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->poisoned, v,
+ TERM_WHITE, "You are poisoned!",
+ TERM_WHITE, "You are no longer poisoned.");
+
+ if (notice)
+ {
+ /* Redraw the "poisoned" */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->afraid", notice observable changes
+ */
+bool_ set_afraid(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->afraid, v,
+ TERM_WHITE, "You are terrified!",
+ TERM_WHITE, "You feel bolder now.");
+
+ if (notice)
+ {
+ /* Redraw the "afraid" */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Mechanics for setting the "paralyzed" field.
+ */
+static bool_ set_paralyzed_aux(int v)
+{
+ bool_ notice;
+
+ /* Normal processing */
+ notice = set_simple_field(
+ &p_ptr->paralyzed, v,
+ TERM_WHITE, "You are paralyzed!",
+ TERM_WHITE, "You can move again.");
+
+ if (notice)
+ {
+ /* Redraw the state */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->paralyzed", notice observable changes
+ */
+bool_ set_paralyzed(int v)
+{
+ /* Paralysis effects do not accumulate -- this is to
+ prevent the uninteresting insta-death effect, but
+ still leave paralyzers highly dangerous if they're
+ faster than the player. */
+
+ if (p_ptr->paralyzed > 0) {
+ return FALSE;
+ }
+
+ /* Normal processing */
+ return set_paralyzed_aux(v);
+}
+
+/*
+ * Decrement "p_ptr->paralyzed", notice observable changes
+ */
+void dec_paralyzed()
+{
+ set_paralyzed_aux(p_ptr->paralyzed - 1);
+}
+
+
+/*
+ * Set "p_ptr->image", notice observable changes
+ *
+ * Note that we must redraw the map when hallucination changes.
+ */
+bool_ set_image(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->image, v,
+ TERM_WHITE, "Oh, wow! Everything looks so cosmic now!",
+ TERM_WHITE, "You can see clearly again.");
+
+ if (notice)
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD | PW_M_LIST);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->lightspeed", notice observable changes
+ */
+bool_ set_light_speed(int v)
+{
+ bool_ notice =
+ set_simple_field(
+ &p_ptr->lightspeed, v,
+ TERM_WHITE, "You feel as if time has stopped!",
+ TERM_WHITE, "You feel time returning to its normal rate.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+bool_ set_fast(int v, int p)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->fast)
+ {
+ msg_print("You feel yourself moving faster!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->fast)
+ {
+ msg_print("You feel yourself slow down.");
+ p = 0;
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->fast = v;
+ p_ptr->speed_factor = p;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Set "p_ptr->slow", notice observable changes
+ */
+bool_ set_slow(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->slow, v,
+ TERM_WHITE, "You feel yourself moving slower!",
+ TERM_WHITE, "You feel yourself speed up.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->shield", notice observable changes
+ */
+bool_ set_shield(int v, int p, s16b o, s16b d1, s16b d2)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->shield, v,
+ TERM_WHITE, "A mystic shield forms around your body!",
+ TERM_WHITE, "Your mystic shield crumbles away.");
+
+ /* Use the values */
+ p_ptr->shield_power = p;
+ p_ptr->shield_opt = o;
+ p_ptr->shield_power_opt = d1;
+ p_ptr->shield_power_opt2 = d2;
+
+ /* Notice? */
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+
+/*
+ * Set "p_ptr->blessed", notice observable changes
+ */
+bool_ set_blessed(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->blessed, v,
+ TERM_WHITE, "You feel righteous!",
+ TERM_WHITE, "The prayer has expired.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->hero", notice observable changes
+ */
+bool_ set_hero(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->hero, v,
+ TERM_WHITE, "You feel like a hero!",
+ TERM_WHITE, "The heroism wears off.");
+
+ if (notice)
+ {
+ /* Recalculate hitpoints */
+ p_ptr->update |= (PU_HP);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->holy", notice observable changes
+ */
+bool_ set_holy(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->holy, v,
+ TERM_WHITE, "You feel a holy aura around you!",
+ TERM_WHITE, "The holy aura vanishes.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->shero", notice observable changes
+ */
+bool_ set_shero(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->shero, v,
+ TERM_WHITE, "You feel like a killing machine!",
+ TERM_WHITE, "You feel less berserk.");
+
+ if (notice)
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS | PU_HP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->protevil", notice observable changes
+ */
+bool_ set_protevil(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->protevil, v,
+ TERM_WHITE, "You feel safe from evil!",
+ TERM_WHITE, "You no longer feel safe from evil.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->protgood", notice observable changes
+ */
+bool_ set_protgood(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->protgood, v,
+ TERM_WHITE, "You feel safe from good!",
+ TERM_WHITE, "You no longer feel safe from good.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->protundead", notice observable changes
+ */
+bool_ set_protundead(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->protundead, v,
+ TERM_WHITE, "You feel safe from undead!",
+ TERM_WHITE, "You no longer feel safe from undead.");
+
+ if (notice) {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->set_shadow", notice observable changes
+ */
+bool_ set_shadow(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_wraith, v,
+ TERM_WHITE, "You leave the physical world and turn into a wraith-being!",
+ TERM_WHITE, "You feel opaque.");
+
+ if (notice)
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+
+
+/*
+ * Set "p_ptr->invuln", notice observable changes
+ */
+bool_ set_invuln(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->invuln, v,
+ TERM_L_BLUE, "Invulnerability!",
+ TERM_L_RED, "The invulnerability wears off.");
+
+ if (notice)
+ {
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Update monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+
+/*
+ * Set "p_ptr->tim_esp", notice observable changes
+ */
+bool_ set_tim_esp(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_esp, v,
+ TERM_WHITE, "You feel your consciousness expand!",
+ TERM_WHITE, "Your consciousness contracts again.");
+
+ if (notice)
+ {
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+/*
+ * Set "p_ptr->tim_thunder", notice observable changes
+ */
+bool_ set_tim_thunder(int v, int p1, int p2)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_thunder)
+ {
+ msg_print("The air around you charges with lightning!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_thunder)
+ {
+ msg_print("The air around you discharges.");
+ notice = TRUE;
+ p1 = p2 = 0;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_thunder = v;
+ p_ptr->tim_thunder_p1 = p1;
+ p_ptr->tim_thunder_p2 = p2;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+/*
+ * Set "p_ptr->tim_invis", notice observable changes
+ */
+bool_ set_tim_invis(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_invis, v,
+ TERM_WHITE, "Your eyes feel very sensitive!",
+ TERM_WHITE, "Your eyes feel less sensitive.");
+
+ if (notice)
+ {
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->tim_infra", notice observable changes
+ */
+bool_ set_tim_infra(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->tim_infra, v,
+ TERM_WHITE, "Your eyes begin to tingle!",
+ TERM_WHITE, "Your eyes stop tingling.");
+
+ if (notice)
+ {
+ /* Update the monsters */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->oppose_acid", notice observable changes
+ */
+bool_ set_oppose_acid(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_acid, v,
+ TERM_WHITE, "You feel resistant to acid!",
+ TERM_WHITE, "You feel less resistant to acid.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->oppose_elec", notice observable changes
+ */
+bool_ set_oppose_elec(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_elec, v,
+ TERM_WHITE, "You feel resistant to electricity!",
+ TERM_WHITE, "You feel less resistant to electricity.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->oppose_fire", notice observable changes
+ */
+bool_ set_oppose_fire(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_fire, v,
+ TERM_WHITE, "You feel resistant to fire!",
+ TERM_WHITE, "You feel less resistant to fire.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->oppose_cold", notice observable changes
+ */
+bool_ set_oppose_cold(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_cold, v,
+ TERM_WHITE, "You feel resistant to cold!",
+ TERM_WHITE, "You feel less resistant to cold.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->oppose_pois", notice observable changes
+ */
+bool_ set_oppose_pois(int v)
+{
+ bool_ notice = set_simple_field(
+ &p_ptr->oppose_pois, v,
+ TERM_WHITE, "You feel resistant to poison!",
+ TERM_WHITE, "You feel less resistant to poison.");
+
+ if (notice)
+ {
+ /* Handle stuff */
+ handle_stuff();
+ }
+
+ /* Result */
+ return notice;
+}
+
+
+/*
+ * Set "p_ptr->tim_regen", notice observable changes
+ */
+bool_ set_tim_regen(int v, int p)
+{
+ bool_ notice = FALSE;
+
+ /* Hack -- Force good values */
+ v = (v > 10000) ? 10000 : (v < 0) ? 0 : v;
+
+ /* Open */
+ if (v)
+ {
+ if (!p_ptr->tim_regen)
+ {
+ msg_print("Your body regenerates much more quickly!");
+ notice = TRUE;
+ }
+ }
+
+ /* Shut */
+ else
+ {
+ if (p_ptr->tim_regen)
+ {
+ p = 0;
+ msg_print("Your body regenerates much more slowly.");
+ notice = TRUE;
+ }
+ }
+
+ /* Use the value */
+ p_ptr->tim_regen = v;
+ p_ptr->tim_regen_pow = p;
+
+ /* Nothing to notice */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0);
+
+ /* 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 (race_flags1_p(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);
+ break;
+ }
+
+ /* Notice */
+ notice = TRUE;
+ }
+
+ /* Use the value */
+ p_ptr->stun = v;
+
+ /* No change */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the "stun" */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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 (race_flags1_p(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);
+ break;
+ }
+
+ /* Notice */
+ notice = TRUE;
+ }
+
+ /* Use the value */
+ p_ptr->cut = v;
+
+ /* No change */
+ if (!notice) return (FALSE);
+
+ /* Disturb */
+ if (disturb_state) disturb(0);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw the "cut" */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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);
+
+ /* Recalculate bonuses */
+ p_ptr->update |= (PU_BONUS);
+
+ /* Redraw hunger */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Result */
+ return (TRUE);
+}
+
+
+/*
+ * Advance experience levels and print experience
+ */
+void check_experience(void)
+{
+ int gained = 0;
+ bool_ level_corruption = FALSE;
+
+
+ /* Hack -- lower limit */
+ if (p_ptr->exp < 0) p_ptr->exp = 0;
+
+ /* Hack -- lower limit */
+ if (p_ptr->max_exp < 0) p_ptr->max_exp = 0;
+
+ /* Hack -- upper limit */
+ if (p_ptr->exp > PY_MAX_EXP) p_ptr->exp = PY_MAX_EXP;
+
+ /* Hack -- upper limit */
+ if (p_ptr->max_exp > PY_MAX_EXP) p_ptr->max_exp = PY_MAX_EXP;
+
+ /* Hack -- maintain "max" experience */
+ if (p_ptr->exp > p_ptr->max_exp) p_ptr->max_exp = p_ptr->exp;
+
+ /* Redraw experience */
+ p_ptr->redraw |= (PR_FRAME);
+
+ /* 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_FRAME);
+
+ /* 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 ((race_flags1_p(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)
+ {
+ p_ptr->skill_last_level = p_ptr->lev;
+ p_ptr->skill_points += modules[game_module_idx].skills.skill_per_level;
+ cmsg_format(TERM_L_GREEN, "You can increase %d more skills.", p_ptr->skill_points);
+ p_ptr->redraw |= PR_FRAME;
+ }
+
+ /* Gain this level's abilities */
+ apply_level_abilities(p_ptr->lev);
+
+ /* Note level gain */
+ {
+ 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_FRAME);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_PLAYER);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ if (level_corruption)
+ {
+ msg_print("You feel different...");
+ corrupt_corrupted();
+ level_corruption = FALSE;
+ }
+ }
+
+ /* Hook it! */
+ {
+ hook_player_level_in in = { gained };
+ process_hooks_new(HOOK_PLAYER_LEVEL, &in, NULL);
+ }
+}
+
+/*
+ * Advance experience levels and print experience
+ */
+void check_experience_obj(object_type *o_ptr)
+{
+ /* Hack -- lower limit */
+ if (o_ptr->exp < 0) o_ptr->exp = 0;
+
+ /* Hack -- upper limit */
+ if (o_ptr->exp > PY_MAX_EXP) o_ptr->exp = PY_MAX_EXP;
+
+ /* Gain levels while possible */
+ while ((o_ptr->elevel < PY_MAX_LEVEL) &&
+ (o_ptr->exp >= (player_exp[o_ptr->elevel - 1] * 5 / 2)))
+ {
+ char buf[100];
+
+ /* Add a level */
+ o_ptr->elevel++;
+
+ /* Get object name */
+ object_desc(buf, o_ptr, 1, 0);
+ cmsg_format(TERM_L_BLUE, "%s gains a level!", buf);
+
+ /* What does it gains ? */
+ object_gain_level(o_ptr);
+ }
+}
+
+
+/*
+ * Gain experience
+ */
+void gain_exp(s32b amount)
+{
+ if ((p_ptr->max_exp > 0) && (race_flags1_p(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;
+
+ /* 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;
+
+ /* 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(std::shared_ptr<monster_race const> r_ptr)
+{
+ cptr 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)
+{
+ const int x = m_ptr->fx;
+ const int y = m_ptr->fy;
+
+ /* Get local object */
+ object_type object_type_body;
+ object_type *i_ptr = &object_type_body;
+
+ /* Get the race information */
+ auto const r_ptr = m_ptr->race();
+
+ /* 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);
+ }
+
+}
+
+
+/*
+ * Check if monster race is in a given list. The list
+ * must be NULL-terminated.
+ */
+static bool_ monster_race_in_list_p(monster_type *m_ptr, cptr races[])
+{
+ int i=0;
+ for (i=0; races[i] != NULL; i++)
+ {
+ if (m_ptr->r_idx == test_monster_name(races[i])) {
+ return TRUE;
+ }
+ }
+ /* Not found */
+ return FALSE;
+}
+
+/*
+ * Handle the "death" of a monster (Gods)
+ */
+static void monster_death_gods(int m_idx, monster_type *m_ptr)
+{
+ if (p_ptr->pgod == GOD_AULE)
+ {
+ /* TODO: This should really be a racial flag
+ which can be added to the r_info file. */
+ cptr DWARVES[] = {
+ "Petty-dwarf",
+ "Petty-dwarf mage",
+ "Dark dwarven warrior",
+ "Dark dwarven smith",
+ "Dark dwarven lord",
+ "Dark dwarven priest",
+ "Dwarven warrior",
+ NULL,
+ };
+ cptr UNIQUE_DWARVES[] = {
+ "Nar, the Dwarf",
+ "Naugladur, Lord of Nogrod",
+ "Telchar the Smith",
+ "Fundin Bluecloak",
+ "Khim, Son of Mim",
+ "Ibun, Son of Mim",
+ "Mim, Betrayer of Turin",
+ NULL,
+ };
+
+ /* Aule dislikes the killing of dwarves */
+ if (monster_race_in_list_p(m_ptr, DWARVES))
+ {
+ inc_piety(GOD_ALL, -20);
+ }
+
+ /* ... and UNIQUE dwarves */
+ if (monster_race_in_list_p(m_ptr, UNIQUE_DWARVES))
+ {
+ inc_piety(GOD_ALL, -500);
+ }
+ }
+
+ if (p_ptr->pgod == GOD_ULMO)
+ {
+ /* He doesn't like it if you kill these monsters */
+ cptr MINOR_RACES[] = {
+ "Swordfish",
+ "Barracuda",
+ "Globefish",
+ "Aquatic bear",
+ "Pike",
+ "Electric eel",
+ "Giant crayfish",
+ "Mermaid",
+ "Leviathan",
+ "Box jellyfish",
+ "Giant piranha",
+ "Piranha",
+ "Ocean naga",
+ "Whale",
+ "Octopus",
+ "Giant octopus",
+ "Drowned soul",
+ "Tiger shark",
+ "Hammerhead shark",
+ "Great white shark",
+ "White shark",
+ "Stargazer",
+ "Flounder",
+ "Giant turtle",
+ "Killer whale",
+ "Water naga",
+ "Behemoth",
+ NULL,
+ };
+ /* These monsters earn higher penalties */
+ cptr MAJOR_RACES[] = {
+ "Seahorse",
+ "Aquatic elven warrior",
+ "Aquatic elven mage",
+ "Wavelord",
+ "The Watcher in the Water",
+ NULL,
+ };
+
+ if (monster_race_in_list_p(m_ptr, MINOR_RACES))
+ {
+ inc_piety(GOD_ALL, -20);
+ }
+
+ if (monster_race_in_list_p(m_ptr, MAJOR_RACES))
+ {
+ inc_piety(GOD_ALL, -500);
+ }
+ }
+
+ if (p_ptr->pgod == GOD_MANDOS)
+ {
+ cptr MINOR_BONUS_RACES[] = {
+ "Vampire",
+ "Master vampire",
+ "Oriental vampire",
+ "Vampire lord",
+ "Vampire orc",
+ "Vampire yeek",
+ "Vampire ogre",
+ "Vampire troll",
+ "Vampire dwarf",
+ "Vampire gnome",
+ "Elder vampire",
+ NULL,
+ };
+ cptr MAJOR_BONUS_RACES[] = {
+ "Vampire elf",
+ "Thuringwethil, the Vampire Messenger",
+ NULL,
+ };
+ cptr MINOR_PENALTY[] = {
+ "Dark elf",
+ "Dark elven druid",
+ "Eol, the Dark Elf",
+ "Maeglin, the Traitor of Gondolin",
+ "Dark elven mage",
+ "Dark elven warrior",
+ "Dark elven priest",
+ "Dark elven lord",
+ "Dark elven warlock",
+ "Dark elven sorcerer",
+ NULL,
+ };
+ cptr MEDIUM_PENALTY[] = {
+ "Glorfindel of Rivendell",
+ "Finrod Felagund",
+ "Thranduil, King of the Wood Elves",
+ "Aquatic elven warrior",
+ "Aquatic elven mage",
+ "High-elven ranger",
+ "Elven archer",
+ NULL,
+ };
+ cptr MAJOR_PENALTY[] = {
+ "Child spirit",
+ "Young spirit",
+ "Mature spirit",
+ "Experienced spirit",
+ "Wise spirit",
+ NULL,
+ };
+
+ if (monster_race_in_list_p(m_ptr, MINOR_BONUS_RACES))
+ {
+ /* He really likes it if you kill Vampires
+ * (but not the adventurer kind :P) */
+ inc_piety(GOD_ALL, 50);
+ }
+
+ if (monster_race_in_list_p(m_ptr, MAJOR_BONUS_RACES))
+ {
+ /* He *loves* it if you kill vampire Elves. He
+ * will also thank you extra kindly if you
+ * kill Thuringwethil */
+ inc_piety(GOD_ALL, 200);
+ }
+
+ if (monster_race_in_list_p(m_ptr, MINOR_PENALTY))
+ {
+ /* He doesn't like it if you kill normal Elves
+ * (means more work for him :P) */
+ inc_piety(GOD_ALL, -20);
+ }
+
+ if (monster_race_in_list_p(m_ptr, MEDIUM_PENALTY))
+ {
+ /* He hates it if you kill coaligned Elves */
+ inc_piety(GOD_ALL, -200);
+ }
+
+ if (monster_race_in_list_p(m_ptr, MAJOR_PENALTY))
+ {
+ /* He *hates* it if you kill the coaligned Spirits */
+ inc_piety(GOD_ALL, -1000);
+ }
+ }
+}
+
+/*
+ * 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 dump_item = 0;
+ int dump_gold = 0;
+
+ monster_type *m_ptr = &m_list[m_idx];
+
+ auto const r_ptr = m_ptr->race();
+
+ bool_ visible = (m_ptr->ml || (r_ptr->flags1 & (RF1_UNIQUE)));
+
+ bool_ create_stairs = FALSE;
+ int force_coin = get_coin_type(r_ptr);
+
+ object_type forge;
+ object_type *q_ptr;
+
+ /* Get the location */
+ int y = m_ptr->fy;
+ int x = m_ptr->fx;
+
+ /* Process the appropriate hooks */
+ {
+ struct hook_monster_death_in in = { m_idx };
+ process_hooks_new(HOOK_MONSTER_DEATH, &in, NULL);
+ }
+
+ /* Per-god processing */
+ monster_death_gods(m_idx, m_ptr);
+
+ /* 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_FRAME);
+
+ /* 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");
+ }
+ }
+
+ /* If the doppleganger die, the variable must be set accordingly */
+ if (r_ptr->flags9 & RF9_DOPPLEGANGER) doppleganger = 0;
+
+ /* Need copy of object list since we're going to mutate it */
+ auto const object_idxs(m_ptr->hold_o_idxs);
+
+ /* Drop objects being carried */
+ for (auto const this_o_idx: object_idxs)
+ {
+ /* Acquire object */
+ object_type * o_ptr = &o_list[this_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_idxs.clear();
+
+ /* 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_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_ptr->name, "the Dawn"))
+ {
+ if (!(randint(20) == 13))
+ {
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 100;
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 20);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (attempts > 0)
+ {
+ if (is_friend(m_ptr) > 0)
+ {
+ if (summon_specific_friendly(wy, wx, 100, SUMMON_DAWN, FALSE))
+ {
+ if (player_can_see_bold(wy, wx))
+ msg_print ("A new warrior steps forth!");
+ }
+ }
+ else
+ {
+ if (summon_specific(wy, wx, 100, SUMMON_DAWN))
+ {
+ if (player_can_see_bold(wy, wx))
+ msg_print ("A new warrior steps forth!");
+ }
+ }
+ }
+ }
+ }
+
+ /* One more ultra-hack: An Unmaker goes out with a big bang! */
+ else if (strstr(r_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_ptr->name, "ink horror"))
+ {
+ for (int i = 0; i < 2; i++)
+ {
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 100;
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 3);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ if (attempts > 0)
+ {
+ if (summon_specific(wy, wx, 100, SUMMON_BLUE_HORROR))
+ {
+ if (player_can_see_bold(wy, wx))
+ msg_print ("A blue horror appears!");
+ }
+ }
+ }
+ }
+
+ /* Mega-Hack -- drop "winner" treasures */
+ else if (r_ptr->flags1 & (RF1_DROP_CHOSEN))
+ {
+ if (strstr(r_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_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_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_ptr->name, "Marda, rider of the Gold Laronth"))
+ {
+ a_idx = ART_MARDA;
+ chance = 50;
+ }
+ else if (strstr(r_ptr->name, "Saruman of Many Colours"))
+ {
+ a_idx = ART_PALANTIR;
+ chance = 30;
+ }
+ else if (strstr(r_ptr->name, "Hagen, son of Alberich"))
+ {
+ a_idx = ART_NIMLOTH;
+ chance = 66;
+ }
+ else if (strstr(r_ptr->name, "Durin's Bane"))
+ {
+ a_idx = ART_CALRIS;
+ chance = 60;
+ }
+ else if (strstr(r_ptr->name, "Gothmog, the High Captain of Balrogs"))
+ {
+ a_idx = ART_GOTHMOG;
+ chance = 50;
+ }
+ else if (strstr(r_ptr->name, "Eol, the Dark Elf"))
+ {
+ a_idx = ART_ANGUIREL;
+ chance = 50;
+ }
+
+ if ((a_idx > 0) && ((randint(99) < chance) || (wizard)))
+ {
+ if (a_info[a_idx].cur_num == 0)
+ {
+ artifact_type *a_ptr = &a_info[a_idx];
+
+ /* Get local object */
+ q_ptr = &forge;
+
+ /* Wipe the object */
+ object_wipe(q_ptr);
+
+ /* Acquire the "kind" index */
+ I_kind = lookup_kind(a_ptr->tval, a_ptr->sval);
+
+ /* Create the artifact */
+ object_prep(q_ptr, I_kind);
+
+ /* Save the name */
+ q_ptr->name1 = a_idx;
+
+ /* Extract the fields */
+ q_ptr->pval = a_ptr->pval;
+ q_ptr->ac = a_ptr->ac;
+ q_ptr->dd = a_ptr->dd;
+ q_ptr->ds = a_ptr->ds;
+ q_ptr->to_a = a_ptr->to_a;
+ q_ptr->to_h = a_ptr->to_h;
+ q_ptr->to_d = a_ptr->to_d;
+ q_ptr->weight = a_ptr->weight;
+
+ /* Hack -- acquire "cursed" flag */
+ if (a_ptr->flags3 & (TR3_CURSED)) q_ptr->ident |= (IDENT_CURSED);
+
+ random_artifact_resistance(q_ptr);
+
+ a_info[a_idx].cur_num = 1;
+
+ q_ptr->found = OBJ_FOUND_MONSTER;
+ q_ptr->found_aux1 = m_ptr->r_idx;
+ q_ptr->found_aux2 = m_ptr->ego;
+ q_ptr->found_aux3 = dungeon_type;
+ q_ptr->found_aux4 = level_or_feat(dungeon_type, dun_level);
+
+ /* Drop the artifact from heaven */
+ drop_near(q_ptr, -1, y, x);
+ }
+ }
+ }
+ }
+
+ /* Hack - the protected monsters must be advanged */
+ else if (r_ptr->flags9 & RF9_WYRM_PROTECT)
+ {
+ int xx = x, yy = y;
+ int attempts = 100;
+
+ cmsg_print(TERM_VIOLET, "This monster was under the protection of a Great Wyrm of Power!");
+
+ do
+ {
+ scatter(&yy, &xx, y, x, 6);
+ }
+ while (!(in_bounds(yy, xx) && cave_floor_bold(yy, xx)) && --attempts);
+
+ place_monster_aux(yy, xx, test_monster_name("Great Wyrm of Power"), FALSE, FALSE, m_ptr->status);
+ }
+
+ /* Let monsters explode! */
+ for (int 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);
+ }
+
+ /* Create a magical staircase */
+ if (create_stairs && (dun_level < d_info[dungeon_type].maxdepth))
+ {
+ for (int i = -1; i <= 1; i++)
+ {
+ for (int 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))
+ {
+ /* Pick a location */
+ int ny, nx;
+ scatter(&ny, &nx, y, x, 1);
+
+ /* 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];
+ auto const r_idx = m_ptr->r_idx;
+ auto const r_ptr = m_ptr->race();
+
+ /* Redraw (later) if needed */
+ if (health_who == m_idx) p_ptr->redraw |= (PR_FRAME);
+
+ /* 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 (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 */
+ const s32b div = p_ptr->max_plv;
+
+ if (m_ptr->status < MSTATUS_FRIEND)
+ {
+ /* Give some experience for the kill */
+ int new_exp = ((long)r_ptr->mexp * m_ptr->level) / div;
+
+ /* Handle fractional experience */
+ const s32b new_exp_frac = ((((long)r_ptr->mexp * m_ptr->level) % div)
+ * 0x10000L / div) + p_ptr->exp_frac;
+
+ /* Keep track of experience */
+ if (new_exp_frac >= 0x10000L)
+ {
+ new_exp++;
+ p_ptr->exp_frac = new_exp_frac - 0x10000L;
+ }
+ else
+ {
+ p_ptr->exp_frac = new_exp_frac;
+ }
+
+ /* Gain experience */
+ gain_exp(new_exp);
+ }
+
+ if (!note)
+ {
+ object_type *o_ptr;
+ u32b f1, f2, f3, f4, f5, esp;
+
+ /* Access the weapon */
+ o_ptr = &p_ptr->inventory[INVEN_WIELD];
+ object_flags(o_ptr, &f1, &f2, &f3, &f4, &f5, &esp);
+
+ /* Can the weapon gain levels ? */
+ if ((o_ptr->k_idx) && (f4 & TR4_LEVELS))
+ {
+ /* Give some experience for the kill */
+ const int 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 = std::max(1, m_ptr->level / 2);
+
+ if (praying_to(GOD_MANWE))
+ {
+ inc_piety(GOD_MANWE, inc);
+ }
+
+ inc = std::max(2, inc);
+
+ inc_piety(GOD_TULKAS, inc / 2);
+
+ if (praying_to(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 = std::max(1, m_ptr->level / 2);
+ inc_piety(GOD_YAVANNA, inc);
+ }
+
+ /* Yavanna doesnt like any killing in her name */
+ if (praying_to(GOD_YAVANNA))
+ {
+ int inc = std::max(1, m_ptr->level / 2);
+ 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_idx == q_ptr->r_idx)
+ {
+ /* Invalidate it */
+ q_ptr->type = 0;
+ }
+ }
+ }
+
+ /* Make note of unique kills */
+ if (r_ptr->flags1 & RF1_UNIQUE)
+ {
+ char note[80];
+
+ /* Write note */
+ sprintf(note, "Killed %s", r_ptr->name);
+
+ 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);
+ }
+
+ /* Apply fear */
+ mon_handle_fear(m_ptr, dam, fear);
+
+ /* 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;
+}
+
+/*
+ * 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);
+
+ /* Recalculate the boundaries */
+ panel_bounds();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+}
+
+
+/*
+ * Map resizing whenever the main term changes size
+ */
+void resize_map(void)
+{
+ /* Only if the dungeon exists */
+ if (!character_dungeon) return;
+
+ /* Mega-Hack -- No panel yet, assume illegal panel */
+ panel_row_min = cur_hgt;
+ panel_row_max = 0;
+ panel_col_min = cur_wid;
+ panel_col_max = 0;
+
+ /* Select player panel */
+ verify_panel();
+
+ /*
+ * The following should be the same as the main window code
+ * in the do_cmd_redraw()
+ */
+
+ /* Combine and reorder the pack (later) */
+ p_ptr->notice |= (PN_COMBINE | PN_REORDER);
+
+ /* Update torch */
+ p_ptr->update |= (PU_TORCH);
+
+ /* Update stuff */
+ p_ptr->update |= (PU_BONUS | PU_HP | PU_MANA | PU_SPELLS | PU_POWERS |
+ PU_SANITY | PU_BODY);
+
+ /* Forget and update view */
+ p_ptr->update |= (PU_UN_VIEW | PU_VIEW | PU_MONSTERS | PU_MON_LITE);
+
+ /* Redraw everything */
+ p_ptr->redraw |= (PR_WIPE | PR_FRAME | PR_MAP);
+
+ /* Hack -- update */
+ handle_stuff();
+
+ /* Redraw */
+ Term_redraw();
+
+ /* Refresh */
+ Term_fresh();
+}
+
+
+/*
+ * Redraw a term when it is resized
+ */
+void resize_window(void)
+{
+ /* Only if the dungeon exists */
+ if (!character_dungeon) return;
+
+ /* Hack -- Activate the Angband window for the redraw */
+ Term_activate(&term_screen[0]);
+
+ /* Hack -- react to changes */
+ Term_xtra(TERM_XTRA_REACT, 0);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_M_LIST | PW_MESSAGE | PW_OVERHEAD |
+ PW_MONSTER | PW_OBJECT);
+
+
+ /* Hack -- update */
+ handle_stuff();
+
+ /* Refresh */
+ Term_fresh();
+}
+
+
+
+
+/*
+ * Monster health description
+ */
+static cptr look_mon_desc(int m_idx)
+{
+ bool_ living = TRUE;
+
+ /* Determine if the monster is "living" (vs "undead") */
+ monster_type *m_ptr = &m_list[m_idx];
+ auto const r_ptr = m_ptr->race();
+ 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" */
+ const int 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");
+}
+
+
+
+/*
+ * Current "comp" function for ang_sort()
+ */
+static bool_ (*ang_sort_comp)(vptr u, vptr v, int a, int b) = nullptr;
+
+/*
+ * Current "swap" function for ang_sort()
+ */
+static void (*ang_sort_swap)(vptr u, vptr v, int a, int b) = nullptr;
+
+
+
+/*
+ * 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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)
+{
+ /* 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 */
+ cave_type *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 (auto const this_o_idx: c_ptr->o_idxs)
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[this_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 "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];
+
+ 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];
+ auto const r_ptr = m_ptr->race();
+
+ /* Mimics special treatment -- looks like an object */
+ if ((r_ptr->flags9 & RF9_MIMIC) && (m_ptr->csleep))
+ {
+ /* Acquire object */
+ object_type *o_ptr = &o_list[m_ptr->mimic_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%s[r,%s]",
+ s1, s2, s3, m_name,
+ m_ptr->level, look_mon_desc(c_ptr->m_idx),
+ (m_ptr->csleep) ? ", asleep" : "",
+ (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 */
+ std::size_t i = 0;
+ for (; i < m_ptr->hold_o_idxs.size(); i++)
+ {
+ /* Acquire object */
+ auto this_o_idx = m_ptr->hold_o_idxs.at(i);
+ object_type *o_ptr = &o_list[this_o_idx];
+
+ /* Obtain an object description */
+ char o_name[80];
+ 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 (i != m_ptr->hold_o_idxs.size())
+ {
+ break;
+ }
+
+ /* Use a preposition */
+ s2 = "on ";
+ }
+ }
+ }
+
+ /* Scan all objects in the grid */
+ {
+ std::size_t i = 0;
+ for (; i < c_ptr->o_idxs.size(); i++)
+ {
+ /* Acquire object */
+ auto this_o_idx = c_ptr->o_idxs.at(i);
+ object_type *o_ptr = &o_list[this_o_idx];
+
+ /* Describe it */
+ if (o_ptr->marked && target_object(y, x, mode, info, &boring, o_ptr, out_val, &s1, &s2, &s3, &query))
+ {
+ break;
+ }
+ }
+
+ /* Double break? */
+ if (i != c_ptr->o_idxs.size())
+ {
+ 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_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_info[c_ptr->special].name;
+ }
+ else
+ {
+ 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_info[c_ptr->special].text;
+ }
+
+ if (p_ptr->wild_mode && (feat == FEAT_TOWN))
+ {
+ s3 = "";
+ name = format("%s(%s)",
+ wf_info[wild_map[y][x].feat].name,
+ wf_info[wild_map[y][x].feat].text);
+ }
+
+ if ((feat == FEAT_FOUNTAIN) && (c_ptr->info & CAVE_IDNT))
+ {
+ 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;
+ }
+
+ info = k_info[lookup_kind(tv, sv)].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;
+ }
+ break;
+ }
+
+ case '-':
+ {
+ if (m-- == 0)
+ {
+ m = temp_n - 1;
+ }
+ break;
+ }
+
+ case 'p':
+ {
+ /* Recenter the map around the player */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Recalculate interesting grids */
+ target_set_prepare(mode);
+
+ y = p_ptr->py;
+ x = p_ptr->px;
+
+ /* Fall through... */
+ }
+
+ case 'o':
+ {
+ flag = FALSE;
+ break;
+ }
+
+ case 'm':
+ {
+ break;
+ }
+
+ default:
+ {
+ /* Extract the action (if any) */
+ d = get_keymap_dir(query);
+
+ if (!d) bell();
+ break;
+ }
+ }
+
+ /* Hack -- move around */
+ if (d)
+ {
+ /* Find a new monster */
+ i = target_pick(temp_y[m], temp_x[m], ddy[d], ddx[d]);
+
+ /* Scroll to find interesting grid */
+ if (i < 0)
+ {
+ int dy;
+ int dx;
+
+ dy = ddy[d];
+ dx = ddx[d];
+
+ /* Note panel change */
+ if (change_panel(dy, dx))
+ {
+ int ty = temp_y[m];
+ int tx = temp_x[m];
+
+ /* Recalculate interesting grids */
+ target_set_prepare(mode);
+
+ /* Find a new monster */
+ i = target_pick(ty, tx, dy, dx);
+
+ /* Restore panel if needed */
+ if (i < 0)
+ {
+ /* Restore panel */
+ change_panel( -dy, -dx);
+
+ /* Recalculate interesting grids */
+ target_set_prepare(mode);
+ }
+ }
+ }
+
+ /* Use that grids */
+ if (i >= 0) m = i;
+ }
+ }
+
+ /* Arbitrary grids */
+ else
+ {
+ /* Access */
+ c_ptr = &cave[y][x];
+
+ /* Default prompt */
+ strcpy(info, "q,t,p,m,+,-,'dir'");
+
+ /* Describe and Prompt (enable "TARGET_LOOK") */
+ query = target_set_aux(y, x, mode | TARGET_LOOK, info);
+
+ /* Cancel tracking */
+ /* health_track(0); */
+
+ /* Assume no direction */
+ d = 0;
+
+ /* Analyze the keypress */
+ switch (query)
+ {
+ case ESCAPE:
+ case 'q':
+ {
+ done = TRUE;
+ break;
+ }
+
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ target_who = -1;
+ target_row = y;
+ target_col = x;
+ done = TRUE;
+ break;
+ }
+
+ case ' ':
+ case '*':
+ case '+':
+ case '-':
+ {
+ break;
+ }
+
+ case 'p':
+ {
+ y = p_ptr->py;
+ x = p_ptr->px;
+ }
+
+ case 'o':
+ {
+ break;
+ }
+
+ case 'm':
+ {
+ flag = TRUE;
+ break;
+ }
+
+ default:
+ {
+ /* Extract the action (if any) */
+ d = get_keymap_dir(query);
+
+ if (!d) bell();
+ break;
+ }
+ }
+
+ /* Handle "direction" */
+ if (d)
+ {
+ int dy = ddy[d];
+ int dx = ddx[d];
+
+ /* Move */
+ y += dy;
+ x += dx;
+
+ /* Do not move horizontally if unnecessary */
+ if (((x < panel_col_min + panel_wid) && (dx > 0)) ||
+ ((x > panel_col_min + panel_wid) && (dx < 0)))
+ {
+ dx = 0;
+ }
+
+ /* Do not move vertically if unnecessary */
+ if (((y < panel_row_min + panel_hgt) && (dy > 0)) ||
+ ((y > panel_row_min + panel_hgt) && (dy < 0)))
+ {
+ dy = 0;
+ }
+ /* Apply the motion */
+ if ((y >= panel_row_min + screen_hgt) ||
+ (y < panel_row_min) ||
+ (x > panel_col_min + screen_wid) ||
+ (x < panel_col_min))
+ {
+ /* Change panel and recalculate interesting grids */
+ if (change_panel(dy, dx)) target_set_prepare(mode);
+ }
+
+ /* Boundary checks */
+ if (!wizard)
+ {
+ /* Hack -- Verify y */
+ if (y <= 0) y = 1;
+ else if (y >= cur_hgt - 1) y = cur_hgt - 2;
+
+ /* Hack -- Verify x */
+ if (x <= 0) x = 1;
+ else if (x >= cur_wid - 1) x = cur_wid - 2;
+ }
+ else
+ {
+ /* Hack -- Verify y */
+ if (y < 0) y = 0;
+ else if (y > cur_hgt - 1) y = cur_hgt - 1;
+
+ /* Hack -- Verify x */
+ if (x < 0) x = 0;
+ else if (x > cur_wid - 1) x = cur_wid - 1;
+ }
+ }
+ }
+ }
+
+ /* Forget */
+ temp_n = 0;
+
+ /* Clear the top line */
+ prt("", 0, 0);
+
+ /* Recenter the map around the player */
+ verify_panel();
+
+ /* Update stuff */
+ p_ptr->update |= (PU_MONSTERS);
+
+ /* Redraw map */
+ p_ptr->redraw |= (PR_MAP);
+
+ /* Window stuff */
+ p_ptr->window |= (PW_OVERHEAD);
+
+ /* Handle stuff */
+ handle_stuff();
+
+ /* Failure to set target */
+ if (!target_who) return (FALSE);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+
+/*
+ * Get an "aiming direction" from the user.
+ *
+ * The "dir" is loaded with 1,2,3,4,6,7,8,9 for "actual direction", and
+ * "0" for "current target", and "-1" for "entry aborted".
+ *
+ * Note that "Force Target", if set, will pre-empt user interaction,
+ * if there is a usable target already set.
+ *
+ * Note that confusion over-rides any (explicit?) user choice.
+ */
+bool_ get_aim_dir(int *dp)
+{
+ int dir;
+
+ char command;
+
+ cptr p;
+
+ if (repeat_pull(dp))
+ {
+ /* Confusion? */
+
+ /* Verify */
+ if (!(*dp == 5 && !target_okay()))
+ {
+ return (TRUE);
+ }
+ }
+
+ /* Initialize */
+ (*dp) = 0;
+
+ /* Global direction */
+ dir = command_dir;
+
+ /* Hack -- auto-target if requested */
+ if (use_old_target && target_okay()) dir = 5;
+
+ /* Ask until satisfied */
+ while (!dir)
+ {
+ /* Choose a prompt */
+ if (!target_okay())
+ {
+ p = "Direction ('*' to choose a target, Escape to cancel)? ";
+ }
+ else
+ {
+ p = "Direction ('5' for target, '*' to re-target, Escape to cancel)? ";
+ }
+
+ /* Get a command (or Cancel) */
+ if (!get_com(p, &command)) break;
+
+ /* Convert various keys to "standard" keys */
+ switch (command)
+ {
+ /* Use current target */
+ case 'T':
+ case 't':
+ case '.':
+ case '5':
+ case '0':
+ {
+ dir = 5;
+ break;
+ }
+
+ /* Set new target */
+ case '*':
+ {
+ if (target_set(TARGET_KILL)) dir = 5;
+ break;
+ }
+
+ default:
+ {
+ /* Extract the action (if any) */
+ dir = get_keymap_dir(command);
+
+ break;
+ }
+ }
+
+ /* Verify requested targets */
+ if ((dir == 5) && !target_okay()) dir = 0;
+
+ /* Error */
+ if (!dir) bell();
+ }
+
+ /* No direction */
+ if (!dir) return (FALSE);
+
+ /* Save the direction */
+ command_dir = dir;
+
+ /* Check for confusion */
+ if (p_ptr->confused)
+ {
+ /* XXX XXX XXX */
+ /* Random direction */
+ dir = ddd[rand_int(8)];
+ }
+
+ /* Notice confusion */
+ if (command_dir != dir)
+ {
+ /* Warn the user */
+ msg_print("You are confused.");
+ }
+
+ /* Save direction */
+ (*dp) = dir;
+
+
+ repeat_push(dir);
+
+ /* A "valid" direction was entered */
+ return (TRUE);
+}
+
+
+
+/*
+ * Request a "movement" direction (1,2,3,4,6,7,8,9) from the user,
+ * and place it into "command_dir", unless we already have one.
+ *
+ * This function should be used for all "repeatable" commands, such as
+ * run, walk, open, close, bash, disarm, spike, tunnel, etc, as well
+ * as all commands which must reference a grid adjacent to the player,
+ * and which may not reference the grid under the player. Note that,
+ * for example, it is no longer possible to "disarm" or "open" chests
+ * in the same grid as the player.
+ *
+ * Direction "5" is illegal and will (cleanly) abort the command.
+ *
+ * This function tracks and uses the "global direction", and uses
+ * that as the "desired direction", to which "confusion" is applied.
+ */
+bool_ get_rep_dir(int *dp)
+{
+ int dir;
+
+ if (repeat_pull(dp))
+ {
+ return (TRUE);
+ }
+
+ /* Initialize */
+ (*dp) = 0;
+
+ /* Global direction */
+ dir = command_dir;
+
+ /* Get a direction */
+ while (!dir)
+ {
+ char ch;
+
+ /* Get a command (or Cancel) */
+ if (!get_com("Direction (Escape to cancel)? ", &ch)) break;
+
+ /* Look up the direction */
+ dir = get_keymap_dir(ch);
+
+ /* Oops */
+ if (!dir) bell();
+ }
+
+ /* Prevent weirdness */
+ if (dir == 5) dir = 0;
+
+ /* Aborted */
+ if (!dir) return (FALSE);
+
+ /* Save desired direction */
+ command_dir = dir;
+
+ /* Apply "confusion" */
+ if (p_ptr->confused)
+ {
+ /* Standard confusion */
+ if (rand_int(100) < 75)
+ {
+ /* Random direction */
+ dir = ddd[rand_int(8)];
+ }
+ }
+
+ /* Notice confusion */
+ if (command_dir != dir)
+ {
+ /* Warn the user */
+ msg_print("You are confused.");
+ }
+
+ /* Save direction */
+ (*dp) = dir;
+
+
+ repeat_push(dir);
+
+ /* Success */
+ return (TRUE);
+}
+
+
+/*
+ * 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;
+}
+
+/*
+ * 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_FRAME);
+ handle_stuff();
+}
+
+static bool_ test_object_wish(char *name, object_type *o_ptr, object_type *forge, const char *what)
+{
+ int i, j, jb, save_aware;
+ char buf[200];
+
+ /* try all objects, this *IS* a very ugly and slow method :( */
+ for (i = 0; i < max_k_idx; i++)
+ {
+ object_kind *k_ptr = &k_info[i];
+
+ o_ptr = forge;
+
+ if (!k_ptr->name) continue;
+ if (k_ptr->flags3 & TR3_NORM_ART) continue;
+ if (k_ptr->flags3 & TR3_INSTA_ART) continue;
+ if (k_ptr->tval == TV_GOLD) continue;
+
+ object_prep(o_ptr, i);
+ o_ptr->name1 = 0;
+ o_ptr->name2 = 0;
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ /* Hack : aware status must be restored after describing the item name */
+ save_aware = k_ptr->aware;
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ object_desc(buf, o_ptr, FALSE, 0);
+ strlower(buf);
+ k_ptr->aware = save_aware;
+
+ if (strstr(name, buf) ||
+ /* Hack hack hackery */
+ (o_ptr->tval == TV_ROD_MAIN && strstr(name, "rod of")))
+ {
+ /* try all ego */
+ for (j = max_e_idx - 1; j >= 0; j--)
+ {
+ ego_item_type *e_ptr = &e_info[j];
+ bool_ ok = FALSE;
+
+ if (j && !e_ptr->name) continue;
+
+ /* Must have the correct fields */
+ if (j)
+ {
+ int z;
+
+ for (z = 0; z < 6; z++)
+ {
+ if (e_ptr->tval[z] == k_ptr->tval)
+ {
+ if ((e_ptr->min_sval[z] <= k_ptr->sval) &&
+ (e_ptr->max_sval[z] >= k_ptr->sval)) ok = TRUE;
+ }
+ if (ok) break;
+ }
+ if (!ok)
+ {
+ continue;
+ }
+ }
+
+ /* try all ego */
+ for (jb = max_e_idx - 1; jb >= 0; jb--)
+ {
+ ego_item_type *eb_ptr = &e_info[jb];
+ bool_ ok = FALSE;
+
+ if (jb && !eb_ptr->name) continue;
+
+ if (j && jb && (e_ptr->before == eb_ptr->before)) continue;
+
+ /* Must have the correct fields */
+ if (jb)
+ {
+ int z;
+
+ for (z = 0; z < 6; z++)
+ {
+ if (eb_ptr->tval[z] == k_ptr->tval)
+ {
+ if ((eb_ptr->min_sval[z] <= k_ptr->sval) &&
+ (eb_ptr->max_sval[z] >= k_ptr->sval)) ok = TRUE;
+ }
+ if (ok) break;
+ }
+ if (!ok)
+ {
+ continue;
+ }
+ }
+
+ object_prep(o_ptr, i);
+ o_ptr->name1 = 0;
+ o_ptr->name2 = j;
+ o_ptr->name2b = jb;
+ apply_magic(o_ptr, dun_level, FALSE, FALSE, FALSE);
+ object_aware(o_ptr);
+ object_known(o_ptr);
+ object_desc(buf, o_ptr, FALSE, 0);
+ strlower(buf);
+
+ if (iequals(buf, name))
+ {
+ /* Don't search any more */
+ return TRUE;
+ }
+ else
+ {
+ /* Restore again the aware status */
+ k_ptr->aware = save_aware;
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+static 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);
+ 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(r_ptr, j)) continue;
+
+ if (j)
+ {
+ if (re_ptr->before)
+ {
+ sprintf(buf, "%s %s", re_ptr->name, r_ptr->name);
+ }
+ else
+ {
+ sprintf(buf, "%s %s", r_ptr->name, re_ptr->name);
+ }
+ }
+ else
+ {
+ sprintf(buf, "%s", r_ptr->name);
+ }
+ strlower(buf);
+
+ if (iequals(mname, buf))
+ {
+ int wy = p_ptr->py, wx = p_ptr->px;
+ int attempts = 100;
+
+ do
+ {
+ scatter(&wy, &wx, p_ptr->py, p_ptr->px, 5);
+ }
+ while (!(in_bounds(wy, wx) && cave_floor_bold(wy, wx)) && --attempts);
+
+ /* Create the monster */
+ if (place_monster_one(wy, wx, i, j, FALSE, mstatus))
+ msg_print("Your wish becomes truth!");
+
+ /* Don't search any more */
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Corrupted have a 1/3 chance of losing a mutation each time this is called,
+ * assuming they have any in the first place
+ */
+static void corrupt_corrupted(void)
+{
+ if (magik(45))
+ {
+ lose_corruption();
+ }
+ else
+ {
+ gain_random_corruption();
+ }
+
+ /* We are done. */
+ return;
+}
+
+/*
+ * 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))
+ {
+ // This code is very reliant on the race_mod_info
+ // elements being simple PODs, in particular the
+ // text pointers being *unmanaged* owned pointers.
+ static_assert(std::is_pod<player_race_mod>::value,
+ "This code needs reworking");
+ // Keep references to owned pointers.
+ auto old_title = race_mod_info[SUBRACE_SAVE].title;
+ auto old_desc = race_mod_info[SUBRACE_SAVE].desc;
+ // Copy everything
+ race_mod_info[SUBRACE_SAVE] = race_mod_info[p_ptr->pracem];
+ // "Undo" copy of title and description (since they're *owned* pointers)
+ race_mod_info[SUBRACE_SAVE].title = old_title;
+ race_mod_info[SUBRACE_SAVE].desc = old_desc;
+ // Replace subrace title with the title currently held by player.
+ set_subrace_title(&race_mod_info[SUBRACE_SAVE], race_mod_info[p_ptr->pracem].title);
+ }
+
+ p_ptr->pracem = racem;
+ rmp_ptr = &race_mod_info[p_ptr->pracem];
+}
+
+void set_subrace_title(player_race_mod *rmp_ptr, cptr name)
+{
+ // Free old title.
+ free(rmp_ptr->title);
+ // Allocate copy of new title.
+ rmp_ptr->title = strdup(name);
+ if (!rmp_ptr->title) {
+ abort();
+ }
+}
+
+void set_subrace_description(player_race_mod *rmp_ptr, cptr desc)
+{
+ // Free old description
+ free(rmp_ptr->desc);
+ // Allocate copy of new description.
+ rmp_ptr->desc = strdup(desc);
+ if (!rmp_ptr->desc) {
+ abort();
+ }
+}
+
+/*
+ * 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_FRAME);
+ p_ptr->update |= (PU_BONUS);
+ handle_stuff();
+
+ lite_spot(p_ptr->py, p_ptr->px);
+}
diff --git a/src/xtra2.hpp b/src/xtra2.hpp
new file mode 100644
index 00000000..10d752a2
--- /dev/null
+++ b/src/xtra2.hpp
@@ -0,0 +1,96 @@
+#pragma once
+
+#include "h-basic.h"
+#include "monster_race_fwd.hpp"
+#include "object_type_fwd.hpp"
+#include "player_race_mod_fwd.hpp"
+
+#include <memory>
+
+extern void do_rebirth(void);
+extern void set_subrace_title(player_race_mod *rmp_ptr, cptr name);
+extern void set_subrace_description(player_race_mod *rmp_ptr, cptr desc);
+extern void switch_subrace(int racem, bool_ copy_old);
+extern void drop_from_wild(void);
+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_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_precognition(int v);
+extern bool_ set_tim_deadly(int v);
+extern bool_ set_tim_reflect(int v);
+extern bool_ set_tim_thunder(int v, int p1, int p2);
+extern bool_ set_strike(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_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 void dec_paralyzed();
+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(std::shared_ptr<monster_race const> 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 bool_ target_okay(void);
+extern bool_ target_set(int mode);
+extern bool_ get_aim_dir(int *dp);
+extern bool_ get_rep_dir(int *dp);
+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 void do_poly_self(void);
+extern bool_ curse_weapon(void);
+extern bool_ curse_armor(void);
+extern void make_wish(void);
+extern void create_between_gate(int dist, int y, int x);
+
+extern "C" {
+ extern void resize_map(void);
+ extern void resize_window(void);
+}
diff --git a/src/z-form.c b/src/z-form.c
new file mode 100644
index 00000000..90d71294
--- /dev/null
+++ b/src/z-form.c
@@ -0,0 +1,678 @@
+/* File: z-form.c */
+
+/* Purpose: Low level text formatting -BEN- */
+
+#include "z-form.h"
+
+#include "z-util.h"
+
+#include <stdlib.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
+ * "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
+ *
+ *
+ * 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("%^-.*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.
+ */
+
+
+
+
+/*
+ * 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;
+ }
+
+
+ /* 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;
+ }
+
+ /* Oops */
+ default:
+ {
+ /* Error -- illegal format char */
+ buf[0] = '\0';
+
+ /* Return "error" */
+ return (0);
+ }
+ }
+
+
+ /* Mega-Hack -- handle "capitilization" */
+ if (do_xtra)
+ {
+ capitalize(tmp);
+ }
+
+ /* 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.
+ */
+static char *vformat(cptr fmt, va_list vp)
+{
+ static char *format_buf = NULL;
+ static size_t format_len = 0;
+
+ /* Initial allocation */
+ if (!format_buf)
+ {
+ format_len = 1024;
+ format_buf = calloc(format_len, sizeof(char));
+ if (format_buf == NULL)
+ {
+ abort(); // Nothing sensible we can do
+ }
+ }
+
+ /* 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 */
+ free(format_buf);
+ format_len = format_len * 2;
+ format_buf = calloc(format_len, sizeof(char));
+ if (format_buf == NULL)
+ {
+ abort(); // Nothing sensible we can do
+ }
+ }
+
+ /* 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() 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 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);
+}
diff --git a/src/z-form.h b/src/z-form.h
new file mode 100644
index 00000000..ac49c658
--- /dev/null
+++ b/src/z-form.h
@@ -0,0 +1,37 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "h-basic.h"
+
+/*
+ * This file provides functions very similar to "sprintf()", but which
+ * not only parse some additional "format sequences", but also enforce
+ * bounds checking, and allow repeated "appends" to the same buffer.
+ *
+ * See "z-form.c" for more detailed information about the routines,
+ * including a list of the legal "format sequences".
+ *
+ * This file makes use "z-util.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 "vformat()" */
+extern char *format(cptr fmt, ...);
+
+/* Vararg interface to "quit()", using "format()" */
+extern void quit_fmt(cptr fmt, ...);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
diff --git a/src/z-rand.cc b/src/z-rand.cc
new file mode 100644
index 00000000..c06b7893
--- /dev/null
+++ b/src/z-rand.cc
@@ -0,0 +1,376 @@
+/* File: z-rand.c */
+
+/* Purpose: a simple random number generator -BEN- */
+
+#include "z-rand.hpp"
+
+
+
+
+/*
+ * 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.
+ */
+static 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);
+}
+
+bool magik(int p) {
+ return rand_int(100) < p;
+}
+
+s32b rand_int(s32b m)
+{
+ return Rand_div(m);
+}
+
+s32b randint(s32b m)
+{
+ return rand_int(m) + 1;
+}
+
+s32b rand_range(s32b a, s32b b)
+{
+ return a + rand_int(1 + b - a);
+}
+
+s32b rand_spread(s32b a, s32b d)
+{
+ return a + rand_int(1 + d + d) - d;
+}
diff --git a/src/z-rand.hpp b/src/z-rand.hpp
new file mode 100644
index 00000000..f2e3ce5c
--- /dev/null
+++ b/src/z-rand.hpp
@@ -0,0 +1,67 @@
+#pragma once
+
+#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 Variables ****/
+
+
+extern bool_ Rand_quick;
+extern u32b Rand_value;
+extern u16b Rand_place;
+extern u32b Rand_state[RAND_DEG];
+
+
+/**** Available Functions ****/
+
+
+void Rand_state_init(u32b seed);
+s32b Rand_mod(s32b m);
+s16b randnor(int mean, int stand);
+s32b damroll(s16b num, s16b sides);
+s32b maxroll(s16b num, s16b sides);
+
+/**
+ * Evaluate to "true" p percent of the time.
+ */
+bool magik(s32b p);
+
+/*
+ * 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"
+ */
+s32b rand_int(s32b m);
+
+/*
+ * Generate a random long integer X where 1<=X<=M
+ * Also, "correctly" handle the case of M<=1
+ */
+s32b randint(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)
+ */
+s32b 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)
+ */
+s32b rand_spread(s32b a, s32b d);
diff --git a/src/z-term.c b/src/z-term.c
new file mode 100644
index 00000000..53b23c02
--- /dev/null
+++ b/src/z-term.c
@@ -0,0 +1,1898 @@
+/*
+ * 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 "z-term.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. We assume that "attr 0" is
+ * "black", with the semantics that "black" text should be
+ * sent to "Term_wipe()" instead of "Term_text()".
+ *
+ * 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.
+ *
+ *
+ * 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->xtra_hook = Perform extra actions
+ * Term->curs_hook = Draw (or Move) the cursor
+ * Term->text_hook = Draw some text in the window
+ *
+ * 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->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.
+ *
+ * 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.
+ *
+ * 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;
+
+/*** Local routines ***/
+
+
+/*
+ * Calloc wrapper which aborts if NULL is returned by calloc
+ */
+static void *safe_calloc(size_t nmemb, size_t size)
+{
+ void *p = calloc(nmemb, size);
+ if ((nmemb > 0) && (p == NULL))
+ {
+ abort();
+ }
+ return p;
+}
+
+/*
+ * Nuke a term_win (see below)
+ */
+static errr term_win_nuke(term_win *s, int w, int h)
+{
+ /* Free the window access arrays */
+ free(s->a);
+ s->a = NULL;
+
+ free(s->c);
+ s->c = NULL;
+
+ /* Free the window content arrays */
+ free(s->va);
+ s->va = NULL;
+
+ free(s->vc);
+ s->vc = NULL;
+
+ /* 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 */
+ s->a = safe_calloc(h, sizeof(byte*));
+ s->c = safe_calloc(h, sizeof(char*));
+
+ /* Make the window content arrays */
+ s->va = safe_calloc(h * w, sizeof(byte));
+ s->vc = safe_calloc(h * w, sizeof(char));
+
+ /* Prepare the window access arrays */
+ for (y = 0; y < h; y++)
+ {
+ s->a[y] = s->va + w * y;
+ s->c[y] = s->vc + w * y;
+ }
+
+ /* 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];
+
+ for (x = 0; x < w; x++)
+ {
+ *s_aa++ = *f_aa++;
+ *s_cc++ = *f_cc++;
+ }
+ }
+
+ /* 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->xtra_hook" hook, if available (see above).
+ * And *hacky* get a return code
+ */
+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_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);
+}
+
+
+
+/*** Efficient routines ***/
+
+
+/*
+ * Mentally draw an attr/char at a given location
+ *
+ * Assumes given location and values are valid.
+ */
+void Term_queue_char(int x, int y, byte a, char c)
+{
+ term_win *scrn = Term->scr;
+
+ byte *scr_aa = &scrn->a[y][x];
+ char *scr_cc = &scrn->c[y][x];
+
+ /* Hack -- Ignore non-changes */
+ if ((*scr_aa == a) && (*scr_cc == c)) return;
+
+ /* Save the "literal" information */
+ *scr_aa = a;
+ *scr_cc = c;
+
+ /* 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 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];
+
+ /* Queue the attr/chars */
+ for ( ; n; x++, s++, n--)
+ {
+ int oa = scr_aa[x];
+ int oc = scr_cc[x];
+
+ /* Hack -- Ignore non-changes */
+ if ((oa == a) && (oc == *s)) continue;
+
+ /* Save the "literal" information */
+ scr_aa[x] = a;
+ scr_cc[x] = *s;
+
+ /* 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;
+ }
+}
+
+
+/*
+ * Blank attribute/character
+ */
+static const byte ATTR_BLANK = TERM_WHITE;
+static const char CHAR_BLANK = ' ';
+
+/*
+ * 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];
+
+ /* Pending length */
+ int fn = 0;
+
+ /* Pending start */
+ int fx = 0;
+
+ /* Pending attr */
+ byte fa = 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)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+
+ /* 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 */
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+
+ /* Forget */
+ fn = 0;
+ }
+
+ /* Save the new color */
+ fa = na;
+ }
+
+ /* Restart and Advance */
+ if (fn++ == 0) fx = x;
+ }
+
+ /* Flush */
+ if (fn)
+ {
+ (void)((*Term->text_hook)(fx, y, fn, fa, &scr_cc[fx]));
+ }
+}
+
+
+
+
+
+/*
+ * 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_FRESH,0)" will be called after
+ * all of the rows have been "flushed".
+ *
+ * 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.
+ *
+ * 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 if no "black" text is ever drawn, and if "attr_blank" is
+ * not "zero", then the "Term_wipe" hook will never be used.
+ *
+ * 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->text_hook) Term->text_hook = Term_text_hack;
+
+
+ /* Handle "total erase" */
+ if (Term->total_erase)
+ {
+ byte na = ATTR_BLANK;
+ char nc = CHAR_BLANK;
+
+ /* 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];
+
+ /* Wipe each column */
+ for (x = 0; x < w; x++)
+ {
+ /* Wipe each grid */
+ *aa++ = na;
+ *cc++ = nc;
+ }
+ }
+
+ /* Redraw every row */
+ Term->y1 = y1 = 0;
+ Term->y2 = y2 = h - 1;
+
+ /* Redraw every column */
+ for (y = 0; y < h; y++)
+ {
+ Term->x1[y] = 0;
+ Term->x2[y] = w - 1;
+ }
+
+ /* Forget "total erase" */
+ Term->total_erase = FALSE;
+ }
+
+
+ /* Cursor update -- Erase old Cursor */
+ if (Term->soft_cursor)
+ {
+ /* Cursor was visible */
+ if (!old->cu && old->cv)
+ {
+ int tx = old->cx;
+ int ty = old->cy;
+
+ byte *old_aa = old->a[ty];
+ char *old_cc = old->c[ty];
+
+ byte oa = old_aa[tx];
+ char oc = old_cc[tx];
+
+ /* Hack -- restore the actual character */
+ (void)((*Term->text_hook)(tx, ty, 1, oa, &oc));
+ }
+ }
+
+ /* 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)
+ {
+ /* Flush the row */
+ Term_fresh_row_text(y, x1, x2);
+
+ /* This row is all done */
+ Term->x1[y] = w;
+ Term->x2[y] = 0;
+ }
+ }
+
+ /* No rows are invalid */
+ Term->y1 = h;
+ Term->y2 = 0;
+ }
+
+
+ /* Cursor update -- Show new Cursor */
+ if (Term->soft_cursor)
+ {
+ /* Draw the cursor */
+ if (!scr->cu && scr->cv)
+ {
+ /* Call the cursor display routine */
+ (void)((*Term->curs_hook)(scr->cx, scr->cy));
+ }
+ }
+
+ /* Cursor Update -- Show new Cursor */
+ else
+ {
+ /* The cursor is useless, hide it */
+ if (scr->cu)
+ {
+ /* Paranoia -- Put the cursor NEAR where it belongs */
+ (void)((*Term->curs_hook)(w - 1, scr->cy));
+
+ /* Make the cursor invisible */
+ /* Term_xtra(TERM_XTRA_SHAPE, 0); */
+ }
+
+ /* The cursor is invisible, hide it */
+ else if (!scr->cv)
+ {
+ /* Paranoia -- Put the cursor where it belongs */
+ (void)((*Term->curs_hook)(scr->cx, scr->cy));
+
+ /* Make the cursor invisible */
+ /* Term_xtra(TERM_XTRA_SHAPE, 0); */
+ }
+
+ /* The cursor is visible, display it correctly */
+ else
+ {
+ /* Put the cursor where it belongs */
+ (void)((*Term->curs_hook)(scr->cx, scr->cy));
+
+ /* Make the cursor visible */
+ Term_xtra(TERM_XTRA_SHAPE, 1);
+ }
+ }
+
+
+ /* Save the "cursor state" */
+ old->cu = scr->cu;
+ old->cv = scr->cv;
+ old->cx = scr->cx;
+ old->cy = scr->cy;
+
+
+ /* Actually flush the output */
+ Term_xtra(TERM_XTRA_FRESH, 0);
+
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Output routines ***/
+
+
+/*
+ * Set the cursor visibility
+ */
+errr Term_set_cursor(int v)
+{
+ /* Already done */
+ if (Term->scr->cv == v) return (1);
+
+ /* Change */
+ Term->scr->cv = v;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Place the cursor at a given location
+ *
+ * Note -- "illegal" requests do not move the cursor.
+ */
+errr Term_gotoxy(int x, int y)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Verify */
+ if ((x < 0) || (x >= w)) return ( -1);
+ if ((y < 0) || (y >= h)) return ( -1);
+
+ /* Remember the cursor */
+ Term->scr->cx = x;
+ Term->scr->cy = y;
+
+ /* The cursor is not useless */
+ Term->scr->cu = 0;
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * At a given location, place an attr/char
+ * Do not change the cursor position
+ * No visual changes until "Term_fresh()".
+ */
+errr Term_draw(int x, int y, byte a, char c)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Verify location */
+ if ((x < 0) || (x >= w)) return ( -1);
+ if ((y < 0) || (y >= h)) return ( -1);
+
+ /* Paranoia -- illegal char */
+ if (!c) return ( -2);
+
+ /* Queue it for later */
+ Term_queue_char(x, y, a, c);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Using the given attr, add the given char at the cursor.
+ *
+ * We return "-2" if the character is "illegal". XXX XXX
+ *
+ * We return "-1" if the cursor is currently unusable.
+ *
+ * We queue the given attr/char for display at the current
+ * cursor location, and advance the cursor to the right,
+ * marking it as unuable and returning "1" if it leaves
+ * the screen, and otherwise returning "0".
+ *
+ * So when this function, or the following one, return a
+ * positive value, future calls to either function will
+ * return negative ones.
+ */
+errr Term_addch(byte a, char c)
+{
+ int w = Term->wid;
+
+ /* Handle "unusable" cursor */
+ if (Term->scr->cu) return ( -1);
+
+ /* Paranoia -- no illegal chars */
+ if (!c) return ( -2);
+
+ /* Queue the given character for display */
+ Term_queue_char(Term->scr->cx, Term->scr->cy, a, c);
+
+ /* 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 = ATTR_BLANK;
+ int nc = CHAR_BLANK;
+
+ byte *scr_aa;
+ char *scr_cc;
+
+ /* 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];
+
+ 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;
+
+ /* 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 = ATTR_BLANK;
+ char nc = 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];
+
+ /* Wipe each column */
+ for (x = 0; x < w; x++)
+ {
+ scr_aa[x] = na;
+ scr_cc[x] = nc;
+ }
+
+ /* 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)
+{
+ /* Force "total erase" */
+ Term->total_erase = TRUE;
+
+ /* Hack -- Refresh */
+ Term_fresh();
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Redraw part of a window.
+ */
+errr Term_redraw_section(int x1, int y1, int x2, int y2)
+{
+ int i, j;
+
+ char *c_ptr;
+
+ /* Bounds checking */
+ if (y2 >= Term->hgt) y2 = Term->hgt - 1;
+ if (x2 >= Term->wid) x2 = Term->wid - 1;
+ if (y1 < 0) y1 = 0;
+ if (x1 < 0) x1 = 0;
+
+ /* Set y limits */
+ Term->y1 = y1;
+ Term->y2 = y2;
+
+ /* Set the x limits */
+ for (i = Term->y1; i <= Term->y2; i++)
+ {
+ Term->x1[i] = x1;
+ Term->x2[i] = x2;
+
+ c_ptr = Term->old->c[i];
+
+ /* Clear the section so it is redrawn */
+ for (j = x1; j <= x2; j++)
+ {
+ /* Hack - set the old character to "none" */
+ c_ptr[j] = 0;
+ }
+ }
+
+ /* Hack -- Refresh */
+ Term_fresh();
+
+ /* Success */
+ return (0);
+}
+
+
+void Term_bell()
+{
+ Term_xtra(TERM_XTRA_NOISE, 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 */
+ if (w)
+ {
+ (*w) = Term->wid;
+ }
+
+ if (h)
+ {
+ (*h) = Term->hgt;
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * Extract the current cursor location
+ */
+errr Term_locate(int *x, int *y)
+{
+ /* Access the cursor */
+ (*x) = Term->scr->cx;
+ (*y) = Term->scr->cy;
+
+ /* Warn about "useless" cursor */
+ if (Term->scr->cu) return (1);
+
+ /* Success */
+ return (0);
+}
+
+
+/*
+ * At a given location, determine the "current" attr and char
+ * Note that this refers to what will be on the window after the
+ * next call to "Term_fresh()". It may or may not already be there.
+ */
+errr Term_what(int x, int y, byte *a, char *c)
+{
+ int w = Term->wid;
+ int h = Term->hgt;
+
+ /* Verify location */
+ if ((x < 0) || (x >= w)) return ( -1);
+ if ((y < 0) || (y >= h)) return ( -1);
+
+ /* Direct access */
+ (*a) = Term->scr->a[y][x];
+ (*c) = Term->scr->c[y][x];
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*** Input routines ***/
+
+
+/*
+ * Flush and forget the input
+ */
+errr Term_flush(void)
+{
+ /* Hack -- Flush all events */
+ Term_xtra(TERM_XTRA_FLUSH, 0);
+
+ /* Forget all keypresses */
+ Term->key_head = Term->key_tail = 0;
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*
+ * Add a keypress to the "queue"
+ */
+errr Term_keypress(int k)
+{
+ /* Hack -- Refuse to enqueue non-keys */
+ if (!k) return ( -1);
+
+ /* Store the char, advance the queue */
+ Term->key_queue[Term->key_head++] = k;
+
+ /* Circular queue, handle wrap */
+ if (Term->key_head == Term->key_size) Term->key_head = 0;
+
+ /* Success (unless overflow) */
+ if (Term->key_head != Term->key_tail) return (0);
+
+ /* Problem */
+ return (1);
+}
+
+
+/*
+ * Add a keypress to the FRONT of the "queue"
+ */
+errr Term_key_push(int k)
+{
+ /* Hack -- Refuse to enqueue non-keys */
+ if (!k) return ( -1);
+
+ /* Hack -- Overflow may induce circular queue */
+ if (Term->key_tail == 0) Term->key_tail = Term->key_size;
+
+ /* Back up, Store the char */
+ Term->key_queue[--Term->key_tail] = k;
+
+ /* Success (unless overflow) */
+ if (Term->key_head != Term->key_tail) return (0);
+
+ /* Problem */
+ return (1);
+}
+
+
+
+
+
+/*
+ * Check for a pending keypress on the key queue.
+ *
+ * Store the keypress, if any, in "ch", and return "0".
+ * Otherwise store "zero" in "ch", and return "1".
+ *
+ * Wait for a keypress if "wait" is true.
+ *
+ * Remove the keypress if "take" is true.
+ */
+errr Term_inkey(char *ch, bool_ wait, bool_ take)
+{
+ /* Assume no key */
+ (*ch) = '\0';
+
+ /* Process queued UI events */
+ Term_xtra(TERM_XTRA_BORED, 0);
+
+ /* 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 */
+ Term->mem = safe_calloc(1, sizeof(struct 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 */
+ save = safe_calloc(1, sizeof(struct 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 */
+ Term->mem = safe_calloc(1, sizeof(struct 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)
+{
+ 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 */
+ free(save);
+
+ /* 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;
+
+ /* Ignore illegal changes */
+ if ((w < 1) || (h < 1)) return ( -1);
+
+
+ /* Ignore non-changes */
+ if ((Term->wid == w) && (Term->hgt == h)) return (1);
+
+ /* 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;
+
+ /* Create new scanners */
+ Term->x1 = safe_calloc(h, sizeof(byte));
+ Term->x2 = safe_calloc(h, sizeof(byte));
+
+ /* Create new window */
+ Term->old = safe_calloc(1, sizeof(struct 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 */
+ Term->scr = safe_calloc(1, sizeof(struct 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 */
+ Term->mem = safe_calloc(1, sizeof(struct term_win));
+
+ /* Initialize new window */
+ term_win_init(Term->mem, w, h);
+
+ /* Save the contents */
+ term_win_copy(Term->mem, hold_mem, wid, hgt);
+ }
+
+ /* Free some arrays */
+ free(hold_x1);
+ hold_x1 = NULL;
+ free(hold_x2);
+ hold_x2 = NULL;
+
+ /* Nuke */
+ term_win_nuke(hold_old, Term->wid, Term->hgt);
+
+ /* Kill */
+ free(hold_old);
+ hold_old = NULL;
+
+ /* 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 */
+ free(hold_scr);
+ hold_scr = NULL;
+
+ /* 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 */
+ free(hold_mem);
+ hold_mem = NULL;
+
+ /* Illegal cursor */
+ if (Term->mem->cx >= w) Term->mem->cu = 1;
+ if (Term->mem->cy >= h) Term->mem->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" */
+ free(t->old);
+ t->old = NULL;
+
+ /* Nuke "requested" */
+ term_win_nuke(t->scr, w, h);
+
+ /* Kill "requested" */
+ free(t->scr);
+ t->scr = NULL;
+
+ /* If needed */
+ if (t->mem)
+ {
+ /* Nuke "memorized" */
+ term_win_nuke(t->mem, w, h);
+
+ /* Kill "memorized" */
+ free(t->mem);
+ t->mem = NULL;
+ }
+
+ /* Free some arrays */
+ free(t->x1);
+ t->x1 = NULL;
+ free(t->x2);
+ t->x2 = NULL;
+
+ /* Free the input queue */
+ free(t->key_queue);
+ t->key_queue = NULL;
+
+ /* 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 */
+ memset(t, 0, sizeof(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 */
+ t->key_queue = safe_calloc(t->key_size, sizeof(char));
+
+
+ /* Save the size */
+ t->wid = w;
+ t->hgt = h;
+
+ /* Allocate change arrays */
+ t->x1 = safe_calloc(h, sizeof(byte));
+ t->x2 = safe_calloc(h, sizeof(byte));
+
+
+ /* Allocate "displayed" */
+ t->old = safe_calloc(1, sizeof(struct term_win));
+
+ /* Initialize "displayed" */
+ term_win_init(t->old, w, h);
+
+
+ /* Allocate "requested" */
+ t->scr = safe_calloc(1, sizeof(struct 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;
+
+
+ /* Success */
+ return (0);
+}
diff --git a/src/z-term.h b/src/z-term.h
new file mode 100644
index 00000000..01795629
--- /dev/null
+++ b/src/z-term.h
@@ -0,0 +1,269 @@
+/* File: z-term.h */
+
+/*
+ * Copyright (c) 1997 Ben Harrison
+ *
+ * This software may be copied and distributed for educational, research,
+ * and not for profit purposes provided that this copyright and statement
+ * are included in all such copies.
+ */
+
+#ifndef INCLUDED_Z_TERM_H
+#define INCLUDED_Z_TERM_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "h-basic.h"
+
+/*
+ * 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;
+
+};
+
+
+
+/*
+ * An actual "term" structure
+ *
+ * - Extra "data" info (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 "icky_corner"
+ * This "term" has an "icky" corner grid
+ *
+ * - Flag "soft_cursor"
+ * This "term" uses a "software" cursor
+ *
+ *
+ *
+ *
+ * - 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 extra actions
+ *
+ * - Hook for placing the cursor
+ *
+ * - 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 data;
+
+ bool_ active_flag;
+ bool_ mapped_flag;
+ bool_ total_erase;
+ bool_ icky_corner;
+ bool_ soft_cursor;
+
+ char *key_queue;
+ u16b key_head;
+ u16b key_tail;
+ u16b key_size;
+
+ byte wid;
+ byte hgt;
+
+ byte y1;
+ byte y2;
+
+ byte *x1;
+ byte *x2;
+
+ term_win *old;
+ term_win *scr;
+
+ term_win *mem;
+
+ void (*init_hook)(term *t);
+ void (*nuke_hook)(term *t);
+
+ errr (*xtra_hook)(int n, int v);
+
+ errr (*curs_hook)(int x, int y);
+
+ errr (*text_hook)(int x, int y, int n, byte a, cptr s);
+
+ void (*resize_hook)(void);
+
+};
+
+
+
+/*** 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 */
+
+
+
+/**** 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_ALIVE" action uses "v" to "activate" (or "close")
+ * The "TERM_XTRA_LEVEL" action uses "v" to "resume" (or "suspend")
+ *
+ * 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_FRESH 6 /* Flush all rows (optional) */
+#define TERM_XTRA_NOISE 7 /* Make a noise (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_RENAME_MAIN_WIN 16 /* Rename the main game window */
+
+
+/**** Available Variables ****/
+
+extern term *Term;
+
+/**** Available Functions ****/
+
+extern errr Term_xtra(int n, int v);
+
+extern void Term_queue_char(int x, int y, byte a, char c);
+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 void Term_bell();
+
+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);
+
+extern errr Term_resize(int w, int h);
+
+extern errr Term_activate(term *t);
+
+extern errr term_nuke(term *t);
+extern errr term_init(term *t, int w, int h, int k);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif
+
diff --git a/src/z-util.c b/src/z-util.c
new file mode 100644
index 00000000..0304a1da
--- /dev/null
+++ b/src/z-util.c
@@ -0,0 +1,108 @@
+/* File: z-util.c */
+
+/* Purpose: Low level utilities -BEN- */
+
+#include "z-util.h"
+
+#include <assert.h>
+
+
+/*
+ * Determine if string "t" is equal to string "t"
+ */
+bool_ streq(cptr a, cptr b)
+{
+ if ((a == NULL) && (b == NULL)) { return TRUE; }
+ if (a == NULL) { return FALSE; }
+ if (b == NULL) { return FALSE; }
+ return (!strcmp(a, b));
+}
+
+
+/*
+ * Determine if string "t" is a suffix of string "s"
+ */
+bool_ suffix(cptr s, cptr t)
+{
+ int tlen = strlen(t);
+ int slen = strlen(s);
+
+ /* Check for incompatible lengths */
+ if (tlen > slen) return (FALSE);
+
+ /* Compare "t" to the end of "s" */
+ return (!strcmp(s + slen - tlen, t));
+}
+
+
+/**
+ * Captialize letter
+ */
+void capitalize(char *s)
+{
+ char *p = s;
+ assert(s != NULL);
+
+ for (; *p; p++)
+ {
+ if (!isspace(*p))
+ {
+ if (islower(*p))
+ {
+ *p = toupper(*p);
+ }
+ /* Done */
+ break;
+ }
+ }
+}
+
+
+/*
+ * 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\n", 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));
+}
diff --git a/src/z-util.h b/src/z-util.h
new file mode 100644
index 00000000..914a64e7
--- /dev/null
+++ b/src/z-util.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "h-basic.h"
+
+
+/*
+ * Extremely basic stuff, like "streq()".
+ */
+
+
+/* Aux functions */
+extern void (*plog_aux)(cptr);
+extern void (*quit_aux)(cptr);
+
+
+/**** Available Functions ****/
+
+
+/* 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);
+
+
+/* Capitalize the first letter of string. Ignores whitespace at the start of string. */
+extern void capitalize(char *s);
+
+/* Print an error message */
+extern void plog(cptr str);
+
+/* Exit, with optional message */
+extern void quit(cptr str);
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
diff --git a/tests/get_level_device.cc b/tests/get_level_device.cc
new file mode 100644
index 00000000..43dce624
--- /dev/null
+++ b/tests/get_level_device.cc
@@ -0,0 +1,184 @@
+#include "lua_bind.hpp"
+#include "spell_type.hpp"
+#include <bandit/bandit.h>
+using namespace bandit;
+
+//
+// Declarations for testing purposes:
+//
+
+s32b get_level_device(spell_type *spell, s32b max, s32b min, s32b device_skill, std::function<s32b(spell_type *, s32b, s32b, s32b, s32b)> lua_get_level_ = lua_get_level);
+
+//
+// Tests
+//
+
+go_bandit([]() {
+
+ describe("get_level_device", []() {
+
+ s32b passed_in_max;
+ s32b passed_in_min;
+
+ // Fake get_level function we can use to detect what's being passed to the real one.
+ auto fake_get_level = [&](struct spell_type *spell, s32b lvl, s32b max, s32b min, s32b bonus) -> s32b {
+ // Store the passed input values for verification purposes
+ passed_in_max = max;
+ passed_in_min = min;
+ // Return the input "lvl" unmodified.
+ return lvl;
+ };
+
+ before_each([&]() {
+ // Reset saved state
+ passed_in_max = -1;
+ passed_in_min = -1;
+ });
+
+ // Magic-Device skill levels that we've tested at.
+ const std::vector<s32b> device_skill_values {
+ 15300, // @15.3
+ 35300, // @35.3
+ 45300, // @45.3
+ 50000 // @50.0
+ };
+
+ // "Remove Curses" spell and expected result levels.
+ auto remove_curses_spell = spell_type_new("TEST: Remove Curses");
+ spell_type_set_difficulty(remove_curses_spell, 10, 42 /* notused */);
+
+ const std::map<s32b, s32b> remove_curses_expected_levels {
+ { 15300, 7 },
+ { 35300, 15 },
+ { 45300, 15 },
+ { 50000, 15 }
+ };
+
+ // "Wish" spell and expected result levels.
+ auto wish_spell = spell_type_new("TEST: Wish");
+ spell_type_set_difficulty(wish_spell, 50, 42 /* notused */);
+
+ const std::map<s32b, s32b> wish_expected_levels {
+ { 15300, 1 },
+ { 35300, 1 },
+ { 45300, 1 },
+ { 50000, 2 }
+ };
+
+ // "Heal Monster" spell and expected result levels.
+ auto heal_monster_spell = spell_type_new("TEST: Heal Monster");
+ spell_type_set_difficulty(heal_monster_spell, 3, 42 /* notused */);
+
+ const std::map<s32b, s32b> heal_monster_expected_levels {
+ { 15300, 108 },
+ { 35300, 152 },
+ { 45300, 152 },
+ { 50000, 152 }
+ };
+
+ // "Teleport Away" spell and expected result levels.
+ auto teleport_away_spell = spell_type_new("TEST: Teleport Away");
+ spell_type_set_difficulty(teleport_away_spell, 23, 42 /* notused */);
+
+ const std::map<s32b, s32b> teleport_away_expected_levels {
+ { 15300, 1 },
+ { 35300, 16 },
+ { 45300, 20 },
+ { 50000, 20 }
+ };
+
+ //
+ // Basic tests for "min <= 0" and "max <= 0".
+ //
+
+ it("should clamp 'min' parameter to 1", [&]() {
+ // Setup
+ s32b device_skill = 100; /* doesn't matter for this test */
+ get_level_max_stick = 1; /* doesn't matter for this test */
+ get_level_use_stick = 1; /* doesn't matter for this test */
+ auto spell = remove_curses_spell; /* doesn't matter for this test */
+ s32b max = 100; /* doesn't matter for this test */
+ s32b min = 0;
+ // Exercise
+ get_level_device(spell, max, min, device_skill, fake_get_level);
+ // Verify
+ AssertThat(passed_in_min, Equals(1));
+ });
+
+ it("should use 50 as default for 'max' parameter if zero or less", [&]() {
+ // Setup
+ s32b device_skill = 100; /* doesn't matter for this test */
+ get_level_max_stick = 1; /* doesn't matter for this test */
+ get_level_use_stick = 1; /* doesn't matter for this test */
+ auto spell = remove_curses_spell; /* doesn't matter for this test */
+ s32b max = 0;
+ s32b min = 25; /* doesn't matter for this test */
+ // Exercise
+ get_level_device(spell, max, min, device_skill, fake_get_level);
+ // Verify
+ AssertThat(passed_in_max, Equals(50));
+ });
+
+ //
+ // Table-driven tests derived from empirical testing
+ // using printf.
+ //
+
+ for (auto device_skill: device_skill_values)
+ {
+ it("calculates 'Remove Curses' staff level correctly for different magic device levels" , [&] {
+ // Setup: Device values for Remove Curses staff
+ get_level_use_stick = 1;
+ get_level_max_stick = 15;
+ // Setup: Max and min
+ s32b max = 50;
+ s32b min = 1;
+ // Exercise
+ s32b actualLevel = get_level_device(remove_curses_spell, max, min, device_skill);
+ // Verify: Check expected levels.
+ AssertThat(actualLevel, Equals(remove_curses_expected_levels.at(device_skill)));
+ });
+
+ it("calculates 'Wish' staff level correctly for different magic device levels", [&] {
+ // Setup: Device values for Wish staff
+ get_level_use_stick = 1;
+ get_level_max_stick = 1;
+ // Setup: Max and min
+ s32b max = 50;
+ s32b min = 1;
+ // Exercise
+ s32b actualLevel = get_level_device(wish_spell, max, min, device_skill);
+ // Verify: Check expected levels.
+ AssertThat(actualLevel, Equals(wish_expected_levels.at(device_skill)));
+ });
+
+ it("calculates 'Heal Monster' wand level correctly for different magic device levels", [&] {
+ // Setup: Device values for Heal Monster wand
+ get_level_use_stick = 1;
+ get_level_max_stick = 20;
+ // Setup: Max and min
+ s32b max = 380;
+ s32b min = 1;
+ // Exercise
+ s32b actualLevel = get_level_device(heal_monster_spell, max, min, device_skill);
+ // Verify: Check expected levels.
+ AssertThat(actualLevel, Equals(heal_monster_expected_levels.at(device_skill)));
+ });
+
+ it("calculates 'Teleport Away' wand level correctly for different magic device levels", [&] {
+ // Setup: Device values for Teleport Away wand
+ get_level_use_stick = 3;
+ get_level_max_stick = 20;
+ // Setup: Max and min
+ s32b max = 50;
+ s32b min = 1;
+ // Exercise
+ s32b actualLevel = get_level_device(teleport_away_spell, max, min, device_skill);
+ // Verify: Check expected levels.
+ AssertThat(actualLevel, Equals(teleport_away_expected_levels.at(device_skill)));
+ });
+ }
+
+ });
+
+});
diff --git a/tests/harness.cc b/tests/harness.cc
new file mode 100644
index 00000000..3a66ad55
--- /dev/null
+++ b/tests/harness.cc
@@ -0,0 +1,7 @@
+#include <bandit/bandit.h>
+using namespace bandit;
+
+int main(int argc, char** argv)
+{
+ return bandit::run(argc, argv);
+}
diff --git a/tests/lua_get_level.cc b/tests/lua_get_level.cc
new file mode 100644
index 00000000..a434903e
--- /dev/null
+++ b/tests/lua_get_level.cc
@@ -0,0 +1,135 @@
+#include "lua_bind.hpp"
+#include "spell_type.hpp"
+#include <bandit/bandit.h>
+using namespace bandit;
+
+go_bandit([]() {
+ describe("lua_get_level", []() {
+
+ auto createEntsPotion = ([]() {
+ auto my_spell = spell_type_new("TEST: Ent's Potion");
+ spell_type_set_difficulty(my_spell, 6, 35); // Copied from standard Ent's Potion spell.
+ return my_spell;
+ });
+
+ auto createSenseHidden = ([]() {
+ auto my_spell = spell_type_new("TEST: Sense Hidden");
+ spell_type_set_difficulty(my_spell, 5, 25); // Copied from standard Sense Hidden spell.
+ return my_spell;
+ });
+
+ //
+ // Test cases derived from empirical testing of the code before refactoring.
+ //
+
+ it("calculates \"Ent's Potion\" level appropriately for Sorcery@1.0", [&](){
+ // Setup
+ auto my_spell = createEntsPotion();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 100, 50, -50, 0);
+ // Verify
+ AssertThat(actualResult, Equals(-4));
+ });
+
+ it("calculates \"Ent's Potion\" cost appropriately for Sorcery@1.0", [&](){
+ // Setup
+ auto my_spell = createEntsPotion();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 100, 15, 7, 0);
+ // Verify
+ AssertThat(actualResult, Equals(7));
+ });
+
+ it("calculates \"Ent's Potion\" level appropriately for Sorcery@25.0", [&](){
+ // Setup
+ auto my_spell = createEntsPotion();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 2500, 50, 1, 0);
+ // Verify
+ AssertThat(actualResult, Equals(20));
+ });
+
+ it("calculates \"Ent's Potion\" cost appropriately for Sorcery@25.0", [&](){
+ // Setup
+ auto my_spell = createEntsPotion();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 2500, 15, 7, 0);
+ // Verify
+ AssertThat(actualResult, Equals(7));
+ });
+
+ it("calculates \"Ent's Potion\" level appropriately for Sorcery@25.0 with +3 equipment SP bonus", [&](){
+ // Setup
+ auto my_spell = createEntsPotion();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 2500, 50, 1, 300);
+ // Verify
+ AssertThat(actualResult, Equals(23));
+ });
+
+ it("calculates \"Ent's Potion\" cost appropriately for Sorcery@25.0 with +3 equipment SP bonus", [&](){
+ // Setup
+ auto my_spell = createEntsPotion();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 2500, 15, 7, 300);
+ // Verify
+ AssertThat(actualResult, Equals(7));
+
+ });
+
+ it("calculates \"Sense Hidden\" level appropriately for Sorcery@1.0", [&](){
+ // Setup
+ auto my_spell = createSenseHidden();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 100, 50, -50, 0);
+ // Verify
+ AssertThat(actualResult, Equals(-3));
+ });
+
+ it("calculates \"Sense Hidden\" cost appropriately for Sorcery@1.0", [&](){
+ // Setup
+ auto my_spell = createSenseHidden();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 100, 10, 2, 0);
+ // Verify
+ AssertThat(actualResult, Equals(2));
+ });
+
+ it("calculates \"Sense Hidden\" level appropriately for Sorcery@25.0", [&](){
+ // Setup
+ auto my_spell = createSenseHidden();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 2500, 50, 1, 0);
+ // Verify
+ AssertThat(actualResult, Equals(21));
+ });
+
+ it("calculates \"Sense Hidden\" cost appropriately for Sorcery@25.0", [&](){
+ // Setup
+ auto my_spell = createSenseHidden();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 2500, 10, 2, 0);
+ // Verify
+ AssertThat(actualResult, Equals(4));
+ });
+
+ it("calculates \"Sense Hidden\" level appropriately for Sorcery@25.0 with +3 equipment SP bonus", [&](){
+ // Setup
+ auto my_spell = createSenseHidden();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 2500, 50, 1, 300);
+ // Verify
+ AssertThat(actualResult, Equals(24));
+ });
+
+ it("calculates \"Sense Hidden\" cost appropriately for Sorcery@25.0 with +3 equipment SP bonus", [&](){
+ // Setup
+ auto my_spell = createSenseHidden();
+ // Exercise
+ auto actualResult = lua_get_level(my_spell, 2500, 10, 2, 300);
+ // Verify
+ AssertThat(actualResult, Equals(4));
+ });
+
+ });
+});
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
diff --git a/vendor/bandit/.travis.yml b/vendor/bandit/.travis.yml
new file mode 100644
index 00000000..f6f5b222
--- /dev/null
+++ b/vendor/bandit/.travis.yml
@@ -0,0 +1,17 @@
+language: cpp
+sudo: true
+compiler:
+ - gcc
+ - clang
+before_install:
+ - sudo add-apt-repository ppa:kubuntu-ppa/backports -y
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq cmake=2.8.12.2-0ubuntu1~ubuntu12.04.1~ppa2
+before_script:
+ - BUILD_DIR=`pwd`/builds
+ - mkdir -p ${BUILD_DIR}
+ - cd ${BUILD_DIR}
+ - cmake ..
+script:
+ - cd ${BUILD_DIR}
+ - make
diff --git a/vendor/bandit/.vimrc b/vendor/bandit/.vimrc
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/vendor/bandit/.vimrc
diff --git a/vendor/bandit/CMakeLists.txt b/vendor/bandit/CMakeLists.txt
new file mode 100644
index 00000000..a777e73b
--- /dev/null
+++ b/vendor/bandit/CMakeLists.txt
@@ -0,0 +1,60 @@
+cmake_minimum_required(VERSION 2.8)
+project(bandit)
+
+option(BANDIT_BUILD_SPECS "Build the Bandit specs" ON)
+option(BANDIT_RUN_SPECS "Run the Bandit specs" ON)
+option(SNOWHOUSE_IS_CPP11 "Build Snowhouse with C++11 settings" ON)
+
+set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+include(cotire/CMake/cotire)
+
+include_directories("${PROJECT_SOURCE_DIR}")
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ./bin)
+
+if (MSVC)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP ")
+else()
+ # Assume GCC-style arguments
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdeprecated -Wdeprecated-declarations -Wshadow -Wall -W -Werror -Wfloat-equal -Wundef -Wendif-labels")
+
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+ endif()
+ endif()
+
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+ endif()
+endif()
+
+#
+# If we're on Mac OS we assume we have libc++, otherwise we assume
+# we don't need it. (TODO: make this check more sofisticated)
+#
+if (CMAKE_HOST_APPLE AND (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+endif()
+
+if (BANDIT_BUILD_SPECS)
+ FILE(GLOB BanditSpecSourceFiles specs/*.cpp specs/**/*.cpp)
+ add_executable(bandit-specs ${BanditSpecSourceFiles} )
+ set_target_properties(bandit-specs PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "specs/specs.h")
+ set_target_properties(bandit-specs PROPERTIES COTIRE_ADD_UNIT_BUILD FALSE)
+ cotire(bandit-specs)
+endif()
+
+add_subdirectory(bandit/assertion_frameworks/snowhouse)
+
+if (BANDIT_BUILD_SPECS AND BANDIT_RUN_SPECS)
+ add_custom_command(TARGET bandit-specs
+ POST_BUILD
+ COMMAND bandit-specs --no-color --reporter=dots
+ WORKING_DIRECTORY ./bin)
+elseif (BANDIT_RUN_SPECS)
+ message(WARNING "Unable to run Bandit specs - set:\n option(BANDIT_BUILD_SPECS, \"Build the Bandit specs\" ON)\nand clear your CMake cache")
+endif()
+
diff --git a/vendor/bandit/LICENSE.md b/vendor/bandit/LICENSE.md
new file mode 100644
index 00000000..9d61348c
--- /dev/null
+++ b/vendor/bandit/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Joakim Karlsson
+
+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
+AUTHORS OR COPYRIGHT HOLDERS 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.
diff --git a/vendor/bandit/README.md b/vendor/bandit/README.md
new file mode 100644
index 00000000..0c95d420
--- /dev/null
+++ b/vendor/bandit/README.md
@@ -0,0 +1,63 @@
+bandit
+======
+[![Build Status](https://travis-ci.org/joakimkarlsson/bandit.png)](https://travis-ci.org/joakimkarlsson/bandit)
+
+Human friendly unit testing for C++11
+
+Bandit is a framework for C++11 that wants to make working with unit tests a pleasant
+experience.
+
+For more information, go to [the bandit website](http://banditcpp.org).
+
+Bandit is released under the [MIT license](LICENSE.md)
+
+#An example
+
+This is a complete test application written in bandit:
+
+```cpp
+#include <bandit/bandit.h>
+using namespace bandit;
+
+// Tell bandit there are tests here.
+go_bandit([](){
+
+ // We're describing how a fuzzbox works.
+ describe("fuzzbox:", [](){
+ guitar_ptr guitar;
+ fuzzbox_ptr fuzzbox;
+
+ // Make sure each test has a fresh setup with
+ // a guitar with a fuzzbox connected to it.
+ before_each([&](){
+ guitar = guitar_ptr(new struct guitar());
+ fuzzbox = fuzzbox_ptr(new struct fuzzbox());
+ guitar->add_effect(*fuzzbox);
+ });
+
+ it("starts in clean mode", [&](){
+ AssertThat(guitar->sound(), Equals(sounds::clean));
+ });
+
+ // Describe what happens when we turn on the fuzzbox.
+ describe("in distorted mode", [&](){
+
+ // Turn on the fuzzbox.
+ before_each([&](){
+ fuzzbox->flip();
+ });
+
+ it("sounds distorted", [&](){
+ AssertThat(guitar->sound(), Equals(sounds::distorted));
+ });
+ });
+ });
+
+});
+
+int main(int argc, char* argv[])
+{
+ // Run the tests.
+ return bandit::run(argc, argv);
+}
+```
diff --git a/vendor/bandit/bandit/adapters/adapter.h b/vendor/bandit/bandit/adapters/adapter.h
new file mode 100644
index 00000000..809212a1
--- /dev/null
+++ b/vendor/bandit/bandit/adapters/adapter.h
@@ -0,0 +1,12 @@
+#ifndef BANDIT_ADAPTER_H
+#define BANDIT_ADAPTER_H
+
+namespace bandit { namespace adapters {
+
+ struct assertion_adapter
+ {
+ virtual void adapt_exceptions(detail::voidfunc_t) = 0;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/adapters/adapters.h b/vendor/bandit/bandit/adapters/adapters.h
new file mode 100644
index 00000000..fbfddaea
--- /dev/null
+++ b/vendor/bandit/bandit/adapters/adapters.h
@@ -0,0 +1,16 @@
+#ifndef BANDIT_ADAPTERS_H
+#define BANDIT_ADAPTERS_H
+
+#include <bandit/adapters/adapter.h>
+#include <bandit/adapters/snowhouse.h>
+
+namespace bandit { namespace detail {
+
+ inline bandit::adapters::assertion_adapter& registered_adapter()
+ {
+ static bandit::adapters::snowhouse_adapter adapter;
+ return adapter;
+ }
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/adapters/snowhouse.h b/vendor/bandit/bandit/adapters/snowhouse.h
new file mode 100644
index 00000000..f0776662
--- /dev/null
+++ b/vendor/bandit/bandit/adapters/snowhouse.h
@@ -0,0 +1,22 @@
+#ifndef BANDIT_ADAPTERS_SNOWHOUSE_H
+#define BANDIT_ADAPTERS_SNOWHOUSE_H
+
+namespace bandit { namespace adapters {
+
+ struct snowhouse_adapter : public assertion_adapter
+ {
+ void adapt_exceptions(detail::voidfunc_t func)
+ {
+ try
+ {
+ func();
+ }
+ catch(const snowhouse::AssertionException& ex)
+ {
+ throw bandit::detail::assertion_exception(ex.GetMessage(), ex.GetFilename(), ex.GetLineNumber());
+ }
+ }
+ };
+
+}}
+#endif
diff --git a/vendor/bandit/bandit/assertion_exception.h b/vendor/bandit/bandit/assertion_exception.h
new file mode 100644
index 00000000..9abc9867
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_exception.h
@@ -0,0 +1,41 @@
+#ifndef BANDIT_ASSERTION_EXCEPTION_H
+#define BANDIT_ASSERTION_EXCEPTION_H
+
+namespace bandit { namespace detail {
+
+ struct assertion_exception : public std::runtime_error
+ {
+ assertion_exception(const std::string& message,
+ const std::string& filename, const unsigned int linenumber)
+ : std::runtime_error(message), file_name_(filename), line_number_(linenumber)
+ {}
+
+ assertion_exception(const std::string& message)
+ : std::runtime_error(message), line_number_(0)
+ {}
+
+ //
+ // To make gcc < 4.7 happy.
+ //
+ assertion_exception(const assertion_exception&) = default;
+ assertion_exception(assertion_exception&&) = default;
+ virtual ~assertion_exception() noexcept
+ {}
+
+ const std::string& file_name() const
+ {
+ return file_name_;
+ }
+
+ unsigned int line_number() const
+ {
+ return line_number_;
+ }
+
+ private:
+ std::string file_name_;
+ unsigned int line_number_;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/BeCloseTo.h b/vendor/bandit/bandit/assertion_frameworks/matchers/BeCloseTo.h
new file mode 100644
index 00000000..e4507c4c
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/BeCloseTo.h
@@ -0,0 +1,55 @@
+#ifndef BANDIT_BECLOSETO_H
+#define BANDIT_BECLOSETO_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ template<typename T>
+ class BeCloseTo : public Matcher
+ {
+ public:
+ explicit BeCloseTo(const T& expectedValue): Matcher(), _expectedValue(expectedValue), _threshold(0.01) {}
+
+ BeCloseTo<T>& within(float threshold)
+ {
+ _threshold = threshold;
+ return *this;
+ }
+
+ template<typename U>
+ bool matches(const U& actualValue) const
+ {
+ return this->subtractable_types_match(actualValue, _expectedValue);
+ }
+
+
+ protected:
+ virtual std::string failure_message_end() const
+ {
+ std::ostringstream ss;
+ ss << "be close to <" << _expectedValue << ">" << " (within " << _threshold << ")";
+ return ss.str();
+ }
+
+ private:
+ template<typename U, typename V>
+ bool subtractable_types_match(const U& actualValue, const V& expectedValue) const
+ {
+ return (actualValue > (expectedValue - _threshold)) && (actualValue < (expectedValue + _threshold));
+ }
+
+
+ private:
+ const T& _expectedValue;
+ float _threshold;
+ };
+
+ template<typename T>
+ BeCloseTo<T> be_close_to(const T& expectedValue)
+ {
+ return BeCloseTo<T>(expectedValue);
+ }
+}}
+
+#endif // BANDIT_BECLOSETO_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/BeEmpty.h b/vendor/bandit/bandit/assertion_frameworks/matchers/BeEmpty.h
new file mode 100644
index 00000000..a83ef994
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/BeEmpty.h
@@ -0,0 +1,32 @@
+#ifndef BANDIT_BEEMPTY_H
+#define BANDIT_BEEMPTY_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ class BeEmpty : public Matcher
+ {
+ private:
+ // BeEmpty & operator=(const BeEmpty &);
+
+ public:
+ explicit BeEmpty() : Matcher() {}
+
+ template<typename U>
+ bool matches(const U& container) const
+ {
+ return container.empty();
+ }
+
+ protected:
+ std::string failure_message_end() const
+ {
+ return std::string("be empty");
+ }
+ };
+
+ static const BeEmpty be_empty = BeEmpty();
+}}
+
+#endif // BANDIT_BEEMPTY_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/BeFalsy.h b/vendor/bandit/bandit/assertion_frameworks/matchers/BeFalsy.h
new file mode 100644
index 00000000..718c6366
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/BeFalsy.h
@@ -0,0 +1,39 @@
+#ifndef BANDIT_BEFALSY_H
+#define BANDIT_BEFALSY_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ class BeFalsy : public Matcher
+ {
+ private:
+ // BeFalsy& operator=(const BeFalsy&);
+
+ public:
+ explicit BeFalsy() : Matcher() {}
+ // ~BeFalsy() {}
+
+ template<typename U>
+ bool matches(const U& actualValue) const
+ {
+ return !actualValue;
+ }
+
+ bool matches(const std::nullptr_t&) const
+ {
+ return true;
+ }
+
+
+ protected:
+ virtual std::string failure_message_end() const
+ {
+ return std::string("evaluate to false");
+ }
+ };
+
+ static const BeFalsy be_falsy = BeFalsy();
+}}
+
+#endif // BANDIT_BEFALSY_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/BeGTE.h b/vendor/bandit/bandit/assertion_frameworks/matchers/BeGTE.h
new file mode 100644
index 00000000..072b2caf
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/BeGTE.h
@@ -0,0 +1,45 @@
+#ifndef BANDIT_BEGREATERTHANOREQUAL_H
+#define BANDIT_BEGREATERTHANOREQUAL_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ template<typename T>
+ class BeGTE : public Matcher
+ {
+ public:
+ explicit BeGTE(const T& expectedValue) : Matcher(), _expectedValue(expectedValue) {}
+
+ template<typename U>
+ bool matches(const U& actualValue) const
+ {
+ return actualValue >= _expectedValue;
+ }
+
+ protected:
+ virtual std::string failure_message_end() const
+ {
+ std::ostringstream ss;
+ ss << "be greater than or equal to <" << _expectedValue << ">";
+ return ss.str();
+ }
+
+ private:
+ const T& _expectedValue;
+ };
+
+ template<typename T>
+ BeGTE<T> be_gte(const T& expectedValue)
+ {
+ return BeGTE<T>(expectedValue);
+ }
+
+ template<typename T>
+ BeGTE<T> be_greater_than_or_equal_to(const T& expectedValue)
+ {
+ return be_gte(expectedValue);
+ }
+}}
+
+#endif // BANDIT_BEGREATERTHANOREQUAL_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/BeGreaterThan.h b/vendor/bandit/bandit/assertion_frameworks/matchers/BeGreaterThan.h
new file mode 100644
index 00000000..95d93d1e
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/BeGreaterThan.h
@@ -0,0 +1,39 @@
+#ifndef BANDIT_BEGREATERTHAN_H
+#define BANDIT_BEGREATERTHAN_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ template<typename T>
+ class BeGreaterThan : public Matcher
+ {
+ public:
+ explicit BeGreaterThan(const T& expectedValue) : Matcher(), _expectedValue(expectedValue) {}
+
+ template<typename U>
+ bool matches(const U& actualValue) const
+ {
+ return actualValue > _expectedValue;
+ }
+
+ protected:
+ virtual std::string failure_message_end() const
+ {
+ std::ostringstream ss;
+ ss << "be greater than <" << _expectedValue << ">";
+ return ss.str();
+ }
+
+ private:
+ const T& _expectedValue;
+ };
+
+ template<typename T>
+ BeGreaterThan<T> be_greater_than(const T& expectedValue)
+ {
+ return BeGreaterThan<T>(expectedValue);
+ }
+}}
+
+#endif // BANDIT_BEGREATERTHAN_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/BeLTE.h b/vendor/bandit/bandit/assertion_frameworks/matchers/BeLTE.h
new file mode 100644
index 00000000..83463f75
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/BeLTE.h
@@ -0,0 +1,45 @@
+#ifndef BANDIT_BELESSTHANOREQUAL_H
+#define BANDIT_BELESSTHANOREQUAL_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ template<typename T>
+ class BeLTE : public Matcher
+ {
+ public:
+ explicit BeLTE(const T& expectedValue) : Matcher(), _expectedValue(expectedValue) {}
+
+ template<typename U>
+ bool matches(const U& actualValue) const
+ {
+ return actualValue <= _expectedValue;
+ }
+
+ protected:
+ virtual std::string failure_message_end() const
+ {
+ std::ostringstream ss;
+ ss << "be less than or equal to <" << _expectedValue << ">";
+ return ss.str();
+ }
+
+ private:
+ const T& _expectedValue;
+ };
+
+ template<typename T>
+ BeLTE<T> be_lte(const T& expectedValue)
+ {
+ return BeLTE<T>(expectedValue);
+ }
+
+ template<typename T>
+ BeLTE<T> be_less_than_or_equal_to(const T& expectedValue)
+ {
+ return be_lte(expectedValue);
+ }
+}}
+
+#endif // BANDIT_BELESSTHANOREQUAL_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/BeLessThan.h b/vendor/bandit/bandit/assertion_frameworks/matchers/BeLessThan.h
new file mode 100644
index 00000000..87a2dc5e
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/BeLessThan.h
@@ -0,0 +1,39 @@
+#ifndef BANDIT_BELESSTHAN_H
+#define BANDIT_BELESSTHAN_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ template<typename T>
+ class BeLessThan : public Matcher
+ {
+ public:
+ explicit BeLessThan(const T& expectedValue) : Matcher(), _expectedValue(expectedValue) {}
+
+ template<typename U>
+ bool matches(const U& actualValue) const
+ {
+ return actualValue < _expectedValue;
+ }
+
+ protected:
+ virtual std::string failure_message_end() const
+ {
+ std::ostringstream ss;
+ ss << "be less than <" << _expectedValue << ">";
+ return ss.str();
+ }
+
+ private:
+ const T& _expectedValue;
+ };
+
+ template<typename T>
+ BeLessThan<T> be_less_than(const T& expectedValue)
+ {
+ return BeLessThan<T>(expectedValue);
+ }
+}}
+
+#endif // BANDIT_BELESSTHAN_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/BeNull.h b/vendor/bandit/bandit/assertion_frameworks/matchers/BeNull.h
new file mode 100644
index 00000000..6e034d10
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/BeNull.h
@@ -0,0 +1,29 @@
+#ifndef BANDIT_BENULL_H
+#define BANDIT_BENULL_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ class BeNull : public Matcher
+ {
+ public:
+ BeNull() : Matcher() {}
+
+ template<typename U>
+ bool matches(U *const & actualValue) const
+ {
+ return !actualValue;
+ }
+
+ protected:
+ std::string failure_message_end() const
+ {
+ return std::string("be nil");
+ }
+ };
+
+ static const BeNull be_null = BeNull();
+}}
+
+#endif // BANDIT_BENULL_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/BeTruthy.h b/vendor/bandit/bandit/assertion_frameworks/matchers/BeTruthy.h
new file mode 100644
index 00000000..c8652538
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/BeTruthy.h
@@ -0,0 +1,35 @@
+#ifndef BANDIT_BETRUTHY_H
+#define BANDIT_BETRUTHY_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ class BeTruthy : public Matcher
+ {
+ public:
+ BeTruthy() : Matcher() {}
+
+ template<typename U>
+ bool matches(const U& actualValue) const
+ {
+ return !!actualValue;
+ }
+
+ bool matches(const std::nullptr_t&) const
+ {
+ return false;
+ }
+
+
+ protected:
+ virtual std::string failure_message_end() const
+ {
+ return std::string("evaluate to true");
+ }
+ };
+
+ static const BeTruthy be_truthy = BeTruthy();
+}}
+
+#endif // BANDIT_BETRUTHY_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/Contain.h b/vendor/bandit/bandit/assertion_frameworks/matchers/Contain.h
new file mode 100644
index 00000000..f048e3a3
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/Contain.h
@@ -0,0 +1,58 @@
+#ifndef BANDIT_CONTAIN_H
+#define BANDIT_CONTAIN_H
+
+#include <cstring>
+#include <vector>
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ template<typename T>
+ class Contain : public Matcher
+ {
+ public:
+ explicit Contain(const T& element) : Matcher(), _element(element) {}
+
+ template<typename U>
+ bool matches(const U& container) const
+ {
+ return container.find(_element) != container.end();
+ }
+
+ template<typename U>
+ bool matches(const std::vector<U>& container) const
+ {
+ return std::find(container.begin(), container.end(), _element) != container.end();
+ }
+
+ bool matches(const char *const container) const
+ {
+ return (_element != NULL) && (container != NULL) && (strstr(container, _element) != NULL);
+ }
+
+ bool matches(char *const container) const
+ {
+ return (_element != NULL) && (container != NULL) && (strstr(container, _element) != NULL);
+ }
+
+ protected:
+ std::string failure_message_end() const
+ {
+ std::ostringstream ss;
+ ss << "contain <" << _element << ">";
+ return ss.str();
+ }
+
+ private:
+ const T& _element;
+ };
+
+ template<typename T>
+ Contain<T> contain(const T& element)
+ {
+ return Contain<T>(element);
+ }
+}}
+
+#endif // BANDIT_CONTAIN_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/Equal.h b/vendor/bandit/bandit/assertion_frameworks/matchers/Equal.h
new file mode 100644
index 00000000..521c6008
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/Equal.h
@@ -0,0 +1,90 @@
+#ifndef BANDIT_EQUAL_H
+#define BANDIT_EQUAL_H
+
+#include <cstring>
+#include <memory>
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ template<typename T>
+ std::ostream& operator<<(std::ostream& os, const std::unique_ptr<T>& obj)
+ {
+ return os << *obj;
+ }
+
+ template<typename T>
+ class Equal : public Matcher
+ {
+ public:
+ explicit Equal(const T& expectedValue) : Matcher(), _expectedValue(expectedValue) {}
+
+ template<typename U>
+ bool matches(const U& actualValue) const
+ {
+ return actualValue == _expectedValue;
+ }
+
+ bool matches(char* actualValue) const
+ {
+ return strcmp(actualValue, &*_expectedValue) == 0;
+ }
+
+ bool matches(const char* actualValue) const
+ {
+ return strcmp(actualValue, &*_expectedValue) == 0;
+ }
+
+ template<typename U>
+ bool matches(const std::unique_ptr<U>& pointer) const
+ {
+ return matches(pointer.get());
+ }
+
+ protected:
+ virtual std::string failure_message_end() const
+ {
+ std::ostringstream ss;
+ ss << "equal <" << _expectedValue << ">";
+ return ss.str();
+ }
+
+ private:
+ const T& _expectedValue;
+ };
+
+ template<typename T>
+ Equal<T> equal(const T& expectedValue)
+ {
+ return Equal<T>(expectedValue);
+ }
+
+ template<typename T, typename U>
+ bool operator==(const ValueProxy<T>& actualValue, const U& expectedValue)
+ {
+ return actualValue.to == expectedValue;
+ }
+
+ template<typename T, typename U>
+ bool operator==(const MatchProxy<T>& matchProxy, const U& expectedValue)
+ {
+ matchProxy(equal(expectedValue));
+ return true;
+ }
+
+ template<typename T, typename U>
+ bool operator!=(const ValueProxy<T>& actualValue, const U& expectedValue)
+ {
+ return actualValue.to != expectedValue;
+ }
+
+ template<typename T, typename U>
+ bool operator!=(const MatchProxy<T>& matchProxy, const U& expectedValue)
+ {
+ matchProxy.negate()(equal(expectedValue));
+ return true;
+ }
+}}
+
+#endif // BANDIT_EQUAL_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/MatchProxy.h b/vendor/bandit/bandit/assertion_frameworks/matchers/MatchProxy.h
new file mode 100644
index 00000000..b637ef0d
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/MatchProxy.h
@@ -0,0 +1,43 @@
+#ifndef BANDIT_MATCHPROXY_H
+#define BANDIT_MATCHPROXY_H
+
+#include "MatcherException.h"
+
+namespace bandit { namespace Matchers
+{
+ template<typename T> class ValueProxy;
+
+ template<typename T>
+ class MatchProxy
+ {
+ private:
+ template<typename U>
+ MatchProxy(const MatchProxy<U>&);
+
+ template<typename U>
+ MatchProxy& operator=(const MatchProxy<U>&);
+
+ public:
+ explicit MatchProxy(const ValueProxy<T>& value, bool negate_ = false) : _value(value), _negate(negate_) {}
+
+ template<typename MatcherType>
+ void operator()(const MatcherType& matcher) const
+ {
+ if( matcher.matches(_value._value) == _negate )
+ {
+ throw MatcherException(_value._filename, _value._lineNumber, matcher.failure_message(_value._value, _negate));
+ }
+ }
+
+ MatchProxy<T> negate() const
+ {
+ return MatchProxy<T>(_value, !_negate);
+ }
+
+ private:
+ const ValueProxy<T>& _value;
+ bool _negate;
+ };
+}}
+
+#endif // BANDIT_MATCHPROXY_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/Matcher.h b/vendor/bandit/bandit/assertion_frameworks/matchers/Matcher.h
new file mode 100644
index 00000000..ad48c0a5
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/Matcher.h
@@ -0,0 +1,74 @@
+#ifndef BANDIT_MATCHER_H
+#define BANDIT_MATCHER_H
+
+#include <sstream>
+
+//#import "CedarStringifiers.h"
+
+namespace bandit { namespace Matchers {
+ class Matcher
+ {
+ public:
+ Matcher() {}
+
+ template<typename U>
+ std::string failure_message(const U& value, bool negate) const
+ {
+ std::ostringstream ss;
+ ss << "Expected <" << value << "> " << (negate ? "to not " : "to ") << failure_message_end();
+ return ss.str();
+ }
+
+ std::string failure_message(char *const value, bool negate) const
+ {
+ return failure_message((value ? value : "NULL"), negate);
+ }
+
+ template<typename U>
+ std::string failure_message(const std::unique_ptr<U>& value, bool negate) const
+ {
+ return failure_message(value.get(), negate);
+ }
+
+ std::string failure_message(const std::nullptr_t pointer, bool negate) const
+ {
+ (void)pointer;
+ return failure_message("nullptr", negate);
+ }
+
+ template<typename U>
+ std::string failure_message(std::function<U>& value, bool negate) const
+ {
+ return failure_message(typeid(value).name(), negate);
+ }
+
+ template<typename U>
+ std::string failure_message(const std::function<U>& value, bool negate) const
+ {
+ return failure_message(typeid(value).name(), negate);
+ }
+
+ template<typename U, template <class T, class = std::allocator<T> > class container >
+ std::string failure_message(const container<U>& value, bool negate) const
+ {
+ return failure_message(typeid(value).name(), negate);
+ }
+
+ template<typename U, template <class T, class = std::less<T>, class = std::allocator<T> > class container >
+ std::string failure_message(const container<U>& value, bool negate) const
+ {
+ return failure_message(typeid(value).name(), negate);
+ }
+
+ template<typename U, typename V, template <class K, class T, class = std::less<K>, class = std::allocator<std::pair<const K,T>> > class container >
+ std::string failure_message(const container<U,V>& value, bool negate) const
+ {
+ return failure_message(typeid(value).name(), negate);
+ }
+
+ protected:
+ virtual std::string failure_message_end() const = 0;
+ };
+}}
+
+#endif // BANDIT_MATCHER_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/MatcherException.h b/vendor/bandit/bandit/assertion_frameworks/matchers/MatcherException.h
new file mode 100644
index 00000000..5d657ed7
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/MatcherException.h
@@ -0,0 +1,16 @@
+#ifndef BANDIT_MATCHER_EXCEPTION_H
+#define BANDIT_MATCHER_EXCEPTION_H
+
+#include <bandit/assertion_exception.h>
+
+namespace bandit { namespace Matchers {
+ class MatcherException : public detail::assertion_exception
+ {
+ public:
+ MatcherException(const std::string& filename, const unsigned linenumber, const std::string& message) : detail::assertion_exception(message, filename, linenumber) {}
+ MatcherException(const MatcherException&) = default;
+ virtual ~MatcherException() noexcept {}
+ };
+}}
+
+#endif // BANDIT_MATCHER_EXCEPTION_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/ThrowException.h b/vendor/bandit/bandit/assertion_frameworks/matchers/ThrowException.h
new file mode 100644
index 00000000..cd8bfc34
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/ThrowException.h
@@ -0,0 +1,60 @@
+#ifndef BANDIT_THROWEXCEPTION_H
+#define BANDIT_THROWEXCEPTION_H
+
+#include "Matcher.h"
+
+namespace bandit { namespace Matchers {
+
+ template <typename T>
+ class ThrowException : public Matcher
+ {
+ public:
+ ThrowException() : Matcher(), _allow_subclasses(false) {}
+ explicit ThrowException(bool allow_subclasses) : Matcher(), _allow_subclasses(allow_subclasses) {}
+
+ template <typename U = std::exception>
+ ThrowException<U> operator()() const
+ {
+ return ThrowException<U>();
+ }
+
+ ThrowException& or_subclass()
+ {
+ _allow_subclasses = true;
+ return *this;
+ }
+
+ template <typename U>
+ bool matches(const U& block) const
+ {
+ try
+ {
+ block();
+ }
+ catch(const T& e)
+ {
+ return true;
+ }
+ catch(...) // Wrong exception
+ {
+ _exception = std::current_exception();
+ }
+
+ return false;
+ }
+
+ protected:
+ std::string failure_message_end() const
+ {
+ return std::string("throw an exception");
+ }
+
+ private:
+ bool _allow_subclasses;
+ mutable std::exception_ptr _exception;
+ };
+
+ static const ThrowException<std::exception> throw_exception;
+}}
+
+#endif // BANDIT_THROWEXCEPTION_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/ValueProxy.h b/vendor/bandit/bandit/assertion_frameworks/matchers/ValueProxy.h
new file mode 100644
index 00000000..0cd5d35c
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/ValueProxy.h
@@ -0,0 +1,26 @@
+#ifndef BANDIT_VALUEPROXY_H
+#define BANDIT_VALUEPROXY_H
+
+#include "MatchProxy.h"
+
+namespace bandit { namespace Matchers {
+
+ template<typename T>
+ class ValueProxy
+ {
+ public:
+ MatchProxy<T> to;
+ MatchProxy<T> to_not;
+
+ explicit ValueProxy(const char* filename, int lineNumber, const T& value) : to(*this), to_not(*this, true), _value(value), _filename(filename), _lineNumber(lineNumber) {}
+
+ private:
+ friend class MatchProxy<T>;
+
+ const T& _value;
+ std::string _filename;
+ int _lineNumber;
+ };
+}}
+
+#endif // BANDIT_VALUEPROXY_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/matchers.h b/vendor/bandit/bandit/assertion_frameworks/matchers/matchers.h
new file mode 100644
index 00000000..033cefcd
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/matchers.h
@@ -0,0 +1,19 @@
+#ifndef BANDIT_MATCHERS_H
+#define BANDIT_MATCHERS_H
+
+#include "must.h"
+
+#include "BeCloseTo.h"
+#include "BeEmpty.h"
+#include "BeFalsy.h"
+#include "BeGreaterThan.h"
+#include "BeGTE.h"
+#include "BeLessThan.h"
+#include "BeLTE.h"
+#include "BeNull.h"
+#include "BeTruthy.h"
+#include "Contain.h"
+#include "Equal.h"
+#include "ThrowException.h"
+
+#endif // BANDIT_MATCHERS_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/matchers/must.h b/vendor/bandit/bandit/assertion_frameworks/matchers/must.h
new file mode 100644
index 00000000..54eedb7f
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/matchers/must.h
@@ -0,0 +1,36 @@
+#ifndef BANDIT_MUST_H
+#define BANDIT_MUST_H
+
+#include "ValueProxy.h"
+
+namespace bandit { namespace Matchers
+{
+ struct ValueMarker
+ {
+ const char* filename;
+ int lineNumber;
+ };
+
+ template<typename T>
+ const ValueProxy<T> operator,(const T& value, const ValueMarker& marker)
+ {
+ return ValueProxy<T>(marker.filename, marker.lineNumber, value);
+ }
+
+ template<typename T>
+ const MatchProxy<T> operator,(const ValueProxy<T>& value, bool negate)
+ {
+ return negate ? value.to_not : value.to;
+ }
+
+ template<typename T, typename MatcherType>
+ void operator,(const MatchProxy<T>& matchProxy, const MatcherType& matcher)
+ {
+ matchProxy(matcher);
+ }
+}}
+
+#define must ,(bandit::Matchers::ValueMarker){__FILE__, __LINE__},false,
+#define must_not ,(bandit::Matchers::ValueMarker){__FILE__, __LINE__},true,
+
+#endif //BANDIT_MUST_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/CMakeLists.txt b/vendor/bandit/bandit/assertion_frameworks/snowhouse/CMakeLists.txt
new file mode 100644
index 00000000..ea43226b
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/CMakeLists.txt
@@ -0,0 +1,49 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(snowhouse)
+
+option(SNOWHOUSE_BUILD_TESTS "Build the Snowhouse tests" ON)
+option(SNOWHOUSE_RUN_TESTS "Run the Snowhouse tests" ON)
+option(SNOWHOUSE_IS_CPP11 "Whether to build this as a C++11 project" OFF)
+
+include_directories("${PROJECT_SOURCE_DIR}")
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ./bin)
+
+set(CMAKE_CXX_FLAGS "-Wfatal-errors -Wall -W -Werror -Wfloat-equal -Wundef -Wendif-labels -Wshadow -pedantic-errors")
+
+if(SNOWHOUSE_IS_CPP11)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wdeprecated")
+
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+ if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
+ else()
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+ endif()
+ endif()
+
+ if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+ endif()
+
+ if (CMAKE_HOST_APPLE AND (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
+ endif()
+endif()
+
+message(${CMAKE_CXX_FLAGS})
+
+if (SNOWHOUSE_BUILD_TESTS)
+ FILE(GLOB SnowhouseSpecSourceFiles example/*.cpp)
+ add_executable(snowhouse-tests ${SnowhouseSpecSourceFiles})
+endif()
+
+if (SNOWHOUSE_BUILD_TESTS AND SNOWHOUSE_RUN_TESTS)
+ add_custom_command(TARGET snowhouse-tests
+ POST_BUILD
+ COMMAND snowhouse-tests
+ WORKING_DIRECTORY ./bin)
+elseif (SNOWHOUSE_RUN_TESTS)
+ message(WARNING "Unable to run snowhouse tests - set:\n option(SNOWHOUSE_BUILD_TESTS, \"Build the Snowhouse tests\" ON)\nand clear your CMakeCache.txt")
+endif()
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/LICENSE_1_0.txt b/vendor/bandit/bandit/assertion_frameworks/snowhouse/LICENSE_1_0.txt
new file mode 100644
index 00000000..36b7cd93
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/LICENSE_1_0.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/README.md b/vendor/bandit/bandit/assertion_frameworks/snowhouse/README.md
new file mode 100644
index 00000000..ecd6e039
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/README.md
@@ -0,0 +1,419 @@
+snowhouse
+=========
+
+An assertion library for C++
+
+Snowhouse is a stand alone assertion framework for C++. It was originally
+developed as part of [Igloo](http://github.com/joakimkarlsson/igloo) and has
+been extracted to be usable in other contexts.
+
+## Usage
+
+```C++
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+
+int main()
+{
+ std::cout << "Testing that 23 is 23" << std::endl;
+ AssertThat(23, Is().EqualTo(23));
+
+ try
+ {
+ AssertThat(12, Is().LessThan(11).And().GreaterThan(99));
+ }
+ catch(const AssertionException& ex)
+ {
+ std::cout << "Apparently this failed:" << std::endl;
+ std::cout << ex.GetMessage() << std::endl;
+ }
+
+ return 0;
+}
+```
+
+### Assertions
+
+Snowhouse uses a constraint based assertion model that is heavily inspired by the
+model used in [NUnit](http://nunit.org/). An assertion in Snowhouse is written
+using the following format:
+
+```cpp
+AssertThat(actual_value, <constraint expression>);
+```
+
+where &lt;constraint expression&gt; is an expression that actual_value is evaluated against when the test is executed.
+
+Constraint expressions come in two basic forms: composite and fluent expressions
+
+#### Composite Expressions
+
+With composite expressions, you can create compact, powerful expressions that combine a set of predefined constraints with ones that you provide yourself.
+
+Example:
+
+```cpp
+AssertThat(length, IsGreaterThan(4) && !Equals(10));
+```
+
+Composite expressions can be any combination of constraints and the standard logical C++ operators.
+
+You can also add your own constraints to be used within composite expressions.
+
+####Fluent Expressions
+
+With fluent expressions, you can create assertions that better convey the intent of a test without exposing implementation-specific details. Fluent expressions aim to help you create tests that are not just by developers for developers, but rather can be read and understood by domain experts.
+
+Fluent expressions also has the ability to make assertions on the elements in a conteiner in a way you cannot achieve with composite expressions.
+
+Example:
+
+```cpp
+AssertThat(length, Is().GreaterThan(4).And().Not().EqualTo(10));
+```
+
+### Basic Constraints
+
+####Equality Constraint
+
+Used to verify equality between actual and expected.
+
+```cpp
+AssertThat(x, Equals(12));
+AssertThat(x, Is().EqualTo(12));
+```
+
+####EqualityWithDelta Constraint
+
+Used to verify equality between actual and expected, allowing the two to differ by a delta.
+
+```cpp
+AssertThat(2.49, EqualsWithDelta(2.5, 0.1));
+AssertThat(2.49, Is().EqualToWithDelta(2.5, 0.1));
+```
+
+####GreaterThan Constraint
+
+Used to verify that actual is greater than a value.
+
+```cpp
+AssertThat(x, IsGreaterThan(4));
+AssertThat(x, Is().GreaterThan(4));
+```
+
+
+####LessThan Constraint
+
+Used to verify that actual is less than a value.
+
+```cpp
+AssertThat(x, IsLessThan(3));
+AssertThat(x, Is().LessThan(3));
+```
+
+####GreaterThanOrEqualTo Constraint
+
+Used to verify that actual is greater than or equal to a value.
+
+```cpp
+AssertThat(x, IsGreaterThanOrEqualTo(5));
+AssertThat(x, Is().GreaterThanOrEqualTo(5));
+```
+
+####LessThanOrEqualTo Constraint
+
+Used to verify that actual is less than or equal to a value.
+
+```cpp
+AssertThat(x, IsLessThanOrEqualTo(6));
+AssertThat(x, Is().LessThanOrEqualTo(6));
+```
+
+### Pointer Constraints
+
+Used to check for `nullptr` equality.
+
+```cpp
+AssertThat(x, IsNull());
+AssertThat(x, Is().Null());
+```
+
+### String Constraints
+
+String assertions in Snowhouse are used to verify the values of STL strings (std::string).
+
+####Equality Constraints
+
+Used to verify that actual is equal to an expected value.
+
+```cpp
+AssertThat(actual_str, Equals("foo"));
+AssertThat(actual_str, Is().EqualTo("foo"));
+```
+
+####Contains Constraint
+
+Used to verify that a string contains a substring.
+
+```cpp
+AssertThat(actual_str, Contains("foo"));
+AssertThat(actual_str, Is().Containing("foo"));
+```
+
+####EndsWith Constraint
+
+Used to verify that a string ends with an expected substring.
+
+```cpp
+AssertThat(actual_str, EndsWith("foo"));
+AssertThat(actual_str, Is().EndingWith("foo"));
+```
+
+####StartsWith Constraint
+
+Used to verify that a string starts with an expected substring.
+
+```cpp
+AssertThat(actual_str, StartsWith("foo"));
+AssertThat(actual_str, Is().StartingWith("foo"));
+```
+
+####HasLength Constraint
+
+Used to verify that a string is of the expected length.
+
+```cpp
+AssertThat(actual_str, HasLength(5));
+AssertThat(actual_str, Is().OfLength(5));
+```
+
+###Constraints on Multi Line Strings
+
+If you have a string that contains multiple lines, you can use the collection constraints to make assertions on the content of that string. This may be handy if you have a string that, for instance, represents the resulting content of a file or a network transmission.
+
+Snowhouse can handle both windows (CR+LF) and unix (LF) line endings
+
+```cpp
+std::string lines = "First line\r\nSecond line\r\nThird line";
+AssertThat(lines, Has().Exactly(1).StartingWith("Second"));
+```
+
+###Container Constraints
+
+The following constraints can be applied to containers in the standard template library:
+
+####Contains Constraint
+
+Used to verify that a container contains an expected value.
+
+```cpp
+AssertThat(container, Contains(12));
+AssertThat(container, Is().Containing(12));
+```
+
+####HasLength Constraint
+
+Used to verify that a container has the expected length.
+
+```cpp
+AssertThat(container, HasLength(3));
+AssertThat(container, Is().OfLength(3));
+```
+
+####IsEmpty Constraint
+
+Used to verify that a container is empty.
+
+```cpp
+AssertThat(contatiner, IsEmpty());
+AssertThat(container, Is().Empty());
+```
+
+####All
+
+Used to verify that all elements of a STL sequence container matches an expectation.
+
+```cpp
+AssertThat(container, Has().All().LessThan(5).Or().EqualTo(66));
+```
+
+####AtLeast
+
+Used to verify that at least a specified amount of elements in a STL sequence container matches an expectation.
+
+```cpp
+AssertThat(container, Has().AtLeast(3).StartingWith("foo"));
+```
+
+####AtMost
+
+Used to verify that at most a specified amount of elements in a STL sequence container matches an expectation.
+
+```cpp
+Assert:That(container, Has().AtMost(2).Not().Containing("failed"));
+```
+
+####Exactly
+
+Used to verify that a STL sequence container has exactly a specified amount of elements that matches an expectation.
+
+```cpp
+AssertThat(container, Has().Exactly(3).GreaterThan(10).And().LessThan(20));
+```
+
+####EqualsContainer
+
+Used to verify that two STL sequence containers are equal.
+
+```cpp
+AssertThat(container1, EqualsContainer(container2));
+AssertThat(container1, Is().EqualToContainer(container2));
+```
+
+#####Predicate functions
+
+You can supply a predicate function or a functor to EqualsContainer to customize how to compare the elements in the two containers.
+
+With a predicate function:
+
+```cpp
+static bool are_my_types_equal(const my_type& lhs, const my_type& rhs)
+{
+ return lhs.my_val_ == rhs.my_val_;
+}
+
+AssertThat(container1, EqualsContainer(container2, are_my_types_equal));
+```
+
+With a functor as predicate:
+
+```cpp
+struct within_delta
+{
+ within_delta(int delta) : delta_(delta) {}
+
+ bool operator()(const my_type& lhs, const my_type& rhs) const
+ {
+ return abs(lhs.my_val_ - rhs.my_val_) <= delta_;
+ }
+
+private:
+ int delta_;
+};
+
+AssertThat(container1, Is().EqualToContainer(container1, within_delta(1));
+```
+
+###Exceptions
+
+Exception constraints can be used to verify that your code throws the correct exceptions.
+
+####AssertThrows
+
+AssertThrows succeeds if the exception thrown by the call is of the supplied type (or one of its subtypes).
+
+```cpp
+AssertThrows(std::logic_error, myObject.a_method(42));
+```
+
+####Making Assertions on the Thrown Exceptions
+
+If AssertThrows succeeds, it will store the thrown exception so that you can make more detailed assertions on it.
+
+```cpp
+AssertThrows(std::logic_error, myObject.a_method(42));
+AssertThat(LastException<std::logic_error>().what(), Is().Containing("logic failure"));
+```
+
+The LastException<> is available in the scope of the call to AssertThrows. An exception is not available between specs in order to avoid the result of one spec contaminating another.
+
+###Custom Constraints
+
+You can add your own constraints to Snowhouse to create more expressive specifications.
+
+####Fulfills Constraints
+
+By defining the following matcher
+
+```cpp
+struct IsEvenNumber
+{
+ bool Matches(const int actual) const
+ {
+ return (actual % 2) == 0;
+ }
+
+ friend std::ostream& operator<<(std::ostream& stm, const IsEvenNumber& );
+};
+
+std::ostream& operator<<(std::ostream& stm, const IsEvenNumber& )
+{
+ stm << "An even number";
+ return stm;
+}
+```
+
+You can create the following constraints in Snowhouse:
+
+```cpp
+AssertThat(42, Fulfills(IsEvenNumber()));
+AssertThat(42, Is().Fulfilling(IsEvenNumber()));
+```
+
+Your custom matcher should implement a method called Matches() that takes a parameter of the type you expect and returns true if the passed parameter fulfills the constraint.
+
+To get more expressive failure messages, you should also implement the streaming operator as in the example above.
+
+##Getting better output for your types
+
+Whenever Snowhouse prints an error message for a type, it will use the stream operator for that type, otherwise it will print "[unsupported type]"
+as a placeholder.
+
+```cpp
+struct MyType { /*...*/ };
+
+AssertThat(myType, Fulfills(MyConstraint());
+```
+
+Will output the following if the constraint fails:
+
+```bash
+Expected: To fulfill my constraint
+Actual: [unsupported type]
+```
+
+If we add a stream operator:
+
+```cpp
+std::ostream& operator<<(std::ostream& stream, const MyType& a)
+{
+ stream << "MyType( x = " << a.x << " )";
+ return stream;
+}
+```
+
+the output will be a bit more readable:
+
+```bash
+Expected: To fullfill my constraint
+Actual: MyType( x = 23 )
+```
+
+##Configurable Failure Handlers
+
+You can provide Snowhouse with custom failure handlers, for example to call `std::terminate` instead of throwing an exception. See `DefaultFailureHandler` for an example of a failure handler. You can derive your own macros with custom failure handlers using `SNOWHOUSE_ASSERT_THAT` and `SNOWHOUSE_ASSERT_THROWS`. See the definitions of `AssertThat` and `AssertThrows` for examples of these. Define `SNOWHOUSE_NO_MACROS` to disable the unprefixed macros `AssertThat` and `AssertThrows`.
+
+### Example Use Cases
+
+#### Assert Program State
+
+Log an error immediately as we may crash if we try to continue. Don't attempt to unwind the stack as we may be inside a destructor or `nothrow` function. We may want to call `std::terminate`, or attempt to muddle along with the rest of the program.
+
+#### Assert Program State in Safe Builds
+
+As above, but only in debug builds.
+
+#### Test Assert
+
+Assert that a test behaved as expected. Throw an exception and let our testing framework deal with the test failure.
+
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/cross_compile.sh b/vendor/bandit/bandit/assertion_frameworks/snowhouse/cross_compile.sh
new file mode 100755
index 00000000..d3a73279
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/cross_compile.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+STATUS=""
+
+function build_for {
+ local CC=$1
+ local CXX=$2
+ local CXX_VERSION=$3
+
+ echo "Compiling for $CC, $CXX, $CXX_VERSION..."
+
+ if [[ "$CXX_VERSION" == "CXX" ]]; then
+ local SNOWHOUSE_IS_CPP11=OFF
+ else
+ local SNOWHOUSE_IS_CPP11=ON
+ fi
+
+ echo "SNOWHOUSE_IS_CPP11=$SNOWHOUSE_IS_CPP11"
+
+ BUILD_DIR=build-$CC-$CXX_VERSION
+ mkdir $BUILD_DIR
+ pushd $BUILD_DIR
+ CC=$CC CXX=$CXX cmake -DSNOWHOUSE_IS_CPP11=$SNOWHOUSE_IS_CPP11 ../..
+ make
+ STATUS="$STATUS\n$BUILD_DIR - Status: $?"
+ popd
+}
+
+if [[ -d builds ]]; then
+ rm -rf builds
+fi
+
+mkdir builds
+pushd builds
+
+build_for gcc-4.5 g++-4.5 CXX
+build_for gcc-4.6 g++-4.6 CXX
+build_for gcc-4.6 g++-4.6 CXX11
+build_for gcc-4.7 g++-4.7 CXX
+build_for gcc-4.7 g++-4.7 CXX11
+build_for gcc-4.8 g++-4.8 CXX
+build_for gcc-4.8 g++-4.8 CXX11
+build_for gcc-4.9 g++-4.9 CXX
+build_for gcc-4.9 g++-4.9 CXX11
+build_for clang clang++ CXX
+build_for clang clang++ CXX11
+popd
+
+echo "============================================"
+echo -e $STATUS
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/basic_assertions.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/basic_assertions.cpp
new file mode 100644
index 00000000..2766ec0a
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/basic_assertions.cpp
@@ -0,0 +1,228 @@
+#include <stdexcept>
+#include <sstream>
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+void throwRuntimeError() {
+ throw std::runtime_error("This is expected");
+}
+
+struct IgnoreErrors {
+ template <class ExpectedType, class ActualType>
+ static void Handle(const ExpectedType&, const ActualType&, const char*, int)
+ {
+ }
+
+ static void Handle(const std::string&)
+ {
+ }
+};
+
+void BasicAssertions()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " ASSERTIONS " << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "ShouldHandleIntegerEquality" << std::endl;
+ {
+ Assert::That(5, Is().EqualTo(5));
+ }
+
+ std::cout << "ShouldDetectIntegerInequality" << std::endl;
+ {
+ AssertTestFails(Assert::That(5, Is().EqualTo(4)), "equal to 4");
+ }
+
+ std::cout << "ShouldDetectIfNotFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(5, Is().Not().EqualTo(5)), "Expected: not equal to 5\nActual: 5\n");
+ }
+
+ std::cout << "ShouldHandleStrings" << std::endl;
+ {
+ Assert::That(std::string("joakim"), Is().EqualTo(std::string("joakim")));
+ }
+
+ std::cout << "ShouldHandleStringsWithoutExplicitTemplateSpecialization" << std::endl;
+ {
+ Assert::That("kim", Is().EqualTo("kim"));
+ }
+
+ std::cout << "ShouldHandleGreaterThan" << std::endl;
+ {
+ Assert::That(5, Is().GreaterThan(4));
+ }
+
+ std::cout << "ShouldDetectWhenGreaterThanFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(5, Is().GreaterThan(5)),
+ "Expected: greater than 5\nActual: 5\n");
+ }
+
+ std::cout << "ShouldHandleLessThan" << std::endl;
+ {
+ Assert::That(5, Is().LessThan(6));
+ }
+
+ std::cout << "ShouldDetectWhenLessThanFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(6, Is().LessThan(5)),
+ "Expected: less than 5\nActual: 6\n");
+ }
+
+ std::cout << "ShouldThrowExplicitFailureMessage" << std::endl;
+ {
+ AssertTestFails(Assert::Failure("foo"), "foo");
+ }
+
+ std::cout << "Should contain location information" << std::endl;
+ {
+ int line;
+ std::string file;
+
+ try
+ {
+ Assert::That(5, Equals(2), "filename", 32);
+ }
+ catch(const AssertionException& e)
+ {
+ line = e.GetLineNumber();
+ file = e.GetFilename();
+ }
+
+ Assert::That(line, Equals(32));
+ Assert::That(file, Equals("filename"));
+ }
+
+ std::cout << "ShouldEnsureExceptionIsThrown" << std::endl;
+ {
+
+ AssertThrows(std::runtime_error, throwRuntimeError());
+ }
+
+ std::cout << "ShouldIgnoreTheError" << std::endl;
+ {
+ ConfigurableAssert<IgnoreErrors>::That(1, Equals(2));
+ }
+
+ std::cout << "================================================" << std::endl;
+ std::cout << " ASSERTIONS EXPRESSION TEMPLATES" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "ShouldHandleIntegerEquality" << std::endl;
+ {
+ Assert::That(5, Equals(5));
+ }
+
+ std::cout << "ShouldDetectIntegerInequality" << std::endl;
+ {
+ AssertTestFails(Assert::That(5, Equals(4)), "equal to 4");
+ }
+
+ std::cout << "ShouldDetectIfNotFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(5, !Equals(5)),
+ "Expected: not equal to 5\nActual: 5\n");
+ }
+
+ std::cout << "ShouldHandleStrings" << std::endl;
+ {
+ Assert::That(std::string("joakim"), Equals(std::string("joakim")));
+ }
+
+ std::cout << "ShouldHandleStringsWithoutExplicitTemplateSpecialization"
+ << std::endl;
+ {
+ Assert::That("kim", Equals("kim"));
+ }
+
+ std::cout << "ShouldHandleGreaterThan" << std::endl;
+ {
+ Assert::That(5, IsGreaterThan(4));
+ }
+
+ std::cout << "ShouldHandleGreaterThanOrEqualTo" << std::endl;
+ {
+ Assert::That(4, IsGreaterThanOrEqualTo(4));
+ Assert::That(5, IsGreaterThanOrEqualTo(4));
+ }
+
+ std::cout << "ShouldDetectWhenGreaterThanFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(5, IsGreaterThan(5)),
+ "Expected: greater than 5\nActual: 5\n");
+ }
+
+ std::cout << "ShouldDetectWhenGreaterThanOrEqualToFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(4, IsGreaterThanOrEqualTo(5)),
+ "Expected: greater than or equal to 5\nActual: 4\n");
+ }
+
+ std::cout << "ShouldHandleLessThan" << std::endl;
+ {
+ Assert::That(5, IsLessThan(6));
+ }
+
+ std::cout << "ShouldHandleLessThanOrEqualTo" << std::endl;
+ {
+ Assert::That(5, IsLessThanOrEqualTo(6));
+ Assert::That(6, IsLessThanOrEqualTo(6));
+ }
+
+ std::cout << "ShouldDetectWhenLessThanFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(6, IsLessThan(5)),
+ "Expected: less than 5\nActual: 6\n");
+ }
+
+ std::cout << "ShouldDetectWhenLessThanOrEqualToFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(6, IsLessThanOrEqualTo(5)),
+ "Expected: less than or equal to 5\nActual: 6\n");
+ }
+
+#if __cplusplus > 199711L
+ std::cout << "ShouldHandleNull" << std::endl;
+ {
+ Assert::That(nullptr, IsNull());
+ }
+
+ std::cout << "ShouldHandleNull" << std::endl;
+ {
+ Assert::That(nullptr, Is().Null());
+ }
+
+ std::cout << "ShouldHandleNotNull" << std::endl;
+ {
+ int anInt = 0;
+ Assert::That(&anInt, ! IsNull());
+ }
+
+ std::cout << "ShouldDetectWhenIsNullFails" << std::endl;
+ {
+ int anInt = 0;
+ std::ostringstream message;
+ message << "Expected: equal to nullptr\nActual: " << &anInt << "\n";
+ AssertTestFails(Assert::That(&anInt, IsNull()), message.str());
+ }
+
+ std::cout << "ShouldDetectWhenIsNullFails" << std::endl;
+ {
+ int anInt = 0;
+ std::ostringstream message;
+ message << "Expected: equal to nullptr\nActual: " << &anInt << "\n";
+ AssertTestFails(Assert::That(&anInt, Is().Null()), message.str());
+ }
+
+ std::cout << "ShouldDetectWhenIsNotNullFails" << std::endl;
+ {
+ std::ostringstream message;
+ message << "Expected: not equal to nullptr\nActual: nullptr\n";
+
+ AssertTestFails(Assert::That(nullptr, ! IsNull()), message.str());
+ }
+#endif
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/boolean_operators.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/boolean_operators.cpp
new file mode 100644
index 00000000..3e4577a5
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/boolean_operators.cpp
@@ -0,0 +1,48 @@
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+void BooleanOperators()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " Boolean operators" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "ShouldHandleIsFalseOperator" << std::endl;
+ {
+ Assert::That(false, IsFalse());
+ }
+
+ std::cout << "ShouldHandleWhenIsFalseFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(true, IsFalse()), "Expected: false");
+ }
+
+ std::cout << "ShouldHandleIsTrueOperator" << std::endl;
+ {
+ Assert::That(true, IsTrue());
+ }
+
+ std::cout << "ShouldHandleWhenIsTrueFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(false, IsTrue()), "Expected: true");
+ }
+
+ std::cout << "ShouldHandleFluentIsTrue" << std::endl;
+ {
+ Assert::That(true, Is().True());
+ AssertTestFails(Assert::That(false, Is().True()), "Expected: true");
+ }
+
+ std::cout << "ShouldHandleFluentIsFalse" << std::endl;
+ {
+ Assert::That(false, Is().False());
+ AssertTestFails(Assert::That(true, Is().False()), "Expected: false");
+ }
+
+ std::cout << "ShouldTreatAssertWithoutConstraintAsBooleanConstrains" << std::endl;
+ {
+ Assert::That(true);
+ }
+}
+
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/container_spec.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/container_spec.cpp
new file mode 100644
index 00000000..c668dffa
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/container_spec.cpp
@@ -0,0 +1,85 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+struct my_type
+{
+ my_type(int my_val)
+ : my_val_(my_val)
+ {}
+
+ friend bool operator==(const my_type&, const my_type&);
+ friend bool operator!=(const my_type&, const my_type&);
+ friend std::ostream& operator<<(std::ostream&, const my_type&);
+
+ int my_val_;
+};
+
+bool operator==(const my_type& lhs, const my_type& rhs)
+{
+ return lhs.my_val_ == rhs.my_val_;
+}
+
+bool operator!=(const my_type& lhs, const my_type& rhs)
+{
+ return !(lhs == rhs);
+}
+
+std::ostream& operator<<(std::ostream& stream, const my_type& item)
+{
+ stream << "(my_type: my_val_=" << item.my_val_ << " )";
+ return stream;
+}
+
+static bool are_my_types_equal(const my_type& lhs, const my_type& rhs)
+{
+ return lhs.my_val_ == rhs.my_val_;
+}
+
+void ContainerConstraints()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " ContainerContstraints" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "it_should_be_able_to_compare_containers_of_custom_types" << std::endl;
+ {
+ const my_type e[] = {my_type(1), my_type(3)};
+ const std::list<my_type> expected(e, e + sizeof(e) / sizeof(e[0]));
+ std::list<my_type> my_container_;
+ my_container_.push_back(my_type(1));
+ my_container_.push_back(my_type(3));
+
+ AssertThat(my_container_, EqualsContainer(expected));
+ }
+
+ std::cout << "it_should_handle_failing_comparisons" << std::endl;
+ {
+ const my_type e[] = {my_type(1), my_type(2)};
+ const std::list<my_type> expected(e, e + sizeof(e) / sizeof(e[0]));
+ std::list<my_type> my_container_;
+ my_container_.push_back(my_type(1));
+ my_container_.push_back(my_type(3));
+
+ AssertTestFails(Assert::That(my_container_, EqualsContainer(expected)),
+ "Expected: [ (my_type: my_val_=1 ), (my_type: my_val_=2 ) ]");
+ }
+
+ std::cout << "it_should_handle_comparison_with_a_predicate_function" << std::endl;
+ {
+ const my_type e[] = {my_type(1), my_type(3)};
+ const std::list<my_type> expected(e, e + sizeof(e) / sizeof(e[0]));
+ std::list<my_type> my_container_;
+ my_container_.push_back(my_type(1));
+ my_container_.push_back(my_type(3));
+
+ Assert::That(my_container_, EqualsContainer(expected, are_my_types_equal));
+ Assert::That(my_container_, Is().EqualToContainer(expected, are_my_types_equal));
+ }
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/custom_matchers_test.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/custom_matchers_test.cpp
new file mode 100644
index 00000000..c5437f9f
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/custom_matchers_test.cpp
@@ -0,0 +1,69 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+struct IsEvenNumberNoStreamOperator
+{
+ bool Matches(const int actual) const
+ {
+ return (actual % 2) == 0;
+ }
+};
+
+struct IsEvenNumberWithStreamOperator
+{
+ bool Matches(const int actual) const
+ {
+ return (actual % 2) == 0;
+ }
+
+ friend std::ostream& operator<<(std::ostream& stm,
+ const IsEvenNumberWithStreamOperator& );
+};
+
+std::ostream& operator<<(std::ostream& stm,
+ const IsEvenNumberWithStreamOperator& )
+{
+ stm << "An even number";
+ return stm;
+}
+
+void CustomMatchers()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " CustomMatchersNoStreamOperator" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "CanHandleCustomMatcher" << std::endl;
+ {
+ Assert::That(2, Fulfills(IsEvenNumberNoStreamOperator()));
+ }
+
+ std::cout << "CustomMatcherWithFluent" << std::endl;
+ {
+ Assert::That(2, Is().Fulfilling(IsEvenNumberNoStreamOperator()));
+ }
+
+ std::cout << "OutputsCorrectMessageWhenFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(3, Fulfills(IsEvenNumberNoStreamOperator())),
+ "Expected: [unsupported type]\nActual: 3");
+ }
+
+
+ std::cout << "================================================" << std::endl;
+ std::cout << "CustomMatcherWithStreamOperator" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "ErrorMessageUsesCustomStreamOperatorIfAvailable" << std::endl;
+ {
+ AssertTestFails(Assert::That(3, Fulfills(IsEvenNumberWithStreamOperator())),
+ "Expected: An even number\nActual: 3");
+ }
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/exceptions_tests.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/exceptions_tests.cpp
new file mode 100644
index 00000000..0f1ac2ab
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/exceptions_tests.cpp
@@ -0,0 +1,97 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2013.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <snowhouse/snowhouse.h>
+#include <stdexcept>
+using namespace snowhouse;
+
+#include "tests.h"
+
+class ClassWithExceptions
+{
+public:
+ int LogicError()
+ {
+ throw std::logic_error("not logical!");
+ }
+
+ double RangeError()
+ {
+ throw std::range_error("range error!");
+ }
+
+ void NoError()
+ {
+ }
+};
+
+void ExceptionTests()
+{
+ ClassWithExceptions objectUnderTest;
+
+ std::cout << "================================================" << std::endl;
+ std::cout << " ExceptionTests" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+
+ std::cout << "CanDetectExceptions" << std::endl;
+ {
+ AssertThrows(std::exception, objectUnderTest.LogicError());
+ }
+
+ std::cout << "CanAssertOnLastException" << std::endl;
+ {
+ AssertThrows(std::logic_error, objectUnderTest.LogicError());
+ Assert::That(LastException<std::logic_error>().what(), Contains("not logical!"));
+ }
+
+ std::cout << "CanDetectWhenWrongExceptionIsThrown" << std::endl;
+ {
+ AssertTestFails(AssertThrows(std::logic_error, objectUnderTest.RangeError()), "Wrong exception");
+ }
+
+ std::cout << "CanPrintExpectedExceptionTypeWhenWrongExceptionIsThrown" << std::endl;
+ {
+ AssertTestFails(AssertThrows(std::logic_error, objectUnderTest.RangeError()), "Expected std::logic_error");
+ }
+
+ std::cout << "CanHaveSeveralExceptionAssertionsInSameSpec" << std::endl;
+ {
+ AssertThrows(std::logic_error, objectUnderTest.LogicError());
+ Assert::That(LastException<std::logic_error>().what(), Contains("not logical!"));
+
+ AssertThrows(std::range_error, objectUnderTest.RangeError());
+ Assert::That(LastException<std::range_error>().what(), Contains("range error!"));
+ }
+
+ std::cout << "CanHaveSeveralExceptionAssertionForTheSameExceptionInSameSpec" << std::endl;
+ {
+ AssertThrows(std::logic_error, objectUnderTest.LogicError());
+ Assert::That(LastException<std::logic_error>().what(), Contains("not logical!"));
+
+ AssertThrows(std::logic_error, objectUnderTest.LogicError());
+ Assert::That(LastException<std::logic_error>().what(), Contains("not logical!"));
+ }
+
+ std::cout << "CanDetectWhenNoExceptionIsThrown" << std::endl;
+ {
+ AssertTestFails(AssertThrows(std::logic_error, objectUnderTest.NoError()), "No exception");
+ }
+
+ std::cout << "CanPrintExpectedExceptionWhenNoExceptionIsThrown" << std::endl;
+ {
+ AssertTestFails(AssertThrows(std::logic_error, objectUnderTest.NoError()), "Expected std::logic_error");
+ }
+
+ std::cout << "ExceptionsAreDestoryedWhenWeExitScope" << std::endl;
+ {
+ {
+ AssertThrows(std::logic_error, objectUnderTest.LogicError());
+ }
+ AssertThrows(AssertionException, LastException<std::logic_error>());
+ Assert::That(LastException<AssertionException>().GetMessage(), Contains("No exception was stored"));
+ }
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/expression_error_handling.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/expression_error_handling.cpp
new file mode 100644
index 00000000..de96f038
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/expression_error_handling.cpp
@@ -0,0 +1,28 @@
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+void ExpressionErrorHandling()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " ExpressionErrorHandling" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::vector<int> collection;
+ collection.push_back(1);
+ collection.push_back(2);
+ collection.push_back(3);
+
+ std::cout << "AnInvalidAllOperationShouldBeReportedProperly" << std::endl;
+ {
+ AssertTestFails(Assert::That(collection, Has().All()),
+ "The expression after \"all\" operator does not yield any result");
+ }
+
+ std::cout << "AnInvalidAtLeastOperationShouldBeReportedProperly" << std::endl;
+ {
+ AssertTestFails(Assert::That(collection, Has().AtLeast(2)),
+ "The expression after \"at least 2\" operator does not yield any result");
+ }
+
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/main.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/main.cpp
new file mode 100644
index 00000000..616b97ff
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/main.cpp
@@ -0,0 +1,43 @@
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+void BooleanOperators();
+void BasicAssertions();
+void ContainerConstraints();
+void CustomMatchers();
+void ExceptionTests();
+void ExpressionErrorHandling();
+void MapTests();
+void OperatorTests();
+void SequenceContainerTests();
+void StringLineTests();
+void StringTests();
+void StringizeTests();
+
+int main()
+{
+ try
+ {
+ BasicAssertions();
+ BooleanOperators();
+ ContainerConstraints();
+ CustomMatchers();
+ ExceptionTests();
+ ExpressionErrorHandling();
+ MapTests();
+ OperatorTests();
+ SequenceContainerTests();
+ StringLineTests();
+ StringTests();
+ StringizeTests();
+ }
+ catch(const AssertionException& e)
+ {
+ std::cout << "Tests failed!" << std::endl;
+ std::cout << e.GetMessage() << std::endl;
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/map_tests.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/map_tests.cpp
new file mode 100644
index 00000000..813b5011
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/map_tests.cpp
@@ -0,0 +1,38 @@
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+void MapTests()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " MapTests" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::map<std::string, int> ages;
+ ages["joakim"] = 38;
+ ages["maria"] = 36;
+ ages["hanna"] = 6;
+ ages["moa"] = 4;
+
+ std::cout << "ContainingShouldDetermineIfKeyExists" << std::endl;
+ {
+ Assert::That(ages, Is().Containing("joakim"));
+ }
+
+ std::cout << "ShouldGiveAProperMessageWhenContainingFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(ages, Is().Not().Containing("hanna")),
+ "Expected: not contains hanna");
+ }
+
+ std::cout << "ContainingShouldDetermineIfKeyExists" << std::endl;
+ {
+ Assert::That(ages, Contains("joakim"));
+ }
+
+ std::cout << "ShouldGiveAProperMessageWhenContainingFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(ages, !Contains("hanna")),
+ "Expected: not contains hanna");
+ }
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/operator_tests.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/operator_tests.cpp
new file mode 100644
index 00000000..3d11ae07
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/operator_tests.cpp
@@ -0,0 +1,137 @@
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+void OperatorTests()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " OperatorTests" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "ShouldHandleAndOperatorExpressionTemplates" << std::endl;
+ {
+ Assert::That(5, IsLessThan(6) && IsGreaterThan(4));
+ }
+
+ std::cout << "ShouldHandleAndOperator" << std::endl;
+ {
+ Assert::That(5, Is().LessThan(6).And().GreaterThan(4));
+ }
+
+ std::cout << "ShouldHandleAndOperatorFailExpressionTemplates" << std::endl;
+ {
+ AssertTestFails(Assert::That(5, IsLessThan(7) && IsGreaterThan(5)),
+ "less than 7 and greater than 5");
+ }
+
+ std::cout << "ShouldHandleAndOperatorFail" << std::endl;
+ {
+ AssertTestFails(Assert::That(5, Is().LessThan(7).And().GreaterThan(5)),
+ "less than 7 and greater than 5");
+ }
+
+ std::cout << "ShouldHandleOrOperator" << std::endl;
+ {
+ Assert::That(12, Is().LessThan(7).Or().GreaterThan(5));
+ }
+
+ std::cout << "ShouldHandleOrOperatorExpressionTemplates" << std::endl;
+ {
+ Assert::That(12, IsLessThan(7) || IsGreaterThan(5));
+ }
+
+ std::cout << "ShouldHandleOrOperatorFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(67, Is().LessThan(12).Or().GreaterThan(99)),
+ "less than 12 or greater than 99");
+ }
+
+ std::cout << "ShouldHandleOrOperatorFailsExpressionTemplates" << std::endl;
+ {
+ AssertTestFails(Assert::That(67, IsLessThan(12) || IsGreaterThan(99)),
+ "less than 12 or greater than 99");
+ }
+
+ std::cout << "ShouldHandleNotOperators" << std::endl;
+ {
+ Assert::That(5, Is().Not().EqualTo(4));
+ }
+
+ std::cout << "ShouldHandleNotOperatorsExpressionTemplates" << std::endl;
+ {
+ Assert::That(5, !Equals(4));
+ }
+
+ std::cout << "ShouldHandleNotOperatorsFails" << std::endl;
+ {
+ AssertTestFails(Assert::That(12, Is().Not().EqualTo(12)), "not equal to 12");
+ }
+
+ std::cout << "ShouldHandleNotOperatorsFailsExpressionTemplates" << std::endl;
+ {
+ AssertTestFails(Assert::That(12, !Equals(12)), "not equal to 12");
+ }
+
+ std::cout << "ShouldHandleNotOperatorsForStrings" << std::endl;
+ {
+ Assert::That("joakim", Is().Not().EqualTo("harry"));
+ }
+
+ std::cout << "ShouldHandleNotOperatorsForStringsExpressionTemplates" << std::endl;
+ {
+ Assert::That("joakim", !Equals("harry"));
+ }
+
+ std::cout << "ShouldHandleBothLeftAndRightAssociativeOperators" << std::endl;
+ {
+ Assert::That(5, Is().GreaterThan(4).And().Not().LessThan(3));
+ }
+
+ std::cout << "ShouldHandleBothLeftAndRightAssociativeOperatorsExpressionTemplates" << std::endl;
+ {
+ Assert::That(5, IsGreaterThan(4)&& !IsLessThan(3));
+ }
+
+ std::cout << "MalformedExpressionYieldsError" << std::endl;
+ {
+ AssertTestFails(Assert::That(4, Is().Not()),
+ "The expression contains a not operator without any operand");
+ }
+
+ std::cout <<
+ "EqualsWithDeltaOperator_should_fail_for_actual_larger_than_delta"
+ << std::endl;
+ {
+ AssertTestFails(Assert::That(3.9, EqualsWithDelta(3, 0.5)),
+ "Expected: equal to 3 (+/- 0.5)");
+ }
+
+ std::cout << "EqualsWithDeltaOperator_should_fail_for_actual_less_than_delta" << std::endl;
+ {
+ AssertTestFails(Assert::That(2.49, EqualsWithDelta(3, 0.5)),
+ "Expected: equal to 3 (+/- 0.5)");
+ }
+
+ std::cout << "EqualsWithDeltaOperator_should_succeed" << std::endl;
+ {
+ Assert::That(2, EqualsWithDelta(1.9, 0.1));
+ }
+
+ std::cout << "Fluent_equals_with_delta_should_fail_for_actual_larger_than_delta" << std::endl;
+ {
+ AssertTestFails(Assert::That(3.9, Is().EqualToWithDelta(3, 0.5)),
+ "Expected: equal to 3 (+/- 0.5)");
+ }
+
+ std::cout << "Fluent_EqualsWithDeltaOperator_should_fail_for_actual_less_than_delta" << std::endl;
+ {
+ AssertTestFails(Assert::That(2.49, Is().EqualToWithDelta(3, 0.5)),
+ "Expected: equal to 3 (+/- 0.5)");
+ }
+
+ std::cout << "Fluent_EqualsWithDeltaOperator_should_succeed" << std::endl;
+ {
+ Assert::That(2, Is().EqualToWithDelta(1.9, 0.1));
+ }
+
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/sequence_container_tests.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/sequence_container_tests.cpp
new file mode 100644
index 00000000..c090cc58
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/sequence_container_tests.cpp
@@ -0,0 +1,192 @@
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+
+template <typename T>
+void SequenceContainerActual()
+{
+ const char* ExpectedActual = "\nActual: [ 1, 2, 3, 5, 8 ]";
+
+ T container;
+ container.clear();
+ container.push_back(1);
+ container.push_back(2);
+ container.push_back(3);
+ container.push_back(5);
+ container.push_back(8);
+
+ std::cout << "ShouldHandleAllOperator" << std::endl;
+ {
+ Assert::That(container, Has().All().GreaterThan(1).Or().LessThan(4));
+ }
+
+ std::cout << "ShouldHandleFailingAllOperator" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Has().All().GreaterThan(4)), std::string("Expected: all greater than 4") + ExpectedActual);
+ }
+
+ std::cout << "SHouldHandleInvalidExpressionAfterAllOperator" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Has().All().Not()), "The expression contains a not operator without any operand");
+ }
+
+ std::cout << "ShouldHandleNoExpressionAfterAllOperator" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Has().All()), "The expression after \"all\" operator does not yield any result");
+ }
+
+ std::cout << "ShouldHandleAtLeastOperator" << std::endl;
+ {
+ Assert::That(container, Has().AtLeast(1).LessThan(5));
+ }
+
+ std::cout << "ShouldHandleFailingAtLeastOperator" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Has().AtLeast(2).LessThan(2)), std::string("Expected: at least 2 less than 2") + ExpectedActual);
+ }
+
+ std::cout << "ShouldHandleExactlyOperator" << std::endl;
+ {
+ Assert::That(container, Has().Exactly(1).EqualTo(3));
+ }
+
+ std::cout << "ShouldHandleFailingExactlyOperator" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Has().Exactly(2).EqualTo(3)), std::string("Expected: exactly 2 equal to 3") + ExpectedActual);
+ }
+
+ std::cout << "ShouldHandleAtMostOperator" << std::endl;
+ {
+ Assert::That(container, Has().AtMost(1).EqualTo(5));
+ }
+
+ std::cout << "ShouldHandleFailingAtMostOperator" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Has().AtMost(1).EqualTo(3).Or().EqualTo(5)), std::string("Expected: at most 1 equal to 3 or equal to 5") + ExpectedActual);
+ }
+
+ std::cout << "ShouldHandleNoneOperator" << std::endl;
+ {
+ Assert::That(container, Has().None().EqualTo(666));
+ }
+
+ std::cout << "ShouldHandleFailingNoneOperator" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Has().None().EqualTo(5)), std::string("Expected: none equal to 5") + ExpectedActual);
+ }
+
+ std::cout << "ShouldHandleContaining" << std::endl;
+ {
+ Assert::That(container, Contains(3));
+ }
+
+ std::cout << "ShouldDetectFailingContains" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Contains(99)), std::string("contains 99") + ExpectedActual);
+ }
+
+ std::cout << "ShouldHandleOfLength" << std::endl;
+ {
+ Assert::That(container, HasLength(5));
+ }
+
+ std::cout << "ShouldHandleFailingOfLength" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, HasLength(7)), std::string("of length 7") + ExpectedActual);
+ }
+
+ std::cout << "ShouldHandleContaining_ExpressionTemplates" << std::endl;
+ {
+ Assert::That(container, Contains(3));
+ }
+
+ std::cout << "ShouldDetectFailingContains_ExpressionTemplates" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Contains(99)), std::string("contains 99") + ExpectedActual);
+ }
+
+ std::cout << "ShouldHandleOfLength_ExpressionTemplates" << std::endl;
+ {
+ Assert::That(container, HasLength(5));
+ }
+
+ std::cout << "ShouldHandleFailingOfLengthForVectors" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, HasLength(7)), std::string("of length 7") + ExpectedActual);
+ }
+
+ std::cout << "ShouldHandleIsEmpty" << std::endl;
+ {
+ T is_empty;
+
+ Assert::That(is_empty, IsEmpty());
+ }
+
+ std::cout << "ShouldHandleFailingIsEmpty" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, IsEmpty()), "of length 0");
+ }
+
+ std::cout << "ShouldHandleFluentIsEmpty" << std::endl;
+ {
+ T is_empty;
+
+ Assert::That(is_empty, Is().Empty());
+ }
+
+ std::cout << "ShouldHandleFailingFluentIsEmpty" << std::endl;
+ {
+ AssertTestFails(Assert::That(container, Is().Empty()), "of length 0");
+ }
+
+ std::cout << "ShouldHandlerEqualsContainer" << std::endl;
+ {
+ std::list<int> expected;
+ expected.assign(container.begin(), container.end());
+
+ AssertThat(container, EqualsContainer(expected));
+ }
+
+ std::cout << "ShouldHandleEqualsContainer_Fluent" << std::endl;
+ {
+ std::list<int> expected;
+ expected.assign(container.begin(), container.end());
+
+ AssertThat(container, Is().EqualToContainer(expected));
+ }
+
+ std::cout << "ShouldHandleFailingEqualsContainer" << std::endl;
+ {
+ const int e[] = {4, 2, 4};
+ std::list<int> expected(e, e + sizeof(e) / sizeof(e[0]));
+
+ AssertTestFails(Assert::That(container, EqualsContainer(expected)), "Expected: [ 4, 2, 4 ]");
+ }
+
+ std::cout << "ShouldHandleFailingEqualsContainer_Fluent" << std::endl;
+ {
+ const int e[] = {4, 2, 4};
+ std::list<int> expected(e, e + sizeof(e) / sizeof(e[0]));
+
+ AssertTestFails(Assert::That(container, Is().EqualToContainer(expected)), "Expected: [ 4, 2, 4 ]");
+ }
+}
+
+void SequenceContainerTests()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " SequenceContainerTests(vector)" << std::endl;
+ std::cout << "================================================" << std::endl;
+ SequenceContainerActual<std::vector<int> >();
+
+ std::cout << "================================================" << std::endl;
+ std::cout << " SequenceContainerTests(list)" << std::endl;
+ std::cout << "================================================" << std::endl;
+ SequenceContainerActual<std::list<int> >();
+
+ std::cout << "================================================" << std::endl;
+ std::cout << " SequenceContainerTests(deque)" << std::endl;
+ std::cout << "================================================" << std::endl;
+ SequenceContainerActual<std::deque<int> >();
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/string_line_tests.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/string_line_tests.cpp
new file mode 100644
index 00000000..8cf90cfb
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/string_line_tests.cpp
@@ -0,0 +1,179 @@
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+void StringLineTests()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " StringLineTests" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "CanAssertThatAtLeastOneLineInAStreamMatches" << std::endl;
+ {
+ Assert::That("First line\n", Has().AtLeast(1).EqualTo("First line"));
+ }
+
+ std::cout << "CanDetectWhenAssertionFails" << std::endl;
+ {
+ AssertTestFails(Assert::That("First line\n", Has().AtLeast(1).EqualTo("Second line")), "Expected: at least 1 equal to Second line");
+ }
+
+ std::cout << "CanHandleLineMissingNewline" << std::endl;
+ {
+ Assert::That("First line", Has().AtLeast(1).EqualTo("First line"));
+ }
+
+ std::cout << "CanHandleSeveralLines" << std::endl;
+ {
+ std::string lines = "First line\nSecond line";
+ Assert::That(lines, Has().Exactly(2).EndingWith("line"));
+ }
+
+ std::cout << "CanHandleWindowsLineEndings" << std::endl;
+ {
+ std::string lines = "First line\r\nSecond line\r\nThird line";
+ Assert::That(lines, Has().Exactly(3).EndingWith("line"));
+ }
+
+ std::cout << "CanMatchBeginningOfLinesWithWindowsLineEndings" << std::endl;
+ {
+ std::string lines = "First line\nSecond line\r\nThird line";
+ Assert::That(lines, Has().Exactly(1).StartingWith("Second"));
+ }
+
+ std::cout << "CanHandleEmptyLinesWhenUsingWindowsLineEndings" << std::endl;
+ {
+ std::string lines = "\r\nSecond line\r\n\r\n";
+ Assert::That(lines, Has().Exactly(2).OfLength(0));
+ }
+
+ std::cout << "CanHandleLastLineMissingNewlineForWindowsLineEndings" << std::endl;
+ {
+ std::string lines = "First line\r\nSecond line";
+ Assert::That(lines, Has().Exactly(2).EndingWith("line"));
+ }
+
+ std::cout << "CanHandleAllEmptyLines" << std::endl;
+ {
+ Assert::That("\n\n\n\n\n\n", Has().Exactly(6).OfLength(0));
+ }
+
+ std::cout << "CanHandleAllEmptyLinesWithWindowsLineEndings" << std::endl;
+ {
+ Assert::That("\r\n\r\n\r\n", Has().Exactly(3).OfLength(0));
+ }
+
+
+ std::cout << "================================================" << std::endl;
+ std::cout << " StringLineParserTests" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+
+ std::cout << "CanParseEmptyString" << std::endl;
+ {
+ std::vector<std::string> res;
+
+ StringLineParser::Parse("", res);
+
+ Assert::That(res, HasLength(0));
+ }
+
+ std::cout << "CanParseSingleLine" << std::endl;
+ {
+ std::vector<std::string> res;
+
+ StringLineParser::Parse("Simple line", res);
+
+ Assert::That(res, HasLength(1));
+ Assert::That(res, Has().Exactly(1).EqualTo("Simple line"));
+ }
+
+ std::cout << "CanParseTwoLines" << std::endl;
+ {
+ std::vector<std::string> res;
+
+ StringLineParser::Parse("One line\nTwo lines", res);
+
+ Assert::That(res, HasLength(2));
+ Assert::That(res, Has().Exactly(1).EqualTo("One line"));
+ Assert::That(res, Has().Exactly(1).EqualTo("Two lines"));
+ }
+
+ std::cout << "CanParseThreeLines" << std::endl;
+ {
+ std::vector<std::string> res;
+
+ StringLineParser::Parse("One line\nTwo lines\nThree lines", res);
+
+ Assert::That(res, HasLength(3));
+ Assert::That(res, Has().Exactly(1).EqualTo("One line"));
+ Assert::That(res, Has().Exactly(1).EqualTo("Two lines"));
+ Assert::That(res, Has().Exactly(1).EqualTo("Three lines"));
+ }
+
+ std::cout << "CanHandleStringEndingWithNewline" << std::endl;
+ {
+ std::vector<std::string> res;
+ StringLineParser::Parse("One line\n", res);
+ Assert::That(res, HasLength(1));
+ Assert::That(res, Has().Exactly(1).EqualTo("One line"));
+ }
+
+ std::cout << "CanHandleSingleLineWithWindowsLineEnding" << std::endl;
+ {
+ std::vector<std::string> res;
+ StringLineParser::Parse("One line\r\n", res);
+ Assert::That(res, HasLength(1));
+ Assert::That(res, Has().Exactly(1).EqualTo("One line"));
+ }
+
+ std::cout << "CanHandleTwoLinesWithWindowsLineEndings" << std::endl;
+ {
+ std::vector<std::string> res;
+ StringLineParser::Parse("One line\r\nTwo lines", res);
+ Assert::That(res, HasLength(2));
+ Assert::That(res, Has().Exactly(1).EqualTo("One line"));
+ Assert::That(res, Has().Exactly(1).EqualTo("Two lines"));
+ }
+
+ std::cout << "CanHandleEmptyLineWithNewline" << std::endl;
+ {
+ std::vector<std::string> res;
+ StringLineParser::Parse("\n", res);
+ Assert::That(res, Is().OfLength(1).And().Exactly(1).OfLength(0));
+ }
+
+ std::cout << "CanHandleTwoEmptyLines" << std::endl;
+ {
+ std::vector<std::string> res;
+ StringLineParser::Parse("\n\n", res);
+ Assert::That(res, HasLength(2));
+ Assert::That(res, Has().Exactly(2).OfLength(0));
+ }
+
+ std::cout << "CanHandleTwoEmptyLinesWithWindowsLineEndings" << std::endl;
+ {
+ std::vector<std::string> res;
+ StringLineParser::Parse("\r\n\r\n", res);
+ Assert::That(res, HasLength(2));
+ Assert::That(res, Has().Exactly(2).OfLength(0));
+ }
+
+ std::cout << "CanHandleCarriageReturnOnly" << std::endl;
+ {
+ std::vector<std::string> res;
+ StringLineParser::Parse("One line\rTwo lines", res);
+ Assert::That(res, HasLength(2));
+ Assert::That(res, Has().Exactly(1).EqualTo("One line"));
+ Assert::That(res, Has().Exactly(1).EqualTo("Two lines"));
+ }
+
+ std::cout << "CanHandleCarriageReturnOnlyAtEndOfString" << std::endl;
+ {
+ std::vector<std::string> res;
+ StringLineParser::Parse("One line\r\nTwo lines\r", res);
+ Assert::That(res, HasLength(2));
+ Assert::That(res, Has().Exactly(1).EqualTo("One line"));
+ Assert::That(res, Has().Exactly(1).EqualTo("Two lines"));
+ }
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/string_tests.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/string_tests.cpp
new file mode 100644
index 00000000..989ad42b
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/string_tests.cpp
@@ -0,0 +1,65 @@
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+void StringTests()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " StringTests" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "ShouldHandleStringContainsConstraint" << std::endl;
+ {
+ Assert::That("abcdef", Contains("bcde"));
+ }
+
+ std::cout << "StringConstraintShouldHandleMatchAtBeginningOfString" << std::endl;
+ {
+ Assert::That("abcdef", Contains("a"));
+ }
+
+ std::cout << "ShouldDetectFailingContains" << std::endl;
+ {
+ AssertTestFails(Assert::That("abcdef", Contains("hello")), "contains hello");
+ }
+
+ std::cout << "ShouldHandleStringStartingWithConstraint" << std::endl;
+ {
+ Assert::That("abcdef", StartsWith("abc"));
+ }
+
+ std::cout << "ShouldHandleStringEndingWithConstraint" << std::endl;
+ {
+ Assert::That("abcdef", EndsWith("def"));
+ }
+
+ std::cout << "ShouldHandleOperatorsForStrings" << std::endl;
+ {
+ Assert::That("abcdef", StartsWith("ab") && EndsWith("ef"));
+ }
+
+ std::cout << "ShouldHandleStringsWithMultipleOperators" << std::endl;
+ {
+ Assert::That("abcdef", StartsWith("ab") && !EndsWith("qwqw"));
+ }
+
+ std::cout << "ShouldHandleOfLength" << std::endl;
+ {
+ Assert::That("12345", HasLength(5));
+ }
+
+ std::cout << "ShouldHandleWeirdLongExpressions" << std::endl;
+ {
+ Assert::That("12345", HasLength(5) && StartsWith("123") && !EndsWith("zyxxy"));
+ }
+
+ std::cout << "ShouldHandleStdStrings" << std::endl;
+ {
+ Assert::That("12345", Contains(std::string("23")));
+ }
+
+ std::cout << "ShouldHandleSimpleChar" << std::endl;
+ {
+ Assert::That("12345", StartsWith('1'));
+ }
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/stringize_tests.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/stringize_tests.cpp
new file mode 100644
index 00000000..a0971274
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/stringize_tests.cpp
@@ -0,0 +1,111 @@
+#include <snowhouse/snowhouse.h>
+using namespace snowhouse;
+#include "tests.h"
+
+namespace
+{
+ // No overload for operator<<(std::ostream&) or specialization of igloo::Stringizer
+ struct WithoutStreamOperator
+ {
+ WithoutStreamOperator(int id)
+ : m_id(id)
+ {
+ }
+
+ bool operator==(const WithoutStreamOperator& rhs) const
+ {
+ return m_id == rhs.m_id;
+ }
+
+ int m_id;
+ };
+
+ // Has operator<<(std::ostream&)
+ struct WithStreamOperator : public WithoutStreamOperator
+ {
+ WithStreamOperator(int id)
+ : WithoutStreamOperator(id)
+ {
+ }
+ };
+
+ std::ostream& operator<<(std::ostream& stream, const WithStreamOperator& a)
+ {
+ stream << a.m_id;
+ return stream;
+ }
+
+ // Has no operator<<(std::ostream&), but a specialization of igloo::Stringizer
+ struct WithoutStreamOperatorButWithStringizer : public WithoutStreamOperator
+ {
+ WithoutStreamOperatorButWithStringizer(int id)
+ : WithoutStreamOperator(id)
+ {
+ }
+ };
+}
+
+namespace snowhouse {
+
+ template<>
+ struct Stringizer< WithoutStreamOperatorButWithStringizer >
+ {
+ static std::string ToString(const WithoutStreamOperatorButWithStringizer& value)
+ {
+ return snowhouse::Stringize(value.m_id);
+ }
+ };
+}
+
+void StringizeTests()
+{
+ std::cout << "================================================" << std::endl;
+ std::cout << " StringizeTests" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "ShouldHandleTypesWithStreamOperators" << std::endl;
+ {
+ WithStreamOperator a(12);
+ WithStreamOperator b(13);
+ AssertTestFails(Assert::That(a, Is().EqualTo(b)), "Expected: equal to 13\nActual: 12");
+ }
+
+ std::cout << "ShouldHandleTypesWithoutStreamOperators" << std::endl;
+ {
+ WithoutStreamOperator a(12);
+ WithoutStreamOperator b(13);
+ AssertTestFails(Assert::That(a, Is().EqualTo(b)), "Expected: equal to [unsupported type]\nActual: [unsupported type]");
+ }
+
+ std::cout << "ShouldHandleTypesWithTraits" << std::endl;
+ {
+ WithoutStreamOperatorButWithStringizer a(12);
+ WithoutStreamOperatorButWithStringizer b(13);
+ AssertTestFails(Assert::That(a, Is().EqualTo(b)), "Expected: equal to 13\nActual: 12");
+ }
+
+ std::cout << "================================================" << std::endl;
+ std::cout << " StringizeTestsExpressionTemplates" << std::endl;
+ std::cout << "================================================" << std::endl;
+
+ std::cout << "ShouldHandleTypesWithStreamOperators" << std::endl;
+ {
+ WithStreamOperator a(12);
+ WithStreamOperator b(13);
+ AssertTestFails(Assert::That(a, Equals(b)), "Expected: equal to 13\nActual: 12");
+ }
+
+ std::cout << "ShouldHandleTypesWithoutStreamOperators" << std::endl;
+ {
+ WithoutStreamOperator a(12);
+ WithoutStreamOperator b(13);
+ AssertTestFails(Assert::That(a, Equals(b)), "Expected: equal to [unsupported type]\nActual: [unsupported type]");
+ }
+
+ std::cout << "ShouldHandleTypesWithTraits" << std::endl;
+ {
+ WithoutStreamOperatorButWithStringizer a(12);
+ WithoutStreamOperatorButWithStringizer b(13);
+ AssertTestFails(Assert::That(a, Is().EqualTo(b)), "Expected: equal to 13\nActual: 12");
+ }
+}
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/tests.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/tests.h
new file mode 100644
index 00000000..9dd1d28c
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/tests.h
@@ -0,0 +1,16 @@
+#ifndef SNOWHOUSE_EXAMPLES_TEST_H
+#define SNOWHOUSE_EXAMPLES_TEST_H
+
+#define AssertTestFails(assertion, expected_error_text) \
+ std::string IGLOO_INTERNAL_expected_error = "Test did not fail"; \
+ try \
+ { \
+ assertion; \
+ } \
+ catch(const AssertionException& exception_from_igloo_assertion) \
+ { \
+ IGLOO_INTERNAL_expected_error = exception_from_igloo_assertion.GetMessage(); \
+ } \
+ Assert::That(IGLOO_INTERNAL_expected_error, Is().Containing(expected_error_text));
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assert.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assert.h
new file mode 100644
index 00000000..64981094
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assert.h
@@ -0,0 +1,126 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ASSERT_H
+#define IGLOO_ASSERT_H
+
+#include "stringize.h"
+#include "stringizers.h"
+
+namespace snowhouse {
+
+ struct DefaultFailureHandler
+ {
+ template <class ExpectedType, class ActualType>
+ static void Handle(const ExpectedType& expected, const ActualType& actual, const char* file_name, int line_number)
+ {
+ std::ostringstream str;
+
+ str << "Expected: " << snowhouse::Stringize(expected) << std::endl;
+ str << "Actual: " << snowhouse::Stringize(actual) << std::endl;
+
+ throw AssertionException(str.str(), file_name, line_number);
+ }
+
+ static void Handle(const std::string& message)
+ {
+ throw AssertionException(message);
+ }
+ };
+
+ template<typename FailureHandler>
+ class ConfigurableAssert
+ {
+ public:
+
+ template <typename ActualType, typename ConstraintListType>
+ static void That(const ActualType& actual, ExpressionBuilder<ConstraintListType> expression)
+ {
+ const char* no_file = "";
+ int line_number = 0;
+
+ ConfigurableAssert<FailureHandler>::That(actual, expression, no_file, line_number);
+ }
+
+ template <typename ActualType, typename ConstraintListType>
+ static void That(const ActualType& actual, ExpressionBuilder<ConstraintListType> expression, const char* file_name, int line_number)
+ {
+ try
+ {
+ ResultStack result;
+ OperatorStack operators;
+ expression.Evaluate(result, operators, actual);
+
+ while (!operators.empty())
+ {
+ ConstraintOperator* op = operators.top();
+ op->PerformOperation(result);
+ operators.pop();
+ }
+
+ if (result.empty())
+ {
+ throw InvalidExpressionException("The expression did not yield any result");
+ }
+
+ if (!result.top())
+ {
+ FailureHandler::Handle(expression, actual, file_name, line_number);
+ }
+ }
+ catch (const InvalidExpressionException& e)
+ {
+ FailureHandler::Handle("Malformed expression: \"" + snowhouse::Stringize(expression) + "\"\n" + e.Message());
+ }
+ }
+
+ template <typename ConstraintListType>
+ static void That(const char* actual, ExpressionBuilder<ConstraintListType> expression)
+ {
+ return That(std::string(actual), expression);
+ }
+
+ template <typename ActualType, typename ExpressionType>
+ static void That(const ActualType& actual, const ExpressionType& expression)
+ {
+ const char* no_file = "";
+ int no_line = 0;
+ That(actual, expression, no_file, no_line);
+ }
+
+ template <typename ActualType, typename ExpressionType>
+ static void That(const ActualType& actual, const ExpressionType& expression, const char* file_name, int line_number)
+ {
+ if (!expression(actual))
+ {
+ FailureHandler::Handle(expression, actual, file_name, line_number);
+ }
+ }
+
+ template <typename ExpressionType>
+ static void That(const char* actual, const ExpressionType& expression)
+ {
+ return That(std::string(actual), expression);
+ }
+
+ static void That(bool actual)
+ {
+ if (!actual)
+ {
+ FailureHandler::Handle("Expected: true\nActual: false");
+ }
+ }
+
+ static void Failure(const std::string& message)
+ {
+ FailureHandler::Handle(message);
+ }
+ };
+
+ typedef ConfigurableAssert<DefaultFailureHandler> Assert;
+}
+
+#endif // IGLOO_ASSERT_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertionexception.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertionexception.h
new file mode 100644
index 00000000..d0747742
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertionexception.h
@@ -0,0 +1,58 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ASSERTIONEXCEPTION_H
+#define IGLOO_ASSERTIONEXCEPTION_H
+
+namespace snowhouse {
+ class AssertionException : public std::exception
+ {
+ public:
+ AssertionException(const std::string& message)
+ : m_message(message), m_fileName(""), m_line(0)
+ {}
+
+ AssertionException(const std::string& message, const std::string& fileName, unsigned int line)
+ : m_message(message), m_fileName(fileName), m_line(line)
+ {}
+
+#if __cplusplus > 199711L
+ AssertionException(const AssertionException&) = default;
+#endif
+
+#if __cplusplus > 199711L
+ virtual ~AssertionException() noexcept
+ {
+ }
+#else
+ virtual ~AssertionException() throw()
+ {
+ }
+#endif
+
+ std::string GetMessage() const
+ {
+ return m_message;
+ }
+
+ std::string GetFilename() const
+ {
+ return m_fileName;
+ }
+
+ unsigned int GetLineNumber() const
+ {
+ return m_line;
+ }
+
+ private:
+ std::string m_message;
+ std::string m_fileName;
+ unsigned int m_line;
+ };
+}
+
+#endif // IGLOO_ASSERTIONEXCEPTION_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertmacro.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertmacro.h
new file mode 100644
index 00000000..df5b4b34
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertmacro.h
@@ -0,0 +1,22 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ASSERTMACRO_H
+#define IGLOO_ASSERTMACRO_H
+
+#include "assert.h"
+
+#define SNOWHOUSE_ASSERT_THAT(p1,p2,FAILURE_HANDLER)\
+ ::snowhouse::ConfigurableAssert<FAILURE_HANDLER>::That((p1), (p2), __FILE__, __LINE__);\
+
+#ifndef SNOWHOUSE_NO_MACROS
+
+#define AssertThat(p1,p2)\
+ SNOWHOUSE_ASSERT_THAT((p1), (p2), ::snowhouse::DefaultFailureHandler);\
+
+#endif // SNOWHOUSE_NO_MACROS
+
+#endif // IGLOO_ASSERTMACRO_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/constraints.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/constraints.h
new file mode 100644
index 00000000..a12433d1
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/constraints.h
@@ -0,0 +1,23 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_CONSTRAINTS_H
+#define IGLOO_CONSTRAINTS_H
+
+#include "containsconstraint.h"
+#include "endswithconstraint.h"
+#include "equalsconstraint.h"
+#include "haslengthconstraint.h"
+#include "isgreaterthanconstraint.h"
+#include "isgreaterthanorequaltoconstraint.h"
+#include "islessthanconstraint.h"
+#include "islessthanorequaltoconstraint.h"
+#include "startswithconstraint.h"
+#include "fulfillsconstraint.h"
+#include "equalswithdeltaconstraint.h"
+#include "equalscontainerconstraint.h"
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/containsconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/containsconstraint.h
new file mode 100644
index 00000000..f20d6800
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/containsconstraint.h
@@ -0,0 +1,80 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_CONTAINSCONSTRAINT_H
+#define IGLOO_CONTAINSCONSTRAINT_H
+
+#include <algorithm>
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template <typename ContainerType>
+ struct find_in_container_traits
+ {
+ template <typename ExpectedType>
+ static bool find(const ContainerType& container, const ExpectedType& expected)
+ {
+ return std::find(container.begin(), container.end(), expected) != container.end();
+ }
+ };
+
+ template <typename KeyType, typename ValueType>
+ struct find_in_container_traits<std::map<KeyType, ValueType> >
+ {
+ template <typename ExpectedType>
+ static bool find(const std::map<KeyType, ValueType>& container, const ExpectedType& expected)
+ {
+ return container.find(expected) != container.end();
+ }
+ };
+
+ template <typename ExpectedType>
+ struct ContainsConstraint : Expression< ContainsConstraint<ExpectedType> >
+ {
+ ContainsConstraint(const ExpectedType& expected)
+ : m_expected(expected) {}
+
+ template <typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ return find_in_container_traits<ActualType>::find(actual, m_expected);
+ }
+
+ bool operator()(const std::string& actual) const
+ {
+ return actual.find(m_expected) != actual.npos;
+ }
+
+ ExpectedType m_expected;
+ };
+
+ template< typename ExpectedType >
+ inline ContainsConstraint<ExpectedType> Contains(const ExpectedType& expected)
+ {
+ return ContainsConstraint<ExpectedType>(expected);
+ }
+
+ inline ContainsConstraint<std::string> Contains(const char* expected)
+ {
+ return ContainsConstraint<std::string>(expected);
+ }
+
+ template< typename ExpectedType >
+ struct Stringizer< ContainsConstraint< ExpectedType > >
+ {
+ static std::string ToString(const ContainsConstraint<ExpectedType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "contains " << snowhouse::Stringize(constraint.m_expected);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/endswithconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/endswithconstraint.h
new file mode 100644
index 00000000..c867e203
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/endswithconstraint.h
@@ -0,0 +1,53 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ENDSWITHCONSTRAINT_H
+#define IGLOO_ENDSWITHCONSTRAINT_H
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template <typename ExpectedType>
+ struct EndsWithConstraint : Expression< EndsWithConstraint<ExpectedType> >
+ {
+ EndsWithConstraint(const ExpectedType& expected)
+ : m_expected(expected) {}
+
+ bool operator()(const std::string& actual) const
+ {
+ size_t expectedPos = actual.length() - m_expected.length();
+ return actual.find(m_expected) == expectedPos;
+ }
+
+ ExpectedType m_expected;
+ };
+
+ template< typename ExpectedType >
+ inline EndsWithConstraint<ExpectedType> EndsWith(const ExpectedType& expected)
+ {
+ return EndsWithConstraint<ExpectedType>(expected);
+ }
+
+ inline EndsWithConstraint<std::string> EndsWith(const char* expected)
+ {
+ return EndsWithConstraint<std::string>(expected);
+ }
+
+ template< typename ExpectedType >
+ struct Stringizer< EndsWithConstraint< ExpectedType > >
+ {
+ static std::string ToString(const EndsWithConstraint<ExpectedType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "ends with " << snowhouse::Stringize(constraint.m_expected);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalsconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalsconstraint.h
new file mode 100644
index 00000000..a47f6bf4
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalsconstraint.h
@@ -0,0 +1,83 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_EQUALSCONSTRAINT_H
+#define IGLOO_EQUALSCONSTRAINT_H
+
+#include <cstddef>
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template< typename ExpectedType >
+ struct EqualsConstraint : Expression< EqualsConstraint<ExpectedType> >
+ {
+ EqualsConstraint(const ExpectedType& expected)
+ : m_expected(expected)
+ {
+ }
+
+ template<typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ return (m_expected == actual);
+ }
+
+ ExpectedType m_expected;
+ };
+
+ template< typename ExpectedType >
+ inline EqualsConstraint<ExpectedType> Equals(const ExpectedType& expected)
+ {
+ return EqualsConstraint<ExpectedType>(expected);
+ }
+
+ inline EqualsConstraint<std::string> Equals(const char* expected)
+ {
+ return EqualsConstraint<std::string>(expected);
+ }
+
+ inline EqualsConstraint<bool> IsFalse()
+ {
+ return EqualsConstraint<bool>(false);
+ }
+
+ inline EqualsConstraint<bool> IsTrue()
+ {
+ return EqualsConstraint<bool>(true);
+ }
+
+#if __cplusplus > 199711L
+ inline EqualsConstraint<std::nullptr_t> IsNull()
+ {
+ return EqualsConstraint<std::nullptr_t>(nullptr);
+ }
+#endif
+
+ template <>
+ struct Stringizer< EqualsConstraint< bool > >
+ {
+ static std::string ToString(const EqualsConstraint<bool>& constraint)
+ {
+ return constraint.m_expected ? "true" : "false";
+ }
+ };
+
+ template< typename ExpectedType >
+ struct Stringizer< EqualsConstraint< ExpectedType > >
+ {
+ static std::string ToString(const EqualsConstraint<ExpectedType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "equal to " << snowhouse::Stringize(constraint.m_expected);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalscontainerconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalscontainerconstraint.h
new file mode 100644
index 00000000..f8650952
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalscontainerconstraint.h
@@ -0,0 +1,80 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_EQUALSCONTAINERCONSTRAINT_H
+#define IGLOO_EQUALSCONTAINERCONSTRAINT_H
+
+namespace snowhouse {
+
+ namespace constraint_internal {
+ template<typename T>
+ inline bool default_comparer(const T& lhs, const T& rhs)
+ {
+ return lhs == rhs;
+ }
+ }
+
+ template< typename ExpectedType, typename BinaryPredicate>
+ struct EqualsContainerConstraint : Expression< EqualsContainerConstraint<ExpectedType, BinaryPredicate> >
+ {
+ EqualsContainerConstraint(const ExpectedType& expected, const BinaryPredicate predicate)
+ : expected_(expected), predicate_(predicate)
+ {}
+
+ template<typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ typename ActualType::const_iterator actual_it;
+ typename ExpectedType::const_iterator expected_it;
+
+ for(actual_it = actual.begin(), expected_it = expected_.begin(); actual_it != actual.end() && expected_it != expected_.end(); actual_it++, expected_it++)
+ {
+ if(!predicate_(*actual_it, *expected_it))
+ {
+ return false;
+ }
+ }
+
+ return actual.size() == expected_.size();
+ }
+
+ const ExpectedType expected_;
+ const BinaryPredicate predicate_;
+
+ private:
+
+#if __cplusplus > 199711L
+#else
+ EqualsContainerConstraint& operator=(const EqualsContainerConstraint&) { return *this; }
+#endif
+
+ };
+
+ template< typename ExpectedType>
+ inline EqualsContainerConstraint<ExpectedType, bool (*)(const typename ExpectedType::value_type&, const typename ExpectedType::value_type&)> EqualsContainer(const ExpectedType& expected)
+ {
+ return EqualsContainerConstraint<ExpectedType, bool (*)(const typename ExpectedType::value_type&, const typename ExpectedType::value_type&)>(expected, constraint_internal::default_comparer);
+ }
+
+ template< typename ExpectedType, typename BinaryPredicate >
+ inline EqualsContainerConstraint<ExpectedType, BinaryPredicate> EqualsContainer(const ExpectedType& expected, const BinaryPredicate predicate)
+ {
+ return EqualsContainerConstraint<ExpectedType, BinaryPredicate>(expected, predicate);
+ }
+
+ template< typename ExpectedType, typename BinaryPredicate >
+ struct Stringizer< EqualsContainerConstraint<ExpectedType, BinaryPredicate> >
+ {
+ static std::string ToString(const EqualsContainerConstraint<ExpectedType, BinaryPredicate>& constraint)
+ {
+ std::ostringstream builder;
+ builder << snowhouse::Stringize(constraint.expected_);
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalswithdeltaconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalswithdeltaconstraint.h
new file mode 100644
index 00000000..b54fb74e
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalswithdeltaconstraint.h
@@ -0,0 +1,51 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_EQUALSWITHDELTACONSTRAINT_H
+#define IGLOO_EQUALSWITHDELTACONSTRAINT_H
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template< typename ExpectedType, typename DeltaType >
+ struct EqualsWithDeltaConstraint : Expression< EqualsWithDeltaConstraint<ExpectedType, DeltaType> >
+ {
+ EqualsWithDeltaConstraint(const ExpectedType& expected, const DeltaType& delta)
+ : m_expected(expected), m_delta(delta)
+ {
+ }
+
+ template<typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ return ((m_expected <= (actual + m_delta)) && (m_expected >= (actual - m_delta)));
+ }
+
+ ExpectedType m_expected;
+ DeltaType m_delta;
+ };
+
+ template< typename ExpectedType, typename DeltaType >
+ inline EqualsWithDeltaConstraint<ExpectedType, DeltaType> EqualsWithDelta(const ExpectedType& expected, const DeltaType& delta)
+ {
+ return EqualsWithDeltaConstraint<ExpectedType, DeltaType>(expected, delta);
+ }
+
+ template< typename ExpectedType, typename DeltaType >
+ struct Stringizer< EqualsWithDeltaConstraint< ExpectedType, DeltaType > >
+ {
+ static std::string ToString(const EqualsWithDeltaConstraint<ExpectedType, DeltaType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "equal to " << snowhouse::Stringize(constraint.m_expected) << " (+/- " << snowhouse::Stringize(constraint.m_delta) << ")";
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/andexpression.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/andexpression.h
new file mode 100644
index 00000000..8b6b7d12
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/andexpression.h
@@ -0,0 +1,46 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ANDEXPRESSION_H
+#define IGLOO_ANDEXPRESSION_H
+
+#include "./expression_fwd.h"
+
+namespace snowhouse {
+
+ template< typename LeftExpression, typename RightExpression >
+ struct AndExpression : Expression< AndExpression<LeftExpression, RightExpression> >
+ {
+ AndExpression(const LeftExpression& left, const RightExpression& right)
+ : m_left(left)
+ , m_right(right)
+ {
+ }
+
+ template< typename ActualType >
+ bool operator()(const ActualType& actual) const
+ {
+ return (m_left(actual) && m_right(actual));
+ }
+
+ LeftExpression m_left;
+ RightExpression m_right;
+ };
+
+ template< typename LeftExpression, typename RightExpression >
+ struct Stringizer< AndExpression<LeftExpression, RightExpression> >
+ {
+ static std::string ToString(const AndExpression<LeftExpression, RightExpression>& expression)
+ {
+ std::ostringstream builder;
+ builder << Stringize(expression.m_left) << " and " << Stringize(expression.m_right);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression.h
new file mode 100644
index 00000000..fa894818
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression.h
@@ -0,0 +1,38 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_EXPRESSION_H
+#define IGLOO_EXPRESSION_H
+
+#include "./notexpression.h"
+#include "./andexpression.h"
+#include "./orexpression.h"
+
+namespace snowhouse {
+
+ template<typename T>
+ struct Expression
+ {
+ NotExpression<T> operator!() const
+ {
+ return NotExpression<T>(static_cast<const T&>(*this));
+ }
+
+ template< typename Right >
+ AndExpression<T, Right> operator&&(const Right& right) const
+ {
+ return AndExpression<T, Right>(static_cast<const T&>(*this), right);
+ }
+
+ template< typename Right >
+ OrExpression<T, Right> operator||(const Right& right) const
+ {
+ return OrExpression<T, Right>(static_cast<const T&>(*this), right);
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression_fwd.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression_fwd.h
new file mode 100644
index 00000000..c0e3706e
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression_fwd.h
@@ -0,0 +1,15 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_EXPRESSION_FWD_H
+#define IGLOO_EXPRESSION_FWD_H
+
+namespace snowhouse {
+ template<typename T>
+ struct Expression;
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/notexpression.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/notexpression.h
new file mode 100644
index 00000000..4785f07b
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/notexpression.h
@@ -0,0 +1,44 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_NOTEXPRESSION_H
+#define IGLOO_NOTEXPRESSION_H
+
+#include "./expression_fwd.h"
+
+namespace snowhouse {
+
+ template< typename ExpressionType >
+ struct NotExpression : Expression< NotExpression<ExpressionType> >
+ {
+ NotExpression(const ExpressionType& expression)
+ : m_expression(expression)
+ {
+ }
+
+ template<typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ return !m_expression(actual);
+ }
+
+ ExpressionType m_expression;
+ };
+
+ template< typename ExpressionType >
+ struct Stringizer< NotExpression<ExpressionType> >
+ {
+ static std::string ToString(const NotExpression<ExpressionType>& expression)
+ {
+ std::ostringstream builder;
+ builder << "not " << snowhouse::Stringize(expression.m_expression);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/orexpression.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/orexpression.h
new file mode 100644
index 00000000..c1b6c12b
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/orexpression.h
@@ -0,0 +1,46 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_OREXPRESSION_H
+#define IGLOO_OREXPRESSION_H
+
+#include "./expression_fwd.h"
+
+namespace snowhouse {
+
+ template< typename LeftExpression, typename RightExpression >
+ struct OrExpression : Expression< OrExpression<LeftExpression, RightExpression> >
+ {
+ OrExpression(const LeftExpression& left, const RightExpression& right)
+ : m_left(left)
+ , m_right(right)
+ {
+ }
+
+ template< typename ActualType >
+ bool operator()(const ActualType& actual) const
+ {
+ return (m_left(actual) || m_right(actual));
+ }
+
+ LeftExpression m_left;
+ RightExpression m_right;
+ };
+
+ template< typename LeftExpression, typename RightExpression >
+ struct Stringizer< OrExpression<LeftExpression, RightExpression> >
+ {
+ static std::string ToString(const OrExpression<LeftExpression, RightExpression>& expression)
+ {
+ std::ostringstream builder;
+ builder << snowhouse::Stringize(expression.m_left) << " or " << snowhouse::Stringize(expression.m_right);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/fulfillsconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/fulfillsconstraint.h
new file mode 100644
index 00000000..577056c1
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/fulfillsconstraint.h
@@ -0,0 +1,51 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef FULFILLSCONSTRAINT_H
+#define FULFILLSCONSTRAINT_H
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template< typename MatcherType >
+ struct FulfillsConstraint : Expression< FulfillsConstraint<MatcherType> >
+ {
+ FulfillsConstraint(const MatcherType& matcher)
+ : m_matcher(matcher)
+ {
+ }
+
+ template<typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ return m_matcher.Matches(actual);
+ }
+
+ MatcherType m_matcher;
+ };
+
+ template< typename MatcherType >
+ inline FulfillsConstraint<MatcherType> Fulfills(const MatcherType& matcher)
+ {
+ return FulfillsConstraint<MatcherType>(matcher);
+ }
+
+ template< typename MatcherType >
+ struct Stringizer< FulfillsConstraint< MatcherType > >
+ {
+ static std::string ToString(const FulfillsConstraint<MatcherType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << snowhouse::Stringize(constraint.m_matcher);
+
+ return builder.str();
+ }
+ };
+
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/haslengthconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/haslengthconstraint.h
new file mode 100644
index 00000000..fb6e7cc9
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/haslengthconstraint.h
@@ -0,0 +1,60 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_HASLENGTHCONSTRAINT_H
+#define IGLOO_HASLENGTHCONSTRAINT_H
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template <typename ExpectedType>
+ struct HasLengthConstraint : Expression< HasLengthConstraint<ExpectedType> >
+ {
+ HasLengthConstraint(const ExpectedType& expected)
+ : m_expected(expected) {}
+
+ template <typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ typedef typename ActualType::size_type SizeType;
+ SizeType expectedSize = static_cast<SizeType>(m_expected);
+ return (actual.size() == expectedSize);
+ }
+
+ ExpectedType m_expected;
+ };
+
+ template< typename ExpectedType >
+ inline HasLengthConstraint<ExpectedType> HasLength(const ExpectedType& expected)
+ {
+ return HasLengthConstraint<ExpectedType>(expected);
+ }
+
+ inline HasLengthConstraint<int> IsEmpty()
+ {
+ return HasLength<int>(0);
+ }
+
+ inline HasLengthConstraint<std::string> HasLength(const char* expected)
+ {
+ return HasLengthConstraint<std::string>(expected);
+ }
+
+ template< typename ExpectedType >
+ struct Stringizer< HasLengthConstraint< ExpectedType > >
+ {
+ static std::string ToString(const HasLengthConstraint<ExpectedType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "of length " << snowhouse::Stringize(constraint.m_expected);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanconstraint.h
new file mode 100644
index 00000000..e7ef01f8
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanconstraint.h
@@ -0,0 +1,55 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ISGREATERTHANCONSTRAINT_H
+#define IGLOO_ISGREATERTHANCONSTRAINT_H
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template< typename ExpectedType >
+ struct IsGreaterThanConstraint : Expression< IsGreaterThanConstraint<ExpectedType> >
+ {
+ IsGreaterThanConstraint(const ExpectedType& expected)
+ : m_expected(expected)
+ {
+ }
+
+ template<typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ return (actual > m_expected);
+ }
+
+ ExpectedType m_expected;
+ };
+
+ template< typename ExpectedType >
+ inline IsGreaterThanConstraint<ExpectedType> IsGreaterThan(const ExpectedType& expected)
+ {
+ return IsGreaterThanConstraint<ExpectedType>(expected);
+ }
+
+ inline IsGreaterThanConstraint<std::string> IsGreaterThan(const char* expected)
+ {
+ return IsGreaterThanConstraint<std::string>(expected);
+ }
+
+ template< typename ExpectedType >
+ struct Stringizer< IsGreaterThanConstraint< ExpectedType > >
+ {
+ static std::string ToString(const IsGreaterThanConstraint<ExpectedType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "greater than " << snowhouse::Stringize(constraint.m_expected);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanorequaltoconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanorequaltoconstraint.h
new file mode 100644
index 00000000..3752887b
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanorequaltoconstraint.h
@@ -0,0 +1,55 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ISGREATERTHANOREQUALTOCONSTRAINT_H
+#define IGLOO_ISGREATERTHANOREQUALTOCONSTRAINT_H
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template< typename ExpectedType >
+ struct IsGreaterThanOrEqualToConstraint : Expression < IsGreaterThanOrEqualToConstraint<ExpectedType> >
+ {
+ IsGreaterThanOrEqualToConstraint(const ExpectedType& expected)
+ : m_expected(expected)
+ {
+ }
+
+ template<typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ return (actual >= m_expected);
+ }
+
+ ExpectedType m_expected;
+ };
+
+ template< typename ExpectedType >
+ inline IsGreaterThanOrEqualToConstraint<ExpectedType> IsGreaterThanOrEqualTo(const ExpectedType& expected)
+ {
+ return IsGreaterThanOrEqualToConstraint<ExpectedType>(expected);
+ }
+
+ inline IsGreaterThanOrEqualToConstraint<std::string> IsGreaterThanOrEqualTo(const char* expected)
+ {
+ return IsGreaterThanOrEqualToConstraint<std::string>(expected);
+ }
+
+ template< typename ExpectedType >
+ struct Stringizer < IsGreaterThanOrEqualToConstraint< ExpectedType > >
+ {
+ static std::string ToString(const IsGreaterThanOrEqualToConstraint<ExpectedType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "greater than or equal to " << snowhouse::Stringize(constraint.m_expected);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanconstraint.h
new file mode 100644
index 00000000..7379dcf7
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanconstraint.h
@@ -0,0 +1,54 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ISLESSTHANCONSTRAINT_H
+#define IGLOO_ISLESSTHANCONSTRAINT_H
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template< typename ExpectedType >
+ struct IsLessThanConstraint : Expression< IsLessThanConstraint<ExpectedType> >
+ {
+ IsLessThanConstraint(const ExpectedType& expected)
+ : m_expected(expected)
+ {
+ }
+
+ template<typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ return (actual < m_expected);
+ }
+
+ ExpectedType m_expected;
+ };
+
+ template< typename ExpectedType >
+ inline IsLessThanConstraint<ExpectedType> IsLessThan(const ExpectedType& expected)
+ {
+ return IsLessThanConstraint<ExpectedType>(expected);
+ }
+
+ inline IsLessThanConstraint<std::string> IsLessThan(const char* expected)
+ {
+ return IsLessThanConstraint<std::string>(expected);
+ }
+
+ template< typename ExpectedType >
+ struct Stringizer< IsLessThanConstraint< ExpectedType > >
+ {
+ static std::string ToString(const IsLessThanConstraint<ExpectedType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "less than " << snowhouse::Stringize(constraint.m_expected);
+
+ return builder.str();
+ }
+ };
+}
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanorequaltoconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanorequaltoconstraint.h
new file mode 100644
index 00000000..36e02ab4
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanorequaltoconstraint.h
@@ -0,0 +1,55 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ISLESSTHANOREQUALTOCONSTRAINT_H
+#define IGLOO_ISLESSTHANOREQUALTOCONSTRAINT_H
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template< typename ExpectedType >
+ struct IsLessThanOrEqualToConstraint : Expression < IsLessThanOrEqualToConstraint<ExpectedType> >
+ {
+ IsLessThanOrEqualToConstraint(const ExpectedType& expected)
+ : m_expected(expected)
+ {
+ }
+
+ template<typename ActualType>
+ bool operator()(const ActualType& actual) const
+ {
+ return (actual <= m_expected);
+ }
+
+ ExpectedType m_expected;
+ };
+
+ template< typename ExpectedType >
+ inline IsLessThanOrEqualToConstraint<ExpectedType> IsLessThanOrEqualTo(const ExpectedType& expected)
+ {
+ return IsLessThanOrEqualToConstraint<ExpectedType>(expected);
+ }
+
+ inline IsLessThanOrEqualToConstraint<std::string> IsLessThanOrEqualTo(const char* expected)
+ {
+ return IsLessThanOrEqualToConstraint<std::string>(expected);
+ }
+
+ template< typename ExpectedType >
+ struct Stringizer < IsLessThanOrEqualToConstraint< ExpectedType > >
+ {
+ static std::string ToString(const IsLessThanOrEqualToConstraint<ExpectedType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "less than or equal to " << snowhouse::Stringize(constraint.m_expected);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/startswithconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/startswithconstraint.h
new file mode 100644
index 00000000..ffdd1696
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/startswithconstraint.h
@@ -0,0 +1,52 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_STARTSWITHCONSTRAINT_H
+#define IGLOO_STARTSWITHCONSTRAINT_H
+
+#include "./expressions/expression.h"
+
+namespace snowhouse {
+
+ template <typename ExpectedType>
+ struct StartsWithConstraint : Expression< StartsWithConstraint<ExpectedType> >
+ {
+ StartsWithConstraint(const ExpectedType& expected)
+ : m_expected(expected) {}
+
+ bool operator()(const std::string& actual) const
+ {
+ return actual.find(m_expected) == 0;
+ }
+
+ ExpectedType m_expected;
+ };
+
+ template< typename ExpectedType >
+ inline StartsWithConstraint<ExpectedType> StartsWith(const ExpectedType& expected)
+ {
+ return StartsWithConstraint<ExpectedType>(expected);
+ }
+
+ inline StartsWithConstraint<std::string> StartsWith(const char* expected)
+ {
+ return StartsWithConstraint<std::string>(expected);
+ }
+
+ template< typename ExpectedType >
+ struct Stringizer< StartsWithConstraint< ExpectedType > >
+ {
+ static std::string ToString(const StartsWithConstraint<ExpectedType>& constraint)
+ {
+ std::ostringstream builder;
+ builder << "starts with " << snowhouse::Stringize(constraint.m_expected);
+
+ return builder.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/exceptions.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/exceptions.h
new file mode 100644
index 00000000..22ad11ef
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/exceptions.h
@@ -0,0 +1,120 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_EXCEPTIONS_H
+#define IGLOO_EXCEPTIONS_H
+
+#include "assert.h"
+
+namespace snowhouse {
+
+ template <typename ExceptionType>
+ class ExceptionStorage
+ {
+ public:
+ static void last_exception(ExceptionType*** e, bool clear=false)
+ {
+ static ExceptionType* last = NULL;
+ if(clear && last)
+ {
+ delete last;
+ return;
+ }
+
+ *e = &last;
+ silly_warning_about_unused_arg(e);
+ }
+
+ static ExceptionType*** silly_warning_about_unused_arg(ExceptionType*** e)
+ {
+ return e;
+ }
+
+ static void store(const ExceptionType& e)
+ {
+ ExceptionType** last = NULL;
+ last_exception(&last);
+ if(*last)
+ {
+ delete *last;
+ *last = NULL;
+ }
+
+ *last = new ExceptionType(e);
+ }
+
+ void compiler_thinks_i_am_unused() {}
+
+ ~ExceptionStorage()
+ {
+ ExceptionType** e = NULL;
+ last_exception(&e);
+ if(*e)
+ {
+ delete *e;
+ *e = NULL;
+ }
+ }
+ };
+
+ template <typename ExceptionType>
+ inline ExceptionType& LastException()
+ {
+ ExceptionType** e = NULL;
+ ExceptionStorage<ExceptionType>::last_exception(&e);
+ if(*e == NULL)
+ {
+ Assert::Failure("No exception was stored");
+ }
+
+ return **e;
+ }
+}
+
+#define IGLOO_CONCAT2(a, b) a##b
+#define IGLOO_CONCAT(a, b) IGLOO_CONCAT2(a, b)
+
+#define SNOWHOUSE_ASSERT_THROWS(EXCEPTION_TYPE, METHOD, FAILURE_HANDLER_TYPE) \
+::snowhouse::ExceptionStorage<EXCEPTION_TYPE> IGLOO_CONCAT(IGLOO_storage_, __LINE__); IGLOO_CONCAT(IGLOO_storage_, __LINE__).compiler_thinks_i_am_unused(); \
+{ \
+ bool wrong_exception = false; \
+ bool no_exception = false; \
+ try \
+ { \
+ METHOD; \
+ no_exception = true; \
+ } \
+ catch (const EXCEPTION_TYPE& e) \
+ { \
+ ::snowhouse::ExceptionStorage<EXCEPTION_TYPE>::store(e); \
+ } \
+ catch(...) \
+ { \
+ wrong_exception = true; \
+ } \
+ if(no_exception) \
+ { \
+ std::ostringstream stm; \
+ stm << "Expected " << #EXCEPTION_TYPE << ". No exception was thrown."; \
+ ::snowhouse::ConfigurableAssert<FAILURE_HANDLER_TYPE>::Failure(stm.str()); \
+ } \
+ if(wrong_exception) \
+ { \
+ std::ostringstream stm; \
+ stm << "Expected " << #EXCEPTION_TYPE << ". Wrong exception was thrown."; \
+ ::snowhouse::ConfigurableAssert<FAILURE_HANDLER_TYPE>::Failure(stm.str()); \
+ } \
+}
+
+#ifndef SNOWHOUSE_NO_MACROS
+
+#define AssertThrows(EXCEPTION_TYPE, METHOD) SNOWHOUSE_ASSERT_THROWS(EXCEPTION_TYPE, (METHOD), ::snowhouse::DefaultFailureHandler)
+
+#endif // SNOWHOUSE_NO_MACROS
+
+#endif
+
+
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintadapter.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintadapter.h
new file mode 100644
index 00000000..b1719288
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintadapter.h
@@ -0,0 +1,39 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_CONSTRAINTADAPTER_H
+#define IGLOO_CONSTRAINTADAPTER_H
+
+namespace snowhouse {
+
+ template <typename ConstraintType>
+ struct ConstraintAdapter
+ {
+ ConstraintAdapter(const ConstraintType& constraint) : m_constraint(constraint)
+ {
+ }
+
+ template <typename ConstraintListType, typename ActualType>
+ void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ result.push(m_constraint(actual));
+ EvaluateConstraintList(list.m_tail, result, operators, actual);
+ }
+
+ ConstraintType m_constraint;
+ };
+
+ template<typename ConstraintType>
+ struct Stringizer< ConstraintAdapter<ConstraintType> >
+ {
+ static std::string ToString(const ConstraintAdapter<ConstraintType>& constraintAdapter)
+ {
+ return snowhouse::Stringize(constraintAdapter.m_constraint);
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintlist.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintlist.h
new file mode 100644
index 00000000..1a62a60e
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintlist.h
@@ -0,0 +1,91 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_CONSTRAINT_H
+#define IGLOO_CONSTRAINT_H
+
+namespace snowhouse {
+
+ struct ConstraintOperator;
+ typedef std::stack<bool> ResultStack;
+ typedef std::stack<ConstraintOperator*> OperatorStack;
+
+ template <typename HT, typename TT>
+ struct ConstraintList
+ {
+ typedef HT HeadType;
+ typedef TT TailType;
+
+ ConstraintList(const HeadType& head, const TailType& tail)
+ : m_head(head), m_tail(tail)
+ {
+ }
+
+ HeadType m_head;
+ TailType m_tail;
+ };
+
+ struct Nil
+ {
+ Nil() {}
+ Nil(const Nil&) {}
+ };
+
+
+ // ---- These structs defines the resulting types of list concatenation operations
+ template <typename L1, typename L2>
+ struct type_concat
+ {
+ typedef ConstraintList<typename L1::HeadType, typename type_concat<typename L1::TailType, L2>::t> t;
+ };
+
+ template <typename L2> struct type_concat<Nil, L2> { typedef L2 t; };
+
+ template <typename L3> inline L3 tr_concat(const Nil&, const Nil&) { return Nil(); }
+
+
+ // ---- These structs define the concatenation operations.
+
+ template <typename LeftList, typename RightList, typename ResultList>
+ struct ListConcat
+ {
+ static ResultList Concatenate(const LeftList& left, const RightList& right)
+ {
+ return ResultList(left.m_head, ListConcat<typename LeftList::TailType, RightList, typename type_concat< typename LeftList::TailType, RightList>::t>::Concatenate(left.m_tail, right));
+ }
+ };
+
+ // Concatenating an empty list with a second list yields the second list
+ template <typename RightList, typename ResultList>
+ struct ListConcat<Nil, RightList, ResultList>
+ {
+ static ResultList Concatenate(const Nil&, const RightList& right)
+ {
+ return right;
+ }
+
+ };
+
+ // Concatenating two empty lists yields an empty list
+ template <typename ResultList>
+ struct ListConcat<Nil, Nil, ResultList>
+ {
+ static ResultList Concatenate(const Nil&, const Nil&)
+ {
+ return Nil();
+ }
+ };
+
+ // ---- The concatenation operation
+
+ template <typename L1, typename L2>
+ inline typename type_concat<L1, L2>::t Concatenate(const L1& list1, const L2& list2)
+ {
+ return ListConcat<L1, L2, typename type_concat<L1, L2>::t>::Concatenate(list1, list2);
+ }
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/expressionbuilder.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/expressionbuilder.h
new file mode 100644
index 00000000..f0889f1d
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/expressionbuilder.h
@@ -0,0 +1,357 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_EXPRESSIONBUILDER_H
+#define IGLOO_EXPRESSIONBUILDER_H
+
+#include <cstddef>
+
+namespace snowhouse {
+
+ // ---- Evaluation of list of constraints
+
+ template <typename ConstraintListType, typename ActualType>
+ inline void EvaluateConstraintList(ConstraintListType& constraint_list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ constraint_list.m_head.Evaluate(constraint_list, result, operators, actual);
+ }
+
+ template <typename ActualType>
+ inline void EvaluateConstraintList(Nil&, ResultStack&, OperatorStack&, const ActualType&) {}
+
+
+ template <typename ConstraintListType>
+ struct ExpressionBuilder
+ {
+ ExpressionBuilder(const ConstraintListType& list) : m_constraint_list(list)
+ {
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<ExpectedType> >, Nil> >::t>
+ EqualTo(const ExpectedType& expected)
+ {
+ typedef ConstraintAdapter<EqualsConstraint<ExpectedType> > ConstraintAdapterType;
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+
+ ConstraintAdapterType constraint(expected);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ template <typename ExpectedType, typename DeltaType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsWithDeltaConstraint<ExpectedType, DeltaType> >, Nil> >::t>
+ EqualToWithDelta(const ExpectedType& expected, const DeltaType& delta)
+ {
+ typedef ConstraintAdapter<EqualsWithDeltaConstraint<ExpectedType, DeltaType> > ConstraintAdapterType;
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+
+ ConstraintAdapterType constraint(EqualsWithDeltaConstraint<ExpectedType, DeltaType>(expected, delta));
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ template <typename MatcherType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<FulfillsConstraint<MatcherType> >, Nil> >::t>
+ Fulfilling(const MatcherType& matcher)
+ {
+ typedef ConstraintAdapter<FulfillsConstraint<MatcherType> > ConstraintAdapterType;
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+
+ ConstraintAdapterType constraint(matcher);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<bool> >, Nil> >::t>
+ False()
+ {
+ return EqualTo<bool>(false);
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<bool> >, Nil> >::t>
+ True()
+ {
+ return EqualTo<bool>(true);
+ }
+
+#if __cplusplus > 199711L
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<std::nullptr_t> >, Nil> >::t>
+ Null()
+ {
+ return EqualTo<std::nullptr_t>(nullptr);
+ }
+#endif
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsConstraint<std::string> >, Nil> >::t>
+ EqualTo(const char* expected)
+ {
+ return EqualTo<std::string>(std::string(expected));
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<IsGreaterThanConstraint<ExpectedType> >, Nil> >::t>
+ GreaterThan(const ExpectedType& expected)
+ {
+ typedef ConstraintAdapter<IsGreaterThanConstraint<ExpectedType> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(expected);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<IsGreaterThanOrEqualToConstraint<ExpectedType> >, Nil> >::t>
+ GreaterThanOrEqualTo(const ExpectedType& expected)
+ {
+ typedef ConstraintAdapter<IsGreaterThanOrEqualToConstraint<ExpectedType> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(expected);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<IsLessThanConstraint<ExpectedType> >, Nil> >::t>
+ LessThan(const ExpectedType& expected)
+ {
+ typedef ConstraintAdapter<IsLessThanConstraint<ExpectedType> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(expected);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<IsLessThanOrEqualToConstraint<ExpectedType> >, Nil> >::t>
+ LessThanOrEqualTo(const ExpectedType& expected)
+ {
+ typedef ConstraintAdapter<IsLessThanOrEqualToConstraint<ExpectedType> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(expected);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<ContainsConstraint<ExpectedType> >, Nil> >::t>
+ Containing(const ExpectedType& expected)
+ {
+ typedef ConstraintAdapter<ContainsConstraint<ExpectedType> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(expected);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<ContainsConstraint<std::string> >, Nil> >::t>
+ Containing(const char* expected)
+ {
+ return Containing<std::string>(std::string(expected));
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EndsWithConstraint<ExpectedType> >, Nil> >::t>
+ EndingWith(const ExpectedType& expected)
+ {
+ typedef ConstraintAdapter<EndsWithConstraint<ExpectedType> > ConstraintAdapterType;
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+
+ ConstraintAdapterType constraint(expected);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EndsWithConstraint<std::string> >, Nil> >::t>
+ EndingWith(const char* expected)
+ {
+ return EndingWith(std::string(expected));
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<StartsWithConstraint<ExpectedType> >, Nil> >::t>
+ StartingWith(const ExpectedType& expected)
+ {
+ typedef ConstraintAdapter<StartsWithConstraint<ExpectedType> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(expected);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<StartsWithConstraint<std::string> >, Nil> >::t>
+ StartingWith(const char* expected)
+ {
+ return StartingWith(std::string(expected));
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<HasLengthConstraint<ExpectedType> >, Nil> >::t>
+ OfLength(const ExpectedType& expected)
+ {
+ typedef ConstraintAdapter<HasLengthConstraint<ExpectedType> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(expected);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<HasLengthConstraint<int> >, Nil> >::t>
+ Empty()
+ {
+ typedef ConstraintAdapter<HasLengthConstraint<int> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(0);
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ template <typename ExpectedType>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsContainerConstraint<ExpectedType, bool (*)(const typename ExpectedType::value_type&, const typename ExpectedType::value_type&)> >, Nil> >::t>
+ EqualToContainer(const ExpectedType& expected)
+ {
+ typedef bool (*DefaultBinaryPredivateType)(const typename ExpectedType::value_type&, const typename ExpectedType::value_type&);
+ typedef ConstraintAdapter<EqualsContainerConstraint<ExpectedType, DefaultBinaryPredivateType> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(EqualsContainerConstraint<ExpectedType, DefaultBinaryPredivateType>(expected, constraint_internal::default_comparer<typename ExpectedType::value_type>));
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ template <typename ExpectedType, typename BinaryPredicate>
+ ExpressionBuilder<typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapter<EqualsContainerConstraint<ExpectedType, BinaryPredicate> >, Nil> >::t>
+ EqualToContainer(const ExpectedType& expected, const BinaryPredicate predicate)
+ {
+ typedef ConstraintAdapter<EqualsContainerConstraint<ExpectedType, BinaryPredicate> > ConstraintAdapterType;
+
+ typedef ExpressionBuilder< typename type_concat<ConstraintListType, ConstraintList<ConstraintAdapterType, Nil> >::t > BuilderType;
+ ConstraintAdapterType constraint(EqualsContainerConstraint<ExpectedType, BinaryPredicate>(expected, predicate));
+ ConstraintList<ConstraintAdapterType, Nil> node(constraint, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ typedef ConstraintList<AndOperator, Nil> AndOperatorNode;
+ typedef ConstraintList<OrOperator, Nil> OrOperatorNode;
+ typedef ConstraintList<NotOperator, Nil> NotOperatorNode;
+ typedef ConstraintList<AllOperator, Nil> AllOperatorNode;
+ typedef ConstraintList<AtLeastOperator, Nil> AtLeastOperatorNode;
+ typedef ConstraintList<ExactlyOperator, Nil> ExactlyOperatorNode;
+ typedef ConstraintList<AtMostOperator, Nil> AtMostOperatorNode;
+ typedef ConstraintList<NoneOperator, Nil> NoneOperatorNode;
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, AllOperatorNode>::t> All()
+ {
+ typedef ExpressionBuilder<typename type_concat<ConstraintListType, AllOperatorNode>::t> BuilderType;
+ AllOperator op;
+ AllOperatorNode node(op, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, AtLeastOperatorNode>::t> AtLeast(unsigned int expected)
+ {
+ typedef ExpressionBuilder<typename type_concat<ConstraintListType, AtLeastOperatorNode>::t> BuilderType;
+ AtLeastOperator op(expected);
+ AtLeastOperatorNode node(op, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, ExactlyOperatorNode>::t> Exactly(unsigned int expected)
+ {
+ typedef ExpressionBuilder<typename type_concat<ConstraintListType, ExactlyOperatorNode>::t> BuilderType;
+ ExactlyOperator op(expected);
+ ExactlyOperatorNode node(op, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, AtMostOperatorNode>::t> AtMost(unsigned int expected)
+ {
+ typedef ExpressionBuilder<typename type_concat<ConstraintListType, AtMostOperatorNode>::t> BuilderType;
+ AtMostOperator op(expected);
+ AtMostOperatorNode node(op, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, NoneOperatorNode>::t> None()
+ {
+ typedef ExpressionBuilder<typename type_concat<ConstraintListType, NoneOperatorNode>::t> BuilderType;
+ NoneOperator op;
+ NoneOperatorNode node(op, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, AndOperatorNode>::t> And()
+ {
+ typedef ExpressionBuilder<typename type_concat<ConstraintListType, AndOperatorNode>::t> BuilderType;
+ AndOperator op;
+ AndOperatorNode node(op, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, OrOperatorNode>::t> Or()
+ {
+ typedef ExpressionBuilder<typename type_concat<ConstraintListType, OrOperatorNode>::t> BuilderType;
+ OrOperator op;
+ OrOperatorNode node(op, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ ExpressionBuilder<typename type_concat<ConstraintListType, NotOperatorNode>::t> Not()
+ {
+ typedef ExpressionBuilder<typename type_concat<ConstraintListType, NotOperatorNode>::t> BuilderType;
+ NotOperator op;
+ NotOperatorNode node(op, Nil());
+ return BuilderType(Concatenate(m_constraint_list, node));
+ }
+
+ template <typename ActualType>
+ void Evaluate(ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ EvaluateConstraintList(m_constraint_list, result, operators, actual);
+ }
+
+ ConstraintListType m_constraint_list;
+ };
+
+ template <typename T>
+ inline void StringizeConstraintList(const T& list, std::ostringstream& stm)
+ {
+ if (stm.tellp() > 0)
+ stm << " ";
+
+ stm << snowhouse::Stringize(list.m_head);
+ StringizeConstraintList(list.m_tail, stm);
+ }
+
+ inline void StringizeConstraintList(const Nil&, std::ostringstream&)
+ {
+ }
+
+ template<typename ConstraintListType>
+ struct Stringizer< ExpressionBuilder<ConstraintListType> >
+ {
+ static std::string ToString(const ExpressionBuilder<ConstraintListType>& builder)
+ {
+ std::ostringstream stm;
+ StringizeConstraintList(builder.m_constraint_list, stm);
+
+ return stm.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/fluent.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/fluent.h
new file mode 100644
index 00000000..6bbd4b81
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/fluent.h
@@ -0,0 +1,38 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_FLUENT_H
+#define IGLOO_FLUENT_H
+
+#include "constraintlist.h"
+#include "constraintadapter.h"
+#include "operators/constraintoperator.h"
+#include "operators/andoperator.h"
+#include "operators/oroperator.h"
+#include "operators/collections/collectionconstraintevaluator.h"
+#include "operators/collections/alloperator.h"
+#include "operators/collections/noneoperator.h"
+#include "operators/collections/atleastoperator.h"
+#include "operators/collections/exactlyoperator.h"
+#include "operators/collections/atmostoperator.h"
+#include "operators/notoperator.h"
+#include "expressionbuilder.h"
+
+namespace snowhouse {
+
+ inline ExpressionBuilder<Nil> Is()
+ {
+ return ExpressionBuilder<Nil>(Nil());
+ }
+
+ inline ExpressionBuilder<Nil> Has()
+ {
+ return ExpressionBuilder<Nil>(Nil());
+ }
+
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/andoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/andoperator.h
new file mode 100644
index 00000000..39670632
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/andoperator.h
@@ -0,0 +1,54 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ANDOPERATOR_H
+#define IGLOO_ANDOPERATOR_H
+
+namespace snowhouse {
+
+ struct AndOperator : public ConstraintOperator
+ {
+ template <typename ConstraintListType, typename ActualType>
+ void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ EvaluateOperatorsWithLessOrEqualPrecedence(*this, operators, result);
+
+ operators.push(this);
+
+ EvaluateConstraintList(list.m_tail, result, operators, actual);
+ }
+
+ void PerformOperation(ResultStack& result)
+ {
+ if(result.size() < 2)
+ {
+ throw InvalidExpressionException("The expression contains an and operator with too few operands");
+ }
+
+ bool right = result.top();
+ result.pop();
+ bool left = result.top();
+ result.pop();
+
+ result.push(left && right);
+ }
+
+ int Precedence() const
+ {
+ return 3;
+ }
+ };
+
+ template<>
+ struct Stringizer<AndOperator>
+ {
+ static std::string ToString(const AndOperator&)
+ {
+ return "and";
+ }
+ };
+}
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/alloperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/alloperator.h
new file mode 100644
index 00000000..4157faf9
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/alloperator.h
@@ -0,0 +1,35 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ALLOPERATOR_H
+#define IGLOO_ALLOPERATOR_H
+
+#include "collectionoperator.h"
+
+namespace snowhouse {
+
+ struct AllOperator : public CollectionOperator
+ {
+ template <typename ConstraintListType, typename ActualType>
+ void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ unsigned int passed_elements = CollectionConstraintEvaluator<ConstraintListType, ActualType>::Evaluate(*this, list, result, operators, actual);
+
+ result.push(passed_elements == actual.size());
+ }
+ };
+
+ template<>
+ struct Stringizer<AllOperator>
+ {
+ static std::string ToString(const AllOperator&)
+ {
+ return "all";
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atleastoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atleastoperator.h
new file mode 100644
index 00000000..d46e697f
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atleastoperator.h
@@ -0,0 +1,41 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ATLEASTOPERATOR_H
+#define IGLOO_ATLEASTOPERATOR_H
+
+#include "collectionoperator.h"
+
+namespace snowhouse {
+
+ struct AtLeastOperator : public CollectionOperator
+ {
+ AtLeastOperator(unsigned int expected) : m_expected(expected) {}
+
+ template <typename ConstraintListType, typename ActualType>
+ void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ unsigned int passed_elements = CollectionConstraintEvaluator<ConstraintListType, ActualType>::Evaluate(*this, list, result, operators, actual);
+
+ result.push(passed_elements >= m_expected);
+ }
+
+ unsigned int m_expected;
+ };
+
+ template<>
+ struct Stringizer<AtLeastOperator>
+ {
+ static std::string ToString(const AtLeastOperator& op)
+ {
+ std::ostringstream stm;
+ stm << "at least " << op.m_expected;
+ return stm.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atmostoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atmostoperator.h
new file mode 100644
index 00000000..53debbc8
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atmostoperator.h
@@ -0,0 +1,39 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_ATMOSTOPERATOR_H
+#define IGLOO_ATMOSTOPERATOR_H
+
+namespace snowhouse {
+
+ struct AtMostOperator : public CollectionOperator
+ {
+ AtMostOperator(unsigned int expected) : m_expected(expected) {}
+
+ template <typename ConstraintListType, typename ActualType>
+ void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ unsigned int passed_elements = CollectionConstraintEvaluator<ConstraintListType, ActualType>::Evaluate(*this, list, result, operators, actual);
+
+ result.push(passed_elements <= m_expected);
+ }
+
+ unsigned int m_expected;
+ };
+
+ template<>
+ struct Stringizer<AtMostOperator>
+ {
+ static std::string ToString(const AtMostOperator& op)
+ {
+ std::ostringstream stm;
+ stm << "at most " << op.m_expected;
+ return stm.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionconstraintevaluator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionconstraintevaluator.h
new file mode 100644
index 00000000..3fa30f2c
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionconstraintevaluator.h
@@ -0,0 +1,113 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_COLLECTIONCONSTRAINTEVALUATOR_H
+#define IGLOO_COLLECTIONCONSTRAINTEVALUATOR_H
+
+#include <string>
+#include "../invalidexpressionexception.h"
+
+namespace snowhouse
+{
+
+template<typename ConstraintListType, typename ActualType>
+struct CollectionConstraintEvaluator
+{
+ static unsigned int Evaluate(const ConstraintOperator& op,
+ ConstraintListType& expression, ResultStack& result,
+ OperatorStack& operators, const ActualType& actual)
+ {
+ ConstraintOperator::EvaluateOperatorsWithLessOrEqualPrecedence(op,
+ operators, result);
+
+ unsigned int passed_elements = 0;
+ typename ActualType::const_iterator it;
+ for(it = actual.begin(); it != actual.end(); it++)
+ {
+ if(ConstraintOperator::EvaluateElementAgainstRestOfExpression(expression,
+ *it))
+ {
+ passed_elements++;
+ }
+ }
+
+ return passed_elements;
+ }
+};
+
+struct StringLineParser
+{
+ static void Parse(const std::string& str, std::vector<std::string>& res)
+ {
+ size_t start = 0;
+ size_t newline = FindNewline(str, start);
+
+ while(newline != std::string::npos)
+ {
+ StoreLine(str, start, newline, res);
+ start = MoveToNextLine(str, newline);
+ newline = FindNewline(str, start);
+ }
+
+ if(start < str.size())
+ {
+ StoreLine(str, start, std::string::npos, res);
+ }
+ }
+
+private:
+ static size_t FindNewline(const std::string& str, size_t start)
+ {
+ return str.find_first_of("\r\n", start);
+ }
+
+ static void StoreLine(const std::string& str, size_t start, size_t end,
+ std::vector<std::string>& res)
+ {
+ std::string line = str.substr(start, end - start);
+ res.push_back(line);
+ }
+
+ static size_t MoveToNextLine(const std::string& str, size_t newline)
+ {
+ if(str.find("\r\n", newline) == newline)
+ {
+ return newline + 2;
+ }
+
+ if(str.find("\n", newline) == newline)
+ {
+ return newline + 1;
+ }
+
+ if(str.find("\r", newline) == newline)
+ {
+ return newline + 1;
+ }
+
+ std::ostringstream stm;
+ stm << "This string seems to contain an invalid line ending at position "
+ << newline << ":\n" << str << std::endl;
+ throw InvalidExpressionException(stm.str());
+ }
+};
+
+template<typename ConstraintListType>
+struct CollectionConstraintEvaluator<ConstraintListType, std::string>
+{
+ static unsigned int Evaluate(const ConstraintOperator& op,
+ ConstraintListType& expression, ResultStack& result,
+ OperatorStack& operators, const std::string& actual)
+ {
+ std::vector<std::string> lines;
+ StringLineParser::Parse(actual, lines);
+ return CollectionConstraintEvaluator<ConstraintListType, std::vector<std::string> >::Evaluate(op, expression, result, operators, lines);
+ }
+};
+
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionoperator.h
new file mode 100644
index 00000000..c1bb8bec
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionoperator.h
@@ -0,0 +1,24 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_COLLECTIONOPERATOR_H
+#define IGLOO_COLLECTIONOPERATOR_H
+
+namespace snowhouse {
+ struct CollectionOperator : public ConstraintOperator
+ {
+ void PerformOperation(ResultStack&)
+ {
+ }
+
+ int Precedence() const
+ {
+ return 1;
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/exactlyoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/exactlyoperator.h
new file mode 100644
index 00000000..81c2dcaa
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/exactlyoperator.h
@@ -0,0 +1,39 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_EXACTLYOPERATOR_H
+#define IGLOO_EXACTLYOPERATOR_H
+
+namespace snowhouse {
+
+ struct ExactlyOperator : public CollectionOperator
+ {
+ ExactlyOperator(unsigned int expected) : m_expected(expected) {}
+
+ template <typename ConstraintListType, typename ActualType>
+ void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ unsigned int passed_elements = CollectionConstraintEvaluator<ConstraintListType, ActualType>::Evaluate(*this, list, result, operators, actual);
+
+ result.push(passed_elements == m_expected);
+ }
+
+ unsigned int m_expected;
+ };
+
+ template<>
+ struct Stringizer< ExactlyOperator >
+ {
+ static std::string ToString(const ExactlyOperator& op)
+ {
+ std::ostringstream stm;
+ stm << "exactly " << op.m_expected;
+ return stm.str();
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/noneoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/noneoperator.h
new file mode 100644
index 00000000..c4570915
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/noneoperator.h
@@ -0,0 +1,33 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_NONEOPERATOR_H
+#define IGLOO_NONEOPERATOR_H
+
+namespace snowhouse {
+
+ struct NoneOperator : public CollectionOperator
+ {
+ template <typename ConstraintListType, typename ActualType>
+ void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ unsigned int passed_elements = CollectionConstraintEvaluator<ConstraintListType, ActualType>::Evaluate(*this, list, result, operators, actual);
+ result.push(passed_elements == 0);
+ }
+ };
+
+ template<>
+ struct Stringizer<NoneOperator>
+ {
+ static std::string ToString(const NoneOperator&)
+ {
+ return "none";
+ }
+ };
+
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/constraintoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/constraintoperator.h
new file mode 100644
index 00000000..eafe6c51
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/constraintoperator.h
@@ -0,0 +1,70 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_CONTRAINTOPERATOR_H
+#define IGLOO_CONTRAINTOPERATOR_H
+
+#include "invalidexpressionexception.h"
+
+namespace snowhouse {
+
+ struct ConstraintOperator
+ {
+#if __cplusplus > 199711L
+#else
+ virtual ~ConstraintOperator() {}
+#endif
+
+ virtual void PerformOperation(ResultStack& result) = 0;
+ virtual int Precedence() const = 0;
+
+ template <typename ConstraintListType, typename ActualType>
+ static bool EvaluateElementAgainstRestOfExpression(ConstraintListType& list, const ActualType& actual)
+ {
+ ResultStack innerResult;
+ OperatorStack innerOperators;
+
+ EvaluateConstraintList(list.m_tail, innerResult, innerOperators, actual);
+ EvaluateAllOperatorsOnStack(innerOperators, innerResult);
+
+ if(innerResult.empty())
+ {
+ throw InvalidExpressionException("The expression after \"" + snowhouse::Stringize(list.m_head) + "\" operator does not yield any result");
+ }
+
+ return innerResult.top();
+ }
+
+ static void EvaluateOperatorsWithLessOrEqualPrecedence(const ConstraintOperator& op, OperatorStack& operators, ResultStack& result)
+ {
+ while(!operators.empty())
+ {
+ ConstraintOperator* op_from_stack = operators.top();
+
+ if(op_from_stack->Precedence() > op.Precedence())
+ {
+ break;
+ }
+
+ op_from_stack->PerformOperation(result);
+ operators.pop();
+ }
+ }
+
+ static void EvaluateAllOperatorsOnStack(OperatorStack& operators, ResultStack& result)
+ {
+ while(!operators.empty())
+ {
+ ConstraintOperator* op = operators.top();
+ op->PerformOperation(result);
+ operators.pop();
+ }
+ }
+ };
+
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/invalidexpressionexception.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/invalidexpressionexception.h
new file mode 100644
index 00000000..404341f1
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/invalidexpressionexception.h
@@ -0,0 +1,28 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_INVALUDEXPRESSIONEXCEPTION_H
+#define IGLOO_INVALUDEXPRESSIONEXCEPTION_H
+
+namespace snowhouse {
+
+ struct InvalidExpressionException
+ {
+ InvalidExpressionException(const std::string& message) : m_message(message)
+ {
+ }
+
+ const std::string& Message() const
+ {
+ return m_message;
+ }
+
+ std::string m_message;
+ };
+
+}
+
+#endif // IGLOO_INVALUDEXPRESSIONEXCEPTION_H
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/notoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/notoperator.h
new file mode 100644
index 00000000..709c8413
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/notoperator.h
@@ -0,0 +1,53 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_NOTOPERATOR_H
+#define IGLOO_NOTOPERATOR_H
+
+namespace snowhouse {
+
+ struct NotOperator : public ConstraintOperator
+ {
+ template <typename ConstraintListType, typename ActualType>
+ void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ EvaluateOperatorsWithLessOrEqualPrecedence(*this, operators, result);
+
+ operators.push(this);
+
+ EvaluateConstraintList(list.m_tail, result, operators, actual);
+ }
+
+ void PerformOperation(ResultStack& result)
+ {
+ if(result.size() < 1)
+ {
+ throw InvalidExpressionException("The expression contains a not operator without any operand");
+ }
+
+ bool right = result.top();
+ result.pop();
+
+ result.push(!right);
+ }
+
+ int Precedence() const
+ {
+ return 2;
+ }
+ };
+
+ template<>
+ struct Stringizer<NotOperator>
+ {
+ static std::string ToString(const NotOperator&)
+ {
+ return "not";
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/oroperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/oroperator.h
new file mode 100644
index 00000000..5a307ff6
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/oroperator.h
@@ -0,0 +1,55 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_OROPERATOR_H
+#define IGLOO_OROPERATOR_H
+
+namespace snowhouse {
+
+ struct OrOperator : public ConstraintOperator
+ {
+ template <typename ConstraintListType, typename ActualType>
+ void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual)
+ {
+ EvaluateOperatorsWithLessOrEqualPrecedence(*this, operators, result);
+
+ operators.push(this);
+
+ EvaluateConstraintList(list.m_tail, result, operators, actual);
+ }
+
+ void PerformOperation(ResultStack& result)
+ {
+ if(result.size() < 2)
+ {
+ throw InvalidExpressionException("The expression contains an or operator with too few operands");
+ }
+
+ bool right = result.top();
+ result.pop();
+ bool left = result.top();
+ result.pop();
+
+ result.push(left || right);
+ }
+
+ int Precedence() const
+ {
+ return 4;
+ }
+ };
+
+ template<>
+ struct Stringizer<OrOperator>
+ {
+ static std::string ToString(const OrOperator&)
+ {
+ return "or";
+ }
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/snowhouse.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/snowhouse.h
new file mode 100644
index 00000000..38214aa7
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/snowhouse.h
@@ -0,0 +1,33 @@
+#ifndef _SNOWHOUSE_H_JK_2013_06_28
+#define _SNOWHOUSE_H_JK_2013_06_28
+
+#define SNOWHOUSE_VERSION "2.1.0"
+
+#if __cplusplus > 199711L
+#ifdef _MSC_VER
+// Visual Studio (including 2013) does not support the noexcept keyword
+#define _ALLOW_KEYWORD_MACROS
+#define noexcept
+#endif
+#endif
+
+
+#include <iostream>
+#include <map>
+#include <vector>
+#include <sstream>
+#include <stack>
+#include <list>
+#include <memory>
+#include <algorithm>
+
+#include "stringize.h"
+#include "constraints/constraints.h"
+#include "fluent/fluent.h"
+#include "assertionexception.h"
+#include "assert.h"
+#include "assertmacro.h"
+#include "exceptions.h"
+
+#endif
+
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringize.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringize.h
new file mode 100644
index 00000000..42249f57
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringize.h
@@ -0,0 +1,104 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_STRINGIZE_H
+#define IGLOO_STRINGIZE_H
+
+#include <cstddef>
+
+namespace snowhouse {
+ namespace detail {
+
+ // This type soaks up any implicit conversions and makes the following operator<<
+ // less preferred than any other such operator found via ADL.
+ struct any
+ {
+ // Conversion constructor for any type.
+ template <class T>
+ any(T const&);
+ };
+
+ // A tag type returned by operator<< for the any struct in this namespace
+ // when T does not support <<.
+ struct tag {};
+
+ // Fallback operator<< for types T that don't support <<.
+ tag operator<<(std::ostream&, any const&);
+
+ // Two overloads to distinguish whether T supports a certain operator expression.
+ // The first overload returns a reference to a two-element character array and is chosen if
+ // T does not support the expression, such as <<, whereas the second overload returns a char
+ // directly and is chosen if T supports the expression. So using sizeof(check(<expression>))
+ // returns 2 for the first overload and 1 for the second overload.
+ typedef char yes;
+ typedef char (&no)[2];
+
+ no check(tag);
+
+ template <class T>
+ yes check(T const&);
+
+ template <class T>
+ struct is_output_streamable
+ {
+ static const T& x;
+ static const bool value = sizeof(check(std::cout << x)) == sizeof(yes);
+ };
+
+ template<typename T, bool type_is_streamable>
+ struct DefaultStringizer
+ {
+ static std::string ToString(const T& value)
+ {
+ std::ostringstream buf;
+ buf << value;
+ return buf.str();
+ }
+ };
+
+ template<typename T>
+ struct DefaultStringizer<T, false>
+ {
+ static std::string ToString(const T&)
+ {
+ return "[unsupported type]";
+ }
+ };
+ }
+
+ template<typename T>
+ struct Stringizer;
+
+ template<typename T>
+ std::string Stringize(const T& value)
+ {
+ return Stringizer<T>::ToString(value);
+ }
+
+ // NOTE: Specialize snowhouse::Stringizer to customize assertion messages
+ template<typename T>
+ struct Stringizer
+ {
+ static std::string ToString(const T& value)
+ {
+ return detail::DefaultStringizer< T, detail::is_output_streamable<T>::value >::ToString(value);
+ }
+ };
+
+#if __cplusplus > 199711L
+ // We need this because nullptr_t has ambiguous overloads of operator<< in the standard library.
+ template<>
+ struct Stringizer<std::nullptr_t>
+ {
+ static std::string ToString(std::nullptr_t)
+ {
+ return "nullptr";
+ }
+ };
+#endif
+}
+
+#endif
diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringizers.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringizers.h
new file mode 100644
index 00000000..79a416de
--- /dev/null
+++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringizers.h
@@ -0,0 +1,60 @@
+
+// Copyright Joakim Karlsson & Kim Gräsman 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef IGLOO_STRINGIZERS_H
+#define IGLOO_STRINGIZERS_H
+
+namespace snowhouse
+{
+
+ namespace detail
+ {
+
+ template<typename Container>
+ struct SequentialContainerStringizer
+ {
+ static std::string
+ ToString(const Container& cont)
+ {
+ std::ostringstream stm;
+ typedef typename Container::const_iterator Iterator;
+
+ stm << "[ ";
+ for (Iterator it = cont.begin(); it != cont.end();)
+ {
+ stm << snowhouse::Stringize(*it);
+
+ if (++it != cont.end())
+ {
+ stm << ", ";
+ }
+ }
+ stm << " ]";
+ return stm.str();
+ }
+ };
+ }
+
+ template<typename T>
+ struct Stringizer<std::vector<T> > : detail::SequentialContainerStringizer<
+ std::vector<T> >
+ {
+ };
+
+ template<typename T>
+ struct Stringizer<std::deque<T> > : detail::SequentialContainerStringizer<
+ std::deque<T> >
+ {
+ };
+
+ template<typename T>
+ struct Stringizer<std::list<T> > : detail::SequentialContainerStringizer<
+ std::list<T> >
+ {
+ };
+}
+
+#endif
diff --git a/vendor/bandit/bandit/bandit.h b/vendor/bandit/bandit/bandit.h
new file mode 100644
index 00000000..c9caeda9
--- /dev/null
+++ b/vendor/bandit/bandit/bandit.h
@@ -0,0 +1,42 @@
+#ifndef BANDIT_BANDIT_H
+#define BANDIT_BANDIT_H
+
+#ifdef _MSC_VER
+// Visual Studio (including 2013) does not support the noexcept keyword
+#define _ALLOW_KEYWORD_MACROS
+#define noexcept
+#endif
+
+#include <cassert>
+#include <functional>
+#include <iostream>
+#include <list>
+#include <deque>
+#include <stdexcept>
+
+#define BANDIT_VERSION "2.0.0"
+
+namespace bandit { namespace detail {
+ typedef std::function<void ()> voidfunc_t;
+}}
+
+#include <bandit/assertion_frameworks/snowhouse/snowhouse/snowhouse.h>
+using namespace snowhouse;
+
+#include <bandit/assertion_frameworks/matchers/matchers.h>
+
+#include <bandit/external/optionparser.h>
+#include <bandit/options.h>
+#include <bandit/test_run_error.h>
+#include <bandit/registration/registration.h>
+#include <bandit/assertion_exception.h>
+#include <bandit/failure_formatters/failure_formatters.h>
+#include <bandit/adapters/adapters.h>
+#include <bandit/listener.h>
+#include <bandit/reporters/reporters.h>
+#include <bandit/context.h>
+#include <bandit/run_policies/run_policies.h>
+#include <bandit/grammar.h>
+#include <bandit/runner.h>
+
+#endif
diff --git a/vendor/bandit/bandit/context.h b/vendor/bandit/bandit/context.h
new file mode 100644
index 00000000..71194253
--- /dev/null
+++ b/vendor/bandit/bandit/context.h
@@ -0,0 +1,97 @@
+#ifndef BANDIT_CONTEXT_H
+#define BANDIT_CONTEXT_H
+
+namespace bandit {
+ namespace detail {
+
+ class context
+ {
+ public:
+ virtual ~context() {}
+ virtual const std::string& name() = 0;
+ virtual void execution_is_starting() = 0;
+ virtual void register_before_each(voidfunc_t func) = 0;
+ virtual void register_after_each(voidfunc_t func) = 0;
+ virtual void run_before_eaches() = 0;
+ virtual void run_after_eaches() = 0;
+ virtual bool hard_skip() = 0;
+ };
+
+ class bandit_context : public context
+ {
+ public:
+ bandit_context(const char* desc, bool hard_skip_a)
+ : desc_(desc), hard_skip_(hard_skip_a), is_executing_(false)
+ {}
+
+ const std::string& name()
+ {
+ return desc_;
+ }
+
+ void execution_is_starting()
+ {
+ is_executing_ = true;
+ }
+
+ void register_before_each(voidfunc_t func)
+ {
+ if(is_executing_)
+ {
+ throw test_run_error("before_each was called after 'describe' or 'it'");
+ }
+
+ before_eaches_.push_back(func);
+ }
+
+ void register_after_each(voidfunc_t func)
+ {
+ if(is_executing_)
+ {
+ throw test_run_error("after_each was called after 'describe' or 'it'");
+ }
+
+ after_eaches_.push_back(func);
+ }
+
+ void run_before_eaches()
+ {
+ run_all(before_eaches_);
+ }
+
+ void run_after_eaches()
+ {
+ run_all(after_eaches_);
+ }
+
+ bool hard_skip()
+ {
+ return hard_skip_;
+ }
+
+ private:
+ void run_all(const std::list<voidfunc_t>& funcs)
+ {
+ auto call_func = [](voidfunc_t f){ f(); };
+
+ for_each(funcs.begin(), funcs.end(), call_func);
+ }
+
+ private:
+ std::string desc_;
+ bool hard_skip_;
+ bool is_executing_;
+ std::list<voidfunc_t> before_eaches_;
+ std::list<voidfunc_t> after_eaches_;
+ };
+ typedef std::deque<context*> contextstack_t;
+
+ inline contextstack_t& context_stack()
+ {
+ static contextstack_t contexts;
+ return contexts;
+ }
+ }
+}
+
+#endif
diff --git a/vendor/bandit/bandit/external/optionparser.h b/vendor/bandit/bandit/external/optionparser.h
new file mode 100644
index 00000000..ffeaac66
--- /dev/null
+++ b/vendor/bandit/bandit/external/optionparser.h
@@ -0,0 +1,2825 @@
+/*
+ * The Lean Mean C++ Option Parser
+ *
+ * Copyright (C) 2012 Matthias S. Benkmann
+ *
+ * The "Software" in the following 2 paragraphs refers to this file containing
+ * the code to The Lean Mean C++ Option Parser.
+ * The "Software" does NOT refer to any other files which you
+ * may have received alongside this file (e.g. as part of a larger project that
+ * incorporates The Lean Mean C++ Option Parser).
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this 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
+ * AUTHORS OR COPYRIGHT HOLDERS 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.
+ */
+
+/*
+ * NOTE: It is recommended that you read the processed HTML doxygen documentation
+ * rather than this source. If you don't know doxygen, it's like javadoc for C++.
+ * If you don't want to install doxygen you can find a copy of the processed
+ * documentation at
+ *
+ * http://optionparser.sourceforge.net/
+ *
+ */
+
+/**
+ * @file
+ *
+ * @brief This is the only file required to use The Lean Mean C++ Option Parser.
+ * Just \#include it and you're set.
+ *
+ * The Lean Mean C++ Option Parser handles the program's command line arguments
+ * (argc, argv).
+ * It supports the short and long option formats of getopt(), getopt_long()
+ * and getopt_long_only() but has a more convenient interface.
+ * The following features set it apart from other option parsers:
+ *
+ * @par Highlights:
+ * <ul style="padding-left:1em;margin-left:0">
+ * <li> It is a header-only library. Just <code>\#include "optionparser.h"</code> and you're set.
+ * <li> It is freestanding. There are no dependencies whatsoever, not even the
+ * C or C++ standard library.
+ * <li> It has a usage message formatter that supports column alignment and
+ * line wrapping. This aids localization because it adapts to
+ * translated strings that are shorter or longer (even if they contain
+ * Asian wide characters).
+ * <li> Unlike getopt() and derivatives it doesn't force you to loop through
+ * options sequentially. Instead you can access options directly like this:
+ * <ul style="margin-top:.5em">
+ * <li> Test for presence of a switch in the argument vector:
+ * @code if ( options[QUIET] ) ... @endcode
+ * <li> Evaluate --enable-foo/--disable-foo pair where the last one used wins:
+ * @code if ( options[FOO].last()->type() == DISABLE ) ... @endcode
+ * <li> Cumulative option (-v verbose, -vv more verbose, -vvv even more verbose):
+ * @code int verbosity = options[VERBOSE].count(); @endcode
+ * <li> Iterate over all --file=&lt;fname> arguments:
+ * @code for (Option* opt = options[FILE]; opt; opt = opt->next())
+ * fname = opt->arg; ... @endcode
+ * <li> If you really want to, you can still process all arguments in order:
+ * @code
+ * for (int i = 0; i < p.optionsCount(); ++i) {
+ * Option& opt = buffer[i];
+ * switch(opt.index()) {
+ * case HELP: ...
+ * case VERBOSE: ...
+ * case FILE: fname = opt.arg; ...
+ * case UNKNOWN: ...
+ * @endcode
+ * </ul>
+ * </ul> @n
+ * Despite these features the code size remains tiny.
+ * It is smaller than <a href="http://uclibc.org">uClibc</a>'s GNU getopt() and just a
+ * couple 100 bytes larger than uClibc's SUSv3 getopt(). @n
+ * (This does not include the usage formatter, of course. But you don't have to use that.)
+ *
+ * @par Download:
+ * Tarball with examples and test programs:
+ * <a style="font-size:larger;font-weight:bold" href="http://sourceforge.net/projects/optionparser/files/optionparser-1.3.tar.gz/download">optionparser-1.3.tar.gz</a> @n
+ * Just the header (this is all you really need):
+ * <a style="font-size:larger;font-weight:bold" href="http://optionparser.sourceforge.net/optionparser.h">optionparser.h</a>
+ *
+ * @par Changelog:
+ * <b>Version 1.3:</b> Compatible with Microsoft Visual C++. @n
+ * <b>Version 1.2:</b> Added @ref option::Option::namelen "Option::namelen" and removed the extraction
+ * of short option characters into a special buffer. @n
+ * Changed @ref option::Arg::Optional "Arg::Optional" to accept arguments if they are attached
+ * rather than separate. This is what GNU getopt() does and how POSIX recommends
+ * utilities should interpret their arguments.@n
+ * <b>Version 1.1:</b> Optional mode with argument reordering as done by GNU getopt(), so that
+ * options and non-options can be mixed. See
+ * @ref option::Parser::parse() "Parser::parse()".
+ *
+ * @par Feedback:
+ * Send questions, bug reports, feature requests etc. to: <tt><b>optionparser-feedback<span id="antispam">&nbsp;(a)&nbsp;</span>lists.sourceforge.net</b></tt>
+ * @htmlonly <script type="text/javascript">document.getElementById("antispam").innerHTML="@"</script> @endhtmlonly
+ *
+ *
+ * @par Example program:
+ * (Note: @c option::* identifiers are links that take you to their documentation.)
+ * @code
+ * #include <iostream>
+ * #include "optionparser.h"
+ *
+ * enum optionIndex { UNKNOWN, HELP, PLUS };
+ * const option::Descriptor usage[] =
+ * {
+ * {UNKNOWN, 0,"" , "" ,option::Arg::None, "USAGE: example [options]\n\n"
+ * "Options:" },
+ * {HELP, 0,"" , "help",option::Arg::None, " --help \tPrint usage and exit." },
+ * {PLUS, 0,"p", "plus",option::Arg::None, " --plus, -p \tIncrement count." },
+ * {UNKNOWN, 0,"" , "" ,option::Arg::None, "\nExamples:\n"
+ * " example --unknown -- --this_is_no_option\n"
+ * " example -unk --plus -ppp file1 file2\n" },
+ * {0,0,0,0,0,0}
+ * };
+ *
+ * int main(int argc, char* argv[])
+ * {
+ * argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present
+ * option::Stats stats(usage, argc, argv);
+ * option::Option options[stats.options_max], buffer[stats.buffer_max];
+ * option::Parser parse(usage, argc, argv, options, buffer);
+ *
+ * if (parse.error())
+ * return 1;
+ *
+ * if (options[HELP] || argc == 0) {
+ * option::printUsage(std::cout, usage);
+ * return 0;
+ * }
+ *
+ * std::cout << "--plus count: " <<
+ * options[PLUS].count() << "\n";
+ *
+ * for (option::Option* opt = options[UNKNOWN]; opt; opt = opt->next())
+ * std::cout << "Unknown option: " << opt->name << "\n";
+ *
+ * for (int i = 0; i < parse.nonOptionsCount(); ++i)
+ * std::cout << "Non-option #" << i << ": " << parse.nonOption(i) << "\n";
+ * }
+ * @endcode
+ *
+ * @par Option syntax:
+ * @li The Lean Mean C++ Option Parser follows POSIX <code>getopt()</code> conventions and supports
+ * GNU-style <code>getopt_long()</code> long options as well as Perl-style single-minus
+ * long options (<code>getopt_long_only()</code>).
+ * @li short options have the format @c -X where @c X is any character that fits in a char.
+ * @li short options can be grouped, i.e. <code>-X -Y</code> is equivalent to @c -XY.
+ * @li a short option may take an argument either separate (<code>-X foo</code>) or
+ * attached (@c -Xfoo). You can make the parser accept the additional format @c -X=foo by
+ * registering @c X as a long option (in addition to being a short option) and
+ * enabling single-minus long options.
+ * @li an argument-taking short option may be grouped if it is the last in the group, e.g.
+ * @c -ABCXfoo or <code> -ABCX foo </code> (@c foo is the argument to the @c -X option).
+ * @li a lone minus character @c '-' is not treated as an option. It is customarily used where
+ * a file name is expected to refer to stdin or stdout.
+ * @li long options have the format @c --option-name.
+ * @li the option-name of a long option can be anything and include any characters.
+ * Even @c = characters will work, but don't do that.
+ * @li [optional] long options may be abbreviated as long as the abbreviation is unambiguous.
+ * You can set a minimum length for abbreviations.
+ * @li [optional] long options may begin with a single minus. The double minus form is always
+ * accepted, too.
+ * @li a long option may take an argument either separate (<code> --option arg </code>) or
+ * attached (<code> --option=arg </code>). In the attached form the equals sign is mandatory.
+ * @li an empty string can be passed as an attached long option argument: <code> --option-name= </code>.
+ * Note the distinction between an empty string as argument and no argument at all.
+ * @li an empty string is permitted as separate argument to both long and short options.
+ * @li Arguments to both short and long options may start with a @c '-' character. E.g.
+ * <code> -X-X </code>, <code>-X -X</code> or <code> --long-X=-X </code>. If @c -X
+ * and @c --long-X take an argument, that argument will be @c "-X" in all 3 cases.
+ * @li If using the built-in @ref option::Arg::Optional "Arg::Optional", optional arguments must
+ * be attached.
+ * @li the special option @c -- (i.e. without a name) terminates the list of
+ * options. Everything that follows is a non-option argument, even if it starts with
+ * a @c '-' character. The @c -- itself will not appear in the parse results.
+ * @li the first argument that doesn't start with @c '-' or @c '--' and does not belong to
+ * a preceding argument-taking option, will terminate the option list and is the
+ * first non-option argument. All following command line arguments are treated as
+ * non-option arguments, even if they start with @c '-' . @n
+ * NOTE: This behaviour is mandated by POSIX, but GNU getopt() only honours this if it is
+ * explicitly requested (e.g. by setting POSIXLY_CORRECT). @n
+ * You can enable the GNU behaviour by passing @c true as first argument to
+ * e.g. @ref option::Parser::parse() "Parser::parse()".
+ * @li Arguments that look like options (i.e. @c '-' followed by at least 1 character) but
+ * aren't, are NOT treated as non-option arguments. They are treated as unknown options and
+ * are collected into a list of unknown options for error reporting. @n
+ * This means that in order to pass a first non-option
+ * argument beginning with the minus character it is required to use the
+ * @c -- special option, e.g.
+ * @code
+ * program -x -- --strange-filename
+ * @endcode
+ * In this example, @c --strange-filename is a non-option argument. If the @c --
+ * were omitted, it would be treated as an unknown option. @n
+ * See @ref option::Descriptor::longopt for information on how to collect unknown options.
+ *
+ */
+
+#ifndef OPTIONPARSER_H_
+#define OPTIONPARSER_H_
+
+/** @brief The namespace of The Lean Mean C++ Option Parser. */
+namespace option
+{
+
+#ifdef _MSC_VER
+#include <intrin.h>
+#pragma intrinsic(_BitScanReverse)
+struct MSC_Builtin_CLZ
+{
+ static int builtin_clz(unsigned x)
+ {
+ unsigned long index;
+ _BitScanReverse(&index, x);
+ return 32-index; // int is always 32bit on Windows, even for target x64
+ }
+};
+#define __builtin_clz(x) MSC_Builtin_CLZ::builtin_clz(x)
+
+#pragma warning( push )
+#pragma warning( disable: 4510 )
+#pragma warning( disable: 4512 )
+#pragma warning( disable: 4610 )
+#pragma warning( disable: 4127 )
+#endif
+
+class Option;
+
+/**
+ * @brief Possible results when checking if an argument is valid for a certain option.
+ *
+ * In the case that no argument is provided for an option that takes an
+ * optional argument, return codes @c ARG_OK and @c ARG_IGNORE are equivalent.
+ */
+enum ArgStatus
+{
+ //! The option does not take an argument.
+ ARG_NONE,
+ //! The argument is acceptable for the option.
+ ARG_OK,
+ //! The argument is not acceptable but that's non-fatal because the option's argument is optional.
+ ARG_IGNORE,
+ //! The argument is not acceptable and that's fatal.
+ ARG_ILLEGAL
+};
+
+/**
+ * @brief Signature of functions that check if an argument is valid for a certain type of option.
+ *
+ * Every Option has such a function assigned in its Descriptor.
+ * @code
+ * Descriptor usage[] = { {UNKNOWN, 0, "", "", Arg::None, ""}, ... };
+ * @endcode
+ *
+ * A CheckArg function has the following signature:
+ * @code ArgStatus CheckArg(const Option& option, bool msg); @endcode
+ *
+ * It is used to check if a potential argument would be acceptable for the option.
+ * It will even be called if there is no argument. In that case @c option.arg will be @c NULL.
+ *
+ * If @c msg is @c true and the function determines that an argument is not acceptable and
+ * that this is a fatal error, it should output a message to the user before
+ * returning @ref ARG_ILLEGAL. If @c msg is @c false the function should remain silent (or you
+ * will get duplicate messages).
+ *
+ * See @ref ArgStatus for the meaning of the return values.
+ *
+ * While you can provide your own functions,
+ * often the following pre-defined checks (which never return @ref ARG_ILLEGAL) will suffice:
+ *
+ * @li @c Arg::None @copybrief Arg::None
+ * @li @c Arg::Optional @copybrief Arg::Optional
+ *
+ */
+typedef ArgStatus (*CheckArg)(const Option& option, bool msg);
+
+/**
+ * @brief Describes an option, its help text (usage) and how it should be parsed.
+ *
+ * The main input when constructing an option::Parser is an array of Descriptors.
+
+ * @par Example:
+ * @code
+ * enum OptionIndex {CREATE, ...};
+ * enum OptionType {DISABLE, ENABLE, OTHER};
+ *
+ * const option::Descriptor usage[] = {
+ * { CREATE, // index
+ * OTHER, // type
+ * "c", // shortopt
+ * "create", // longopt
+ * Arg::None, // check_arg
+ * "--create Tells the program to create something." // help
+ * }
+ * , ...
+ * };
+ * @endcode
+ */
+struct Descriptor
+{
+ /**
+ * @brief Index of this option's linked list in the array filled in by the parser.
+ *
+ * Command line options whose Descriptors have the same index will end up in the same
+ * linked list in the order in which they appear on the command line. If you have
+ * multiple long option aliases that refer to the same option, give their descriptors
+ * the same @c index.
+ *
+ * If you have options that mean exactly opposite things
+ * (e.g. @c --enable-foo and @c --disable-foo ), you should also give them the same
+ * @c index, but distinguish them through different values for @ref type.
+ * That way they end up in the same list and you can just take the last element of the
+ * list and use its type. This way you get the usual behaviour where switches later
+ * on the command line override earlier ones without having to code it manually.
+ *
+ * @par Tip:
+ * Use an enum rather than plain ints for better readability, as shown in the example
+ * at Descriptor.
+ */
+ const unsigned index;
+
+ /**
+ * @brief Used to distinguish between options with the same @ref index.
+ * See @ref index for details.
+ *
+ * It is recommended that you use an enum rather than a plain int to make your
+ * code more readable.
+ */
+ const int type;
+
+ /**
+ * @brief Each char in this string will be accepted as a short option character.
+ *
+ * The string must not include the minus character @c '-' or you'll get undefined
+ * behaviour.
+ *
+ * If this Descriptor should not have short option characters, use the empty
+ * string "". NULL is not permitted here!
+ *
+ * See @ref longopt for more information.
+ */
+ const char* const shortopt;
+
+ /**
+ * @brief The long option name (without the leading @c -- ).
+ *
+ * If this Descriptor should not have a long option name, use the empty
+ * string "". NULL is not permitted here!
+ *
+ * While @ref shortopt allows multiple short option characters, each
+ * Descriptor can have only a single long option name. If you have multiple
+ * long option names referring to the same option use separate Descriptors
+ * that have the same @ref index and @ref type. You may repeat
+ * short option characters in such an alias Descriptor but there's no need to.
+ *
+ * @par Dummy Descriptors:
+ * You can use dummy Descriptors with an
+ * empty string for both @ref shortopt and @ref longopt to add text to
+ * the usage that is not related to a specific option. See @ref help.
+ * The first dummy Descriptor will be used for unknown options (see below).
+ *
+ * @par Unknown Option Descriptor:
+ * The first dummy Descriptor in the list of Descriptors,
+ * whose @ref shortopt and @ref longopt are both the empty string, will be used
+ * as the Descriptor for unknown options. An unknown option is a string in
+ * the argument vector that is not a lone minus @c '-' but starts with a minus
+ * character and does not match any Descriptor's @ref shortopt or @ref longopt. @n
+ * Note that the dummy descriptor's @ref check_arg function @e will be called and
+ * its return value will be evaluated as usual. I.e. if it returns @ref ARG_ILLEGAL
+ * the parsing will be aborted with <code>Parser::error()==true</code>. @n
+ * if @c check_arg does not return @ref ARG_ILLEGAL the descriptor's
+ * @ref index @e will be used to pick the linked list into which
+ * to put the unknown option. @n
+ * If there is no dummy descriptor, unknown options will be dropped silently.
+ *
+ */
+ const char* const longopt;
+
+ /**
+ * @brief For each option that matches @ref shortopt or @ref longopt this function
+ * will be called to check a potential argument to the option.
+ *
+ * This function will be called even if there is no potential argument. In that case
+ * it will be passed @c NULL as @c arg parameter. Do not confuse this with the empty
+ * string.
+ *
+ * See @ref CheckArg for more information.
+ */
+ const CheckArg check_arg;
+
+ /**
+ * @brief The usage text associated with the options in this Descriptor.
+ *
+ * You can use option::printUsage() to format your usage message based on
+ * the @c help texts. You can use dummy Descriptors where
+ * @ref shortopt and @ref longopt are both the empty string to add text to
+ * the usage that is not related to a specific option.
+ *
+ * See option::printUsage() for special formatting characters you can use in
+ * @c help to get a column layout.
+ *
+ * @attention
+ * Must be UTF-8-encoded. If your compiler supports C++11 you can use the "u8"
+ * prefix to make sure string literals are properly encoded.
+ */
+ const char* help;
+};
+
+/**
+ * @brief A parsed option from the command line together with its argument if it has one.
+ *
+ * The Parser chains all parsed options with the same Descriptor::index together
+ * to form a linked list. This allows you to easily implement all of the common ways
+ * of handling repeated options and enable/disable pairs.
+ *
+ * @li Test for presence of a switch in the argument vector:
+ * @code if ( options[QUIET] ) ... @endcode
+ * @li Evaluate --enable-foo/--disable-foo pair where the last one used wins:
+ * @code if ( options[FOO].last()->type() == DISABLE ) ... @endcode
+ * @li Cumulative option (-v verbose, -vv more verbose, -vvv even more verbose):
+ * @code int verbosity = options[VERBOSE].count(); @endcode
+ * @li Iterate over all --file=&lt;fname> arguments:
+ * @code for (Option* opt = options[FILE]; opt; opt = opt->next())
+ * fname = opt->arg; ... @endcode
+ */
+class Option
+{
+ Option* next_;
+ Option* prev_;
+public:
+ /**
+ * @brief Pointer to this Option's Descriptor.
+ *
+ * Remember that the first dummy descriptor (see @ref Descriptor::longopt) is used
+ * for unknown options.
+ *
+ * @attention
+ * @c desc==NULL signals that this Option is unused. This is the default state of
+ * elements in the result array. You don't need to test @c desc explicitly. You
+ * can simply write something like this:
+ * @code
+ * if (options[CREATE])
+ * {
+ * ...
+ * }
+ * @endcode
+ * This works because of <code> operator const Option*() </code>.
+ */
+ const Descriptor* desc;
+
+ /**
+ * @brief The name of the option as used on the command line.
+ *
+ * The main purpose of this string is to be presented to the user in messages.
+ *
+ * In the case of a long option, this is the actual @c argv pointer, i.e. the first
+ * character is a '-'. In the case of a short option this points to the option
+ * character within the @c argv string.
+ *
+ * Note that in the case of a short option group or an attached option argument, this
+ * string will contain additional characters following the actual name. Use @ref namelen
+ * to filter out the actual option name only.
+ *
+ */
+ const char* name;
+
+ /**
+ * @brief Pointer to this Option's argument (if any).
+ *
+ * NULL if this option has no argument. Do not confuse this with the empty string which
+ * is a valid argument.
+ */
+ const char* arg;
+
+ /**
+ * @brief The length of the option @ref name.
+ *
+ * Because @ref name points into the actual @c argv string, the option name may be
+ * followed by more characters (e.g. other short options in the same short option group).
+ * This value is the number of bytes (not characters!) that are part of the actual name.
+ *
+ * For a short option, this length is always 1. For a long option this length is always
+ * at least 2 if single minus long options are permitted and at least 3 if they are disabled.
+ *
+ * @note
+ * In the pathological case of a minus within a short option group (e.g. @c -xf-z), this
+ * length is incorrect, because this case will be misinterpreted as a long option and the
+ * name will therefore extend to the string's 0-terminator or a following '=" character
+ * if there is one. This is irrelevant for most uses of @ref name and @c namelen. If you
+ * really need to distinguish the case of a long and a short option, compare @ref name to
+ * the @c argv pointers. A long option's @c name is always identical to one of them,
+ * whereas a short option's is never.
+ */
+ int namelen;
+
+ /**
+ * @brief Returns Descriptor::type of this Option's Descriptor, or 0 if this Option
+ * is invalid (unused).
+ *
+ * Because this method (and last(), too) can be used even on unused Options with desc==0, you can (provided
+ * you arrange your types properly) switch on type() without testing validity first.
+ * @code
+ * enum OptionType { UNUSED=0, DISABLED=0, ENABLED=1 };
+ * enum OptionIndex { FOO };
+ * const Descriptor usage[] = {
+ * { FOO, ENABLED, "", "enable-foo", Arg::None, 0 },
+ * { FOO, DISABLED, "", "disable-foo", Arg::None, 0 },
+ * { 0, 0, 0, 0, 0, 0 } };
+ * ...
+ * switch(options[FOO].last()->type()) // no validity check required!
+ * {
+ * case ENABLED: ...
+ * case DISABLED: ... // UNUSED==DISABLED !
+ * }
+ * @endcode
+ */
+ int type() const
+ {
+ return desc == 0 ? 0 : desc->type;
+ }
+
+ /**
+ * @brief Returns Descriptor::index of this Option's Descriptor, or -1 if this Option
+ * is invalid (unused).
+ */
+ int index() const
+ {
+ return desc == 0 ? -1 : desc->index;
+ }
+
+ /**
+ * @brief Returns the number of times this Option (or others with the same Descriptor::index)
+ * occurs in the argument vector.
+ *
+ * This corresponds to the number of elements in the linked list this Option is part of.
+ * It doesn't matter on which element you call count(). The return value is always the same.
+ *
+ * Use this to implement cumulative options, such as -v, -vv, -vvv for
+ * different verbosity levels.
+ *
+ * Returns 0 when called for an unused/invalid option.
+ */
+ int count()
+ {
+ int c = (desc == 0 ? 0 : 1);
+ Option* p = first();
+ while (!p->isLast())
+ {
+ ++c;
+ p = p->next_;
+ };
+ return c;
+ }
+
+ /**
+ * @brief Returns true iff this is the first element of the linked list.
+ *
+ * The first element in the linked list is the first option on the command line
+ * that has the respective Descriptor::index value.
+ *
+ * Returns true for an unused/invalid option.
+ */
+ bool isFirst() const
+ {
+ return isTagged(prev_);
+ }
+
+ /**
+ * @brief Returns true iff this is the last element of the linked list.
+ *
+ * The last element in the linked list is the last option on the command line
+ * that has the respective Descriptor::index value.
+ *
+ * Returns true for an unused/invalid option.
+ */
+ bool isLast() const
+ {
+ return isTagged(next_);
+ }
+
+ /**
+ * @brief Returns a pointer to the first element of the linked list.
+ *
+ * Use this when you want the first occurrence of an option on the command line to
+ * take precedence. Note that this is not the way most programs handle options.
+ * You should probably be using last() instead.
+ *
+ * @note
+ * This method may be called on an unused/invalid option and will return a pointer to the
+ * option itself.
+ */
+ Option* first()
+ {
+ Option* p = this;
+ while (!p->isFirst())
+ p = p->prev_;
+ return p;
+ }
+
+ /**
+ * @brief Returns a pointer to the last element of the linked list.
+ *
+ * Use this when you want the last occurrence of an option on the command line to
+ * take precedence. This is the most common way of handling conflicting options.
+ *
+ * @note
+ * This method may be called on an unused/invalid option and will return a pointer to the
+ * option itself.
+ *
+ * @par Tip:
+ * If you have options with opposite meanings (e.g. @c --enable-foo and @c --disable-foo), you
+ * can assign them the same Descriptor::index to get them into the same list. Distinguish them by
+ * Descriptor::type and all you have to do is check <code> last()->type() </code> to get
+ * the state listed last on the command line.
+ */
+ Option* last()
+ {
+ return first()->prevwrap();
+ }
+
+ /**
+ * @brief Returns a pointer to the previous element of the linked list or NULL if
+ * called on first().
+ *
+ * If called on first() this method returns NULL. Otherwise it will return the
+ * option with the same Descriptor::index that precedes this option on the command
+ * line.
+ */
+ Option* prev()
+ {
+ return isFirst() ? 0 : prev_;
+ }
+
+ /**
+ * @brief Returns a pointer to the previous element of the linked list with wrap-around from
+ * first() to last().
+ *
+ * If called on first() this method returns last(). Otherwise it will return the
+ * option with the same Descriptor::index that precedes this option on the command
+ * line.
+ */
+ Option* prevwrap()
+ {
+ return untag(prev_);
+ }
+
+ /**
+ * @brief Returns a pointer to the next element of the linked list or NULL if called
+ * on last().
+ *
+ * If called on last() this method returns NULL. Otherwise it will return the
+ * option with the same Descriptor::index that follows this option on the command
+ * line.
+ */
+ Option* next()
+ {
+ return isLast() ? 0 : next_;
+ }
+
+ /**
+ * @brief Returns a pointer to the next element of the linked list with wrap-around from
+ * last() to first().
+ *
+ * If called on last() this method returns first(). Otherwise it will return the
+ * option with the same Descriptor::index that follows this option on the command
+ * line.
+ */
+ Option* nextwrap()
+ {
+ return untag(next_);
+ }
+
+ /**
+ * @brief Makes @c new_last the new last() by chaining it into the list after last().
+ *
+ * It doesn't matter which element you call append() on. The new element will always
+ * be appended to last().
+ *
+ * @attention
+ * @c new_last must not yet be part of a list, or that list will become corrupted, because
+ * this method does not unchain @c new_last from an existing list.
+ */
+ void append(Option* new_last)
+ {
+ Option* p = last();
+ Option* f = first();
+ p->next_ = new_last;
+ new_last->prev_ = p;
+ new_last->next_ = tag(f);
+ f->prev_ = tag(new_last);
+ }
+
+ /**
+ * @brief Casts from Option to const Option* but only if this Option is valid.
+ *
+ * If this Option is valid (i.e. @c desc!=NULL), returns this.
+ * Otherwise returns NULL. This allows testing an Option directly
+ * in an if-clause to see if it is used:
+ * @code
+ * if (options[CREATE])
+ * {
+ * ...
+ * }
+ * @endcode
+ * It also allows you to write loops like this:
+ * @code for (Option* opt = options[FILE]; opt; opt = opt->next())
+ * fname = opt->arg; ... @endcode
+ */
+ operator const Option*() const
+ {
+ return desc ? this : 0;
+ }
+
+ /**
+ * @brief Casts from Option to Option* but only if this Option is valid.
+ *
+ * If this Option is valid (i.e. @c desc!=NULL), returns this.
+ * Otherwise returns NULL. This allows testing an Option directly
+ * in an if-clause to see if it is used:
+ * @code
+ * if (options[CREATE])
+ * {
+ * ...
+ * }
+ * @endcode
+ * It also allows you to write loops like this:
+ * @code for (Option* opt = options[FILE]; opt; opt = opt->next())
+ * fname = opt->arg; ... @endcode
+ */
+ operator Option*()
+ {
+ return desc ? this : 0;
+ }
+
+ /**
+ * @brief Creates a new Option that is a one-element linked list and has NULL
+ * @ref desc, @ref name, @ref arg and @ref namelen.
+ */
+ Option() :
+ desc(0), name(0), arg(0), namelen(0)
+ {
+ prev_ = tag(this);
+ next_ = tag(this);
+ }
+
+ /**
+ * @brief Creates a new Option that is a one-element linked list and has the given
+ * values for @ref desc, @ref name and @ref arg.
+ *
+ * If @c name_ points at a character other than '-' it will be assumed to refer to a
+ * short option and @ref namelen will be set to 1. Otherwise the length will extend to
+ * the first '=' character or the string's 0-terminator.
+ */
+ Option(const Descriptor* desc_, const char* name_, const char* arg_)
+ {
+ init(desc_, name_, arg_);
+ }
+
+ /**
+ * @brief Makes @c *this a copy of @c orig except for the linked list pointers.
+ *
+ * After this operation @c *this will be a one-element linked list.
+ */
+ void operator=(const Option& orig)
+ {
+ init(orig.desc, orig.name, orig.arg);
+ }
+
+ /**
+ * @brief Makes @c *this a copy of @c orig except for the linked list pointers.
+ *
+ * After this operation @c *this will be a one-element linked list.
+ */
+ Option(const Option& orig)
+ {
+ init(orig.desc, orig.name, orig.arg);
+ }
+
+private:
+ /**
+ * @internal
+ * @brief Sets the fields of this Option to the given values (extracting @c name if necessary).
+ *
+ * If @c name_ points at a character other than '-' it will be assumed to refer to a
+ * short option and @ref namelen will be set to 1. Otherwise the length will extend to
+ * the first '=' character or the string's 0-terminator.
+ */
+ void init(const Descriptor* desc_, const char* name_, const char* arg_)
+ {
+ desc = desc_;
+ name = name_;
+ arg = arg_;
+ prev_ = tag(this);
+ next_ = tag(this);
+ namelen = 0;
+ if (name == 0)
+ return;
+ namelen = 1;
+ if (name[0] != '-')
+ return;
+ while (name[namelen] != 0 && name[namelen] != '=')
+ ++namelen;
+ }
+
+ static Option* tag(Option* ptr)
+ {
+ return (Option*) ((unsigned long long) ptr | 1);
+ }
+
+ static Option* untag(Option* ptr)
+ {
+ return (Option*) ((unsigned long long) ptr & ~1ull);
+ }
+
+ static bool isTagged(Option* ptr)
+ {
+ return ((unsigned long long) ptr & 1);
+ }
+};
+
+/**
+ * @brief Functions for checking the validity of option arguments.
+ *
+ * @copydetails CheckArg
+ *
+ * The following example code
+ * can serve as starting place for writing your own more complex CheckArg functions:
+ * @code
+ * struct Arg: public option::Arg
+ * {
+ * static void printError(const char* msg1, const option::Option& opt, const char* msg2)
+ * {
+ * fprintf(stderr, "ERROR: %s", msg1);
+ * fwrite(opt.name, opt.namelen, 1, stderr);
+ * fprintf(stderr, "%s", msg2);
+ * }
+ *
+ * static option::ArgStatus Unknown(const option::Option& option, bool msg)
+ * {
+ * if (msg) printError("Unknown option '", option, "'\n");
+ * return option::ARG_ILLEGAL;
+ * }
+ *
+ * static option::ArgStatus Required(const option::Option& option, bool msg)
+ * {
+ * if (option.arg != 0)
+ * return option::ARG_OK;
+ *
+ * if (msg) printError("Option '", option, "' requires an argument\n");
+ * return option::ARG_ILLEGAL;
+ * }
+ *
+ * static option::ArgStatus NonEmpty(const option::Option& option, bool msg)
+ * {
+ * if (option.arg != 0 && option.arg[0] != 0)
+ * return option::ARG_OK;
+ *
+ * if (msg) printError("Option '", option, "' requires a non-empty argument\n");
+ * return option::ARG_ILLEGAL;
+ * }
+ *
+ * static option::ArgStatus Numeric(const option::Option& option, bool msg)
+ * {
+ * char* endptr = 0;
+ * if (option.arg != 0 && strtol(option.arg, &endptr, 10)){};
+ * if (endptr != option.arg && *endptr == 0)
+ * return option::ARG_OK;
+ *
+ * if (msg) printError("Option '", option, "' requires a numeric argument\n");
+ * return option::ARG_ILLEGAL;
+ * }
+ * };
+ * @endcode
+ */
+struct Arg
+{
+ //! @brief For options that don't take an argument: Returns ARG_NONE.
+ static ArgStatus None(const Option&, bool)
+ {
+ return ARG_NONE;
+ }
+
+ //! @brief Returns ARG_OK if the argument is attached and ARG_IGNORE otherwise.
+ static ArgStatus Optional(const Option& option, bool)
+ {
+ if (option.arg && option.name[option.namelen] != 0)
+ return ARG_OK;
+ else
+ return ARG_IGNORE;
+ }
+};
+
+/**
+ * @brief Determines the minimum lengths of the buffer and options arrays used for Parser.
+ *
+ * Because Parser doesn't use dynamic memory its output arrays have to be pre-allocated.
+ * If you don't want to use fixed size arrays (which may turn out too small, causing
+ * command line arguments to be dropped), you can use Stats to determine the correct sizes.
+ * Stats work cumulative. You can first pass in your default options and then the real
+ * options and afterwards the counts will reflect the union.
+ */
+struct Stats
+{
+ /**
+ * @brief Number of elements needed for a @c buffer[] array to be used for
+ * @ref Parser::parse() "parsing" the same argument vectors that were fed
+ * into this Stats object.
+ *
+ * @note
+ * This number is always 1 greater than the actual number needed, to give
+ * you a sentinel element.
+ */
+ unsigned buffer_max;
+
+ /**
+ * @brief Number of elements needed for an @c options[] array to be used for
+ * @ref Parser::parse() "parsing" the same argument vectors that were fed
+ * into this Stats object.
+ *
+ * @note
+ * @li This number is always 1 greater than the actual number needed, to give
+ * you a sentinel element.
+ * @li This number depends only on the @c usage, not the argument vectors, because
+ * the @c options array needs exactly one slot for each possible Descriptor::index.
+ */
+ unsigned options_max;
+
+ /**
+ * @brief Creates a Stats object with counts set to 1 (for the sentinel element).
+ */
+ Stats() :
+ buffer_max(1), options_max(1) // 1 more than necessary as sentinel
+ {
+ }
+
+ /**
+ * @brief Creates a new Stats object and immediately updates it for the
+ * given @c usage and argument vector. You may pass 0 for @c argc and/or @c argv,
+ * if you just want to update @ref options_max.
+ *
+ * @note
+ * The calls to Stats methods must match the later calls to Parser methods.
+ * See Parser::parse() for the meaning of the arguments.
+ */
+ Stats(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, //
+ bool single_minus_longopt = false) :
+ buffer_max(1), options_max(1) // 1 more than necessary as sentinel
+ {
+ add(gnu, usage, argc, argv, min_abbr_len, single_minus_longopt);
+ }
+
+ //! @brief Stats(...) with non-const argv.
+ Stats(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, //
+ bool single_minus_longopt = false) :
+ buffer_max(1), options_max(1) // 1 more than necessary as sentinel
+ {
+ add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt);
+ }
+
+ //! @brief POSIX Stats(...) (gnu==false).
+ Stats(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, //
+ bool single_minus_longopt = false) :
+ buffer_max(1), options_max(1) // 1 more than necessary as sentinel
+ {
+ add(false, usage, argc, argv, min_abbr_len, single_minus_longopt);
+ }
+
+ //! @brief POSIX Stats(...) (gnu==false) with non-const argv.
+ Stats(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, //
+ bool single_minus_longopt = false) :
+ buffer_max(1), options_max(1) // 1 more than necessary as sentinel
+ {
+ add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt);
+ }
+
+ /**
+ * @brief Updates this Stats object for the
+ * given @c usage and argument vector. You may pass 0 for @c argc and/or @c argv,
+ * if you just want to update @ref options_max.
+ *
+ * @note
+ * The calls to Stats methods must match the later calls to Parser methods.
+ * See Parser::parse() for the meaning of the arguments.
+ */
+ void add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, //
+ bool single_minus_longopt = false);
+
+ //! @brief add() with non-const argv.
+ void add(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, //
+ bool single_minus_longopt = false)
+ {
+ add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt);
+ }
+
+ //! @brief POSIX add() (gnu==false).
+ void add(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, //
+ bool single_minus_longopt = false)
+ {
+ add(false, usage, argc, argv, min_abbr_len, single_minus_longopt);
+ }
+
+ //! @brief POSIX add() (gnu==false) with non-const argv.
+ void add(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, //
+ bool single_minus_longopt = false)
+ {
+ add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt);
+ }
+private:
+ class CountOptionsAction;
+};
+
+/**
+ * @brief Checks argument vectors for validity and parses them into data
+ * structures that are easier to work with.
+ *
+ * @par Example:
+ * @code
+ * int main(int argc, char* argv[])
+ * {
+ * argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present
+ * option::Stats stats(usage, argc, argv);
+ * option::Option options[stats.options_max], buffer[stats.buffer_max];
+ * option::Parser parse(usage, argc, argv, options, buffer);
+ *
+ * if (parse.error())
+ * return 1;
+ *
+ * if (options[HELP])
+ * ...
+ * @endcode
+ */
+class Parser
+{
+ int op_count; //!< @internal @brief see optionsCount()
+ int nonop_count; //!< @internal @brief see nonOptionsCount()
+ const char** nonop_args; //!< @internal @brief see nonOptions()
+ bool err; //!< @internal @brief see error()
+public:
+
+ /**
+ * @brief Creates a new Parser.
+ */
+ Parser() :
+ op_count(0), nonop_count(0), nonop_args(0), err(false)
+ {
+ }
+
+ /**
+ * @brief Creates a new Parser and immediately parses the given argument vector.
+ * @copydetails parse()
+ */
+ Parser(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[],
+ int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) :
+ op_count(0), nonop_count(0), nonop_args(0), err(false)
+ {
+ parse(gnu, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
+ }
+
+ //! @brief Parser(...) with non-const argv.
+ Parser(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[],
+ int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) :
+ op_count(0), nonop_count(0), nonop_args(0), err(false)
+ {
+ parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
+ }
+
+ //! @brief POSIX Parser(...) (gnu==false).
+ Parser(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], int min_abbr_len = 0,
+ bool single_minus_longopt = false, int bufmax = -1) :
+ op_count(0), nonop_count(0), nonop_args(0), err(false)
+ {
+ parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
+ }
+
+ //! @brief POSIX Parser(...) (gnu==false) with non-const argv.
+ Parser(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0,
+ bool single_minus_longopt = false, int bufmax = -1) :
+ op_count(0), nonop_count(0), nonop_args(0), err(false)
+ {
+ parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
+ }
+
+ /**
+ * @brief Parses the given argument vector.
+ *
+ * @param gnu if true, parse() will not stop at the first non-option argument. Instead it will
+ * reorder arguments so that all non-options are at the end. This is the default behaviour
+ * of GNU getopt() but is not conforming to POSIX. @n
+ * Note, that once the argument vector has been reordered, the @c gnu flag will have
+ * no further effect on this argument vector. So it is enough to pass @c gnu==true when
+ * creating Stats.
+ * @param usage Array of Descriptor objects that describe the options to support. The last entry
+ * of this array must have 0 in all fields.
+ * @param argc The number of elements from @c argv that are to be parsed. If you pass -1, the number
+ * will be determined automatically. In that case the @c argv list must end with a NULL
+ * pointer.
+ * @param argv The arguments to be parsed. If you pass -1 as @c argc the last pointer in the @c argv
+ * list must be NULL to mark the end.
+ * @param options Each entry is the first element of a linked list of Options. Each new option
+ * that is parsed will be appended to the list specified by that Option's
+ * Descriptor::index. If an entry is not yet used (i.e. the Option is invalid),
+ * it will be replaced rather than appended to. @n
+ * The minimum length of this array is the greatest Descriptor::index value that
+ * occurs in @c usage @e PLUS ONE.
+ * @param buffer Each argument that is successfully parsed (including unknown arguments, if they
+ * have a Descriptor whose CheckArg does not return @ref ARG_ILLEGAL) will be stored in this
+ * array. parse() scans the array for the first invalid entry and begins writing at that
+ * index. You can pass @c bufmax to limit the number of options stored.
+ * @param min_abbr_len Passing a value <code> min_abbr_len > 0 </code> enables abbreviated long
+ * options. The parser will match a prefix of a long option as if it was
+ * the full long option (e.g. @c --foob=10 will be interpreted as if it was
+ * @c --foobar=10 ), as long as the prefix has at least @c min_abbr_len characters
+ * (not counting the @c -- ) and is unambiguous.
+ * @n Be careful if combining @c min_abbr_len=1 with @c single_minus_longopt=true
+ * because the ambiguity check does not consider short options and abbreviated
+ * single minus long options will take precedence over short options.
+ * @param single_minus_longopt Passing @c true for this option allows long options to begin with
+ * a single minus. The double minus form will still be recognized. Note that
+ * single minus long options take precedence over short options and short option
+ * groups. E.g. @c -file would be interpreted as @c --file and not as
+ * <code> -f -i -l -e </code> (assuming a long option named @c "file" exists).
+ * @param bufmax The greatest index in the @c buffer[] array that parse() will write to is
+ * @c bufmax-1. If there are more options, they will be processed (in particular
+ * their CheckArg will be called) but not stored. @n
+ * If you used Stats::buffer_max to dimension this array, you can pass
+ * -1 (or not pass @c bufmax at all) which tells parse() that the buffer is
+ * "large enough".
+ * @attention
+ * Remember that @c options and @c buffer store Option @e objects, not pointers. Therefore it
+ * is not possible for the same object to be in both arrays. For those options that are found in
+ * both @c buffer[] and @c options[] the respective objects are independent copies. And only the
+ * objects in @c options[] are properly linked via Option::next() and Option::prev().
+ * You can iterate over @c buffer[] to
+ * process all options in the order they appear in the argument vector, but if you want access to
+ * the other Options with the same Descriptor::index, then you @e must access the linked list via
+ * @c options[]. You can get the linked list in options from a buffer object via something like
+ * @c options[buffer[i].index()].
+ */
+ void parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[],
+ int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1);
+
+ //! @brief parse() with non-const argv.
+ void parse(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[],
+ int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1)
+ {
+ parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
+ }
+
+ //! @brief POSIX parse() (gnu==false).
+ void parse(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[],
+ int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1)
+ {
+ parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
+ }
+
+ //! @brief POSIX parse() (gnu==false) with non-const argv.
+ void parse(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0,
+ bool single_minus_longopt = false, int bufmax = -1)
+ {
+ parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax);
+ }
+
+ /**
+ * @brief Returns the number of valid Option objects in @c buffer[].
+ *
+ * @note
+ * @li The returned value always reflects the number of Options in the buffer[] array used for
+ * the most recent call to parse().
+ * @li The count (and the buffer[]) includes unknown options if they are collected
+ * (see Descriptor::longopt).
+ */
+ int optionsCount()
+ {
+ return op_count;
+ }
+
+ /**
+ * @brief Returns the number of non-option arguments that remained at the end of the
+ * most recent parse() that actually encountered non-option arguments.
+ *
+ * @note
+ * A parse() that does not encounter non-option arguments will leave this value
+ * as well as nonOptions() undisturbed. This means you can feed the Parser a
+ * default argument vector that contains non-option arguments (e.g. a default filename).
+ * Then you feed it the actual arguments from the user. If the user has supplied at
+ * least one non-option argument, all of the non-option arguments from the default
+ * disappear and are replaced by the user's non-option arguments. However, if the
+ * user does not supply any non-option arguments the defaults will still be in
+ * effect.
+ */
+ int nonOptionsCount()
+ {
+ return nonop_count;
+ }
+
+ /**
+ * @brief Returns a pointer to an array of non-option arguments (only valid
+ * if <code>nonOptionsCount() >0 </code>).
+ *
+ * @note
+ * @li parse() does not copy arguments, so this pointer points into the actual argument
+ * vector as passed to parse().
+ * @li As explained at nonOptionsCount() this pointer is only changed by parse() calls
+ * that actually encounter non-option arguments. A parse() call that encounters only
+ * options, will not change nonOptions().
+ */
+ const char** nonOptions()
+ {
+ return nonop_args;
+ }
+
+ /**
+ * @brief Returns <b><code>nonOptions()[i]</code></b> (@e without checking if i is in range!).
+ */
+ const char* nonOption(int i)
+ {
+ return nonOptions()[i];
+ }
+
+ /**
+ * @brief Returns @c true if an unrecoverable error occurred while parsing options.
+ *
+ * An illegal argument to an option (i.e. CheckArg returns @ref ARG_ILLEGAL) is an
+ * unrecoverable error that aborts the parse. Unknown options are only an error if
+ * their CheckArg function returns @ref ARG_ILLEGAL. Otherwise they are collected.
+ * In that case if you want to exit the program if either an illegal argument
+ * or an unknown option has been passed, use code like this
+ *
+ * @code
+ * if (parser.error() || options[UNKNOWN])
+ * exit(1);
+ * @endcode
+ *
+ */
+ bool error()
+ {
+ return err;
+ }
+
+private:
+ friend struct Stats;
+ class StoreOptionAction;
+ struct Action;
+
+ /**
+ * @internal
+ * @brief This is the core function that does all the parsing.
+ * @retval false iff an unrecoverable error occurred.
+ */
+ static bool workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action,
+ bool single_minus_longopt, bool print_errors, int min_abbr_len);
+
+ /**
+ * @internal
+ * @brief Returns true iff @c st1 is a prefix of @c st2 and
+ * in case @c st2 is longer than @c st1, then
+ * the first additional character is '='.
+ *
+ * @par Examples:
+ * @code
+ * streq("foo", "foo=bar") == true
+ * streq("foo", "foobar") == false
+ * streq("foo", "foo") == true
+ * streq("foo=bar", "foo") == false
+ * @endcode
+ */
+ static bool streq(const char* st1, const char* st2)
+ {
+ while (*st1 != 0)
+ if (*st1++ != *st2++)
+ return false;
+ return (*st2 == 0 || *st2 == '=');
+ }
+
+ /**
+ * @internal
+ * @brief Like streq() but handles abbreviations.
+ *
+ * Returns true iff @c st1 and @c st2 have a common
+ * prefix with the following properties:
+ * @li (if min > 0) its length is at least @c min characters or the same length as @c st1 (whichever is smaller).
+ * @li (if min <= 0) its length is the same as that of @c st1
+ * @li within @c st2 the character following the common prefix is either '=' or end-of-string.
+ *
+ * Examples:
+ * @code
+ * streqabbr("foo", "foo=bar",<anything>) == true
+ * streqabbr("foo", "fo=bar" , 2) == true
+ * streqabbr("foo", "fo" , 2) == true
+ * streqabbr("foo", "fo" , 0) == false
+ * streqabbr("foo", "f=bar" , 2) == false
+ * streqabbr("foo", "f" , 2) == false
+ * streqabbr("fo" , "foo=bar",<anything>) == false
+ * streqabbr("foo", "foobar" ,<anything>) == false
+ * streqabbr("foo", "fobar" ,<anything>) == false
+ * streqabbr("foo", "foo" ,<anything>) == true
+ * @endcode
+ */
+ static bool streqabbr(const char* st1, const char* st2, long long min)
+ {
+ const char* st1start = st1;
+ while (*st1 != 0 && (*st1 == *st2))
+ {
+ ++st1;
+ ++st2;
+ }
+
+ return (*st1 == 0 || (min > 0 && (st1 - st1start) >= min)) && (*st2 == 0 || *st2 == '=');
+ }
+
+ /**
+ * @internal
+ * @brief Returns true iff character @c ch is contained in the string @c st.
+ *
+ * Returns @c true for @c ch==0 .
+ */
+ static bool instr(char ch, const char* st)
+ {
+ while (*st != 0 && *st != ch)
+ ++st;
+ return *st == ch;
+ }
+
+ /**
+ * @internal
+ * @brief Rotates <code>args[-count],...,args[-1],args[0]</code> to become
+ * <code>args[0],args[-count],...,args[-1]</code>.
+ */
+ static void shift(const char** args, int count)
+ {
+ for (int i = 0; i > -count; --i)
+ {
+ const char* temp = args[i];
+ args[i] = args[i - 1];
+ args[i - 1] = temp;
+ }
+ }
+};
+
+/**
+ * @internal
+ * @brief Interface for actions Parser::workhorse() should perform for each Option it
+ * parses.
+ */
+struct Parser::Action
+{
+ /**
+ * @brief Called by Parser::workhorse() for each Option that has been successfully
+ * parsed (including unknown
+ * options if they have a Descriptor whose Descriptor::check_arg does not return
+ * @ref ARG_ILLEGAL.
+ *
+ * Returns @c false iff a fatal error has occured and the parse should be aborted.
+ */
+ virtual bool perform(Option&)
+ {
+ return true;
+ }
+
+ /**
+ * @brief Called by Parser::workhorse() after finishing the parse.
+ * @param numargs the number of non-option arguments remaining
+ * @param args pointer to the first remaining non-option argument (if numargs > 0).
+ *
+ * @return
+ * @c false iff a fatal error has occurred.
+ */
+ virtual bool finished(int numargs, const char** args)
+ {
+ (void) numargs;
+ (void) args;
+ return true;
+ }
+};
+
+/**
+ * @internal
+ * @brief An Action to pass to Parser::workhorse() that will increment a counter for
+ * each parsed Option.
+ */
+class Stats::CountOptionsAction: public Parser::Action
+{
+ unsigned* buffer_max;
+public:
+ /**
+ * Creates a new CountOptionsAction that will increase @c *buffer_max_ for each
+ * parsed Option.
+ */
+ CountOptionsAction(unsigned* buffer_max_) :
+ buffer_max(buffer_max_)
+ {
+ }
+
+ bool perform(Option&)
+ {
+ if (*buffer_max == 0x7fffffff)
+ return false; // overflow protection: don't accept number of options that doesn't fit signed int
+ ++*buffer_max;
+ return true;
+ }
+};
+
+/**
+ * @internal
+ * @brief An Action to pass to Parser::workhorse() that will store each parsed Option in
+ * appropriate arrays (see Parser::parse()).
+ */
+class Parser::StoreOptionAction: public Parser::Action
+{
+ Parser& parser;
+ Option* options;
+ Option* buffer;
+ int bufmax; //! Number of slots in @c buffer. @c -1 means "large enough".
+public:
+ /**
+ * @brief Creates a new StoreOption action.
+ * @param parser_ the parser whose op_count should be updated.
+ * @param options_ each Option @c o is chained into the linked list @c options_[o.desc->index]
+ * @param buffer_ each Option is appended to this array as long as there's a free slot.
+ * @param bufmax_ number of slots in @c buffer_. @c -1 means "large enough".
+ */
+ StoreOptionAction(Parser& parser_, Option options_[], Option buffer_[], int bufmax_) :
+ parser(parser_), options(options_), buffer(buffer_), bufmax(bufmax_)
+ {
+ // find first empty slot in buffer (if any)
+ int bufidx = 0;
+ while ((bufmax < 0 || bufidx < bufmax) && buffer[bufidx])
+ ++bufidx;
+
+ // set parser's optionCount
+ parser.op_count = bufidx;
+ }
+
+ bool perform(Option& option)
+ {
+ if (bufmax < 0 || parser.op_count < bufmax)
+ {
+ if (parser.op_count == 0x7fffffff)
+ return false; // overflow protection: don't accept number of options that doesn't fit signed int
+
+ buffer[parser.op_count] = option;
+ int idx = buffer[parser.op_count].desc->index;
+ if (options[idx])
+ options[idx].append(buffer[parser.op_count]);
+ else
+ options[idx] = buffer[parser.op_count];
+ ++parser.op_count;
+ }
+ return true; // NOTE: an option that is discarded because of a full buffer is not fatal
+ }
+
+ bool finished(int numargs, const char** args)
+ {
+ // only overwrite non-option argument list if there's at least 1
+ // new non-option argument. Otherwise we keep the old list. This
+ // makes it easy to use default non-option arguments.
+ if (numargs > 0)
+ {
+ parser.nonop_count = numargs;
+ parser.nonop_args = args;
+ }
+
+ return true;
+ }
+};
+
+inline void Parser::parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[],
+ Option buffer[], int min_abbr_len, bool single_minus_longopt, int bufmax)
+{
+ StoreOptionAction action(*this, options, buffer, bufmax);
+ err = !workhorse(gnu, usage, argc, argv, action, single_minus_longopt, true, min_abbr_len);
+}
+
+inline void Stats::add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len,
+ bool single_minus_longopt)
+{
+ // determine size of options array. This is the greatest index used in the usage + 1
+ int i = 0;
+ while (usage[i].shortopt != 0)
+ {
+ if (usage[i].index + 1 >= options_max)
+ options_max = (usage[i].index + 1) + 1; // 1 more than necessary as sentinel
+
+ ++i;
+ }
+
+ CountOptionsAction action(&buffer_max);
+ Parser::workhorse(gnu, usage, argc, argv, action, single_minus_longopt, false, min_abbr_len);
+}
+
+inline bool Parser::workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action,
+ bool single_minus_longopt, bool print_errors, int min_abbr_len)
+{
+ // protect against NULL pointer
+ if (args == 0)
+ numargs = 0;
+
+ int nonops = 0;
+
+ while (numargs != 0 && *args != 0)
+ {
+ const char* param = *args; // param can be --long-option, -srto or non-option argument
+
+ // in POSIX mode the first non-option argument terminates the option list
+ // a lone minus character is a non-option argument
+ if (param[0] != '-' || param[1] == 0)
+ {
+ if (gnu)
+ {
+ ++nonops;
+ ++args;
+ if (numargs > 0)
+ --numargs;
+ continue;
+ }
+ else
+ break;
+ }
+
+ // -- terminates the option list. The -- itself is skipped.
+ if (param[1] == '-' && param[2] == 0)
+ {
+ shift(args, nonops);
+ ++args;
+ if (numargs > 0)
+ --numargs;
+ break;
+ }
+
+ bool handle_short_options;
+ const char* longopt_name;
+ if (param[1] == '-') // if --long-option
+ {
+ handle_short_options = false;
+ longopt_name = param + 2;
+ }
+ else
+ {
+ handle_short_options = true;
+ longopt_name = param + 1; //for testing a potential -long-option
+ }
+
+ bool try_single_minus_longopt = single_minus_longopt;
+ bool have_more_args = (numargs > 1 || numargs < 0); // is referencing argv[1] valid?
+
+ do // loop over short options in group, for long options the body is executed only once
+ {
+ int idx = 0;
+
+ const char* optarg = 0;
+
+ /******************** long option **********************/
+ if (handle_short_options == false || try_single_minus_longopt)
+ {
+ idx = 0;
+ while (usage[idx].longopt != 0 && !streq(usage[idx].longopt, longopt_name))
+ ++idx;
+
+ if (usage[idx].longopt == 0 && min_abbr_len > 0) // if we should try to match abbreviated long options
+ {
+ int i1 = 0;
+ while (usage[i1].longopt != 0 && !streqabbr(usage[i1].longopt, longopt_name, min_abbr_len))
+ ++i1;
+ if (usage[i1].longopt != 0)
+ { // now test if the match is unambiguous by checking for another match
+ int i2 = i1 + 1;
+ while (usage[i2].longopt != 0 && !streqabbr(usage[i2].longopt, longopt_name, min_abbr_len))
+ ++i2;
+
+ if (usage[i2].longopt == 0) // if there was no second match it's unambiguous, so accept i1 as idx
+ idx = i1;
+ }
+ }
+
+ // if we found something, disable handle_short_options (only relevant if single_minus_longopt)
+ if (usage[idx].longopt != 0)
+ handle_short_options = false;
+
+ try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group
+
+ optarg = longopt_name;
+ while (*optarg != 0 && *optarg != '=')
+ ++optarg;
+ if (*optarg == '=') // attached argument
+ ++optarg;
+ else
+ // possibly detached argument
+ optarg = (have_more_args ? args[1] : 0);
+ }
+
+ /************************ short option ***********************************/
+ if (handle_short_options)
+ {
+ if (*++param == 0) // point at the 1st/next option character
+ break; // end of short option group
+
+ idx = 0;
+ while (usage[idx].shortopt != 0 && !instr(*param, usage[idx].shortopt))
+ ++idx;
+
+ if (param[1] == 0) // if the potential argument is separate
+ optarg = (have_more_args ? args[1] : 0);
+ else
+ // if the potential argument is attached
+ optarg = param + 1;
+ }
+
+ const Descriptor* descriptor = &usage[idx];
+
+ if (descriptor->shortopt == 0) /************** unknown option ********************/
+ {
+ // look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options
+ idx = 0;
+ while (usage[idx].shortopt != 0 && (usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0))
+ ++idx;
+ descriptor = (usage[idx].shortopt == 0 ? 0 : &usage[idx]);
+ }
+
+ if (descriptor != 0)
+ {
+ Option option(descriptor, param, optarg);
+ switch (descriptor->check_arg(option, print_errors))
+ {
+ case ARG_ILLEGAL:
+ return false; // fatal
+ case ARG_OK:
+ // skip one element of the argument vector, if it's a separated argument
+ if (optarg != 0 && have_more_args && optarg == args[1])
+ {
+ shift(args, nonops);
+ if (numargs > 0)
+ --numargs;
+ ++args;
+ }
+
+ // No further short options are possible after an argument
+ handle_short_options = false;
+
+ break;
+ case ARG_IGNORE:
+ case ARG_NONE:
+ option.arg = 0;
+ break;
+ }
+
+ if (!action.perform(option))
+ return false;
+ }
+
+ } while (handle_short_options);
+
+ shift(args, nonops);
+ ++args;
+ if (numargs > 0)
+ --numargs;
+
+ } // while
+
+ if (numargs > 0 && *args == 0) // It's a bug in the caller if numargs is greater than the actual number
+ numargs = 0; // of arguments, but as a service to the user we fix this if we spot it.
+
+ if (numargs < 0) // if we don't know the number of remaining non-option arguments
+ { // we need to count them
+ numargs = 0;
+ while (args[numargs] != 0)
+ ++numargs;
+ }
+
+ return action.finished(numargs + nonops, args - nonops);
+}
+
+/**
+ * @internal
+ * @brief The implementation of option::printUsage().
+ */
+struct PrintUsageImplementation
+{
+ /**
+ * @internal
+ * @brief Interface for Functors that write (part of) a string somewhere.
+ */
+ struct IStringWriter
+ {
+ /**
+ * @brief Writes the given number of chars beginning at the given pointer somewhere.
+ */
+ virtual void operator()(const char*, int)
+ {
+ }
+ };
+
+ /**
+ * @internal
+ * @brief Encapsulates a function with signature <code>func(string, size)</code> where
+ * string can be initialized with a const char* and size with an int.
+ */
+ template<typename Function>
+ struct FunctionWriter: public IStringWriter
+ {
+ Function* write;
+
+ virtual void operator()(const char* str, int size)
+ {
+ (*write)(str, size);
+ }
+
+ FunctionWriter(Function* w) :
+ write(w)
+ {
+ }
+ };
+
+ /**
+ * @internal
+ * @brief Encapsulates a reference to an object with a <code>write(string, size)</code>
+ * method like that of @c std::ostream.
+ */
+ template<typename OStream>
+ struct OStreamWriter: public IStringWriter
+ {
+ OStream& ostream;
+
+ virtual void operator()(const char* str, int size)
+ {
+ ostream.write(str, size);
+ }
+
+ OStreamWriter(OStream& o) :
+ ostream(o)
+ {
+ }
+ };
+
+ /**
+ * @internal
+ * @brief Like OStreamWriter but encapsulates a @c const reference, which is
+ * typically a temporary object of a user class.
+ */
+ template<typename Temporary>
+ struct TemporaryWriter: public IStringWriter
+ {
+ const Temporary& userstream;
+
+ virtual void operator()(const char* str, int size)
+ {
+ userstream.write(str, size);
+ }
+
+ TemporaryWriter(const Temporary& u) :
+ userstream(u)
+ {
+ }
+ };
+
+ /**
+ * @internal
+ * @brief Encapsulates a function with the signature <code>func(fd, string, size)</code> (the
+ * signature of the @c write() system call)
+ * where fd can be initialized from an int, string from a const char* and size from an int.
+ */
+ template<typename Syscall>
+ struct SyscallWriter: public IStringWriter
+ {
+ Syscall* write;
+ int fd;
+
+ virtual void operator()(const char* str, int size)
+ {
+ (*write)(fd, str, size);
+ }
+
+ SyscallWriter(Syscall* w, int f) :
+ write(w), fd(f)
+ {
+ }
+ };
+
+ /**
+ * @internal
+ * @brief Encapsulates a function with the same signature as @c std::fwrite().
+ */
+ template<typename Function, typename Stream>
+ struct StreamWriter: public IStringWriter
+ {
+ Function* fwrite;
+ Stream* stream;
+
+ virtual void operator()(const char* str, int size)
+ {
+ (*fwrite)(str, size, 1, stream);
+ }
+
+ StreamWriter(Function* w, Stream* s) :
+ fwrite(w), stream(s)
+ {
+ }
+ };
+
+ /**
+ * @internal
+ * @brief Sets <code> i1 = max(i1, i2) </code>
+ */
+ static void upmax(int& i1, int i2)
+ {
+ i1 = (i1 >= i2 ? i1 : i2);
+ }
+
+ /**
+ * @internal
+ * @brief Moves the "cursor" to column @c want_x assuming it is currently at column @c x
+ * and sets @c x=want_x .
+ * If <code> x > want_x </code>, a line break is output before indenting.
+ *
+ * @param write Spaces and possibly a line break are written via this functor to get
+ * the desired indentation @c want_x .
+ * @param[in,out] x the current indentation. Set to @c want_x by this method.
+ * @param want_x the desired indentation.
+ */
+ static void indent(IStringWriter& write, int& x, int want_x)
+ {
+ int indent = want_x - x;
+ if (indent < 0)
+ {
+ write("\n", 1);
+ indent = want_x;
+ }
+
+ if (indent > 0)
+ {
+ char space = ' ';
+ for (int i = 0; i < indent; ++i)
+ write(&space, 1);
+ x = want_x;
+ }
+ }
+
+ /**
+ * @brief Returns true if ch is the unicode code point of a wide character.
+ *
+ * @note
+ * The following character ranges are treated as wide
+ * @code
+ * 1100..115F
+ * 2329..232A (just 2 characters!)
+ * 2E80..A4C6 except for 303F
+ * A960..A97C
+ * AC00..D7FB
+ * F900..FAFF
+ * FE10..FE6B
+ * FF01..FF60
+ * FFE0..FFE6
+ * 1B000......
+ * @endcode
+ */
+ static bool isWideChar(unsigned ch)
+ {
+ if (ch == 0x303F)
+ return false;
+
+ return ((0x1100 <= ch && ch <= 0x115F) || (0x2329 <= ch && ch <= 0x232A) || (0x2E80 <= ch && ch <= 0xA4C6)
+ || (0xA960 <= ch && ch <= 0xA97C) || (0xAC00 <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFAFF)
+ || (0xFE10 <= ch && ch <= 0xFE6B) || (0xFF01 <= ch && ch <= 0xFF60) || (0xFFE0 <= ch && ch <= 0xFFE6)
+ || (0x1B000 <= ch));
+ }
+
+ /**
+ * @internal
+ * @brief Splits a @c Descriptor[] array into tables, rows, lines and columns and
+ * iterates over these components.
+ *
+ * The top-level organizational unit is the @e table.
+ * A table begins at a Descriptor with @c help!=NULL and extends up to
+ * a Descriptor with @c help==NULL.
+ *
+ * A table consists of @e rows. Due to line-wrapping and explicit breaks
+ * a row may take multiple lines on screen. Rows within the table are separated
+ * by \\n. They never cross Descriptor boundaries. This means a row ends either
+ * at \\n or the 0 at the end of the help string.
+ *
+ * A row consists of columns/cells. Columns/cells within a row are separated by \\t.
+ * Line breaks within a cell are marked by \\v.
+ *
+ * Rows in the same table need not have the same number of columns/cells. The
+ * extreme case are interjections, which are rows that contain neither \\t nor \\v.
+ * These are NOT treated specially by LinePartIterator, but they are treated
+ * specially by printUsage().
+ *
+ * LinePartIterator iterates through the usage at 3 levels: table, row and part.
+ * Tables and rows are as described above. A @e part is a line within a cell.
+ * LinePartIterator iterates through 1st parts of all cells, then through the 2nd
+ * parts of all cells (if any),... @n
+ * Example: The row <code> "1 \v 3 \t 2 \v 4" </code> has 2 cells/columns and 4 parts.
+ * The parts will be returned in the order 1, 2, 3, 4.
+ *
+ * It is possible that some cells have fewer parts than others. In this case
+ * LinePartIterator will "fill up" these cells with 0-length parts. IOW, LinePartIterator
+ * always returns the same number of parts for each column. Note that this is different
+ * from the way rows and columns are handled. LinePartIterator does @e not guarantee that
+ * the same number of columns will be returned for each row.
+ *
+ */
+ class LinePartIterator
+ {
+ const Descriptor* tablestart; //!< The 1st descriptor of the current table.
+ const Descriptor* rowdesc; //!< The Descriptor that contains the current row.
+ const char* rowstart; //!< Ptr to 1st character of current row within rowdesc->help.
+ const char* ptr; //!< Ptr to current part within the current row.
+ int col; //!< Index of current column.
+ int len; //!< Length of the current part (that ptr points at) in BYTES
+ int screenlen; //!< Length of the current part in screen columns (taking narrow/wide chars into account).
+ int max_line_in_block; //!< Greatest index of a line within the block. This is the number of \\v within the cell with the most \\vs.
+ int line_in_block; //!< Line index within the current cell of the current part.
+ int target_line_in_block; //!< Line index of the parts we should return to the user on this iteration.
+ bool hit_target_line; //!< Flag whether we encountered a part with line index target_line_in_block in the current cell.
+
+ /**
+ * @brief Determines the byte and character lengths of the part at @ref ptr and
+ * stores them in @ref len and @ref screenlen respectively.
+ */
+ void update_length()
+ {
+ screenlen = 0;
+ for (len = 0; ptr[len] != 0 && ptr[len] != '\v' && ptr[len] != '\t' && ptr[len] != '\n'; ++len)
+ {
+ ++screenlen;
+ unsigned ch = (unsigned char) ptr[len];
+ if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte
+ {
+ // int __builtin_clz (unsigned int x)
+ // Returns the number of leading 0-bits in x, starting at the most significant bit
+ unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff);
+ ch = ch & mask; // mask out length bits, we don't verify their correctness
+ while (((unsigned char) ptr[len + 1] ^ 0x80) <= 0x3F) // while next byte is continuation byte
+ {
+ ch = (ch << 6) ^ (unsigned char) ptr[len + 1] ^ 0x80; // add continuation to char code
+ ++len;
+ }
+ // ch is the decoded unicode code point
+ if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case
+ ++screenlen;
+ }
+ }
+ }
+
+ public:
+ //! @brief Creates an iterator for @c usage.
+ LinePartIterator(const Descriptor usage[]) :
+ tablestart(usage), rowdesc(0), rowstart(0), ptr(0), col(-1), len(0), max_line_in_block(0), line_in_block(0),
+ target_line_in_block(0), hit_target_line(true)
+ {
+ }
+
+ /**
+ * @brief Moves iteration to the next table (if any). Has to be called once on a new
+ * LinePartIterator to move to the 1st table.
+ * @retval false if moving to next table failed because no further table exists.
+ */
+ bool nextTable()
+ {
+ // If this is NOT the first time nextTable() is called after the constructor,
+ // then skip to the next table break (i.e. a Descriptor with help == 0)
+ if (rowdesc != 0)
+ {
+ while (tablestart->help != 0 && tablestart->shortopt != 0)
+ ++tablestart;
+ }
+
+ // Find the next table after the break (if any)
+ while (tablestart->help == 0 && tablestart->shortopt != 0)
+ ++tablestart;
+
+ restartTable();
+ return rowstart != 0;
+ }
+
+ /**
+ * @brief Reset iteration to the beginning of the current table.
+ */
+ void restartTable()
+ {
+ rowdesc = tablestart;
+ rowstart = tablestart->help;
+ ptr = 0;
+ }
+
+ /**
+ * @brief Moves iteration to the next row (if any). Has to be called once after each call to
+ * @ref nextTable() to move to the 1st row of the table.
+ * @retval false if moving to next row failed because no further row exists.
+ */
+ bool nextRow()
+ {
+ if (ptr == 0)
+ {
+ restartRow();
+ return rowstart != 0;
+ }
+
+ while (*ptr != 0 && *ptr != '\n')
+ ++ptr;
+
+ if (*ptr == 0)
+ {
+ if ((rowdesc + 1)->help == 0) // table break
+ return false;
+
+ ++rowdesc;
+ rowstart = rowdesc->help;
+ }
+ else // if (*ptr == '\n')
+ {
+ rowstart = ptr + 1;
+ }
+
+ restartRow();
+ return true;
+ }
+
+ /**
+ * @brief Reset iteration to the beginning of the current row.
+ */
+ void restartRow()
+ {
+ ptr = rowstart;
+ col = -1;
+ len = 0;
+ screenlen = 0;
+ max_line_in_block = 0;
+ line_in_block = 0;
+ target_line_in_block = 0;
+ hit_target_line = true;
+ }
+
+ /**
+ * @brief Moves iteration to the next part (if any). Has to be called once after each call to
+ * @ref nextRow() to move to the 1st part of the row.
+ * @retval false if moving to next part failed because no further part exists.
+ *
+ * See @ref LinePartIterator for details about the iteration.
+ */
+ bool next()
+ {
+ if (ptr == 0)
+ return false;
+
+ if (col == -1)
+ {
+ col = 0;
+ update_length();
+ return true;
+ }
+
+ ptr += len;
+ while (true)
+ {
+ switch (*ptr)
+ {
+ case '\v':
+ upmax(max_line_in_block, ++line_in_block);
+ ++ptr;
+ break;
+ case '\t':
+ if (!hit_target_line) // if previous column did not have the targetline
+ { // then "insert" a 0-length part
+ update_length();
+ hit_target_line = true;
+ return true;
+ }
+
+ hit_target_line = false;
+ line_in_block = 0;
+ ++col;
+ ++ptr;
+ break;
+ case 0:
+ case '\n':
+ if (!hit_target_line) // if previous column did not have the targetline
+ { // then "insert" a 0-length part
+ update_length();
+ hit_target_line = true;
+ return true;
+ }
+
+ if (++target_line_in_block > max_line_in_block)
+ {
+ update_length();
+ return false;
+ }
+
+ hit_target_line = false;
+ line_in_block = 0;
+ col = 0;
+ ptr = rowstart;
+ continue;
+ default:
+ ++ptr;
+ continue;
+ } // switch
+
+ if (line_in_block == target_line_in_block)
+ {
+ update_length();
+ hit_target_line = true;
+ return true;
+ }
+ } // while
+ }
+
+ /**
+ * @brief Returns the index (counting from 0) of the column in which
+ * the part pointed to by @ref data() is located.
+ */
+ int column()
+ {
+ return col;
+ }
+
+ /**
+ * @brief Returns the index (counting from 0) of the line within the current column
+ * this part belongs to.
+ */
+ int line()
+ {
+ return target_line_in_block; // NOT line_in_block !!! It would be wrong if !hit_target_line
+ }
+
+ /**
+ * @brief Returns the length of the part pointed to by @ref data() in raw chars (not UTF-8 characters).
+ */
+ int length()
+ {
+ return len;
+ }
+
+ /**
+ * @brief Returns the width in screen columns of the part pointed to by @ref data().
+ * Takes multi-byte UTF-8 sequences and wide characters into account.
+ */
+ int screenLength()
+ {
+ return screenlen;
+ }
+
+ /**
+ * @brief Returns the current part of the iteration.
+ */
+ const char* data()
+ {
+ return ptr;
+ }
+ };
+
+ /**
+ * @internal
+ * @brief Takes input and line wraps it, writing out one line at a time so that
+ * it can be interleaved with output from other columns.
+ *
+ * The LineWrapper is used to handle the last column of each table as well as interjections.
+ * The LineWrapper is called once for each line of output. If the data given to it fits
+ * into the designated width of the last column it is simply written out. If there
+ * is too much data, an appropriate split point is located and only the data up to this
+ * split point is written out. The rest of the data is queued for the next line.
+ * That way the last column can be line wrapped and interleaved with data from
+ * other columns. The following example makes this clearer:
+ * @code
+ * Column 1,1 Column 2,1 This is a long text
+ * Column 1,2 Column 2,2 that does not fit into
+ * a single line.
+ * @endcode
+ *
+ * The difficulty in producing this output is that the whole string
+ * "This is a long text that does not fit into a single line" is the
+ * 1st and only part of column 3. In order to produce the above
+ * output the string must be output piecemeal, interleaved with
+ * the data from the other columns.
+ */
+ class LineWrapper
+ {
+ static const int bufmask = 15; //!< Must be a power of 2 minus 1.
+ /**
+ * @brief Ring buffer for length component of pair (data, length).
+ */
+ int lenbuf[bufmask + 1];
+ /**
+ * @brief Ring buffer for data component of pair (data, length).
+ */
+ const char* datbuf[bufmask + 1];
+ /**
+ * @brief The indentation of the column to which the LineBuffer outputs. LineBuffer
+ * assumes that the indentation has already been written when @ref process()
+ * is called, so this value is only used when a buffer flush requires writing
+ * additional lines of output.
+ */
+ int x;
+ /**
+ * @brief The width of the column to line wrap.
+ */
+ int width;
+ int head; //!< @brief index for next write
+ int tail; //!< @brief index for next read - 1 (i.e. increment tail BEFORE read)
+
+ /**
+ * @brief Multiple methods of LineWrapper may decide to flush part of the buffer to
+ * free up space. The contract of process() says that only 1 line is output. So
+ * this variable is used to track whether something has output a line. It is
+ * reset at the beginning of process() and checked at the end to decide if
+ * output has already occurred or is still needed.
+ */
+ bool wrote_something;
+
+ bool buf_empty()
+ {
+ return ((tail + 1) & bufmask) == head;
+ }
+
+ bool buf_full()
+ {
+ return tail == head;
+ }
+
+ void buf_store(const char* data, int len)
+ {
+ lenbuf[head] = len;
+ datbuf[head] = data;
+ head = (head + 1) & bufmask;
+ }
+
+ //! @brief Call BEFORE reading ...buf[tail].
+ void buf_next()
+ {
+ tail = (tail + 1) & bufmask;
+ }
+
+ /**
+ * @brief Writes (data,len) into the ring buffer. If the buffer is full, a single line
+ * is flushed out of the buffer into @c write.
+ */
+ void output(IStringWriter& write, const char* data, int len)
+ {
+ if (buf_full())
+ write_one_line(write);
+
+ buf_store(data, len);
+ }
+
+ /**
+ * @brief Writes a single line of output from the buffer to @c write.
+ */
+ void write_one_line(IStringWriter& write)
+ {
+ if (wrote_something) // if we already wrote something, we need to start a new line
+ {
+ write("\n", 1);
+ int _ = 0;
+ indent(write, _, x);
+ }
+
+ if (!buf_empty())
+ {
+ buf_next();
+ write(datbuf[tail], lenbuf[tail]);
+ }
+
+ wrote_something = true;
+ }
+ public:
+
+ /**
+ * @brief Writes out all remaining data from the LineWrapper using @c write.
+ * Unlike @ref process() this method indents all lines including the first and
+ * will output a \\n at the end (but only if something has been written).
+ */
+ void flush(IStringWriter& write)
+ {
+ if (buf_empty())
+ return;
+ int _ = 0;
+ indent(write, _, x);
+ wrote_something = false;
+ while (!buf_empty())
+ write_one_line(write);
+ write("\n", 1);
+ }
+
+ /**
+ * @brief Process, wrap and output the next piece of data.
+ *
+ * process() will output at least one line of output. This is not necessarily
+ * the @c data passed in. It may be data queued from a prior call to process().
+ * If the internal buffer is full, more than 1 line will be output.
+ *
+ * process() assumes that the a proper amount of indentation has already been
+ * output. It won't write any further indentation before the 1st line. If
+ * more than 1 line is written due to buffer constraints, the lines following
+ * the first will be indented by this method, though.
+ *
+ * No \\n is written by this method after the last line that is written.
+ *
+ * @param write where to write the data.
+ * @param data the new chunk of data to write.
+ * @param len the length of the chunk of data to write.
+ */
+ void process(IStringWriter& write, const char* data, int len)
+ {
+ wrote_something = false;
+
+ while (len > 0)
+ {
+ if (len <= width) // quick test that works because utf8width <= len (all wide chars have at least 2 bytes)
+ {
+ output(write, data, len);
+ len = 0;
+ }
+ else // if (len > width) it's possible (but not guaranteed) that utf8len > width
+ {
+ int utf8width = 0;
+ int maxi = 0;
+ while (maxi < len && utf8width < width)
+ {
+ int charbytes = 1;
+ unsigned ch = (unsigned char) data[maxi];
+ if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte
+ {
+ // int __builtin_clz (unsigned int x)
+ // Returns the number of leading 0-bits in x, starting at the most significant bit
+ unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff);
+ ch = ch & mask; // mask out length bits, we don't verify their correctness
+ while ((maxi + charbytes < len) && //
+ (((unsigned char) data[maxi + charbytes] ^ 0x80) <= 0x3F)) // while next byte is continuation byte
+ {
+ ch = (ch << 6) ^ (unsigned char) data[maxi + charbytes] ^ 0x80; // add continuation to char code
+ ++charbytes;
+ }
+ // ch is the decoded unicode code point
+ if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case
+ {
+ if (utf8width + 2 > width)
+ break;
+ ++utf8width;
+ }
+ }
+ ++utf8width;
+ maxi += charbytes;
+ }
+
+ // data[maxi-1] is the last byte of the UTF-8 sequence of the last character that fits
+ // onto the 1st line. If maxi == len, all characters fit on the line.
+
+ if (maxi == len)
+ {
+ output(write, data, len);
+ len = 0;
+ }
+ else // if (maxi < len) at least 1 character (data[maxi] that is) doesn't fit on the line
+ {
+ int i;
+ for (i = maxi; i >= 0; --i)
+ if (data[i] == ' ')
+ break;
+
+ if (i >= 0)
+ {
+ output(write, data, i);
+ data += i + 1;
+ len -= i + 1;
+ }
+ else // did not find a space to split at => split before data[maxi]
+ { // data[maxi] is always the beginning of a character, never a continuation byte
+ output(write, data, maxi);
+ data += maxi;
+ len -= maxi;
+ }
+ }
+ }
+ }
+ if (!wrote_something) // if we didn't already write something to make space in the buffer
+ write_one_line(write); // write at most one line of actual output
+ }
+
+ /**
+ * @brief Constructs a LineWrapper that wraps its output to fit into
+ * screen columns @c x1 (incl.) to @c x2 (excl.).
+ *
+ * @c x1 gives the indentation LineWrapper uses if it needs to indent.
+ */
+ LineWrapper(int x1, int x2) :
+ x(x1), width(x2 - x1), head(0), tail(bufmask)
+ {
+ if (width < 2) // because of wide characters we need at least width 2 or the code breaks
+ width = 2;
+ }
+ };
+
+ /**
+ * @internal
+ * @brief This is the implementation that is shared between all printUsage() templates.
+ * Because all printUsage() templates share this implementation, there is no template bloat.
+ */
+ static void printUsage(IStringWriter& write, const Descriptor usage[], int width = 80, //
+ int last_column_min_percent = 50, int last_column_own_line_max_percent = 75)
+ {
+ if (width < 1) // protect against nonsense values
+ width = 80;
+
+ if (width > 10000) // protect against overflow in the following computation
+ width = 10000;
+
+ int last_column_min_width = ((width * last_column_min_percent) + 50) / 100;
+ int last_column_own_line_max_width = ((width * last_column_own_line_max_percent) + 50) / 100;
+ if (last_column_own_line_max_width == 0)
+ last_column_own_line_max_width = 1;
+
+ LinePartIterator part(usage);
+ while (part.nextTable())
+ {
+
+ /***************** Determine column widths *******************************/
+
+ const int maxcolumns = 8; // 8 columns are enough for everyone
+ int col_width[maxcolumns];
+ int lastcolumn;
+ int leftwidth;
+ int overlong_column_threshold = 10000;
+ do
+ {
+ lastcolumn = 0;
+ for (int i = 0; i < maxcolumns; ++i)
+ col_width[i] = 0;
+
+ part.restartTable();
+ while (part.nextRow())
+ {
+ while (part.next())
+ {
+ if (part.column() < maxcolumns)
+ {
+ upmax(lastcolumn, part.column());
+ if (part.screenLength() < overlong_column_threshold)
+ // We don't let rows that don't use table separators (\t or \v) influence
+ // the width of column 0. This allows the user to interject section headers
+ // or explanatory paragraphs that do not participate in the table layout.
+ if (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t'
+ || part.data()[part.length()] == '\v')
+ upmax(col_width[part.column()], part.screenLength());
+ }
+ }
+ }
+
+ /*
+ * If the last column doesn't fit on the same
+ * line as the other columns, we can fix that by starting it on its own line.
+ * However we can't do this for any of the columns 0..lastcolumn-1.
+ * If their sum exceeds the maximum width we try to fix this by iteratively
+ * ignoring the widest line parts in the width determination until
+ * we arrive at a series of column widths that fit into one line.
+ * The result is a layout where everything is nicely formatted
+ * except for a few overlong fragments.
+ * */
+
+ leftwidth = 0;
+ overlong_column_threshold = 0;
+ for (int i = 0; i < lastcolumn; ++i)
+ {
+ leftwidth += col_width[i];
+ upmax(overlong_column_threshold, col_width[i]);
+ }
+
+ } while (leftwidth > width);
+
+ /**************** Determine tab stops and last column handling **********************/
+
+ int tabstop[maxcolumns];
+ tabstop[0] = 0;
+ for (int i = 1; i < maxcolumns; ++i)
+ tabstop[i] = tabstop[i - 1] + col_width[i - 1];
+
+ int rightwidth = width - tabstop[lastcolumn];
+ bool print_last_column_on_own_line = false;
+ if (rightwidth < last_column_min_width && rightwidth < col_width[lastcolumn])
+ {
+ print_last_column_on_own_line = true;
+ rightwidth = last_column_own_line_max_width;
+ }
+
+ // If lastcolumn == 0 we must disable print_last_column_on_own_line because
+ // otherwise 2 copies of the last (and only) column would be output.
+ // Actually this is just defensive programming. It is currently not
+ // possible that lastcolumn==0 and print_last_column_on_own_line==true
+ // at the same time, because lastcolumn==0 => tabstop[lastcolumn] == 0 =>
+ // rightwidth==width => rightwidth>=last_column_min_width (unless someone passes
+ // a bullshit value >100 for last_column_min_percent) => the above if condition
+ // is false => print_last_column_on_own_line==false
+ if (lastcolumn == 0)
+ print_last_column_on_own_line = false;
+
+ LineWrapper lastColumnLineWrapper(width - rightwidth, width);
+ LineWrapper interjectionLineWrapper(0, width);
+
+ part.restartTable();
+
+ /***************** Print out all rows of the table *************************************/
+
+ while (part.nextRow())
+ {
+ int x = -1;
+ while (part.next())
+ {
+ if (part.column() > lastcolumn)
+ continue; // drop excess columns (can happen if lastcolumn == maxcolumns-1)
+
+ if (part.column() == 0)
+ {
+ if (x >= 0)
+ write("\n", 1);
+ x = 0;
+ }
+
+ indent(write, x, tabstop[part.column()]);
+
+ if ((part.column() < lastcolumn)
+ && (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t'
+ || part.data()[part.length()] == '\v'))
+ {
+ write(part.data(), part.length());
+ x += part.screenLength();
+ }
+ else // either part.column() == lastcolumn or we are in the special case of
+ // an interjection that doesn't contain \v or \t
+ {
+ // NOTE: This code block is not necessarily executed for
+ // each line, because some rows may have fewer columns.
+
+ LineWrapper& lineWrapper = (part.column() == 0) ? interjectionLineWrapper : lastColumnLineWrapper;
+
+ if (!print_last_column_on_own_line)
+ lineWrapper.process(write, part.data(), part.length());
+ }
+ } // while
+
+ if (print_last_column_on_own_line)
+ {
+ part.restartRow();
+ while (part.next())
+ {
+ if (part.column() == lastcolumn)
+ {
+ write("\n", 1);
+ int _ = 0;
+ indent(write, _, width - rightwidth);
+ lastColumnLineWrapper.process(write, part.data(), part.length());
+ }
+ }
+ }
+
+ write("\n", 1);
+ lastColumnLineWrapper.flush(write);
+ interjectionLineWrapper.flush(write);
+ }
+ }
+ }
+
+}
+;
+
+/**
+ * @brief Outputs a nicely formatted usage string with support for multi-column formatting
+ * and line-wrapping.
+ *
+ * printUsage() takes the @c help texts of a Descriptor[] array and formats them into
+ * a usage message, wrapping lines to achieve the desired output width.
+ *
+ * <b>Table formatting:</b>
+ *
+ * Aside from plain strings which are simply line-wrapped, the usage may contain tables. Tables
+ * are used to align elements in the output.
+ *
+ * @code
+ * // Without a table. The explanatory texts are not aligned.
+ * -c, --create |Creates something.
+ * -k, --kill |Destroys something.
+ *
+ * // With table formatting. The explanatory texts are aligned.
+ * -c, --create |Creates something.
+ * -k, --kill |Destroys something.
+ * @endcode
+ *
+ * Table formatting removes the need to pad help texts manually with spaces to achieve
+ * alignment. To create a table, simply insert \\t (tab) characters to separate the cells
+ * within a row.
+ *
+ * @code
+ * const option::Descriptor usage[] = {
+ * {..., "-c, --create \tCreates something." },
+ * {..., "-k, --kill \tDestroys something." }, ...
+ * @endcode
+ *
+ * Note that you must include the minimum amount of space desired between cells yourself.
+ * Table formatting will insert further spaces as needed to achieve alignment.
+ *
+ * You can insert line breaks within cells by using \\v (vertical tab).
+ *
+ * @code
+ * const option::Descriptor usage[] = {
+ * {..., "-c,\v--create \tCreates\vsomething." },
+ * {..., "-k,\v--kill \tDestroys\vsomething." }, ...
+ *
+ * // results in
+ *
+ * -c, Creates
+ * --create something.
+ * -k, Destroys
+ * --kill something.
+ * @endcode
+ *
+ * You can mix lines that do not use \\t or \\v with those that do. The plain
+ * lines will not mess up the table layout. Alignment of the table columns will
+ * be maintained even across these interjections.
+ *
+ * @code
+ * const option::Descriptor usage[] = {
+ * {..., "-c, --create \tCreates something." },
+ * {..., "----------------------------------" },
+ * {..., "-k, --kill \tDestroys something." }, ...
+ *
+ * // results in
+ *
+ * -c, --create Creates something.
+ * ----------------------------------
+ * -k, --kill Destroys something.
+ * @endcode
+ *
+ * You can have multiple tables within the same usage whose columns are
+ * aligned independently. Simply insert a dummy Descriptor with @c help==0.
+ *
+ * @code
+ * const option::Descriptor usage[] = {
+ * {..., "Long options:" },
+ * {..., "--very-long-option \tDoes something long." },
+ * {..., "--ultra-super-mega-long-option \tTakes forever to complete." },
+ * {..., 0 }, // ---------- table break -----------
+ * {..., "Short options:" },
+ * {..., "-s \tShort." },
+ * {..., "-q \tQuick." }, ...
+ *
+ * // results in
+ *
+ * Long options:
+ * --very-long-option Does something long.
+ * --ultra-super-mega-long-option Takes forever to complete.
+ * Short options:
+ * -s Short.
+ * -q Quick.
+ *
+ * // Without the table break it would be
+ *
+ * Long options:
+ * --very-long-option Does something long.
+ * --ultra-super-mega-long-option Takes forever to complete.
+ * Short options:
+ * -s Short.
+ * -q Quick.
+ * @endcode
+ *
+ * <b>Output methods:</b>
+ *
+ * Because TheLeanMeanC++Option parser is freestanding, you have to provide the means for
+ * output in the first argument(s) to printUsage(). Because printUsage() is implemented as
+ * a set of template functions, you have great flexibility in your choice of output
+ * method. The following example demonstrates typical uses. Anything that's similar enough
+ * will work.
+ *
+ * @code
+ * #include <unistd.h> // write()
+ * #include <iostream> // cout
+ * #include <sstream> // ostringstream
+ * #include <cstdio> // fwrite()
+ * using namespace std;
+ *
+ * void my_write(const char* str, int size) {
+ * fwrite(str, size, 1, stdout);
+ * }
+ *
+ * struct MyWriter {
+ * void write(const char* buf, size_t size) const {
+ * fwrite(str, size, 1, stdout);
+ * }
+ * };
+ *
+ * struct MyWriteFunctor {
+ * void operator()(const char* buf, size_t size) {
+ * fwrite(str, size, 1, stdout);
+ * }
+ * };
+ * ...
+ * printUsage(my_write, usage); // custom write function
+ * printUsage(MyWriter(), usage); // temporary of a custom class
+ * MyWriter writer;
+ * printUsage(writer, usage); // custom class object
+ * MyWriteFunctor wfunctor;
+ * printUsage(&wfunctor, usage); // custom functor
+ * printUsage(write, 1, usage); // write() to file descriptor 1
+ * printUsage(cout, usage); // an ostream&
+ * printUsage(fwrite, stdout, usage); // fwrite() to stdout
+ * ostringstream sstr;
+ * printUsage(sstr, usage); // an ostringstream&
+ *
+ * @endcode
+ *
+ * @par Notes:
+ * @li the @c write() method of a class that is to be passed as a temporary
+ * as @c MyWriter() is in the example, must be a @c const method, because
+ * temporary objects are passed as const reference. This only applies to
+ * temporary objects that are created and destroyed in the same statement.
+ * If you create an object like @c writer in the example, this restriction
+ * does not apply.
+ * @li a functor like @c MyWriteFunctor in the example must be passed as a pointer.
+ * This differs from the way functors are passed to e.g. the STL algorithms.
+ * @li All printUsage() templates are tiny wrappers around a shared non-template implementation.
+ * So there's no penalty for using different versions in the same program.
+ * @li printUsage() always interprets Descriptor::help as UTF-8 and always produces UTF-8-encoded
+ * output. If your system uses a different charset, you must do your own conversion. You
+ * may also need to change the font of the console to see non-ASCII characters properly.
+ * This is particularly true for Windows.
+ * @li @b Security @b warning: Do not insert untrusted strings (such as user-supplied arguments)
+ * into the usage. printUsage() has no protection against malicious UTF-8 sequences.
+ *
+ * @param prn The output method to use. See the examples above.
+ * @param usage the Descriptor[] array whose @c help texts will be formatted.
+ * @param width the maximum number of characters per output line. Note that this number is
+ * in actual characters, not bytes. printUsage() supports UTF-8 in @c help and will
+ * count multi-byte UTF-8 sequences properly. Asian wide characters are counted
+ * as 2 characters.
+ * @param last_column_min_percent (0-100) The minimum percentage of @c width that should be available
+ * for the last column (which typically contains the textual explanation of an option).
+ * If less space is available, the last column will be printed on its own line, indented
+ * according to @c last_column_own_line_max_percent.
+ * @param last_column_own_line_max_percent (0-100) If the last column is printed on its own line due to
+ * less than @c last_column_min_percent of the width being available, then only
+ * @c last_column_own_line_max_percent of the extra line(s) will be used for the
+ * last column's text. This ensures an indentation. See example below.
+ *
+ * @code
+ * // width=20, last_column_min_percent=50 (i.e. last col. min. width=10)
+ * --3456789 1234567890
+ * 1234567890
+ *
+ * // width=20, last_column_min_percent=75 (i.e. last col. min. width=15)
+ * // last_column_own_line_max_percent=75
+ * --3456789
+ * 123456789012345
+ * 67890
+ *
+ * // width=20, last_column_min_percent=75 (i.e. last col. min. width=15)
+ * // last_column_own_line_max_percent=33 (i.e. max. 5)
+ * --3456789
+ * 12345
+ * 67890
+ * 12345
+ * 67890
+ * @endcode
+ */
+template<typename OStream>
+void printUsage(OStream& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50,
+ int last_column_own_line_max_percent = 75)
+{
+ PrintUsageImplementation::OStreamWriter<OStream> write(prn);
+ PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
+}
+
+template<typename Function>
+void printUsage(Function* prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50,
+ int last_column_own_line_max_percent = 75)
+{
+ PrintUsageImplementation::FunctionWriter<Function> write(prn);
+ PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
+}
+
+template<typename Temporary>
+void printUsage(const Temporary& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50,
+ int last_column_own_line_max_percent = 75)
+{
+ PrintUsageImplementation::TemporaryWriter<Temporary> write(prn);
+ PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
+}
+
+template<typename Syscall>
+void printUsage(Syscall* prn, int fd, const Descriptor usage[], int width = 80, int last_column_min_percent = 50,
+ int last_column_own_line_max_percent = 75)
+{
+ PrintUsageImplementation::SyscallWriter<Syscall> write(prn, fd);
+ PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
+}
+
+template<typename Function, typename Stream>
+void printUsage(Function* prn, Stream* stream, const Descriptor usage[], int width = 80, int last_column_min_percent =
+ 50,
+ int last_column_own_line_max_percent = 75)
+{
+ PrintUsageImplementation::StreamWriter<Function, Stream> write(prn, stream);
+ PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent);
+}
+
+}
+// namespace option
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif /* OPTIONPARSER_H_ */
diff --git a/vendor/bandit/bandit/failure_formatters/default_failure_formatter.h b/vendor/bandit/bandit/failure_formatters/default_failure_formatter.h
new file mode 100644
index 00000000..48cc9021
--- /dev/null
+++ b/vendor/bandit/bandit/failure_formatters/default_failure_formatter.h
@@ -0,0 +1,30 @@
+#ifndef BANDIT_DEFAULT_FAILURE_FORMATTER_H
+#define BANDIT_DEFAULT_FAILURE_FORMATTER_H
+
+namespace bandit { namespace detail {
+
+ struct default_failure_formatter : public failure_formatter
+ {
+ std::string format(const assertion_exception& err) const
+ {
+ std::stringstream ss;
+ if(err.file_name().size())
+ {
+ ss << err.file_name();
+
+ if(err.line_number())
+ {
+ ss << ":" << err.line_number();
+ }
+
+ ss << ": ";
+ }
+
+ ss << err.what();
+
+ return ss.str();
+ }
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/failure_formatters/failure_formatter.h b/vendor/bandit/bandit/failure_formatters/failure_formatter.h
new file mode 100644
index 00000000..486624f0
--- /dev/null
+++ b/vendor/bandit/bandit/failure_formatters/failure_formatter.h
@@ -0,0 +1,13 @@
+#ifndef BANDIT_FAILURE_FORMATTER_H
+#define BANDIT_FAILURE_FORMATTER_H
+
+namespace bandit { namespace detail {
+
+ struct failure_formatter
+ {
+ virtual std::string format(const assertion_exception&) const = 0;
+ };
+ typedef std::unique_ptr<failure_formatter> failure_formatter_ptr;
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/failure_formatters/failure_formatters.h b/vendor/bandit/bandit/failure_formatters/failure_formatters.h
new file mode 100644
index 00000000..d0914651
--- /dev/null
+++ b/vendor/bandit/bandit/failure_formatters/failure_formatters.h
@@ -0,0 +1,16 @@
+#ifndef BANDIT_FAILURE_FORMATTERS
+#define BANDIT_FAILURE_FORMATTERS
+
+#include "failure_formatter.h"
+#include "default_failure_formatter.h"
+#include "visual_studio_failure_formatter.h"
+
+namespace bandit { namespace detail {
+ inline failure_formatter& registered_failure_formatter()
+ {
+ static default_failure_formatter formatter;
+ return formatter;
+ }
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/failure_formatters/visual_studio_failure_formatter.h b/vendor/bandit/bandit/failure_formatters/visual_studio_failure_formatter.h
new file mode 100644
index 00000000..6ff3fdeb
--- /dev/null
+++ b/vendor/bandit/bandit/failure_formatters/visual_studio_failure_formatter.h
@@ -0,0 +1,36 @@
+#ifndef BANDIT_VISUAL_STUDIO_FAILURE_FORMATTER_H
+#define BANDIT_VISUAL_STUDIO_FAILURE_FORMATTER_H
+
+namespace bandit { namespace detail {
+
+ struct visual_studio_failure_formatter : public failure_formatter
+ {
+ std::string format(const assertion_exception& err) const
+ {
+ std::stringstream ss;
+ if(err.file_name().size())
+ {
+ ss << err.file_name();
+
+ if(err.line_number())
+ {
+ ss << "(" << err.line_number() << ")";
+ }
+
+ ss << ": ";
+ }
+ else
+ {
+ ss << "bandit: ";
+ }
+
+ ss << err.what();
+
+ return ss.str();
+
+ }
+ };
+
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/grammar.h b/vendor/bandit/bandit/grammar.h
new file mode 100644
index 00000000..1f973344
--- /dev/null
+++ b/vendor/bandit/bandit/grammar.h
@@ -0,0 +1,185 @@
+#ifndef BANDIT_GRAMMAR_H
+#define BANDIT_GRAMMAR_H
+
+namespace bandit {
+
+ inline void describe(const char* desc, detail::voidfunc_t func,
+ detail::listener& listener, detail::contextstack_t& context_stack,
+ bool hard_skip = false)
+ {
+ listener.context_starting(desc);
+
+ context_stack.back()->execution_is_starting();
+
+ detail::bandit_context ctxt(desc, hard_skip);
+
+ context_stack.push_back(&ctxt);
+ try
+ {
+ func();
+ }
+ catch(const bandit::detail::test_run_error& error)
+ {
+ listener.test_run_error(desc, error);
+ }
+
+ context_stack.pop_back();
+
+ listener.context_ended(desc);
+ }
+
+ inline void describe(const char* desc, detail::voidfunc_t func)
+ {
+ describe(desc, func, detail::registered_listener(), detail::context_stack());
+ }
+
+ inline void describe_skip(const char* desc, detail::voidfunc_t func,
+ detail::listener& listener, detail::contextstack_t& context_stack)
+ {
+ bool skip = true;
+ describe(desc, func, listener, context_stack, skip);
+ }
+
+ inline void describe_skip(const char* desc, detail::voidfunc_t func)
+ {
+ describe_skip(desc, func, detail::registered_listener(),
+ detail::context_stack());
+ }
+
+ inline void xdescribe(const char* desc, detail::voidfunc_t func,
+ detail::listener& listener=detail::registered_listener(),
+ detail::contextstack_t& context_stack=detail::context_stack())
+ {
+ describe_skip(desc, func, listener, context_stack);
+ }
+
+ inline void before_each(detail::voidfunc_t func,
+ detail::contextstack_t& context_stack)
+ {
+ context_stack.back()->register_before_each(func);
+ }
+
+ inline void before_each(detail::voidfunc_t func)
+ {
+ before_each(func, detail::context_stack());
+ }
+
+ inline void after_each(detail::voidfunc_t func,
+ detail::contextstack_t& context_stack)
+ {
+ context_stack.back()->register_after_each(func);
+ }
+
+ inline void after_each(detail::voidfunc_t func)
+ {
+ after_each(func, detail::context_stack());
+ }
+
+ inline void it_skip(const char* desc, detail::voidfunc_t, detail::listener& listener)
+ {
+ listener.it_skip(desc);
+ }
+
+ inline void it_skip(const char* desc, detail::voidfunc_t func)
+ {
+ it_skip(desc, func, detail::registered_listener());
+ }
+
+ inline void xit(const char* desc, detail::voidfunc_t func, detail::listener& listener=detail::registered_listener())
+ {
+ it_skip(desc, func, listener);
+ }
+
+ inline void it(const char* desc, detail::voidfunc_t func, detail::listener& listener,
+ detail::contextstack_t& context_stack,
+ bandit::adapters::assertion_adapter& assertion_adapter,
+ detail::run_policy& run_policy)
+ {
+ if(!run_policy.should_run(desc, context_stack))
+ {
+ it_skip(desc, func, listener);
+ return;
+ }
+
+ listener.it_starting(desc);
+
+ context_stack.back()->execution_is_starting();
+
+ auto run_before_eaches = [&](){
+ for_each(context_stack.begin(), context_stack.end(), [](detail::context* ctxt){
+ ctxt->run_before_eaches();
+ });
+ };
+
+ auto run_after_eaches = [&](){
+ for_each(context_stack.begin(), context_stack.end(), [](detail::context* ctxt){
+ ctxt->run_after_eaches();
+ });
+ };
+
+ bool we_have_been_successful_so_far = false;
+ try
+ {
+ assertion_adapter.adapt_exceptions([&](){
+ run_before_eaches();
+
+ func();
+ we_have_been_successful_so_far = true;
+ });
+ }
+ catch(const bandit::detail::assertion_exception& ex)
+ {
+ listener.it_failed(desc, ex);
+ run_policy.encountered_failure();
+ }
+ catch(const std::exception& ex)
+ {
+ std::string err = std::string("exception: ") + ex.what();
+ listener.it_failed(desc, bandit::detail::assertion_exception(err));
+ run_policy.encountered_failure();
+ }
+ catch(...)
+ {
+ listener.it_unknown_error(desc);
+ run_policy.encountered_failure();
+ }
+
+ try
+ {
+ assertion_adapter.adapt_exceptions([&](){
+ run_after_eaches();
+
+ if(we_have_been_successful_so_far)
+ {
+ listener.it_succeeded(desc);
+ }
+ });
+ }
+ catch(const bandit::detail::assertion_exception& ex)
+ {
+ listener.it_failed(desc, ex);
+ run_policy.encountered_failure();
+ }
+ catch(const std::exception& ex)
+ {
+ std::string err = std::string("exception: ") + ex.what();
+ listener.it_failed(desc, bandit::detail::assertion_exception(err));
+ run_policy.encountered_failure();
+ }
+ catch(...)
+ {
+ listener.it_unknown_error(desc);
+ run_policy.encountered_failure();
+ }
+ }
+
+ inline void it(const char* desc, detail::voidfunc_t func)
+ {
+ it(desc, func, detail::registered_listener(), detail::context_stack(),
+ detail::registered_adapter(), detail::registered_run_policy());
+ }
+
+
+}
+
+#endif
diff --git a/vendor/bandit/bandit/listener.h b/vendor/bandit/bandit/listener.h
new file mode 100644
index 00000000..07501fcf
--- /dev/null
+++ b/vendor/bandit/bandit/listener.h
@@ -0,0 +1,27 @@
+#ifndef BANDIT_LISTENER_H
+#define BANDIT_LISTENER_H
+
+namespace bandit { namespace detail {
+ struct listener
+ {
+ virtual ~listener() {}
+
+ virtual void test_run_starting() = 0;
+ virtual void test_run_complete() = 0;
+
+ virtual void context_starting(const char* desc) = 0;
+ virtual void context_ended(const char* desc) = 0;
+ virtual void test_run_error(const char* desc, const test_run_error& error) = 0;
+
+ virtual void it_starting(const char* desc) = 0;
+ virtual void it_succeeded(const char* desc) = 0;
+ virtual void it_failed(const char* desc, const detail::assertion_exception& ex) = 0;
+ virtual void it_unknown_error(const char* desc) = 0;
+ virtual void it_skip(const char* desc) = 0;
+
+ virtual bool did_we_pass() const = 0;
+ };
+ typedef std::unique_ptr<listener> listener_ptr;
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/options.h b/vendor/bandit/bandit/options.h
new file mode 100644
index 00000000..493512cf
--- /dev/null
+++ b/vendor/bandit/bandit/options.h
@@ -0,0 +1,111 @@
+#ifndef BANDIT_OPTIONS_H
+#define BANDIT_OPTIONS_H
+
+namespace bandit { namespace detail {
+
+ // TODO: print any unknown options
+ // TODO: check for parser errors
+ struct options
+ {
+
+ options(int argc, char* argv[])
+ {
+ argc -= (argc>0); argv += (argc>0); // Skip program name (argv[0]) if present
+ option::Stats stats(usage(), argc, argv);
+ options_.resize(stats.options_max);
+ std::vector<option::Option> buffer(stats.buffer_max);
+ option::Parser parse(usage(), argc, argv, options_.data(), buffer.data());
+ parsed_ok_ = !parse.error();
+ }
+
+ bool help() const
+ {
+ return options_[HELP] != NULL;
+ }
+
+ void print_usage() const
+ {
+ option::printUsage(std::cout, usage());
+ }
+
+ bool version() const
+ {
+ return options_[VERSION] != NULL;
+ }
+
+ const char* reporter() const
+ {
+ return options_[REPORTER].arg;
+ }
+
+ bool no_color() const
+ {
+ return options_[NO_COLOR] != NULL;
+ }
+
+ typedef enum
+ {
+ FORMATTER_DEFAULT,
+ FORMATTER_VS,
+ FORMATTER_UNKNOWN
+ } formatters;
+
+ formatters formatter() const
+ {
+ std::string arg = options_[FORMATTER].arg ? options_[FORMATTER].arg : "";
+ if(arg == "vs")
+ {
+ return formatters::FORMATTER_VS;
+ }
+
+ return formatters::FORMATTER_DEFAULT;
+ }
+
+ const char* skip() const
+ {
+ return options_[SKIP].arg ? options_[SKIP].arg : "";
+ }
+
+ const char* only() const
+ {
+ return options_[ONLY].arg ? options_[ONLY].arg : "";
+ }
+
+ bool break_on_failure() const
+ {
+ return options_[BREAK_ON_FAILURE] != NULL;
+ }
+
+ private:
+ enum option_index { UNKNOWN, VERSION, HELP, REPORTER, NO_COLOR,
+ FORMATTER, SKIP, ONLY, BREAK_ON_FAILURE };
+
+ static const option::Descriptor* usage()
+ {
+ static const option::Descriptor usage[] =
+ {
+ {UNKNOWN, 0, "", "", option::Arg::None, "USAGE: <executable> [options]\n\n"
+ "Options:" },
+ {VERSION, 0, "", "version", option::Arg::None, " --version, \tPrint version of bandit"},
+ {HELP, 0, "", "help", option::Arg::None, " --help, \tPrint usage and exit."},
+ {REPORTER, 0, "", "reporter", option::Arg::Optional, " --reporter=<reporter>, \tSelect reporter (dots, singleline, xunit, info, spec)"},
+ {NO_COLOR, 0, "", "no-color", option::Arg::None, " --no-color, \tSuppress colors in output"},
+ {FORMATTER, 0, "", "formatter", option::Arg::Optional, " --formatter=<formatter>, \tSelect formatting of errors (default, vs)"},
+ {SKIP, 0, "", "skip", option::Arg::Optional, " --skip=<substring>, \tskip all 'describe' and 'it' containing substring"},
+ {ONLY, 0, "", "only", option::Arg::Optional, " --only=<substring>, \tonly run 'describe' and 'it' containing substring"},
+ {BREAK_ON_FAILURE, 0, "", "break-on-failure", option::Arg::Optional, " --break-on-failure, \tstop test run on first failing test"},
+ {0, 0, 0, 0, 0, 0}
+ };
+
+ return usage;
+ }
+
+ private:
+ std::vector<option::Option> options_;
+ bool parsed_ok_;
+
+ };
+
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/registration/registrar.h b/vendor/bandit/bandit/registration/registrar.h
new file mode 100644
index 00000000..d57a1f46
--- /dev/null
+++ b/vendor/bandit/bandit/registration/registrar.h
@@ -0,0 +1,25 @@
+#ifndef BANDIT_REGISTRAR_H
+#define BANDIT_REGISTRAR_H
+
+namespace bandit { namespace detail {
+
+ struct spec_registrar
+ {
+ spec_registrar( bandit::detail::voidfunc_t func)
+ {
+ bandit::detail::specs().push_back(func);
+ }
+ };
+
+}}
+
+#define go_bandit \
+ static bandit::detail::spec_registrar bandit_registrar
+
+#define SPEC_BEGIN(name) \
+go_bandit([]{
+
+#define SPEC_END \
+});
+
+#endif
diff --git a/vendor/bandit/bandit/registration/registration.h b/vendor/bandit/bandit/registration/registration.h
new file mode 100644
index 00000000..ad3f8b06
--- /dev/null
+++ b/vendor/bandit/bandit/registration/registration.h
@@ -0,0 +1,7 @@
+#ifndef BANDIT_REGISTRATION_H
+#define BANDIT_REGISTRATION_H
+
+#include <bandit/registration/spec_registry.h>
+#include <bandit/registration/registrar.h>
+
+#endif
diff --git a/vendor/bandit/bandit/registration/spec_registry.h b/vendor/bandit/bandit/registration/spec_registry.h
new file mode 100644
index 00000000..50c35402
--- /dev/null
+++ b/vendor/bandit/bandit/registration/spec_registry.h
@@ -0,0 +1,17 @@
+#ifndef BANDIT_SPEC_REGISTRY_H
+#define BANDIT_SPEC_REGISTRY_H
+
+namespace bandit {
+ namespace detail {
+ typedef std::list<voidfunc_t> spec_registry;
+
+ inline detail::spec_registry& specs()
+ {
+ static detail::spec_registry registry;
+ return registry;
+ }
+ }
+
+}
+
+#endif
diff --git a/vendor/bandit/bandit/reporters/colorizer.h b/vendor/bandit/bandit/reporters/colorizer.h
new file mode 100644
index 00000000..e8979eec
--- /dev/null
+++ b/vendor/bandit/bandit/reporters/colorizer.h
@@ -0,0 +1,141 @@
+#ifndef BANDIT_REPORTERS_COLORIZER_H
+#define BANDIT_REPORTERS_COLORIZER_H
+
+#ifdef _WIN32
+ #ifndef NOMINMAX
+ #define NOMINMAX
+ #endif
+
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+#endif
+
+namespace bandit { namespace detail {
+
+#ifdef _WIN32
+ struct colorizer
+ {
+ colorizer(bool colors_enabled = true)
+ : colors_enabled_(colors_enabled),
+ stdout_handle_(GetStdHandle(STD_OUTPUT_HANDLE))
+ {
+ original_color_ = get_console_color();
+ }
+
+ const char* green() const
+ {
+ if(colors_enabled_)
+ {
+ set_console_color(FOREGROUND_GREEN);
+ }
+ return "";
+ }
+
+ const char* yellow() const
+ {
+ if(colors_enabled_)
+ {
+ set_console_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
+ }
+ return "";
+ }
+
+ const char* blue() const
+ {
+ if(colors_enabled_)
+ {
+ set_console_color(FOREGROUND_BLUE);
+ }
+ return "";
+ }
+
+ const char* red() const
+ {
+ if(colors_enabled_)
+ {
+ set_console_color(FOREGROUND_RED);
+ }
+ return "";
+ }
+
+ const char* white() const
+ {
+ if(colors_enabled_)
+ {
+ set_console_color(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
+ }
+ return "";
+ }
+
+ const char* reset() const
+ {
+ if(colors_enabled_)
+ {
+ set_console_color(original_color_);
+ }
+ return "";
+ }
+
+ private:
+ WORD get_console_color() const
+ {
+ CONSOLE_SCREEN_BUFFER_INFO info{};
+ GetConsoleScreenBufferInfo(stdout_handle_, &info);
+ return info.wAttributes;
+ }
+
+ void set_console_color(WORD color) const
+ {
+ SetConsoleTextAttribute(stdout_handle_, color);
+ }
+
+ private:
+ bool colors_enabled_;
+ HANDLE stdout_handle_;
+ WORD original_color_;
+ };
+
+#else
+ struct colorizer
+ {
+ colorizer(bool colors_enabled = true)
+ : colors_enabled_(colors_enabled)
+ {}
+
+ const char* green() const
+ {
+ return colors_enabled_ ? "\033[1;32m" : "";
+ }
+
+ const char* yellow() const
+ {
+ return colors_enabled_ ? "\033[1;33m" : "";
+ }
+
+ const char* blue() const
+ {
+ return colors_enabled_ ? "\033[1;34m" : "";
+ }
+
+ const char* red() const
+ {
+ return colors_enabled_ ? "\033[1;31m" : "";
+ }
+
+ const char* white() const
+ {
+ return colors_enabled_ ? "\033[1;37m" : "";
+ }
+
+ const char* reset() const
+ {
+ return colors_enabled_ ? "\033[0m" : "";
+ }
+
+ private:
+ bool colors_enabled_;
+ };
+#endif
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/reporters/dots_reporter.h b/vendor/bandit/bandit/reporters/dots_reporter.h
new file mode 100644
index 00000000..3c5083fe
--- /dev/null
+++ b/vendor/bandit/bandit/reporters/dots_reporter.h
@@ -0,0 +1,69 @@
+#ifndef BANDIT_DOTS_REPORTER_H
+#define BANDIT_DOTS_REPORTER_H
+
+namespace bandit { namespace detail {
+
+ struct dots_reporter : public progress_reporter
+ {
+ dots_reporter(std::ostream& stm, const failure_formatter& failure_formatter,
+ const detail::colorizer& colorizer)
+ : progress_reporter(failure_formatter), stm_(stm), colorizer_(colorizer)
+ {}
+
+ dots_reporter(const failure_formatter& failure_formatter, const detail::colorizer& colorizer)
+ : progress_reporter(failure_formatter), stm_(std::cout), colorizer_(colorizer)
+ {}
+
+ dots_reporter& operator=(const dots_reporter&) { return *this; }
+
+ void test_run_complete()
+ {
+ progress_reporter::test_run_complete();
+
+ stm_ << std::endl;
+
+ test_run_summary summary(specs_run_, specs_failed_, specs_succeeded_, specs_skipped_, failures_,
+ test_run_errors_, colorizer_);
+ summary.write(stm_);
+ stm_.flush();
+ }
+
+ void test_run_error(const char* desc, const struct test_run_error& err)
+ {
+ progress_reporter::test_run_error(desc, err);
+
+ std::stringstream ss;
+ ss << std::endl;
+ ss << "Failed to run \"" << current_context_name() << "\": error \"" << err.what() << "\"" << std::endl;
+
+ test_run_errors_.push_back(ss.str());
+ }
+
+ void it_succeeded(const char* desc)
+ {
+ progress_reporter::it_succeeded(desc);
+ stm_ << colorizer_.green() << "." << colorizer_.reset();
+ stm_.flush();
+ }
+
+ void it_failed(const char* desc, const assertion_exception& ex)
+ {
+ progress_reporter::it_failed(desc, ex);
+ stm_ << colorizer_.red() << "F" << colorizer_.reset();
+ stm_.flush();
+ }
+
+ void it_unknown_error(const char* desc)
+ {
+ progress_reporter::it_unknown_error(desc);
+ stm_ << colorizer_.red() << "E" << colorizer_.reset();
+ stm_.flush();
+ }
+
+ private:
+ std::ostream& stm_;
+ const detail::colorizer& colorizer_;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/reporters/info_reporter.h b/vendor/bandit/bandit/reporters/info_reporter.h
new file mode 100644
index 00000000..f9b987d0
--- /dev/null
+++ b/vendor/bandit/bandit/reporters/info_reporter.h
@@ -0,0 +1,194 @@
+#ifndef BANDIT_INFO_REPORTER_H
+#define BANDIT_INFO_REPORTER_H
+
+namespace bandit {
+namespace detail {
+
+struct info_reporter : public progress_reporter
+{
+ info_reporter(std::ostream &stm, const failure_formatter &failure_formatter,
+ const detail::colorizer &colorizer)
+ : progress_reporter(failure_formatter)
+ , stm_(stm)
+ , colorizer_(colorizer)
+ , indentation_(0)
+ {}
+
+ info_reporter(const failure_formatter &failure_formatter, const detail::colorizer &colorizer)
+ : progress_reporter(failure_formatter)
+ , stm_(std::cout)
+ , colorizer_(colorizer)
+ , indentation_(0)
+ {}
+
+ info_reporter &operator=(const info_reporter &)
+ {
+ return *this;
+ }
+
+ void summary()
+ {
+ stm_
+ << colorizer_.white()
+ << "Tests run: " << specs_run_
+ << std::endl;
+ if (specs_skipped_ > 0) {
+ stm_
+ << colorizer_.yellow()
+ << "Skipped: " << specs_skipped_
+ << std::endl;
+ }
+ if (specs_succeeded_ > 0) {
+ stm_
+ << colorizer_.green()
+ << "Passed: " << specs_succeeded_
+ << std::endl;
+ }
+ if (specs_failed_ > 0) {
+ stm_
+ << colorizer_.red()
+ << "Failed: " << specs_failed_
+ << std::endl;
+ std::for_each(failures_.begin(), failures_.end(), [&](const std::string &failure) {
+ stm_
+ << colorizer_.white()
+ << " (*) "
+ << colorizer_.red()
+ << failure << std::endl;
+ });
+ }
+ if (test_run_errors_.size() > 0) {
+ stm_
+ << colorizer_.red()
+ << "Errors: " << test_run_errors_.size()
+ << std::endl;
+ std::for_each(test_run_errors_.begin(), test_run_errors_.end(), [&](const std::string &error) {
+ stm_
+ << colorizer_.white()
+ << " (*) "
+ << colorizer_.red()
+ << error << std::endl;
+ });
+ }
+ stm_
+ << colorizer_.reset()
+ << std::endl;
+ }
+
+ void test_run_complete()
+ {
+ progress_reporter::test_run_complete();
+ stm_ << std::endl;
+ summary();
+ stm_.flush();
+ }
+
+ void test_run_error(const char *desc, const struct test_run_error &err)
+ {
+ progress_reporter::test_run_error(desc, err);
+
+ std::stringstream ss;
+ ss << std::endl;
+ ss << "Failed to run \"" << current_context_name() << "\": error \"" << err.what() << "\"" << std::endl;
+
+ test_run_errors_.push_back(ss.str());
+ }
+
+ virtual void context_starting(const char *desc)
+ {
+ progress_reporter::context_starting(desc);
+
+ stm_
+ << indent()
+ << colorizer_.blue()
+ << "begin "
+ << colorizer_.white()
+ << desc
+ << colorizer_.reset()
+ << std::endl;
+ ++indentation_;
+ stm_.flush();
+
+ }
+
+ virtual void context_ended(const char *desc)
+ {
+ progress_reporter::context_ended(desc);
+ --indentation_;
+ stm_
+ << indent()
+ << colorizer_.blue()
+ << "end "
+ << colorizer_.reset()
+ << desc << std::endl;
+ }
+
+ virtual void it_starting(const char *desc)
+ {
+ progress_reporter::it_starting(desc);
+ stm_
+ << indent()
+ << colorizer_.yellow()
+ << "[ TEST ]"
+ << colorizer_.reset()
+ << " it " << desc;
+ ++indentation_;
+ stm_.flush();
+ }
+
+ virtual void it_succeeded(const char *desc)
+ {
+ progress_reporter::it_succeeded(desc);
+ --indentation_;
+ stm_
+ << "\r" << indent()
+ << colorizer_.green()
+ << "[ PASS ]"
+ << colorizer_.reset()
+ << " it " << desc
+ << std::endl;
+ stm_.flush();
+ }
+
+ virtual void it_failed(const char *desc, const assertion_exception &ex)
+ {
+ progress_reporter::it_failed(desc, ex);
+ --indentation_;
+ stm_
+ << "\r" << indent()
+ << colorizer_.red()
+ << "[ FAIL ]"
+ << colorizer_.reset()
+ << " it " << desc
+ << std::endl;
+ stm_.flush();
+ }
+
+ virtual void it_unknown_error(const char *desc)
+ {
+ progress_reporter::it_unknown_error(desc);
+ --indentation_;
+ stm_
+ << "\r" << indent()
+ << colorizer_.red()
+ << "-ERROR->"
+ << colorizer_.reset()
+ << " it " << desc
+ << std::endl;
+ stm_.flush();
+ }
+
+private:
+ std::string indent()
+ {
+ return std::string(2*indentation_, ' ');
+ }
+
+ std::ostream &stm_;
+ const detail::colorizer &colorizer_;
+ int indentation_;
+};
+}
+}
+
+#endif
diff --git a/vendor/bandit/bandit/reporters/progress_reporter.h b/vendor/bandit/bandit/reporters/progress_reporter.h
new file mode 100644
index 00000000..d9dc47bd
--- /dev/null
+++ b/vendor/bandit/bandit/reporters/progress_reporter.h
@@ -0,0 +1,116 @@
+#ifndef BANDIT_PROGRESS_REPORTER_H
+#define BANDIT_PROGRESS_REPORTER_H
+
+namespace bandit { namespace detail {
+
+ struct progress_reporter : public listener
+ {
+ progress_reporter(const detail::failure_formatter& failure_formatter)
+ : specs_run_(0), specs_succeeded_(0), specs_failed_(0), specs_skipped_(0),
+ failure_formatter_(failure_formatter)
+ {}
+
+ progress_reporter& operator=(const progress_reporter&) { return *this; }
+
+ virtual void test_run_starting()
+ {
+ specs_run_ = 0;
+ specs_succeeded_ = 0;
+ specs_failed_ = 0;
+ specs_skipped_ = 0;
+ failures_.clear();
+ contexts_.clear();
+ }
+
+ virtual void test_run_complete()
+ {
+ }
+
+ virtual void context_starting(const char* desc)
+ {
+ contexts_.push_back(std::string(desc));
+ }
+
+ virtual void context_ended(const char*)
+ {
+ contexts_.pop_back();
+ }
+
+ virtual void test_run_error(const char*, const struct test_run_error&)
+ {}
+
+ void it_starting(const char*)
+ {
+ specs_run_++;
+ }
+
+ void it_succeeded(const char*)
+ {
+ specs_succeeded_++;
+ }
+
+ void it_failed(const char* desc, const assertion_exception& ex)
+ {
+ specs_failed_++;
+
+ std::stringstream ss;
+ ss << std::endl;
+ ss << current_context_name() << " " << desc << ":" << std::endl;
+ ss << failure_formatter_.format(ex);
+
+ failures_.push_back(ss.str());
+ }
+
+ void it_unknown_error(const char* desc)
+ {
+ specs_failed_++;
+
+ std::stringstream ss;
+ ss << std::endl;
+ ss << current_context_name() << " " << desc << ":" << std::endl;
+ ss << "Unknown exception";
+ ss << std::endl;
+
+ failures_.push_back(ss.str());
+ }
+
+ void it_skip(const char* /* desc */)
+ {
+ specs_skipped_++;
+ }
+
+ bool did_we_pass() const
+ {
+ return specs_run_ > 0 && specs_failed_ == 0 && test_run_errors_.size() == 0;
+ }
+
+ protected:
+ std::string current_context_name()
+ {
+ std::string name;
+
+ std::for_each(contexts_.begin(), contexts_.end(), [&](const std::string context){
+ if(name.size() > 0)
+ {
+ name += " ";
+ }
+
+ name += context;
+ });
+
+ return name;
+ }
+
+ protected:
+ int specs_run_;
+ int specs_succeeded_;
+ int specs_failed_;
+ int specs_skipped_;
+ const detail::failure_formatter& failure_formatter_;
+ std::list<std::string> contexts_;
+ std::list<std::string> failures_;
+ std::list<std::string> test_run_errors_;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/reporters/reporters.h b/vendor/bandit/bandit/reporters/reporters.h
new file mode 100644
index 00000000..12179270
--- /dev/null
+++ b/vendor/bandit/bandit/reporters/reporters.h
@@ -0,0 +1,29 @@
+#ifndef BANDIT_REPORTERS_H
+#define BANDIT_REPORTERS_H
+
+#include <bandit/reporters/colorizer.h>
+#include <bandit/reporters/progress_reporter.h>
+#include <bandit/reporters/test_run_summary.h>
+#include <bandit/reporters/dots_reporter.h>
+#include <bandit/reporters/single_line_reporter.h>
+#include <bandit/reporters/xunit_reporter.h>
+#include <bandit/reporters/info_reporter.h>
+#include <bandit/reporters/spec_reporter.h>
+
+namespace bandit { namespace detail {
+
+ inline listener& registered_listener(listener* reporter = NULL)
+ {
+ static struct listener* reporter_;
+
+ if(reporter)
+ {
+ reporter_ = reporter;
+ }
+
+ return *reporter_;
+ }
+
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/reporters/single_line_reporter.h b/vendor/bandit/bandit/reporters/single_line_reporter.h
new file mode 100644
index 00000000..08d1c08d
--- /dev/null
+++ b/vendor/bandit/bandit/reporters/single_line_reporter.h
@@ -0,0 +1,86 @@
+#ifndef BANDIT_REPORTERS_SINGLE_LINE_REPORTER_H
+#define BANDIT_REPORTERS_SINGLE_LINE_REPORTER_H
+
+namespace bandit { namespace detail {
+
+ struct single_line_reporter : public progress_reporter
+ {
+ single_line_reporter(std::ostream& stm, const failure_formatter& failure_formatter,
+ const detail::colorizer& colorizer)
+ : progress_reporter(failure_formatter), stm_(stm), colorizer_(colorizer)
+ {}
+
+ single_line_reporter(const failure_formatter& failure_formatter,
+ const detail::colorizer& colorizer)
+ : progress_reporter(failure_formatter), stm_(std::cout), colorizer_(colorizer)
+ {}
+
+ single_line_reporter& operator=(const single_line_reporter&) { return *this; }
+
+ void test_run_complete()
+ {
+ progress_reporter::test_run_complete();
+
+ stm_ << std::endl;
+
+ test_run_summary summary(specs_run_, specs_failed_, specs_succeeded_, specs_skipped_, failures_,
+ test_run_errors_, colorizer_);
+ summary.write(stm_);
+ }
+
+ void test_run_error(const char* desc, const struct test_run_error& err)
+ {
+ progress_reporter::test_run_error(desc, err);
+
+ std::stringstream ss;
+ ss << std::endl;
+ ss << "Failed to run \"" << current_context_name() << "\": error \"" << err.what() << "\"" << std::endl;
+
+ test_run_errors_.push_back(ss.str());
+ }
+
+ void it_starting(const char* desc)
+ {
+ print_status_line();
+ progress_reporter::it_starting(desc);
+ }
+
+ void it_succeeded(const char* desc)
+ {
+ progress_reporter::it_succeeded(desc);
+ print_status_line();
+ }
+
+ void it_failed(const char* desc, const assertion_exception& ex)
+ {
+ progress_reporter::it_failed(desc, ex);
+ print_status_line();
+ }
+
+ void it_unknown_error(const char* desc)
+ {
+ progress_reporter::it_unknown_error(desc);
+ print_status_line();
+ }
+
+ private:
+ void print_status_line()
+ {
+ stm_ << '\r';
+ stm_ << "Executed " << specs_run_ << " tests.";
+
+ if(specs_failed_)
+ {
+ stm_ << " " << specs_succeeded_ << " succeeded. " << colorizer_.red() << specs_failed_ <<
+ " failed." << colorizer_.reset();
+ }
+ stm_.flush();
+ }
+
+ private:
+ std::ostream& stm_;
+ const detail::colorizer& colorizer_;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/reporters/spec_reporter.h b/vendor/bandit/bandit/reporters/spec_reporter.h
new file mode 100644
index 00000000..6d63bfb0
--- /dev/null
+++ b/vendor/bandit/bandit/reporters/spec_reporter.h
@@ -0,0 +1,126 @@
+#ifndef BANDIT_SPEC_REPORTER_H
+#define BANDIT_SPEC_REPORTER_H
+
+namespace bandit { namespace detail {
+
+ struct spec_reporter : public progress_reporter
+ {
+ spec_reporter(std::ostream& stm, const failure_formatter& failure_formatter,
+ const detail::colorizer& colorizer)
+ : progress_reporter(failure_formatter), stm_(stm), colorizer_(colorizer), indentation_(0)
+ {}
+
+ spec_reporter(const failure_formatter& failure_formatter, const detail::colorizer& colorizer)
+ : progress_reporter(failure_formatter), stm_(std::cout), colorizer_(colorizer), indentation_(0)
+ {}
+
+ spec_reporter& operator=(const spec_reporter&) { return *this; }
+
+ void test_run_complete()
+ {
+ progress_reporter::test_run_complete();
+
+ stm_ << std::endl;
+
+ test_run_summary summary(specs_run_, specs_failed_, specs_succeeded_, specs_skipped_, failures_,
+ test_run_errors_, colorizer_);
+ summary.write(stm_);
+ stm_.flush();
+ }
+
+ void test_run_error(const char* desc, const struct test_run_error& err)
+ {
+ progress_reporter::test_run_error(desc, err);
+
+ std::stringstream ss;
+ ss << std::endl;
+ ss << "Failed to run \"" << current_context_name() << "\": error \"" << err.what() << "\"" << std::endl;
+
+ test_run_errors_.push_back(ss.str());
+ }
+
+ virtual void context_starting(const char* desc)
+ {
+ progress_reporter::context_starting(desc);
+
+ stm_ << indent();
+ stm_ << "describe " << desc << std::endl;
+ increase_indent();
+ stm_.flush();
+
+ }
+
+ virtual void context_ended(const char* desc)
+ {
+ progress_reporter::context_ended(desc);
+ decrease_indent();
+ }
+
+ virtual void it_starting(const char* desc)
+ {
+ progress_reporter::it_starting(desc);
+ stm_ << indent() << "- it " << desc << " ... ";
+ stm_.flush();
+ }
+
+ virtual void it_succeeded(const char* desc)
+ {
+ progress_reporter::it_succeeded(desc);
+ stm_ << colorizer_.green();
+ stm_ << "OK";
+ stm_ << colorizer_.reset();
+ stm_ << std::endl;
+ stm_.flush();
+ }
+
+ virtual void it_failed(const char* desc, const assertion_exception& ex)
+ {
+ progress_reporter::it_failed(desc, ex);
+ stm_ << colorizer_.red();
+ stm_ << "FAILED";
+ stm_ << colorizer_.reset();
+ stm_ << std::endl;
+ stm_.flush();
+ }
+
+ virtual void it_unknown_error(const char* desc)
+ {
+ progress_reporter::it_unknown_error(desc);
+ stm_ << colorizer_.red();
+ stm_ << "ERROR";
+ stm_ << colorizer_.reset();
+ stm_ << std::endl;
+ stm_.flush();
+ }
+
+ virtual void it_skip(const char* desc)
+ {
+ progress_reporter::it_skip(desc);
+ stm_ << indent() << "- it " << desc << " ... SKIPPED" << std::endl;
+ stm_.flush();
+ }
+
+ private:
+ void increase_indent()
+ {
+ indentation_++;
+ }
+
+ void decrease_indent()
+ {
+ indentation_--;
+ }
+
+ std::string indent()
+ {
+ return std::string(indentation_, '\t');
+ }
+
+ private:
+ std::ostream& stm_;
+ const detail::colorizer& colorizer_;
+ int indentation_;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/reporters/test_run_summary.h b/vendor/bandit/bandit/reporters/test_run_summary.h
new file mode 100644
index 00000000..aa1d4a59
--- /dev/null
+++ b/vendor/bandit/bandit/reporters/test_run_summary.h
@@ -0,0 +1,90 @@
+#ifndef BANDIT_TEST_RUN_SUMMARY_H
+#define BANDIT_TEST_RUN_SUMMARY_H
+
+namespace bandit { namespace detail {
+
+ struct test_run_summary
+ {
+ test_run_summary(int specs_run, int specs_failed, int specs_succeeded, int specs_skipped,
+ const std::list<std::string>& failures, const std::list<std::string>& test_run_errors,
+ const detail::colorizer& colorizer)
+ : specs_run_(specs_run), specs_succeeded_(specs_succeeded), specs_failed_(specs_failed),
+ specs_skipped_(specs_skipped), failures_(failures), test_run_errors_(test_run_errors),
+ colorizer_(colorizer)
+ {}
+
+ test_run_summary& operator=(const test_run_summary&) { return *this; }
+
+ void write(std::ostream& stm)
+ {
+ if(specs_run_ == 0 && test_run_errors_.size() == 0)
+ {
+ stm << colorizer_.red();
+ stm << "Could not find any tests.";
+ stm << colorizer_.reset();
+ stm << std::endl;
+ return;
+ }
+
+ if(specs_failed_ == 0 && test_run_errors_.size() == 0)
+ {
+ stm << colorizer_.green();
+ stm << "Success!";
+ stm << colorizer_.reset();
+ stm << std::endl;
+ }
+
+ if(test_run_errors_.size() > 0)
+ {
+ std::for_each(test_run_errors_.begin(), test_run_errors_.end(),
+ [&](const std::string& error){
+ stm << error << std::endl;
+ });
+ }
+
+
+ if(specs_failed_ > 0)
+ {
+ stm << colorizer_.red();
+ stm << "There were failures!";
+ stm << colorizer_.reset() << std::endl;
+ std::for_each(failures_.begin(), failures_.end(),
+ [&](const std::string& failure) {
+ stm << failure << std::endl;
+ });
+ stm << std::endl;
+ }
+
+ stm << "Test run complete. " << specs_run_ << " tests run. " << specs_succeeded_ <<
+ " succeeded.";
+
+ if(specs_skipped_ > 0)
+ {
+ stm << " " << specs_skipped_ << " skipped.";
+ }
+
+ if(specs_failed_ > 0)
+ {
+ stm << " " << specs_failed_ << " failed.";
+ }
+
+ if(test_run_errors_.size() > 0)
+ {
+ stm << " " << test_run_errors_.size() << " test run errors.";
+ }
+
+ stm << std::endl;
+ }
+
+ private:
+ int specs_run_;
+ int specs_succeeded_;
+ int specs_failed_;
+ int specs_skipped_;
+ std::list<std::string> failures_;
+ std::list<std::string> test_run_errors_;
+ const detail::colorizer& colorizer_;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/reporters/xunit_reporter.h b/vendor/bandit/bandit/reporters/xunit_reporter.h
new file mode 100644
index 00000000..15f6ea29
--- /dev/null
+++ b/vendor/bandit/bandit/reporters/xunit_reporter.h
@@ -0,0 +1,109 @@
+#ifndef BANDIT_REPORTERS_XUNIT_REPORTER_H
+#define BANDIT_REPORTERS_XUNIT_REPORTER_H
+
+namespace bandit { namespace detail {
+
+ struct xunit_reporter : public progress_reporter
+ {
+ xunit_reporter(std::ostream& stm, const failure_formatter& formatter)
+ : progress_reporter(formatter), stm_(stm)
+ {
+ }
+
+ xunit_reporter(const failure_formatter& formatter)
+ : progress_reporter(formatter), stm_(std::cout)
+ {
+ }
+
+ void it_starting(const char* desc)
+ {
+ progress_reporter::it_starting(desc);
+ work_stm_ << "\t<testcase classname=\"" << escape(current_context_name()) << "\" ";
+ work_stm_ << "name=\"" << escape(desc) << "\" time=\"0\">\n";
+ }
+
+ void it_succeeded(const char* desc)
+ {
+ progress_reporter::it_succeeded(desc);
+ work_stm_ << "\t</testcase>\n";
+ }
+
+ void it_failed(const char* desc, const assertion_exception& ex)
+ {
+ progress_reporter::it_failed(desc, ex);
+ work_stm_ << "\t\t<failure message=\"" << escape(failure_formatter_.format(ex)) << "\" />\n";
+ work_stm_ << "\t</testcase>\n";
+ }
+
+ void it_unknown_error(const char* desc)
+ {
+ progress_reporter::it_unknown_error(desc);
+ work_stm_ << "\t\t<failure message=\"Unknown exception\" />\n";
+ work_stm_ << "\t</testcase>\n";
+ }
+
+ void it_skip(const char* desc)
+ {
+ progress_reporter::it_skip(desc);
+ work_stm_ << "\t<testcase classname=\"" << escape(current_context_name()) << "\" ";
+ work_stm_ << "name=\"" << escape(desc) << "\" time=\"0\">\n";
+ work_stm_ << "\t\t<skipped />\n";
+ work_stm_ << "\t</testcase>\n";
+ }
+
+ void test_run_complete()
+ {
+ stm_ << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
+ stm_ << "<testsuite name=\"bandit\" tests=\"" << specs_run_ << "\" errors=\"0\" failures=\""
+ << specs_failed_ << "\"";
+
+ if(specs_skipped_ > 0)
+ {
+ stm_ << " skipped=\"" << specs_skipped_ << "\"";
+ }
+
+ stm_ << ">\n";
+
+ stm_ << work_stm_.str();
+
+ stm_ << "</testsuite>\n";
+ }
+
+ private:
+ std::string escape(const std::string& str)
+ {
+ std::stringstream stm;
+
+ std::for_each(str.begin(), str.end(), [&](char c){
+ switch(c)
+ {
+ case '&':
+ stm << "&amp;";
+ break;
+ case '<':
+ stm << "&lt;";
+ break;
+ case '>':
+ stm << "&gt;";
+ break;
+ case '\\':
+ stm << "&apos;";
+ break;
+ case '\"':
+ stm << "&quot;";
+ break;
+ default:
+ stm << c;
+ }
+ });
+
+ return stm.str();
+ }
+
+ private:
+ std::ostream& stm_;
+ std::stringstream work_stm_;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/run_policies/always_run_policy.h b/vendor/bandit/bandit/run_policies/always_run_policy.h
new file mode 100644
index 00000000..29bdc627
--- /dev/null
+++ b/vendor/bandit/bandit/run_policies/always_run_policy.h
@@ -0,0 +1,16 @@
+#ifndef BANDIT_ALWAYS_RUN_POLICY_H
+#define BANDIT_ALWAYS_RUN_POLICY_H
+
+namespace bandit { namespace detail {
+
+ struct always_run_policy : public run_policy
+ {
+ bool should_run(const char* /* it_name */, const contextstack_t& /* contexts */) const
+ {
+ return true;
+ }
+ };
+
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/run_policies/bandit_run_policy.h b/vendor/bandit/bandit/run_policies/bandit_run_policy.h
new file mode 100644
index 00000000..4a5c0808
--- /dev/null
+++ b/vendor/bandit/bandit/run_policies/bandit_run_policy.h
@@ -0,0 +1,161 @@
+#ifndef BANDIT_BANDIT_RUN_POLICY_H
+#define BANDIT_BANDIT_RUN_POLICY_H
+
+namespace bandit { namespace detail {
+
+ struct bandit_run_policy : public run_policy
+ {
+ bandit_run_policy(const char* skip_pattern, const char* only_pattern, bool break_on_failure)
+ : run_policy(), skip_pattern_(skip_pattern), only_pattern_(only_pattern), break_on_failure_(break_on_failure)
+ {}
+
+ bool should_run(const char* it_name, const contextstack_t& contexts) const
+ {
+ if(break_on_failure_ && has_encountered_failure())
+ {
+ return false;
+ }
+
+ //
+ // Never run if a context has been marked as skip
+ // using 'describe_skip'
+ //
+ if(has_context_with_hard_skip(contexts))
+ {
+ return false;
+ }
+
+ //
+ // Always run if no patterns have been specifed
+ //
+ if(!has_skip_pattern() && !has_only_pattern())
+ {
+ return true;
+ }
+
+ if(has_only_pattern() && !has_skip_pattern())
+ {
+ return context_matches_only_pattern(contexts)
+ || matches_only_pattern(it_name);
+ }
+
+ if(has_skip_pattern() && !has_only_pattern())
+ {
+ bool skip = context_matches_skip_pattern(contexts) ||
+ matches_skip_pattern(it_name);
+ return !skip;
+ }
+
+ //
+ // If we've come this far, both 'skip' and 'only'
+ // have been specified.
+ //
+ // If our contexts match 'only' we're still good
+ // regardless of whether there's a 'skip' somewhere
+ // in the context stack as well.
+ if(context_matches_only_pattern(contexts))
+ {
+ //
+ // We can still mark the current 'it' as 'skip'
+ // and ignore it. We check that here.
+ //
+ return !matches_skip_pattern(it_name);
+ }
+
+ //
+ // If we've gotten this far, the context matches 'skip'
+ // We can still run this spec if it is specifically marked
+ // as 'only'.
+ //
+ return matches_only_pattern(it_name);
+ }
+
+ private:
+ bool has_context_with_hard_skip(const contextstack_t& contexts) const
+ {
+ contextstack_t::const_iterator it;
+ for(it = contexts.begin(); it != contexts.end(); it++)
+ {
+ if((*it)->hard_skip())
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool has_only_pattern() const
+ {
+ return only_pattern_.size() > 0;
+ }
+
+ bool has_skip_pattern() const
+ {
+ return skip_pattern_.size() > 0;
+ }
+
+ bool context_matches_only_pattern(const contextstack_t& contexts) const
+ {
+ contextstack_t::const_iterator it;
+ for(it = contexts.begin(); it != contexts.end(); it++)
+ {
+ if(matches_only_pattern((*it)->name()))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool context_matches_skip_pattern(const contextstack_t& contexts) const
+ {
+ contextstack_t::const_iterator it;
+ for(it = contexts.begin(); it != contexts.end(); it++)
+ {
+ if(matches_skip_pattern((*it)->name()))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool matches_only_pattern(const char* name) const
+ {
+ std::string n(name);
+ return matches_only_pattern(n);
+ }
+
+ bool matches_only_pattern(const std::string& name) const
+ {
+ return matches_pattern(name, only_pattern_);
+ }
+
+ bool matches_skip_pattern(const char* name) const
+ {
+ std::string n(name);
+ return matches_skip_pattern(n);
+ }
+
+ bool matches_skip_pattern(const std::string& name) const
+ {
+ return matches_pattern(name, skip_pattern_);
+ }
+
+ bool matches_pattern(const std::string& name, const std::string& pattern) const
+ {
+ return name.find(pattern) != std::string::npos;
+ }
+
+ private:
+ std::string skip_pattern_;
+ std::string only_pattern_;
+ bool break_on_failure_;
+ };
+
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/run_policies/never_run_policy.h b/vendor/bandit/bandit/run_policies/never_run_policy.h
new file mode 100644
index 00000000..003fd889
--- /dev/null
+++ b/vendor/bandit/bandit/run_policies/never_run_policy.h
@@ -0,0 +1,14 @@
+#ifndef BANDIT_NEVER_RUN_POLICY_H
+#define BANDIT_NEVER_RUN_POLICY_H
+
+namespace bandit { namespace detail {
+
+ struct never_run_policy : public run_policy
+ {
+ bool should_run(const char* /* it_name */, const contextstack_t& /* contexts */) const
+ {
+ return false;
+ }
+ };
+}}
+#endif
diff --git a/vendor/bandit/bandit/run_policies/run_policies.h b/vendor/bandit/bandit/run_policies/run_policies.h
new file mode 100644
index 00000000..88d8dbb5
--- /dev/null
+++ b/vendor/bandit/bandit/run_policies/run_policies.h
@@ -0,0 +1,9 @@
+#ifndef BANDIT_RUN_POLICIES_H
+#define BANDIT_RUN_POLICIES_H
+
+#include "run_policy.h"
+#include "always_run_policy.h"
+#include "never_run_policy.h"
+#include "bandit_run_policy.h"
+
+#endif
diff --git a/vendor/bandit/bandit/run_policies/run_policy.h b/vendor/bandit/bandit/run_policies/run_policy.h
new file mode 100644
index 00000000..4a6e8e1d
--- /dev/null
+++ b/vendor/bandit/bandit/run_policies/run_policy.h
@@ -0,0 +1,44 @@
+#ifndef BANDIT_RUN_POLICY_H
+#define BANDIT_RUN_POLICY_H
+
+namespace bandit { namespace detail {
+
+ struct run_policy
+ {
+ run_policy() : encountered_failure_(false) {}
+ run_policy(const run_policy& other) = default;
+ run_policy(run_policy&&) = default;
+ virtual ~run_policy() {}
+
+ virtual bool should_run(const char* it_name, const contextstack_t& contexts) const = 0;
+
+ virtual void encountered_failure()
+ {
+ encountered_failure_ = true;
+ }
+
+ virtual bool has_encountered_failure() const
+ {
+ return encountered_failure_;
+ }
+
+ private:
+ bool encountered_failure_;
+ };
+ typedef std::unique_ptr<run_policy> run_policy_ptr;
+
+ inline run_policy& registered_run_policy(run_policy* policy = NULL)
+ {
+ static struct run_policy* policy_;
+
+ if(policy)
+ {
+ policy_ = policy;
+ }
+
+ return *policy_;
+ }
+
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/runner.h b/vendor/bandit/bandit/runner.h
new file mode 100644
index 00000000..1f8dcd11
--- /dev/null
+++ b/vendor/bandit/bandit/runner.h
@@ -0,0 +1,103 @@
+#ifndef BANDIT_RUNNER_H
+#define BANDIT_RUNNER_H
+
+namespace bandit {
+
+ namespace detail {
+
+ inline run_policy_ptr create_run_policy(const options& opt)
+ {
+ return run_policy_ptr(new bandit_run_policy(opt.skip(), opt.only(), opt.break_on_failure()));
+ }
+
+ inline listener_ptr create_reporter(const options& opt,
+ const failure_formatter* formatter, const colorizer& colorizer)
+ {
+ std::string name(opt.reporter() ? opt.reporter() : "");
+
+ if(name == "singleline")
+ {
+ return std::unique_ptr<detail::listener>(new single_line_reporter(*formatter, colorizer));
+ }
+
+ if(name == "xunit")
+ {
+ return std::unique_ptr<detail::listener>(new xunit_reporter(*formatter));
+ }
+
+ if(name == "info")
+ {
+ return std::unique_ptr<detail::listener>(new info_reporter(*formatter, colorizer));
+ }
+
+ if(name == "spec")
+ {
+ return std::unique_ptr<detail::listener>(new spec_reporter(*formatter, colorizer));
+ }
+
+ return std::unique_ptr<detail::listener>(new dots_reporter(*formatter, colorizer));
+ }
+
+ typedef std::function<listener_ptr (const std::string&, const failure_formatter*)> reporter_factory_fn;
+ typedef std::function<detail::listener* (detail::listener*)> register_reporter_fn;
+
+ inline failure_formatter_ptr create_formatter(const options& opt)
+ {
+ if(opt.formatter() == options::formatters::FORMATTER_VS)
+ {
+ return failure_formatter_ptr(new visual_studio_failure_formatter());
+ }
+
+ return failure_formatter_ptr(new default_failure_formatter());
+ }
+ }
+
+ inline int run(const detail::options& opt, const detail::spec_registry& specs,
+ detail::contextstack_t& context_stack, detail::listener& listener)
+ {
+ if(opt.help())
+ {
+ opt.print_usage();
+ return 0;
+ }
+
+ if(opt.version())
+ {
+ std::cout << "bandit version " << BANDIT_VERSION << std::endl;
+ return 0;
+ }
+
+ auto call_func = [](const detail::voidfunc_t& func) {
+ func();
+ };
+
+ listener.test_run_starting();
+
+ bool hard_skip = false;
+ detail::bandit_context global_context("", hard_skip);
+ context_stack.push_back(&global_context);
+
+ for_each(specs.begin(), specs.end(), call_func);
+
+ listener.test_run_complete();
+
+ return listener.did_we_pass() ? 0 : 1;
+ }
+
+ inline int run(int argc, char* argv[])
+ {
+ detail::options opt(argc, argv);
+ detail::failure_formatter_ptr formatter(create_formatter(opt));
+ bandit::detail::colorizer colorizer(!opt.no_color());
+ detail::listener_ptr reporter(create_reporter(opt, formatter.get(), colorizer));
+
+ detail::registered_listener(reporter.get());
+
+ detail::run_policy_ptr run_policy = create_run_policy(opt);
+ registered_run_policy(run_policy.get());
+
+ return run(opt, detail::specs(), detail::context_stack(), *reporter);
+ }
+}
+
+#endif
diff --git a/vendor/bandit/bandit/skip_policies/always_include_policy.h b/vendor/bandit/bandit/skip_policies/always_include_policy.h
new file mode 100644
index 00000000..2e978308
--- /dev/null
+++ b/vendor/bandit/bandit/skip_policies/always_include_policy.h
@@ -0,0 +1,16 @@
+#ifndef BANDIT_ALWAYS_INCLUDE_POLICY_H
+#define BANDIT_ALWAYS_INCLUDE_POLICY_H
+
+namespace bandit { namespace detail {
+
+ struct always_include_policy : public skip_policy
+ {
+ bool should_skip(const char*) const
+ {
+ return false;
+ }
+ };
+
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/skip_policies/always_skip_policy.h b/vendor/bandit/bandit/skip_policies/always_skip_policy.h
new file mode 100644
index 00000000..9d6a4bfc
--- /dev/null
+++ b/vendor/bandit/bandit/skip_policies/always_skip_policy.h
@@ -0,0 +1,15 @@
+#ifndef BANDIT_ALWAYS_SKIP_POLICY_H
+#define BANDIT_ALWAYS_SKIP_POLICY_H
+
+namespace bandit { namespace detail {
+
+ struct always_skip_policy : public skip_policy
+ {
+ bool should_skip(const char*) const
+ {
+ return true;
+ }
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/skip_policies/name_contains_skip_policy.h b/vendor/bandit/bandit/skip_policies/name_contains_skip_policy.h
new file mode 100644
index 00000000..da727c3d
--- /dev/null
+++ b/vendor/bandit/bandit/skip_policies/name_contains_skip_policy.h
@@ -0,0 +1,28 @@
+#ifndef BANDIT_NAME_CONTAINS_SKIP_POLICY_H
+#define BANDIT_NAME_CONTAINS_SKIP_POLICY_H
+
+namespace bandit { namespace detail {
+ struct name_contains_skip_policy : public skip_policy
+ {
+ name_contains_skip_policy(const char* pattern)
+ : pattern_(pattern)
+ {}
+
+ bool should_skip(const char* name) const
+ {
+ if(pattern_.size() == 0)
+ {
+ return false;
+ }
+
+ std::string n(name);
+ bool skip = n.find(pattern_) != std::string::npos;
+ return skip;
+ }
+
+ private:
+ const std::string pattern_;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/bandit/skip_policies/skip_policies.h b/vendor/bandit/bandit/skip_policies/skip_policies.h
new file mode 100644
index 00000000..f3fbfbfd
--- /dev/null
+++ b/vendor/bandit/bandit/skip_policies/skip_policies.h
@@ -0,0 +1,9 @@
+#ifndef BANDIT_SKIP_POLICIES
+#define BANDIT_SKIP_POLICIES
+
+#include <bandit/skip_policies/skip_policy.h>
+#include <bandit/skip_policies/always_include_policy.h>
+#include <bandit/skip_policies/always_skip_policy.h>
+#include <bandit/skip_policies/name_contains_skip_policy.h>
+
+#endif
diff --git a/vendor/bandit/bandit/skip_policies/skip_policy.h b/vendor/bandit/bandit/skip_policies/skip_policy.h
new file mode 100644
index 00000000..ca606dfb
--- /dev/null
+++ b/vendor/bandit/bandit/skip_policies/skip_policy.h
@@ -0,0 +1,29 @@
+#ifndef BANDIT_SKIP_POLICY_H
+#define BANDIT_SKIP_POLICY_H
+
+namespace bandit {
+
+ struct skip_policy
+ {
+ virtual bool should_skip(const char* name) const = 0;
+ };
+ typedef std::unique_ptr<skip_policy> skip_policy_ptr;
+
+ namespace detail {
+
+ inline skip_policy& registered_skip_policy(skip_policy* policy = NULL)
+ {
+ static struct skip_policy* policy_;
+
+ if(policy)
+ {
+ policy_ = policy;
+ }
+
+ return *policy_;
+ }
+ }
+
+}
+
+#endif
diff --git a/vendor/bandit/bandit/test_run_error.h b/vendor/bandit/bandit/test_run_error.h
new file mode 100644
index 00000000..307ef3fe
--- /dev/null
+++ b/vendor/bandit/bandit/test_run_error.h
@@ -0,0 +1,12 @@
+#ifndef BANDIT_TEST_RUN_ERROR
+#define BANDIT_TEST_RUN_ERROR
+
+namespace bandit { namespace detail {
+
+ struct test_run_error : public std::runtime_error
+ {
+ test_run_error(const char* message) : std::runtime_error(message) {}
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/cmake/cotire.cmake b/vendor/bandit/cmake/cotire.cmake
new file mode 100644
index 00000000..a6e3141c
--- /dev/null
+++ b/vendor/bandit/cmake/cotire.cmake
@@ -0,0 +1,3185 @@
+# - cotire (compile time reducer)
+#
+# See the cotire manual for usage hints.
+#
+#=============================================================================
+# Copyright 2012-2013 Sascha Kratky
+#
+# 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 AUTHORS OR COPYRIGHT
+# HOLDERS 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.
+#=============================================================================
+
+if(__COTIRE_INCLUDED)
+ return()
+endif()
+set(__COTIRE_INCLUDED TRUE)
+
+# call cmake_minimum_required, but prevent modification of the CMake policy stack in include mode
+# cmake_minimum_required also sets the policy version as a side effect, which we have to avoid
+if (NOT CMAKE_SCRIPT_MODE_FILE)
+ cmake_policy(PUSH)
+endif()
+# we need the CMake variables CMAKE_SCRIPT_MODE_FILE and CMAKE_ARGV available since 2.8.5
+# we need APPEND_STRING option for set_property available since 2.8.6
+cmake_minimum_required(VERSION 2.8.6)
+if (NOT CMAKE_SCRIPT_MODE_FILE)
+ cmake_policy(POP)
+endif()
+
+set (COTIRE_CMAKE_MODULE_FILE "${CMAKE_CURRENT_LIST_FILE}")
+set (COTIRE_CMAKE_MODULE_VERSION "1.4.1")
+
+include(CMakeParseArguments)
+include(ProcessorCount)
+
+function (cotire_determine_compiler_version _language _versionPrefix)
+ if (NOT ${_versionPrefix}_VERSION)
+ # use CMake's predefined compiler version variable (available since CMake 2.8.8)
+ if (DEFINED CMAKE_${_language}_COMPILER_VERSION)
+ set (${_versionPrefix}_VERSION "${CMAKE_${_language}_COMPILER_VERSION}")
+ elseif (WIN32)
+ # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared
+ unset (ENV{VS_UNICODE_OUTPUT})
+ string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1)
+ execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1}
+ ERROR_VARIABLE _versionLine OUTPUT_QUIET TIMEOUT 10)
+ string (REGEX REPLACE ".*Version *([0-9]+(\\.[0-9]+)*).*" "\\1" ${_versionPrefix}_VERSION "${_versionLine}")
+ else()
+ # assume GCC like command line interface
+ string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1)
+ execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1} "-dumpversion"
+ OUTPUT_VARIABLE ${_versionPrefix}_VERSION
+ RESULT_VARIABLE _result
+ OUTPUT_STRIP_TRAILING_WHITESPACE TIMEOUT 10)
+ if (_result)
+ set (${_versionPrefix}_VERSION "")
+ endif()
+ endif()
+ if (${_versionPrefix}_VERSION)
+ set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" CACHE INTERNAL "${_language} compiler version")
+ endif()
+ set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" PARENT_SCOPE)
+ if (COTIRE_DEBUG)
+ message (STATUS "${CMAKE_${_language}_COMPILER} version ${${_versionPrefix}_VERSION}")
+ endif()
+ endif()
+endfunction()
+
+function (cotire_get_source_file_extension _sourceFile _extVar)
+ # get_filename_component returns extension from first occurrence of . in file name
+ # this function computes the extension from last occurrence of . in file name
+ string (FIND "${_sourceFile}" "." _index REVERSE)
+ if (_index GREATER -1)
+ math (EXPR _index "${_index} + 1")
+ string (SUBSTRING "${_sourceFile}" ${_index} -1 _sourceExt)
+ else()
+ set (_sourceExt "")
+ endif()
+ set (${_extVar} "${_sourceExt}" PARENT_SCOPE)
+endfunction()
+
+macro (cotire_check_is_path_relative_to _path _isRelativeVar)
+ set (${_isRelativeVar} FALSE)
+ if (IS_ABSOLUTE "${_path}")
+ foreach (_dir ${ARGN})
+ file (RELATIVE_PATH _relPath "${_dir}" "${_path}")
+ if (NOT _relPath OR (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\."))
+ set (${_isRelativeVar} TRUE)
+ break()
+ endif()
+ endforeach()
+ endif()
+endmacro()
+
+function (cotire_filter_language_source_files _language _sourceFilesVar _excludedSourceFilesVar _cotiredSourceFilesVar)
+ set (_sourceFiles "")
+ set (_excludedSourceFiles "")
+ set (_cotiredSourceFiles "")
+ if (CMAKE_${_language}_SOURCE_FILE_EXTENSIONS)
+ set (_languageExtensions "${CMAKE_${_language}_SOURCE_FILE_EXTENSIONS}")
+ else()
+ set (_languageExtensions "")
+ endif()
+ if (CMAKE_${_language}_IGNORE_EXTENSIONS)
+ set (_ignoreExtensions "${CMAKE_${_language}_IGNORE_EXTENSIONS}")
+ else()
+ set (_ignoreExtensions "")
+ endif()
+ if (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS)
+ set (_excludeExtensions "${COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS}")
+ else()
+ set (_excludeExtensions "")
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_language} source file extensions: ${_languageExtensions}")
+ message (STATUS "${_language} ignore extensions: ${_ignoreExtensions}")
+ message (STATUS "${_language} exclude extensions: ${_excludeExtensions}")
+ endif()
+ foreach (_sourceFile ${ARGN})
+ get_source_file_property(_sourceIsHeaderOnly "${_sourceFile}" HEADER_FILE_ONLY)
+ get_source_file_property(_sourceIsExternal "${_sourceFile}" EXTERNAL_OBJECT)
+ get_source_file_property(_sourceIsSymbolic "${_sourceFile}" SYMBOLIC)
+ get_source_file_property(_sourceLanguage "${_sourceFile}" LANGUAGE)
+ set (_sourceIsFiltered FALSE)
+ if (NOT _sourceIsHeaderOnly AND NOT _sourceIsExternal AND NOT _sourceIsSymbolic)
+ cotire_get_source_file_extension("${_sourceFile}" _sourceExt)
+ if (_sourceExt)
+ list (FIND _ignoreExtensions "${_sourceExt}" _ignoreIndex)
+ if (_ignoreIndex LESS 0)
+ list (FIND _excludeExtensions "${_sourceExt}" _excludeIndex)
+ if (_excludeIndex GREATER -1)
+ list (APPEND _excludedSourceFiles "${_sourceFile}")
+ else()
+ list (FIND _languageExtensions "${_sourceExt}" _sourceIndex)
+ if (_sourceIndex GREATER -1)
+ set (_sourceIsFiltered TRUE)
+ elseif ("${_sourceLanguage}" STREQUAL "${_language}")
+ # add to excluded sources, if file is not ignored and has correct language without having the correct extension
+ list (APPEND _excludedSourceFiles "${_sourceFile}")
+ endif()
+ endif()
+ endif()
+ endif()
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_sourceFile} filtered=${_sourceIsFiltered} language=${_sourceLanguage} header=${_sourceIsHeaderOnly}")
+ endif()
+ if (_sourceIsFiltered)
+ get_source_file_property(_sourceIsExcluded "${_sourceFile}" COTIRE_EXCLUDED)
+ get_source_file_property(_sourceIsCotired "${_sourceFile}" COTIRE_TARGET)
+ get_source_file_property(_sourceCompileFlags "${_sourceFile}" COMPILE_FLAGS)
+ if (COTIRE_DEBUG)
+ message (STATUS "${_sourceFile} excluded=${_sourceIsExcluded} cotired=${_sourceIsCotired}")
+ endif()
+ if (_sourceIsCotired)
+ list (APPEND _cotiredSourceFiles "${_sourceFile}")
+ elseif (_sourceIsExcluded OR _sourceCompileFlags)
+ list (APPEND _excludedSourceFiles "${_sourceFile}")
+ else()
+ list (APPEND _sourceFiles "${_sourceFile}")
+ endif()
+ endif()
+ endforeach()
+ if (COTIRE_DEBUG)
+ message (STATUS "All: ${ARGN}")
+ message (STATUS "${_language}: ${_sourceFiles}")
+ message (STATUS "Excluded: ${_excludedSourceFiles}")
+ message (STATUS "Cotired: ${_cotiredSourceFiles}")
+ endif()
+ set (${_sourceFilesVar} ${_sourceFiles} PARENT_SCOPE)
+ set (${_excludedSourceFilesVar} ${_excludedSourceFiles} PARENT_SCOPE)
+ set (${_cotiredSourceFilesVar} ${_cotiredSourceFiles} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_objects_with_property_on _filteredObjectsVar _property _type)
+ set (_filteredObjects "")
+ foreach (_object ${ARGN})
+ get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET)
+ if (_isSet)
+ get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property})
+ if (_propertyValue)
+ list (APPEND _filteredObjects "${_object}")
+ endif()
+ endif()
+ endforeach()
+ set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_objects_with_property_off _filteredObjectsVar _property _type)
+ set (_filteredObjects "")
+ foreach (_object ${ARGN})
+ get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET)
+ if (_isSet)
+ get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property})
+ if (NOT _propertyValue)
+ list (APPEND _filteredObjects "${_object}")
+ endif()
+ endif()
+ endforeach()
+ set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_file_property_values _valuesVar _property)
+ set (_values "")
+ foreach (_sourceFile ${ARGN})
+ get_source_file_property(_propertyValue "${_sourceFile}" ${_property})
+ if (_propertyValue)
+ list (APPEND _values "${_propertyValue}")
+ endif()
+ endforeach()
+ set (${_valuesVar} ${_values} PARENT_SCOPE)
+endfunction()
+
+function (cotrie_resolve_config_properites _configurations _propertiesVar)
+ set (_properties "")
+ foreach (_property ${ARGN})
+ if ("${_property}" MATCHES "<CONFIG>")
+ foreach (_config ${_configurations})
+ string (TOUPPER "${_config}" _upperConfig)
+ string (REPLACE "<CONFIG>" "${_upperConfig}" _configProperty "${_property}")
+ list (APPEND _properties ${_configProperty})
+ endforeach()
+ else()
+ list (APPEND _properties ${_property})
+ endif()
+ endforeach()
+ set (${_propertiesVar} ${_properties} PARENT_SCOPE)
+endfunction()
+
+function (cotrie_copy_set_properites _configurations _type _source _target)
+ cotrie_resolve_config_properites("${_configurations}" _properties ${ARGN})
+ foreach (_property ${_properties})
+ get_property(_isSet ${_type} ${_source} PROPERTY ${_property} SET)
+ if (_isSet)
+ get_property(_propertyValue ${_type} ${_source} PROPERTY ${_property})
+ set_property(${_type} ${_target} PROPERTY ${_property} "${_propertyValue}")
+ endif()
+ endforeach()
+endfunction()
+
+function (cotire_filter_compile_flags _language _flagFilter _matchedOptionsVar _unmatchedOptionsVar)
+ if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ set (_flagPrefix "[/-]")
+ else()
+ set (_flagPrefix "--?")
+ endif()
+ set (_optionFlag "")
+ set (_matchedOptions "")
+ set (_unmatchedOptions "")
+ foreach (_compileFlag ${ARGN})
+ if (_compileFlag)
+ if (_optionFlag AND NOT "${_compileFlag}" MATCHES "^${_flagPrefix}")
+ # option with separate argument
+ list (APPEND _matchedOptions "${_compileFlag}")
+ set (_optionFlag "")
+ elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})$")
+ # remember option
+ set (_optionFlag "${CMAKE_MATCH_2}")
+ elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})(.+)$")
+ # option with joined argument
+ list (APPEND _matchedOptions "${CMAKE_MATCH_3}")
+ set (_optionFlag "")
+ else()
+ # flush remembered option
+ if (_optionFlag)
+ list (APPEND _matchedOptions "${_optionFlag}")
+ set (_optionFlag "")
+ endif()
+ # add to unfiltered options
+ list (APPEND _unmatchedOptions "${_compileFlag}")
+ endif()
+ endif()
+ endforeach()
+ if (_optionFlag)
+ list (APPEND _matchedOptions "${_optionFlag}")
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "Filter ${_flagFilter}")
+ if (_matchedOptions)
+ message (STATUS "Matched ${_matchedOptions}")
+ endif()
+ if (_unmatchedOptions)
+ message (STATUS "Unmatched ${_unmatchedOptions}")
+ endif()
+ endif()
+ set (${_matchedOptionsVar} ${_matchedOptions} PARENT_SCOPE)
+ set (${_unmatchedOptionsVar} ${_unmatchedOptions} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_target_compile_flags _config _language _directory _target _flagsVar)
+ string (TOUPPER "${_config}" _upperConfig)
+ # collect options from CMake language variables
+ set (_compileFlags "")
+ if (CMAKE_${_language}_FLAGS)
+ set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS}")
+ endif()
+ if (CMAKE_${_language}_FLAGS_${_upperConfig})
+ set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS_${_upperConfig}}")
+ endif()
+ if (_target)
+ # add option from CMake target type variable
+ get_target_property(_targetType ${_target} TYPE)
+ if (POLICY CMP0018)
+ # handle POSITION_INDEPENDENT_CODE property introduced with CMake 2.8.9 if policy CMP0018 is turned on
+ cmake_policy(GET CMP0018 _PIC_Policy)
+ else()
+ # default to old behavior
+ set (_PIC_Policy "OLD")
+ endif()
+ if (COTIRE_DEBUG)
+ message(STATUS "CMP0018=${_PIC_Policy}")
+ endif()
+ if (_PIC_Policy STREQUAL "NEW")
+ # NEW behavior: honor the POSITION_INDEPENDENT_CODE target property
+ get_target_property(_targetPIC ${_target} POSITION_INDEPENDENT_CODE)
+ if (_targetPIC)
+ if (_targetType STREQUAL "EXECUTABLE" AND CMAKE_${_language}_COMPILE_OPTIONS_PIE)
+ set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIE}")
+ elseif (CMAKE_${_language}_COMPILE_OPTIONS_PIC)
+ set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIC}")
+ endif()
+ endif()
+ else()
+ # OLD behavior or policy not set: use the value of CMAKE_SHARED_LIBRARY_<Lang>_FLAGS
+ if (_targetType STREQUAL "MODULE_LIBRARY")
+ # flags variable for module library uses different name SHARED_MODULE
+ # (e.g., CMAKE_SHARED_MODULE_C_FLAGS)
+ set (_targetType SHARED_MODULE)
+ endif()
+ if (CMAKE_${_targetType}_${_language}_FLAGS)
+ set (_compileFlags "${_compileFlags} ${CMAKE_${_targetType}_${_language}_FLAGS}")
+ endif()
+ endif()
+ endif()
+ if (_directory)
+ # add_definitions may have been used to add flags to the compiler command
+ get_directory_property(_dirDefinitions DIRECTORY "${_directory}" DEFINITIONS)
+ if (_dirDefinitions)
+ set (_compileFlags "${_compileFlags} ${_dirDefinitions}")
+ endif()
+ endif()
+ if (_target)
+ # add target compile options
+ get_target_property(_targetflags ${_target} COMPILE_FLAGS)
+ if (_targetflags)
+ set (_compileFlags "${_compileFlags} ${_targetflags}")
+ endif()
+ endif()
+ if (UNIX)
+ separate_arguments(_compileFlags UNIX_COMMAND "${_compileFlags}")
+ elseif(WIN32)
+ separate_arguments(_compileFlags WINDOWS_COMMAND "${_compileFlags}")
+ else()
+ separate_arguments(_compileFlags)
+ endif()
+ # platform specific flags
+ if (APPLE)
+ get_target_property(_architectures ${_target} OSX_ARCHITECTURES_${_upperConfig})
+ if (NOT _architectures)
+ get_target_property(_architectures ${_target} OSX_ARCHITECTURES)
+ endif()
+ foreach (_arch ${_architectures})
+ list (APPEND _compileFlags "-arch" "${_arch}")
+ endforeach()
+ if (CMAKE_OSX_SYSROOT AND CMAKE_OSX_SYSROOT_DEFAULT AND CMAKE_${_language}_HAS_ISYSROOT)
+ if (NOT "${CMAKE_OSX_SYSROOT}" STREQUAL "${CMAKE_OSX_SYSROOT_DEFAULT}")
+ list (APPEND _compileFlags "-isysroot" "${CMAKE_OSX_SYSROOT}")
+ endif()
+ endif()
+ if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG)
+ list (APPEND _compileFlags "${CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}")
+ endif()
+ endif()
+ if (COTIRE_DEBUG AND _compileFlags)
+ message (STATUS "Target ${_target} compile flags ${_compileFlags}")
+ endif()
+ set (${_flagsVar} ${_compileFlags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_target_include_directories _config _language _targetSourceDir _targetBinaryDir _target _includeDirsVar)
+ set (_includeDirs "")
+ # default include dirs
+ if (CMAKE_INCLUDE_CURRENT_DIR)
+ list (APPEND _includeDirs "${_targetBinaryDir}")
+ list (APPEND _includeDirs "${_targetSourceDir}")
+ endif()
+ # parse additional include directories from target compile flags
+ set (_targetFlags "")
+ cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags)
+ cotire_filter_compile_flags("${_language}" "I" _dirs _ignore ${_targetFlags})
+ if (_dirs)
+ list (APPEND _includeDirs ${_dirs})
+ endif()
+ # target include directories
+ get_directory_property(_dirs DIRECTORY "${_targetSourceDir}" INCLUDE_DIRECTORIES)
+ if (_target)
+ get_target_property(_targetDirs ${_target} INCLUDE_DIRECTORIES)
+ if (_targetDirs)
+ list (APPEND _dirs ${_targetDirs})
+ list (REMOVE_DUPLICATES _dirs)
+ endif()
+ endif()
+ list (LENGTH _includeDirs _projectInsertIndex)
+ foreach (_dir ${_dirs})
+ if (CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE)
+ cotire_check_is_path_relative_to("${_dir}" _isRelative "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}")
+ if (_isRelative)
+ list (LENGTH _includeDirs _len)
+ if (_len EQUAL _projectInsertIndex)
+ list (APPEND _includeDirs "${_dir}")
+ else()
+ list (INSERT _includeDirs _projectInsertIndex "${_dir}")
+ endif()
+ math (EXPR _projectInsertIndex "${_projectInsertIndex} + 1")
+ else()
+ list (APPEND _includeDirs "${_dir}")
+ endif()
+ else()
+ list (APPEND _includeDirs "${_dir}")
+ endif()
+ endforeach()
+ list (REMOVE_DUPLICATES _includeDirs)
+ if (CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES)
+ list (REMOVE_ITEM _includeDirs ${CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES})
+ endif()
+ if (COTIRE_DEBUG AND _includeDirs)
+ message (STATUS "Target ${_target} include dirs ${_includeDirs}")
+ endif()
+ set (${_includeDirsVar} ${_includeDirs} PARENT_SCOPE)
+endfunction()
+
+macro (cotire_make_C_identifier _identifierVar _str)
+ # mimic CMake SystemTools::MakeCindentifier behavior
+ if ("${_str}" MATCHES "^[0-9].+$")
+ set (_str "_${str}")
+ endif()
+ string (REGEX REPLACE "[^a-zA-Z0-9]" "_" ${_identifierVar} "${_str}")
+endmacro()
+
+function (cotire_get_target_export_symbol _target _exportSymbolVar)
+ set (_exportSymbol "")
+ get_target_property(_targetType ${_target} TYPE)
+ get_target_property(_enableExports ${_target} ENABLE_EXPORTS)
+ if (_targetType MATCHES "(SHARED|MODULE)_LIBRARY" OR
+ (_targetType STREQUAL "EXECUTABLE" AND _enableExports))
+ get_target_property(_exportSymbol ${_target} DEFINE_SYMBOL)
+ if (NOT _exportSymbol)
+ set (_exportSymbol "${_target}_EXPORTS")
+ endif()
+ cotire_make_C_identifier(_exportSymbol "${_exportSymbol}")
+ endif()
+ set (${_exportSymbolVar} ${_exportSymbol} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_target_compile_definitions _config _language _directory _target _definitionsVar)
+ string (TOUPPER "${_config}" _upperConfig)
+ set (_configDefinitions "")
+ # CMAKE_INTDIR for multi-configuration build systems
+ if (NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".")
+ list (APPEND _configDefinitions "CMAKE_INTDIR=\"${_config}\"")
+ endif()
+ # target export define symbol
+ cotire_get_target_export_symbol("${_target}" _defineSymbol)
+ if (_defineSymbol)
+ list (APPEND _configDefinitions "${_defineSymbol}")
+ endif()
+ # directory compile definitions
+ get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS)
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS_${_upperConfig})
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ # target compile definitions
+ get_target_property(_definitions ${_target} COMPILE_DEFINITIONS)
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ get_target_property(_definitions ${_target} COMPILE_DEFINITIONS_${_upperConfig})
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ # parse additional compile definitions from target compile flags
+ # and don't look at directory compile definitions, which we already handled
+ set (_targetFlags "")
+ cotire_get_target_compile_flags("${_config}" "${_language}" "" "${_target}" _targetFlags)
+ cotire_filter_compile_flags("${_language}" "D" _definitions _ignore ${_targetFlags})
+ if (_definitions)
+ list (APPEND _configDefinitions ${_definitions})
+ endif()
+ list (REMOVE_DUPLICATES _configDefinitions)
+ if (COTIRE_DEBUG AND _configDefinitions)
+ message (STATUS "Target ${_target} compile definitions ${_configDefinitions}")
+ endif()
+ set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_target_compiler_flags _config _language _directory _target _compilerFlagsVar)
+ # parse target compile flags omitting compile definitions and include directives
+ set (_targetFlags "")
+ cotire_get_target_compile_flags("${_config}" "${_language}" "${_directory}" "${_target}" _targetFlags)
+ set (_compilerFlags "")
+ cotire_filter_compile_flags("${_language}" "[ID]" _ignore _compilerFlags ${_targetFlags})
+ if (COTIRE_DEBUG AND _compilerFlags)
+ message (STATUS "Target ${_target} compiler flags ${_compilerFlags}")
+ endif()
+ set (${_compilerFlagsVar} ${_compilerFlags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_add_sys_root_paths _pathsVar)
+ if (APPLE)
+ if (CMAKE_OSX_SYSROOT AND CMAKE_${_language}_HAS_ISYSROOT)
+ foreach (_path IN LISTS ${_pathsVar})
+ if (IS_ABSOLUTE "${_path}")
+ get_filename_component(_path "${CMAKE_OSX_SYSROOT}/${_path}" ABSOLUTE)
+ if (EXISTS "${_path}")
+ list (APPEND ${_pathsVar} "${_path}")
+ endif()
+ endif()
+ endforeach()
+ endif()
+ endif()
+ set (${_pathsVar} ${${_pathsVar}} PARENT_SCOPE)
+ if (COTIRE_DEBUG)
+ message (STATUS "${_pathsVar}=${${_pathsVar}}")
+ endif()
+endfunction()
+
+function (cotire_get_source_extra_properties _sourceFile _pattern _resultVar)
+ set (_extraProperties ${ARGN})
+ set (_result "")
+ if (_extraProperties)
+ list (FIND _extraProperties "${_sourceFile}" _index)
+ if (_index GREATER -1)
+ math (EXPR _index "${_index} + 1")
+ list (LENGTH _extraProperties _len)
+ math (EXPR _len "${_len} - 1")
+ foreach (_index RANGE ${_index} ${_len})
+ list (GET _extraProperties ${_index} _value)
+ if ("${_value}" MATCHES "${_pattern}")
+ list (APPEND _result "${_value}")
+ else()
+ break()
+ endif()
+ endforeach()
+ endif()
+ endif()
+ set (${_resultVar} ${_result} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_compile_definitions _config _language _sourceFile _definitionsVar)
+ set (_compileDefinitions "")
+ if (NOT CMAKE_SCRIPT_MODE_FILE)
+ string (TOUPPER "${_config}" _upperConfig)
+ get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS)
+ if (_definitions)
+ list (APPEND _compileDefinitions ${_definitions})
+ endif()
+ get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS_${_upperConfig})
+ if (_definitions)
+ list (APPEND _compileDefinitions ${_definitions})
+ endif()
+ endif()
+ cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+(=.*)?$" _definitions ${ARGN})
+ if (_definitions)
+ list (APPEND _compileDefinitions ${_definitions})
+ endif()
+ if (COTIRE_DEBUG AND _compileDefinitions)
+ message (STATUS "Source ${_sourceFile} compile definitions ${_compileDefinitions}")
+ endif()
+ set (${_definitionsVar} ${_compileDefinitions} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_files_compile_definitions _config _language _definitionsVar)
+ set (_configDefinitions "")
+ foreach (_sourceFile ${ARGN})
+ cotire_get_source_compile_definitions("${_config}" "${_language}" "${_sourceFile}" _sourceDefinitions)
+ if (_sourceDefinitions)
+ list (APPEND _configDefinitions "${_sourceFile}" ${_sourceDefinitions} "-")
+ endif()
+ endforeach()
+ set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_undefs _sourceFile _property _sourceUndefsVar)
+ set (_sourceUndefs "")
+ if (NOT CMAKE_SCRIPT_MODE_FILE)
+ get_source_file_property(_undefs "${_sourceFile}" ${_property})
+ if (_undefs)
+ list (APPEND _sourceUndefs ${_undefs})
+ endif()
+ endif()
+ cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+$" _undefs ${ARGN})
+ if (_undefs)
+ list (APPEND _sourceUndefs ${_undefs})
+ endif()
+ if (COTIRE_DEBUG AND _sourceUndefs)
+ message (STATUS "Source ${_sourceFile} ${_property} undefs ${_sourceUndefs}")
+ endif()
+ set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_source_files_undefs _property _sourceUndefsVar)
+ set (_sourceUndefs "")
+ foreach (_sourceFile ${ARGN})
+ cotire_get_source_undefs("${_sourceFile}" ${_property} _undefs)
+ if (_undefs)
+ list (APPEND _sourceUndefs "${_sourceFile}" ${_undefs} "-")
+ endif()
+ endforeach()
+ set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE)
+endfunction()
+
+macro (cotire_set_cmd_to_prologue _cmdVar)
+ set (${_cmdVar} "${CMAKE_COMMAND}")
+ if (COTIRE_DEBUG)
+ list (APPEND ${_cmdVar} "--warn-uninitialized")
+ endif()
+ list (APPEND ${_cmdVar} "-DCOTIRE_BUILD_TYPE:STRING=$<CONFIGURATION>")
+ if (COTIRE_VERBOSE)
+ list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=ON")
+ elseif("${CMAKE_GENERATOR}" MATCHES "Makefiles")
+ list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=$(VERBOSE)")
+ endif()
+endmacro()
+
+function (cotire_init_compile_cmd _cmdVar _language _compilerExe _compilerArg1)
+ if (NOT _compilerExe)
+ set (_compilerExe "${CMAKE_${_language}_COMPILER}")
+ endif()
+ if (NOT _compilerArg1)
+ set (_compilerArg1 ${CMAKE_${_language}_COMPILER_ARG1})
+ endif()
+ string (STRIP "${_compilerArg1}" _compilerArg1)
+ set (${_cmdVar} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE)
+endfunction()
+
+macro (cotire_add_definitions_to_cmd _cmdVar _language)
+ foreach (_definition ${ARGN})
+ if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ list (APPEND ${_cmdVar} "/D${_definition}")
+ else()
+ list (APPEND ${_cmdVar} "-D${_definition}")
+ endif()
+ endforeach()
+endmacro()
+
+macro (cotire_add_includes_to_cmd _cmdVar _language)
+ foreach (_include ${ARGN})
+ if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ file (TO_NATIVE_PATH "${_include}" _include)
+ list (APPEND ${_cmdVar} "/I${_include}")
+ else()
+ list (APPEND ${_cmdVar} "-I${_include}")
+ endif()
+ endforeach()
+endmacro()
+
+macro (cotire_add_compile_flags_to_cmd _cmdVar)
+ foreach (_flag ${ARGN})
+ list (APPEND ${_cmdVar} "${_flag}")
+ endforeach()
+endmacro()
+
+function (cotire_check_file_up_to_date _fileIsUpToDateVar _file)
+ set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE)
+ set (_triggerFile "")
+ foreach (_dependencyFile ${ARGN})
+ if (EXISTS "${_dependencyFile}" AND "${_dependencyFile}" IS_NEWER_THAN "${_file}")
+ set (_triggerFile "${_dependencyFile}")
+ break()
+ endif()
+ endforeach()
+ get_filename_component(_fileName "${_file}" NAME)
+ if (EXISTS "${_file}")
+ if (_triggerFile)
+ if (COTIRE_VERBOSE)
+ message (STATUS "${_fileName} update triggered by ${_triggerFile} change.")
+ endif()
+ else()
+ if (COTIRE_VERBOSE)
+ message (STATUS "${_fileName} is up-to-date.")
+ endif()
+ set (${_fileIsUpToDateVar} TRUE PARENT_SCOPE)
+ endif()
+ else()
+ if (COTIRE_VERBOSE)
+ message (STATUS "${_fileName} does not exist yet.")
+ endif()
+ endif()
+endfunction()
+
+macro (cotire_find_closest_relative_path _headerFile _includeDirs _relPathVar)
+ set (${_relPathVar} "")
+ foreach (_includeDir ${_includeDirs})
+ if (IS_DIRECTORY "${_includeDir}")
+ file (RELATIVE_PATH _relPath "${_includeDir}" "${_headerFile}")
+ if (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.")
+ string (LENGTH "${${_relPathVar}}" _closestLen)
+ string (LENGTH "${_relPath}" _relLen)
+ if (_closestLen EQUAL 0 OR _relLen LESS _closestLen)
+ set (${_relPathVar} "${_relPath}")
+ endif()
+ endif()
+ elseif ("${_includeDir}" STREQUAL "${_headerFile}")
+ # if path matches exactly, return short non-empty string
+ set (${_relPathVar} "1")
+ break()
+ endif()
+ endforeach()
+endmacro()
+
+macro (cotire_check_header_file_location _headerFile _insideIncudeDirs _outsideIncudeDirs _headerIsInside)
+ # check header path against ignored and honored include directories
+ cotire_find_closest_relative_path("${_headerFile}" "${_insideIncudeDirs}" _insideRelPath)
+ if (_insideRelPath)
+ # header is inside, but could be become outside if there is a shorter outside match
+ cotire_find_closest_relative_path("${_headerFile}" "${_outsideIncudeDirs}" _outsideRelPath)
+ if (_outsideRelPath)
+ string (LENGTH "${_insideRelPath}" _insideRelPathLen)
+ string (LENGTH "${_outsideRelPath}" _outsideRelPathLen)
+ if (_outsideRelPathLen LESS _insideRelPathLen)
+ set (${_headerIsInside} FALSE)
+ else()
+ set (${_headerIsInside} TRUE)
+ endif()
+ else()
+ set (${_headerIsInside} TRUE)
+ endif()
+ else()
+ # header is outside
+ set (${_headerIsInside} FALSE)
+ endif()
+endmacro()
+
+macro (cotire_check_ignore_header_file_path _headerFile _headerIsIgnoredVar)
+ if (NOT EXISTS "${_headerFile}")
+ set (${_headerIsIgnoredVar} TRUE)
+ elseif (IS_DIRECTORY "${_headerFile}")
+ set (${_headerIsIgnoredVar} TRUE)
+ elseif ("${_headerFile}" MATCHES "\\.\\.|[_-]fixed" AND "${_headerFile}" MATCHES "\\.h$")
+ # heuristic: ignore C headers with embedded parent directory references or "-fixed" or "_fixed" in path
+ # these often stem from using GCC #include_next tricks, which may break the precompiled header compilation
+ # with the error message "error: no include path in which to search for header.h"
+ set (${_headerIsIgnoredVar} TRUE)
+ else()
+ set (${_headerIsIgnoredVar} FALSE)
+ endif()
+endmacro()
+
+macro (cotire_check_ignore_header_file_ext _headerFile _ignoreExtensionsVar _headerIsIgnoredVar)
+ # check header file extension
+ cotire_get_source_file_extension("${_headerFile}" _headerFileExt)
+ set (${_headerIsIgnoredVar} FALSE)
+ if (_headerFileExt)
+ list (FIND ${_ignoreExtensionsVar} "${_headerFileExt}" _index)
+ if (_index GREATER -1)
+ set (${_headerIsIgnoredVar} TRUE)
+ endif()
+ endif()
+endmacro()
+
+macro (cotire_parse_line _line _headerFileVar _headerDepthVar)
+ if (MSVC)
+ # cl.exe /showIncludes output looks different depending on the language pack used, e.g.:
+ # English: "Note: including file: C:\directory\file"
+ # German: "Hinweis: Einlesen der Datei: C:\directory\file"
+ # We use a very general regular expression, relying on the presence of the : characters
+ if ("${_line}" MATCHES ":( +)([^:]+:[^:]+)$")
+ # Visual Studio compiler output
+ string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar})
+ get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" ABSOLUTE)
+ else()
+ set (${_headerFileVar} "")
+ set (${_headerDepthVar} 0)
+ endif()
+ else()
+ if ("${_line}" MATCHES "^(\\.+) (.*)$")
+ # GCC like output
+ string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar})
+ if (IS_ABSOLUTE "${CMAKE_MATCH_2}")
+ set (${_headerFileVar} "${CMAKE_MATCH_2}")
+ else()
+ get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" REALPATH)
+ endif()
+ else()
+ set (${_headerFileVar} "")
+ set (${_headerDepthVar} 0)
+ endif()
+ endif()
+endmacro()
+
+function (cotire_parse_includes _language _scanOutput _ignoredIncudeDirs _honoredIncudeDirs _ignoredExtensions _selectedIncludesVar _unparsedLinesVar)
+ if (WIN32)
+ # prevent CMake macro invocation errors due to backslash characters in Windows paths
+ string (REPLACE "\\" "/" _scanOutput "${_scanOutput}")
+ endif()
+ # canonize slashes
+ string (REPLACE "//" "/" _scanOutput "${_scanOutput}")
+ # prevent semicolon from being interpreted as a line separator
+ string (REPLACE ";" "\\;" _scanOutput "${_scanOutput}")
+ # then separate lines
+ string (REGEX REPLACE "\n" ";" _scanOutput "${_scanOutput}")
+ list (LENGTH _scanOutput _len)
+ # remove duplicate lines to speed up parsing
+ list (REMOVE_DUPLICATES _scanOutput)
+ list (LENGTH _scanOutput _uniqueLen)
+ if (COTIRE_VERBOSE)
+ message (STATUS "Scanning ${_uniqueLen} unique lines of ${_len} for includes")
+ if (_ignoredExtensions)
+ message (STATUS "Ignored extensions: ${_ignoredExtensions}")
+ endif()
+ if (_ignoredIncudeDirs)
+ message (STATUS "Ignored paths: ${_ignoredIncudeDirs}")
+ endif()
+ if (_honoredIncudeDirs)
+ message (STATUS "Included paths: ${_honoredIncudeDirs}")
+ endif()
+ endif()
+ set (_sourceFiles ${ARGN})
+ set (_selectedIncludes "")
+ set (_unparsedLines "")
+ # stack keeps track of inside/outside project status of processed header files
+ set (_headerIsInsideStack "")
+ foreach (_line IN LISTS _scanOutput)
+ if (_line)
+ cotire_parse_line("${_line}" _headerFile _headerDepth)
+ if (_headerFile)
+ cotire_check_header_file_location("${_headerFile}" "${_ignoredIncudeDirs}" "${_honoredIncudeDirs}" _headerIsInside)
+ if (COTIRE_DEBUG)
+ message (STATUS "${_headerDepth}: ${_headerFile} ${_headerIsInside}")
+ endif()
+ # update stack
+ list (LENGTH _headerIsInsideStack _stackLen)
+ if (_headerDepth GREATER _stackLen)
+ math (EXPR _stackLen "${_stackLen} + 1")
+ foreach (_index RANGE ${_stackLen} ${_headerDepth})
+ list (APPEND _headerIsInsideStack ${_headerIsInside})
+ endforeach()
+ else()
+ foreach (_index RANGE ${_headerDepth} ${_stackLen})
+ list (REMOVE_AT _headerIsInsideStack -1)
+ endforeach()
+ list (APPEND _headerIsInsideStack ${_headerIsInside})
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_headerIsInsideStack}")
+ endif()
+ # header is a candidate if it is outside project
+ if (NOT _headerIsInside)
+ # get parent header file's inside/outside status
+ if (_headerDepth GREATER 1)
+ math (EXPR _index "${_headerDepth} - 2")
+ list (GET _headerIsInsideStack ${_index} _parentHeaderIsInside)
+ else()
+ set (_parentHeaderIsInside TRUE)
+ endif()
+ # select header file if parent header file is inside project
+ # (e.g., a project header file that includes a standard header file)
+ if (_parentHeaderIsInside)
+ cotire_check_ignore_header_file_path("${_headerFile}" _headerIsIgnored)
+ if (NOT _headerIsIgnored)
+ cotire_check_ignore_header_file_ext("${_headerFile}" _ignoredExtensions _headerIsIgnored)
+ if (NOT _headerIsIgnored)
+ list (APPEND _selectedIncludes "${_headerFile}")
+ else()
+ # fix header's inside status on stack, it is ignored by extension now
+ list (REMOVE_AT _headerIsInsideStack -1)
+ list (APPEND _headerIsInsideStack TRUE)
+ endif()
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_headerFile} ${_ignoredExtensions} ${_headerIsIgnored}")
+ endif()
+ endif()
+ endif()
+ else()
+ if (MSVC)
+ # for cl.exe do not keep unparsed lines which solely consist of a source file name
+ string (FIND "${_sourceFiles}" "${_line}" _index)
+ if (_index LESS 0)
+ list (APPEND _unparsedLines "${_line}")
+ endif()
+ else()
+ list (APPEND _unparsedLines "${_line}")
+ endif()
+ endif()
+ endif()
+ endforeach()
+ list (REMOVE_DUPLICATES _selectedIncludes)
+ set (${_selectedIncludesVar} ${_selectedIncludes} PARENT_SCOPE)
+ set (${_unparsedLinesVar} ${_unparsedLines} PARENT_SCOPE)
+endfunction()
+
+function (cotire_scan_includes _includesVar)
+ set(_options "")
+ set(_oneValueArgs COMPILER_ID COMPILER_EXECUTABLE COMPILER_VERSION LANGUAGE UNPARSED_LINES)
+ set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ set (_sourceFiles ${_option_UNPARSED_ARGUMENTS})
+ if (NOT _option_LANGUAGE)
+ set (_option_LANGUAGE "CXX")
+ endif()
+ if (NOT _option_COMPILER_ID)
+ set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}")
+ endif()
+ set (_cmd "${_option_COMPILER_EXECUTABLE}" ${_option_COMPILER_ARG1})
+ cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}")
+ cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS})
+ cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS})
+ cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_INCLUDE_DIRECTORIES})
+ cotire_add_makedep_flags("${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" _cmd)
+ # only consider existing source files for scanning
+ set (_existingSourceFiles "")
+ foreach (_sourceFile ${_sourceFiles})
+ if (EXISTS "${_sourceFile}")
+ list (APPEND _existingSourceFiles "${_sourceFile}")
+ endif()
+ endforeach()
+ if (NOT _existingSourceFiles)
+ set (${_includesVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ list (APPEND _cmd ${_existingSourceFiles})
+ if (COTIRE_VERBOSE)
+ message (STATUS "execute_process: ${_cmd}")
+ endif()
+ if (_option_COMPILER_ID MATCHES "MSVC")
+ if (COTIRE_DEBUG)
+ message (STATUS "clearing VS_UNICODE_OUTPUT")
+ endif()
+ # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared
+ unset (ENV{VS_UNICODE_OUTPUT})
+ endif()
+ execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+ RESULT_VARIABLE _result OUTPUT_QUIET ERROR_VARIABLE _output)
+ if (_result)
+ message (STATUS "Result ${_result} scanning includes of ${_existingSourceFiles}.")
+ endif()
+ cotire_parse_includes(
+ "${_option_LANGUAGE}" "${_output}"
+ "${_option_IGNORE_PATH}" "${_option_INCLUDE_PATH}"
+ "${_option_IGNORE_EXTENSIONS}"
+ _includes _unparsedLines
+ ${_sourceFiles})
+ set (${_includesVar} ${_includes} PARENT_SCOPE)
+ if (_option_UNPARSED_LINES)
+ set (${_option_UNPARSED_LINES} ${_unparsedLines} PARENT_SCOPE)
+ endif()
+endfunction()
+
+macro (cotire_append_undefs _contentsVar)
+ set (_undefs ${ARGN})
+ if (_undefs)
+ list (REMOVE_DUPLICATES _undefs)
+ foreach (_definition ${_undefs})
+ list (APPEND ${_contentsVar} "#undef ${_definition}")
+ endforeach()
+ endif()
+endmacro()
+
+macro (cotire_comment_str _language _commentText _commentVar)
+ if ("${_language}" STREQUAL "CMAKE")
+ set (${_commentVar} "# ${_commentText}")
+ else()
+ set (${_commentVar} "/* ${_commentText} */")
+ endif()
+endmacro()
+
+function (cotire_write_file _language _file _contents _force)
+ get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME)
+ cotire_comment_str("${_language}" "${_moduleName} ${COTIRE_CMAKE_MODULE_VERSION} generated file" _header1)
+ cotire_comment_str("${_language}" "${_file}" _header2)
+ set (_contents "${_header1}\n${_header2}\n${_contents}")
+ if (COTIRE_DEBUG)
+ message (STATUS "${_contents}")
+ endif()
+ if (_force OR NOT EXISTS "${_file}")
+ file (WRITE "${_file}" "${_contents}")
+ else()
+ file (READ "${_file}" _oldContents)
+ if (NOT "${_oldContents}" STREQUAL "${_contents}")
+ file (WRITE "${_file}" "${_contents}")
+ else()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_file} unchanged")
+ endif()
+ endif()
+ endif()
+endfunction()
+
+function (cotire_generate_unity_source _unityFile)
+ set(_options "")
+ set(_oneValueArgs LANGUAGE)
+ set(_multiValueArgs
+ DEPENDS SOURCES_COMPILE_DEFINITIONS
+ PRE_UNDEFS SOURCES_PRE_UNDEFS POST_UNDEFS SOURCES_POST_UNDEFS PROLOGUE EPILOGUE)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ if (_option_DEPENDS)
+ cotire_check_file_up_to_date(_unityFileIsUpToDate "${_unityFile}" ${_option_DEPENDS})
+ if (_unityFileIsUpToDate)
+ return()
+ endif()
+ endif()
+ set (_sourceFiles ${_option_UNPARSED_ARGUMENTS})
+ if (NOT _option_PRE_UNDEFS)
+ set (_option_PRE_UNDEFS "")
+ endif()
+ if (NOT _option_SOURCES_PRE_UNDEFS)
+ set (_option_SOURCES_PRE_UNDEFS "")
+ endif()
+ if (NOT _option_POST_UNDEFS)
+ set (_option_POST_UNDEFS "")
+ endif()
+ if (NOT _option_SOURCES_POST_UNDEFS)
+ set (_option_SOURCES_POST_UNDEFS "")
+ endif()
+ set (_contents "")
+ if (_option_PROLOGUE)
+ list (APPEND _contents ${_option_PROLOGUE})
+ endif()
+ if (_option_LANGUAGE AND _sourceFiles)
+ if ("${_option_LANGUAGE}" STREQUAL "CXX")
+ list (APPEND _contents "#ifdef __cplusplus")
+ elseif ("${_option_LANGUAGE}" STREQUAL "C")
+ list (APPEND _contents "#ifndef __cplusplus")
+ endif()
+ endif()
+ set (_compileUndefinitions "")
+ foreach (_sourceFile ${_sourceFiles})
+ cotire_get_source_compile_definitions(
+ "${_option_CONFIGURATION}" "${_option_LANGUAGE}" "${_sourceFile}" _compileDefinitions
+ ${_option_SOURCES_COMPILE_DEFINITIONS})
+ cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_PRE_UNDEFS _sourcePreUndefs ${_option_SOURCES_PRE_UNDEFS})
+ cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_POST_UNDEFS _sourcePostUndefs ${_option_SOURCES_POST_UNDEFS})
+ if (_option_PRE_UNDEFS)
+ list (APPEND _compileUndefinitions ${_option_PRE_UNDEFS})
+ endif()
+ if (_sourcePreUndefs)
+ list (APPEND _compileUndefinitions ${_sourcePreUndefs})
+ endif()
+ if (_compileUndefinitions)
+ cotire_append_undefs(_contents ${_compileUndefinitions})
+ set (_compileUndefinitions "")
+ endif()
+ if (_sourcePostUndefs)
+ list (APPEND _compileUndefinitions ${_sourcePostUndefs})
+ endif()
+ if (_option_POST_UNDEFS)
+ list (APPEND _compileUndefinitions ${_option_POST_UNDEFS})
+ endif()
+ foreach (_definition ${_compileDefinitions})
+ if ("${_definition}" MATCHES "^([a-zA-Z0-9_]+)=(.+)$")
+ list (APPEND _contents "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}")
+ list (INSERT _compileUndefinitions 0 "${CMAKE_MATCH_1}")
+ else()
+ list (APPEND _contents "#define ${_definition}")
+ list (INSERT _compileUndefinitions 0 "${_definition}")
+ endif()
+ endforeach()
+ get_filename_component(_sourceFile "${_sourceFile}" ABSOLUTE)
+ if (WIN32)
+ file (TO_NATIVE_PATH "${_sourceFile}" _sourceFile)
+ endif()
+ list (APPEND _contents "#include \"${_sourceFile}\"")
+ endforeach()
+ if (_compileUndefinitions)
+ cotire_append_undefs(_contents ${_compileUndefinitions})
+ set (_compileUndefinitions "")
+ endif()
+ if (_option_LANGUAGE AND _sourceFiles)
+ list (APPEND _contents "#endif")
+ endif()
+ if (_option_EPILOGUE)
+ list (APPEND _contents ${_option_EPILOGUE})
+ endif()
+ list (APPEND _contents "")
+ string (REPLACE ";" "\n" _contents "${_contents}")
+ if (COTIRE_VERBOSE)
+ message ("${_contents}")
+ endif()
+ cotire_write_file("${_option_LANGUAGE}" "${_unityFile}" "${_contents}" TRUE)
+endfunction()
+
+function (cotire_generate_prefix_header _prefixFile)
+ set(_options "")
+ set(_oneValueArgs LANGUAGE COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION)
+ set(_multiValueArgs DEPENDS COMPILE_DEFINITIONS COMPILE_FLAGS
+ INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ if (_option_DEPENDS)
+ cotire_check_file_up_to_date(_prefixFileIsUpToDate "${_prefixFile}" ${_option_DEPENDS})
+ if (_prefixFileIsUpToDate)
+ return()
+ endif()
+ endif()
+ set (_epilogue "")
+ if (_option_COMPILER_ID MATCHES "Intel")
+ # Intel compiler requires hdrstop pragma to stop generating PCH file
+ set (_epilogue "#pragma hdrstop")
+ endif()
+ set (_sourceFiles ${_option_UNPARSED_ARGUMENTS})
+ cotire_scan_includes(_selectedHeaders ${_sourceFiles}
+ LANGUAGE "${_option_LANGUAGE}"
+ COMPILER_EXECUTABLE "${_option_COMPILER_EXECUTABLE}"
+ COMPILER_ID "${_option_COMPILER_ID}"
+ COMPILER_VERSION "${_option_COMPILER_VERSION}"
+ COMPILE_DEFINITIONS ${_option_COMPILE_DEFINITIONS}
+ COMPILE_FLAGS ${_option_COMPILE_FLAGS}
+ INCLUDE_DIRECTORIES ${_option_INCLUDE_DIRECTORIES}
+ IGNORE_PATH ${_option_IGNORE_PATH}
+ INCLUDE_PATH ${_option_INCLUDE_PATH}
+ IGNORE_EXTENSIONS ${_option_IGNORE_EXTENSIONS}
+ UNPARSED_LINES _unparsedLines)
+ cotire_generate_unity_source("${_prefixFile}" EPILOGUE ${_epilogue} LANGUAGE "${_option_LANGUAGE}" ${_selectedHeaders})
+ set (_unparsedLinesFile "${_prefixFile}.log")
+ if (_unparsedLines)
+ if (COTIRE_VERBOSE OR NOT _selectedHeaders)
+ list (LENGTH _unparsedLines _skippedLineCount)
+ file (RELATIVE_PATH _unparsedLinesFileRelPath "${CMAKE_BINARY_DIR}" "${_unparsedLinesFile}")
+ message (STATUS "${_skippedLineCount} line(s) skipped, see ${_unparsedLinesFileRelPath}")
+ endif()
+ string (REPLACE ";" "\n" _unparsedLines "${_unparsedLines}")
+ endif()
+ file (WRITE "${_unparsedLinesFile}" "${_unparsedLines}\n")
+endfunction()
+
+function (cotire_add_makedep_flags _language _compilerID _compilerVersion _flagsVar)
+ set (_flags ${${_flagsVar}})
+ if (_compilerID MATCHES "MSVC")
+ # cl.exe options used
+ # /nologo suppresses display of sign-on banner
+ # /TC treat all files named on the command line as C source files
+ # /TP treat all files named on the command line as C++ source files
+ # /EP preprocess to stdout without #line directives
+ # /showIncludes list include files
+ set (_sourceFileTypeC "/TC")
+ set (_sourceFileTypeCXX "/TP")
+ if (_flags)
+ # append to list
+ list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /showIncludes)
+ else()
+ # return as a flag string
+ set (_flags "${_sourceFileType${_language}} /EP /showIncludes")
+ endif()
+ elseif (_compilerID MATCHES "GNU")
+ # GCC options used
+ # -H print the name of each header file used
+ # -E invoke preprocessor
+ # -fdirectives-only do not expand macros, requires GCC >= 4.3
+ if (_flags)
+ # append to list
+ list (APPEND _flags -H -E)
+ if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0")
+ list (APPEND _flags "-fdirectives-only")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "-H -E")
+ if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0")
+ set (_flags "${_flags} -fdirectives-only")
+ endif()
+ endif()
+ elseif (_compilerID MATCHES "Clang")
+ # Clang options used
+ # -H print the name of each header file used
+ # -E invoke preprocessor
+ if (_flags)
+ # append to list
+ list (APPEND _flags -H -E)
+ else()
+ # return as a flag string
+ set (_flags "-H -E")
+ endif()
+ elseif (_compilerID MATCHES "Intel")
+ if (WIN32)
+ # Windows Intel options used
+ # /nologo do not display compiler version information
+ # /QH display the include file order
+ # /EP preprocess to stdout, omitting #line directives
+ # /TC process all source or unrecognized file types as C source files
+ # /TP process all source or unrecognized file types as C++ source files
+ set (_sourceFileTypeC "/TC")
+ set (_sourceFileTypeCXX "/TP")
+ if (_flags)
+ # append to list
+ list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /QH)
+ else()
+ # return as a flag string
+ set (_flags "${_sourceFileType${_language}} /EP /QH")
+ endif()
+ else()
+ # Linux / Mac OS X Intel options used
+ # -H print the name of each header file used
+ # -EP preprocess to stdout, omitting #line directives
+ # -Kc++ process all source or unrecognized file types as C++ source files
+ if (_flags)
+ # append to list
+ if ("${_language}" STREQUAL "CXX")
+ list (APPEND _flags -Kc++)
+ endif()
+ list (APPEND _flags -H -EP)
+ else()
+ # return as a flag string
+ if ("${_language}" STREQUAL "CXX")
+ set (_flags "-Kc++ ")
+ endif()
+ set (_flags "${_flags}-H -EP")
+ endif()
+ endif()
+ else()
+ message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.")
+ endif()
+ set (${_flagsVar} ${_flags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_add_pch_compilation_flags _language _compilerID _compilerVersion _prefixFile _pchFile _hostFile _flagsVar)
+ set (_flags ${${_flagsVar}})
+ if (_compilerID MATCHES "MSVC")
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
+ file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative)
+ # cl.exe options used
+ # /Yc creates a precompiled header file
+ # /Fp specifies precompiled header binary file name
+ # /FI forces inclusion of file
+ # /TC treat all files named on the command line as C source files
+ # /TP treat all files named on the command line as C++ source files
+ # /Zs syntax check only
+ set (_sourceFileTypeC "/TC")
+ set (_sourceFileTypeCXX "/TP")
+ if (_flags)
+ # append to list
+ list (APPEND _flags /nologo "${_sourceFileType${_language}}"
+ "/Yc${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}")
+ else()
+ # return as a flag string
+ set (_flags "/Yc\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
+ endif()
+ elseif (_compilerID MATCHES "GNU|Clang")
+ # GCC / Clang options used
+ # -x specify the source language
+ # -c compile but do not link
+ # -o place output in file
+ set (_xLanguage_C "c-header")
+ set (_xLanguage_CXX "c++-header")
+ if (_flags)
+ # append to list
+ list (APPEND _flags "-x" "${_xLanguage_${_language}}" "-c" "${_prefixFile}" -o "${_pchFile}")
+ else()
+ # return as a flag string
+ set (_flags "-x ${_xLanguage_${_language}} -c \"${_prefixFile}\" -o \"${_pchFile}\"")
+ endif()
+ elseif (_compilerID MATCHES "Intel")
+ if (WIN32)
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
+ file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative)
+ # Windows Intel options used
+ # /nologo do not display compiler version information
+ # /Yc create a precompiled header (PCH) file
+ # /Fp specify a path or file name for precompiled header files
+ # /FI tells the preprocessor to include a specified file name as the header file
+ # /TC process all source or unrecognized file types as C source files
+ # /TP process all source or unrecognized file types as C++ source files
+ # /Zs syntax check only
+ # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
+ set (_sourceFileTypeC "/TC")
+ set (_sourceFileTypeCXX "/TP")
+ if (_flags)
+ # append to list
+ list (APPEND _flags /nologo "${_sourceFileType${_language}}"
+ "/Yc" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ list (APPEND _flags "/Wpch-messages")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "/Yc /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ set (_flags "${_flags} /Wpch-messages")
+ endif()
+ endif()
+ else()
+ # Linux / Mac OS X Intel options used
+ # -pch-dir location for precompiled header files
+ # -pch-create name of the precompiled header (PCH) to create
+ # -Kc++ process all source or unrecognized file types as C++ source files
+ # -fsyntax-only check only for correct syntax
+ # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
+ get_filename_component(_pchDir "${_pchFile}" PATH)
+ get_filename_component(_pchName "${_pchFile}" NAME)
+ set (_xLanguage_C "c-header")
+ set (_xLanguage_CXX "c++-header")
+ if (_flags)
+ # append to list
+ if ("${_language}" STREQUAL "CXX")
+ list (APPEND _flags -Kc++)
+ endif()
+ list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-create" "${_pchName}" "-fsyntax-only" "${_hostFile}")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ list (APPEND _flags "-Wpch-messages")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-create \"${_pchName}\"")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ set (_flags "${_flags} -Wpch-messages")
+ endif()
+ endif()
+ endif()
+ else()
+ message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.")
+ endif()
+ set (${_flagsVar} ${_flags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_add_pch_inclusion_flags _language _compilerID _compilerVersion _prefixFile _pchFile _flagsVar)
+ set (_flags ${${_flagsVar}})
+ if (_compilerID MATCHES "MSVC")
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
+ # cl.exe options used
+ # /Yu uses a precompiled header file during build
+ # /Fp specifies precompiled header binary file name
+ # /FI forces inclusion of file
+ if (_flags)
+ # append to list
+ list (APPEND _flags "/Yu${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}")
+ else()
+ # return as a flag string
+ set (_flags "/Yu\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
+ endif()
+ elseif (_compilerID MATCHES "GNU")
+ # GCC options used
+ # -include process include file as the first line of the primary source file
+ # -Winvalid-pch warns if precompiled header is found but cannot be used
+ if (_flags)
+ # append to list
+ list (APPEND _flags "-include" "${_prefixFile}" "-Winvalid-pch")
+ else()
+ # return as a flag string
+ set (_flags "-include \"${_prefixFile}\" -Winvalid-pch")
+ endif()
+ elseif (_compilerID MATCHES "Clang")
+ # Clang options used
+ # -include process include file as the first line of the primary source file
+ # -Qunused-arguments don't emit warning for unused driver arguments
+ if (_flags)
+ # append to list
+ list (APPEND _flags "-include" "${_prefixFile}" "-Qunused-arguments")
+ else()
+ # return as a flag string
+ set (_flags "-include \"${_prefixFile}\" -Qunused-arguments")
+ endif()
+ elseif (_compilerID MATCHES "Intel")
+ if (WIN32)
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
+ # Windows Intel options used
+ # /Yu use a precompiled header (PCH) file
+ # /Fp specify a path or file name for precompiled header files
+ # /FI tells the preprocessor to include a specified file name as the header file
+ # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
+ if (_flags)
+ # append to list
+ list (APPEND _flags "/Yu" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ list (APPEND _flags "/Wpch-messages")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "/Yu /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ set (_flags "${_flags} /Wpch-messages")
+ endif()
+ endif()
+ else()
+ # Linux / Mac OS X Intel options used
+ # -pch-dir location for precompiled header files
+ # -pch-use name of the precompiled header (PCH) to use
+ # -include process include file as the first line of the primary source file
+ # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2)
+ get_filename_component(_pchDir "${_pchFile}" PATH)
+ get_filename_component(_pchName "${_pchFile}" NAME)
+ if (_flags)
+ # append to list
+ list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-use" "${_pchName}")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ list (APPEND _flags "-Wpch-messages")
+ endif()
+ else()
+ # return as a flag string
+ set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-use \"${_pchName}\"")
+ if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0")
+ set (_flags "${_flags} -Wpch-messages")
+ endif()
+ endif()
+ endif()
+ else()
+ message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.")
+ endif()
+ set (${_flagsVar} ${_flags} PARENT_SCOPE)
+endfunction()
+
+function (cotire_precompile_prefix_header _prefixFile _pchFile _hostFile)
+ set(_options "")
+ set(_oneValueArgs COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION LANGUAGE)
+ set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ if (NOT _option_LANGUAGE)
+ set (_option_LANGUAGE "CXX")
+ endif()
+ if (NOT _option_COMPILER_ID)
+ set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}")
+ endif()
+ cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}")
+ cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS})
+ cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS})
+ cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_INCLUDE_DIRECTORIES})
+ cotire_add_pch_compilation_flags(
+ "${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" "${_hostFile}" _cmd)
+ if (COTIRE_VERBOSE)
+ message (STATUS "execute_process: ${_cmd}")
+ endif()
+ if (_option_COMPILER_ID MATCHES "MSVC")
+ if (COTIRE_DEBUG)
+ message (STATUS "clearing VS_UNICODE_OUTPUT")
+ endif()
+ # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared
+ unset (ENV{VS_UNICODE_OUTPUT})
+ endif()
+ execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE _result)
+ if (_result)
+ message (FATAL_ERROR "Error ${_result} precompiling ${_prefixFile}.")
+ endif()
+endfunction()
+
+function (cotire_check_precompiled_header_support _language _targetSourceDir _target _msgVar)
+ set (_unsupportedCompiler
+ "Precompiled headers not supported for ${_language} compiler ${CMAKE_${_language}_COMPILER_ID}")
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC")
+ # supported since Visual Studio C++ 6.0
+ # and CMake does not support an earlier version
+ set (${_msgVar} "" PARENT_SCOPE)
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU")
+ # GCC PCH support requires version >= 3.4
+ cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
+ if ("${COTIRE_${_language}_COMPILER_VERSION}" MATCHES ".+" AND
+ "${COTIRE_${_language}_COMPILER_VERSION}" VERSION_LESS "3.4.0")
+ set (${_msgVar} "${_unsupportedCompiler} version ${COTIRE_${_language}_COMPILER_VERSION}." PARENT_SCOPE)
+ else()
+ set (${_msgVar} "" PARENT_SCOPE)
+ endif()
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang")
+ # all Clang versions have PCH support
+ set (${_msgVar} "" PARENT_SCOPE)
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel")
+ # Intel PCH support requires version >= 8.0.0
+ cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
+ if ("${COTIRE_${_language}_COMPILER_VERSION}" MATCHES ".+" AND
+ "${COTIRE_${_language}_COMPILER_VERSION}" VERSION_LESS "8.0.0")
+ set (${_msgVar} "${_unsupportedCompiler} version ${COTIRE_${_language}_COMPILER_VERSION}." PARENT_SCOPE)
+ else()
+ set (${_msgVar} "" PARENT_SCOPE)
+ endif()
+ else()
+ set (${_msgVar} "${_unsupportedCompiler}." PARENT_SCOPE)
+ endif()
+ if (APPLE)
+ # PCH compilation not supported by GCC / Clang for multi-architecture builds (e.g., i386, x86_64)
+ if (CMAKE_CONFIGURATION_TYPES)
+ set (_configs ${CMAKE_CONFIGURATION_TYPES})
+ elseif (CMAKE_BUILD_TYPE)
+ set (_configs ${CMAKE_BUILD_TYPE})
+ else()
+ set (_configs "None")
+ endif()
+ foreach (_config ${_configs})
+ set (_targetFlags "")
+ cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags)
+ cotire_filter_compile_flags("${_language}" "arch" _architectures _ignore ${_targetFlags})
+ list (LENGTH _architectures _numberOfArchitectures)
+ if (_numberOfArchitectures GREATER 1)
+ string (REPLACE ";" ", " _architectureStr "${_architectures}")
+ set (${_msgVar}
+ "Precompiled headers not supported on Darwin for multi-architecture builds (${_architectureStr})."
+ PARENT_SCOPE)
+ break()
+ endif()
+ endforeach()
+ endif()
+endfunction()
+
+macro (cotire_get_intermediate_dir _cotireDir)
+ get_filename_component(${_cotireDir} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${COTIRE_INTDIR}" ABSOLUTE)
+endmacro()
+
+macro (cotire_setup_file_extension_variables)
+ set (_unityFileExt_C ".c")
+ set (_unityFileExt_CXX ".cxx")
+ set (_prefixFileExt_C ".h")
+ set (_prefixFileExt_CXX ".hxx")
+endmacro()
+
+function (cotire_make_single_unity_source_file_path _language _target _unityFileVar)
+ cotire_setup_file_extension_variables()
+ if (NOT DEFINED _unityFileExt_${_language})
+ set (${_unityFileVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}")
+ set (_unityFileName "${_unityFileBaseName}${_unityFileExt_${_language}}")
+ cotire_get_intermediate_dir(_baseDir)
+ set (_unityFile "${_baseDir}/${_unityFileName}")
+ set (${_unityFileVar} "${_unityFile}" PARENT_SCOPE)
+ if (COTIRE_DEBUG)
+ message(STATUS "${_unityFile}")
+ endif()
+endfunction()
+
+function (cotire_make_unity_source_file_paths _language _target _maxIncludes _unityFilesVar)
+ cotire_setup_file_extension_variables()
+ if (NOT DEFINED _unityFileExt_${_language})
+ set (${_unityFileVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}")
+ cotire_get_intermediate_dir(_baseDir)
+ set (_startIndex 0)
+ set (_index 0)
+ set (_unityFiles "")
+ set (_sourceFiles ${ARGN})
+ foreach (_sourceFile ${_sourceFiles})
+ get_source_file_property(_startNew "${_sourceFile}" COTIRE_START_NEW_UNITY_SOURCE)
+ math (EXPR _unityFileCount "${_index} - ${_startIndex}")
+ if (_startNew OR (_maxIncludes GREATER 0 AND NOT _unityFileCount LESS _maxIncludes))
+ if (_index GREATER 0)
+ # start new unity file segment
+ math (EXPR _endIndex "${_index} - 1")
+ set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}")
+ list (APPEND _unityFiles "${_baseDir}/${_unityFileName}")
+ endif()
+ set (_startIndex ${_index})
+ endif()
+ math (EXPR _index "${_index} + 1")
+ endforeach()
+ list (LENGTH _sourceFiles _numberOfSources)
+ if (_startIndex EQUAL 0)
+ # there is only a single unity file
+ cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFiles)
+ elseif (_startIndex LESS _numberOfSources)
+ # end with final unity file segment
+ math (EXPR _endIndex "${_index} - 1")
+ set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}")
+ list (APPEND _unityFiles "${_baseDir}/${_unityFileName}")
+ endif()
+ set (${_unityFilesVar} ${_unityFiles} PARENT_SCOPE)
+ if (COTIRE_DEBUG)
+ message(STATUS "${_unityFiles}")
+ endif()
+endfunction()
+
+function (cotire_unity_to_prefix_file_path _language _target _unityFile _prefixFileVar)
+ cotire_setup_file_extension_variables()
+ if (NOT DEFINED _unityFileExt_${_language})
+ set (${_prefixFileVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}")
+ set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}")
+ string (REPLACE "${_unityFileBaseName}" "${_prefixFileBaseName}" _prefixFile "${_unityFile}")
+ string (REGEX REPLACE "${_unityFileExt_${_language}}$" "${_prefixFileExt_${_language}}" _prefixFile "${_prefixFile}")
+ set (${_prefixFileVar} "${_prefixFile}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_make_prefix_file_name _language _target _prefixFileBaseNameVar _prefixFileNameVar)
+ cotire_setup_file_extension_variables()
+ if (NOT _language)
+ set (_prefixFileBaseName "${_target}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}")
+ set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_C}")
+ elseif (DEFINED _prefixFileExt_${_language})
+ set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}")
+ set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_${_language}}")
+ else()
+ set (_prefixFileBaseName "")
+ set (_prefixFileName "")
+ endif()
+ set (${_prefixFileBaseNameVar} "${_prefixFileBaseName}" PARENT_SCOPE)
+ set (${_prefixFileNameVar} "${_prefixFileName}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_make_prefix_file_path _language _target _prefixFileVar)
+ cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName)
+ set (${_prefixFileVar} "" PARENT_SCOPE)
+ if (_prefixFileName)
+ if (NOT _language)
+ set (_language "C")
+ endif()
+ if (MSVC OR CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang|Intel")
+ cotire_get_intermediate_dir(_baseDir)
+ set (${_prefixFileVar} "${_baseDir}/${_prefixFileName}" PARENT_SCOPE)
+ endif()
+ endif()
+endfunction()
+
+function (cotire_make_pch_file_path _language _targetSourceDir _target _pchFileVar)
+ cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName)
+ set (${_pchFileVar} "" PARENT_SCOPE)
+ if (_prefixFileBaseName AND _prefixFileName)
+ cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _msg)
+ if (NOT _msg)
+ if (XCODE)
+ # For Xcode, we completely hand off the compilation of the prefix header to the IDE
+ return()
+ endif()
+ cotire_get_intermediate_dir(_baseDir)
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC")
+ # MSVC uses the extension .pch added to the prefix header base name
+ set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pch" PARENT_SCOPE)
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang")
+ # GCC / Clang look for a precompiled header corresponding to the prefix header with the extension .gch appended
+ set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.gch" PARENT_SCOPE)
+ elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel")
+ # Intel uses the extension .pchi added to the prefix header base name
+ set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pchi" PARENT_SCOPE)
+ endif()
+ endif()
+ endif()
+endfunction()
+
+function (cotire_select_unity_source_files _unityFile _sourcesVar)
+ set (_sourceFiles ${ARGN})
+ if (_sourceFiles AND "${_unityFile}" MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}_([0-9]+)_([0-9]+)")
+ set (_startIndex ${CMAKE_MATCH_1})
+ set (_endIndex ${CMAKE_MATCH_2})
+ list (LENGTH _sourceFiles _numberOfSources)
+ if (NOT _startIndex LESS _numberOfSources)
+ math (EXPR _startIndex "${_numberOfSources} - 1")
+ endif()
+ if (NOT _endIndex LESS _numberOfSources)
+ math (EXPR _endIndex "${_numberOfSources} - 1")
+ endif()
+ set (_files "")
+ foreach (_index RANGE ${_startIndex} ${_endIndex})
+ list (GET _sourceFiles ${_index} _file)
+ list (APPEND _files "${_file}")
+ endforeach()
+ else()
+ set (_files ${_sourceFiles})
+ endif()
+ set (${_sourcesVar} ${_files} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_unity_source_dependencies _language _target _dependencySourcesVar)
+ set (_dependencySources "")
+ # depend on target's generated source files
+ cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${ARGN})
+ if (_generatedSources)
+ # but omit all generated source files that have the COTIRE_EXCLUDED property set to true
+ cotire_get_objects_with_property_on(_excludedGeneratedSources COTIRE_EXCLUDED SOURCE ${_generatedSources})
+ if (_excludedGeneratedSources)
+ list (REMOVE_ITEM _generatedSources ${_excludedGeneratedSources})
+ endif()
+ # and omit all generated source files that have the COTIRE_DEPENDENCY property set to false explicitly
+ cotire_get_objects_with_property_off(_excludedNonDependencySources COTIRE_DEPENDENCY SOURCE ${_generatedSources})
+ if (_excludedNonDependencySources)
+ list (REMOVE_ITEM _generatedSources ${_excludedNonDependencySources})
+ endif()
+ if (_generatedSources)
+ list (APPEND _dependencySources ${_generatedSources})
+ endif()
+ endif()
+ if (COTIRE_DEBUG AND _dependencySources)
+ message (STATUS "${_language} ${_target} unity source depends on ${_dependencySources}")
+ endif()
+ set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE)
+endfunction()
+
+function (cotire_get_prefix_header_dependencies _language _target _dependencySourcesVar)
+ # depend on target source files marked with custom COTIRE_DEPENDENCY property
+ set (_dependencySources "")
+ cotire_get_objects_with_property_on(_dependencySources COTIRE_DEPENDENCY SOURCE ${ARGN})
+ if (COTIRE_DEBUG AND _dependencySources)
+ message (STATUS "${_language} ${_target} prefix header DEPENDS ${_dependencySources}")
+ endif()
+ set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE)
+endfunction()
+
+function (cotire_generate_target_script _language _configurations _targetSourceDir _targetBinaryDir _target _targetScriptVar)
+ set (COTIRE_TARGET_SOURCES ${ARGN})
+ get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME)
+ set (_targetCotireScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_moduleName}")
+ cotire_get_prefix_header_dependencies(${_language} ${_target} COTIRE_TARGET_PREFIX_DEPENDS ${COTIRE_TARGET_SOURCES})
+ cotire_get_unity_source_dependencies(${_language} ${_target} COTIRE_TARGET_UNITY_DEPENDS ${COTIRE_TARGET_SOURCES})
+ # set up variables to be configured
+ set (COTIRE_TARGET_LANGUAGE "${_language}")
+ cotire_determine_compiler_version("${COTIRE_TARGET_LANGUAGE}" COTIRE_${_language}_COMPILER)
+ get_target_property(COTIRE_TARGET_IGNORE_PATH ${_target} COTIRE_PREFIX_HEADER_IGNORE_PATH)
+ cotire_add_sys_root_paths(COTIRE_TARGET_IGNORE_PATH)
+ get_target_property(COTIRE_TARGET_INCLUDE_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PATH)
+ cotire_add_sys_root_paths(COTIRE_TARGET_INCLUDE_PATH)
+ get_target_property(COTIRE_TARGET_PRE_UNDEFS ${_target} COTIRE_UNITY_SOURCE_PRE_UNDEFS)
+ get_target_property(COTIRE_TARGET_POST_UNDEFS ${_target} COTIRE_UNITY_SOURCE_POST_UNDEFS)
+ get_target_property(COTIRE_TARGET_MAXIMUM_NUMBER_OF_INCLUDES ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES)
+ cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_PRE_UNDEFS COTIRE_TARGET_SOURCES_PRE_UNDEFS ${COTIRE_TARGET_SOURCES})
+ cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_POST_UNDEFS COTIRE_TARGET_SOURCES_POST_UNDEFS ${COTIRE_TARGET_SOURCES})
+ set (COTIRE_TARGET_CONFIGURATION_TYPES "${_configurations}")
+ foreach (_config ${_configurations})
+ string (TOUPPER "${_config}" _upperConfig)
+ cotire_get_target_include_directories(
+ "${_config}" "${_language}" "${_targetSourceDir}" "${_targetBinaryDir}" "${_target}" COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig})
+ cotire_get_target_compile_definitions(
+ "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig})
+ cotire_get_target_compiler_flags(
+ "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig})
+ cotire_get_source_files_compile_definitions(
+ "${_config}" "${_language}" COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig} ${COTIRE_TARGET_SOURCES})
+ endforeach()
+ get_cmake_property(_vars VARIABLES)
+ string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+" _matchVars "${_vars}")
+ # remove COTIRE_VERBOSE which is passed as a CMake define on command line
+ list (REMOVE_ITEM _matchVars COTIRE_VERBOSE)
+ set (_contents "")
+ foreach (_var IN LISTS _matchVars ITEMS
+ MSVC CMAKE_GENERATOR CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES
+ CMAKE_${_language}_COMPILER_ID CMAKE_${_language}_COMPILER CMAKE_${_language}_COMPILER_ARG1
+ CMAKE_${_language}_SOURCE_FILE_EXTENSIONS)
+ if (DEFINED ${_var})
+ string (REPLACE "\"" "\\\"" _value "${${_var}}")
+ set (_contents "${_contents}set (${_var} \"${_value}\")\n")
+ endif()
+ endforeach()
+ cotire_write_file("CMAKE" "${_targetCotireScript}" "${_contents}" FALSE)
+ set (${_targetScriptVar} "${_targetCotireScript}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_pch_file_compilation _language _targetBinaryDir _targetScript _prefixFile _pchFile)
+ set (_sourceFiles ${ARGN})
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ # for Visual Studio and Intel, we attach the precompiled header compilation to the first source file
+ # the remaining files include the precompiled header, see cotire_setup_prefix_file_inclusion
+ if (_sourceFiles)
+ file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative)
+ file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative)
+ list (GET _sourceFiles 0 _hostFile)
+ set (_flags "")
+ cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
+ cotire_add_pch_compilation_flags(
+ "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" "${_hostFile}" _flags)
+ set_property (SOURCE ${_hostFile} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
+ set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_OUTPUTS "${_pchFile}")
+ # make first source file depend on prefix header
+ set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}")
+ endif()
+ elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja")
+ # for makefile based generator, we add a custom command to precompile the prefix header
+ if (_targetScript)
+ cotire_set_cmd_to_prologue(_cmds)
+ list (GET _sourceFiles 0 _hostFile)
+ list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "precompile" "${_targetScript}" "${_prefixFile}" "${_pchFile}" "${_hostFile}")
+ file (RELATIVE_PATH _pchFileRelPath "${CMAKE_BINARY_DIR}" "${_pchFile}")
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: OUTPUT ${_pchFile} ${_cmds} DEPENDS ${_prefixFile} IMPLICIT_DEPENDS ${_language} ${_prefixFile}")
+ endif()
+ set_property (SOURCE "${_pchFile}" PROPERTY GENERATED TRUE)
+ add_custom_command(OUTPUT "${_pchFile}"
+ COMMAND ${_cmds}
+ DEPENDS "${_prefixFile}"
+ IMPLICIT_DEPENDS ${_language} "${_prefixFile}"
+ WORKING_DIRECTORY "${_targetSourceDir}"
+ COMMENT "Building ${_language} precompiled header ${_pchFileRelPath}" VERBATIM)
+ endif()
+ endif()
+endfunction()
+
+function (cotire_setup_prefix_file_inclusion _language _target _wholeTarget _prefixFile _pchFile)
+ set (_sourceFiles ${ARGN})
+ if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ # for Visual Studio and Intel, we include the precompiled header in all but the first source file
+ # the first source file does the precompiled header compilation, see cotire_setup_pch_file_compilation
+ list (LENGTH _sourceFiles _numberOfSourceFiles)
+ if (_numberOfSourceFiles GREATER 1)
+ # mark sources as cotired to prevent them from being used in another cotired target
+ set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}")
+ list (REMOVE_AT _sourceFiles 0)
+ set (_flags "")
+ cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
+ cotire_add_pch_inclusion_flags(
+ "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" _flags)
+ set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
+ # make source files depend on precompiled header
+ set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}")
+ endif()
+ elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja")
+ if (NOT _wholeTarget)
+ # for makefile based generator, we force the inclusion of the prefix header for a subset
+ # of the source files, if this is a multi-language target or has excluded files
+ set (_flags "")
+ cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
+ cotire_add_pch_inclusion_flags(
+ "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" _flags)
+ set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
+ # mark sources as cotired to prevent them from being used in another cotired target
+ set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}")
+ endif()
+ # make source files depend on precompiled header
+ set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}")
+ endif()
+endfunction()
+
+function (cotire_get_first_set_property_value _propertyValueVar _type _object)
+ set (_properties ${ARGN})
+ foreach (_property ${_properties})
+ get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property})
+ if (_propertyValue)
+ set (${_propertyValueVar} ${_propertyValue} PARENT_SCOPE)
+ return()
+ endif()
+ endforeach()
+ set (${_propertyValueVar} "" PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_combine_command _language _sourceDir _targetScript _joinedFile _cmdsVar)
+ set (_files ${ARGN})
+ set (_filesPaths "")
+ foreach (_file ${_files})
+ if (IS_ABSOLUTE "${_file}")
+ set (_filePath "${_file}")
+ else()
+ get_filename_component(_filePath "${_sourceDir}/${_file}" ABSOLUTE)
+ endif()
+ file (RELATIVE_PATH _fileRelPath "${_sourceDir}" "${_filePath}")
+ if (NOT IS_ABSOLUTE "${_fileRelPath}" AND NOT "${_fileRelPath}" MATCHES "^\\.\\.")
+ list (APPEND _filesPaths "${_fileRelPath}")
+ else()
+ list (APPEND _filesPaths "${_filePath}")
+ endif()
+ endforeach()
+ cotire_set_cmd_to_prologue(_prefixCmd)
+ list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "combine")
+ if (_targetScript)
+ list (APPEND _prefixCmd "${_targetScript}")
+ endif()
+ list (APPEND _prefixCmd "${_joinedFile}" ${_filesPaths})
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: OUTPUT ${_joinedFile} COMMAND ${_prefixCmd} DEPENDS ${_files}")
+ endif()
+ set_property (SOURCE "${_joinedFile}" PROPERTY GENERATED TRUE)
+ file (RELATIVE_PATH _joinedFileRelPath "${CMAKE_BINARY_DIR}" "${_joinedFile}")
+ get_filename_component(_joinedFileName "${_joinedFileRelPath}" NAME_WE)
+ if (_language AND _joinedFileName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$")
+ set (_comment "Generating ${_language} unity source ${_joinedFileRelPath}")
+ elseif (_language AND _joinedFileName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$")
+ set (_comment "Generating ${_language} prefix header ${_joinedFileRelPath}")
+ else()
+ set (_comment "Generating ${_joinedFileRelPath}")
+ endif()
+ add_custom_command(
+ OUTPUT "${_joinedFile}"
+ COMMAND ${_prefixCmd}
+ DEPENDS ${_files}
+ COMMENT "${_comment}"
+ WORKING_DIRECTORY "${_sourceDir}" VERBATIM)
+ list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd})
+ set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_target_pch_usage _languages _targetSourceDir _target _wholeTarget)
+ if (XCODE)
+ # for Xcode, we attach a pre-build action to generate the unity sources and prefix headers
+ # if necessary, we also generate a single prefix header which includes all language specific prefix headers
+ set (_prefixFiles "")
+ foreach (_language ${_languages})
+ get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER)
+ if (_prefixFile)
+ list (APPEND _prefixFiles "${_prefixFile}")
+ endif()
+ endforeach()
+ set (_cmds ${ARGN})
+ list (LENGTH _prefixFiles _numberOfPrefixFiles)
+ if (_numberOfPrefixFiles GREATER 1)
+ cotire_make_prefix_file_path("" ${_target} _prefixHeader)
+ cotire_setup_combine_command("" "${_targetSourceDir}" "" "${_prefixHeader}" _cmds ${_prefixFiles})
+ else()
+ set (_prefixHeader "${_prefixFiles}")
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: TARGET ${_target} PRE_BUILD ${_cmds}")
+ endif()
+ add_custom_command(TARGET "${_target}"
+ PRE_BUILD ${_cmds}
+ WORKING_DIRECTORY "${_targetSourceDir}"
+ COMMENT "Updating target ${_target} prefix headers" VERBATIM)
+ # make Xcode precompile the generated prefix header with ProcessPCH and ProcessPCH++
+ set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES")
+ set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${_prefixHeader}")
+ elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja")
+ # for makefile based generator, we force inclusion of the prefix header for all target source files
+ # if this is a single-language target without any excluded files
+ if (_wholeTarget)
+ set (_language "${_languages}")
+ # for Visual Studio and Intel, precompiled header inclusion is always done on the source file level
+ # see cotire_setup_prefix_file_inclusion
+ if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER)
+ get_property(_pchFile TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER)
+ set (_flags "")
+ cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER)
+ cotire_add_pch_inclusion_flags(
+ "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}"
+ "${_prefixFile}" "${_pchFile}" _flags)
+ set_property (TARGET ${_target} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ")
+ endif()
+ endif()
+ endif()
+endfunction()
+
+function (cotire_setup_unity_generation_commands _language _targetSourceDir _target _targetScript _unityFiles _cmdsVar)
+ set (_dependencySources "")
+ cotire_get_unity_source_dependencies(${_language} ${_target} _dependencySources ${ARGN})
+ foreach (_unityFile ${_unityFiles})
+ file (RELATIVE_PATH _unityFileRelPath "${CMAKE_BINARY_DIR}" "${_unityFile}")
+ set_property (SOURCE "${_unityFile}" PROPERTY GENERATED TRUE)
+ # set up compiled unity source dependencies
+ # this ensures that missing source files are generated before the unity file is compiled
+ if (COTIRE_DEBUG AND _dependencySources)
+ message (STATUS "${_unityFile} OBJECT_DEPENDS ${_dependencySources}")
+ endif()
+ if (_dependencySources)
+ set_property (SOURCE "${_unityFile}" PROPERTY OBJECT_DEPENDS ${_dependencySources})
+ endif()
+ if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ # unity file compilation results in potentially huge object file, thus use /bigobj by default unter MSVC and Windows Intel
+ set_property (SOURCE "${_unityFile}" APPEND_STRING PROPERTY COMPILE_FLAGS "/bigobj")
+ endif()
+ cotire_set_cmd_to_prologue(_unityCmd)
+ list (APPEND _unityCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "unity" "${_targetScript}" "${_unityFile}")
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: OUTPUT ${_unityFile} COMMAND ${_unityCmd} DEPENDS ${_targetScript}")
+ endif()
+ add_custom_command(
+ OUTPUT "${_unityFile}"
+ COMMAND ${_unityCmd}
+ DEPENDS "${_targetScript}"
+ COMMENT "Generating ${_language} unity source ${_unityFileRelPath}"
+ WORKING_DIRECTORY "${_targetSourceDir}" VERBATIM)
+ list (APPEND ${_cmdsVar} COMMAND ${_unityCmd})
+ endforeach()
+ list (LENGTH _unityFiles _numberOfUnityFiles)
+ if (_numberOfUnityFiles GREATER 1)
+ # create a joint unity file from all unity file segments
+ cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile)
+ cotire_setup_combine_command(${_language} "${_targetSourceDir}" "${_targetScript}" "${_unityFile}" ${_cmdsVar} ${_unityFiles})
+ endif()
+ set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_single_prefix_generation_command _language _target _targetSourceDir _targetScript _prefixFile _unityFile _cmdsVar)
+ set (_sourceFiles ${ARGN})
+ set (_dependencySources "")
+ cotire_get_prefix_header_dependencies(${_language} ${_target} _dependencySources ${_sourceFiles})
+ cotire_set_cmd_to_prologue(_prefixCmd)
+ list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "prefix" "${_targetScript}" "${_prefixFile}" "${_unityFile}")
+ set_property (SOURCE "${_prefixFile}" PROPERTY GENERATED TRUE)
+ if (COTIRE_DEBUG)
+ message (STATUS "add_custom_command: OUTPUT ${_prefixFile} COMMAND ${_prefixCmd} DEPENDS ${_targetScript} ${_unityFile} ${_dependencySources}")
+ endif()
+ file (RELATIVE_PATH _prefixFileRelPath "${CMAKE_BINARY_DIR}" "${_prefixFile}")
+ add_custom_command(
+ OUTPUT "${_prefixFile}" "${_prefixFile}.log"
+ COMMAND ${_prefixCmd}
+ DEPENDS "${_targetScript}" "${_unityFile}" ${_dependencySources}
+ COMMENT "Generating ${_language} prefix header ${_prefixFileRelPath}"
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM)
+ list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd})
+ set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_multi_prefix_generation_command _language _target _targetSourceDir _targetScript _prefixFile _unityFiles _cmdsVar)
+ set (_sourceFiles ${ARGN})
+ list (LENGTH _unityFiles _numberOfUnityFiles)
+ if (_numberOfUnityFiles GREATER 1)
+ cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile)
+ cotire_setup_single_prefix_generation_command(
+ ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}"
+ "${_prefixFile}" "${_unityFile}" ${_cmdsVar} ${_sourceFiles})
+ else()
+ cotire_setup_single_prefix_generation_command(
+ ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}"
+ "${_prefixFile}" "${_unityFiles}" ${_cmdsVar} ${_sourceFiles})
+ endif()
+ set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE)
+endfunction()
+
+function (cotire_init_cotire_target_properties _target)
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER TRUE)
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD TRUE)
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN FALSE)
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_SOURCE_DIR}")
+ cotire_check_is_path_relative_to("${CMAKE_BINARY_DIR}" _isRelative "${CMAKE_SOURCE_DIR}")
+ if (NOT _isRelative)
+ set_property(TARGET ${_target} APPEND PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_BINARY_DIR}")
+ endif()
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH "")
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS "")
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS SET)
+ if (NOT _isSet)
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS "")
+ endif()
+ get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES SET)
+ if (NOT _isSet)
+ if (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES)
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}")
+ else()
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "")
+ endif()
+ endif()
+endfunction()
+
+function (cotire_make_target_message _target _languages _disableMsg _targetMsgVar)
+ get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
+ get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD)
+ string (REPLACE ";" " " _languagesStr "${_languages}")
+ string (REPLACE ";" ", " _excludedStr "${ARGN}")
+ set (_targetMsg "")
+ if (NOT _languages)
+ set (_targetMsg "Target ${_target} cannot be cotired.")
+ if (_disableMsg)
+ set (_targetMsg "${_targetMsg} ${_disableMsg}")
+ endif()
+ elseif (NOT _targetUsePCH AND NOT _targetAddSCU)
+ set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build and precompiled header.")
+ if (_disableMsg)
+ set (_targetMsg "${_targetMsg} ${_disableMsg}")
+ endif()
+ elseif (NOT _targetUsePCH)
+ if (_allExcludedSourceFiles)
+ set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without precompiled header.")
+ else()
+ set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header.")
+ endif()
+ if (_disableMsg)
+ set (_targetMsg "${_targetMsg} ${_disableMsg}")
+ endif()
+ elseif (NOT _targetAddSCU)
+ if (_allExcludedSourceFiles)
+ set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without unity build.")
+ else()
+ set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build.")
+ endif()
+ else()
+ if (_allExcludedSourceFiles)
+ set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr}.")
+ else()
+ set (_targetMsg "${_languagesStr} target ${_target} cotired.")
+ endif()
+ endif()
+ set (${_targetMsgVar} "${_targetMsg}" PARENT_SCOPE)
+endfunction()
+
+function (cotire_choose_target_languages _targetSourceDir _target _targetLanguagesVar)
+ set (_languages ${ARGN})
+ set (_allSourceFiles "")
+ set (_allExcludedSourceFiles "")
+ set (_allCotiredSourceFiles "")
+ set (_targetLanguages "")
+ get_target_property(_targetType ${_target} TYPE)
+ get_target_property(_targetSourceFiles ${_target} SOURCES)
+ get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
+ get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD)
+ set (_disableMsg "")
+ foreach (_language ${_languages})
+ get_target_property(_prefixHeader ${_target} COTIRE_${_language}_PREFIX_HEADER)
+ get_target_property(_unityBuildFile ${_target} COTIRE_${_language}_UNITY_SOURCE)
+ if (_prefixHeader OR _unityBuildFile)
+ message (WARNING "Target ${_target} has already been cotired.")
+ set (${_targetLanguagesVar} "" PARENT_SCOPE)
+ return()
+ endif()
+ if (_targetUsePCH AND "${_language}" STREQUAL "C" OR "${_language}" STREQUAL "CXX")
+ cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _disableMsg)
+ if (_disableMsg)
+ set (_targetUsePCH FALSE)
+ endif()
+ endif()
+ set (_sourceFiles "")
+ set (_excludedSources "")
+ set (_cotiredSources "")
+ cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles})
+ if (_sourceFiles OR _excludedSources OR _cotiredSources)
+ list (APPEND _targetLanguages ${_language})
+ endif()
+ if (_sourceFiles)
+ list (APPEND _allSourceFiles ${_sourceFiles})
+ endif()
+ if (_excludedSources)
+ list (APPEND _allExcludedSourceFiles ${_excludedSources})
+ endif()
+ if (_cotiredSources)
+ list (APPEND _allCotiredSourceFiles ${_cotiredSources})
+ endif()
+ endforeach()
+ set (_targetMsgLevel STATUS)
+ if (NOT _targetLanguages)
+ string (REPLACE ";" " or " _languagesStr "${_languages}")
+ set (_disableMsg "No ${_languagesStr} source files.")
+ set (_targetUsePCH FALSE)
+ set (_targetAddSCU FALSE)
+ endif()
+ if (_targetUsePCH)
+ list (LENGTH _allSourceFiles _numberOfSources)
+ if (_numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES})
+ set (_disableMsg "Too few applicable sources.")
+ set (_targetUsePCH FALSE)
+ elseif (_allCotiredSourceFiles)
+ cotire_get_source_file_property_values(_cotireTargets COTIRE_TARGET ${_allCotiredSourceFiles})
+ list (REMOVE_DUPLICATES _cotireTargets)
+ string (REPLACE ";" ", " _cotireTargetsStr "${_cotireTargets}")
+ set (_disableMsg "Target sources already include a precompiled header for target(s) ${_cotireTargets}.")
+ set (_disableMsg "${_disableMsg} Set target property COTIRE_ENABLE_PRECOMPILED_HEADER to FALSE for targets ${_target},")
+ set (_disableMsg "${_disableMsg} ${_cotireTargetsStr} to get a workable build system.")
+ set (_targetMsgLevel SEND_ERROR)
+ set (_targetUsePCH FALSE)
+ elseif (XCODE AND _allExcludedSourceFiles)
+ # for Xcode, we cannot apply the precompiled header to individual sources, only to the whole target
+ set (_disableMsg "Exclusion of source files not supported for generator Xcode.")
+ set (_targetUsePCH FALSE)
+ elseif (XCODE AND "${_targetType}" STREQUAL "OBJECT_LIBRARY")
+ # for Xcode, we cannot apply the required PRE_BUILD action to generate the prefix header to an OBJECT_LIBRARY target
+ set (_disableMsg "Required PRE_BUILD action not supported for OBJECT_LIBRARY targets for generator Xcode.")
+ set (_targetUsePCH FALSE)
+ endif()
+ endif()
+ set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER ${_targetUsePCH})
+ set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD ${_targetAddSCU})
+ cotire_make_target_message(${_target} "${_targetLanguages}" "${_disableMsg}" _targetMsg ${_allExcludedSourceFiles})
+ if (_targetMsg)
+ if (NOT DEFINED COTIREMSG_${_target})
+ set (COTIREMSG_${_target} "")
+ endif()
+ if (COTIRE_VERBOSE OR NOT "${_targetMsgLevel}" STREQUAL "STATUS" OR
+ NOT "${COTIREMSG_${_target}}" STREQUAL "${_targetMsg}")
+ # cache message to avoid redundant messages on re-configure
+ set (COTIREMSG_${_target} "${_targetMsg}" CACHE INTERNAL "${_target} cotire message.")
+ message (${_targetMsgLevel} "${_targetMsg}")
+ endif()
+ endif()
+ set (${_targetLanguagesVar} ${_targetLanguages} PARENT_SCOPE)
+endfunction()
+
+function (cotire_compute_unity_max_number_of_includes _target _maxIncludesVar)
+ set (_sourceFiles ${ARGN})
+ get_target_property(_maxIncludes ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES)
+ if (_maxIncludes MATCHES "(-j|--parallel|--jobs) ?([0-9]*)")
+ set (_numberOfThreads "${CMAKE_MATCH_2}")
+ if (NOT _numberOfThreads)
+ # use all available cores
+ ProcessorCount(_numberOfThreads)
+ endif()
+ list (LENGTH _sourceFiles _numberOfSources)
+ math (EXPR _maxIncludes "(${_numberOfSources} + ${_numberOfThreads} - 1) / ${_numberOfThreads}")
+ # a unity source segment must not contain less than COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES files
+ if (_maxIncludes LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES})
+ set (_maxIncludes ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES})
+ endif()
+ elseif (NOT _maxIncludes MATCHES "[0-9]+")
+ set (_maxIncludes 0)
+ endif()
+ if (COTIRE_DEBUG)
+ message (STATUS "${_target} unity source max includes = ${_maxIncludes}")
+ endif()
+ set (${_maxIncludesVar} ${_maxIncludes} PARENT_SCOPE)
+endfunction()
+
+function (cotire_process_target_language _language _configurations _targetSourceDir _targetBinaryDir _target _wholeTargetVar _cmdsVar)
+ set (${_cmdsVar} "" PARENT_SCOPE)
+ get_target_property(_targetSourceFiles ${_target} SOURCES)
+ set (_sourceFiles "")
+ set (_excludedSources "")
+ set (_cotiredSources "")
+ cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles})
+ if (NOT _sourceFiles AND NOT _cotiredSources)
+ return()
+ endif()
+ set (_wholeTarget ${${_wholeTargetVar}})
+ set (_cmds "")
+ # check for user provided unity source file list
+ get_property(_unitySourceFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE_INIT)
+ if (NOT _unitySourceFiles)
+ set (_unitySourceFiles ${_sourceFiles} ${_cotiredSources})
+ endif()
+ cotire_generate_target_script(
+ ${_language} "${_configurations}" "${_targetSourceDir}" "${_targetBinaryDir}" ${_target} _targetScript ${_unitySourceFiles})
+ cotire_compute_unity_max_number_of_includes(${_target} _maxIncludes ${_unitySourceFiles})
+ cotire_make_unity_source_file_paths(${_language} ${_target} ${_maxIncludes} _unityFiles ${_unitySourceFiles})
+ if (NOT _unityFiles)
+ return()
+ endif()
+ cotire_setup_unity_generation_commands(
+ ${_language} "${_targetSourceDir}" ${_target} "${_targetScript}" "${_unityFiles}" _cmds ${_unitySourceFiles})
+ cotire_make_prefix_file_path(${_language} ${_target} _prefixFile)
+ if (_prefixFile)
+ # check for user provided prefix header files
+ get_property(_prefixHeaderFiles TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT)
+ if (_prefixHeaderFiles)
+ cotire_setup_combine_command(${_language} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" _cmds ${_prefixHeaderFiles})
+ else()
+ cotire_setup_multi_prefix_generation_command(
+ ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_unityFiles}" _cmds ${_unitySourceFiles})
+ endif()
+ get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
+ if (_targetUsePCH)
+ cotire_make_pch_file_path(${_language} "${_targetSourceDir}" ${_target} _pchFile)
+ if (_pchFile)
+ cotire_setup_pch_file_compilation(
+ ${_language} "${_targetBinaryDir}" "${_targetScript}" "${_prefixFile}" "${_pchFile}" ${_sourceFiles})
+ if (_excludedSources)
+ set (_wholeTarget FALSE)
+ endif()
+ cotire_setup_prefix_file_inclusion(
+ ${_language} ${_target} ${_wholeTarget} "${_prefixFile}" "${_pchFile}" ${_sourceFiles})
+ endif()
+ endif()
+ endif()
+ # mark target as cotired for language
+ set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE "${_unityFiles}")
+ if (_prefixFile)
+ set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER "${_prefixFile}")
+ if (_targetUsePCH AND _pchFile)
+ set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER "${_pchFile}")
+ endif()
+ endif()
+ set (${_wholeTargetVar} ${_wholeTarget} PARENT_SCOPE)
+ set (${_cmdsVar} ${_cmds} PARENT_SCOPE)
+endfunction()
+
+function (cotire_setup_clean_target _target)
+ set (_cleanTargetName "${_target}${COTIRE_CLEAN_TARGET_SUFFIX}")
+ if (NOT TARGET "${_cleanTargetName}")
+ cotire_set_cmd_to_prologue(_cmds)
+ get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" ABSOLUTE)
+ list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${_outputDir}" "${COTIRE_INTDIR}" "${_target}")
+ add_custom_target(${_cleanTargetName} COMMAND ${_cmds} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
+ COMMENT "Cleaning up target ${_target} cotire generated files" VERBATIM)
+ cotire_init_target("${_cleanTargetName}")
+ endif()
+endfunction()
+
+function (cotire_setup_pch_target _languages _configurations _target)
+ if ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja")
+ # for makefile based generators, we add a custom target to trigger the generation of the cotire related files
+ set (_dependsFiles "")
+ foreach (_language ${_languages})
+ set (_props COTIRE_${_language}_PREFIX_HEADER COTIRE_${_language}_UNITY_SOURCE)
+ if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel")
+ # Visual Studio and Intel only create precompiled header as a side effect
+ list (INSERT _props 0 COTIRE_${_language}_PRECOMPILED_HEADER)
+ endif()
+ cotire_get_first_set_property_value(_dependsFile TARGET ${_target} ${_props})
+ if (_dependsFile)
+ list (APPEND _dependsFiles "${_dependsFile}")
+ endif()
+ endforeach()
+ if (_dependsFiles)
+ set (_pchTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}")
+ add_custom_target("${_pchTargetName}" DEPENDS ${_dependsFiles})
+ cotire_init_target("${_pchTargetName}")
+ cotire_add_to_pch_all_target(${_pchTargetName})
+ endif()
+ else()
+ # for other generators, we add the "clean all" target to clean up the precompiled header
+ cotire_setup_clean_all_target()
+ endif()
+endfunction()
+
+function (cotire_setup_unity_build_target _languages _configurations _targetSourceDir _target)
+ get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME)
+ if (NOT _unityTargetName)
+ set (_unityTargetName "${_target}${COTIRE_UNITY_BUILD_TARGET_SUFFIX}")
+ endif()
+ # determine unity target sub type
+ get_target_property(_targetType ${_target} TYPE)
+ if ("${_targetType}" STREQUAL "EXECUTABLE")
+ get_target_property(_isWin32 ${_target} WIN32_EXECUTABLE)
+ get_target_property(_isMacOSX_Bundle ${_target} MACOSX_BUNDLE)
+ if (_isWin32)
+ set (_unityTargetSubType WIN32)
+ elseif (_isMacOSX_Bundle)
+ set (_unityTargetSubType MACOSX_BUNDLE)
+ else()
+ set (_unityTargetSubType "")
+ endif()
+ elseif (_targetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY")
+ set (_unityTargetSubType "${CMAKE_MATCH_1}")
+ else()
+ message (WARNING "Unknown target type ${_targetType}.")
+ return()
+ endif()
+ # determine unity target sources
+ get_target_property(_targetSourceFiles ${_target} SOURCES)
+ set (_unityTargetSources ${_targetSourceFiles})
+ foreach (_language ${_languages})
+ get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE)
+ if (_unityFiles)
+ # remove source files that are included in the unity source
+ set (_sourceFiles "")
+ set (_excludedSources "")
+ set (_cotiredSources "")
+ cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles})
+ if (_sourceFiles OR _cotiredSources)
+ list (REMOVE_ITEM _unityTargetSources ${_sourceFiles} ${_cotiredSources})
+ endif()
+ # if cotire is applied to a target which has not been added in the current source dir,
+ # non-existing files cannot be referenced from the unity build target (this is a CMake restriction)
+ if (NOT "${_targetSourceDir}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}")
+ set (_nonExistingFiles "")
+ foreach (_file ${_unityTargetSources})
+ if (NOT EXISTS "${_file}")
+ list (APPEND _nonExistingFiles "${_file}")
+ endif()
+ endforeach()
+ if (_nonExistingFiles)
+ if (COTIRE_VERBOSE)
+ message (STATUS "removing non-existing ${_nonExistingFiles} from ${_unityTargetName}")
+ endif()
+ list (REMOVE_ITEM _unityTargetSources ${_nonExistingFiles})
+ endif()
+ endif()
+ # add unity source files instead
+ list (APPEND _unityTargetSources ${_unityFiles})
+ endif()
+ endforeach()
+ if (COTIRE_DEBUG)
+ message (STATUS "add ${_targetType} ${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}")
+ endif()
+ # generate unity target
+ if ("${_targetType}" STREQUAL "EXECUTABLE")
+ add_executable(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources})
+ else()
+ add_library(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources})
+ endif()
+ set (_outputDirProperties
+ ARCHIVE_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY_<CONFIG>
+ LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY_<CONFIG>
+ RUNTIME_OUTPUT_DIRECTORY RUNTIME_OUTPUT_DIRECTORY_<CONFIG>)
+ # copy output location properties
+ if (COTIRE_UNITY_OUTPUT_DIRECTORY)
+ set (_setDefaultOutputDir TRUE)
+ if (IS_ABSOLUTE "${COTIRE_UNITY_OUTPUT_DIRECTORY}")
+ set (_outputDir "${COTIRE_UNITY_OUTPUT_DIRECTORY}")
+ else()
+ cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties})
+ cotrie_resolve_config_properites("${_configurations}" _properties ${_outputDirProperties})
+ foreach (_property ${_properties})
+ get_property(_outputDir TARGET ${_target} PROPERTY ${_property})
+ if (_outputDir)
+ get_filename_component(_outputDir "${_outputDir}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE)
+ set_property(TARGET ${_unityTargetName} PROPERTY ${_property} "${_outputDir}")
+ set (_setDefaultOutputDir FALSE)
+ endif()
+ endforeach()
+ if (_setDefaultOutputDir)
+ get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE)
+ endif()
+ endif()
+ if (_setDefaultOutputDir)
+ set_target_properties(${_unityTargetName} PROPERTIES
+ ARCHIVE_OUTPUT_DIRECTORY "${_outputDir}"
+ LIBRARY_OUTPUT_DIRECTORY "${_outputDir}"
+ RUNTIME_OUTPUT_DIRECTORY "${_outputDir}")
+ endif()
+ else()
+ cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties})
+ endif()
+ # copy output name
+ cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ ARCHIVE_OUTPUT_NAME ARCHIVE_OUTPUT_NAME_<CONFIG>
+ LIBRARY_OUTPUT_NAME LIBRARY_OUTPUT_NAME_<CONFIG>
+ OUTPUT_NAME OUTPUT_NAME_<CONFIG>
+ RUNTIME_OUTPUT_NAME RUNTIME_OUTPUT_NAME_<CONFIG>
+ PREFIX <CONFIG>_POSTFIX SUFFIX)
+ # copy compile stuff
+ cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ COMPILE_DEFINITIONS COMPILE_DEFINITIONS_<CONFIG>
+ COMPILE_FLAGS Fortran_FORMAT
+ INCLUDE_DIRECTORIES
+ INTERPROCEDURAL_OPTIMIZATION INTERPROCEDURAL_OPTIMIZATION_<CONFIG>
+ POSITION_INDEPENDENT_CODE)
+ # copy link stuff
+ cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ BUILD_WITH_INSTALL_RPATH INSTALL_RPATH INSTALL_RPATH_USE_LINK_PATH SKIP_BUILD_RPATH
+ LINKER_LANGUAGE LINK_DEPENDS
+ LINK_FLAGS LINK_FLAGS_<CONFIG>
+ LINK_INTERFACE_LIBRARIES LINK_INTERFACE_LIBRARIES_<CONFIG>
+ LINK_INTERFACE_MULTIPLICITY LINK_INTERFACE_MULTIPLICITY_<CONFIG>
+ LINK_SEARCH_START_STATIC LINK_SEARCH_END_STATIC
+ STATIC_LIBRARY_FLAGS STATIC_LIBRARY_FLAGS_<CONFIG>
+ NO_SONAME SOVERSION VERSION)
+ # copy Qt stuff
+ cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ AUTOMOC AUTOMOC_MOC_OPTIONS)
+ # copy cmake stuff
+ cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ IMPLICIT_DEPENDS_INCLUDE_TRANSFORM RULE_LAUNCH_COMPILE RULE_LAUNCH_CUSTOM RULE_LAUNCH_LINK)
+ # copy platform stuff
+ if (APPLE)
+ cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ BUNDLE BUNDLE_EXTENSION FRAMEWORK INSTALL_NAME_DIR MACOSX_BUNDLE_INFO_PLIST MACOSX_FRAMEWORK_INFO_PLIST
+ OSX_ARCHITECTURES OSX_ARCHITECTURES_<CONFIG> PRIVATE_HEADER PUBLIC_HEADER RESOURCE)
+ elseif (WIN32)
+ cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName}
+ GNUtoMS
+ PDB_NAME PDB_NAME_<CONFIG> PDB_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY_<CONFIG>
+ VS_DOTNET_REFERENCES VS_GLOBAL_KEYWORD VS_GLOBAL_PROJECT_TYPES VS_KEYWORD
+ VS_SCC_AUXPATH VS_SCC_LOCALPATH VS_SCC_PROJECTNAME VS_SCC_PROVIDER
+ VS_WINRT_EXTENSIONS VS_WINRT_REFERENCES)
+ endif()
+ # use output name from original target
+ get_target_property(_targetOutputName ${_unityTargetName} OUTPUT_NAME)
+ if (NOT _targetOutputName)
+ set_property(TARGET ${_unityTargetName} PROPERTY OUTPUT_NAME "${_target}")
+ endif()
+ # use export symbol from original target
+ cotire_get_target_export_symbol("${_target}" _defineSymbol)
+ if (_defineSymbol)
+ set_property(TARGET ${_unityTargetName} PROPERTY DEFINE_SYMBOL "${_defineSymbol}")
+ if ("${_targetType}" STREQUAL "EXECUTABLE")
+ set_property(TARGET ${_unityTargetName} PROPERTY ENABLE_EXPORTS TRUE)
+ endif()
+ endif()
+ cotire_init_target(${_unityTargetName})
+ cotire_add_to_unity_all_target(${_unityTargetName})
+ set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_TARGET_NAME "${_unityTargetName}")
+endfunction(cotire_setup_unity_build_target)
+
+function (cotire_target _target)
+ set(_options "")
+ set(_oneValueArgs SOURCE_DIR BINARY_DIR)
+ set(_multiValueArgs LANGUAGES CONFIGURATIONS)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ if (NOT _option_SOURCE_DIR)
+ set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ endif()
+ if (NOT _option_BINARY_DIR)
+ set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+ if (NOT _option_LANGUAGES)
+ get_property (_option_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES)
+ endif()
+ if (NOT _option_CONFIGURATIONS)
+ if (CMAKE_CONFIGURATION_TYPES)
+ set (_option_CONFIGURATIONS ${CMAKE_CONFIGURATION_TYPES})
+ elseif (CMAKE_BUILD_TYPE)
+ set (_option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}")
+ else()
+ set (_option_CONFIGURATIONS "None")
+ endif()
+ endif()
+ # trivial checks
+ get_target_property(_imported ${_target} IMPORTED)
+ if (_imported)
+ message (WARNING "Imported target ${_target} cannot be cotired.")
+ return()
+ endif()
+ # check if target needs to be cotired for build type
+ # when using configuration types, the test is performed at build time
+ cotire_init_cotire_target_properties(${_target})
+ if (NOT CMAKE_CONFIGURATION_TYPES)
+ if (CMAKE_BUILD_TYPE)
+ list (FIND _option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}" _index)
+ else()
+ list (FIND _option_CONFIGURATIONS "None" _index)
+ endif()
+ if (_index EQUAL -1)
+ if (COTIRE_DEBUG)
+ message (STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} not cotired (${_option_CONFIGURATIONS})")
+ endif()
+ return()
+ endif()
+ endif()
+ # choose languages that apply to the target
+ cotire_choose_target_languages("${_option_SOURCE_DIR}" "${_target}" _targetLanguages ${_option_LANGUAGES})
+ if (NOT _targetLanguages)
+ return()
+ endif()
+ list (LENGTH _targetLanguages _numberOfLanguages)
+ if (_numberOfLanguages GREATER 1)
+ set (_wholeTarget FALSE)
+ else()
+ set (_wholeTarget TRUE)
+ endif()
+ set (_cmds "")
+ foreach (_language ${_targetLanguages})
+ cotire_process_target_language("${_language}" "${_option_CONFIGURATIONS}"
+ "${_option_SOURCE_DIR}" "${_option_BINARY_DIR}" ${_target} _wholeTarget _cmd)
+ if (_cmd)
+ list (APPEND _cmds ${_cmd})
+ endif()
+ endforeach()
+ get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD)
+ if (_targetAddSCU)
+ cotire_setup_unity_build_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" "${_option_SOURCE_DIR}" ${_target})
+ endif()
+ get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER)
+ if (_targetUsePCH)
+ cotire_setup_target_pch_usage("${_targetLanguages}" "${_option_SOURCE_DIR}" ${_target} ${_wholeTarget} ${_cmds})
+ cotire_setup_pch_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target})
+ endif()
+ get_target_property(_targetAddCleanTarget ${_target} COTIRE_ADD_CLEAN)
+ if (_targetAddCleanTarget)
+ cotire_setup_clean_target(${_target})
+ endif()
+endfunction()
+
+function (cotire_cleanup _binaryDir _cotireIntermediateDirName _targetName)
+ if (_targetName)
+ file (GLOB_RECURSE _cotireFiles "${_binaryDir}/${_targetName}*.*")
+ else()
+ file (GLOB_RECURSE _cotireFiles "${_binaryDir}/*.*")
+ endif()
+ # filter files in intermediate directory
+ set (_filesToRemove "")
+ foreach (_file ${_cotireFiles})
+ get_filename_component(_dir "${_file}" PATH)
+ get_filename_component(_dirName "${_dir}" NAME)
+ if ("${_dirName}" STREQUAL "${_cotireIntermediateDirName}")
+ list (APPEND _filesToRemove "${_file}")
+ endif()
+ endforeach()
+ if (_filesToRemove)
+ if (COTIRE_VERBOSE)
+ message (STATUS "removing ${_filesToRemove}")
+ endif()
+ file (REMOVE ${_filesToRemove})
+ endif()
+endfunction()
+
+function (cotire_init_target _targetName)
+ if (COTIRE_TARGETS_FOLDER)
+ set_target_properties(${_targetName} PROPERTIES FOLDER "${COTIRE_TARGETS_FOLDER}")
+ endif()
+ if (MSVC_IDE)
+ set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE)
+ endif()
+endfunction()
+
+function (cotire_add_to_pch_all_target _pchTargetName)
+ set (_targetName "${COTIRE_PCH_ALL_TARGET_NAME}")
+ if (NOT TARGET "${_targetName}")
+ add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM)
+ cotire_init_target("${_targetName}")
+ endif()
+ cotire_setup_clean_all_target()
+ add_dependencies(${_targetName} ${_pchTargetName})
+endfunction()
+
+function (cotire_add_to_unity_all_target _unityTargetName)
+ set (_targetName "${COTIRE_UNITY_BUILD_ALL_TARGET_NAME}")
+ if (NOT TARGET "${_targetName}")
+ add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM)
+ cotire_init_target("${_targetName}")
+ endif()
+ cotire_setup_clean_all_target()
+ add_dependencies(${_targetName} ${_unityTargetName})
+endfunction()
+
+function (cotire_setup_clean_all_target)
+ set (_targetName "${COTIRE_CLEAN_ALL_TARGET_NAME}")
+ if (NOT TARGET "${_targetName}")
+ cotire_set_cmd_to_prologue(_cmds)
+ list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${CMAKE_BINARY_DIR}" "${COTIRE_INTDIR}")
+ add_custom_target(${_targetName} COMMAND ${_cmds}
+ WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" COMMENT "Cleaning up all cotire generated files" VERBATIM)
+ cotire_init_target("${_targetName}")
+ endif()
+endfunction()
+
+function (cotire)
+ set(_options "")
+ set(_oneValueArgs SOURCE_DIR BINARY_DIR)
+ set(_multiValueArgs LANGUAGES CONFIGURATIONS)
+ cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
+ set (_targets ${_option_UNPARSED_ARGUMENTS})
+ if (NOT _option_SOURCE_DIR)
+ set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+ endif()
+ if (NOT _option_BINARY_DIR)
+ set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}")
+ endif()
+ foreach (_target ${_targets})
+ if (TARGET ${_target})
+ cotire_target(${_target} LANGUAGES ${_option_LANGUAGES} CONFIGURATIONS ${_option_CONFIGURATIONS}
+ SOURCE_DIR "${_option_SOURCE_DIR}" BINARY_DIR "${_option_BINARY_DIR}")
+ else()
+ message (WARNING "${_target} is not a target")
+ endif()
+ endforeach()
+endfunction()
+
+if (CMAKE_SCRIPT_MODE_FILE)
+
+ # cotire is being run in script mode
+ # locate -P on command args
+ set (COTIRE_ARGC -1)
+ foreach (_index RANGE ${CMAKE_ARGC})
+ if (COTIRE_ARGC GREATER -1)
+ set (COTIRE_ARGV${COTIRE_ARGC} "${CMAKE_ARGV${_index}}")
+ math (EXPR COTIRE_ARGC "${COTIRE_ARGC} + 1")
+ elseif ("${CMAKE_ARGV${_index}}" STREQUAL "-P")
+ set (COTIRE_ARGC 0)
+ endif()
+ endforeach()
+
+ # include target script if available
+ if ("${COTIRE_ARGV2}" MATCHES "\\.cmake$")
+ # the included target scripts sets up additional variables relating to the target (e.g., COTIRE_TARGET_SOURCES)
+ include("${COTIRE_ARGV2}")
+ endif()
+
+ if (COTIRE_DEBUG)
+ message (STATUS "${COTIRE_ARGV0} ${COTIRE_ARGV1} ${COTIRE_ARGV2} ${COTIRE_ARGV3} ${COTIRE_ARGV4} ${COTIRE_ARGV5}")
+ endif()
+
+ if (WIN32)
+ # for MSVC, compiler IDs may not always be set correctly
+ if (MSVC)
+ set (CMAKE_C_COMPILER_ID "MSVC")
+ set (CMAKE_CXX_COMPILER_ID "MSVC")
+ endif()
+ endif()
+
+ if (NOT COTIRE_BUILD_TYPE)
+ set (COTIRE_BUILD_TYPE "None")
+ endif()
+ string (TOUPPER "${COTIRE_BUILD_TYPE}" _upperConfig)
+ set (_includeDirs ${COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}})
+ set (_compileDefinitions ${COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}})
+ set (_compileFlags ${COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}})
+ # check if target has been cotired for actual build type COTIRE_BUILD_TYPE
+ list (FIND COTIRE_TARGET_CONFIGURATION_TYPES "${COTIRE_BUILD_TYPE}" _index)
+ if (_index GREATER -1)
+ set (_sources ${COTIRE_TARGET_SOURCES})
+ set (_sourcesDefinitions ${COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig}})
+ else()
+ if (COTIRE_DEBUG)
+ message (STATUS "COTIRE_BUILD_TYPE=${COTIRE_BUILD_TYPE} not cotired (${COTIRE_TARGET_CONFIGURATION_TYPES})")
+ endif()
+ set (_sources "")
+ set (_sourcesDefinitions "")
+ endif()
+ set (_targetPreUndefs ${COTIRE_TARGET_PRE_UNDEFS})
+ set (_targetPostUndefs ${COTIRE_TARGET_POST_UNDEFS})
+ set (_sourcesPreUndefs ${COTIRE_TARGET_SOURCES_PRE_UNDEFS})
+ set (_sourcesPostUndefs ${COTIRE_TARGET_SOURCES_POST_UNDEFS})
+
+ if ("${COTIRE_ARGV1}" STREQUAL "unity")
+
+ cotire_select_unity_source_files("${COTIRE_ARGV3}" _sources ${_sources})
+ cotire_generate_unity_source(
+ "${COTIRE_ARGV3}" ${_sources}
+ LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
+ DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV2}"
+ SOURCES_COMPILE_DEFINITIONS ${_sourcesDefinitions}
+ PRE_UNDEFS ${_targetPreUndefs}
+ POST_UNDEFS ${_targetPostUndefs}
+ SOURCES_PRE_UNDEFS ${_sourcesPreUndefs}
+ SOURCES_POST_UNDEFS ${_sourcesPostUndefs})
+
+ elseif ("${COTIRE_ARGV1}" STREQUAL "prefix")
+
+ set (_files "")
+ foreach (_index RANGE 4 ${COTIRE_ARGC})
+ if (COTIRE_ARGV${_index})
+ list (APPEND _files "${COTIRE_ARGV${_index}}")
+ endif()
+ endforeach()
+
+ cotire_generate_prefix_header(
+ "${COTIRE_ARGV3}" ${_files}
+ COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}"
+ COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1}
+ COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}"
+ COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}"
+ LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
+ DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV4}" ${COTIRE_TARGET_PREFIX_DEPENDS}
+ IGNORE_PATH "${COTIRE_TARGET_IGNORE_PATH};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH}"
+ INCLUDE_PATH ${COTIRE_TARGET_INCLUDE_PATH}
+ IGNORE_EXTENSIONS "${CMAKE_${COTIRE_TARGET_LANGUAGE}_SOURCE_FILE_EXTENSIONS};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS}"
+ INCLUDE_DIRECTORIES ${_includeDirs}
+ COMPILE_DEFINITIONS ${_compileDefinitions}
+ COMPILE_FLAGS ${_compileFlags})
+
+ elseif ("${COTIRE_ARGV1}" STREQUAL "precompile")
+
+ set (_files "")
+ foreach (_index RANGE 5 ${COTIRE_ARGC})
+ if (COTIRE_ARGV${_index})
+ list (APPEND _files "${COTIRE_ARGV${_index}}")
+ endif()
+ endforeach()
+
+ cotire_precompile_prefix_header(
+ "${COTIRE_ARGV3}" "${COTIRE_ARGV4}" "${COTIRE_ARGV5}"
+ COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}"
+ COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1}
+ COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}"
+ COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}"
+ LANGUAGE "${COTIRE_TARGET_LANGUAGE}"
+ INCLUDE_DIRECTORIES ${_includeDirs}
+ COMPILE_DEFINITIONS ${_compileDefinitions}
+ COMPILE_FLAGS ${_compileFlags})
+
+ elseif ("${COTIRE_ARGV1}" STREQUAL "combine")
+
+ if (COTIRE_TARGET_LANGUAGE)
+ set (_startIndex 3)
+ else()
+ set (_startIndex 2)
+ endif()
+ set (_files "")
+ foreach (_index RANGE ${_startIndex} ${COTIRE_ARGC})
+ if (COTIRE_ARGV${_index})
+ list (APPEND _files "${COTIRE_ARGV${_index}}")
+ endif()
+ endforeach()
+ if (COTIRE_TARGET_LANGUAGE)
+ cotire_generate_unity_source(${_files} LANGUAGE "${COTIRE_TARGET_LANGUAGE}")
+ else()
+ cotire_generate_unity_source(${_files})
+ endif()
+
+ elseif ("${COTIRE_ARGV1}" STREQUAL "cleanup")
+
+ cotire_cleanup("${COTIRE_ARGV2}" "${COTIRE_ARGV3}" "${COTIRE_ARGV4}")
+
+ else()
+ message (FATAL_ERROR "Unknown cotire command \"${COTIRE_ARGV1}\".")
+ endif()
+
+else()
+
+ # cotire is being run in include mode
+ # set up all variable and property definitions
+
+ unset (COTIRE_C_COMPILER_VERSION CACHE)
+ unset (COTIRE_CXX_COMPILER_VERSION CACHE)
+
+ if (NOT DEFINED COTIRE_DEBUG_INIT)
+ if (DEFINED COTIRE_DEBUG)
+ set (COTIRE_DEBUG_INIT ${COTIRE_DEBUG})
+ else()
+ set (COTIRE_DEBUG_INIT FALSE)
+ endif()
+ endif()
+ option (COTIRE_DEBUG "Enable cotire debugging output?" ${COTIRE_DEBUG_INIT})
+
+ if (NOT DEFINED COTIRE_VERBOSE_INIT)
+ if (DEFINED COTIRE_VERBOSE)
+ set (COTIRE_VERBOSE_INIT ${COTIRE_VERBOSE})
+ else()
+ set (COTIRE_VERBOSE_INIT FALSE)
+ endif()
+ endif()
+ option (COTIRE_VERBOSE "Enable cotire verbose output?" ${COTIRE_VERBOSE_INIT})
+
+ set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS "inc;inl;ipp" CACHE STRING
+ "Ignore headers with the listed file extensions from the generated prefix header.")
+
+ set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH "" CACHE STRING
+ "Ignore headers from these directories when generating the prefix header.")
+
+ set (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS "m;mm" CACHE STRING
+ "Ignore sources with the listed file extensions from the generated unity source.")
+
+ set (COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES "3" CACHE STRING
+ "Minimum number of sources in target required to enable use of precompiled header.")
+
+ if (NOT DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT)
+ if (DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES)
+ set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT ${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES})
+ elseif ("${CMAKE_GENERATOR}" MATCHES "JOM|Ninja|Visual Studio")
+ # enable parallelization for generators that run multiple jobs by default
+ set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "-j")
+ else()
+ set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "0")
+ endif()
+ endif()
+ set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT}" CACHE STRING
+ "Maximum number of source files to include in a single unity source file.")
+
+ if (NOT COTIRE_PREFIX_HEADER_FILENAME_SUFFIX)
+ set (COTIRE_PREFIX_HEADER_FILENAME_SUFFIX "_prefix")
+ endif()
+ if (NOT COTIRE_UNITY_SOURCE_FILENAME_SUFFIX)
+ set (COTIRE_UNITY_SOURCE_FILENAME_SUFFIX "_unity")
+ endif()
+ if (NOT COTIRE_INTDIR)
+ set (COTIRE_INTDIR "cotire")
+ endif()
+ if (NOT COTIRE_PCH_ALL_TARGET_NAME)
+ set (COTIRE_PCH_ALL_TARGET_NAME "all_pch")
+ endif()
+ if (NOT COTIRE_UNITY_BUILD_ALL_TARGET_NAME)
+ set (COTIRE_UNITY_BUILD_ALL_TARGET_NAME "all_unity")
+ endif()
+ if (NOT COTIRE_CLEAN_ALL_TARGET_NAME)
+ set (COTIRE_CLEAN_ALL_TARGET_NAME "clean_cotire")
+ endif()
+ if (NOT COTIRE_CLEAN_TARGET_SUFFIX)
+ set (COTIRE_CLEAN_TARGET_SUFFIX "_clean_cotire")
+ endif()
+ if (NOT COTIRE_PCH_TARGET_SUFFIX)
+ set (COTIRE_PCH_TARGET_SUFFIX "_pch")
+ endif()
+ if (NOT COTIRE_UNITY_BUILD_TARGET_SUFFIX)
+ set (COTIRE_UNITY_BUILD_TARGET_SUFFIX "_unity")
+ endif()
+ if (NOT DEFINED COTIRE_TARGETS_FOLDER)
+ set (COTIRE_TARGETS_FOLDER "cotire")
+ endif()
+ if (NOT DEFINED COTIRE_UNITY_OUTPUT_DIRECTORY)
+ if ("${CMAKE_GENERATOR}" MATCHES "Ninja")
+ # generated Ninja build files do not work if the unity target produces the same output file as the cotired target
+ set (COTIRE_UNITY_OUTPUT_DIRECTORY "unity")
+ else()
+ set (COTIRE_UNITY_OUTPUT_DIRECTORY "")
+ endif()
+ endif()
+
+ # define cotire cache variables
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH"
+ BRIEF_DOCS "Ignore headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "The variable can be set to a semicolon separated list of include directories."
+ "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header."
+ "If not defined, defaults to empty list."
+ )
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS"
+ BRIEF_DOCS "Ignore includes with the listed file extensions from the generated prefix header."
+ FULL_DOCS
+ "The variable can be set to a semicolon separated list of file extensions."
+ "If a header file extension matches one in the list, it will be excluded from the generated prefix header."
+ "Includes with an extension in CMAKE_<LANG>_SOURCE_FILE_EXTENSIONS are always ignored."
+ "If not defined, defaults to inc;inl;ipp."
+ )
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS"
+ BRIEF_DOCS "Exclude sources with the listed file extensions from the generated unity source."
+ FULL_DOCS
+ "The variable can be set to a semicolon separated list of file extensions."
+ "If a source file extension matches one in the list, it will be excluded from the generated unity source file."
+ "Source files with an extension in CMAKE_<LANG>_IGNORE_EXTENSIONS are always excluded."
+ "If not defined, defaults to m;mm."
+ )
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES"
+ BRIEF_DOCS "Minimum number of sources in target required to enable use of precompiled header."
+ FULL_DOCS
+ "The variable can be set to an integer > 0."
+ "If a target contains less than that number of source files, cotire will not enable the use of the precompiled header for the target."
+ "If not defined, defaults to 3."
+ )
+
+ define_property(
+ CACHED_VARIABLE PROPERTY "COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES"
+ BRIEF_DOCS "Maximum number of source files to include in a single unity source file."
+ FULL_DOCS
+ "This may be set to an integer >= 0."
+ "If 0, cotire will only create a single unity source file."
+ "If a target contains more than that number of source files, cotire will create multiple unity source files for it."
+ "Can be set to \"-j\" to optimize the count of unity source files for the number of available processor cores."
+ "Can be set to \"-j jobs\" to optimize the number of unity source files for the given number of simultaneous jobs."
+ "Is used to initialize the target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES."
+ "Defaults to \"-j\" for the generators Visual Studio, JOM or Ninja. Defaults to 0 otherwise."
+ )
+
+ # define cotire directory properties
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER"
+ BRIEF_DOCS "Modify build command of cotired targets added in this directory to make use of the generated precompiled header."
+ FULL_DOCS
+ "See target property COTIRE_ENABLE_PRECOMPILED_HEADER."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_ADD_UNITY_BUILD"
+ BRIEF_DOCS "Add a new target that performs a unity build for cotired targets added in this directory."
+ FULL_DOCS
+ "See target property COTIRE_ADD_UNITY_BUILD."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_ADD_CLEAN"
+ BRIEF_DOCS "Add a new target that cleans all cotire generated files for cotired targets added in this directory."
+ FULL_DOCS
+ "See target property COTIRE_ADD_CLEAN."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH"
+ BRIEF_DOCS "Ignore headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "See target property COTIRE_PREFIX_HEADER_IGNORE_PATH."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH"
+ BRIEF_DOCS "Honor headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "See target property COTIRE_PREFIX_HEADER_INCLUDE_PATH."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS"
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each source file."
+ FULL_DOCS
+ "See target property COTIRE_UNITY_SOURCE_PRE_UNDEFS."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS"
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each source file."
+ FULL_DOCS
+ "See target property COTIRE_UNITY_SOURCE_POST_UNDEFS."
+ )
+
+ define_property(
+ DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES"
+ BRIEF_DOCS "Maximum number of source files to include in a single unity source file."
+ FULL_DOCS
+ "See target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES."
+ )
+
+ # define cotire target properties
+
+ define_property(
+ TARGET PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" INHERITED
+ BRIEF_DOCS "Modify this target's build command to make use of the generated precompiled header."
+ FULL_DOCS
+ "If this property is set to TRUE, cotire will modify the build command to make use of the generated precompiled header."
+ "Irrespective of the value of this property, cotire will setup custom commands to generate the unity source and prefix header for the target."
+ "For makefile based generators cotire will also set up a custom target to manually invoke the generation of the precompiled header."
+ "The target name will be set to this target's name with the suffix _pch appended."
+ "Inherited from directory."
+ "Defaults to TRUE."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_ADD_UNITY_BUILD" INHERITED
+ BRIEF_DOCS "Add a new target that performs a unity build for this target."
+ FULL_DOCS
+ "If this property is set to TRUE, cotire creates a new target of the same type that uses the generated unity source file instead of the target sources."
+ "Most of the relevant target properties will be copied from this target to the new unity build target."
+ "Target dependencies and linked libraries have to be manually set up for the new unity build target."
+ "The unity target name will be set to this target's name with the suffix _unity appended."
+ "Inherited from directory."
+ "Defaults to TRUE."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_ADD_CLEAN" INHERITED
+ BRIEF_DOCS "Add a new target that cleans all cotire generated files for this target."
+ FULL_DOCS
+ "If this property is set to TRUE, cotire creates a new target that clean all files (unity source, prefix header, precompiled header)."
+ "The clean target name will be set to this target's name with the suffix _clean_cotire appended."
+ "Inherited from directory."
+ "Defaults to FALSE."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" INHERITED
+ BRIEF_DOCS "Ignore headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "The property can be set to a list of directories."
+ "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header."
+ "Inherited from directory."
+ "If not set, this property is initialized to \${CMAKE_SOURCE_DIR};\${CMAKE_BINARY_DIR}."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" INHERITED
+ BRIEF_DOCS "Honor headers from these directories when generating the prefix header."
+ FULL_DOCS
+ "The property can be set to a list of directories."
+ "If a header file is found in one of these directories or sub-directories, it will be included in the generated prefix header."
+ "If a header file is both selected by COTIRE_PREFIX_HEADER_IGNORE_PATH and COTIRE_PREFIX_HEADER_INCLUDE_PATH,"
+ "the option which yields the closer relative path match wins."
+ "Inherited from directory."
+ "If not set, this property is initialized to the empty list."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" INHERITED
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each target source file."
+ FULL_DOCS
+ "This may be set to a semicolon-separated list of preprocessor symbols."
+ "cotire will add corresponding #undef directives to the generated unit source file before each target source file."
+ "Inherited from directory."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" INHERITED
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each target source file."
+ FULL_DOCS
+ "This may be set to a semicolon-separated list of preprocessor symbols."
+ "cotire will add corresponding #undef directives to the generated unit source file after each target source file."
+ "Inherited from directory."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" INHERITED
+ BRIEF_DOCS "Maximum number of source files to include in a single unity source file."
+ FULL_DOCS
+ "This may be set to an integer > 0."
+ "If a target contains more than that number of source files, cotire will create multiple unity build files for it."
+ "If not set, cotire will only create a single unity source file."
+ "Inherited from directory."
+ "Defaults to empty."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_UNITY_SOURCE_INIT"
+ BRIEF_DOCS "User provided unity source file to be used instead of the automatically generated one."
+ FULL_DOCS
+ "If set, cotire will only add the given file(s) to the generated unity source file."
+ "If not set, cotire will add all the target source files to the generated unity source file."
+ "The property can be set to a user provided unity source file."
+ "Defaults to empty."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_PREFIX_HEADER_INIT"
+ BRIEF_DOCS "User provided prefix header file to be used instead of the automatically generated one."
+ FULL_DOCS
+ "If set, cotire will add the given header file(s) to the generated prefix header file."
+ "If not set, cotire will generate a prefix header by tracking the header files included by the unity source file."
+ "The property can be set to a user provided prefix header file (e.g., stdafx.h)."
+ "Defaults to empty."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_UNITY_SOURCE"
+ BRIEF_DOCS "Read-only property. The generated <LANG> unity source file(s)."
+ FULL_DOCS
+ "cotire sets this property to the path of the generated <LANG> single computation unit source file for the target."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_PREFIX_HEADER"
+ BRIEF_DOCS "Read-only property. The generated <LANG> prefix header file."
+ FULL_DOCS
+ "cotire sets this property to the full path of the generated <LANG> language prefix header for the target."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_<LANG>_PRECOMPILED_HEADER"
+ BRIEF_DOCS "Read-only property. The generated <LANG> precompiled header file."
+ FULL_DOCS
+ "cotire sets this property to the full path of the generated <LANG> language precompiled header binary for the target."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ TARGET PROPERTY "COTIRE_UNITY_TARGET_NAME"
+ BRIEF_DOCS "The name of the generated unity build target corresponding to this target."
+ FULL_DOCS
+ "This property can be set to the desired name of the unity target that will be created by cotire."
+ "If not set, the unity target name will be set to this target's name with the suffix _unity appended."
+ "After this target has been processed by cotire, the property is set to the actual name of the generated unity target."
+ "Defaults to empty string."
+ )
+
+ # define cotire source properties
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_EXCLUDED"
+ BRIEF_DOCS "Do not modify source file's build command."
+ FULL_DOCS
+ "If this property is set to TRUE, the source file's build command will not be modified to make use of the precompiled header."
+ "The source file will also be excluded from the generated unity source file."
+ "Source files that have their COMPILE_FLAGS property set will be excluded by default."
+ "Defaults to FALSE."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_DEPENDENCY"
+ BRIEF_DOCS "Add this source file to dependencies of the automatically generated prefix header file."
+ FULL_DOCS
+ "If this property is set to TRUE, the source file is added to dependencies of the generated prefix header file."
+ "If the file is modified, cotire will re-generate the prefix header source upon build."
+ "Defaults to FALSE."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS"
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of this source file."
+ FULL_DOCS
+ "This may be set to a semicolon-separated list of preprocessor symbols."
+ "cotire will add corresponding #undef directives to the generated unit source file before this file is included."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS"
+ BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of this source file."
+ FULL_DOCS
+ "This may be set to a semicolon-separated list of preprocessor symbols."
+ "cotire will add corresponding #undef directives to the generated unit source file after this file is included."
+ "Defaults to empty string."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_START_NEW_UNITY_SOURCE"
+ BRIEF_DOCS "Start a new unity source file which includes this source file as the first one."
+ FULL_DOCS
+ "If this property is set to TRUE, cotire will complete the current unity file and start a new one."
+ "The new unity source file will include this source file as the first one."
+ "This property essentially works as a separator for unity source files."
+ "Defaults to FALSE."
+ )
+
+ define_property(
+ SOURCE PROPERTY "COTIRE_TARGET"
+ BRIEF_DOCS "Read-only property. Mark this source file as cotired for the given target."
+ FULL_DOCS
+ "cotire sets this property to the name of target, that the source file's build command has been altered for."
+ "Defaults to empty string."
+ )
+
+ message (STATUS "cotire ${COTIRE_CMAKE_MODULE_VERSION} loaded.")
+
+endif()
diff --git a/vendor/bandit/cross_compile.sh b/vendor/bandit/cross_compile.sh
new file mode 100755
index 00000000..7be77aaa
--- /dev/null
+++ b/vendor/bandit/cross_compile.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+build_results=()
+
+function build_for {
+ CC=$1
+ CXX=$2
+ BUILD_DIR=$CC
+
+ mkdir $BUILD_DIR
+ pushd $BUILD_DIR
+ CC=$CC CXX=$CXX cmake ../..
+ make
+ build_results+=("$CC: $?")
+ popd
+}
+
+if [[ -d builds ]]; then
+ rm -rf builds
+fi
+
+mkdir builds
+pushd builds
+
+build_for clang-3.6 clang++-3.6
+build_for gcc-5 g++-5
+build_for clang clang++
+build_for gcc-4.9 g++-4.9
+build_for gcc-4.8 g++-4.8
+build_for gcc-4.7 g++-4.7
+build_for gcc-4.6 g++-4.6
+build_for gcc-4.5 g++-4.5
+
+popd
+
+echo
+echo "Result:"
+for res in "${build_results[@]}"
+do
+ echo $res
+done
+
+echo "Done"
diff --git a/vendor/bandit/specs/before_each_after_each.spec.cpp b/vendor/bandit/specs/before_each_after_each.spec.cpp
new file mode 100644
index 00000000..29d40574
--- /dev/null
+++ b/vendor/bandit/specs/before_each_after_each.spec.cpp
@@ -0,0 +1,78 @@
+#include <specs/specs.h>
+
+namespace bf = bandit::fakes;
+
+go_bandit([](){
+
+ describe("before_each/after_each", [&](){
+ std::unique_ptr<bandit::detail::contextstack_t> context_stack;
+ std::unique_ptr<bf::fake_context> context;
+
+ before_each([&](){
+ context = std::unique_ptr<bf::fake_context>(new bf::fake_context());
+ context_stack = std::unique_ptr<bandit::detail::contextstack_t>(new bandit::detail::contextstack_t());
+ context_stack->push_back(context.get());
+ });
+
+ describe("before_each", [&](){
+ bandit::detail::voidfunc_t before_each_fn;
+
+ before_each([&](){
+ before_each_fn = [](){};
+ });
+
+ it("registers itself for the current context in the stack", [&](){
+ before_each(before_each_fn, *context_stack);
+ Assert::That(context->call_log(), Has().Exactly(1).EqualTo("register_before_each"));
+ });
+
+ });
+
+ describe("after_each", [&](){
+ bandit::detail::voidfunc_t after_each_fn;
+
+ before_each([&](){
+ after_each_fn = [](){};
+ });
+
+ it("registers itself for the current context in the stack", [&](){
+ after_each(after_each_fn, *context_stack);
+ Assert::That(context->call_log(), Has().Exactly(1).EqualTo("register_after_each"));
+ });
+
+ });
+ });
+
+ describe("before_each/after_each integration", [&](){
+ bandit::specs::logging_fake logger;
+
+ before_each([&](){
+ logger.log() << "first before_each called" << std::endl;
+ });
+
+ before_each([&](){
+ logger.log() << "second before_each called" << std::endl;
+ });
+
+ after_each([&](){
+ logger.log() << "first after_each called" << std::endl;
+ });
+
+ after_each([&](){
+ logger.log() << "second after_each called" << std::endl;
+ });
+
+ it("should only have called the before_each functions for the first test", [&](){
+ Assert::That(logger.call_log(), Has().Exactly(1).EqualTo("first before_each called"));
+ Assert::That(logger.call_log(), Has().Exactly(1).EqualTo("second before_each called"));
+ Assert::That(logger.call_log(), Has().None().Containing("after_each"));
+ });
+
+ it("should have called 'before_each' function twice, and 'after_each' functions once for the second test", [&](){
+ Assert::That(logger.call_log(), Has().Exactly(2).EqualTo("first before_each called"));
+ Assert::That(logger.call_log(), Has().Exactly(2).EqualTo("second before_each called"));
+ Assert::That(logger.call_log(), Has().Exactly(1).EqualTo("first after_each called"));
+ Assert::That(logger.call_log(), Has().Exactly(1).EqualTo("second after_each called"));
+ });
+ });
+});
diff --git a/vendor/bandit/specs/context.spec.cpp b/vendor/bandit/specs/context.spec.cpp
new file mode 100644
index 00000000..d517ef80
--- /dev/null
+++ b/vendor/bandit/specs/context.spec.cpp
@@ -0,0 +1,44 @@
+#include <specs/specs.h>
+
+go_bandit([](){
+
+ describe("bandit_context:", [&](){
+
+ std::unique_ptr<bandit::detail::bandit_context> context;
+
+ before_each([&](){
+ bool hard_skip = false;
+ context = std::unique_ptr<bandit::detail::bandit_context>(
+ new bandit::detail::bandit_context("context name", hard_skip));
+ });
+
+ it("is ok to register before_each as it is not executing", [&](){
+ context->register_before_each([](){});
+ });
+
+ it("is ok to register after_each as it is not executing", [&](){
+ context->register_after_each([](){});
+ });
+
+ describe("is executing", [&](){
+
+ before_each([&](){
+ context->execution_is_starting();
+ });
+
+ it("is not ok to register before_each", [&](){
+ AssertThrows(bandit::detail::test_run_error, context->register_before_each([](){}));
+ Assert::That(LastException<bandit::detail::test_run_error>().what(),
+ Equals("before_each was called after 'describe' or 'it'"));
+ });
+
+ it("is not ok to register after_each", [&](){
+ AssertThrows(bandit::detail::test_run_error, context->register_after_each([](){}));
+ Assert::That(LastException<bandit::detail::test_run_error>().what(),
+ Equals("after_each was called after 'describe' or 'it'"));
+ });
+ });
+
+ });
+
+});
diff --git a/vendor/bandit/specs/describe.spec.cpp b/vendor/bandit/specs/describe.spec.cpp
new file mode 100644
index 00000000..d4600a28
--- /dev/null
+++ b/vendor/bandit/specs/describe.spec.cpp
@@ -0,0 +1,117 @@
+#include <specs/specs.h>
+
+using namespace bandit::fakes;
+namespace bd = bandit::detail;
+
+SPEC_BEGIN(describe)
+
+ describe("describe:", [](){
+ bandit::detail::voidfunc_t describe_fn;
+ fake_reporter_ptr reporter;
+ std::unique_ptr<bd::contextstack_t> context_stack;
+ std::unique_ptr<fake_context> global_context;
+
+ before_each([&](){
+ reporter = fake_reporter_ptr(new fake_reporter());
+
+ context_stack = std::unique_ptr<bd::contextstack_t>(new bd::contextstack_t());
+
+ global_context = std::unique_ptr<fake_context>(new fake_context());
+ context_stack->push_back(global_context.get());
+ });
+
+
+ auto call_describe = [&](){
+ describe("context name", describe_fn, *reporter, *context_stack);
+ };
+
+ describe("with a succeeding 'it'", [&](){
+ int context_stack_size_while_running;
+
+ before_each([&](){
+ context_stack_size_while_running = 0;
+ describe_fn = [&](){context_stack_size_while_running = context_stack->size();};
+ });
+
+ it("tells its parent context that execution has started", [&](){
+ // This is important as once execution has started,
+ // before_each and after_each calls cannot be guaranteed to
+ // be run before any 'it' method.
+
+ call_describe();
+ AssertThat(global_context->call_log(), Has().AtLeast(1).EqualTo("execution_is_starting"));
+ });
+
+ it("tells reporter it's starting a run", [&](){
+ call_describe();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("context_starting: context name"));
+ });
+
+ it("tells reporter it's finished a run", [&](){
+ call_describe();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("context_ended: context name"));
+ });
+
+ it("pushes a new context during execution", [&](){
+ call_describe();
+ AssertThat(context_stack_size_while_running, Equals(2));
+ });
+
+ it("pops the context from the stack after execution so that only the global context is left", [&](){
+ call_describe();
+ AssertThat(*context_stack, Is().OfLength(1));
+ });
+
+ });
+
+ describe("with test run error", [&](){
+ //
+ // This can occur if after_each or before_each are called
+ // after execution has started for a context.
+ //
+
+ before_each([&](){
+ describe_fn = [&](){ throw bandit::detail::test_run_error("we dun goofed!"); };
+ });
+
+ it("doesn't propagate the error", [&](){
+ call_describe();
+ });
+
+ it("tells reporter to report the error", [&](){
+ call_describe();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("test_run_error: context name (we dun goofed!)"));
+ });
+
+ });
+
+ describe("skip", [&](){
+ bool context_is_hard_skip;
+ describe_fn =
+ [&](){ context_is_hard_skip = context_stack->back()->hard_skip(); };
+
+ before_each([&](){
+ context_is_hard_skip = false;
+ });
+
+ describe("describe_skip", [&](){
+
+ it("pushes a context marked as skipped on the stack", [&](){
+ describe_skip("context name", describe_fn, *reporter, *context_stack);
+ AssertThat(context_is_hard_skip, IsTrue());
+ });
+
+ });
+
+ describe("xdescribe", [&](){
+
+ it("pushes a context marked as skipped on the stack", [&](){
+ xdescribe("context name", describe_fn, *reporter, *context_stack);
+ AssertThat(context_is_hard_skip, IsTrue());
+ });
+
+ });
+ });
+ });
+
+SPEC_END
diff --git a/vendor/bandit/specs/failure_formatters/default_formatter.spec.cpp b/vendor/bandit/specs/failure_formatters/default_formatter.spec.cpp
new file mode 100644
index 00000000..6d29b694
--- /dev/null
+++ b/vendor/bandit/specs/failure_formatters/default_formatter.spec.cpp
@@ -0,0 +1,21 @@
+#include <specs/specs.h>
+namespace bd = bandit::detail;
+
+go_bandit([](){
+
+ describe("default failure formatter", [&](){
+ bd::default_failure_formatter formatter;
+
+ it("formats assertions with file and line number", [&](){
+ bd::assertion_exception exception("message", "file", 321);
+ AssertThat(formatter.format(exception), Equals("file:321: message"));
+ });
+
+ it("formats assertions without file and line number", [&](){
+ bd::assertion_exception exception("message");
+ AssertThat(formatter.format(exception), Equals("message"));
+ });
+
+ });
+
+});
diff --git a/vendor/bandit/specs/failure_formatters/visual_studio_failure_formatter.spec.cpp b/vendor/bandit/specs/failure_formatters/visual_studio_failure_formatter.spec.cpp
new file mode 100644
index 00000000..0d283c8e
--- /dev/null
+++ b/vendor/bandit/specs/failure_formatters/visual_studio_failure_formatter.spec.cpp
@@ -0,0 +1,22 @@
+#include <specs/specs.h>
+namespace bd = bandit::detail;
+
+go_bandit([](){
+
+ describe("Visual Studio failure formatter:", [&](){
+
+ bd::visual_studio_failure_formatter formatter;
+
+ it("formats assertions with file and line number", [&](){
+ bd::assertion_exception exception("message", "file", 321);
+ AssertThat(formatter.format(exception), Equals("file(321): message"));
+ });
+
+ it("formats assertions without file and line number", [&](){
+ bd::assertion_exception exception("message");
+ AssertThat(formatter.format(exception), Equals("bandit: message"));
+ });
+
+ });
+
+});
diff --git a/vendor/bandit/specs/fakes/fake_context.h b/vendor/bandit/specs/fakes/fake_context.h
new file mode 100644
index 00000000..e5d1d870
--- /dev/null
+++ b/vendor/bandit/specs/fakes/fake_context.h
@@ -0,0 +1,69 @@
+#ifndef BANDIT_FAKE_CONTEXT_H
+#define BANDIT_FAKE_CONTEXT_H
+
+namespace bandit { namespace fakes {
+
+ struct fake_context : public bandit::detail::context, public bandit::specs::logging_fake
+ {
+ fake_context() : hard_skip_(false), name_("fake_context"),
+ custom_after_each_([](){}), custom_before_each_([](){})
+ {}
+
+ const std::string& name()
+ {
+ log() << "name" << std::endl;
+ return name_;
+ }
+
+ void execution_is_starting()
+ {
+ log() << "execution_is_starting" << std::endl;
+ }
+
+ void register_before_each(detail::voidfunc_t)
+ {
+ log() << "register_before_each" << std::endl;
+ }
+
+ void register_after_each(detail::voidfunc_t)
+ {
+ log() << "register_after_each" << std::endl;
+ }
+
+ void run_before_eaches()
+ {
+ log() << "run_before_eaches" << std::endl;
+ custom_before_each_();
+ }
+
+ void run_after_eaches()
+ {
+ log() << "run_after_eaches" << std::endl;
+ custom_after_each_();
+ }
+
+ bool hard_skip()
+ {
+ log() << "hard_skip: returning " << hard_skip_ << std::endl;
+ return hard_skip_;
+ }
+
+ void with_after_each(detail::voidfunc_t call)
+ {
+ custom_after_each_ = call;
+ }
+
+ void with_before_each(detail::voidfunc_t call)
+ {
+ custom_before_each_ = call;
+ }
+
+ private:
+ bool hard_skip_;
+ std::string name_;
+ detail::voidfunc_t custom_after_each_;
+ detail::voidfunc_t custom_before_each_;
+ };
+}}
+
+#endif
diff --git a/vendor/bandit/specs/fakes/fake_reporter.h b/vendor/bandit/specs/fakes/fake_reporter.h
new file mode 100644
index 00000000..032ed44d
--- /dev/null
+++ b/vendor/bandit/specs/fakes/fake_reporter.h
@@ -0,0 +1,78 @@
+#ifndef BANDIT_SPECS_FAKE_REPORTER_H
+#define BANDIT_SPECS_FAKE_REPORTER_H
+
+namespace bandit { namespace fakes {
+ struct fake_reporter :
+ public bandit::detail::listener,
+ public bandit::specs::logging_fake
+ {
+ fake_reporter() : test_run_status_(true)
+ {}
+
+ void test_run_starting()
+ {
+ log() << "test_run_starting" << std::endl;
+ }
+
+ void test_run_complete()
+ {
+ log() << "test_run_complete" << std::endl;
+ }
+
+ void context_starting(const char* desc)
+ {
+ log() << "context_starting: " << desc << std::endl;
+ }
+
+ void context_ended(const char* desc)
+ {
+ log() << "context_ended: " << desc << std::endl;
+ }
+
+ void test_run_error(const char* desc, const struct bandit::detail::test_run_error& err)
+ {
+ log() << "test_run_error: " << desc << " (" << strip_newline(err.what()) << ")" << std::endl;
+ }
+
+ void it_starting(const char* desc)
+ {
+ log() << "it_starting: " << desc << std::endl;
+ }
+
+ void it_succeeded(const char* desc)
+ {
+ log() << "it_succeeded: " << desc << std::endl;
+ }
+
+ void it_failed(const char* desc, const bandit::detail::assertion_exception& ex)
+ {
+ log() << "it_failed: " << desc << " (" << strip_newline(ex.what()) << ")" << std::endl;
+ }
+
+ void it_unknown_error(const char* desc)
+ {
+ log() << "it_unknown_error: " << desc << std::endl;
+ }
+
+ void it_skip(const char* desc)
+ {
+ log() << "it_skip: " << desc << std::endl;
+ }
+
+ bool did_we_pass() const
+ {
+ return test_run_status_;
+ }
+
+ void set_test_run_status(bool status)
+ {
+ test_run_status_ = status;
+ }
+
+ private:
+ bool test_run_status_;
+ };
+ typedef std::unique_ptr<fake_reporter> fake_reporter_ptr;
+}}
+
+#endif
diff --git a/vendor/bandit/specs/fakes/fakes.h b/vendor/bandit/specs/fakes/fakes.h
new file mode 100644
index 00000000..a48517f6
--- /dev/null
+++ b/vendor/bandit/specs/fakes/fakes.h
@@ -0,0 +1,8 @@
+#ifndef BANDIT_SPECS_FAKES_H
+#define BANDIT_SPECS_FAKES_H
+
+#include <specs/fakes/logging_fake.h>
+#include <specs/fakes/fake_reporter.h>
+#include <specs/fakes/fake_context.h>
+
+#endif
diff --git a/vendor/bandit/specs/fakes/logging_fake.h b/vendor/bandit/specs/fakes/logging_fake.h
new file mode 100644
index 00000000..ac1d3dd0
--- /dev/null
+++ b/vendor/bandit/specs/fakes/logging_fake.h
@@ -0,0 +1,32 @@
+#ifndef BANDIT_SPECS_LOGGING_FAKE_H
+#define BANDIT_SPECS_LOGGING_FAKE_H
+#include <sstream>
+
+namespace bandit { namespace specs {
+
+ struct logging_fake
+ {
+ std::ostream& log()
+ {
+ return logstm_;
+ }
+
+ std::string strip_newline(const char* val)
+ {
+ std::string no_newline = val;
+ std::transform(no_newline.begin(), no_newline.end(), no_newline.begin(), [](const char& c) {
+ return (c == '\n' || c == '\r') ? ' ' : c;
+ });
+ return no_newline;
+ }
+
+ std::string call_log()
+ {
+ return logstm_.str();
+ }
+
+ private:
+ std::stringstream logstm_;
+ };
+}}
+#endif
diff --git a/vendor/bandit/specs/fuzzbox.spec.cpp b/vendor/bandit/specs/fuzzbox.spec.cpp
new file mode 100644
index 00000000..6515a554
--- /dev/null
+++ b/vendor/bandit/specs/fuzzbox.spec.cpp
@@ -0,0 +1,77 @@
+#include <specs/specs.h>
+
+namespace fuzzbox {
+
+ typedef enum {
+ clean,
+ distorted
+ } sounds;
+
+ struct fuzzbox
+ {
+ fuzzbox() : sound_(sounds::clean)
+ {}
+
+ void flip()
+ {
+ sound_ = sounds::distorted;
+ }
+
+ sounds sound()
+ {
+ return sound_;
+ }
+
+ private:
+ sounds sound_;
+ };
+ typedef std::unique_ptr<fuzzbox> fuzzbox_ptr;
+
+ struct guitar
+ {
+ void add_effect(fuzzbox* effect)
+ {
+ effect_ = effect;
+ }
+
+ sounds sound()
+ {
+ return effect_->sound();
+ }
+
+ private:
+ fuzzbox* effect_;
+ };
+ typedef std::unique_ptr<guitar> guitar_ptr;
+
+go_bandit([](){
+
+ describe("fuzzbox:", [](){
+ guitar_ptr guitar;
+ fuzzbox_ptr fuzzbox;
+
+ before_each([&](){
+ guitar = guitar_ptr(new struct guitar());
+ fuzzbox = fuzzbox_ptr(new struct fuzzbox());
+ guitar->add_effect(fuzzbox.get());
+ });
+
+ it("starts in clean mode", [&](){
+ AssertThat(guitar->sound(), Equals(sounds::clean));
+ });
+
+ describe("in distorted mode", [&](){
+
+ before_each([&](){
+ fuzzbox->flip();
+ });
+
+ it("sounds distorted", [&](){
+ AssertThat(guitar->sound(), Equals(sounds::distorted));
+ });
+ });
+ });
+
+});
+
+}
diff --git a/vendor/bandit/specs/it.spec.cpp b/vendor/bandit/specs/it.spec.cpp
new file mode 100644
index 00000000..287a1ede
--- /dev/null
+++ b/vendor/bandit/specs/it.spec.cpp
@@ -0,0 +1,355 @@
+#include <specs/specs.h>
+using namespace bandit::fakes;
+namespace bd = bandit::detail;
+
+go_bandit([](){
+ describe("it:", [&](){
+ bd::voidfunc_t it_func;
+ fake_reporter_ptr reporter;
+ std::unique_ptr<bd::contextstack_t> contexts;
+ std::unique_ptr<fake_context> context;
+ bandit::adapters::snowhouse_adapter assertion_adapter;
+ bd::run_policy_ptr run_policy;
+
+ before_each([&](){
+ reporter = fake_reporter_ptr(new fake_reporter());
+ contexts = std::unique_ptr<bd::contextstack_t>(new bd::contextstack_t());
+ context = std::unique_ptr<fake_context>(new fake_context());
+ contexts->push_back(context.get());
+
+ run_policy = bd::run_policy_ptr(new bd::always_run_policy());
+ });
+
+ auto call_it = [&]() {
+ it("my it", it_func, *reporter, *contexts, assertion_adapter, *run_policy);
+ };
+
+ it("tells the current context that execution has started", [&](){
+ // This is important as once execution has started,
+ // before_each and after_each calls cannot be guaranteed to
+ // be run before any 'it' method.
+
+ call_it();
+ AssertThat(context->call_log(), Has().AtLeast(1).EqualTo("execution_is_starting"));
+ });
+
+ describe("with succeeding test", [&](){
+ before_each([&](){
+ it_func = [](){};
+ });
+
+ it("tells reporter it's starting", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_starting: my it"));
+ });
+
+ it("tells reporter it's succeeded", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_succeeded: my it"));
+ });
+
+ it("calls before_each in context", [&](){
+ call_it();
+ AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_before_eaches"));
+ });
+
+ it("calls after_each in context", [&](){
+ call_it();
+ AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_after_eaches"));
+ });
+
+ describe("but with a failing after_each", [&](){
+
+ before_each([&](){
+ context->with_after_each([](){ AssertThat(2, Equals(3)); });
+ });
+
+ it("tells reporter it's failed", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_failed: my it (Expected: equal to 3 Actual: 2 )"));
+ });
+
+ it("doesn't report a succeeding test", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().None().EqualTo("it_succeeded: my it"));
+ });
+
+ it("tells run_policy that we have a failing test", [&](){
+ call_it();
+ AssertThat(run_policy->has_encountered_failure(), IsTrue());
+ });
+ });
+
+ describe("but with a std::exception in after_each", [&](){
+
+ before_each([&](){
+ context->with_after_each([](){ throw std::logic_error("logic is wrong!"); });
+ });
+
+ it("tells reporter it's failed", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_failed: my it (exception: logic is wrong!)"));
+ });
+
+ it("doesn't report a succeeding test", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().None().EqualTo("it_succeeded: my it"));
+ });
+
+ it("tells run_policy that we have a failing test", [&](){
+ call_it();
+ AssertThat(run_policy->has_encountered_failure(), IsTrue());
+ });
+
+ });
+
+ describe("but with an unknown error in after_each", [&](){
+
+ before_each([&](){
+ context->with_after_each([](){ throw 25; });
+ });
+
+ it("tells reporter it's failed", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_unknown_error: my it"));
+ });
+
+ it("doesn't report a succeeding test", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().None().EqualTo("it_succeeded: my it"));
+ });
+
+ it("tells run_policy that we have a failing test", [&](){
+ call_it();
+ AssertThat(run_policy->has_encountered_failure(), IsTrue());
+ });
+ });
+
+ describe("but with a failing before_each", [&](){
+
+ before_each([&](){
+ context->with_before_each([](){ AssertThat(2, Equals(3)); });
+ });
+
+ it("tells reporter it's failed", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_failed: my it (Expected: equal to 3 Actual: 2 )"));
+ });
+
+ it("doesn't report a succeeding test", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().None().EqualTo("it_succeeded: my it"));
+ });
+
+ it("tells run_policy that we have a failing test", [&](){
+ call_it();
+ AssertThat(run_policy->has_encountered_failure(), IsTrue());
+ });
+ });
+
+ describe("but with a std::exception in before_each", [&](){
+
+ before_each([&](){
+ context->with_before_each([](){ throw std::logic_error("logic is wrong!"); });
+ });
+
+ it("tells reporter it's failed", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_failed: my it (exception: logic is wrong!)"));
+ });
+
+ it("doesn't report a succeeding test", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().None().EqualTo("it_succeeded: my it"));
+ });
+
+ it("tells run_policy that we have a failing test", [&](){
+ call_it();
+ AssertThat(run_policy->has_encountered_failure(), IsTrue());
+ });
+ });
+
+ describe("but with an unknown error in before_each", [&](){
+
+ before_each([&](){
+ context->with_before_each([](){ throw 25; });
+ });
+
+ it("tells reporter it's failed", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_unknown_error: my it"));
+ });
+
+ it("doesn't report a succeeding test", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().None().EqualTo("it_succeeded: my it"));
+ });
+
+ it("tells run_policy that we have a failing test", [&](){
+ call_it();
+ AssertThat(run_policy->has_encountered_failure(), IsTrue());
+ });
+ });
+
+ });
+
+ describe("with failing test", [&](){
+ before_each([&](){
+ it_func = [](){ AssertThat(3, Equals(2)); };
+ });
+
+ it("tells reporter it's failed", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_failed: my it (Expected: equal to 2 Actual: 3 )"));
+ });
+
+ it("calls before_each in context", [&](){
+ call_it();
+ AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_before_eaches"));
+ });
+
+ it("calls after_each in context", [&](){
+ call_it();
+ AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_after_eaches"));
+ });
+
+ it("tells run_policy that we have a failing test", [&](){
+ call_it();
+ AssertThat(run_policy->has_encountered_failure(), IsTrue());
+ });
+ });
+
+
+ describe("with crashing test", [&](){
+ before_each([&](){
+ it_func = [](){ throw 44; };
+ });
+
+ it("tells reporter it's failed", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_unknown_error: my it"));
+ });
+
+ it("calls before_each in context", [&](){
+ call_it();
+ AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_before_eaches"));
+ });
+
+ it("calls after_each in context", [&](){
+ call_it();
+ AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_after_eaches"));
+ });
+
+ it("tells run_policy that we have a failing test", [&](){
+ call_it();
+ AssertThat(run_policy->has_encountered_failure(), IsTrue());
+ });
+ });
+
+ describe("with test throwing exception based on 'std::exception'", [&](){
+
+ before_each([&](){
+ it_func = [](){ throw std::logic_error("logic error"); };
+ });
+
+ it("tells reporter it's failed", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_failed: my it (exception: logic error)"));
+ });
+
+ it("calls before_each in context", [&](){
+ call_it();
+ AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_before_eaches"));
+ });
+
+ it("calls after_each in context", [&](){
+ call_it();
+ AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_after_eaches"));
+ });
+
+ it("tells run_policy that we have a failing test", [&](){
+ call_it();
+ AssertThat(run_policy->has_encountered_failure(), IsTrue());
+ });
+
+ });
+
+ describe("it_skip", [&](){
+
+ it("tells reporter it's skipped", [&](){
+ it_skip("my it", [](){}, *reporter);
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_skip: my it"));
+ });
+
+ it("doesn't call function", [&](){
+ bool called = false;
+ it_skip("my it", [&](){ called = true; }, *reporter);
+ AssertThat(called, IsFalse());
+ });
+
+ });
+
+ describe("xit", [&](){
+
+ it("tells reporter it's skipped", [&](){
+ xit("my it", [](){}, *reporter);
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_skip: my it"));
+ });
+
+ it("doesn't call function", [&](){
+ bool called = false;
+ xit("my it", [&](){ called = true; }, *reporter);
+ AssertThat(called, IsFalse());
+ });
+
+ });
+
+ describe("with a run policy that says to skip this 'it'", [&](){
+ bool it_was_called;
+
+ before_each([&](){
+ run_policy = bd::run_policy_ptr(new bd::never_run_policy());
+ it_func = [&](){ it_was_called = true; };
+ it_was_called = false;
+ });
+
+ it("tells reporter it's skipped", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_skip: my it"));
+ });
+
+ it("doesn't call function", [&](){
+ call_it();
+ AssertThat(it_was_called, IsFalse());
+ });
+
+ });
+
+ describe("skipping", [&](){
+ bool it_was_called;
+
+ before_each([&](){
+ it_func = [&](){ it_was_called = true; };
+ it_was_called = false;
+ });
+
+ describe("with a policy that says to skip this it", [&](){
+
+ before_each([&](){
+ run_policy = bd::run_policy_ptr(new bd::never_run_policy());
+ });
+
+ it("tells reporter it's skipped", [&](){
+ call_it();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_skip: my it"));
+ });
+
+ it("doesn't call function", [&](){
+ call_it();
+ AssertThat(it_was_called, IsFalse());
+ });
+
+ });
+ });
+ });
+});
diff --git a/vendor/bandit/specs/main.cpp b/vendor/bandit/specs/main.cpp
new file mode 100644
index 00000000..dde5de2d
--- /dev/null
+++ b/vendor/bandit/specs/main.cpp
@@ -0,0 +1,6 @@
+#include <specs/specs.h>
+
+int main(int argc, char* argv[])
+{
+ return bandit::run(argc, argv);
+}
diff --git a/vendor/bandit/specs/matchers/be_close_to.cpp b/vendor/bandit/specs/matchers/be_close_to.cpp
new file mode 100644
index 00000000..64309673
--- /dev/null
+++ b/vendor/bandit/specs/matchers/be_close_to.cpp
@@ -0,0 +1,112 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::BeCloseTo)
+
+describe("be_close_to matcher", []{
+ describe("when the actual value is declared as a float", [&]{
+ float actualValue = 2.0 / 3.0;
+
+ describe("and the expected value is also a float", [&]{
+ float expectedValue;
+
+ describe("with an explicit threshold", [&]{
+ float threshold = 0.1;
+
+ describe("and the values are within the given threshold", [&]{
+ before_each([&]{
+ expectedValue = 2.0 / 3.0 + 0.01;
+ });
+
+ it("must accept a positive match", [&]{
+ actualValue must be_close_to(expectedValue).within(threshold);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_close_to(expectedValue).within(threshold); }());
+ });
+ });
+
+ describe("and the values are not within the given threshold", [&]{
+ before_each([&]{
+ expectedValue = 2.0 / 3.0 + 0.2;
+ });
+
+ it("must accept a negative match", [&]{
+ actualValue must_not be_close_to(expectedValue).within(threshold);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_close_to(expectedValue).within(threshold); }());
+ });
+ });
+ });
+
+ describe("without an explicit threshold", [&]{
+ describe("and the values are within the default threshold", [&]{
+ before_each([&]{
+ expectedValue = 2.0 / 3.0 + 0.000001;
+ });
+
+ it("must accept a positive match", [&]{
+ actualValue must be_close_to(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_close_to(expectedValue); }());
+ });
+ });
+
+ describe("and the values are not within the default threshold", [&]{
+ before_each([&]{
+ expectedValue = 2.0 / 3.0 + 0.1;
+ });
+
+ it("must accept a negative match", [&]{
+ actualValue must_not be_close_to(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_close_to(expectedValue); }());
+ });
+ });
+ });
+ });
+
+ describe("and the expected value is a compatible non-float type", [&]{
+ int expectedValue;
+ float threshold = 1;
+
+ describe("and the values are within the given threshold", [&]{
+ before_each([&]{
+ expectedValue = 1;
+ });
+
+ it("must accept a positive match", [&]{
+ actualValue must be_close_to(expectedValue).within(threshold);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_close_to(expectedValue).within(threshold); }());
+ });
+ });
+
+ describe("and the values are not within the given threshold", [&]{
+ before_each([&]{
+ expectedValue = 5;
+ });
+
+ it("must accept a negative match", [&]{
+ actualValue must_not be_close_to(expectedValue).within(threshold);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_close_to(expectedValue).within(threshold); }());
+ });
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/be_empty.cpp b/vendor/bandit/specs/matchers/be_empty.cpp
new file mode 100644
index 00000000..3ed4a6f9
--- /dev/null
+++ b/vendor/bandit/specs/matchers/be_empty.cpp
@@ -0,0 +1,89 @@
+#include <set>
+
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::BeEmpty)
+
+describe("be_empty matcher", [&]{
+ describe("when the value is an STL vector", [&]{
+ describe("which is empty", [&]{
+ std::vector<int> container;
+
+ it("must pass a positive match", [&]{
+ container must be_empty;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ container must_not be_empty; }());
+ });
+ });
+
+ describe("which is not empty", [&]{
+ std::vector<int> container {2, 7};
+
+ it("must pass a negative match", [&]{
+ container must_not be_empty;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ container must be_empty; }());
+ });
+ });
+ });
+
+ describe("when the value is an STL map", [&]{
+ describe("which is empty", [&]{
+ std::map<int, int> container;
+
+ it("must pass a positive match", [&]{
+ container must be_empty;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ container must_not be_empty; }());
+ });
+ });
+
+ describe("which is not empty", [&]{
+ std::map<int, int> container {{5, 6}, {7,10}};
+
+ it("must pass a negative match", [&]{
+ container must_not be_empty;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ container must be_empty; }());
+ });
+ });
+ });
+
+ describe("when the value is an STL set", [&]{
+ describe("which is empty", [&]{
+ std::set<int> container;
+
+ it("must pass a positive match", [&]{
+ container must be_empty;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ container must_not be_empty; }());
+ });
+ });
+
+ describe("which is not empty", [&]{
+ std::set<int> container {5, 7};
+
+ it("must pass a negative match", [&]{
+ container must_not be_empty;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ container must be_empty; }());
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/be_falsy.cpp b/vendor/bandit/specs/matchers/be_falsy.cpp
new file mode 100644
index 00000000..d8c71c1b
--- /dev/null
+++ b/vendor/bandit/specs/matchers/be_falsy.cpp
@@ -0,0 +1,85 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::BeFalsy)
+
+describe("be_falsy matcher", [&]{
+ describe("when the value is a built-in type", [&]{
+ bool value;
+
+ describe("which evaluates to false", [&]{
+ before_each([&]{
+ value = false;
+ });
+
+ it("must accept a positive match", [&]{
+ value must be_falsy;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ value must_not be_falsy; }());
+ });
+ });
+
+ describe("which evaluates to true", [&]{
+ before_each([&]{
+ value = true;
+ });
+
+ it("must accept a negative match", [&]{
+ value must_not be_falsy;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ value must be_falsy; }());
+ });
+ });
+ });
+
+ describe("when the value is nullptr", [&]{
+ auto value = nullptr;
+
+ it("must accept a positive match", [&]{
+ value must be_falsy;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ value must_not be_falsy; }());
+ });
+ });
+
+ describe("when the value is a pointer", [&]{
+ char* value;
+
+ describe("which evaluates to false", [&]{
+ before_each([&]{
+ value = NULL;
+ });
+
+ it("must accept a positive match", [&]{
+ value must be_falsy;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ value must_not be_falsy; }());
+ });
+ });
+
+ describe("which evaluates to true", [&]{
+ before_each([&]{
+ value = (char*)"cat";
+ });
+
+ it("must accept a negative match", [&]{
+ value must_not be_falsy;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ value must be_falsy; }());
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/be_greater_than.cpp b/vendor/bandit/specs/matchers/be_greater_than.cpp
new file mode 100644
index 00000000..17a97fe3
--- /dev/null
+++ b/vendor/bandit/specs/matchers/be_greater_than.cpp
@@ -0,0 +1,105 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::BeGreaterThan)
+
+describe("be_greater_than matcher", []{
+ describe("when the actual value is a built-in type", [&]{
+ int actualValue = 10;
+
+ describe("and the expected value is the same built-in type", [&]{
+ int expectedValue;
+
+ describe("and the actual value is greater than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 1;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_greater_than(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_greater_than(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value is less than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 100;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_greater_than(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_greater_than(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value equals the expected value", [&]{
+ before_each([&]{
+ expectedValue = actualValue;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_greater_than(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_greater_than(expectedValue); }());
+ });
+ });
+ });
+
+ describe("and the expected value is a different, but comparable, built-in type", [&]{
+ float expectedValue;
+
+ describe("and the actual value is greater than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 1.1;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_greater_than(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_greater_than(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value is less than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 100.1;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_greater_than(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_greater_than(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value equals the expected value", [&]{
+ before_each([&]{
+ expectedValue = actualValue;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_greater_than(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_greater_than(expectedValue); }());
+ });
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/be_gte.cpp b/vendor/bandit/specs/matchers/be_gte.cpp
new file mode 100644
index 00000000..f0e18313
--- /dev/null
+++ b/vendor/bandit/specs/matchers/be_gte.cpp
@@ -0,0 +1,120 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+
+SPEC_BEGIN(Matchers::BeGTE)
+
+describe("be_gte matcher", [&]{
+ int someInteger = 10;
+
+ describe("when the actual value is a built-in type", [&]{
+ int actualValue = someInteger;
+
+ describe("and the expected value is the same built-in type", [&]{
+ int expectedValue;
+
+ describe("and the actual value is greater than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 1;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_gte(expectedValue);
+ actualValue must be_greater_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_gte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must_not be_greater_than_or_equal_to(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value is less than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 100;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_gte(expectedValue);
+ actualValue must_not be_greater_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_gte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must be_greater_than_or_equal_to(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value equals the expected value", [&]{
+ before_each([&]{
+ expectedValue = actualValue;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_gte(expectedValue);
+ actualValue must be_greater_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_gte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must_not be_greater_than_or_equal_to(expectedValue); }());
+ });
+ });
+ });
+
+ describe("and the expected value is a different, but comparable, built-in type", [&]{
+ float expectedValue;
+
+ describe("and the actual value is greater than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 1.1;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_gte(expectedValue);
+ actualValue must be_greater_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_gte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must_not be_greater_than_or_equal_to(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value is less than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 100.1;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_gte(expectedValue);
+ actualValue must_not be_greater_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_gte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must be_greater_than_or_equal_to(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value equals the expected value", [&]{
+ before_each([&]{
+ expectedValue = someInteger / 1.0;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_gte(expectedValue);
+ actualValue must be_greater_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_gte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must_not be_greater_than_or_equal_to(expectedValue); }());
+ });
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/be_less_than.cpp b/vendor/bandit/specs/matchers/be_less_than.cpp
new file mode 100644
index 00000000..30f60c47
--- /dev/null
+++ b/vendor/bandit/specs/matchers/be_less_than.cpp
@@ -0,0 +1,105 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::BeLessThan)
+
+describe("be_less_than matcher", []{
+ describe("when the actual value is a built-in type", [&]{
+ int actualValue = 10;
+
+ describe("and the expected value is the same built-in type", [&]{
+ int expectedValue;
+
+ describe("and the actual value is greater than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 1;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_less_than(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_less_than(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value is less than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 100;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_less_than(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_less_than(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value equals the expected value", [&]{
+ before_each([&]{
+ expectedValue = actualValue;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_less_than(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_less_than(expectedValue); }());
+ });
+ });
+ });
+
+ describe("and the expected value is a different, but comparable, built-in type", [&]{
+ float expectedValue;
+
+ describe("and the actual value is greater than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 1.1;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_less_than(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_less_than(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value is less than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 100.1;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_less_than(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_less_than(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value equals the expected value", [&]{
+ before_each([&]{
+ expectedValue = actualValue;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_less_than(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_less_than(expectedValue); }());
+ });
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/be_lte.cpp b/vendor/bandit/specs/matchers/be_lte.cpp
new file mode 100644
index 00000000..443ac1c5
--- /dev/null
+++ b/vendor/bandit/specs/matchers/be_lte.cpp
@@ -0,0 +1,119 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::BeLTE)
+
+describe("be_lte matcher", [&]{
+ int someInteger = 10;
+
+ describe("when the actual value is a built-in type", [&]{
+ int actualValue = someInteger;
+
+ describe("and the expected value is the same built-in type", [&]{
+ int expectedValue;
+
+ describe("and the actual value is greater than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 1;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_lte(expectedValue);
+ actualValue must_not be_less_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_lte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must be_less_than_or_equal_to(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value is less than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 100;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_lte(expectedValue);
+ actualValue must be_less_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_lte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must_not be_less_than_or_equal_to(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value equals the expected value", [&]{
+ before_each([&]{
+ expectedValue = actualValue;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_lte(expectedValue);
+ actualValue must be_less_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_lte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must_not be_less_than_or_equal_to(expectedValue); }());
+ });
+ });
+ });
+
+ describe("and the expected value is a different, but comparable, built-in type", [&]{
+ float expectedValue;
+
+ describe("and the actual value is greater than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 1.1;
+ });
+
+ it("must pass a negative match", [&]{
+ actualValue must_not be_lte(expectedValue);
+ actualValue must_not be_less_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must be_lte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must be_less_than_or_equal_to(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value is less than the expected value", [&]{
+ before_each([&]{
+ expectedValue = 100.1;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_lte(expectedValue);
+ actualValue must be_less_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_lte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must_not be_less_than_or_equal_to(expectedValue); }());
+ });
+ });
+
+ describe("and the actual value equals the expected value", [&]{
+ before_each([&]{
+ expectedValue = someInteger / 1.0;
+ });
+
+ it("must pass a positive match", [&]{
+ actualValue must be_lte(expectedValue);
+ actualValue must be_less_than_or_equal_to(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not be_lte(expectedValue); }());
+ AssertThrows(std::exception, [&]{ actualValue must_not be_less_than_or_equal_to(expectedValue); }());
+ });
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/be_null.cpp b/vendor/bandit/specs/matchers/be_null.cpp
new file mode 100644
index 00000000..ae3cd40d
--- /dev/null
+++ b/vendor/bandit/specs/matchers/be_null.cpp
@@ -0,0 +1,43 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::BeNull)
+
+describe("be_null matcher", [&]{
+ describe("when the value is a pointer to a built-in type", [&]{
+ int* value;
+
+ describe("which is NULL", [&]{
+ before_each([&]{
+ value = NULL;
+ });
+
+ it("must pass a positive match", [&]{
+ value must be_null;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ value must_not be_null; }());
+ });
+ });
+
+ describe("which is not NULL", [&]{
+ int i = 7;
+
+ before_each([&]{
+ value = &i;
+ });
+
+ it("must pass a negative match", [&]{
+ value must_not be_null;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ value must be_null; }());
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/be_truthy.cpp b/vendor/bandit/specs/matchers/be_truthy.cpp
new file mode 100644
index 00000000..5e583fbf
--- /dev/null
+++ b/vendor/bandit/specs/matchers/be_truthy.cpp
@@ -0,0 +1,85 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::BeTruthy)
+
+describe("be_truthy matcher", [&]{
+ describe("when the value is a built-in type", [&]{
+ bool value;
+
+ describe("which evaluates to false", [&]{
+ before_each([&]{
+ value = false;
+ });
+
+ it("must accept a negative match", [&]{
+ value must_not be_truthy;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ value must be_truthy; }());
+ });
+ });
+
+ describe("which evaluates to true", [&]{
+ before_each([&]{
+ value = true;
+ });
+
+ it("must accept a positive match", [&]{
+ value must be_truthy;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ value must_not be_truthy; }());
+ });
+ });
+ });
+
+ describe("when the value is nullptr", [&]{
+ auto value = nullptr;
+
+ it("must accept a negative match", [&]{
+ value must_not be_truthy;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ value must be_truthy; }());
+ });
+ });
+
+ describe("when the value is a pointer", [&]{
+ char* value;
+
+ describe("which evaluates to false", [&]{
+ before_each([&]{
+ value = NULL;
+ });
+
+ it("must accept a negative match", [&]{
+ value must_not be_truthy;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ value must be_truthy; }());
+ });
+ });
+
+ describe("which evaluates to true", [&]{
+ before_each([&]{
+ value = (char*)"cat";
+ });
+
+ it("must accept a positive match", [&]{
+ value must be_truthy;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ value must_not be_truthy; }());
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/contain.cpp b/vendor/bandit/specs/matchers/contain.cpp
new file mode 100644
index 00000000..2c0b4b3b
--- /dev/null
+++ b/vendor/bandit/specs/matchers/contain.cpp
@@ -0,0 +1,156 @@
+#include <set>
+
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::Contain)
+
+describe("contain matcher", [&]{
+ std::string element0("element0");
+ std::string element1("element1");
+
+ describe("when the container is an STL vector", [&]{
+ describe("which contains the element", [&]{
+ std::vector<std::string> container {element0, element1};
+
+ it("must pass a positive match", [&]{
+ container must contain(element1);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ container must_not contain(element1); }());
+ });
+ });
+
+ describe("which does not contain the element", [&]{
+ std::vector<int> container;
+
+ it("must pass a negative match", [&]{
+ container must_not contain(4);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ container must contain(4); }());
+ });
+ });
+ });
+
+ describe("when the container is an STL map", [&]{
+ describe("which contains the expected key", [&]{
+ std::map<int, int> container {{5, 6}, {7,10}};
+
+ it("must pass a positive match", [&]{
+ container must contain(5);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ container must_not contain(5); }());
+ });
+ });
+
+ describe("which does not contain the expected value", [&]{
+ std::map<int, int> container;
+
+ it("must pass a negative match", [&]{
+ container must_not contain(6);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ container must contain(6); }());
+ });
+ });
+ });
+
+ describe("when the container is an STL set", [&]{
+ describe("which contains the element", [&]{
+ std::set<int> container {5, 7};
+
+ it("must pass a positive match", [&]{
+ container must contain(7);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ container must_not contain(7); }());
+ });
+ });
+
+ describe("which does not contain the element", [&]{
+ std::set<int> container;
+
+ it("must pass a negative match", [&]{
+ container must_not contain(7);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ container must contain(7); }());
+ });
+ });
+ });
+
+ describe("when the container is a C string", [&]{
+ describe("which is null", [&]{
+ char* container = NULL;
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ container must contain("foo"); }());
+ });
+ });
+
+ describe("which contains the substring", [&]{
+ char* container = (char*)"jack and jill";
+ char* element = (char*)"jack";
+
+ it("must pass a positive match", [&]{
+ container must contain(element);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ container must_not contain(element); }());
+ });
+ });
+
+ describe("which does not contain the substring", [&]{
+ char* container = (char*)"batman and robin";
+ char* element = (char*)"catwoman";
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ container must contain(element); }());
+ });
+
+ it("must pass a negative match", [&]{
+ container must_not contain(element);
+ });
+ });
+ });
+
+ describe("when the container is a const C string", [&]{
+ describe("which contains the substring", [&]{
+ const char* container = (char*)"jack and jill";
+ const char* element = (char*)"jack";
+
+ it("must pass a positive match", [&]{
+ container must contain(element);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ container must_not contain(element); }());
+ });
+ });
+
+ describe("which does not contain the substring", [&]{
+ const char* container = (char*)"batman and robin";
+ const char* element = (char*)"catwoman";
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ container must contain(element); }());
+ });
+
+ it("must pass a negative match", [&]{
+ container must_not contain(element);
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/matchers/equal.cpp b/vendor/bandit/specs/matchers/equal.cpp
new file mode 100644
index 00000000..f7f31b0b
--- /dev/null
+++ b/vendor/bandit/specs/matchers/equal.cpp
@@ -0,0 +1,214 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::Equal)
+
+describe("when the actual value is a built-in type", []{
+ int actualValue = 1;
+
+ describe("and the expected value is the same built-in type", [&]{
+ int expectedValue;
+
+ describe("and the values are equal", [&]{
+ before_each([&]{
+ expectedValue = 1;
+ });
+
+ it("must accept a positive match", [&]{
+ actualValue must equal(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not equal(expectedValue); }());
+ });
+ });
+
+ describe("and the values are not equal", [&]{
+ before_each([&]{
+ expectedValue = 147;
+ });
+
+ it("must accept a negative match", [&]{
+ actualValue must_not equal(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must equal(expectedValue); }());
+ });
+ });
+ });
+
+ describe("and the expected value is a different, but comparable, built-in type", [&]{
+ long int expectedValue;
+
+ describe("and the values are equal", [&]{
+ before_each([&]{
+ expectedValue = 1;
+ });
+
+ it("must accept a positive match", [&]{
+ actualValue must equal(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not equal(expectedValue); }());
+ });
+ });
+
+ describe("and the values are not equal", [&]{
+ before_each([&]{
+ expectedValue = 42;
+ });
+
+ it("must accept a negative match", [&]{
+ actualValue must_not equal(expectedValue);
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must equal(expectedValue); }());
+ });
+ });
+ });
+});
+
+describe("when the actual value is declared as a C string", []{
+ char* actualValue = (char*)"actualValue";
+
+ describe("and the expected value is declared as a C string", [&]{
+ std::unique_ptr<char> expectedValue;
+
+ before_each([&]{
+ expectedValue.reset((char*)calloc(strlen(actualValue) + 1, sizeof(char)));
+ });
+
+ describe("and the values are equal", [&]{
+ before_each([&]{
+ stpcpy(expectedValue.get(), actualValue);
+ });
+
+ it("must accept a positive match", [&]{
+ actualValue must equal(expectedValue.get());
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not equal(expectedValue.get()); }());
+ });
+ });
+
+ describe("and the values are not equal", [&]{
+ before_each([&]{
+ stpcpy(expectedValue.get(), "expectedVal");
+ });
+
+ it("must accept a negative match", [&]{
+ actualValue must_not equal(expectedValue.get());
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must equal(expectedValue.get()); }());
+ });
+ });
+ });
+
+ describe("and the expected value is declared as a const C string", [&]{
+ const char *expectedValue;
+
+ describe("and the values are equal", [&]{
+ before_each([&]{
+ expectedValue = "actualValue";
+ });
+
+ it("must accept a positive match", [&]{
+ actualValue must equal(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not equal(expectedValue); }());
+ });
+ });
+ });
+
+ describe("when the expected value is a unique_ptr to a C string", [&]{
+ std::unique_ptr<char> expectedValue;
+
+ before_each([&]{
+ expectedValue.reset((char*)calloc(strlen(actualValue) + 1, sizeof(char)));
+ });
+
+ describe("and the values are equal", [&]{
+ before_each([&]{
+ stpcpy(expectedValue.get(), actualValue);
+ });
+
+ it("must accept a positive match", [&]{
+ actualValue must equal(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not equal(expectedValue); }());
+ });
+ });
+ });
+});
+
+describe("when the actual value is a unique_ptr", []{
+ std::unique_ptr<char> actualValue;
+ auto expectedValue = (char*)"expectedValue";
+
+ before_each([&]{
+ actualValue.reset((char*)calloc(strlen(expectedValue) + 1, sizeof(char)));
+ });
+
+ describe("when the strings are equal", [&]{
+ before_each([&]{
+ stpcpy(actualValue.get(), expectedValue);
+ });
+
+ it("must accept a positive match", [&]{
+ actualValue must equal(expectedValue);
+ });
+ });
+
+ describe("when the strings are not equal", [&]{
+ before_each([&]{
+ stpcpy(actualValue.get(), "hello");
+ });
+
+ it("must accept a negative match", [&]{
+ actualValue must_not equal(expectedValue);
+ });
+ });
+});
+
+describe("when the actual value is declared as char array", []{
+ describe("and the expected value is declared as a C string", []{
+ char actualValue[] = "actualValue";
+
+ describe("and the values are equal", [&]{
+ char* expectedValue = (char*)"actualValue";
+
+ it("must accept a positive match", [&]{
+ actualValue must equal(expectedValue);
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must_not equal(expectedValue); }());
+ });
+ });
+
+ describe("and the values are not equal", [&]{
+ char* expectedValue = (char*)"expectedValue";
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ actualValue must equal(expectedValue); }());
+ });
+
+ it("must accept a negative match", [&]{
+ actualValue must_not equal(expectedValue);
+ });
+ });
+ });
+});
+
+SPEC_END \ No newline at end of file
diff --git a/vendor/bandit/specs/matchers/throw_exception.cpp b/vendor/bandit/specs/matchers/throw_exception.cpp
new file mode 100644
index 00000000..c7531d5f
--- /dev/null
+++ b/vendor/bandit/specs/matchers/throw_exception.cpp
@@ -0,0 +1,104 @@
+#include <specs/specs.h>
+
+using namespace bandit::Matchers;
+
+SPEC_BEGIN(Matchers::ThrowException)
+
+describe("throw_exception", []{
+ describe("when no exception is specified", [&]{
+ std::exception exception;
+
+ std::function<void()> exception_block = [&]{ throw exception; };
+
+ describe("when the block throws an exception", [&]{
+ it("must pass a positive match", [&]{
+ exception_block must throw_exception;
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(MatcherException, [&]{ exception_block must_not throw_exception; }());
+ });
+ });
+
+ describe("when the block does not throw an exception", [&]{
+ std::function<void()> quiet_block = [&]{};
+
+ it("must pass a negative match", [&]{
+ quiet_block must_not throw_exception;
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ quiet_block must throw_exception; }());
+ });
+ });
+ });
+
+ describe("with an exception class specified", [&]{
+ std::logic_error expected_exception("logic_error");
+
+ describe("when the block throws the expected exception", [&]{
+ std::function<void()> exception_block = [&]{ throw expected_exception; };
+
+ it("must pass a positive match", [&]{
+ exception_block must throw_exception.operator()<decltype(expected_exception)>();
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ exception_block must_not throw_exception.operator()<decltype(expected_exception)>(); }());
+ });
+ });
+
+ // TODO: Because C++ lacks reflection, there's no way to implement
+ // subclass-checking. I'm leaving these tests here for when the
+ // language has evolved sufficiently.
+ xdescribe("when the block throws a sublass of the specified exception", [&]{
+ std::function<void()> subclass_block = [&]{ throw std::invalid_argument("invalid argument"); };
+
+ describe("when subclasses are expected", [&]{
+ it("must pass a positive match", [&]{
+ subclass_block must throw_exception.operator()<std::logic_error>().or_subclass();
+ });
+
+ it("must reject a negative match", [&]{
+ AssertThrows(std::exception, [&]{ subclass_block must_not throw_exception.operator()<std::logic_error>().or_subclass(); }());
+ });
+ });
+
+ describe("when subclasses are not expected", [&]{
+ it("must pass a negative match", [&]{
+ subclass_block must_not throw_exception.operator()<std::logic_error>();
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ subclass_block must throw_exception.operator()<std::logic_error>(); }());
+ });
+ });
+ });
+
+ describe("when the block throws an unrelated exception", [&]{
+ std::function<void()> unrelated_block = [&]{ throw std::range_error("range error"); };
+
+ it("must pass a negative match", [&]{
+ unrelated_block must_not throw_exception.operator()<decltype(expected_exception)>();
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ unrelated_block must throw_exception.operator()<decltype(expected_exception)>(); }());
+ });
+ });
+
+ describe("when the block does not throw an exception", [&]{
+ std::function<void()> quiet_block = [&]{};
+
+ it("must pass a negative match", [&]{
+ quiet_block must_not throw_exception.operator()<decltype(expected_exception)>();
+ });
+
+ it("must reject a positive match", [&]{
+ AssertThrows(std::exception, [&]{ quiet_block must throw_exception.operator()<decltype(expected_exception)>(); }());
+ });
+ });
+ });
+});
+
+SPEC_END
diff --git a/vendor/bandit/specs/options.spec.cpp b/vendor/bandit/specs/options.spec.cpp
new file mode 100644
index 00000000..74d057ec
--- /dev/null
+++ b/vendor/bandit/specs/options.spec.cpp
@@ -0,0 +1,121 @@
+#include <specs/specs.h>
+
+using namespace bandit::specs::util;
+namespace bd = bandit::detail;
+
+go_bandit([](){
+
+ describe("options:", [&](){
+
+ it("parses the '--help' option", [&](){
+ const char* args[] = {"executable", "--help"};
+ argv_helper argv(2, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+
+ AssertThat(opt.help(), IsTrue());
+ });
+
+ it("parses the '--version' option", [&](){
+ const char* args[] = {"executable", "--version"};
+ argv_helper argv(2, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+
+ AssertThat(opt.version(), IsTrue());
+ });
+
+ it("parses the '--no-color' option", [&](){
+ const char* args[] = {"executable", "--no-color"};
+ argv_helper argv(2, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+
+ AssertThat(opt.no_color(), IsTrue());
+ });
+
+ it("parser the '--formatter=vs' option", [&](){
+ const char* args[] = {"executable", "--formatter=vs"};
+ argv_helper argv(2, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+ AssertThat(opt.formatter(), Equals(bd::options::formatters::FORMATTER_VS));
+ });
+
+ it("parser the '--formatter=default' option", [&](){
+ const char* args[] = {"executable", "--formatter=default"};
+ argv_helper argv(2, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+ AssertThat(opt.formatter(), Equals(bd::options::formatters::FORMATTER_DEFAULT));
+ });
+
+ it("parses the '--skip=\"substring\"' option", [&](){
+ const char* args[] = {"executable", "--skip=substring"};
+ argv_helper argv(2, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+ AssertThat(opt.skip(), Equals("substring"));
+ });
+
+ it("parses skip as empty string if not present", [&](){
+ const char* args[] = {"executable"};
+ argv_helper argv(1, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+ AssertThat(opt.skip(), Equals(""));
+ });
+
+ it("parses the '--only=\"substring\"' option", [&](){
+ const char* args[] = {"executable", "--only=substring"};
+ argv_helper argv(2, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+ AssertThat(opt.only(), Equals("substring"));
+ });
+
+ it("parses only as empty string if not present", [&](){
+ const char* args[] = {"executable"};
+ argv_helper argv(1, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+ AssertThat(opt.only(), Equals(""));
+ });
+
+ it("parses the '--break-on-failure' oprtion", [&](){
+ const char* args[] = {"executable", "--break-on-failure"};
+ argv_helper argv(2, args);
+
+ bd::options opt(argv.argc(), argv.argv());
+
+ AssertThat(opt.break_on_failure(), IsTrue());
+ });
+
+ describe("with no arguments", [&](){
+ const char* args[] = {"executable"};
+ argv_helper argv(1, args);
+ bd::options opt(argv.argc(), argv.argv());
+
+ it("cannot find '--help'", [&](){
+ AssertThat(opt.help(), IsFalse());
+ });
+
+ it("cannot find '--version'", [&](){
+ AssertThat(opt.version(), IsFalse());
+ });
+
+ it("cannot find '--no-color'", [&](){
+ AssertThat(opt.no_color(), IsFalse());
+ });
+
+ it("cannot fine '--break-on-failure'", [&](){
+ AssertThat(opt.break_on_failure(), IsFalse())
+ });
+
+ it("uses default formatter for '--formatter'", [&](){
+ AssertThat(opt.formatter(), Equals(bd::options::formatters::FORMATTER_DEFAULT));
+ });
+ });
+ });
+
+});
diff --git a/vendor/bandit/specs/reporters/colorizer.spec.cpp b/vendor/bandit/specs/reporters/colorizer.spec.cpp
new file mode 100644
index 00000000..7708ec81
--- /dev/null
+++ b/vendor/bandit/specs/reporters/colorizer.spec.cpp
@@ -0,0 +1,45 @@
+#ifndef _WIN32
+#include <specs/specs.h>
+
+go_bandit([](){
+
+ describe("colorizer: ", [&](){
+
+ describe("colors enabled", [&](){
+ bandit::detail::colorizer colorizer;
+
+ it("can set color to green", [&](){
+ AssertThat(colorizer.green(), Equals("\033[1;32m"));
+ });
+
+ it("set color to red", [&](){
+ AssertThat(colorizer.red(), Equals("\033[1;31m"));
+ });
+ it("resets color", [&](){
+ AssertThat(colorizer.reset(), Equals("\033[0m"));
+ });
+
+ });
+
+ describe("colors disabled", [&](){
+
+ bandit::detail::colorizer colorizer(false);
+
+ it("ignores setting color to green", [&](){
+ AssertThat(colorizer.green(), Equals(""));
+ });
+
+ it("ignores setting color to red", [&](){
+ AssertThat(colorizer.red(), Equals(""));
+ });
+
+ it("ignores resetting colors", [&](){
+ AssertThat(colorizer.reset(), Equals(""));
+ });
+
+ });
+
+ });
+
+});
+#endif \ No newline at end of file
diff --git a/vendor/bandit/specs/reporters/dots_reporter.spec.cpp b/vendor/bandit/specs/reporters/dots_reporter.spec.cpp
new file mode 100644
index 00000000..f06c8d77
--- /dev/null
+++ b/vendor/bandit/specs/reporters/dots_reporter.spec.cpp
@@ -0,0 +1,202 @@
+#include <specs/specs.h>
+namespace bd = bandit::detail;
+
+go_bandit([](){
+
+ describe("dots_reporter:", [&](){
+ std::unique_ptr<std::stringstream> stm;
+ std::unique_ptr<bd::dots_reporter> reporter;
+ bd::default_failure_formatter formatter;
+ bd::colorizer colorizer(false);
+
+ before_each([&](){
+ stm = std::unique_ptr<std::stringstream>(new std::stringstream());
+ reporter = std::unique_ptr<bd::dots_reporter>(
+ new bd::dots_reporter(*stm, formatter, colorizer));
+ });
+
+ auto output = [&](){ return stm->str(); };
+
+ describe("an empty test run", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->test_run_complete();
+ });
+
+ it("reports no tests where run", [&](){
+ AssertThat(output(), Equals("\nCould not find any tests.\n"));
+ });
+
+ it("is not considered successful", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(false));
+ });
+
+ });
+
+ describe("a successful test run", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->it_starting("my test");
+ reporter->it_succeeded("my test");
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports a successful test run", [&](){
+ AssertThat(output(), Contains("Success!"));
+ AssertThat(output(), EndsWith("Test run complete. 1 tests run. 1 succeeded.\n"));
+ });
+
+ it("displays a dot for the successful test", [&](){
+ AssertThat(output(), StartsWith("."));
+ });
+
+ it("reports a successful test run", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(true));
+ });
+ });
+
+ describe("a failing test run", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->it_starting("my test");
+
+ bd::assertion_exception exception("assertion failed!", "some_file", 123);
+ reporter->it_failed("my test", exception);
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports a failing test run in summary", [&](){
+ AssertThat(output(), EndsWith("Test run complete. 1 tests run. 0 succeeded. 1 failed.\n"));
+ });
+
+ it("reports the failed assertion", [&](){
+ AssertThat(output(), Contains("my context my test:\nsome_file:123: assertion failed!"));
+ });
+
+ it("only reports assertion failure once", [&](){
+ AssertThat(output(), Has().Exactly(1).EndingWith("assertion failed!"));
+ });
+
+ it("reports an 'F' for the failed assertion", [&](){
+ AssertThat(output(), StartsWith("F"));
+ });
+
+ it("reports a failed test run", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(false));
+ });
+ });
+
+ describe("a test run with a non assertion_exception thrown", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->it_starting("my test");
+
+ reporter->it_unknown_error("my test");
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports an 'E' for the failed test", [&](){
+ AssertThat(output(), StartsWith("E"));
+ });
+
+ it("reports the failed test", [&](){
+ AssertThat(output(), Contains("my context my test:\nUnknown exception"))
+ });
+
+ });
+
+ describe("a failing test run with nested contexts", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->context_starting("a nested context");
+ reporter->it_starting("my test");
+
+ bd::assertion_exception exception("assertion failed!", "some_file", 123);
+ reporter->it_failed("my test", exception);
+
+ reporter->context_ended("a nested context");
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports a failing test run in summary", [&](){
+ AssertThat(output(), EndsWith("Test run complete. 1 tests run. 0 succeeded. 1 failed.\n"));
+ });
+
+ it("reports the failed assertion", [&](){
+ AssertThat(output(), Contains("my context a nested context my test:\nsome_file:123: assertion failed!"));
+ });
+
+ it("reports an 'F' for the failed assertion", [&](){
+ AssertThat(output(), StartsWith("F"));
+ });
+
+ it("reports a failed test run", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(false));
+ });
+
+ });
+
+ describe("a context with test run errors", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+
+ bd::test_run_error error("we dun goofed!");
+ reporter->test_run_error("my context", error);
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports that the context has failed", [&](){
+ AssertThat(output(), Contains("Failed to run \"my context\": error \"we dun goofed!\""));
+ });
+
+ it("reports test run errors in summary", [&](){
+ AssertThat(output(), EndsWith("Test run complete. 0 tests run. 0 succeeded. 1 test run errors.\n"))
+ });
+
+ it("reports a failed test run", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(false));
+ });
+ });
+
+ describe("a context with a skipped test", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+
+ reporter->it_starting("my test");
+ reporter->it_succeeded("my test");
+ reporter->it_skip("my skipped test");
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports that there is one skipped test in the summary", [&](){
+ AssertThat(output(), EndsWith("Test run complete. 1 tests run. 1 succeeded. 1 skipped.\n"));
+ });
+
+ });
+ });
+
+
+});
diff --git a/vendor/bandit/specs/reporters/single_line_reporter.spec.cpp b/vendor/bandit/specs/reporters/single_line_reporter.spec.cpp
new file mode 100644
index 00000000..ef7b5206
--- /dev/null
+++ b/vendor/bandit/specs/reporters/single_line_reporter.spec.cpp
@@ -0,0 +1,201 @@
+#include <specs/specs.h>
+namespace bd = bandit::detail;
+
+go_bandit([](){
+
+ describe("single line reporter", [&](){
+ std::unique_ptr<std::stringstream> stm;
+ std::unique_ptr<bd::single_line_reporter> reporter;
+ bd::default_failure_formatter formatter;
+ bd::colorizer colorizer(false);
+
+ before_each([&](){
+ stm = std::unique_ptr<std::stringstream>(new std::stringstream());
+ reporter = std::unique_ptr<bd::single_line_reporter>(
+ new bd::single_line_reporter(*stm, formatter, colorizer));
+ });
+
+ auto output = [&](){ return stm->str(); };
+
+ describe("an empty test run", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->test_run_complete();
+ });
+
+ it("reports that no tests were run", [&](){
+ AssertThat(output(), Equals("\nCould not find any tests.\n"));
+ });
+
+ it("is not considered successful", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(false));
+ });
+ });
+
+ describe("a successful test run", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->it_starting("my test");
+ reporter->it_succeeded("my test");
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports a successful test run", [&](){
+ AssertThat(output(), EndsWith("Test run complete. 1 tests run. 1 succeeded.\n"));
+ });
+
+ it("displays progress for the test", [&](){
+ AssertThat(output(), StartsWith("\rExecuted 0 tests."
+ "\rExecuted 1 tests."));
+ });
+
+ it("reports a successful test run", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(true));
+ });
+ });
+
+ describe("a failing test run", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->it_starting("my test");
+
+ bd::assertion_exception exception("assertion failed!", "some_file", 123);
+ reporter->it_failed("my test", exception);
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports a failing test run in summary", [&](){
+ AssertThat(output(), EndsWith("Test run complete. 1 tests run. 0 succeeded. 1 failed.\n"));
+ });
+
+ it("reports the failed assertion", [&](){
+ AssertThat(output(), Contains("my context my test:\nsome_file:123: assertion failed!"));
+ });
+
+ it("reports failing test in progress", [&](){
+ AssertThat(output(), StartsWith("\rExecuted 0 tests."
+ "\rExecuted 1 tests. 0 succeeded. 1 failed."));
+ });
+
+ it("reports a failed test run", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(false));
+ });
+ });
+
+ describe("a test run with a non assertion_exception thrown", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->it_starting("my test");
+
+ reporter->it_unknown_error("my test");
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports failing test in progress", [&](){
+ AssertThat(output(), StartsWith("\rExecuted 0 tests."
+ "\rExecuted 1 tests. 0 succeeded. 1 failed."));
+ });
+
+ it("reports the failed test", [&](){
+ AssertThat(output(), Contains("my context my test:\nUnknown exception"))
+ });
+
+ });
+
+ describe("a failing test run with nested contexts", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->context_starting("a nested context");
+ reporter->it_starting("my test");
+
+ bd::assertion_exception exception("assertion failed!", "some_file", 123);
+ reporter->it_failed("my test", exception);
+
+ reporter->context_ended("a nested context");
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports a failing test run in summary", [&](){
+ AssertThat(output(), EndsWith("Test run complete. 1 tests run. 0 succeeded. 1 failed.\n"));
+ });
+
+ it("reports the failed assertion", [&](){
+ AssertThat(output(), Contains("my context a nested context my test:\nsome_file:123: assertion failed!"));
+ });
+
+ it("displays a failed test in progress report", [&](){
+ AssertThat(output(), StartsWith("\rExecuted 0 tests."
+ "\rExecuted 1 tests. 0 succeeded. 1 failed."));
+ });
+
+ it("reports a failed test run", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(false));
+ });
+
+ });
+
+ describe("a context with test run errors", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+
+ bd::test_run_error error("we dun goofed!");
+ reporter->test_run_error("my context", error);
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports that the context has failed", [&](){
+ AssertThat(output(), Contains("Failed to run \"my context\": error \"we dun goofed!\""));
+ });
+
+ it("reports test run errors in summary", [&](){
+ AssertThat(output(), EndsWith("Test run complete. 0 tests run. 0 succeeded. 1 test run errors.\n"))
+ });
+
+ it("reports a failed test run", [&](){
+ AssertThat(reporter->did_we_pass(), Equals(false));
+ });
+ });
+
+ describe("a context with a skipped test", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+
+ reporter->it_starting("my test");
+ reporter->it_succeeded("my test");
+ reporter->it_skip("my skipped test");
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("reports that there is one skipped test in the summary", [&](){
+ AssertThat(output(), EndsWith("Test run complete. 1 tests run. 1 succeeded. 1 skipped.\n"));
+ });
+
+ });
+
+
+ });
+
+});
diff --git a/vendor/bandit/specs/reporters/xunit_reporter.spec.cpp b/vendor/bandit/specs/reporters/xunit_reporter.spec.cpp
new file mode 100644
index 00000000..07f0c3b7
--- /dev/null
+++ b/vendor/bandit/specs/reporters/xunit_reporter.spec.cpp
@@ -0,0 +1,161 @@
+#include <specs/specs.h>
+namespace bd = bandit::detail;
+
+go_bandit([](){
+
+ describe("xunit_reporter:", [&](){
+ std::unique_ptr<std::stringstream> stm;
+ bd::default_failure_formatter formatter;
+ std::unique_ptr<bd::xunit_reporter> reporter;
+
+ auto output = [&](){ return stm->str(); };
+
+ before_each([&](){
+ stm = std::unique_ptr<std::stringstream>(new std::stringstream());
+ reporter = std::unique_ptr<bd::xunit_reporter>(new bd::xunit_reporter(*stm, formatter));
+ });
+
+ describe("an empty test run", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->test_run_complete();
+ });
+
+ it("adds a header to the output", [&](){
+ AssertThat(output(), StartsWith("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"));
+ });
+
+ it("outputs an empty test report", [&](){
+ AssertThat(output(), Contains(
+ "<testsuite name=\"bandit\" tests=\"0\" errors=\"0\" failures=\"0\">\n"
+ "</testsuite>\n"));
+ });
+
+ });
+
+ describe("a test run with one, successful, test", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->it_starting("my test");
+ reporter->it_succeeded("my test");
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("outputs info about the successful test", [&](){
+ AssertThat(output(), Contains(
+ "<testsuite name=\"bandit\" tests=\"1\" errors=\"0\" failures=\"0\">\n"
+ "\t<testcase classname=\"my context\" name=\"my test\" time=\"0\">\n"
+ "\t</testcase>\n"
+ "</testsuite>\n"));
+ });
+ });
+
+ describe("a test run with one, failing test", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->it_starting("my test");
+
+ bd::assertion_exception exception("assertion failed!", "some_file", 123);
+ reporter->it_failed("my test", exception);
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+
+ });
+
+ it("outputs the failing test", [&](){
+ AssertThat(output(), Contains(
+ "<testsuite name=\"bandit\" tests=\"1\" errors=\"0\" failures=\"1\">\n"
+ "\t<testcase classname=\"my context\" name=\"my test\" time=\"0\">\n"
+ "\t\t<failure message=\"some_file:123: assertion failed!\" />\n"
+ "\t</testcase>\n"
+ "</testsuite>\n"));
+ });
+
+ });
+
+ describe("a test run with one test with an unknown error", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+ reporter->it_starting("my test");
+
+ reporter->it_unknown_error("my test");
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("outputs the erroneous test", [&](){
+ AssertThat(output(), Contains(
+ "<testsuite name=\"bandit\" tests=\"1\" errors=\"0\" failures=\"1\">\n"
+ "\t<testcase classname=\"my context\" name=\"my test\" time=\"0\">\n"
+ "\t\t<failure message=\"Unknown exception\" />\n"
+ "\t</testcase>\n"
+ "</testsuite>\n"));
+ });
+
+ });
+
+ describe("a test run with one test failing with characters that need escaping", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context & < > \\ \"");
+ reporter->it_starting("my test & < > \\ \"");
+
+ bd::assertion_exception exception("assertion failed & < > \\ \"", "some_file", 123);
+ reporter->it_failed("my test & < > \\ \"", exception);
+
+ reporter->context_ended("my context & < > \\ \"");
+ reporter->test_run_complete();
+ });
+
+ it("outputs the escaped characters", [&](){
+ AssertThat(output(), Contains(
+ "<testsuite name=\"bandit\" tests=\"1\" errors=\"0\" failures=\"1\">\n"
+ "\t<testcase classname=\"my context &amp; &lt; &gt; &apos; &quot;\" name=\"my test &amp; &lt; &gt; &apos; &quot;\" time=\"0\">\n"
+ "\t\t<failure message=\"some_file:123: assertion failed &amp; &lt; &gt; &apos; &quot;\" />\n"
+ "\t</testcase>\n"
+ "</testsuite>\n"));
+ });
+
+ });
+
+ describe("a context with a skipped test", [&](){
+
+ before_each([&](){
+ reporter->test_run_starting();
+ reporter->context_starting("my context");
+
+ reporter->it_starting("my test");
+ reporter->it_succeeded("my test");
+ reporter->it_skip("my skipped test");
+
+ reporter->context_ended("my context");
+ reporter->test_run_complete();
+ });
+
+ it("outputs info about the skipped test", [&](){
+ AssertThat(output(), Contains(
+ "<testsuite name=\"bandit\" tests=\"1\" errors=\"0\" failures=\"0\" skipped=\"1\">\n"
+ "\t<testcase classname=\"my context\" name=\"my test\" time=\"0\">\n"
+ "\t</testcase>\n"
+ "\t<testcase classname=\"my context\" name=\"my skipped test\" time=\"0\">\n"
+ "\t\t<skipped />\n"
+ "\t</testcase>\n"
+ "</testsuite>\n"));
+ });
+
+ });
+
+ });
+
+});
diff --git a/vendor/bandit/specs/run.spec.cpp b/vendor/bandit/specs/run.spec.cpp
new file mode 100644
index 00000000..6e59bac7
--- /dev/null
+++ b/vendor/bandit/specs/run.spec.cpp
@@ -0,0 +1,77 @@
+#include <specs/specs.h>
+using namespace bandit::fakes;
+using namespace bandit::specs::util;
+namespace bd = bandit::detail;
+
+go_bandit([](){
+
+ describe("run:", [&](){
+ std::unique_ptr<bd::spec_registry> specs;
+ std::unique_ptr<argv_helper> argv;
+ fake_reporter_ptr reporter;
+ std::unique_ptr<bd::contextstack_t> context_stack;
+
+ auto call_run = [&]() -> int {
+ bd::options opt(argv->argc(), argv->argv());
+ return bandit::run(opt, *specs, *context_stack, *reporter);
+ };
+
+ before_each([&](){
+ specs = std::unique_ptr<bd::spec_registry>(new bd::spec_registry());
+
+ reporter = fake_reporter_ptr(new fake_reporter());
+
+ context_stack = std::unique_ptr<bd::contextstack_t>(new bd::contextstack_t());
+
+ const char* args[] = {"executable"};
+ argv = std::unique_ptr<argv_helper>(new argv_helper(1, args));
+ });
+
+ it("pushes the global context on the context stack", [&](){
+ call_run();
+ AssertThat(*context_stack, Is().OfLength(1));
+ });
+
+ describe("a successful test run", [&](){
+ int number_of_specs_called;
+
+ before_each([&](){
+ number_of_specs_called = 0;
+ specs->push_back([&](){ number_of_specs_called++; });
+ });
+
+ it("calls the context", [&](){
+ call_run();
+ AssertThat(number_of_specs_called, Equals(1));
+ });
+
+ it("tells reporter a test run is about to start", [&](){
+ call_run();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("test_run_starting"));
+ });
+
+ it("tells reporter a test run has completed", [&](){
+ call_run();
+ AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("test_run_complete"));
+ });
+
+ it("returns 0 as no specs failed", [&](){
+ AssertThat(call_run(), Equals(0));
+ });
+ });
+
+
+ describe("a failing test run", [&](){
+
+ before_each([&](){
+ reporter->set_test_run_status(false);
+ });
+
+ it("returns a non-zero error code", [&](){
+ AssertThat(call_run(), IsGreaterThan(0));
+ });
+
+ });
+ });
+
+});
diff --git a/vendor/bandit/specs/run_policies/bandit_run_policy.spec.cpp b/vendor/bandit/specs/run_policies/bandit_run_policy.spec.cpp
new file mode 100644
index 00000000..75f56bc6
--- /dev/null
+++ b/vendor/bandit/specs/run_policies/bandit_run_policy.spec.cpp
@@ -0,0 +1,250 @@
+#include <specs/specs.h>
+
+go_bandit([](){
+ namespace bd = bandit::detail;
+
+ describe("bandit run policy", [&](){
+ std::unique_ptr<bd::contextstack_t> contextstack;
+ std::unique_ptr<bd::context> global_context;
+ std::string only_pattern;
+ std::string skip_pattern;
+ bool break_on_failure;
+
+ auto create_policy = [&]() -> bd::bandit_run_policy {
+ return bd::bandit_run_policy(skip_pattern.c_str(), only_pattern.c_str(), break_on_failure);
+ };
+
+ before_each([&](){
+ contextstack = std::unique_ptr<bd::contextstack_t>(new bd::contextstack_t());
+ bool hard_skip = false;
+ global_context = std::unique_ptr<bd::context>(new bd::bandit_context("", hard_skip));
+ contextstack->push_back(global_context.get());
+ break_on_failure = false;
+ });
+
+ describe("neither skip nor only specified", [&](){
+ before_each([&](){
+ only_pattern = "";
+ skip_pattern = "";
+ });
+
+ it("always says run", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsTrue());
+ });
+
+ describe("with 'break-on-failure' set", [&](){
+
+ before_each([&](){
+ break_on_failure = true;
+ });
+
+ it("says run if no failure has been encountered", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsTrue());
+ });
+
+ it("says don't run if a failure has been encountered", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ policy.encountered_failure();
+ AssertThat(policy.should_run("it name", *contextstack), IsFalse());
+ });
+
+ });
+
+ describe("has context marked with 'hard_skip' in stack", [&](){
+ std::unique_ptr<bd::context> hard_skip_context;
+
+ before_each([&](){
+ bool hard_skip = true;
+ hard_skip_context = std::unique_ptr<bd::context>(new bd::bandit_context("always ignore", hard_skip));
+ contextstack->push_back(hard_skip_context.get());
+ });
+
+ it("never runs", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsFalse());
+ AssertThat(policy.should_run("it name matches 'skip'", *contextstack), IsFalse());
+ AssertThat(policy.should_run("it name matches 'only'", *contextstack), IsFalse());
+ });
+
+ });
+
+ });
+
+ describe("'skip' specified, 'only' unspecified", [&](){
+
+ before_each([&](){
+ only_pattern = "";
+ skip_pattern = "skip";
+ });
+
+ describe("current context matches 'skip'", [&](){
+ std::unique_ptr<bd::context> current_context;
+
+ before_each([&](){
+ bool hard_skip = false;
+ current_context = std::unique_ptr<bd::context>(new bd::bandit_context("context matches 'skip'", hard_skip));
+ contextstack->push_back(current_context.get());
+ });
+
+ it("never runs", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsFalse());
+ });
+
+ });
+
+ describe("current context doesn't match 'skip'", [&](){
+ std::unique_ptr<bd::context> current_context;
+
+ before_each([&](){
+ bool hard_skip = false;
+ current_context = std::unique_ptr<bd::context>(new bd::bandit_context("context doesn't match", hard_skip));
+ contextstack->push_back(current_context.get());
+ });
+
+ it("runs if spec's name doesn't match", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsTrue());
+ });
+
+ it("doesn't run if spec's name matches", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name matching 'skip'", *contextstack), IsFalse());
+ });
+
+ });
+
+ });
+
+ describe("'only' specified, 'skip' unspecified", [&](){
+
+ before_each([&](){
+ only_pattern = "only";
+ skip_pattern = "";
+ });
+
+ describe("current context matches 'only'", [&](){
+ std::unique_ptr<bd::context> current_context;
+
+ before_each([&](){
+ bool hard_skip = false;
+ current_context = std::unique_ptr<bd::context>(new bd::bandit_context("context matches 'only'", hard_skip));
+ contextstack->push_back(current_context.get());
+ });
+
+ it("always runs", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsTrue());
+ });
+
+ });
+
+ describe("current context doesn't match 'only'", [&](){
+ std::unique_ptr<bd::context> current_context;
+
+ before_each([&](){
+ bool hard_skip = false;
+ current_context = std::unique_ptr<bd::context>(new bd::bandit_context("context doesn't match", hard_skip));
+ contextstack->push_back(current_context.get());
+ });
+
+ it("doesn't run if spec's name doesn't match", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsFalse());
+ });
+
+ it("runs if spec's name matches", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name matching 'only'", *contextstack), IsTrue());
+ });
+
+ });
+
+ });
+
+ describe("'skip' specified, 'only' specified", [&](){
+
+ before_each([&](){
+ only_pattern = "only";
+ skip_pattern = "skip";
+ });
+
+ describe("current context matches 'skip'", [&](){
+ std::unique_ptr<bd::context> current_context;
+
+ before_each([&](){
+ bool hard_skip = false;
+ current_context = std::unique_ptr<bd::context>(new bd::bandit_context("context matches 'skip'", hard_skip));
+ contextstack->push_back(current_context.get());
+ });
+
+ it("doesn't run if 'it' doesn't match 'only'", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsFalse());
+ });
+
+ it("runs if 'it' matches 'only'", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it matches 'only'", *contextstack), IsTrue());
+ });
+
+ });
+
+ describe("current context 'only'", [&](){
+ std::unique_ptr<bd::context> current_context;
+
+ before_each([&](){
+ bool hard_skip = false;
+ current_context = std::unique_ptr<bd::context>(new bd::bandit_context("context matches 'only'", hard_skip));
+ contextstack->push_back(current_context.get());
+ });
+
+ it("runs if spec's name doesn't match anything", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsTrue());
+ });
+
+ it("doesn't run if spec's name matches 'skip'", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name matching 'skip'", *contextstack), IsFalse());
+ });
+
+ });
+
+ describe("has both 'only' and 'skip' in context stack", [&](){
+ std::unique_ptr<bd::context> current_context;
+ std::unique_ptr<bd::context> parent_context;
+
+ before_each([&](){
+ bool hard_skip = false;
+ current_context = std::unique_ptr<bd::context>(new bd::bandit_context("context matches 'only'", hard_skip));
+ parent_context = std::unique_ptr<bd::context>(new bd::bandit_context("context matches 'skip'", hard_skip));
+ contextstack->push_back(parent_context.get());
+ contextstack->push_back(current_context.get());
+ });
+
+ it("runs if spec's name doesn't match anything", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name", *contextstack), IsTrue());
+ });
+
+ it("doesn't run if spec's name matches 'skip'", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name matching 'skip'", *contextstack), IsFalse());
+ });
+ it("runs if spec's name matches 'only'", [&](){
+ bd::bandit_run_policy policy = create_policy();
+ AssertThat(policy.should_run("it name matching 'only'", *contextstack), IsTrue());
+ });
+
+ });
+
+ });
+
+
+ });
+
+});
+
diff --git a/vendor/bandit/specs/specs.h b/vendor/bandit/specs/specs.h
new file mode 100644
index 00000000..219e89ee
--- /dev/null
+++ b/vendor/bandit/specs/specs.h
@@ -0,0 +1,10 @@
+#ifndef BANDIT_SPECS
+#define BANDIT_SPECS
+
+#include <bandit/bandit.h>
+using namespace bandit;
+
+#include <specs/fakes/fakes.h>
+#include <specs/util/util.h>
+
+#endif
diff --git a/vendor/bandit/specs/synopsis.spec.cpp b/vendor/bandit/specs/synopsis.spec.cpp
new file mode 100644
index 00000000..3b717f75
--- /dev/null
+++ b/vendor/bandit/specs/synopsis.spec.cpp
@@ -0,0 +1,54 @@
+#include <specs/specs.h>
+
+go_bandit([](){
+ describe("my first spec", [&]() {
+ int a;
+
+ before_each([&](){
+ a = 99;
+ });
+
+ it("should be initialized", [&](){
+ AssertThat(a, Equals(99));
+ a = 102;
+ });
+
+ describe("nested spec", [&](){
+
+ before_each([&](){
+ a += 3;
+ });
+
+ it("should build on outer spec", [&](){
+ AssertThat(a, Equals(102));
+ a = 666;
+ });
+
+ it("should build on outer spec yet again", [&](){
+ AssertThat(a, Equals(102));
+ a = 667;
+ });
+
+ });
+
+ it("should be initialized before each it", [&](){
+ AssertThat(a, Equals(99));
+ });
+ });
+
+ describe("my second spec", [&](){
+ int b;
+
+ before_each([&](){
+ b = 22;
+ });
+
+ before_each([&](){
+ b += 3;
+ });
+
+ it("should be 25", [&](){
+ AssertThat(b, Equals(25));
+ });
+ });
+});
diff --git a/vendor/bandit/specs/util/argv_helper.h b/vendor/bandit/specs/util/argv_helper.h
new file mode 100644
index 00000000..dac26765
--- /dev/null
+++ b/vendor/bandit/specs/util/argv_helper.h
@@ -0,0 +1,62 @@
+#ifndef BANDIT_SPECS_ARGV_HELPER_H
+#define BANDIT_SPECS_ARGV_HELPER_H
+
+#include <string.h>
+
+namespace bandit { namespace specs { namespace util {
+
+ //
+ // main() is supposed to receive its arguments as a non const 'char* argv[]'.
+ // This is a pain to create for each test. It's a whole lot easier to create
+ // a 'const char* argv[]' construct.
+ //
+ // This class helps copy from 'const char**' to 'char**' and handle cleanup
+ // automatically.
+ //
+ struct argv_helper
+ {
+ argv_helper(int argc_a, const char* argv_a[])
+ : argc_(argc_a)
+ {
+ non_const_argv_ = new char*[argc_];
+ for(int i=0; i < argc_; i++)
+ {
+ std::string s(argv_a[i]);
+ non_const_argv_[i] = new char[s.size() + 1];
+ for(size_t c=0;c<s.size();c++)
+ {
+ non_const_argv_[i][c] = s[c];
+ }
+ non_const_argv_[i][s.size()] = 0;
+ }
+ }
+
+
+
+ ~argv_helper()
+ {
+ for(int i=0; i < argc_; i++)
+ {
+ delete[] non_const_argv_[i];
+ }
+
+ delete[] non_const_argv_;
+ }
+
+ char** argv()
+ {
+ return non_const_argv_;
+ }
+
+ int argc()
+ {
+ return argc_;
+ }
+
+ private:
+ int argc_;
+ char** non_const_argv_;
+ };
+
+}}}
+#endif
diff --git a/vendor/bandit/specs/util/util.h b/vendor/bandit/specs/util/util.h
new file mode 100644
index 00000000..7ed17dd8
--- /dev/null
+++ b/vendor/bandit/specs/util/util.h
@@ -0,0 +1,6 @@
+#ifndef BANDIT_SPECS_UTIL_H
+#define BANDIT_SPECS_UTIL_H
+
+#include "argv_helper.h"
+
+#endif