From da2dfa749d12f20dfa54ab4b63845988a0c9a14a Mon Sep 17 00:00:00 2001 From: Twinaphex Date: Sat, 12 May 2012 22:26:23 +0200 Subject: [PATCH] Initial commit --- AUTHORS | 163 ++ COPYING | 367 +++ FAQ | 26 + Makefile | 90 + Makefile.libretro | 105 + Makefile.prboomdata | 103 + README | 252 ++ TODO | 1525 ++++++++++ config.h | 93 + data/graphics/boxcc.ppm | 4 + data/graphics/boxcl.ppm | 4 + data/graphics/boxcr.ppm | 4 + data/graphics/boxlc.ppm | 4 + data/graphics/boxll.ppm | 4 + data/graphics/boxlr.ppm | 4 + data/graphics/boxuc.ppm | 4 + data/graphics/boxul.ppm | 4 + data/graphics/boxur.ppm | 4 + data/graphics/dig0.ppm | Bin 0 -> 116 bytes data/graphics/dig1.ppm | Bin 0 -> 116 bytes data/graphics/dig2.ppm | Bin 0 -> 116 bytes data/graphics/dig3.ppm | Bin 0 -> 116 bytes data/graphics/dig4.ppm | Bin 0 -> 116 bytes data/graphics/dig45.ppm | Bin 0 -> 116 bytes data/graphics/dig47.ppm | Bin 0 -> 116 bytes data/graphics/dig5.ppm | Bin 0 -> 116 bytes data/graphics/dig58.ppm | Bin 0 -> 116 bytes data/graphics/dig6.ppm | Bin 0 -> 116 bytes data/graphics/dig7.ppm | Bin 0 -> 116 bytes data/graphics/dig8.ppm | Bin 0 -> 116 bytes data/graphics/dig9.ppm | Bin 0 -> 116 bytes data/graphics/dig91.ppm | Bin 0 -> 116 bytes data/graphics/dig93.ppm | Bin 0 -> 116 bytes data/graphics/diga.ppm | Bin 0 -> 116 bytes data/graphics/digb.ppm | Bin 0 -> 116 bytes data/graphics/digc.ppm | Bin 0 -> 116 bytes data/graphics/digd.ppm | Bin 0 -> 116 bytes data/graphics/dige.ppm | Bin 0 -> 116 bytes data/graphics/digf.ppm | Bin 0 -> 116 bytes data/graphics/digg.ppm | Bin 0 -> 116 bytes data/graphics/digh.ppm | Bin 0 -> 116 bytes data/graphics/digi.ppm | Bin 0 -> 116 bytes data/graphics/digj.ppm | Bin 0 -> 116 bytes data/graphics/digk.ppm | Bin 0 -> 116 bytes data/graphics/digl.ppm | Bin 0 -> 116 bytes data/graphics/digm.ppm | Bin 0 -> 116 bytes data/graphics/dign.ppm | Bin 0 -> 116 bytes data/graphics/digo.ppm | Bin 0 -> 116 bytes data/graphics/digp.ppm | Bin 0 -> 116 bytes data/graphics/digq.ppm | Bin 0 -> 116 bytes data/graphics/digr.ppm | Bin 0 -> 116 bytes data/graphics/digs.ppm | Bin 0 -> 116 bytes data/graphics/digt.ppm | Bin 0 -> 116 bytes data/graphics/digu.ppm | Bin 0 -> 116 bytes data/graphics/digv.ppm | Bin 0 -> 116 bytes data/graphics/digw.ppm | Bin 0 -> 116 bytes data/graphics/digx.ppm | Bin 0 -> 116 bytes data/graphics/digy.ppm | Bin 0 -> 116 bytes data/graphics/digz.ppm | Bin 0 -> 116 bytes data/graphics/m_auto.ppm | Bin 0 -> 4604 bytes data/graphics/m_butt1.ppm | Bin 0 -> 688 bytes data/graphics/m_butt2.ppm | Bin 0 -> 688 bytes data/graphics/m_chat.ppm | Bin 0 -> 6944 bytes data/graphics/m_colors.ppm | Bin 0 -> 56322 bytes data/graphics/m_compat.ppm | Bin 0 -> 9869 bytes data/graphics/m_enem.ppm | Bin 0 -> 4108 bytes data/graphics/m_generl.ppm | Bin 0 -> 4333 bytes data/graphics/m_horsen.ppm | Bin 0 -> 12302 bytes data/graphics/m_keybnd.ppm | Bin 0 -> 6854 bytes data/graphics/m_mess.ppm | Bin 0 -> 5099 bytes data/graphics/m_palno.ppm | Bin 0 -> 158 bytes data/graphics/m_palsel.ppm | Bin 0 -> 254 bytes data/graphics/m_setup.ppm | Bin 0 -> 3028 bytes data/graphics/m_stat.ppm | Bin 0 -> 9104 bytes data/graphics/m_vbox.ppm | Bin 0 -> 12917 bytes data/graphics/m_versen.ppm | Bin 0 -> 6158 bytes data/graphics/m_weap.ppm | Bin 0 -> 4559 bytes data/graphics/prboom.ppm | Bin 0 -> 4063 bytes data/graphics/stbr123.ppm | Bin 0 -> 203 bytes data/graphics/stbr124.ppm | Bin 0 -> 203 bytes data/graphics/stbr125.ppm | Bin 0 -> 203 bytes data/graphics/stbr126.ppm | Bin 0 -> 203 bytes data/graphics/stbr127.ppm | Bin 0 -> 203 bytes data/graphics/stcfn096.ppm | Bin 0 -> 59 bytes data/graphics/stkeys6.ppm | Bin 0 -> 158 bytes data/graphics/stkeys7.ppm | Bin 0 -> 158 bytes data/graphics/stkeys8.ppm | Bin 0 -> 158 bytes data/lumps/animated.lmp | Bin 0 -> 510 bytes data/lumps/b_end.lmp | 0 data/lumps/b_start.lmp | 0 data/lumps/c_end.lmp | 0 data/lumps/c_start.lmp | 0 data/lumps/crblue.lmp | Bin 0 -> 256 bytes data/lumps/crblue2.lmp | Bin 0 -> 256 bytes data/lumps/crbrick.lmp | Bin 0 -> 256 bytes data/lumps/crbrown.lmp | Bin 0 -> 256 bytes data/lumps/crgold.lmp | Bin 0 -> 256 bytes data/lumps/crgray.lmp | Bin 0 -> 256 bytes data/lumps/crgreen.lmp | Bin 0 -> 256 bytes data/lumps/crorange.lmp | Bin 0 -> 256 bytes data/lumps/crred.lmp | Bin 0 -> 256 bytes data/lumps/crtan.lmp | Bin 0 -> 256 bytes data/lumps/cryellow.lmp | Bin 0 -> 256 bytes data/lumps/endboom.lmp | 1 + data/lumps/gammatbl.lmp | 3 + data/lumps/sinetabl.lmp | Bin 0 -> 40960 bytes data/lumps/switches.lmp | Bin 0 -> 820 bytes data/lumps/tangtabl.lmp | Bin 0 -> 16384 bytes data/lumps/tantoang.lmp | Bin 0 -> 8196 bytes data/lumps/watermap.lmp | Bin 0 -> 8704 bytes data/palette.rgb | Bin 0 -> 768 bytes data/prboom.txt | 187 ++ data/rd_graphic.c | 166 ++ data/rd_graphic.h | 8 + data/rd_main.c | 162 ++ data/rd_output.c | 143 + data/rd_output.h | 10 + data/rd_palette.c | 93 + data/rd_palette.h | 10 + data/rd_sound.c | 72 + data/rd_sound.h | 7 + data/rd_util.c | 113 + data/rd_util.h | 38 + data/sprites/dogsa1.ppm | Bin 0 -> 1723 bytes data/sprites/dogsa2a8.ppm | Bin 0 -> 2362 bytes data/sprites/dogsa3a7.ppm | Bin 0 -> 2536 bytes data/sprites/dogsa4a6.ppm | Bin 0 -> 2101 bytes data/sprites/dogsa5.ppm | Bin 0 -> 1723 bytes data/sprites/dogsb1.ppm | Bin 0 -> 1543 bytes data/sprites/dogsb2b8.ppm | Bin 0 -> 2188 bytes data/sprites/dogsb3b7.ppm | Bin 0 -> 2362 bytes data/sprites/dogsb4b6.ppm | Bin 0 -> 2014 bytes data/sprites/dogsb5.ppm | Bin 0 -> 1492 bytes data/sprites/dogsc1.ppm | Bin 0 -> 1723 bytes data/sprites/dogsc2c8.ppm | Bin 0 -> 1927 bytes data/sprites/dogsc3c7.ppm | Bin 0 -> 2362 bytes data/sprites/dogsc4c6.ppm | Bin 0 -> 2101 bytes data/sprites/dogsc5.ppm | Bin 0 -> 1723 bytes data/sprites/dogsd1.ppm | Bin 0 -> 1723 bytes data/sprites/dogsd2d8.ppm | Bin 0 -> 2101 bytes data/sprites/dogsd3d7.ppm | Bin 0 -> 2362 bytes data/sprites/dogsd4d6.ppm | Bin 0 -> 2275 bytes data/sprites/dogsd5.ppm | Bin 0 -> 1492 bytes data/sprites/dogse1.ppm | Bin 0 -> 1723 bytes data/sprites/dogse2.ppm | Bin 0 -> 2362 bytes data/sprites/dogse3.ppm | Bin 0 -> 2449 bytes data/sprites/dogse4.ppm | Bin 0 -> 2188 bytes data/sprites/dogse5.ppm | Bin 0 -> 1492 bytes data/sprites/dogse6.ppm | Bin 0 -> 2188 bytes data/sprites/dogse7.ppm | Bin 0 -> 2449 bytes data/sprites/dogse8.ppm | Bin 0 -> 2362 bytes data/sprites/dogsf1.ppm | Bin 0 -> 1723 bytes data/sprites/dogsf2.ppm | Bin 0 -> 2275 bytes data/sprites/dogsf3.ppm | Bin 0 -> 2710 bytes data/sprites/dogsf4.ppm | Bin 0 -> 2275 bytes data/sprites/dogsf5.ppm | Bin 0 -> 1441 bytes data/sprites/dogsf6.ppm | Bin 0 -> 2275 bytes data/sprites/dogsf7.ppm | Bin 0 -> 2710 bytes data/sprites/dogsf8.ppm | Bin 0 -> 2275 bytes data/sprites/dogsg1.ppm | Bin 0 -> 1543 bytes data/sprites/dogsg2.ppm | Bin 0 -> 2014 bytes data/sprites/dogsg3.ppm | Bin 0 -> 2188 bytes data/sprites/dogsg4.ppm | Bin 0 -> 1927 bytes data/sprites/dogsg5.ppm | Bin 0 -> 1357 bytes data/sprites/dogsg6.ppm | Bin 0 -> 1927 bytes data/sprites/dogsg7.ppm | Bin 0 -> 2188 bytes data/sprites/dogsg8.ppm | Bin 0 -> 2101 bytes data/sprites/dogsh1.ppm | Bin 0 -> 1288 bytes data/sprites/dogsh2.ppm | Bin 0 -> 1573 bytes data/sprites/dogsh3.ppm | Bin 0 -> 1729 bytes data/sprites/dogsh4.ppm | Bin 0 -> 1729 bytes data/sprites/dogsh5.ppm | Bin 0 -> 1339 bytes data/sprites/dogsh6.ppm | Bin 0 -> 1729 bytes data/sprites/dogsh7.ppm | Bin 0 -> 1729 bytes data/sprites/dogsh8.ppm | Bin 0 -> 1573 bytes data/sprites/dogsi0.ppm | Bin 0 -> 1723 bytes data/sprites/dogsj0.ppm | Bin 0 -> 1813 bytes data/sprites/dogsk0.ppm | Bin 0 -> 1795 bytes data/sprites/dogsl0.ppm | Bin 0 -> 1333 bytes data/sprites/dogsm0.ppm | Bin 0 -> 1093 bytes data/sprites/dogsn0.ppm | Bin 0 -> 1138 bytes data/sprites/tnt1a0.ppm | Bin 0 -> 14 bytes prboom.wad | Bin 0 -> 316880 bytes src/SDL/i_net.c | 285 ++ src/SDL/i_sound.c | 722 +++++ src/SDL/i_system.c | 434 +++ src/SDL/i_video.c | 384 +++ src/am_map.c | 1585 +++++++++++ src/am_map.h | 111 + src/d_client.c | 533 ++++ src/d_deh.c | 3118 +++++++++++++++++++++ src/d_deh.h | 1118 ++++++++ src/d_englsh.h | 707 +++++ src/d_event.h | 127 + src/d_ipxgate.c | 291 ++ src/d_ipxgate.o | Bin 0 -> 9248 bytes src/d_items.c | 140 + src/d_items.h | 59 + src/d_main.c | 1567 +++++++++++ src/d_main.h | 80 + src/d_net.h | 214 ++ src/d_player.h | 234 ++ src/d_server.c | 737 +++++ src/d_think.h | 94 + src/d_ticcmd.h | 59 + src/doomdata.h | 204 ++ src/doomdef.h | 321 +++ src/doomstat.c | 100 + src/doomstat.h | 313 +++ src/doomtype.h | 132 + src/dstrings.c | 85 + src/dstrings.h | 80 + src/f_finale.c | 668 +++++ src/f_finale.h | 56 + src/f_wipe.c | 195 ++ src/f_wipe.h | 45 + src/g_game.c | 2625 ++++++++++++++++++ src/g_game.h | 181 ++ src/hu_lib.c | 701 +++++ src/hu_lib.h | 247 ++ src/hu_stuff.c | 1598 +++++++++++ src/hu_stuff.h | 90 + src/i_main.h | 44 + src/i_network.h | 74 + src/i_sound.h | 120 + src/i_system.h | 74 + src/i_video.h | 64 + src/info.c | 4837 ++++++++++++++++++++++++++++++++ src/info.h | 1490 ++++++++++ src/libretro/libretro.c | 1223 +++++++++ src/libretro/libretro.h | 283 ++ src/libretro/link.T | 5 + src/lprintf.c | 112 + src/lprintf.h | 61 + src/m_argv.c | 63 + src/m_argv.h | 47 + src/m_bbox.c | 58 + src/m_bbox.h | 56 + src/m_cheat.c | 724 +++++ src/m_cheat.h | 58 + src/m_fixed.h | 102 + src/m_menu.c | 5341 ++++++++++++++++++++++++++++++++++++ src/m_menu.h | 184 ++ src/m_misc.c | 916 +++++++ src/m_misc.h | 109 + src/m_random.c | 140 + src/m_random.h | 150 + src/m_swap.h | 134 + src/md5.c | 240 ++ src/md5.h | 47 + src/mmus2mid.c | 735 +++++ src/mmus2mid.h | 76 + src/p_ceilng.c | 467 ++++ src/p_checksum.c | 98 + src/p_checksum.h | 4 + src/p_doors.c | 711 +++++ src/p_enemy.c | 2584 +++++++++++++++++ src/p_enemy.h | 118 + src/p_floor.c | 1045 +++++++ src/p_genlin.c | 1164 ++++++++ src/p_inter.c | 908 ++++++ src/p_inter.h | 75 + src/p_lights.c | 443 +++ src/p_map.c | 2335 ++++++++++++++++ src/p_map.h | 92 + src/p_maputl.c | 683 +++++ src/p_maputl.h | 89 + src/p_mobj.c | 1496 ++++++++++ src/p_mobj.h | 403 +++ src/p_plats.c | 437 +++ src/p_pspr.c | 857 ++++++ src/p_pspr.h | 120 + src/p_saveg.c | 1032 +++++++ src/p_saveg.h | 66 + src/p_setup.c | 1878 +++++++++++++ src/p_setup.h | 57 + src/p_sight.c | 333 +++ src/p_spec.c | 3353 ++++++++++++++++++++++ src/p_spec.h | 1141 ++++++++ src/p_switch.c | 1150 ++++++++ src/p_telept.c | 345 +++ src/p_tick.c | 291 ++ src/p_tick.h | 75 + src/p_user.c | 456 +++ src/p_user.h | 47 + src/prboom.wad | Bin 0 -> 228743 bytes src/protocol.h | 96 + src/r_bsp.c | 565 ++++ src/r_bsp.h | 64 + src/r_data.c | 614 +++++ src/r_data.h | 108 + src/r_defs.h | 427 +++ src/r_demo.c | 88 + src/r_demo.h | 45 + src/r_draw.c | 483 ++++ src/r_draw.h | 150 + src/r_drawcolpipeline.inl | 50 + src/r_drawcolumn.inl | 330 +++ src/r_drawflush.inl | 208 ++ src/r_drawspan.inl | 133 + src/r_filter.c | 119 + src/r_filter.h | 116 + src/r_fps.c | 451 +++ src/r_fps.h | 76 + src/r_main.c | 587 ++++ src/r_main.h | 118 + src/r_patch.c | 759 +++++ src/r_patch.h | 108 + src/r_plane.c | 461 ++++ src/r_plane.h | 67 + src/r_segs.c | 825 ++++++ src/r_segs.h | 44 + src/r_sky.c | 56 + src/r_sky.h | 55 + src/r_state.h | 116 + src/r_things.c | 999 +++++++ src/r_things.h | 72 + src/s_sound.c | 688 +++++ src/s_sound.h | 100 + src/sounds.c | 237 ++ src/sounds.h | 296 ++ src/st_lib.c | 359 +++ src/st_lib.h | 209 ++ src/st_stuff.c | 1098 ++++++++ src/st_stuff.h | 102 + src/tables.c | 128 + src/tables.h | 93 + src/v_video.c | 601 ++++ src/v_video.h | 176 ++ src/version.c | 38 + src/version.h | 40 + src/w_memcache.c | 114 + src/w_mmap.c | 270 ++ src/w_wad.c | 514 ++++ src/w_wad.h | 157 ++ src/wi_stuff.c | 2003 ++++++++++++++ src/wi_stuff.h | 64 + src/z_bmalloc.c | 115 + src/z_bmalloc.h | 52 + src/z_zone.c | 346 +++ src/z_zone.h | 97 + tests/boxes.pl | 61 + tests/crosses.pl | 76 + tests/demo-testing.csv | 24 + tests/lmpwatch.pl | 182 ++ tests/runtests.py | 177 ++ 346 files changed, 82740 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 FAQ create mode 100644 Makefile create mode 100644 Makefile.libretro create mode 100644 Makefile.prboomdata create mode 100644 README create mode 100644 TODO create mode 100644 config.h create mode 100644 data/graphics/boxcc.ppm create mode 100644 data/graphics/boxcl.ppm create mode 100644 data/graphics/boxcr.ppm create mode 100644 data/graphics/boxlc.ppm create mode 100644 data/graphics/boxll.ppm create mode 100644 data/graphics/boxlr.ppm create mode 100644 data/graphics/boxuc.ppm create mode 100644 data/graphics/boxul.ppm create mode 100644 data/graphics/boxur.ppm create mode 100644 data/graphics/dig0.ppm create mode 100644 data/graphics/dig1.ppm create mode 100644 data/graphics/dig2.ppm create mode 100644 data/graphics/dig3.ppm create mode 100644 data/graphics/dig4.ppm create mode 100644 data/graphics/dig45.ppm create mode 100644 data/graphics/dig47.ppm create mode 100644 data/graphics/dig5.ppm create mode 100644 data/graphics/dig58.ppm create mode 100644 data/graphics/dig6.ppm create mode 100644 data/graphics/dig7.ppm create mode 100644 data/graphics/dig8.ppm create mode 100644 data/graphics/dig9.ppm create mode 100644 data/graphics/dig91.ppm create mode 100644 data/graphics/dig93.ppm create mode 100644 data/graphics/diga.ppm create mode 100644 data/graphics/digb.ppm create mode 100644 data/graphics/digc.ppm create mode 100644 data/graphics/digd.ppm create mode 100644 data/graphics/dige.ppm create mode 100644 data/graphics/digf.ppm create mode 100644 data/graphics/digg.ppm create mode 100644 data/graphics/digh.ppm create mode 100644 data/graphics/digi.ppm create mode 100644 data/graphics/digj.ppm create mode 100644 data/graphics/digk.ppm create mode 100644 data/graphics/digl.ppm create mode 100644 data/graphics/digm.ppm create mode 100644 data/graphics/dign.ppm create mode 100644 data/graphics/digo.ppm create mode 100644 data/graphics/digp.ppm create mode 100644 data/graphics/digq.ppm create mode 100644 data/graphics/digr.ppm create mode 100644 data/graphics/digs.ppm create mode 100644 data/graphics/digt.ppm create mode 100644 data/graphics/digu.ppm create mode 100644 data/graphics/digv.ppm create mode 100644 data/graphics/digw.ppm create mode 100644 data/graphics/digx.ppm create mode 100644 data/graphics/digy.ppm create mode 100644 data/graphics/digz.ppm create mode 100644 data/graphics/m_auto.ppm create mode 100644 data/graphics/m_butt1.ppm create mode 100644 data/graphics/m_butt2.ppm create mode 100644 data/graphics/m_chat.ppm create mode 100644 data/graphics/m_colors.ppm create mode 100644 data/graphics/m_compat.ppm create mode 100644 data/graphics/m_enem.ppm create mode 100644 data/graphics/m_generl.ppm create mode 100644 data/graphics/m_horsen.ppm create mode 100644 data/graphics/m_keybnd.ppm create mode 100644 data/graphics/m_mess.ppm create mode 100644 data/graphics/m_palno.ppm create mode 100644 data/graphics/m_palsel.ppm create mode 100644 data/graphics/m_setup.ppm create mode 100644 data/graphics/m_stat.ppm create mode 100644 data/graphics/m_vbox.ppm create mode 100644 data/graphics/m_versen.ppm create mode 100644 data/graphics/m_weap.ppm create mode 100644 data/graphics/prboom.ppm create mode 100644 data/graphics/stbr123.ppm create mode 100644 data/graphics/stbr124.ppm create mode 100644 data/graphics/stbr125.ppm create mode 100644 data/graphics/stbr126.ppm create mode 100644 data/graphics/stbr127.ppm create mode 100644 data/graphics/stcfn096.ppm create mode 100644 data/graphics/stkeys6.ppm create mode 100644 data/graphics/stkeys7.ppm create mode 100644 data/graphics/stkeys8.ppm create mode 100644 data/lumps/animated.lmp create mode 100644 data/lumps/b_end.lmp create mode 100644 data/lumps/b_start.lmp create mode 100644 data/lumps/c_end.lmp create mode 100644 data/lumps/c_start.lmp create mode 100644 data/lumps/crblue.lmp create mode 100644 data/lumps/crblue2.lmp create mode 100644 data/lumps/crbrick.lmp create mode 100644 data/lumps/crbrown.lmp create mode 100644 data/lumps/crgold.lmp create mode 100644 data/lumps/crgray.lmp create mode 100644 data/lumps/crgreen.lmp create mode 100644 data/lumps/crorange.lmp create mode 100644 data/lumps/crred.lmp create mode 100644 data/lumps/crtan.lmp create mode 100644 data/lumps/cryellow.lmp create mode 100644 data/lumps/endboom.lmp create mode 100644 data/lumps/gammatbl.lmp create mode 100644 data/lumps/sinetabl.lmp create mode 100644 data/lumps/switches.lmp create mode 100644 data/lumps/tangtabl.lmp create mode 100644 data/lumps/tantoang.lmp create mode 100644 data/lumps/watermap.lmp create mode 100644 data/palette.rgb create mode 100644 data/prboom.txt create mode 100644 data/rd_graphic.c create mode 100644 data/rd_graphic.h create mode 100644 data/rd_main.c create mode 100644 data/rd_output.c create mode 100644 data/rd_output.h create mode 100644 data/rd_palette.c create mode 100644 data/rd_palette.h create mode 100644 data/rd_sound.c create mode 100644 data/rd_sound.h create mode 100644 data/rd_util.c create mode 100644 data/rd_util.h create mode 100644 data/sprites/dogsa1.ppm create mode 100644 data/sprites/dogsa2a8.ppm create mode 100644 data/sprites/dogsa3a7.ppm create mode 100644 data/sprites/dogsa4a6.ppm create mode 100644 data/sprites/dogsa5.ppm create mode 100644 data/sprites/dogsb1.ppm create mode 100644 data/sprites/dogsb2b8.ppm create mode 100644 data/sprites/dogsb3b7.ppm create mode 100644 data/sprites/dogsb4b6.ppm create mode 100644 data/sprites/dogsb5.ppm create mode 100644 data/sprites/dogsc1.ppm create mode 100644 data/sprites/dogsc2c8.ppm create mode 100644 data/sprites/dogsc3c7.ppm create mode 100644 data/sprites/dogsc4c6.ppm create mode 100644 data/sprites/dogsc5.ppm create mode 100644 data/sprites/dogsd1.ppm create mode 100644 data/sprites/dogsd2d8.ppm create mode 100644 data/sprites/dogsd3d7.ppm create mode 100644 data/sprites/dogsd4d6.ppm create mode 100644 data/sprites/dogsd5.ppm create mode 100644 data/sprites/dogse1.ppm create mode 100644 data/sprites/dogse2.ppm create mode 100644 data/sprites/dogse3.ppm create mode 100644 data/sprites/dogse4.ppm create mode 100644 data/sprites/dogse5.ppm create mode 100644 data/sprites/dogse6.ppm create mode 100644 data/sprites/dogse7.ppm create mode 100644 data/sprites/dogse8.ppm create mode 100644 data/sprites/dogsf1.ppm create mode 100644 data/sprites/dogsf2.ppm create mode 100644 data/sprites/dogsf3.ppm create mode 100644 data/sprites/dogsf4.ppm create mode 100644 data/sprites/dogsf5.ppm create mode 100644 data/sprites/dogsf6.ppm create mode 100644 data/sprites/dogsf7.ppm create mode 100644 data/sprites/dogsf8.ppm create mode 100644 data/sprites/dogsg1.ppm create mode 100644 data/sprites/dogsg2.ppm create mode 100644 data/sprites/dogsg3.ppm create mode 100644 data/sprites/dogsg4.ppm create mode 100644 data/sprites/dogsg5.ppm create mode 100644 data/sprites/dogsg6.ppm create mode 100644 data/sprites/dogsg7.ppm create mode 100644 data/sprites/dogsg8.ppm create mode 100644 data/sprites/dogsh1.ppm create mode 100644 data/sprites/dogsh2.ppm create mode 100644 data/sprites/dogsh3.ppm create mode 100644 data/sprites/dogsh4.ppm create mode 100644 data/sprites/dogsh5.ppm create mode 100644 data/sprites/dogsh6.ppm create mode 100644 data/sprites/dogsh7.ppm create mode 100644 data/sprites/dogsh8.ppm create mode 100644 data/sprites/dogsi0.ppm create mode 100644 data/sprites/dogsj0.ppm create mode 100644 data/sprites/dogsk0.ppm create mode 100644 data/sprites/dogsl0.ppm create mode 100644 data/sprites/dogsm0.ppm create mode 100644 data/sprites/dogsn0.ppm create mode 100644 data/sprites/tnt1a0.ppm create mode 100644 prboom.wad create mode 100644 src/SDL/i_net.c create mode 100644 src/SDL/i_sound.c create mode 100644 src/SDL/i_system.c create mode 100644 src/SDL/i_video.c create mode 100644 src/am_map.c create mode 100644 src/am_map.h create mode 100644 src/d_client.c create mode 100644 src/d_deh.c create mode 100644 src/d_deh.h create mode 100644 src/d_englsh.h create mode 100644 src/d_event.h create mode 100644 src/d_ipxgate.c create mode 100644 src/d_ipxgate.o create mode 100644 src/d_items.c create mode 100644 src/d_items.h create mode 100644 src/d_main.c create mode 100644 src/d_main.h create mode 100644 src/d_net.h create mode 100644 src/d_player.h create mode 100644 src/d_server.c create mode 100644 src/d_think.h create mode 100644 src/d_ticcmd.h create mode 100644 src/doomdata.h create mode 100644 src/doomdef.h create mode 100644 src/doomstat.c create mode 100644 src/doomstat.h create mode 100644 src/doomtype.h create mode 100644 src/dstrings.c create mode 100644 src/dstrings.h create mode 100644 src/f_finale.c create mode 100644 src/f_finale.h create mode 100644 src/f_wipe.c create mode 100644 src/f_wipe.h create mode 100644 src/g_game.c create mode 100644 src/g_game.h create mode 100644 src/hu_lib.c create mode 100644 src/hu_lib.h create mode 100644 src/hu_stuff.c create mode 100644 src/hu_stuff.h create mode 100644 src/i_main.h create mode 100644 src/i_network.h create mode 100644 src/i_sound.h create mode 100644 src/i_system.h create mode 100644 src/i_video.h create mode 100644 src/info.c create mode 100644 src/info.h create mode 100644 src/libretro/libretro.c create mode 100755 src/libretro/libretro.h create mode 100644 src/libretro/link.T create mode 100644 src/lprintf.c create mode 100644 src/lprintf.h create mode 100644 src/m_argv.c create mode 100644 src/m_argv.h create mode 100644 src/m_bbox.c create mode 100644 src/m_bbox.h create mode 100644 src/m_cheat.c create mode 100644 src/m_cheat.h create mode 100644 src/m_fixed.h create mode 100644 src/m_menu.c create mode 100644 src/m_menu.h create mode 100644 src/m_misc.c create mode 100644 src/m_misc.h create mode 100644 src/m_random.c create mode 100644 src/m_random.h create mode 100644 src/m_swap.h create mode 100644 src/md5.c create mode 100644 src/md5.h create mode 100644 src/mmus2mid.c create mode 100644 src/mmus2mid.h create mode 100644 src/p_ceilng.c create mode 100644 src/p_checksum.c create mode 100644 src/p_checksum.h create mode 100644 src/p_doors.c create mode 100644 src/p_enemy.c create mode 100644 src/p_enemy.h create mode 100644 src/p_floor.c create mode 100644 src/p_genlin.c create mode 100644 src/p_inter.c create mode 100644 src/p_inter.h create mode 100644 src/p_lights.c create mode 100644 src/p_map.c create mode 100644 src/p_map.h create mode 100644 src/p_maputl.c create mode 100644 src/p_maputl.h create mode 100644 src/p_mobj.c create mode 100644 src/p_mobj.h create mode 100644 src/p_plats.c create mode 100644 src/p_pspr.c create mode 100644 src/p_pspr.h create mode 100644 src/p_saveg.c create mode 100644 src/p_saveg.h create mode 100644 src/p_setup.c create mode 100644 src/p_setup.h create mode 100644 src/p_sight.c create mode 100644 src/p_spec.c create mode 100644 src/p_spec.h create mode 100644 src/p_switch.c create mode 100644 src/p_telept.c create mode 100644 src/p_tick.c create mode 100644 src/p_tick.h create mode 100644 src/p_user.c create mode 100644 src/p_user.h create mode 100644 src/prboom.wad create mode 100644 src/protocol.h create mode 100644 src/r_bsp.c create mode 100644 src/r_bsp.h create mode 100644 src/r_data.c create mode 100644 src/r_data.h create mode 100644 src/r_defs.h create mode 100644 src/r_demo.c create mode 100644 src/r_demo.h create mode 100644 src/r_draw.c create mode 100644 src/r_draw.h create mode 100644 src/r_drawcolpipeline.inl create mode 100644 src/r_drawcolumn.inl create mode 100644 src/r_drawflush.inl create mode 100644 src/r_drawspan.inl create mode 100644 src/r_filter.c create mode 100644 src/r_filter.h create mode 100644 src/r_fps.c create mode 100644 src/r_fps.h create mode 100644 src/r_main.c create mode 100644 src/r_main.h create mode 100644 src/r_patch.c create mode 100644 src/r_patch.h create mode 100644 src/r_plane.c create mode 100644 src/r_plane.h create mode 100644 src/r_segs.c create mode 100644 src/r_segs.h create mode 100644 src/r_sky.c create mode 100644 src/r_sky.h create mode 100644 src/r_state.h create mode 100644 src/r_things.c create mode 100644 src/r_things.h create mode 100644 src/s_sound.c create mode 100644 src/s_sound.h create mode 100644 src/sounds.c create mode 100644 src/sounds.h create mode 100644 src/st_lib.c create mode 100644 src/st_lib.h create mode 100644 src/st_stuff.c create mode 100644 src/st_stuff.h create mode 100644 src/tables.c create mode 100644 src/tables.h create mode 100644 src/v_video.c create mode 100644 src/v_video.h create mode 100644 src/version.c create mode 100644 src/version.h create mode 100644 src/w_memcache.c create mode 100644 src/w_mmap.c create mode 100644 src/w_wad.c create mode 100644 src/w_wad.h create mode 100644 src/wi_stuff.c create mode 100644 src/wi_stuff.h create mode 100644 src/z_bmalloc.c create mode 100644 src/z_bmalloc.h create mode 100644 src/z_zone.c create mode 100644 src/z_zone.h create mode 100755 tests/boxes.pl create mode 100755 tests/crosses.pl create mode 100644 tests/demo-testing.csv create mode 100755 tests/lmpwatch.pl create mode 100755 tests/runtests.py diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..7839f8ed --- /dev/null +++ b/AUTHORS @@ -0,0 +1,163 @@ +This file is now the amalgamated list of authors, contributors and credits +for PrBoom. Hopefully by keeping these all in one place, they will remain +more accurate. + +Doom was originally written by id software; when playing with any id main +wad file, you can see their list of credits, which includes the list of +programmers. After some years, they released the source code, to allow +others to work on improving the game. + +One of the first projects was DosDoom, by Chi Hoang. This was a quick port +of the released source code, which was for Linux, to DOS. This was then +picked up by TeamTNT (http://www.teamtnt.com/), who produced Boom, a greatly +debugged and extended version of Doom. The Boom programmers were Lee +Killough, Jim Flynn, Rand Phares, Ty Halderman. + +Several projects started working from the Boom source code. One was PrBoom, +made by Florian Schulze, that ported the code to Windows, added suport for +higher resolutions and later OpenGL. Another was Marine's Best Friend +(known as MBF) by Lee Killough, which fixed a lot of Boom bugs and added +many new game features. Finally, there was LxDoom, a port of Boom to Linux +by Colin Phipps. + +In October 1999, id Software re-released the Doom source code under the +GNU General Public License. TeamTNT have also changed to the new license, +and the other sources mentioned above have all allowed their code to be +GPLed. So PrBoom is covered by the GPL. + +In May 2000, LxDoom, PrBoom, and a derived port called LSDLDoom, merged into +one. The current authors of PrBoom are: + +Florian Schulze +Colin Phipps +Neil Stevens - Mac OS X porting +Andrey Budko +Rob Young (RjY) + +Our thanks go to all the authors of the ports mentioned above, and also the +following people who contributed code to LxDoom or PrBoom: + +Jess Haas +Of LSDLdoom, who merged his project into PrBoom, contributing his SDL code. + +Nicolas Kalkhof +Much work on the OpenGL renderer. + +James "Quasar" Haley +Ever willing to talk about source ideas, and has pointed me in the direction of +a lot of Boom and MBF bugs; also various bits code from his port Eternity have +been used, such as the BEX enhancements. + +Bob Aman (sporkmonger.com) +Created the RMUDAnsiTextView class used in the Mac launcher. + +Gady Kozma gady@math.tau.ac.il +Added hires to the SVGALib version of LxDoom, and other useful patches. + +Dick Leban +Lots of feedback about portability issues and helping get the network code +working properly back at v1.3.6. + +Eduardo Casino Almao +Lots of helpful feedback and suggestions, but more importantly actually getting +to grips with the code and giving very precise bug reports and patches. + +Joey Hess +For numerous patches, like the glibc fixes and window manager updates, and +help with the music. + +Ben Winslow +Various useful patches, like the colour ENDOOM code. + +Josh Parsons josh@schlick.anu.edu.au +Sent me the patches to use autoconf for configuring LxDoom. + +Steve Van Devender +Found the bug causing slight noise at the start of sounds playing, and other +patches. + +Barry Mead +Improvements to the mouse code and other odd patches. + +Mattias Kunkel +Made the lxdoom.spec file for creating LxDoom RPMs. + +Vicente Aguilar vicente@hal.dhis.org +Handy patch for the file handling + +Benjamin L McGee +Patch fixing the joystick code. + +Chris Young +Patch improving the ENDOOM printing + +Peter Jay Salzman +Cleanup patches + +Oliver Kraus +Send bug reports and patches for Solaris/Sparc. + +Paul S Jenner +Nice patch to make RPM building easier + +Julian +Fixed inline asm for gcc-2.95 (from Eternity) + +Lionel Ulmer +Patch to fix alignment problems on ARM processors. + +Ville Vuorinen +Spotted and helped patch the player spawn bug, as well as helping with some +Win32 issues. + +Steven Elliot +Misc patches. + +Andreas Dehmel +Spotted & patched a savegame bug. + +Jon Dowland +Bug reports & fixes, documentation improvements. + +If you have sent in patches and I forgot to list you, I apologise. Please email +me and I will add you. + +Also, thanks to the following people who have helped in various ways: + +Simon "fraggle" Howard +More MBF bugs. + +Robert Phipps +Network game testing, feature suggestions etc. + +Udo Monk +His port xdoom is very portable, and I referred to his code sometimes for help +with the X stuff; also his collection of Doom tools (XWadTools) is the +definitive tools collection for Linux. + +Andre Majorel +For Yadex, so I can debug those problematic levels more easily. + +Michael Heasley +Author of musserver, which helped me first add music support. + +Rafael Reilova +Helped with the music server program for LxDoom + +Frederic Oghdayan +For useful feedback on LxDoom v1.0.1, and repeating his bug reports until I +believed them :-). + +Adam Hegyi +Prompted me to hunt down those last few demo sync bugs, and provided some useful +insights and example demos to help. + +Adam Williamson +Pointing me toward yet another compatibility bug. + +Ingo van Lil +Another bug spotter. + +Everyone who contributed indirectly to MBF and Boom and Doom; see the +respective documentation files. + diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..f698bce6 --- /dev/null +++ b/COPYING @@ -0,0 +1,367 @@ +GNU GENERAL PUBLIC LICENSE +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble +The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public License +is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit +to using it. (Some other Free Software Foundation software is covered +by the GNU Library General Public License instead.) You can apply it +to your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge +for this service if you wish), that you receive source code or can +get it if you want it, that you can change the software or use pieces +of it in new free programs; and that you know you can do these +things. + +To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + +We protect your rights with two steps: (1) copyright the software, +and (2) offer you this license which gives you legal permission to +copy, distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, +we want its recipients to know that what they have is not the +original, so that any problems introduced by others will not reflect +on the original authors' reputations. + +Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making +the program proprietary. To prevent this, we have made it clear that +any patent must be licensed for everyone's free use or not licensed +at all. + +The precise terms and conditions for copying, distribution and +modification follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License applies to any program or other work which contains a +notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the +Program is covered only if its contents constitute a work based on +the Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any +warranty; and give any other recipients of the Program a copy of this +License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a +fee. + +2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + +a) You must cause the modified files to carry prominent notices +stating that you changed the files and the date of any change. + +b) You must cause any work that you distribute or publish, that in +whole or in part contains or is derived from the Program or any part +thereof, to be licensed as a whole at no charge to all third parties +under the terms of this License. + +c) If the modified program normally reads commands interactively when +run, you must cause it, when started running for such interactive use +in the most ordinary way, to print or display an announcement +including an appropriate copyright notice and a notice that there is +no warranty (or else, saying that you provide a warranty) and that +users may redistribute the program under these conditions, and +telling the user how to view a copy of this License. (Exception: if +the Program itself is interactive but does not normally print such an +announcement, your work based on the Program is not required to print +an announcement.) +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the +Program with the Program (or with a work based on the Program) on a +volume of a storage or distribution medium does not bring the other +work under the scope of this License. + +3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the +following: + +a) Accompany it with the complete corresponding machine-readable +source code, which must be distributed under the terms of Sections 1 +and 2 above on a medium customarily used for software interchange; +or, + +b) Accompany it with a written offer, valid for at least three years, +to give any third party, for a charge no more than your cost of +physically performing source distribution, a complete +machine-readable copy of the corresponding source code, to be +distributed under the terms of Sections 1 and 2 above on a medium +customarily used for software interchange; or, + +c) Accompany it with the information you received as to the offer to +distribute corresponding source code. (This alternative is allowed +only for noncommercial distribution and only if you received the +program in object code or executable form with such an offer, in +accord with Subsection b above.) +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this +License. However, parties who have received copies, or rights, from +you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject +to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted +herein. You are not responsible for enforcing compliance by third +parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do +not excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not distribute the Program at all. For example, +if a patent license would not permit royalty-free redistribution of +the Program by all those who receive copies directly or indirectly +through you, then the only way you could satisfy both it and this +License would be to refrain entirely from distribution of the +Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended +to apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is +willing to distribute software through any other system and a +licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + +9. The Free Software Foundation may publish revised and/or new +versions of the General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published +by the Free Software Foundation. If the Program does not specify a +version number of this License, you may choose any version ever +published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the +author to ask for permission. For software which is copyrighted by +the Free Software Foundation, write to the Free Software Foundation; +we sometimes make exceptions for this. Our decision will be guided by +the two goals of preserving the free status of all derivatives of our +free software and of promoting the sharing and reuse of software +generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + +END OF TERMS AND CONDITIONS +How to Apply These Terms to Your New Programs +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make +it free software which everyone can redistribute and change under +these terms. + +To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + +one line to give the program's name and an idea of what it does. +Copyright (C) yyyy name of author + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +02111-1307, USA. + +Also add information on how to contact you by electronic and paper +mail. + +If the program is interactive, make it output a short notice like +this when it starts in an interactive mode: + +Gnomovision version 69, Copyright (C) yyyy name of author +Gnomovision comes with ABSOLUTELY NO WARRANTY; for details +type `show w'. This is free software, and you are welcome +to redistribute it under certain conditions; type `show c' +for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and +`show c'; they could even be mouse-clicks or menu items--whatever +suits your program. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the +program, if necessary. Here is a sample; alter the names: + +Yoyodyne, Inc., hereby disclaims all copyright +interest in the program `Gnomovision' +(which makes passes at compilers) written +by James Hacker. + +signature of Ty Coon, 1 April 1989 +Ty Coon, President of Vice + +This General Public License does not permit incorporating your +program into proprietary programs. If your program is a subroutine +library, you may consider it more useful to permit linking +proprietary applications with the library. If this is what you want +to do, use the GNU Library General Public License instead of this +License. + + +---------------------------------------------------------------------- +---------- +Return to GNU's home page. +FSF & GNU inquiries & questions to gnu@gnu.org. Other ways to contact +the FSF. + +Comments on these web pages to webmasters@www.gnu.org, send other +questions to gnu@gnu.org. + +Copyright notice above. +Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, +MA 02111, USA + +Updated: 16 Feb 1998 tower + diff --git a/FAQ b/FAQ new file mode 100644 index 00000000..99656aed --- /dev/null +++ b/FAQ @@ -0,0 +1,26 @@ +Compatibility/Demos: + +Q: I'm trying to play a demo, but it gets out of sync, what's wrong? +A: Read README.compatibility if you don't have already. +A: If everything failes report us which demo got out of sync and where we can +get it. Don't send it to us directly. Now hope the best that it gets fixed. + +Linux: + +Q: I get wrong colors when using SVGALib, can't you do your job right? +A: The SDL Library is broken, bug the SDL people about it. + +OpenGL: + +Q: I'm trying to get PrBoom with OpenGL working on my 3dfx Voodoo card on +Windows, but it doesn't work or is very slow. +A: Use Mesa3D (http://www.mesa3d.org) with 3dfx support. + +Q: The sky looks wrong in OpenGL, WTF? +A: It seems like you are using Mesa3D. Try a newer version and if it's still not +fixed bug them about it. +A: Are you using Windows? Try using another glu32.dll. + +Q: I get a compile error saying PFNGLCOLORTABLEEXTPROC isn't defined +A: Get a newer version of your OpenGL headers/drivers. This problem occured on +nvidia cards. If you still can't get it working, the please tell us. diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..d7ed2813 --- /dev/null +++ b/Makefile @@ -0,0 +1,90 @@ +DEBUG=0 + +ifeq ($(platform),) +platform = unix +ifeq ($(shell uname -a),) + platform = win +else ifneq ($(findstring MINGW,$(shell uname -a)),) + platform = win +else ifneq ($(findstring Darwin,$(shell uname -a)),) + platform = osx +else ifneq ($(findstring win,$(shell uname -a)),) + platform = win +endif +endif + +CC = gcc +DOOMSRC = src +PORTSRCDIR = $(DOOMSRC)/SDL + +ifeq ($(platform), unix) + TARGET := prboom +else ifeq ($(platform), osx) + TARGET := prboom +else ifeq ($(platform), ps3) + TARGET := libretro.a + CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe + AR = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ar.exe + CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__ +else ifeq ($(platform), sncps3) + TARGET := libretro.a + CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe + AR = $(CELL_SDK)/host-win32/sn/bin/ps3snarl.exe + CFLAGS += -DBLARGG_BIG_ENDIAN=1 -D__ppc__ +else ifeq ($(platform), xenon) + TARGET := libretro.a + CC = xenon-gcc + AR = xenon-ar + CFLAGS += -D__LIBXENON__ -m32 -D__ppc__ +else ifeq ($(platform), wii) + TARGET := libretro.a + CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc + AR = $(DEVKITPPC)/bin/powerpc-eabi-ar + CFLAGS += -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float -DBLARGG_BIG_ENDIAN=1 -D__ppc__ +else + TARGET := prboom.exe + CC = gcc + SHARED := -static-libgcc -static-libstdc++ + CFLAGS += -D__WIN32__ -D__WIN32_LIBRETRO__ +endif + +ifeq ($(DEBUG), 1) +CFLAGS += -O0 -g +else +CFLAGS += -O3 +endif + +PORTOBJECTS = ./$(PORTSRCDIR)/i_system.o ./$(PORTSRCDIR)/i_video.o ./$(PORTSRCDIR)/i_sound.o + + +OBJECTS = ./$(DOOMSRC)/am_map.o ./$(DOOMSRC)/d_deh.o ./$(DOOMSRC)/d_items.o ./$(DOOMSRC)/d_main.o ./$(DOOMSRC)/doomstat.o ./$(DOOMSRC)/dstrings.o ./$(DOOMSRC)/f_finale.o ./$(DOOMSRC)/f_wipe.o ./$(DOOMSRC)/g_game.o ./$(DOOMSRC)/hu_lib.o ./$(DOOMSRC)/hu_stuff.o ./$(DOOMSRC)/info.o ./$(DOOMSRC)/m_argv.o ./$(DOOMSRC)/m_bbox.o ./$(DOOMSRC)/m_cheat.o ./$(DOOMSRC)/m_menu.o ./$(DOOMSRC)/m_misc.o ./$(DOOMSRC)/m_random.o ./$(DOOMSRC)/p_ceilng.o ./$(DOOMSRC)/p_doors.o ./$(DOOMSRC)/p_enemy.o ./$(DOOMSRC)/p_floor.o ./$(DOOMSRC)/p_inter.o ./$(DOOMSRC)/p_lights.o ./$(DOOMSRC)/p_map.o ./$(DOOMSRC)/p_maputl.o ./$(DOOMSRC)/p_mobj.o ./$(DOOMSRC)/p_plats.o ./$(DOOMSRC)/p_pspr.o ./$(DOOMSRC)/p_saveg.o ./$(DOOMSRC)/p_setup.o ./$(DOOMSRC)/p_sight.o ./$(DOOMSRC)/p_spec.o ./$(DOOMSRC)/p_switch.o ./$(DOOMSRC)/p_telept.o ./$(DOOMSRC)/p_tick.o ./$(DOOMSRC)/p_user.o ./$(DOOMSRC)/r_bsp.o ./$(DOOMSRC)/r_data.o ./$(DOOMSRC)/r_draw.o ./$(DOOMSRC)/r_main.o ./$(DOOMSRC)/r_plane.o ./$(DOOMSRC)/r_segs.o ./$(DOOMSRC)/r_sky.o ./$(DOOMSRC)/r_things.o ./$(DOOMSRC)/r_patch.o ./$(DOOMSRC)/s_sound.o ./$(DOOMSRC)/sounds.o ./$(DOOMSRC)/st_lib.o ./$(DOOMSRC)/st_stuff.o ./$(DOOMSRC)/tables.o ./$(DOOMSRC)/v_video.o ./$(DOOMSRC)/w_wad.o ./$(DOOMSRC)/z_zone.o ./$(DOOMSRC)/w_memcache.o ./$(DOOMSRC)/r_fps.o ./$(DOOMSRC)/r_filter.o ./$(DOOMSRC)/p_genlin.o ./$(DOOMSRC)/r_demo.o ./$(DOOMSRC)/z_bmalloc.o ./$(DOOMSRC)/lprintf.o ./$(DOOMSRC)/wi_stuff.o ./$(DOOMSRC)/p_checksum.o ./$(DOOMSRC)/md5.o ./$(DOOMSRC)/version.o ./$(DOOMSRC)/d_client.o ./$(DOOMSRC)/mmus2mid.o $(PORTOBJECTS) + +INCLUDES = -I. -I.. -I$(DOOMSRC) -ISDL +DEFINES = -DHAVE_INTTYPES_H -DINLINE=inline -DHAVE_SDL + +ifeq ($(platform), sncps3) +WARNINGS_DEFINES = +CODE_DEFINES = +else +WARNINGS_DEFINES = -Wall -W -Wno-unused-parameter +CODE_DEFINES = -fomit-frame-pointer -std=gnu99 +endif + +COMMON_DEFINES += $(CODE_DEFINES) $(WARNINGS_DEFINES) -DNDEBUG=1 $(fpic) +SDL_INCPATH := $(shell pkg-config sdl --cflags) + +CFLAGS += $(DEFINES) $(COMMON_DEFINES) $(SDL_INCPATH) + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(CC) $(INCLUDES) $(CFLAGS) $(SDL_INCPATH) -o $@ $(OBJECTS) -lm -lSDL -lSDL_net -lSDL_mixer + +%.o: %.c + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJECTS) $(TARGET) + +.PHONY: clean + diff --git a/Makefile.libretro b/Makefile.libretro new file mode 100644 index 00000000..82d6b338 --- /dev/null +++ b/Makefile.libretro @@ -0,0 +1,105 @@ +DEBUG=0 + +ifeq ($(platform),) +platform = unix +ifeq ($(shell uname -a),) + platform = win +else ifneq ($(findstring MINGW,$(shell uname -a)),) + platform = win +else ifneq ($(findstring Darwin,$(shell uname -a)),) + platform = osx +else ifneq ($(findstring win,$(shell uname -a)),) + platform = win +endif +endif + +CC = gcc +DOOMSRC = src +PORTSRCDIR = src/libretro + +ifeq ($(platform), unix) + TARGET := libretro.so + fpic := -fPIC + SHARED := -shared -Wl,--version-script=src/libretro/link.T -Wl,-no-undefined + CFLAGS += -D_GNU_SOURCE=1 +else ifeq ($(platform), osx) + TARGET := libretro.dylib + fpic := -fPIC + SHARED := -dynamiclib +else ifeq ($(platform), ps3) + TARGET := libretro.a + CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe + AR = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ar.exe + CFLAGS += -DWORDS_BIGENDIAN=1 -D_GNU_SOURCE=1 +else ifeq ($(platform), sncps3) + TARGET := libretro.a + CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe + AR = $(CELL_SDK)/host-win32/sn/bin/ps3snarl.exe + CFLAGS += -DWORDS_BIGENDIAN=1 -D_GNU_SOURCE=1 +else ifeq ($(platform), xenon) + TARGET := libretro.a + CC = xenon-gcc + AR = xenon-ar + CFLAGS += -D__LIBXENON__ -m32 -D__ppc__ -DWORDS_BIGENDIAN=1 -D_GNU_SOURCE=1 +else ifeq ($(platform), wii) + TARGET := libretro.a + CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc + AR = $(DEVKITPPC)/bin/powerpc-eabi-ar + CFLAGS += -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float -DWORDS_BIGENDIAN=1 -D_GNU_SOURCE=1 +else + TARGET := libretro.dll + CC = gcc + fpic := -fPIC + LD_FLAGS := -fPIC + SHARED := -shared -static-libgcc -static-libstdc++ -s -Wl,--version-script=src/libretro/link.T + CFLAGS += -D__WIN32__ -D__WIN32_LIBRETRO__ +endif + +ifeq ($(DEBUG), 1) +CFLAGS += -O0 -g +else +CFLAGS += -O3 +endif + +PORTOBJECTS = ./$(PORTSRCDIR)/libretro.o + +OBJECTS = ./$(DOOMSRC)/am_map.o ./$(DOOMSRC)/d_deh.o ./$(DOOMSRC)/d_items.o ./$(DOOMSRC)/d_main.o ./$(DOOMSRC)/doomstat.o ./$(DOOMSRC)/dstrings.o ./$(DOOMSRC)/f_finale.o ./$(DOOMSRC)/f_wipe.o ./$(DOOMSRC)/g_game.o ./$(DOOMSRC)/hu_lib.o ./$(DOOMSRC)/hu_stuff.o ./$(DOOMSRC)/info.o ./$(DOOMSRC)/m_argv.o ./$(DOOMSRC)/m_bbox.o ./$(DOOMSRC)/m_cheat.o ./$(DOOMSRC)/m_menu.o ./$(DOOMSRC)/m_misc.o ./$(DOOMSRC)/m_random.o ./$(DOOMSRC)/p_ceilng.o ./$(DOOMSRC)/p_doors.o ./$(DOOMSRC)/p_enemy.o ./$(DOOMSRC)/p_floor.o ./$(DOOMSRC)/p_inter.o ./$(DOOMSRC)/p_lights.o ./$(DOOMSRC)/p_map.o ./$(DOOMSRC)/p_maputl.o ./$(DOOMSRC)/p_mobj.o ./$(DOOMSRC)/p_plats.o ./$(DOOMSRC)/p_pspr.o ./$(DOOMSRC)/p_saveg.o ./$(DOOMSRC)/p_setup.o ./$(DOOMSRC)/p_sight.o ./$(DOOMSRC)/p_spec.o ./$(DOOMSRC)/p_switch.o ./$(DOOMSRC)/p_telept.o ./$(DOOMSRC)/p_tick.o ./$(DOOMSRC)/p_user.o ./$(DOOMSRC)/r_bsp.o ./$(DOOMSRC)/r_data.o ./$(DOOMSRC)/r_draw.o ./$(DOOMSRC)/r_main.o ./$(DOOMSRC)/r_plane.o ./$(DOOMSRC)/r_segs.o ./$(DOOMSRC)/r_sky.o ./$(DOOMSRC)/r_things.o ./$(DOOMSRC)/r_patch.o ./$(DOOMSRC)/s_sound.o ./$(DOOMSRC)/sounds.o ./$(DOOMSRC)/st_lib.o ./$(DOOMSRC)/st_stuff.o ./$(DOOMSRC)/tables.o ./$(DOOMSRC)/v_video.o ./$(DOOMSRC)/w_wad.o ./$(DOOMSRC)/z_zone.o ./$(DOOMSRC)/w_memcache.o ./$(DOOMSRC)/r_fps.o ./$(DOOMSRC)/r_filter.o ./$(DOOMSRC)/p_genlin.o ./$(DOOMSRC)/r_demo.o ./$(DOOMSRC)/z_bmalloc.o ./$(DOOMSRC)/lprintf.o ./$(DOOMSRC)/wi_stuff.o ./$(DOOMSRC)/p_checksum.o ./$(DOOMSRC)/md5.o ./$(DOOMSRC)/version.o ./$(DOOMSRC)/d_client.o ./$(DOOMSRC)/mmus2mid.o $(PORTOBJECTS) + +INCLUDES = -I. -I.. -Isrc +DEFINES = -DHAVE_INTTYPES_H -D__LIBRETRO__ -DINLINE=inline + +ifeq ($(platform), sncps3) +WARNINGS_DEFINES = +CODE_DEFINES = +else +WARNINGS_DEFINES = -Wall -W -Wno-unused-parameter +CODE_DEFINES = -fomit-frame-pointer -std=gnu99 +endif + +COMMON_DEFINES += $(CODE_DEFINES) $(WARNINGS_DEFINES) -DNDEBUG=1 $(fpic) + +CFLAGS += $(DEFINES) $(COMMON_DEFINES) + +all: $(TARGET) + +$(TARGET): $(OBJECTS) +ifeq ($(platform), ps3) + $(AR) rcs $@ $(OBJECTS) +else ifeq ($(platform), sncps3) + $(AR) rcs $@ $(OBJECTS) +else ifeq ($(platform), xenon) + $(AR) rcs $@ $(OBJECTS) +else ifeq ($(platform), wii) + $(AR) rcs $@ $(OBJECTS) +else + $(CC) $(fpic) $(SHARED) $(INCLUDES) -o $@ $(OBJECTS) -lm +endif + +%.o: %.c + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJECTS) $(TARGET) + +.PHONY: clean + diff --git a/Makefile.prboomdata b/Makefile.prboomdata new file mode 100644 index 00000000..7112f5e5 --- /dev/null +++ b/Makefile.prboomdata @@ -0,0 +1,103 @@ +DEBUG=0 + +ifeq ($(platform),) +platform = unix +ifeq ($(shell uname -a),) + platform = win +else ifneq ($(findstring MINGW,$(shell uname -a)),) + platform = win +else ifneq ($(findstring Darwin,$(shell uname -a)),) + platform = osx +else ifneq ($(findstring win,$(shell uname -a)),) + platform = win +endif +endif + +CC = gcc + +ifeq ($(platform), unix) + TARGET := rdatawad +else ifeq ($(platform), osx) + TARGET := rdatawad +else + TARGET := rdatawad.exe + CC = gcc + SHARED := -static-libgcc -static-libstdc++ + CFLAGS += -D__WIN32__ -D__WIN32_LIBRETRO__ +endif + +ifeq ($(DEBUG), 1) +CFLAGS += -O0 -g +else +CFLAGS += -O3 +endif + +OBJECTS = data/rd_graphic.o data/rd_output.o data/rd_palette.o data/rd_sound.o data/rd_util.o data/rd_main.o + +INCLUDES = -I. -I.. -Idata +DEFINES = -DHAVE_INTTYPES_H -D__LIBRETRO__ -DINLINE=inline + +WARNINGS_DEFINES = -Wall -W -Wno-unused-parameter +CODE_DEFINES = -fomit-frame-pointer -std=gnu99 + +COMMON_DEFINES += $(CODE_DEFINES) $(WARNINGS_DEFINES) -DNDEBUG=1 $(fpic) + +CFLAGS += $(DEFINES) $(COMMON_DEFINES) + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(CC) $(INCLUDES) $(CFLAGS) -o $@ $(OBJECTS) -lm + +%.o: %.c + $(CC) $(INCLUDES) $(CFLAGS) -c -o $@ $< + +clean: + rm -f $(OBJECTS) $(TARGET) + +PALETTE = data/palette.rgb +LUMPS = data/lumps/switches.lmp data/lumps/animated.lmp data/lumps/crbrick.lmp \ + data/lumps/crtan.lmp data/lumps/crgray.lmp data/lumps/crgreen.lmp \ + data/lumps/crbrown.lmp data/lumps/crgold.lmp data/lumps/crred.lmp \ + data/lumps/crblue.lmp data/lumps/crblue2.lmp data/lumps/crorange.lmp \ + data/lumps/cryellow.lmp +COLORMAPS = data/lumps/watermap.lmp +TABLES = data/lumps/sinetabl.lmp data/lumps/tangtabl.lmp data/lumps/tantoang.lmp \ + data/lumps/gammatbl.lmp +GRAPHICS = data/graphics/dig0.ppm data/graphics/dig1.ppm data/graphics/dig2.ppm \ + data/graphics/dig3.ppm data/graphics/dig4.ppm data/graphics/dig5.ppm \ + data/graphics/dig6.ppm data/graphics/dig7.ppm data/graphics/dig8.ppm \ + data/graphics/dig9.ppm data/graphics/diga.ppm data/graphics/digb.ppm \ + data/graphics/digc.ppm data/graphics/digd.ppm data/graphics/dige.ppm \ + data/graphics/digf.ppm data/graphics/digg.ppm data/graphics/digh.ppm \ + data/graphics/digi.ppm data/graphics/digj.ppm data/graphics/digk.ppm \ + data/graphics/digl.ppm data/graphics/digm.ppm data/graphics/dign.ppm \ + data/graphics/digo.ppm data/graphics/digp.ppm data/graphics/digq.ppm \ + data/graphics/digr.ppm data/graphics/digs.ppm data/graphics/digt.ppm \ + data/graphics/digu.ppm data/graphics/digv.ppm data/graphics/digw.ppm \ + data/graphics/digx.ppm data/graphics/digy.ppm data/graphics/digz.ppm \ + data/graphics/dig45.ppm data/graphics/dig47.ppm data/graphics/dig58.ppm \ + data/graphics/dig91.ppm data/graphics/dig93.ppm data/graphics/stbr123.ppm \ + data/graphics/stbr124.ppm data/graphics/stbr125.ppm data/graphics/stbr126.ppm \ + data/graphics/stbr127.ppm data/graphics/boxul.ppm data/graphics/boxuc.ppm \ + data/graphics/boxur.ppm data/graphics/boxcl.ppm data/graphics/boxcc.ppm \ + data/graphics/boxcr.ppm data/graphics/boxll.ppm data/graphics/boxlc.ppm \ + data/graphics/boxlr.ppm data/graphics/stkeys6.ppm data/graphics/stkeys7.ppm \ + data/graphics/stkeys8.ppm data/graphics/stcfn096.ppm \ + data/graphics/m_butt1.ppm data/graphics/m_butt2.ppm \ + data/graphics/m_colors.ppm data/graphics/m_palno.ppm \ + data/graphics/m_palsel.ppm data/graphics/m_vbox.ppm \ + data/graphics/m_generl.ppm data/graphics/m_setup.ppm \ + data/graphics/m_auto.ppm data/graphics/m_keybnd.ppm \ + data/graphics/m_compat.ppm data/graphics/m_enem.ppm \ + data/graphics/m_mess.ppm data/graphics/m_stat.ppm \ + data/graphics/m_weap.ppm data/graphics/m_horsen.ppm \ + data/graphics/m_versen.ppm data/graphics/prboom.ppm +SPRITES = data/sprites/tnt1a0.ppm +SPRITEP = 0,0,data/sprites/tnt1a0.ppm +WAD_CMDLINE = -palette $(PALETTE) -lumps $(LUMPS) -marker C_START -lumps $(COLORMAPS) -marker C_END -marker B_START -lumps $(TABLES) -marker B_END -graphics $(GRAPHICS) -marker SS_START -sprites $(SPRITEP) -marker SS_END + +make-prboomwad: + ./rdatawad -I data $(WAD_CMDLINE) -o prboom.wad + +.PHONY: clean make-prboomwad diff --git a/README b/README new file mode 100644 index 00000000..d1ac70df --- /dev/null +++ b/README @@ -0,0 +1,252 @@ +PrBoom 2.5.0 +============ + +PrBoom is a version of the classic 3D shoot'em'up game Doom, originally +written by id Software. + +See the file AUTHORS in this distribution for a list of authors and +other contributors, and a history of the projects PrBoom is derived +from. + +PrBoom is made available under the GNU General Public License. See the +file COPYING included in this distribution for details. + +Please see the NEWS file included for changes since the previous version. + +Game data - WADs +---------------- + +(This section is aimed at people not familiar with Doom and the +data files it uses.) + +PrBoom is a game engine - it provides a program to play Doom levels, but +it doesn't include any levels itself. More importantly, you need all the +sounds, sprites, and other graphics that make up the Doom environment. +So to play PrBoom, you need one of the main Doom date files from id +Software - either doom.wad, doom2.wad, tnt.wad or plutonia.wad from one +of the commercial Doom games, or the shareware doom1.wad. This file +is called the IWAD. + +PrBoom also supports playing Doom add-on levels, called "PWADs", which +are small extra .wad files which just contain extra levels or other +resources. PWADs are ONLY ADD-ONS, you still need the original IWAD +that they are designed to work with. In practice, most PWADs on the +Internet require doom2.wad (although some work with doom.wad). + +If you don't own any of the Doom games, get the shareware doom1.wad +from doom19s.zip on Doomworld's shareware download page. But note that you +will not be able to play most add-ons. +http://www.doomworld.com/files/shareware.shtml + +Windows Installation +-------------------- + +Just extract the zip to a directory of your choice and copy your IWAD +files into it. Now you can make shortcuts and add "-iwad filename.wad" +to them. + +The SDL_mixer library used by PrBoom supports software MIDI music +synthesis. If you want to hear the Doom music, you need a set of +Timidity instrument patches. Do a web search for timidity patch sets, +there are plenty around. +These patch sets are a large download (>5megs). +SDL_mixer does not currently support hardware MIDI synthesis. But we +have added a hacked version of SDL_mixer with native midi support. If +you like to try it out, rename SDL_mixer_beta.dll to SDL_mixer.dll. +You should rename the original SDL_mixer.dll before. Tell us if it +works or not. Please note, that there might be bugs in the native midi +implementation. + +Linux Installation +------------------ + +For UNIX, Linux, and other POSIX systems, you need the SDL libraries in +order to use PrBoom. If you haven't already done so, visit +http://prboom.sourceforge.net/linux.html and follow the instructions there +for your system, downloading the necessary libraries, and either +installing the binary RPM package or compiling PrBoom from source. + +Once you've done that, you'll need to copy your IWAD file (see the section +above if you don't know what this is) to a directory where PrBoom can find +it. Make /usr/local/share/games/doom/, and copy your IWAD (all of your +IWADs, if you own more than one) to that directory. + +Mac OS Installation +------------------- + +Copy your IWAD (see above) into your home folder under +Library:Application Support:PrBoom (this folder will be created for you the +first time you run PrBoom). + +First Use +--------- + +If it's the first time you've run PrBoom, you'll need to do some configuring +to get the controls and display right for you. + +On a new installation, PrBoom runs at 640x480 resolution. If you have used +PrBoom before, you may have an old config file in your home directory which +specifies a lower resolution, such as Doom's normal 320x200. You can use the +-width and -height parameters to select a higher resolution, e.g.: + +prboom -width 640 -height 400 + +sets the resolution. This setting is remembered for future sessions. For +other parameters, see the included README.command-line. + +You may also wish to customise the key bindings. PrBoom's default keybindings +are the same as the original Doom; unlike original Doom, you can change key +bindings in the game. In the in-game menus, go to Options, Settings, Key +Bindings. + +On Mac OS X, you can't use the command line, but after running the program +once, you can edit YOURHOME:Library:Application Support:PrBoom:prboom.cfg to +change settings like your screen resolution. + +Features +-------- + + This is all the features PrBoom has compared to the original Doom game + - it's intended to give you an idea of the enhancements, rather than + burying you in details. + + See http://prboom.sourceforge.net/about.html for an HTML version of + this list. + + This is shamelessly modelled on Boom's changes pages. By each + change, there's the name of the port the feature comes from (so it's + compatible with). + +Playing the game + + * Supports loading dehacked files at the command line, or in WADs + (BOOM, MBF) + * Supports PWADs containing sprites and flats (BOOM) + * Save games and demos completely store game parameters (BOOM, + MBF) + * Savegames store list of loaded WAD files, warning if wrong files + loaded (BOOM, MBF) + +Game engine + + * Player bobbing improved, optional (BOOM, MBF) + * Friction effects (BOOM), affecting players and monsters + (MBF) + * Wind, current, conveyor effects (BOOM) + * Far more flexible scrolling wall/floor types (BOOM) + * Always run (BOOM) + * Weapon change logic overhauled and improved (BOOM) + * Support for friendly monsters, helper dogs (MBF) + * Monster target finding code improved (MBF) + * AI improvements (MBF) + * Bouncy and touchy things (MBF) + * New code pointers (MBF) + * Per-level and animated skies (MBF) + * Generalised line types system gives complete flexibility + (BOOM) + * Elevators (BOOM) + * Translucent sprites, walls (BOOM) + * Independent floor and ceiling lighting (BOOM) + * Silent teleports (BOOM) + * Deep water, true underwater areas (BOOM) + * Icon of Sin telefragging made more consistent (MBF) + * Fix large numbers of game bugs (BOOM, MBF, LxDoom) + * Support arbitrary texture heights (BOOM) + +Screen + + * High resolution support (PrBoom) + * Optional message console, multiple message lines (BOOM) + * Status bar shows health/armour/ammo in colours (BOOM) + * Heads up display, showing ammo, health, keys overlayed on view + (BOOM) + +Multiplayer + + * Spy mode improved (BOOM) + * Support for loadgame in a net game (LxDoom) + * Client server style network games (LxDoom) + +Automap + + * No limit on marks (BOOM) + * Rotation and overlay modes (DOSDoom, LxDoom) + * Map shows coordinates (BOOM), optionally follow pointer + (MBF) + * Teleport lines, key doors and switches marked specially (BOOM) + * Keys, secrets visible on map with cheat codes (BOOM) + * Colours fully configurable from menus (BOOM) + +Intermission screens + + * Par times hidden when not relevant (BOOM) + * Total episode time shown (LxDoom) + +Menus + + * F1 help screen shows current key setup (BOOM) + * Key bindings, monster behaviour, and compatibility settings all set + in menus (BOOM, MBF) + +Compatibility + + * Game is capable of behaving like any of: original Doom v1.9, Boom + v2.02, MBF (BOOM, MBF, LxDoom) + * Plays most original Doom v1.9 demos (more than Boom or MBF) + * Plays most Boom v2.02 demos (apart from levels with friction + effects everything should work). + * Plays some DOSDoom, earlier Doom, earlier Boom, and LxDoom demos. + * Plays all MBF demos. + * Auto-correction of common bugs in old levels (MBF), with + warnings (LxDoom) + * Fine control of options controlling compatibility and new features + (MBF) + +Controls + + * Greater control of key bindings from in game menus (BOOM) + * More accurate mouse sensitivity control (BOOM, LxDoom) + +Misc + + * Screenshot code improved, supports BMPs (BOOM) + * Support for ENDOOM and ENDBOOM (BOOM, LxDoom) + * -timedemo and -fastdemo options (BOOM) + * Real time frame rate, segs, visplanes, sprites display + (LxDoom) + * Various extra cheat codes (BOOM, LxDoom) + +Internals + + * Greatly improved internal memory management (BOOM, LxDoom) + * Startup time greatly shortened by lazy generation of some lookups + (DOSDoom, LxDoom) + * Removed internal limits (BOOM) + +Other Tips +---------- + +On Linux, SDL tries to detect an appropriate video device automatically. +If you want to overrite the default, you can set the SDL_VIDEODRIVER +enviromental variable. At a bash prompt, this is as easy as running: + +SDL_VIDEODRIVER=fbcon prboom +or +SDL_VIDEODRIVER=svga prboom + +Details +------- + +Details on these extra features are split into separate text files: + +README.demos provides a guide to PrBoom's demo support +README.compatibility describes PrBoom's various compatibility + options and modes +README.command-line gives a command line reference for prboom, + prboom-game-server, and the format of boom.cfg. + On UNIX/Linux systems use the man pages instead. + +Editing features are not covered in the docs with this package. We plan +to bundle the editing docs as a separate download. Watch our website +for news. + diff --git a/TODO b/TODO new file mode 100644 index 00000000..85cb2148 --- /dev/null +++ b/TODO @@ -0,0 +1,1525 @@ +Entrys marked by a * are fixed. + +-------------------------------------------------------------------------- +33. (2000/12/24) Single key quit + +Colin: opulent wants the old 'q quits demo recording' feature back. + I suggest making it a configurable key setting, not bound to any + key by default. +Proff: For 2.2.4? + +-------------------------------------------------------------------------- +34. (2002/07/28) Fix documentation + +Proff: +- add mp3 stuff +- add new opengl options +- update FAQ? + +-------------------------------------------------------------------------- +*35. (2002/07/28) Fix OpenGL segfault on Linux + +Proff: +Possibly caused by using gluImageScale when paletted textures used. +Either write my own scaling functions, which would allow mipmapping +for paletted textures, or just kick the scaling. I'm for the first +if I get the time. Small fix would be disabling gluImageScale when +paletted textures used. +Proff: Will hopefully be fixed in 2.3.x + +-------------------------------------------------------------------------- +*36. (2002/07/28) Add option to move backward with second mousebutton + +Proff: like it was in my old prboom version +Proff: 2.3.x + +-------------------------------------------------------------------------- + +List of revisions from PrBoom 2.3: + +[!] - cph needs to check +[+] - already merged +[ ] - empty or not wanted +[-] - interesting but not merged yet + ++ r597 | Add player number to spawn as a parameter to P_SpawnPlayer, repl ++ r598 | Improved P_InterceptVector that isn't subject to overflows Remov ++ r599 | Check whether usleep(3) is supported, and fall back on select(2) ++ r602 | Changed long long to int_64_t. +- r603 | Added chasecam patch and fixed long long problems. Worked withou + Chasecam +- r604 | Added p_chase.* + Chasecam + r605 | Support saving games at all compat levels - this will be necessa ++ r606 | Linux byteorder.h macros are unsigned, so we must force them to + r607 | Linux byteorder.h macros are unsigned, so we must force them to +- r608 | Nice catch Quasar` - fix sprite clipping for cameras in underwat + Chasecam + r609 | Minor fix + r610 | General cleanup of EV_BuildStairs Fix scanning for multiple stai + r611 | Separate demo sync stuff into its own section Add Final Doom tel + r612 | Fix dropoff flag for Boom demos + r613 | Fix D_DoomExeDir. Win2000 and possibly others don't report the f ++ r614 | Fix D_DoomExeDir. Win2000 and possibly others don't report the f + r615 | Bump version number + r616 | Merge rewritten R_DrawSpan from the dev tree + r617 | Final demo support refinements, inc. -complevel (which was docum + r618 | Include spec file in the tarball, and build RPM with -ta + r619 | From the 2.3.x tree: Cleaned up sound code. I_StartSound gets ch + r620 | Bump version number. + r621 | Various things for 2.2.2 + r622 | Update with WAD generated from the dev tree, no v1.2 compat save + r623 | Imported doublebuffering and fullscreen toggle from dev tree. + r624 | Put samplerate a little bit down, because when timidity is used + r625 | Update boom.cfg.5 with new video options, from Proff Man page fi + r626 | This commit was manufactured by cvs2svn to create tag 'prboom_2_ + r627 | Released 2.2.2 ++ r628 | comp_stairs fix and EV_BuildStairs cleanup from stable branch ++ r629 | Boom dropoff compat fix, from stable branch ++ r630 | *** empty log message *** ++ r631 | Merge newer version from stable branch Switch to %configure and ++ r632 | Very minor spacing fixes ++ r633 | Must distribute prboom.txt so the make can pass prboom.wad + r634 | Distros have caught up with us and are shipping SDL 1.2 now Clea + r635 | Fix typo ++ r636 | Rationalise the light level calcs for sprites and walls - new fu ++ r637 | SDL video corectness fix - use SDL_MUSTLOCK to determine if we h ++ r638 | We need malloc wrappers to do error handling etc; just go back t ++ r639 | New I_Read wrapper for read(2) - partial read handling - error h ++ r640 | Clear curline after use, so preventing R_ColourMap applying it t + r641 | New savegame format revision, save compat level in saves so we c ++ r642 | Get rid of the SIGPIPE handler - it's been unused since we switc ++ r643 | Merge in Proff's port of the BEX extensions from Eternity for mu ++ r644 | Fix some d_deh.c compile warnings Make lots of internal processi ++ r645 | Eliminate another static buffer in favour of doom_printf ++ r646 | Doom v1.2 did not animate 2s middle textures (cf levels/d-f/dmsp + r647 | We always compile with SDL_mixer these days, so drop all the ho + (will break Mac) ++ r648 | Clean up POSIX build process - remove obsolete stuff about no-ne ++ r649 | Clarify the base[xy]scale logic a bit, use correct global variab ++ r650 | Eliminate some redundant variables and calculations in R_StoreWa ++ r651 | Remove sprite{width,offset,topoffset}, these were just caches of + r652 | Fixed latest changes for Visual C++. + (not necessary) + r653 | Added the new menu code from SMMU. + (we don't want the new menu code) + r654 | HEADER_SIZE must be derived from sizeof(memblock_t), which can b + r655 | Fix typo causing keypad 5 to map to PAD0 ++ r656 | Fix keypad 5 Use SDL_SetPalette (SDL1.2 feature which allows us + r657 | Update POSIX Makefile for new menu system files Remove obsoleted + (the useful stuff is already in) + r658 | Fix running with no existing config file (but the currently comm + (g_bind is not used) + r659 | Removed unused I_ConTextAttr. + r660 | I'm stupid or somewhat, I_ConTextAttr IS used. I should check th ++ r661 | For consistency, keep all linked thinkers on a class list - have ++ r662 | Remove duplicate extern for thinkercap from doomstat.h Move comm ++ r663 | Remove old Linux joystick code + r667 | Move config file stuff to g_config.c Split out code for specific + (g_config) + r668 | Added g_config to VisualC project files. + (g_config) + r669 | Added g_config.h. + (g_config) ++ r670 | Minor cleanup + r671 | Makefile change for g_config.[ch], which I forgot to commit yest + (g_config) + r672 | This commit was manufactured by cvs2svn to create branch 'axes_s + r673 | Robert Sherwood's axes patch + r674 | G_SaveDefaults callback set up only after G_LoadDefaults done Ad ++ r675 | Removed Dreamcast stuff which doesn't belong here. + r676 | Updated Dreamcast stuff (some bugs left). + (Dreamcast) + r677 | Updated/cleaned up Dreamcast stuff (some bugs left). Fixed some + (Dreamcast) + r678 | Updated/cleaned up Dreamcast stuff. Works now, the bugs weren't + (Dreamcast) + r679 | Added sound to Dreamcast version. + (Dreamcast) + r680 | Fixed tolower(*s++) bug. + (Binding/Console) + r681 | Fixed D_DoomExeDir for Dreamcast. + (Dreamcast) + r682 | Added keyboard support for Dreamcast. + (Dreamcast) + r683 | From rsherwood: Axes now scale against the appropriate values (i + r684 | Added "Installation From CVS" (Steven Elliott). ++ r685 | Added "Installation From CVS" (Steven Elliott). + (was already merged) + r686 | P_SpawnPlayer must only be called with mthing_t's from the playe ++ r687 | On the right branch this time, merge the fix for my player start + (was already merged) + 688-707 + r708 | Updated. + (TODO) ++ r709 | Intermission screen demo sync bug fixed Also fix a possible time + (was already merged) + r710 | Added additional check for extensions. + r711 | Fix for compiling prboom_server on windows. ++ r712 | Fixed some z_zone related problems. + (was already merged) ++ r713 | Fix WAD bugs that can cause crashes even in demo compatibility m + (was already merged) + r714 | New config file name prboom.config for both GL and non-GL versio + (new config format) ++ r715 | Pull forward fixes from stable tree: Fix numeric keypad. Kill th + (was already merged) ++ r716 | Another fix fromt he stable branch: Fix fastparm and respawnparm + (was already merged) + r717 | HEADER_SIZE must be derived from sizeof(memblock_t), which can b + r718 | Crop some old bits Update on progress Add comp_sound idea + r719 | Update some email addresses + r720 | *** empty log message *** + r721 | Correct compatibility levels comment + r722 | Import items from 2.2.2 and 2.2.3 + r723 | pedantic html fix + r724 | Add super shotgun/A_CheckReload fix. ++ r725 | Fix file handle leak in R_InitTranMap when called before WADs lo ++ r726 | Make A_CheckReload actually work. Compatibility optioned for old + (was already merged) + r727 | Fix typo. ++ r728 | Some patches from selliot to improve handling of missing monster ++ r729 | Make M_ReadFile return -1 on error, so we can distniguish an emp + (parts were missing) ++ r730 | LOL, backslash escaping, what was I thinking, the windows users + (was already merged) +- r731 | Initialise gamestate so it's not GS_LEVEL. This stops a SEGV if + (Chasecam) +- r732 | Merged in the axes stuff. It's a more general replacement of the + (Generalise axes handling for input devices) ++ r733 | Clear player mobj's at level end - they're allocated PU_LEVEL so ++ r734 | Add back in horrible kludge used in 2.2.x to stop desyncs in the + (was already merged) + r735 | i_joy.c is gone ++ r736 | Removed now unused X11 and OSS. ++ r737 | New comp_sound compatibility option: - player only ever hears th + (was already merged) + r738 | *** empty log message *** + r739 | Updated. ++ r740 | Moved D_DoomExeDir and FindWADFile to i_system. Renamed to I_Doo + (cleaned up) ++ r741 | -forceoldbsp is valid for non-GL builds + (was already merged) ++ r742 | Fix comp_skymap. Looks like I just overlooked this one when I di + (was already merged) ++ r743 | Added the MP3 loading. MP3 will only be loaded if the music lump + (was already merged) + r744 | Load default config from a wad if config file not found. + (new config format) + r745 | Strings in menuitem_t will be const + r746 | Add the latest new compatibility levels. + (console) + r747 | Enable gamma correction with new config system. + (console) + r748 | Extension usage from 2.2.x. + (from 2.2) +- r749 | Save sensitivity settings. + (axes) + r750 | Small fix for keybinding names. + (binding) + r751 | Credits. + (new menu stuff) + r752 | Fix version strings. + r753 | Updated. + r754 | Hopefully fix some Linux/NVidia problems. + r755 | *** empty log message *** + r756 | Minor corrections + r757 | More corrections + r758 | Another HTML fix. + r759 | Move ssg fix to the right section. + r760 | Add DivlineSide coord-swapping bug. Add more info on p_sight.c d ++ r761 | Fix swapped coord in LOS calcs involving east-west walls (this + (from 2.2) ++ r762 | Auto-fix WAD bug where 1S line uses negative sidedef number othe + r763 | Add back screenshot capability. + (menu) + r764 | Eliminate tmthing, redundant. Save tmx & tmy over P_CreateSecNod ++ r765 | Remove tmflags, redundant. Preserve tmx & tmy over calls to P_Cr + (was already merged) + r766 | Revised P_CreateSecNodeList details. ++ r767 | Added fix for 800x600 bug by John Popplewell, but currently put + r768 | Enabled fix for 800x600 bug by John Popplewell. Finetuned the va + (skipped, related to previous) + r769 | Removed debug hack. + r770 | Added statusbar console variables. Worked a little bit on the me + (menu, console) + r771 | Added hud and cheat console variables. Worked a little bit on th ++ r772 | Better invulnerability drawing for normal OpenGL (not paletted). + (2.2) +- r773 | Added better dynamic OpenGL loader (Linux makefiles need updates + (Dynamic OpenGL) +- r774 | Tried fixing release bug in tesselator. Not sucessful yet. + (Tesselator) + r775 | Sorting config file output (Lucas Pope). + (config) + r776 | Small bugfix for VC7. isprint doesn't like anything above 255 (L + (binding) + r777 | Disabling gluTesselator till it's fixed. + (Tesselator) +- r778 | New and unified software rendering by Lucas Pope. This adds 16 a + (renderer) + r779 | Accidently added this in the last update. This is the new sound +- r780 | Added fix for 800x600 bug by John Popplewell which wasn't in the + (renderer) +- r781 | Unified patch rendering (filtering etc) (Lucas Pope). + (renderer) + r782 | Added more console commands. + (console) + r783 | Added more console commands. Fixed OpenGL mode. + (console) +- r784 | Implemented on the fly video mode changing (8,16,32 bit). + (renderer) + r785 | updated + r786 | added s_samplerate console var + (console) + r787 | enclosed the signal handlers with #ifndef _DEBUG + r788 | small comment change + (binding) +- r789 | added the RDRAW_FILTER_ROUNDED filter method that combines the s + (renderer) +- r790 | added RDRAW_FILTER_ROUNDED support + (renderer) + r791 | Reverted back to old sound code, due to some problems. +- r792 | fixed V_PlotPatch clipping, still not perfect, but better. also + (renderer) +- r793 | added bufferWidth/Height and convertToBGRA parameters for the V_ + (renderer) +- r794 | Fixed some bugs in the V_GetPlotted* function family. + (renderer) +- r795 | just some small little fixes + (renderer) +- r796 | Using new V_GetPlotted functions for the OpenGL texture generati + (renderer) +- r797 | Fix include filename case for compilation on Linux + (renderer) +- r798 | __cdecl presumably only needed on Win32; not available with gcc + (renderer) +- r799 | Case sensitive filename fixes + (renderer) +- r800 | Removed __cdecl. + (renderer) + r801 | showMessages now defaults to on. + (console) +- r802 | Fixed OpenGL/Software clash. + (renderer) +- r803 | Made OpenGL library name configurable. Added dynamic screen reso + (renderer) +- r804 | Removed test code. + (renderer) +- r805 | Moved DrawLine from am_map to v_video. Added all wrappers for Op + (renderer) +- r806 | Fixed resizing in windowed mode. + (renderer) +- r807 | Fixed warnings about different const modifier. Added small check + (renderer) +- r808 | Fixed some bugs (release version now works). Touched the memory + (renderer) + r809 | String const fix + (console) + r810 | Minor fixes + (menu) +- r811 | Removed all references to V_DrawMemPatch. + (renderer) + r812 | More const fixes. + (console) +- r813 | Screenshot is now a video system function. + (renderer) +- r814 | Removed GL_DOOM. + (renderer) +- r815 | Removed all remaining GL_DOOMs before full unification. + (renderer) +- r816 | Unified software and OpenGL into one executable. Nothing changed + (renderer) ++ r817 | cleaned up the PAUSE patch rendering ++ r818 | fixed a major bug that caused any deh patch that modified a mons + (was already merged) + r819 | Made slowturntics variable. + (console) + r820 | Merge "time" portability fix fom dev branch (wi_stuff.c:1.6->1.7 + r821 | Bump version numbers Add link to HP-UX compilation instructions +- r822 | Fix inlining, min/max macros for gcc + (renderer) + r823 | Add gl_dyn.c, r_filter.h +- r824 | new TPatch format, VIDD integration, many fixed video issues + (renderer) +- r825 | new TPatch format, VIDD integration + (renderer) +- r826 | Fixed OpenGL calls to the new patch functions. Updated VisualC++ + (renderer) +- r827 | Newlines at EOF to passify gcc. + (renderer) ++ r828 | Remove duplicate BuildBEXTables (well spotted, Quasar`). + (was already merged) +- r829 | Add r_patch.c. + (renderer) + r830 | BEX-equivalent strings output should have spaces around the = ( ++ r831 | BEX-equivalent strings output should have spaces around the = ( + (was already merged) +- r832 | Removed debug code. + (renderer) +- r833 | More error checking. + (renderer) +- r834 | Some code reorganisation. Added infinitePerspective which replac + (renderer) + r835 | Some code reorganisation. + r836 | Don't use paletted textures as default. + r837 | backported from 2.3.x: fixed a major bug that caused any deh pat +- r838 | Removed glu tesselation code, as it doesn't work properly and is + (renderer) + r839 | Don't use paletted textures as default. + r840 | Backported better invulnerability drawing for normal OpenGL (not + r841 | Removed glu tesselation code, as it doesn't work properly and is + r842 | Updated. + r843 | Bumped version number. + r844 | Commit Mead's/my countable item automap highlighting. + r845 | Fix SEGV on failed lookup for BEX codepointer. + r846 | Always rangecheck patch drawing to the framebuffer, we have no g + r847 | Respawn frame bug "reported" on DW forums by Graf Zahl. + r848 | Don't treat linedef types in the 0x8000-0xffff range as generali + r849 | Fix DEH_MOBJINFOMAX. + r850 | Removed need for far clipping plane. + r851 | Updated and added some details. + r852 | Added some stuff from 2.2.x. + r853 | Update and cleanup. + r854 | Clean up some code indenting. + r855 | Split visplane duplication into new function. Fix sky-over-sky H ++ r856 | From prboom_stable_2_2: Split visplane duplication into new func + (2.2) + r857 | Updated. + r858 | Fix erroneous extra shot noise when chaingun runs out of ammFix ++ r859 | Fix chaingun bullet-noise-after-last-bullet bug. + (was already merged) + r860 | Compatibility option the chaingun sound fix (Bahdko made me, hon + r861 | Add chaingun extra shot sound fix. HTML cleanups. Obscure email + r862 | Obscure email addresses against spam harvesting. + r863 | Another email link obscured. + r864 | Fix for crash when trying to load empty slot. + (menu) + r865 | Removed $Id: $ lines. This is for the future switch to subversio + r866 | Removed $Id: $ lines. This is for the future switch to subversio + r867 | Wow, I totally messed up the last checkin. At all places where a + r868 | Wow, I totally messed up the last checkin. At all places where a + r869 | Fixed compiling of prboom_server. + r870 | Wow, I totally messed up the last checkin. At all places where a +- r871 | Fixed double dcvars.translation mapping in 16 and 32 bit. + (renderer) +- r872 | Fixed tranlation mapping for rounding filter in 8 bit. + (renderer) + r873 | Added r_patchfilter and r_patchslope console variables. + (console) +- r874 | Patch drawing defaults to point filter and square borders as thi + (renderer) + r875 | Fix crash when doing timedemo from the menu. + (menu) +- r876 | Added translucent patch drawing (used in video settings menu). + (renderer) +- r877 | Added more menu graphics from smmu. + (menu graphics) + r878 | Added r_homflash console command. + (console) + r879 | Use smaller slider graphics from smmu. Boolean values are switch + (menu) + r880 | Output of lprintf now also goes to console. + (console) + r881 | Added names for keypad keys. + (binding) +- r882 | Renamed r_videomode to r_rendermode. The r_width, r_height and r + (rendering) + r883 | Video settings menu is much more complete (OpenGL stuff missing) + (menu) + r884 | Updated. ++ r885 | Made I_SoundInit callable more that once. + r886 | The snd_samplerate command is toggleable and only allows values + (console) + r887 | Fixed sound menu. + (menu) + r888 | Added use_mouse console variable. + (console) + r889 | Fixed compilation warning related to defdemoname. Made G_Compati + (console) + r890 | Moved default_compatibility_level and compatibility_level consol + (console) + r891 | Some small changes. Split up compatibility into two pages. + (menu) + r892 | Added p_cmd.c and made small related changes. This adds monster + (console) + r893 | Removed cvs log. + r894 | Removed non OpenGL versions. +- r895 | Added OpenGL console commands. Added OpenGL settings to video me + (renderer) +- r896 | Added ViddSys. Added viddsys project for VisualC6. Enabled vidd + (VIDD) + r897 | Added some stuff. + r898 | Removed unused stuff. + (console) + r899 | Reworked the menus a bit. Disabled some non working stuff for no + (menu) + r900 | Moved demo stuff from g_game to g_demo. + (g_demo) +- r901 | Implemented iwad console command for on the fly iwad switching. + (IWAD switching) +- r902 | Documentation for vidd. + (VIDD) + r903 | Fix drawing when console is fullscreen. + (console) + r904 | Added C_RunTextCmdf. Added c_addcommand_stats console command wh + (console) + r905 | Changed to use C_RunTextCmdf. + (menu) + r906 | Added iwad selection in "features->load wad". + (menu) +- r907 | Changed iwad from console command to console string and prevent + (IWAD switching) + r908 | Using g_iwad (from iwad console string) for default iwad when no + (IWAD switching, menu) +- r909 | Sound functions take const mobj_t * instead of void * for locati + (VIDD) + r910 | Moved includes bacuase of some changes in the header files. +- r911 | Sound functions take const mobj_t * instead of void * for locati + (IWAD switching) + r912 | Added c_net from smmu. + (net from smmu) + r913 | Added fraggle script files from smmu, but didn't integrate it in + (fraggle script) +- r914 | Made wall and floor filters seperatly selectable (to allow round + (renderer) +- r915 | Added Lucas Pope to credits. + (renderer) + r916 | Several small changes to let fraggle script stuff compile. Added + (fraggle script) + r917 | Added fraggle script files. Moved some files into project subfol + (fraggle script) +- r918 | Three font graphics from SMMU added. + (font graphics) +- r919 | Merging more stuff from SMMU: v_misc added and moved font handli + (font handling - better get it from Eternity) +- r920 | Using the new V_Text* functions here instead of implementing ess + (font handling) + r921 | Removed unused externs. + r922 | Some cleanup. Using V_IsPrint to check if char is printable. Add + (console) +- r923 | Initialize the font in v_misc. + (font handling) + r924 | For iwad switching: Initialize patches (R_Init) before any call + r925 | Definitions for silentmove sector flags. + r926 | Added totalfrags in preparation for the coming smmu hud code. + (smmu hud) + r927 | Added lightlevel_t. Added prototypes for new functions from smmu + (fraggle script) + r928 | Fixed type cast compiler warnings. + (fraggle script) + r929 | Using the recently added V_DrawBox function. + (menu) + r930 | Prevent adding a command twice, as this causes an endless loop l + (console) +- r931 | Typecast to prevent warning. + (renderer) + r932 | Added linedefs for scripting from smmu. + (fraggle script) + r933 | Last log entry is wrong. This added silentmove. + (fraggle script) +- r934 | Adding silentmove and using plat_up, plat_stop and palt_down con + (some constants) +- r935 | Added T_LightFade and P_FadeLight from smmu. + (P_FadeLight) + r936 | Enabled T_AddCommands and V_AddCommands. Some moving around of * + (console) + r937 | New hud from smmu with some small related changes. The fullscree + (HUD) + r938 | Removed cvs log. +- r939 | Removed message from player_t, use doom_printf or player_printf + (Removed message from player_t, use doom_printf or player_printf instead) +- r940 | Fix chasecam when teleporting. + (chasecam) +- r941 | Added r_blockmap. + (blockmap) +- r942 | Some code cleanups and documentation fixes. Very small changes f + (several changes) +- r943 | Enabled V_FPSDrawer: + (FPS drawer) + r944 | Added and enabled more smmu stuff. + (smmu) +- r945 | Fixed V_WriteTextXYGapFont call from using v_font to supplied fo + (font) + r946 | Small cleanups and added some variables from smmu. +- r947 | prboom.wad now loaded together with iwad. Using IndentifyVersion + (IWAD switching) + r948 | Added deh_loaded variable from smmu. + (console) + r949 | gravity is now a variable. + (gravity variable) + r950 | Renamed G_DeferedInitNew to G_DeferedInitNewNum and G_InitNew to + (hubs) + r951 | Moved G_Ticker to the end of the file. + r952 | Added p_info and p_hubs from smmu. + (hubs) + r953 | Some definitions (intermission camera related) and the barest mi + (intermission camera) + r954 | Check if soundfx is enabled in S_StopSounds to fix crash. + r955 | Moved "extern int screenblocks;" to r_main.h + r956 | Removed now unused extern int key_* definitions. Added G_Scrambl +- r957 | Print errors to console instead of calling I_Error. + (renderer) + r958 | Put code from g_game (G_DoLoadLevel) to init the sky texture nam + (smmu) + r959 | More things from smmu in P_SetupLevel. + (smmu) + r960 | Removed unused key_* definitions. Moved key_map_* definitions to +- r961 | Z_PrintStats is Z_DrawStats now and called from HU_Drawer. It's + (font) + r962 | MAXLOADWADS now 2 again, cause prboom is implicitely loaded. +- r963 | bodyqueue fix from mbf. + (bodyqueue) + r964 | New sound code from smmu. + (smmu - sound) + r965 | Reenabled quit sounds. Added i_endoom_delay to change delay when +- r966 | Small changes and additions from smmu. + (chasecam) + r967 | P_SetupLevel and G_InitNew take a levelname instead of gameepiso + (smmu) + r968 | Added death messages from smmu. + (smmu) + r969 | Bump version number for autoconf. + r970 | Sprite offsets must be flipped for flipped sprites. Thanks to fr + r971 | Further improve the P_CreateSecNodeList global variable cleanups + r972 | Fix endianness issues in tic field of network packets. Should be + r973 | cf http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=174541 Remov + r974 | Get line lengths right in README.command-line + r975 | Added more intermission code from smmu. Made some functions stat + (smmu) + r976 | Added use_startmap and startlevel. Added functions for dynamic w + (smmu) + r977 | Enabled some smmu stuff which didn't work before. + (smmu) + r978 | Removed ChangeLog as it's generated. + r979 | Reintroduce -nomouse option; essential for debugging. + r980 | Add comp_sound stuff, backported from the development version. + r981 | Set compatiility options correctly in netgames in old compatibil + r982 | Add support to network game server for reading config files. It + r983 | Added explanation for AM_PATH_SDL problem to compile from cvs se + r984 | Added explanation for AM_PATH_SDL problem to compile from cvs se + r985 | Added correct eol property. + r986 | Added correct eol property. + r987 | Fixed Makefile.am. Small posix related fixes. + r988 | Added Visual Studio .NET project files. + r989 | Added tab before each line of files. + r990 | More Makefile and posix fixes. + r991 | Posix fixes for OpenGL. Now always compiled in, as there are no + r992 | Makefiles are weird. Replaced tabs with spaces in file list. + r993 | Added VISUALCNET to distribution. + r994 | Added inl and vidd directorys to makefiles. + r995 | Fix end of line. + r996 | Fix case. + r997 | Fix case. + r998 | Added src/inl/Makefile and src/vidd/Makefile. + r999 | Fix end of line. + r1000 | Removed trailing slashes in I_DoomExeDir. + r1001 | Moved includes from gl_intern.h to gl_main.c and gl_texture.c. P + r1002 | Remove ChangeLog from distribution tarball - we don't have one. + r1003 | Remove readme.txt from distribution, as it is not in the reposit + r1004 | Sanity checking for netcmd numbers. Add more entries to netcmd e + (console, net) +- r1005 | Endian fixes. + (renderer) + r1006 | Removed PFNGLCOLORTABLEEXTPROC definition and added FAQ entry. + r1007 | Added GL_DOOM define. +- r1008 | More endian fixes. + (renderer) + r1009 | Reorder comp_sound to match 2.2.4. ++ r1010 | Import fixes from stable tree, revisions 823,825,827: - Respawn + (2.2) ++ r1011 | Port forward fraggle's sprite offset flipping fix. Clean up and + (sprite clipping fix) ++ r1012 | Port forward the fix for global variable overwriting (tmbbox) in + (2.2) + r1013 | New address for Barry Mead. + r1014 | Fix non-GL_Doom compilation. + r1015 | Allow s_sound.c to specify volume with greater precision. This a + r1016 | Work around defect in SDLNet_UDP_Bind channel support. + r1017 | Add a feedback mechanism to keep clients more accurately synchro + r1018 | Upadte email address and download address. + r1019 | Remove direct smpeg dependency - it should be pulled in automati + r1020 | Remove debugging accidentally committed yesterday, and update NE + r1021 | Add weapon pickup sounds, sprite flipping, and updated p_map.c f + r1022 | Protect another email address. + r1023 | Hide another email. + r1024 | Update for 2.2.4 release. ++ r1025 | Bring forward generalised line range fix from 2.2.x branch. + (2.2) + r1026 | Fix xtratics - it was generating negative tic #s and the server + r1027 | Added Makefile.am for extra files in generated tar. + r1028 | Tag the 2.2.4 release. + r1029 | Added more missing files to the Makefiles. + r1030 | Added missing GenEnd. ++ r1031 | Updates to spec file (from 2.2.4). + (2.2) ++ r1032 | Do not link smpeg (ported from 2.2.4). + (2.2) ++ r1033 | From 2.2.4, less strict automake checks. + (2.2) ++ r1034 | Port back-off support from 2.2.4. Port endianness fixes from 2.2 + (2.2) ++ r1035 | As in 2.2.4, removed ChangeLog. + (2.2) ++ r1036 | Improve accuracy of sound calculations (sound range fix ported f + (2.2) ++ r1037 | Update NEWS with final 2.2.4 list. + (2.2) + r1038 | Tagging PrBoom 2.3.0 + r1039 | PrBoom 2.3.0 released. + r1040 | Explain a bit that this is a rough'n'ready beta release. Solicit + r1041 | Windows release was bad. + r1042 | Some feedback. + r1043 | Updated. + r1044 | Correct the off-screen checking in HUlib_drawTextLine. This stop + r1045 | Bumped version number. + r1046 | Fix config.h for Windows. All references to "../config.h" were r + r1047 | Fix eol. + r1048 | Report error if port bind failed. Sanity checking on player numb + r1049 | Load OpenGL libraries only when needed. +- r1050 | I think this description of the networking is worth saving. + (network explanation) + r1051 | Take screenshot - f12 Quit - f10 Gama correction - f11 + (config) + r1052 | Added autorun to menu. Updated todo. + (menu) + r1053 | Moved and added stuff for redesign. + r1054 | Forgot to rename. + r1055 | Mostly working like the old pages. I have to fix the tool first. + r1056 | With SimpleTAL 3.5, a small hack in pubtal and some small fixes + r1057 | Add navigation and reordered some things. + r1058 | Added hover effect for links. + r1059 | Added missing template. + r1060 | A very small readme on how to build the website. + r1061 | Nicer rollover effect. + r1062 | Fix eol. I finally found the option to prevent my editor from me + r1063 | Fix eol. I finally found the option to prevent my editor from me +- r1064 | More endian fixes. Now it works on big endian systems as far as + (renderer) + r1065 | Fixed ignore-filter. + r1066 | Not needed anymore. ++ r1067 | Obvious bug in my attempts to fix the T_VerticalDoor corruption + (2.2) + r1068 | Fix overzealous safety check added in last update. + r1069 | If server is closed before game start, exit the client too. + r1070 | We should call D_QuitNetGame for any client exit after the serve + r1071 | Better management of players joining and quitting during startup + r1072 | Demo players are after this: total live monsters count in the HU + r1073 | Set savegame root. + r1074 | fake_contrast should be on by default (used to be on but configu + r1075 | Bump version number. + r1076 | Marked hub and script related stuff with ifdefs. + (hub) +- r1077 | Fixed textcolours array, this caused segfaults. + (font) +- r1078 | Fix prboom.wad not found [Patch 949349]. + (IWAD switching) +- r1079 | Fix texture pegging for upper textures [Patch 897801] [Bug 59999 + (renderer) + r1080 | Set svn:eol-style property. + r1081 | Set svn:eol-style property. + r1082 | Make line_t junk static in A_LineEffect. ++ r1083 | Make line_t junk static in A_LineEffect [Bug 946686]. + (2.2) + r1084 | Fix for new GCC warnings. +- r1085 | Wrap extremely long lines. + (renderer) + r1086 | Fix Bug #845129. Filenames with dots didn't work properly (and s +- r1087 | Fixed Bug #929248. Buffer overflow in F_TextWrite. + (font) + r1088 | Added SDLK_CARET as console key. + r1089 | Fix compilation. +- r1090 | (Partly) fix bug #851055. (Re)implemented multipatch textures. D + (renderer) +- r1091 | Fix bug #810700. It was some rounding error introduced with the + (renderer) +- r1092 | Limit the posts of a patch to the limits of the texture. As a si + (renderer) +- r1093 | Removed some unused code. I think this got obsolete with the new + (renderer) + r1094 | Added an ifdef USE_ULL_SUFFIX. This should be added with a test + r1095 | Fix bug #826682. Unavailable IWADs get disabled. + (menu, iwad switching) + r1096 | Fix savegames (Bug #896092). + (smmu savegames) +- r1097 | Fix multiple-patches-on-one-column textures (Bug #851055). + (renderer) + r1098 | Bump version number. + r1099 | Load OpenGL library only once. + r1100 | Fix bug #926548 and enhance widgets with the possibility to choo + (hud) + r1101 | Fix bug #810562. Bounded keys weren't checked when playing demo. + (binding) + r1102 | Tried to achive savegame compatibility with 2.2.x, but failed. T + (smmu savegame) + r1103 | Update email address. + r1104 | Use SDL_MUSTLOCK to determine whether si render direct to buffer + r1105 | Bump version number. + r1106 | A few minor fixes so far. + r1107 | Fix --enable-dogs + r1108 | Cheatcodes didn't work anymore and this change wasn't necessary + (binding) + r1109 | Updated. +- r1110 | Removed W_UnlockLump* calls made obsolte by the new renderer. + (renderer) + r1111 | Added a sanity check to prevent problems. + r1112 | Prevent compiler warning. + r1113 | Fix position of automap text widgets when using high-res. + (hud) + r1114 | Added devparm as console command. + (console) +- r1115 | Made R_GetTextureColumn simpler by using the new R_GetTextureCom + (renderer) +- r1116 | Added missing R_GetTextureColumn call for dcvars.prevcolumn. + (renderer) +- r1117 | Use user pointer on Z_Malloc. + (renderer) +- r1118 | Use normal malloc instead of Z_Malloc. + (renderer) + r1119 | Fix compiler warnings and a weird compiler error in Visual Studi + (VIDD) + r1120 | Updated to Visual Studio .NET 2003. + r1121 | Added test for ULL number suffix. + r1122 | Import r418 from trunk: Simplify event handling, logic is all in + r1123 | Update for new dmalloc API. + r1124 | Import r1115 from trunk, fixing dmalloc support. More consistent + r1125 | Merge r941 from trunk (plus Lee's comments from MBF): bodyqueue + r1126 | Import r707 from trunk, for bug #592350: Use M_ReadFile for Find + r1127 | Import r417 from trunk: No need for screenshot to be a gameactio +- r1128 | Fix texture offset when using linear filter in software mode. + (renderer) +- r1129 | Fixed bug #834202. Translucency setting wasn't saved. Patches ar + (renderer) + r1130 | Fixed several compiler warnings. + r1131 | Import r514 from trunk: Better inline asm from Eternity. + r1132 | Fast forward to given map # in demo playback. + r1133 | Fix desync caused by timing differences in intermission screen w ++ r1134 | Intermission screen desync on secrets counter. for e.g. 30cn3519 + (2.2) + r1135 | Another demo sync fix for Boom. + r1136 | monkeys, traditional_menu, sts_always_red and weapon_recoil set + r1137 | Import r712 from trunk (needed to complement r418 imported yeste +- r1138 | Imported new colour translation code from SMMU/Eternity. + (renderer) + r1139 | Fixed a few warnings. Let z_zone.c compile on windows. Add defau + r1140 | Import r708 from trunk, needed to complement r707 imported yeste + r1141 | Ripped out tranmap caching. +- r1142 | Don't initialise sound before I_Init(). + (init sound) + r1143 | Import 2.3.x prboom.wad. + r1144 | Blazing door hitting an obstacle should make a blazing door soun +- r1145 | Blazing door closing and hitting something should make a blazing + (blazing door sound) ++ r1146 | Live monster counter for HUD, imported from stable_2.2 r1064. + (2.2) + r1147 | Another blazing door noise bug. ++ r1148 | The new simplified z_zone implementation. It's just a wrapper ar + (z_zone) + r1149 | #include "z_zone.h", for malloc. ++ r1150 | Clear tmthing after each tic - could be referencing a freed obje + (z_zone) ++ r1151 | Demo fast forward to given map #, from stable_2.2. + (2.2) + r1152 | Fix autoconf warning. ++ r1153 | Live monster counter now worksa with archville resurrections. + (2.2) + r1154 | Live monster counter now counts archville resurrections. ++ r1155 | Fix r1142: need p_map.h for P_Map*. + (z_zone) + r1156 | Fix a possible SEGV due to tmthing holding a dangling pointer if +- r1157 | Made patches and texture composites of the new software renderer + (renderer) ++ r1158 | Added memory_size variable, which is the threshold at which purg + (z_zone) ++ r1159 | Fixed long standing backspace printing bug on windows. + (was already merged) + r1160 | Fixed long standing backspace printing bug on windows. ++ r1161 | Fixed missing 0 at end of string. + (was already merged) + r1162 | Fixed missing 0 at end of string. + r1163 | Add \n when printing HUD messages to console. + (console) ++ r1164 | Fixed rather big memory leak. + (was already merged) + r1165 | Fixed rather big memory leak. +- r1166 | Set edgeSloping when combining posts. + (renderer) + r1167 | Fixed config for new PubTal. Changed CSS a little bit. + r1168 | Generalised doors now make sounds appropriate to their speed. Th +- r1169 | Fix sounds based on speed of generalised doors. Fix comp_sound i + (sounds) +- r1170 | More original Doom like default settings. + (default settings) + r1171 | Padding for short REJECT lumps (fixes rq22-318.lmp and probably + r1172 | Make it possible to abort network checking on windows. +- r1173 | Make it possible to abort network checking on windows. + (networking on windows) +- r1174 | Completely removed G_ChangedPlayerColour. + (G_ChangedPlayerColor) +- r1175 | Added portable snprintf. Compile with warnings as errors on wind + (psnprintf) +- r1176 | Made some buffers bigger, so they will not overrun. + (buffers) +- r1177 | Small enhancements and bugfixes from Eternity. Fix indentation. + (renderer) + r1178 | Removed limit on aliases (from Eternity). Small enhancements and + (console) +- r1179 | Unify V_WriteText* and V_StringWidth* functions. + (font) + r1180 | Small enhancements and bugfixes from Eternity. Fix indentation. + (console) + r1181 | Fix compilation with gcc 3.4.x - inconsistency between header an + r1182 | Add calls to SMMU/Eternity net code. + (net) + r1183 | Fix dist target for newer autoconf. + r1184 | Enhanced Edition v1.9 demo support. + r1185 | Fix compilation of server. + (net) + r1186 | Implemented printing of tabs. ++ r1187 | Implemented printing of tabs. + (was already merged) + r1188 | Fix crash when player respawns in multiplayer due to new P_MapSt + r1189 | Small fix from Eternity. Small code cleanups. Fix compilation of + (net) +- r1190 | Fix bug #810566. Music stops playing after sound settings change + (sound) ++ r1191 | Fix player respawn crashes with new P_MapStart/End + (z_zone) + r1192 | Updated. + r1193 | Bug fixes from Eternity. Small code cleanups. Enabled some more + (smmu fixes from eternity) + r1194 | Bugfixes from Eternity. Small code cleanups. Enabled more stuff. + (smmu fixes from eternity) ++ r1195 | Renamed namespace to li_namespace. + (was already merged) ++ r1196 | Removed stealth monsters, they weren't supported in the renderer ++ r1197 | Ripped out the remaining stealth monster bits. The flags in p_mo + r1198 | Added psnprntf.* and m_fcvt.*. + r1199 | Added pastebin. + r1200 | Start level code also uses P_Map* and must be wrapped (else P_Ma ++ r1201 | Start level code also uses P_Map* and must be wrapped (else P_Ma + (2.2) + r1202 | Small changes to css. Moved passwords outside of pastebin.php. R +- r1203 | Don't use systems snprintf as replacement for psnprintf. + (psnprintf) +- r1204 | Use psnprintf instead of snprintf or sprintf. + (psnprintf) + r1205 | Fix for newer autoconf. +! r1206 | Fix prboom_3_compatibility savegames. + (savegame) ++ r1207 | Longtics/EE support ported from stable_2.2. + (2.2) + r1208 | doom2.exe only used the first 10 deathmatch starts. ++ r1209 | doom2.exe only used the first 10 deathmatch starts. + (was already merged) + r1210 | Transmit data for tic 0. This probably caused the first-tic desy + r1211 | Experimental proxy server between ipxsetup.exe's IPX networking + r1212 | Works now - fixed packet sizes and tic wrapping calcs. + r1213 | Make the backoff stronger, needed to need down the lag with doom + r1214 | Pack and pad the packet header struct, and ensure that the paddi + r1215 | Make demo noise a bit less intrusive. + r1216 | Prevent SEGV if server incorrectly give a bad consoleplayer numb + r1217 | Extra confirmation phase, to deal with clients that quit or lose + r1218 | MF_BLOCKMAP missing from Doom -> PrBoom flags conversion, and th + r1219 | non-SDL_net net code is gone. Remove makefile conditionals, as t + r1220 | Move GL_DOOM and COMPILE_VIDD from project settings to config.h. +- r1221 | Avoid crash when prboom.wad is not found. + (IWAD switching) +- r1222 | Avoid crash when the opengl extension string is too long. + (renderer) +- r1223 | Fix printing of opengl extension. The length was calculated inco + (renderer) ++ r1224 | Another fix for tabulators in the windows console window. + (was already merged) + r1225 | Backport of the fix for printing opengl extensions and the tabul + r1226 | Fix up .NET project files. + r1227 | updated. +- r1228 | Fix compilation of prboom_server. + (psnprintf) + r1229 | Fix compilation of prboom_server. + r1230 | EE got called v1.91 + r1231 | EE got called v1.91 ++ r1232 | Fix dehacked mobj bits conversion, cf r1218 from stable branch a + (was already merged) + r1233 | monster_backing off by default too. + r1234 | Fix my email address. + r1235 | Bring my email addresses up to date. + r1236 | PrBoom 2.2.5. + r1237 | PrBoom 2.3.0. + r1238 | Small fix to prevent unwanted newlines. + r1239 | Created tag for PrBoom 2.2.5. + r1240 | Bump version number. + r1241 | autoconf and rpm disagree about the use of an explicit platform + r1242 | Created tag for PrBoom 2.3.1. + r1243 | Bumped version number. + r1244 | Fix intermission screen splat/YaH code, which was crashing due t ++ r1245 | Fix off-screen you-are-here's on Inferno intermission screens. + (was already merged) +- r1246 | Don't play music when volume is 0. + (music volume 0) ++ r1247 | Fix fuzz drawing for hi-re. + (was already merged) + r1248 | Fix fuzz drawing for hi-re. +- r1249 | Fixed patch drawing with VPT_FLIP. + (renderer) + r1250 | updated. + r1251 | Fix hang when one network client exits. + r1252 | Update default server parameters to match the client. + r1253 | Fix some serious memory leaks. + r1254 | and news for the mem leak. + r1255 | Nicer diagnostics of player states. + r1256 | Fix NOSECTOR and NOBLOCKMAP effects in old dehacked patches. ++ r1257 | Fix nosector/noblockmap mixup. + (was already merged) + r1258 | and the NEWS item for the deh fix. +- r1259 | Set default netgame options the same as the default prboom.confi + (defaults) + r1260 | Fixed player spawn sound. ++ r1261 | Fixed player spawn sound. + (was already merged) + r1262 | Player reborn TFOG bug. +- r1263 | No oof sound on open 2S lines when comp_sound. + (sounds) ++ r1264 | Quick hack to enable network client to ride over temporary loss + (was already merged) + r1265 | Use g_game.h instead of redefining stuff. Backspace repeat. ++ r1266 | Bring into line with intended protocol, from stable branch. + (2.2) ++ r1267 | Bring slightly more into line with my work from 2.2.x. r1048+r10 + (was already merged) + r1268 | Created bundle folder remotely. + r1269 | Created bundle for prboom2. ++ r1270 | Make I_WaitForPacket more useful, have a timeout. + (2.2) + r1271 | Repeat init packet - it might get lost or server is slow startin ++ r1272 | fix last commit. + (was already merged) ++ r1273 | Return time to next tick. + (2.2) +- r1274 | Enable key repeat. + (key repeat) + r1275 | Remove unused consdata in ticcmd. + (console) + r1276 | Treat warnings as errors. + r1277 | Fix some warnings. + r1278 | Don't what I mean, not what I say :-). Don't call SDL_GetTicks t + r1279 | More intelligent timing in TryRunTics. + r1280 | Smarter delays in TryRunTics. Also, listen for network packets w + r1281 | Use packet_set for PKT_TICC. + r1282 | Shorter delay while waiting on packet, so windows users have the + r1283 | Fix tntem cheat. +- r1284 | Fix HOM detection drawing. + (renderer) +- r1309 | Fix bug [1046368] and patch [1199312]. + () +DONE! + +-------------------------------------------------------------------------- + +PrBoom-plus revisions since "Stuff to port from prboom-plus" + + + unexamined ++ done +% partially done +- of interest +? not sure +x not of interest + ! needs attention + ++ r2312 | fix of hanging decoration disappearing in Batman Doom MAP02 +? r2313 | comp[comp_oofsound] - done ++ r2319 | the global variable r_NoInterpolate is not necessary any more (s ++ r2320 | don't thrash cpu during pausing (should be applied to prboom) ++ r2321 | do nothing if a pause has been pressed during playback. pausing +x r2322 | "-auto" for autoloading of wads according to the lmp file-name +?! r2323 | correction of smart items clipping code: not a fully dead corpse +?! r2324 | heh, previous commiting was unsaved version.fixed +?! r2325 | there are three modes of sprite clipping in opengl: +?! r2326 | gl_sprite_offset moved to globals; things with MF_MISSILE should +?! r2327 | some comment changed ++ r2328 | fixed(?) cph's bug(?) introduced in r1431 ++ r2329 | fix for 2328. maybe better possible +x r2330 | bug with def_arr type (Z_Realloc do not fill additional region o ++ r2331 | comp_doorstuck should be forced for boom201 compatibility (at le +x r2332 | launcher ++ r2333 | cph's one more bug: The current behaviour with comp_666 is that ++ r2334 | Check for prevention of possible collisions between level of com ++ r2335 | revert to r2333 ++ r2336 | Additional check of gameepisode in A_BossDeath is added, because +?! r2337 | small fix for opengl, sky and mouselook +x r2338 | progress bar during demo playback works during skipping +x r2339 | minimization of distinctions with trunk +x r2341 | Merged r2304:2340 from trunk +- r2342 | Optional removal of a quit sound delay (it is old Prboom-Plus's +- r2344 | Improved support for Doom v1.2: +x r2345 | - the progress bar did not work correctly for demos with more th +x r2346 | Predefined translucency setting (comp_translucency) should not o +x r2347 | Prevention of division by zero in G_DoPlayDemo() if players coun ++ r2348 | Handling of unrecognized demo formats ++ r2349 | fix for r2348 (Doom v1.2 demos) ++ r2350 | comments +x r2351 | Files for -auto should be searched in all standard places: curre +x r2352 | Sometimes the figures for kills, items, etc., stop getting updat +? r2353 | warning C4018: '==' : signed/unsigned mismatch +x r2356 | to avoid conflicts prboom-plus.wad should be added with full pat +x r2357 | launcher +x r2358 | launcher again +x r2359 | launcher again again +x r2360 | added I_vWarning() function +? r2361 | I_FindFile: searching exe dir before current dir, check wfname a ++ r2362 | Better compatibility with boom v2.01. There are no more desync o ++ r2363 | fix for r2362 ++ r2364 | demover is global now. it's required for demos recorded in "demo ++ r2365 | Three separate definitions of max and min are moved in doomtype. +x r2366 | Compilation fixes: ++ r2367 | max, min are renamed to MAX, MIN to avoid all possible warnings ++ r2368 | removing unnecessary code (after r2367) +?! r2369 | spriteclip code shouldn't be applied to things with MF_NOGRAVITY +x r2370 | Removed files and folders with compiled pcre libs +x r2371 | pcre projects are added to main solution +? r2372 | New savegame format with continuous numbering. Now it is not nec +? r2373 | missed PACKAGEVERSION in VisualC6\config.h +? r2374 | ALL_IN_ONE define for having PrBoom-Plus.wad in the exe (win32 o +? r2375 | fix compilation +x r2376 | Merged r2340:2355 from trunk ++ r2377 | Fixed mbf_compatibility incompatibility. There is no more desync +x r2378 | Demo Progress Bar during skipping will be updated not more often +x r2380 | All-in-one doesn't find resource wad when using -auto ++ r2381 | "All boss types can trigger tag 666 at ExM8" -> "Emulate pre-Ult ++ r2382 | There is no more crashes on boom200 demos (wrongly header readin ++! r2383 | Animated middle textures with a zero index should be forced. +?! r2384 | New mouse code without SDL lags. Win32 mouse handling was remove ++ r2385 | non traditional menu was removed to avoid bugs with Alien Vendet +?! r2386 | New mouse code: Sounds as nonsense, but SDL mouse lags are depen +? r2387 | do not clear events queue at the start (like vanilla) +?! r2388 | some comments about new mouse code +?! r2389 | There is no more win32 specific code in new mouse code. Now it w +x r2390 | launcher: wrong strings in history combo +- r2391 | sound_noquitsound is renamed to misc_fastexit +?! r2392 | missed SDL_WM_GrabInput(SDL_GRAB_OFF) ++ r2393 | do not process mouse input in menu because it's buggy and annoyi +x r2394 | Merged r2355:2379 from trunk +% r2395 | fix some compilation problems on MAC and POSIX +?! r2396 | very small improvement ++ r2397 | Fixed Boom incompatibilities. There are no more desyncs on Donce ++ r2398 | Fixed Boom incompatibilities. Original friction and bobbing code +x r2399 | bump version to 2.4.8.2 +x r2400 | REJECT overflow cannot be emulated if the REJECT size is not div +x r2401 | Not used function (RejectOverrunAddInt) has been removed +?! r2402 | Boom's color maps are supported in OpenGL mode +?! r2403 | Boom's colormaps: some comments are added +?! r2404 | Effect of invulnerability uses a colormap instead of hard-coding +?! r2405 | comp[comp_skymap] works in OpenGL mode now; +x r2406 | "Paper Items" setting is not applied to hanging things. +x r2407 | always grab the mouse in camera mode when playing levels and men +- r2408 | [-] PrBoom bug: %DOOMWADDIR% had no effect for PWADs. +?! r2409 | Boom's colormaps in OpenGL: fix crash in gld_Precache +-! r2410 | New mus -> mid conversion code thanks to Ben Ryves lprintf in PCSOUND files +?! r2485 | bug introduced in r2482 - wrong indexes were used ++ r2486 | PrBoom bug: Par times are not shown ++ r2487 | Sometimes the pistol sounds that play while the tallies are bein +x r2488 | Has made compatible my correction of par times (r2486) with "-au + r2489 | fix "-emulation" code + r2490 | let's show the warning if savegame is from the previous version + r2491 | has made dynamic changing of gamespeed more smooth + r2492 | "-force_lxdoom_demo_compatibility" comman-line switch for emulat ++ r2493 | On ExM8 of movie runs, the total time is often displayed wrong. + r2494 | -force_lxdoom_demo_compatibility: there are no more desynchs on + r2495 | The ultimate 'ATI sucks' fix: Some of ATIs graphics cards are so ++ r2496 | big comment for r2473: + r2497 | fix for demoprogressbar with veeeeeeery long demos on high resol + r2498 | fix compilation problems + r2499 | gl_render_precise variable for control of the new seamless code + r2500 | Move the window to the screen center if stupid SDL create it in + r2501 | Fix crash when the program wants to S_AdjustSoundParams() for pl + r2502 | The local variable should be used in G_ReadDemoHeader() instead + r2503 | check for overrun in G_ReadDemoHeader() + r2504 | allow -recordfromto for all levels of compatibility + r2505 | "-emulate prboom_ver" includes bug with direct switch to SSG in + r2506 | huge speedup (up to 10x) on levels with sectors which have many + r2507 | fix for r2506 + r2508 | -emultae prboom-ver stuff: few new entries are added + r2509 | Monsters spawned by Icon of Sin should not be countable for tota + r2510 | comment for previous commit (r2509) + r2511 | S_DISABLE and CR_DISABLE for disabled items in menu to avoid bug + r2512 | Sound initialization was broken in r2508 if "-skipsec" command-l + r2513 | Attempt to revert some old changes made in prboom+. Probably all + r2514 | A lot of cleanups, speedups and bugfixes in render: + r2515 | Avoid crashes at end of demos if DEMOMARKER (0x80) does not exis + r2516 | fix for r2515 + r2517 | fix for r2506 (huge speedup on levels with sectors which have ma + r2518 | Fox for "The ultimate 'ATI sucks' fix" (r2495) + r2519 | fix compilation problems + r2520 | Compatibility with common mapping errors "linedefs w/o tags appl + r2521 | M_DoScreenShot did not work with "-videodriver directx" on fulls + r2522 | Optimization of "quality" mode of "rendering quality" setting. V + r2523 | fix compilation problems + r2524 | Do not use seamless code ("quality" mode) in software mode + r2525 | missed M_ChangeGLRenderPrecise call + r2526 | In GL mode, the IDRATE info for sprites is always displayed as 0 + r2527 | Try to fix building prboom-plus under linux again. + r2528 | Optimization of "quality" mode of "rendering quality" setting. F + r2529 | fix r2513: statusbar were not updated in automap mode on softwar + r2530 | There is no more visual glitches with sky on Icarus map14 and He + r2531 | nothing. comment. + r2532 | Fix of no warning (flashes between shadowed and solid) when invi + r2533 | PrBoom bug: When putting -devparm in the command line, prboom-pl + r2534 | Do not rebuild blockmap on the same level (after save/load) + r2535 | Blinking during demoskip on fullscreen + r2536 | Don't thrash cpu if the window doesnt have focus + r2537 | Original P_FindNextHighestFloor() is restored for demo_compatibi + r2538 | Ability to force -nomonsters and -respawn for playback of 1.2 de + r2539 | 1. gl_boom_colormaps config's variable to reduce memory use + r2540 | USE_GLU_IMAGESCALE is temporarily enabled to avoid some bugs + r2541 | optimization: make heightlist static to avoid malloc/free pair i + r2543 | DemoProgresBar continued to be displayed for in-wad demos (demo1 + r2544 | There are TWO bugs in the ouch face code. Not only was the condi + r2545 | Move mouse sensitivity menu upwards a little to avoid overlap wi + r2546 | The bug in algorithm of splitting of a sector into the closed co + r2547 | There is a new command-line switch "-shorttics". This makes it p + r2548 | fix compilation problem + r2549 | Fix compilation on Mac OS X. + r2550 | fix sound origin for large levels + r2551 | fix previous revision r2550 + r2552 | Premature program exit during map26 in 4-player coop demo 29uv4p + r2553 | dded two new precalculated float fields in GLTexture struct + r2554 | Wrong calculation of x2 coordinate of a weapon in gld_DrawWeapon + r2555 | fix for r2554 + r2556 | M_ChangeFOV() and M_ChangeGLRenderPrecise() should be under cond + r2557 | two video pages should be cleared in gld_Init to avoid blink dur + r2558 | Remove any line which has a clone with the same vertexes and ori + r2559 | "Allow Boom colormaps" setting => "Allow colormaps" + r2560 | wipe screen effect in OpenGL + r2561 | wipe screen effect in OpenGL: fix memory overflow + r2562 | fixed slowdown during screen melt at 1024x768 on some systems + r2563 | fix menu bug after r2560 (wipe screen effect in OpenGL) + r2564 | Changes in algorithm of GL wipe. Now it works more quickly, requ + r2565 | Add gl_wipe.c to src/Makefile.am + r2566 | Transferred sky texture on scrolled wall was not scrolled if mou + r2567 | merge R_CheckTextureNumForName from prboom + r2568 | Fix compilation on Mac OS X: add gl_wipe.c to src/MAC/Rakefile + r2569 | vertical scroll on transferred sky texture works now too (if mou + r2570 | scroll stuff from the latest revisions is temporally disabled + r2571 | scroll on transferred sky texture is enabled again + r2572 | [-] GL wipe: white screen during final screen animation in some + r2573 | sky property-transfer linedef types should be applied only for M + r2574 | sky transfer under prboom_comp now + r2575 | Reuse of a current palette to avoid black screen at software ful + r2576 | unnecessary (duplicated) call of UpdateGrab() is removed from D_ + r2577 | More adequate warning of possibility of desynch in compatibility + r2578 | from chocolate-doom r958: Initialise tracksize variable before m + r2580 | hires textures + r2581 | hires: + r2582 | hires patches + r2583 | fake colormaps for hires patches + r2584 | fix progress of load hires on fullscreen + r2585 | hires: fix for hires patches and sts_always_red; optimization of + r2586 | hires: another fix for progress bar + r2587 | all gluBuild2DMipmaps entries had wrong second parameter: gl_tex + r2588 | caching of non power of two hires textures. it speed up loading + r2589 | Import the clipper code from gzdoom. Thanks to GrafZahl. PrBoom + r2592 | new defaults + r2593 | Type of useType in gl_hires.c/gld_HiresGetTextureName was unknow + r2594 | HAVE_LIBSDL_IMAGE stuff: sources now are compilable even if you + r2595 | configure.in: Test for SDL_image being installed. + r2600 | duplicated items are removed in prboom-plus.wad + r2601 | fix compilation problems with msvc2005 + r2602 | another fix compilation problems with msvc2005 + r2603 | definition for hires textures on MAC + r2605 | plus needs SDL_image + r2606 | Don't redefine M_PI when already defined; reduces compiler spew + r2607 | Find hires textures on more platforms and with less case sensiti + r2608 | Find hires textures in lowercase in cases of case-sensitive file + r2609 | fix SEGV in HU_Start() when gamemap is not initialized + r2610 | update docs + r2611 | History is no longer stored on the Internet + r2612 | strlwr is not portable + r2613 | Update doc/Makefile.am to include prboom-plus-* documentation fi + r2614 | Check for strlwr in configure.in (used in gl_hires.c) + r2615 | Fixed crash in M_ResetDefaults(). + r2616 | fixed bug if translucency percentage is less than 50, then all t + r2618 | Disabling transparency in OpenGL for original sprites which are + r2619 | typo + r2620 | allowing to bind mouse buttons through in-game menu was broken i + r2621 | fix bug during recording non-compatible demos (complevel >= 9) w + r2622 | do not update progress of loading hires more often than 35 times + r2623 | Support for DDS format of hires textures. DDSes can be loaded wi + r2624 | config.h: do not link SDL_image.lib if HAVE_LIBSDL_IMAGE is not + r2625 | do not update progress of hires loading if no real hires was loa + r2626 | configurable level of aniso (from in-game menu Off..16x) + r2627 | Better distinguish the PrBoom launcher from the PrBoom Plus one + r2628 | execution time of gld_Precache() -> stdout.txt + r2629 | Mouse look did not work on automap in overlay mode. + r2630 | Do not try to find hires textures in non existing dirs over and + r2631 | universal SNPRINTF instead of + r2632 | Applying SNPRINTF for whole code + r2633 | small fix for SNPRINTF (defines) + r2634 | wrong index in launcher. how it worked earlier? + r2635 | Technique of rendering to texture; Real black and white effect f + r2636 | some compatibility stuff with new invul effect and old hardware + r2637 | More 'real' invul effect without using colormaps (needs opengl 1 + r2638 | More informative message if frame buffer object (FBO) has not be + r2639 | GL_RGBA8 for SceneImageTextureFBO instead of gl_tex_format. Is i + r2640 | Do not try to create a frame buffer if GL_EXT_framebuffer_object ++ r2641 | Ability to use only the allowed CPUs. For example it is necessar ++ r2642 | %d for GetLastError() instead of %s of course + r2643 | gl_compatibility variable; all OpenGL extentions will be disable + r2644 | FBO: cleanup + r2645 | An alternative way of drawing the sky was added (gl_drawskys == + r2646 | Solution for Visual Studio 2008. + r2647 | Bump version number to 2.4.8.3 + r2648 | OpenGL: Fake contrast should add/remove one light level for wall + r2649 | Fix condition for bringing up the launcher if SHIFT key is press + r2650 | fix Visual Studio 2008 solution + r2651 | help_friends is zero by default now, because it super slow. I ha + r2652 | wrong GL_TEXTURE_MAG_FILTER for detail texture + r2653 | fixed bug in gl wipe + r2654 | Fix bug in new clipper code. Some unnecessary lines were drawn. + r2655 | Do not use FBO for all frames. Should be used only if motion blu + r2656 | Never grab the mouse when window does not have focus + r2657 | Check and fix wrong references to non-existent vertexes in SEGS + r2658 | More informative warning for previous revision + r2659 | Disable demo recording on levels with invalid nodes (see previou + r2660 | fix for error quit message in r2659 + r2661 | More precise rotation of sprite if render_paperitems is equal to + r2662 | Additional sector light mode was added (Options\General\Sector L + r2665 | Fix compilation on Mac OS X 10.5 when using 10.4u SDK. + r2667 | _exit() instead of ExitProcess() for "-resetgamma" + r2668 | Restore of startup gamma if window loses focus + r2669 | fix compilation problems + r2672 | Unknown gl nodes will be ignored instead of I_Error message + r2673 | Avoid zdoom's code in additional sector light mode. Now it is fr + r2674 | optimization of sprite rotation + r2675 | Restoring original visual behaviour for demo_compatibility. View + r2676 | Ability to play wads with wrong flat names. Unknown flats will b + r2677 | Make missing sounds non-fatal. Makes sense for doom.wad v1.2 for + r2678 | fix bug of r2674 + r2679 | Revert r2530. Icarus map14 and Hell Revialed map20 will have pro + r2680 | Refactor the intercepts overrun code so that it should work prop + r2681 | Launcher (win32): Now you can associate the current EXE with DOO + r2682 | New logic in music detection code from chocolate-doom. + r2683 | sky property-transfer linedef types will be applied for boom com + r2684 | Try to fix SmoothEdges on big endian systems; fixes sprite issue + r2685 | refix SmoothEdges for little endian and tabs replaced with space + r2686 | Fix bug with sky in the bottom part of an imaginary skybox aroun + r2687 | opengl: more correct (similar to software) drawing of skies with + r2688 | Clear the screen to black in V_AllocScreen(). Makes sense for wi + r2689 | there is no restriction for length of midies. fix for r2682 + r2690 | process_affinity_mask is 1 by default + r2691 | another try to fix problem with sky on map14 @ Icarus.wad sector + r2696 | Fixed some warnings about uninitialized/unused variables and shu + r2700 | Merged r2423:2699 from trunk (hicolor) + r2701 | Advanced syntax for -geom command-line switch + r2702 | Fix compilation. GCC doesn't like casted lvalues: (byte *)psrc - + r2705 | fix for latest merge r2700 and r2440 + r2707 | added description to r_screenmultiply.c.h; + r2708 | Screens works now for non 8-bit modes in software (16-bit, 32-bi + r2712 | dogs + r2719 | Merged r2699:2718 from trunk + r2723 | Speed up in R_PointToAngleEx() in software mode + r2726 | Merged r2718:2725 from trunk + r2727 | gl_render_precise now is known as render_precise and applies to + r2728 | comment added for r2727 + r2729 | better code for r2723 + r2730 | lower memory usage if glcolormaps are used + r2731 | Clean the memory from GL textures, etc in I_ShutdownGraphics + r2732 | Simplification of the code for the previous revision r2731 + r2733 | Speedup of level reloading in OpenGL mode + r2735 | Fixed error in OpenGL if upper texture on the linedef with "Tran + r2742 | Merged r2725:2741 from trunk + r2743 | Fixed bug with GL gamma in GZDoom mode for some buggy drivers: + r2744 | Use MEM_SEEK_SET for memio, not SEEK_SET (from chocolate-doom an + r2755 | Fix for r2733 (Speedup of level reloading in OpenGL mode): + r2756 | Remove duplicate declaration of G_ReadDemoHeader in e6y.h + r2757 | Fix {m,c}alloc_IfSameLevel build failure + r2758 | comperr_hangsolid: Pretend there's a fake ceiling under a body + r2761 | explicit type was missed in r2733 + r2762 | Removal of duplicates from cfg. + r2763 | Fix for r2682: non-MUS/MIDI music (MOD, S3M, etc) was broken the + r2764 | Fixed crash (Not enough storage is available to process this com + r2765 | Fixed crash if numbers of wads/lmps/dehs is greater than 100. + r2766 | Fix vanilla unprecise calculation (vibrations) of the texture co + r2767 | Fix wrong processing of the "Blue Armor Class" string from a DEH + r2768 | Fix wrong processing of the "Green Armor Class" string from a DE + r2769 | comments for the previous two revisions + r2770 | Ability to play demos recorded with old prbooms with wrong proce + r2771 | Previous fixes for wrong processing of armor classes from a deh + r2772 | Pump the event loop (SDL_PumpEvents) in I_InitInputs() before fi + r2773 | New 'mixed' lighting mode for opengl. It uses gamma ramps like i + r2774 | hardware gamma stuff has been moved to gl_gamma.c + r2775 | Fix build failure, add new file gl_gamma.c to src/Makefile.am + r2776 | Fix compilation with MSVC2008 + r2777 | There is no more necessity to restart glboom after change of tex + r2778 | Fix compilation problems for nongl configurations + r2779 | More accurate patch drawing. Predefined arrays are used instead + r2780 | fix gamma in glboom mode (introduced in r2773, r2774) + r2781 | refix "Respawn frame" part of r2117 + r2782 | "Flashing HOM indicator" option did nothing. Fixed. + r2783 | Ability to use negative numbers with -skipsec switch. "-skipsec + r2784 | Fixed crash in case if WI_drawOnLnode will try to get access to + r2785 | Added demo compatibility with chex.exe + r2786 | The latest column of patches was not drawn. Introduced in r2779 ++ r2787 | The level of compatibility at which fix for comp_stairs should b + r2788 | fixed stupid mistake in chex stuff (r2785) ++ r2789 | key_setup was not saved in cfg + r2790 | detection of more unsupported demo formats + r2791 | Texture bug. GL only. Example in ksutra.wad map15, "COOL!" area. + r2792 | videodriver name now saved in cfg + r2793 | The fourth episode for pre ultimate doom complevels is not allow + r2794 | More precise weapon drawing in GL: there is no more line of grap + r2795 | make GL stuff from the previous revision (r2794) under GL_DOOM d + r2796 | One more 'ATI sucks' revision. + r2797 | fixed anisotropic filtering. 16x now is 16x instead of 4x + r2798 | Prepare for release 2.4.8.3. + r2799 | missed change log entries + r2801 | Text mode emulation code from Chocolate-Doom + r2802 | New application icon and loading it through SDL + r2803 | Updated MSVC project files for using text mode emulation code + r2804 | ENDOOM support using text mode emulation + r2805 | missed icon.c and python script from chocolate-doom for generati + r2806 | Add textscreen to autotools + r2807 | Add icon.c and doomkeys.h to autotools + r2808 | quick and lazy fix for memory overrun introduced in r2779 + r2809 | Restore version banner output on exit + r2810 | -r2766 + r2811 | Separate out list of extensions to be added to music_tmp + r2812 | Construct temporary filename from music_tmp with extension added + r2813 | I_ShutdownMusic: delete music_tmp and all its possible extension + r2814 | Minor music_tmp-related fixes + r2815 | prevent recursive exits + r2816 | stop music before shutdown + r2817 | New algorithm for detection of fake flats and ceilings. It is mu + r2818 | Fix build failure, add new file gl_missingtexture.c to src/Makef + r2819 | r2817+: better handling of fake floors/ceilings for sectors with + r2820 | bump version to 2.4.8.4 + r2821 | Update docs and change log + r2822 | Fixed compilation on Mac? + r2823 | added gl_gamma to Rakefile + r2824 | Shift Mac build to 10.5 target + r2825 | Remove headers from mac build + r2827 | bump version to 2.4.8.5 + r2828 | Fixed crash when nodes used for in-wad demo playback are not equ ++ r2829 | Set processor affinity under non-Windows platforms using the POS ++ r2830 | random symbol after #endif ++ r2831 | I_SetAffinityMask() has moved to i_main.c from e6y.c ++ r2832 | Fix compilation on *nix + r2833 | Eating of Alt-Tab event to prevent switching to the automap afte ++ r2834 | Stupid bug in deh processing. Thanks Death-Destiny for the hint + r2835 | Fixed old bug in searching responsefile. + r2836 | fixed crash with @responsefile + r2837 | change log for 2.4.8.5 + r2838 | bump version to 2.4.8.6 ++ r2839 | For now, set a no-op I_SetAffinityMask for the Mac + r2850 | Resolution limitation is removed. Memory usage has decreased as + r2852 | fixed crash if screenblocks is not equal to 11 (introduced in th + r2853 | fixed hi-color modes for r2850 + r2863 | fixed bug with caching planes heights (introduced in r2850) + r2864 | "PrBoom-Plus" -> PACKAGE_TITLE + r2865 | Temporarily define PACKAGE_TITLE in configure.in and MAC/config. + r2866 | launcher_enable variable now has three values: "never", "smart" + r2891 | Status Bar was affected by light level of the sector on some on- ++ r2892 | Fixed crash with FixedDiv(-2147483648,x) ++ r2893 | added comment for the previous revision r2892 to know what happe ++ r2894 | 2892++ (more detailed comment for crash in FixedDiv) + r2907 | Unused ConvertMus() is removed. There are no more compiling erro + r2912 | fix for r2793: do not try to show the fourth episode in menu for + r2913 | Trying to optimise screen pitch for reducing of CPU cache misses + r2914 | changed formatting of stdout for the previous revision (r2913) + r2915 | fixed bug in r2817? + r2916 | another try of r2766: Fix vanilla unprecise calculation (vibrati + r2917 | fixed wipe in software + r2934 | New super-cool algorithm for drawing ceiling/floor texture into + r2937 | sectorclosed array is not necessary anymore. it is sector's flag + r2938 | Additional check for super-cool algo from r2934. We do not need + r2939 | Merged r2741:2936 from trunk (without *nix stuff) + r2940 | bump version to 2.5.0.1 + r2941 | precache level with normal demoplayback and level_precache 1 + r2942 | Merge r2841-2849,2862,2922,2928 from trunk + r2943 | Do not preprocess GL data during skipping, because it potentiall + r2944 | Hack PCSOUND SDL driver to compile without SDL_mixer + r2945 | Remove PC speaker sound menu options in the absence of SDL_mixer + r2946 | Merge r2910-2911 from trunk + r2947 | Push bleeding floor/ceiling textures back a little in the z-buff + r2948 | fix for r2943 + r2949 | don't write mus/midi to file. just play it from memory + r2950 | using MIX_DEFAULT_FORMAT constant instead of check for endian + r2951 | checking rw_midi for NULL before SDL_FreeRW(rw_midi) + r2952 | don't use SDL_FreeRW for rw_midi at all + r2953 | +SDL_InitSubSystem(SDL_INIT_AUDIO) in I_InitSound + r2954 | '\n' is added in warning in W_SafeGetNumForName() + r2955 | Don't use MIX_DEFAULT_FORMAT when HAVE_MIXER is not defined + r2956 | Trying to use Mix_LoadMUS() if Mix_LoadMUS_RW() is failed + r2957 | Define USE_RWOPS before including SDL_mixer.h + r2958 | old part of R_ShowStats is removed + r2959 | Update docs and change log + r2960 | removed unused include + r2961 | Rename vldoor_e types 'open'/'close' to 'openDoor'/'closeDoor' + r2962 | check for USE_WINDOWS_LAUNCHER in corresponding places instead o + r2963 | Add -lglu32 to link command for cygwin/mingw32 + r2964 | added USE_WIN32_PCSOUND_DRIVER define for ability to build with + r2965 | changelog + +-------------------------------------------------------------------------- + +Unresolved issues mentioned in the Doomworld thread since 2.4.7 +http://www.doomworld.com/vb/source-ports/23525-prboom-issues-thread/ + +- http://www.doomworld.com/vb/post/644107 + mbf options lumps (bad idea, dehacked changing cheats is annoying enough) +- http://www.doomworld.com/vb/post/648619 + suggestion to steal chocolate-doom's netgame code +- http://www.doomworld.com/vb/post/654924 + cheat codes don't work in -altdeath in single player but do in vanilla +- http://www.doomworld.com/vb/post/699446 + http://www.doomworld.com/vb/post/700314 + http://www.doomworld.com/vb/post/712531 + several different people complaining about the music diff --git a/config.h b/config.h new file mode 100644 index 00000000..2059507e --- /dev/null +++ b/config.h @@ -0,0 +1,93 @@ +/* config.h. Generated from config.h.in by configure. */ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to be the path where Doom WADs are stored */ +#define DOOMWADDIR "/usr/local/share/games/doom" + +#define SCREENWIDTH 320 +#define SCREENHEIGHT 200 +#define SCREENPITCH 640 + +#define SURFACE_WIDTH 320 +#define SURFACE_BYTE_PITCH SCREENPITCH +#define SURFACE_SHORT_PITCH SCREENWIDTH +#define SURFACE_INT_PITCH 160 +#define SURFACE_PIXEL_DEPTH 2 + +/* Define to 1 if you have the header file. */ +#define HAVE_ASM_BYTEORDER_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +#ifdef HAVE_SDL + +/* Define to 1 if you have the `SDL_mixer' library (-lSDL_mixer). */ +#define HAVE_LIBSDL_MIXER 1 + +/* Define to 1 if you have the 'SDL_mixer' library (-lSDL_mixer) and Timidity. */ +#define HAVE_MIDI_MIXER 1 + +/* Define if you have the SDL net library -lSDL_net */ +#define HAVE_LIBSDL_NET 1 + +/* Define if you want to use the SDL net lib */ +#define USE_SDL_NET 1 + +#endif + +#ifdef HAVE_NET + +/* Define to 1 if you have the `inet_aton' function. */ +#define HAVE_INET_ATON 1 + +/* Define to 1 if you have the `inet_ntop' function. */ +#define HAVE_INET_NTOP 1 + +/* Define to 1 if you have the `inet_pton' function. */ +#define HAVE_INET_PTON 1 + +#endif + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Name of package */ +#define PACKAGE "prboom" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "prboom" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "prboom 2.5.0" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "prboom" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "2.5.0" + +/* Set to the attribute to apply to struct definitions to make them packed */ +#define PACKEDATTR __attribute__((packed)) + +/* Version number of package */ +#define VERSION "2.5.0" + +/* Define this to perform id checks on zone blocks, to detect corrupted and + illegally freed blocks */ +#define ZONEIDCHECK 1 + +/* Define to strcasecmp, if we have it */ +#define stricmp strcasecmp + +/* Define to strncasecmp, if we have it */ +#define strnicmp strncasecmp diff --git a/data/graphics/boxcc.ppm b/data/graphics/boxcc.ppm new file mode 100644 index 00000000..44f63165 --- /dev/null +++ b/data/graphics/boxcc.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +C3?/3+?/C3?/3+?/C3?/C3?/?/3+?/3+C33+?/3+?/?/?/?/?/?/3+?/C3?/C3?/?/?/3+?/?/?/?/?/?/?/?/?/3+?/3+C33+?/3+?/?/C3?/C3?/3+?/C3?/3+?/C3 \ No newline at end of file diff --git a/data/graphics/boxcl.ppm b/data/graphics/boxcl.ppm new file mode 100644 index 00000000..f7b9a1a7 --- /dev/null +++ b/data/graphics/boxcl.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +#+7#C3C3C3?/C3#+# 7#C3K7C33+C3/7# 7#K7C3C3?/?/#+# 7#?/C3?/?/?//7# 7#C3C3?/3+?//77#C3?/C3?/?/#+# 7#C3C33+C33+/7# 7#C3C3C3?/3+ \ No newline at end of file diff --git a/data/graphics/boxcr.ppm b/data/graphics/boxcr.ppm new file mode 100644 index 00000000..e777dd56 --- /dev/null +++ b/data/graphics/boxcr.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +3+?/C3C3C37##+3+C33+C3C37## #+?/?/C3?/C37## /7C3C3K7K7C37## #+C3?/C3K7K77## /7?/?/C3C3K77#/7C33+C3K7C37## #+C3?/C3C3C37## /7 \ No newline at end of file diff --git a/data/graphics/boxlc.ppm b/data/graphics/boxlc.ppm new file mode 100644 index 00000000..cb67f181 --- /dev/null +++ b/data/graphics/boxlc.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +?/?/3+?/C3?/C3?/?/?/3+?/?/?/?/?/?/?/?/?/3+?/3+C33+?/3+?/?/C3?/C3?/3+?/C3?/3+?/C3/77?'/77?'/7/77?'7?'/7#+/7#+#+/7#+/7# # # # # # \ No newline at end of file diff --git a/data/graphics/boxll.ppm b/data/graphics/boxll.ppm new file mode 100644 index 00000000..fc5cdc4e --- /dev/null +++ b/data/graphics/boxll.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +#+# 7#?/C3?/?/?//7# 7#C3C3?/3+?//77#C3?/C3?/?/#+# 7#C3C33+C33+/7# 7#C3C3C3?/3+#+# #+7?'/7/77?'7?'/7#+#+#+#+/7#+/7#+# # # # # \ No newline at end of file diff --git a/data/graphics/boxlr.ppm b/data/graphics/boxlr.ppm new file mode 100644 index 00000000..6e399a95 --- /dev/null +++ b/data/graphics/boxlr.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +C3C3K7K7C37## #+C3?/C3K7K77## /7?/?/C3C3K77#/7C33+C3K7C37## #+C3?/C3C3C37## /7/77?'/77?'/7/7# #+/7#+/7#+/7#+#+/7# # # # # #+ \ No newline at end of file diff --git a/data/graphics/boxuc.ppm b/data/graphics/boxuc.ppm new file mode 100644 index 00000000..d298b2e0 --- /dev/null +++ b/data/graphics/boxuc.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +/7#+/7#+#+/7#+/7# # # # # # / / / / / / / / C3?/3+?/C3?/3+?/C3?/C3?/?/3+?/3+C33+?/3+?/?/?/?/?/?/3+?/C3?/C3?/?/?/3+?/?/?/?/?/ \ No newline at end of file diff --git a/data/graphics/boxul.ppm b/data/graphics/boxul.ppm new file mode 100644 index 00000000..1301b8d8 --- /dev/null +++ b/data/graphics/boxul.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +7?'#+#+#+#+/7#+/7#+# # # # # #+# / / / / / / #+7#C3C3C3?/C3#+# 7#C3K7C33+C3/7# 7#K7C3C3?/?/#+# 7#?/C3?/?/?//7# 7#C3C3?/3+?/ \ No newline at end of file diff --git a/data/graphics/boxur.ppm b/data/graphics/boxur.ppm new file mode 100644 index 00000000..15e0b7c5 --- /dev/null +++ b/data/graphics/boxur.ppm @@ -0,0 +1,4 @@ +P6 +8 8 +255 +/7#+/7#+/7#+#+7?'# # # # # #+/ / / / / / # #+3+?/C3C3C37##+3+C33+C3C37## #+?/?/C3?/C37## /7C3C3K7K7C37## #+C3?/C3K7K77## /7 \ No newline at end of file diff --git a/data/graphics/dig0.ppm b/data/graphics/dig0.ppm new file mode 100644 index 0000000000000000000000000000000000000000..e0c2e58faa16a40ab8edc2c08b555c6c6932544a GIT binary patch literal 116 wcmWGA<1$q+=Q1)i_5X40i!OjKig6KskfM^7h05N9~0ssI2 literal 0 HcmV?d00001 diff --git a/data/graphics/dig2.ppm b/data/graphics/dig2.ppm new file mode 100644 index 0000000000000000000000000000000000000000..044ccc08e815223d623532dc69c31000767b83b1 GIT binary patch literal 116 wcmWGA<1$q+=Q1)iQvhK=^g?9e8j%%4^)oO40MAMj AYXATM literal 0 HcmV?d00001 diff --git a/data/graphics/dig4.ppm b/data/graphics/dig4.ppm new file mode 100644 index 0000000000000000000000000000000000000000..e462df07114f7778a3d97a73ae0124a7e0d72980 GIT binary patch literal 116 zcmWGA<1$q+=Q1)ia2x3D3SS6GT(}l@J)(bKOrWXJZ?hx7l literal 0 HcmV?d00001 diff --git a/data/graphics/dig93.ppm b/data/graphics/dig93.ppm new file mode 100644 index 0000000000000000000000000000000000000000..714c9ebebc485f07d4c9af4ccd87faac40104502 GIT binary patch literal 116 rcmWGA<1$q+=Q1)i_5X40i!NY~>MN$mb2{H`;9~ltZ literal 0 HcmV?d00001 diff --git a/data/graphics/diga.ppm b/data/graphics/diga.ppm new file mode 100644 index 0000000000000000000000000000000000000000..a7f5a43535c478b7911b8b770d63f61bc1a9df7e GIT binary patch literal 116 zcmWGA<1$q+=Q1)iy#5~ literal 0 HcmV?d00001 diff --git a/data/graphics/digb.ppm b/data/graphics/digb.ppm new file mode 100644 index 0000000000000000000000000000000000000000..e6ef5625d4be9a9b5d0104361865ad81d4fadeb8 GIT binary patch literal 116 vcmWGA<1$q+=Q1)iN#$lZ&huWGoT@0Buzf<^TWy literal 0 HcmV?d00001 diff --git a/data/graphics/digd.ppm b/data/graphics/digd.ppm new file mode 100644 index 0000000000000000000000000000000000000000..c57e815300ebcc8101a918ed08580a3ad818cd95 GIT binary patch literal 116 zcmWGA<1$q+=Q1)i;FA~ZC literal 0 HcmV?d00001 diff --git a/data/graphics/digf.ppm b/data/graphics/digf.ppm new file mode 100644 index 0000000000000000000000000000000000000000..58db7b66396e2cd2f0d0983ffafb696c60e26bc4 GIT binary patch literal 116 scmWGA<1$q+=Q1)ig&T95WOG+85jVU CxDtl| literal 0 HcmV?d00001 diff --git a/data/graphics/digi.ppm b/data/graphics/digi.ppm new file mode 100644 index 0000000000000000000000000000000000000000..0664d3a4e0f8807cb124f14344eec3f63138d3a7 GIT binary patch literal 116 scmWGA<1$q+=Q1)i#8 literal 0 HcmV?d00001 diff --git a/data/graphics/digl.ppm b/data/graphics/digl.ppm new file mode 100644 index 0000000000000000000000000000000000000000..ecc76465728675c17ca74f374b23c40d3ca9b7ee GIT binary patch literal 116 qcmWGA<1$q+=Q1)igz+eAaj5KVm<={ E01I&xCjbBd literal 0 HcmV?d00001 diff --git a/data/graphics/dign.ppm b/data/graphics/dign.ppm new file mode 100644 index 0000000000000000000000000000000000000000..c150ddb2b4bdd0b3300a719754c26692ddf4c5b8 GIT binary patch literal 116 zcmWGA<1$q+=Q1)i{#LopO11kmqOn(s_ literal 0 HcmV?d00001 diff --git a/data/graphics/digu.ppm b/data/graphics/digu.ppm new file mode 100644 index 0000000000000000000000000000000000000000..c2eaf1512d2c5d3046eda10144789dc5a3d1e4e4 GIT binary patch literal 116 tcmWGA<1$q+=Q1)ij5-9)x literal 0 HcmV?d00001 diff --git a/data/graphics/digv.ppm b/data/graphics/digv.ppm new file mode 100644 index 0000000000000000000000000000000000000000..0951ea55eac51a2fde1ceb01af6b3283dce5e266 GIT binary patch literal 116 zcmWGA<1$q+=Q1)iONX^=Pw08K^$0QH{} A$p8QV literal 0 HcmV?d00001 diff --git a/data/graphics/digx.ppm b/data/graphics/digx.ppm new file mode 100644 index 0000000000000000000000000000000000000000..e8550353086b685104a550dff00c618336a97d60 GIT binary patch literal 116 zcmWGA<1$q+=Q1)i;s0Rf=NaFT%m E0QnIU$p8QV literal 0 HcmV?d00001 diff --git a/data/graphics/digy.ppm b/data/graphics/digy.ppm new file mode 100644 index 0000000000000000000000000000000000000000..79d85815eee4222d8bba023c99c9dbe7f0053f70 GIT binary patch literal 116 zcmWGA<1$q+=Q1)i%Fd&%(NPxW;EFFLYAOJ!j00M9T6Tk#O0384+@adMibbZGj zIRHyXSJidu)cNu7-uJIJpFe&1`1$VU%iZ10c6<9;eA~7;UX#7^F5po2+jiQvzem6r z{QG?_FW@Zh^R#V$MgStrp!=YP~!Ox+XXO z9hj9ct8|GnVue!iU%Jyv@u|K7j4-^yeFWLhL52_Moo8T3A6cyeRGH1bJjKJ&G`vD2 zfluX+NBEq5k|ncLHi{A!*##g7PcL98B_e;XWHlnz7m0}&-VePjvh%0hsCcrb2w)CK zt-YLm(sxsgm1RZB`vQ~%mxd8X*(^YOo-Xz{+Ibg~493J53AGJP_wm=4Dj5)-faj93 z1-%_*lk%DWC7F}HZpgI8)5ogEG%8XGh)Q@Gn?4-pWwq4uJdN3s zGE5@9Y7!qBvnARB;Gng>b89>k^T;ZxzMhyEs!BUDKGA0k<#Dh+-b*7@Hvq#y3Qn-a z;DM5tK(DkTmd#**VYCoD>#!fc3S(rFq#lM+iKt1fm?RZ(c@FufXUJ9oKF`Mm$}bmV z$Sv(m-W58DEPp9h3@N4J=JhEn0G~9%ru5#Bz&<84WI-C`XpwatpEea*^Td1Mgc(lL zF%3hhq~dwO_$*m&9IjmKJT^`1x^;|t5-(%Rv5$4W?At(QijP(&{P11r;&Ow zOvFf&)DzH@m@?~NYH}L$7|D?3W#N5<6phb0v|z|qgs0I|#0M!wldQfGn=lO7K3eG{R(kF)H@FIg{P;Ecg`?hBjEFalUowo}DQ z7k}aeWejb^Hiimm;-e%!-U3Q)1{O%4^!ss^t|rBngDjBGh&AB|uDG^HGv%fr=TzOGmae8pYq6!prfN p!VJ@Q&IUJCP)(TyR8X)8d^S~ZsVQ?s(-ctsi{R}_<+FIk{{d44tq=eJ literal 0 HcmV?d00001 diff --git a/data/graphics/m_butt1.ppm b/data/graphics/m_butt1.ppm new file mode 100644 index 0000000000000000000000000000000000000000..28ce5e3a6860363811192af7f73e914294df016d GIT binary patch literal 688 zcmaKqD-Oa?5Jk~NAP@)yBnTt{QV@uWii!$IDv$^Sq7sQfRIq@`3Iq~?=mJ;(i$Jmj zBph<-q=7(k^D=MVnQuEAIeoX?cb$RjI=3uF_v5B4JlAxIN3S~1n-Jp=dnjKANVc7L zvgJhAIOq~?PZ;7qqI3ne%zipX;og%GodOx%sJWXD;Ulm0$P)#6NzVWt>SPK@CFIAW>l1;~?Dzlx literal 0 HcmV?d00001 diff --git a/data/graphics/m_butt2.ppm b/data/graphics/m_butt2.ppm new file mode 100644 index 0000000000000000000000000000000000000000..90d2195ca8a539e83894f54cab746c23fc27c0a4 GIT binary patch literal 688 zcmaKqD-Oa?5JjttKp-j*kRU1vGzEdEsHmubqymXRRw6492o``uAdmgwtuIeXHdzrX+Z@Y^pRAKw4^@bJEU_)ziK+Px^w8&B@}FZ_-B z{~`8r*iWr}ZtbPD*B+ewfLRjNd(@|~mhdp^59T7u`xR|@zP7A;?2P` zNOBmzBS;ybA7v{|p(@9>q9yb`jccS_QfRj2;bwx6ag>dl`EuZ?&z(!v^4fcC1%mC% zgap^V^w(4hlJ%yUfCwy6)0^q}jOz#G9uUy*OA~XsHK+*NB&W-lrfN$zt8Gq1R@6z$ zA>`6axfy(jUfW#zc3dGi=kb~a-b)Ux{W0DYXXoB>8#zLptTchnvRpqj4xIV3#0T=w!Qdzzm}4S~B;h!zd^nTKntWyW{vsc9dg`NwNu^4O+%`ZtFO zMK0MIu*{(OO7% z$_-itFl4fq-i{w~E--}ELDEvIQ=G@8F|dpXXkv+ILAK5D4}I3YI5KZSlHjo7w0TXo zfriE&rioU_e8Yw!VE(_?OnXMGRwi?G@vt*UBj$NLO6+Op)_L=qs?i7LAXW$(eg3Om zvUFTk>GI95iR=;t1DQqK1J!DDa$t@Y56FjJvJIgWq7$D$JzWE;m}!?ASt{W?GpNSt#aW4G-PD{!n4%vt1Vm*hmpP6!QB zV9D0aK<>rNc^b4NbLn3DYSxw(k$ZTJxR-1jSIDYyRki)9fo;n^GEbbDGcKP?-;+o7 EUlM%^BLDyZ literal 0 HcmV?d00001 diff --git a/data/graphics/m_colors.ppm b/data/graphics/m_colors.ppm new file mode 100644 index 0000000000000000000000000000000000000000..29b415d7e9048608663457618ff49a642aa3e88d GIT binary patch literal 56322 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
  • tjFF3`ws4tMj!x~;f3u}IK z5T($Tfxxw8AaHFN2wYnR0@s#-z@uyO=0+)WH-o^#nml(>iDK47*Hq2}(KU8jOQnco zz@ldjE<`N00(NG$BwvqpN;P4rD4ta%zb%S+mHezI=2h}>QOv94-J+OR$s0v6uaa{~ z^MYTu9lneDwv8IAI>nkcaVUAex#@~Bh6V^0Rh13@a zTz!GS)fWg{eSyFUbHtK29@aEdC8BHc=0+)WH-nUXem7%(EBX9x#{NdchL-0}S)%zt z!{{mb{Geg;QW191?Nbqg8NZic2s<6@w%%@d|IdycTO4Uobl-?roOEQ1|?F8yeIzf59U>2diKnV3Eowz(-(jW6ZU*69D zvu6y*q32dOR+y0%6U8Z@tig1=jV`!C88J}=9|_Qk%fRpe8A+@?D%3(Jg#HF5xNYp5SVt#|QidAM@C zS0WtWB>&J}@U1MwPz00h*K}-W#&O0;mJj(>hWbHtdJ(VIn-`4b?eb0fEQhyqy<}%s zcL_Yusx9Qqb0yV-!F+PRcVA--#W7K?rsnqoihp)`{@X>)Jac4V?#T;$Hdxtjac*Jm z6@E)EwLMk*+X=mzZ_=me)tKiCVHiaGTmR=vd)c12Z@mi-{MmEbty#IVEm)eT73+2C&+O6@L@ z)?$qo$fCSXj-dde&`AdJY+KwFz)CSBE24@~>l5Mt3o?Lo!O&j?QI&>Z^7*~AlDRNo z>rKja?v5~=!9yAcgJ2?c8sw=F%2sd} zUU_zPYW%L@5hU1EycXU|Eg$~S_J7>ou~L^DCErhE^v|bPFqG3o$v(ExTsest+11n) zN!)tZR$lu_+-5>D;FGOWHZPd1iVuYkjR%Y)+wW}#7wz)}RRE|+&B&UjBpM8;(4>MI zIr$=tssu}bs~}a(`?_LAdlp=5N_2sI1zv4*R$uMX(M0+>lNzMHFlD-!q_+^dadYZjFKq@__p_?1Rv~TcHk7$yx=8gf5mc<7 z4gicWStlx|NZ#&c0+*`bQ}PP@sVxe>WgfTInu=4H=OgCR-CaLLjZ?B=QhxYy-T>70 zws)vqA}z!$`55?W87FZ%ql5+nO64m1&@duWfz^^BFMW`ya^lgKy);@R3~ih+1O{2{ z649>kvcq}@@O4!~En1RJ&1i6)`30pa3lO7CiZ+h((BdKn^kLu}sI(RdJ|(Vu@o2-Z zQ2DVD3R;4zS&HAaAGUmPQcE_5P>IJ}8N!&LYnt9T)Gm>fFdOp;m4?9qcy1aveTqUO zGmdy>2u4y;L!oxrIjZV=sY}Jrs!Fy_Y$3;=)|DzA zaa3g{jfqo!Y|}*;HkPb$lq6DTrclKyxC@W-rCHP>L6zs=+gr%zMfG!~3I>?|Lo38g z9$IY9@W=wQwb_kBp}Jk9c6z}h*QnRYPj`9<7ywcVrO4(~Er$SLCfmpDM^qCHT-Szsd)2eMo0j&GYJx9N39-29O;p%6k)6d)(eMwN4_X)>h?L zAF~{_q$+W$dIerdl=Q0>EUH?OjU_mYQ_DHDO_Cb@R54YQBXRQo8db*A0WrFJem zs~R2%vyNd1+YB%)%oRhzsxa56&B%O@TRgAED?}K>**DeQRSS(sn%e;?h=`H&UvB?6}G2oB8@VsLXK;VCNggcz*?l7*-w?b;#YEN#&KnlG#836 z`EyA{3e!84ot=VRNx{t7 kAyPbXu4IRl>)Dbk@tI+coYj1((OiC|dq{?JW*7kf0(y|Wod5s; literal 0 HcmV?d00001 diff --git a/data/graphics/m_enem.ppm b/data/graphics/m_enem.ppm new file mode 100644 index 0000000000000000000000000000000000000000..59ce0015b625049b3dc7e6fa3eaea9fc3a8fe4eb GIT binary patch literal 4108 zcmd_q!EGB+3(W*5EY2XAWg|g!>ceX^PpmL?78#S+@x~;TSPqgm>k%6y@-kD!rmhG822-zNKYz?h zx|HB;K|(2-#9`JoA+4zM$QhNe*|a!S&s{>MtWHta)2JOXJQ-MT2t8iAqJfd{ zGQsmO*_Riq9y(0PkO)m1fT|;;QOP6w@$e8%UDdl$LL-LgmU=QCX|W6gDRb^e<2|k$ z5_4wws7$?AhKaAkL4a{GrPJS;LuheR;mp92X?f|pn3k!+$k*dWiM)F4_m+d;M5P)e z74u~BOOO5M$ENx9nGik$)bPo`q*rhq4x(xuN%(9hb8n>!oGO*Vo6RGugMiii@LMGA zMJt0gMSfS=}9oKbm8q2g6pW+qw#$gCT60E1~i&cj!`jQnf&kRx+S zSxPM6W00AL&&p}6b}%c`6#(Z(R;;t+xbDH*mK^^kA(EA1<#b^bRs M7I>X=OEpw~09xLBMF0Q* literal 0 HcmV?d00001 diff --git a/data/graphics/m_generl.ppm b/data/graphics/m_generl.ppm new file mode 100644 index 0000000000000000000000000000000000000000..69bf5fc7ed2c8688383d9eecfd69c27b7de96bcb GIT binary patch literal 4333 zcmd_q&23vj3{_*_q_4(81r-v_3PY>9R0POTmL30K&Us*)Xk%oTaKC5T#1fKw!&%($X_a(kyuR%x--$`>d?W&RtK z)EcZcaW<1_pZc!kOH=;Z+RxU0^?(KZI=BCBP52JeRi>^uQ?G`6 zrzSGNTR$V7Qzo(YiYZ6ojiwFLoFU5tCYMKWtvRdL^J_oH6-Ub=aPWBv;@X{(;${9z z^cJR+=SIg#P!l!n8uopk>p*+URdTZ z?f@B;+V|^a1Rb{643ioL4dC7Y|FJA>^*Low&TD4}7$!u1dsZJfh zz}Q=RsQrutObSe$f`y13FM09z{e*MvvuQcc?opolFCGP@do(rfh1nQ!%#h=LWQco0 z^)i)_kNc@_#tZ;X_5O1|+Q~XeX+QS%h?}J1+Glg#dLVGYH^U+%GbGum|J)%vF-s7j zmKZg4xsLyNVBd_SQ)rBSJ?%Wp0!q=O!_JxHY2H-`{v3>>-+PK=y}&wf;M{W(7e zpn2?@cf!b_!K#-&Qut=O%_GT7tNn!U)Vrx^3?fm>NSGWApZ%z2?Ai~S8MGvieCjPS zQIIDB3su+keh%G2V+J!@)JikG2vOrG3=_eTT$q#@a15WWTi=h2fTv+U$OMJb%X{|{ z1V|x-Ip(z!v?A5m<@c43CNT0@pow@njq1z;7yF4+s&tO?zw7Hfi4v)l;^LZllt$kX z<6rvBcALD6E=kJ3H0%fA9!;-EJD{Z=0u#(96uUgCG#6x(C=mgayaLvEnl~m{5vjxQ zNgsQo79v8UlazeA0Gj!Gci!e0e?eZ}A~Oz5r1(?sLc3qnNHnb%&^7L-WK&Y*i)=Qg z3$lzCsiqXN7YY2l!a2I&i0}R5@%(H+J8oHme{QBC97-UGUgYhJ*mo9 PZ)NsCF*hwz-Ru1WABu`B literal 0 HcmV?d00001 diff --git a/data/graphics/m_horsen.ppm b/data/graphics/m_horsen.ppm new file mode 100644 index 0000000000000000000000000000000000000000..5b46c80faff72df6eca656a273a03ebb924bfe0d GIT binary patch literal 12302 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 UJQzIA9AwdM8y^Jp#$prv3!gJl4FCWD literal 0 HcmV?d00001 diff --git a/data/graphics/m_vbox.ppm b/data/graphics/m_vbox.ppm new file mode 100644 index 0000000000000000000000000000000000000000..3a4135e26bdb1b8df33ed50112a5b42ac91107e5 GIT binary patch literal 12917 zcmeI&u?fRK5JXYMSA|=UjBIyssk?DWZqo~bk3igTx&K82JJDlMr1zt(ujg&Mw{73s z*Qda=K<~Y}AD;qq0t}{tPk}5@o2tU`g{iRk6c`Jz6T-ObMS+E#5JXWR2(S}ED0@{DPQaQ@?N?vGAe|1OaK K;qJC35Cq^o8$}pcwqz^!5Xe0M3}68)fQ1kM24DaNumA$!0SJYBn$l}cX8aTX zyqs2QjcU4Us;m30{r&dh>iX-KtJ{m~o12SoyImi<%gc&B4_6)-O7FWy@FQbat#{b>op?!#6TuE|iRk-(g-0e=S`fIlnumFCpUlSmkx6#ijlQ^vHC?gNd zqCKvH_f5ZAFyo=vx91dMwH`h>4nCb?QtNHNrl5d>*{@0q8^XmRF z(P#?5HHJAfvNK6}YtQFMruVif&MaKjCopVQv~;ULlMlx%0H4NZ+X<_}XoFxD6@q!Q zf9=rvxm?MkK$%C_k*;BI1;ZHylCDHJ`wB(Ao5=orN$dmKLr8E++to z;V2!~apdQ)XPl7xDutze0#zKPnhVBPRkDPkl(pD)7OpA-`}94t)B==rBuf);tvEIV zJOD`ozKAm|2B}T23>fFs3^D7`O_e0 zSG7hNi@IAs){{0Q%P`#AprgcI37W_?vh#HHb|b)^%`?CiU=PdRtJ4?9yu7JqS2zh7 zue`+(bPyuwTAtx3FO4&lyiygwP_h<+PTiFRC;Ro(Xld=7(T@U}SCWTGP=;I8lrTl~ zweW-v+1X6Z^0`2FeNB|1ILR`HF7e58S%p1xCPJtTAX6f+hj}uWWK^R(yz0B84V*V{-oSYS=MB7X1AhR4ONKylBdf1LuO+3<{xzgXh*V^f#q9Ol24NVIatU8 z%f7(uP*X;v7+5}l1_v`Tbjh!Ou37mPq>(nN*1;lxrx1Ay7D%SC2{CjG+~<;iDh3&* z_qn_zgY@iP3}TsloP6-dVt`kbNn@tKpp6$m0)U0b1!Nci!^{96zi*j8SI&%?Pg0&KRY?NbRYfa{T(b|Vg37o^xY$^IDNW!7QVJR+Hf zdG>Zt`Ni01kiv90CClfCIPyE&u}907!wSDQ#tig{CQB8=ihGQJInfj(Km z@gec4*FU*3%#1xa4+C72&raC(io6A747oNe$cCMP4Dsmeg`C2_v?%4fm?b!syG~Da zeJiLD*v4`C+L!g4A3uIcFeBPcw>W_8+kl8CsiVJbDJOVYzt4!@#eD8+=fPOnBTHYq zQ8waN&uJU^K`SXpoS|M`FXXD=n54$4Rb(5!5Z+`eDFbL`$QnS6pwhokSQ(uE@GZQ^s7bx@XMf#c3n}954kahr=~{<+B2{Ml*{*GXF{ z$U>sy{M!3!#Eio8y6u=mE`?f%44>>d#oL+0<4RyIO(UYU3?1!cJdp)gd1IzglH)mA zLZ*NZ(TTY7Y{RSCaT}gWyrcwZEG@l|l&e}uJTgN*tzXw&OgIRD=lMwOKU~yy;BK{vNj!fcLk8Co84N;Hm L5(pb2j*A2UZ(SZP literal 0 HcmV?d00001 diff --git a/data/graphics/stbr125.ppm b/data/graphics/stbr125.ppm new file mode 100644 index 0000000000000000000000000000000000000000..c860d62ba9d82d994ccb38b8187d01cffa1b2bef GIT binary patch literal 203 zcmWGAH?Q3XgV>C&?Fs3w8xr90>4dfT6g6i%i~n(lFr8U^px8`27Y>Gt6I zq9qN!)Q@#;1uJPv;yzE0fD(aLCXqDsQPKd@!<6I3DUktddMxX(%xfTnpLG3iNdtnW z>)ShzK{Vhe-KFP2G{AISNsd^Sf$46*7RUFK;F)}kWH8wXmIXXK$Jw87`K|;$!MMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=c#KpzN#>dCV$jQmd%FD~Z!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;p>Vo0` literal 0 HcmV?d00001 diff --git a/data/lumps/crblue2.lmp b/data/lumps/crblue2.lmp new file mode 100644 index 0000000000000000000000000000000000000000..3c19d5ba937da37a1978e6291e8e5f7abaf1fc9b GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm<^ z&o0j}F)}kWH8wXmIXXK$Jw87`K|;?&MMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=g$jQmd%FD~l%+1Zt&d<-l!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;|-h(m# literal 0 HcmV?d00001 diff --git a/data/lumps/crbrick.lmp b/data/lumps/crbrick.lmp new file mode 100644 index 0000000000000000000000000000000000000000..ed4f68b7a6499a87e45f6dcaf9268fecedd7f57d GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;2 zE-o%GF)}kWH8wXmIXXK$Jw87`K|(G>MMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*);S5)>8~8XO)VA|xg#Dl9I*!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;!nsw#? literal 0 HcmV?d00001 diff --git a/data/lumps/crbrown.lmp b/data/lumps/crbrown.lmp new file mode 100644 index 0000000000000000000000000000000000000000..a47d222387382307ae04393bc3b87822a3af5c5e GIT binary patch literal 256 zcmZQzWMXDvWn<^yMC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y9(n*XuVh zG%_|ZH8Z!cw6eCbwX=6{bn16;b#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R4laf8gMu!$*!DJAUHisnci9o;!cx;-$-1 zu3o!-nN BeewVR literal 0 HcmV?d00001 diff --git a/data/lumps/crgold.lmp b/data/lumps/crgold.lmp new file mode 100644 index 0000000000000000000000000000000000000000..8a6d530fbce2589b89f4bb338747514e88a0c99e GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm3F)}kWH8wXmIXXK$Jw87`K|-fQMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=2prN6nqNAgvq@|^%rl+UC!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;TM}QIl literal 0 HcmV?d00001 diff --git a/data/lumps/crgray.lmp b/data/lumps/crgray.lmp new file mode 100644 index 0000000000000000000000000000000000000000..384f2d491de5269ef341d9ef6054d25f7fd76730 GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;& zZZ2*xF)}kWH8wXmIXXK$Jw87`K|*dsMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)<7QdCx0T3lXWVq|7$YHV)6!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;j_MC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y7i6*Q+-$ zG%_|ZH8Z!cw6eCbwX=6{bgFl8b#wRd^z!!c_45x13RUz zF>}`JIdkXDU$Ah|;w4L$Enl&6)#^2C*R4m_f8gMu!$*!DJAUHisnci9o;!cx;-$-1 zu3o!-MC+6cQE@6%&_`l#-T_m6KOcR8m$^Ra4i{)Y86xU+=zw zp^>qPshPQjrIodft)0DtqtksCS2uSLPcLsDUqAnVz@Xre(6I1`$f)R;*tqzF#H8eu z)U@=B%&hF3+`RmP!lL4m(z5c3%Bt#`+PeCN#-`?$*0%PJ&aUpB-oE|`6DLicGIiSY z88c_io-=pe`~?daEnc#8+42=DSFK*NcHR057cX7Da`oEv8#iy=zH|59{R0ON9X@jO z*zpr5Pn|w<_S|`N`3DalJ$~}^+4C1KU%h_w_TBprA3uHm^7Y&IA3uNn{`2?We*kFl BgfajC literal 0 HcmV?d00001 diff --git a/data/lumps/crred.lmp b/data/lumps/crred.lmp new file mode 100644 index 0000000000000000000000000000000000000000..79d0e57754df6c1da901988d6887bf9e2ee938f6 GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJmRaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=Iv9hzYwYImoxw^Z&y}rM|!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>-PLV>aX literal 0 HcmV?d00001 diff --git a/data/lumps/crtan.lmp b/data/lumps/crtan.lmp new file mode 100644 index 0000000000000000000000000000000000000000..08d070f066a498e04e1e9c9b23b3813f3a4d652f GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm;Y zEly4_F)}kWH8wXmIXXK$Jw87`K|)SMMMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*);yGBh?gIy^o=LPSPLN=#0`!NSAD#m2|T z$;!*j&Cbuz(bCh@)z;V8+1lIO-QM5e;o{@u<>u$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;rR(NFq literal 0 HcmV?d00001 diff --git a/data/lumps/cryellow.lmp b/data/lumps/cryellow.lmp new file mode 100644 index 0000000000000000000000000000000000000000..edcdca740c64fa8b185aa2c86097378ce0e7acac GIT binary patch literal 256 zcmV+b0ssC00RjUA1qKHQ2?`4g4Gs?w5fT#=6&4p585$cL9UdPbAtECrB_<~*DJm=H z=Pu_kF)}kWH8wXmIXXK$Jw87`K|<$5MMg(RNlHshO-@fxQBqS>RaRG6Sz23MU0z>c zVPa!sWoBn+X=-b1ZEkOHadLBXb#`}nd3t+%eSUv{fr5jCg@%WSiHeJijgF6yk&=^? zm6n&7nVOrNot~edp`xRtrKYE-sj922t*)=&;Njuo;^X7wu$;>FVq3?e6dJ@$&QZ_4fDp`TG0( G{r>;CrG*jz literal 0 HcmV?d00001 diff --git a/data/lumps/endboom.lmp b/data/lumps/endboom.lmp new file mode 100644 index 00000000..62805b9f --- /dev/null +++ b/data/lumps/endboom.lmp @@ -0,0 +1 @@ +                                                                                       @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @BO OOO OOO OMO O-O OaO ODOOOOOMO OeOnOgOiOnOeO OmOoOdOiOfOiOcOaOtOiOoOnO @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @     @ @ @ @ @ @ @ @ @ @DKeKsKiKgKnKeKdK,K KiKmKpKlKeKmKeKnKtKeKdK KaKnKdK KpKrKeKsKeKnKtKeKdK KtKoK KyKoKuK KbKyK KTKeKaKmKTKNKTK K @ @ @ @ @ @ @ @                                                                                     This is the Phase I release of the BOOM executable, a DOOM source code      modification by TeamTNT.  Please review the text files for information       about how to play with and edit for the new engine features.                                                                                                          (C) Copyright 1998, TeamTNT - All rights reserved                                                                                                                           Thanks for playing!                                                                                                                DOOM is Copyright (C) id Software and the source is modified with              their permission based on the public release in December 1997.                                                                                                 Keep up with TeamTNT goings on.  See our web page at:                                      <http://www.teamtnt.com>                                                                                                                                                                                                \ No newline at end of file diff --git a/data/lumps/gammatbl.lmp b/data/lumps/gammatbl.lmp new file mode 100644 index 00000000..27f345f2 --- /dev/null +++ b/data/lumps/gammatbl.lmp @@ -0,0 +1,3 @@ + +  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ +  !"$%&'()*,-./012346789:;<=>?@ABCEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !#$&'(*+-./02346789;<=>?ABCDEFHIJKLMNOPRSTUVWXYZ[\]^_`abdefghijklmnopqrrstuvwxyz{|}~ "$&()+-/124579:<=?@ACDFGHJKLMOPQRTUVWXZ[\]^_`bcdefghijklmnopqrstuvwxyz{|}~ $'*-02579<>@BDEGIKLNPQSTVWYZ\]^`abdefgijklmnpqrstuvwxyz{|}~ \ No newline at end of file diff --git a/data/lumps/sinetabl.lmp b/data/lumps/sinetabl.lmp new file mode 100644 index 0000000000000000000000000000000000000000..d190722fc3241c92525921a04e651dec426aa780 GIT binary patch literal 40960 zcmbTDn=ks34O2v7mze&AW$B? zumooi5-SiWjjmXLlZYET5Ga8T_zp)ABTgVt1nm%ogLs2NaRY%?n2No4fxPhofv+$D zzu*aS#t#GaRk(x{i2{KN2*Wa* zL*m4NKpAw$BK(Q?NdkeA=!CgAhFD1hfnxXuGjRxS@nNz+pf#ppA6_7D@<5;&CgK-7 zLCzGs7mUFU+(*`wfk0i1z-HV;`c#2H4GhLQT*G_s1p;57FIM0JlBNy>%ApsQ;0zL` z2?Rbx7tF^A#7-Ls6h|m#;V|Ce!*slEOv66BK;HCx2bh3ec#Iqw0)a0Pj%~P$%oziL z+8Bn7_y=h-1p-wu0IP8s$ukE6pW|CB#a{@?!nK30Sb#qeJ1egdp_qlkc!v+)=Q_bu z?8S5B$rcDS!8rVchsc(lYXT#&1-FnPM<7rggRmA?kuql>P!YYc3}=xjS0L~ix?%zT zK^YZy%7&hQud7@?SjLwJjV1^K%%8NcEwau(v6LO8bJ4l;hoZvlg`7FUt7FxNf8 zuoP#I@S{MW6guHM97Ui=AW#^sF%5h13?CE?1R5g(+i@3}iUk6-Fa+yx6)B7J_uyMB z!5Jj@nBNjQVh)br9SW7;{a`YF#S`TCBoJtTQP_f;NLP~U3jMJX7m%bBpBuVjK8_>i zr@VZ$K_vF#89pe@_k%ImhTF*S8P^5|VihhTX&L@Zbi)FiK+LlInP`J)*o$c7D#z!J z(b$TcNLQZMiGEmtb4c_#zejY&claIeP^dy6&;k>&3lH&r#Xz7AhGIRgB4s6hKj?|? zaT2k=2n33vEh4cO(a2Sq-zi36Gj1SF74948jioq^cvZPiBNS2Cj~B>Yjn4_;*ovD- zTb*kbeXtB?5WfchJwg$M{dkTKYVw(2G`8RdQrF^nf-o$>Da5JGeH3jGi9L9V9Ci2& zV>mY8DpJ(t9*6E&fMW>MT$8+Rt5eU@BaIC{+ByGvP6`e2(2k;!Z zTJi5;r1F5=mt)dHNV?Uyitq1o>48U@nLad&=4m86UY{oSt>&171_Lz>{c!-SOa=jr8 z3vm>0kUNa~C5B=(&LUoK?pbJwarhB`BSjycbLfB>*o_Cs*q7fpdSO0};3abQ<9>vJ z_yMO7vp>HFG{GpW$3-L@z;}+;n1F5g2gwKWe$XC~_yu>7b`Z}Abi-`?h9}58m}>-K zSb!sVfgD5l4ACD;Z~||UXDHV-24e+IA;z#kpa2?SI9B5<;tc0AM^lW#I-Ezm5qw@~ zjxpGXOGq%1-!obu0-JCdA)|P&Xo<1dj4KEk&Hs*87>mugf{<|jceKP+O3D|@Jun-4a1W^?c`hRqlkg+1B4j#$2fo5ctimY-@xcs!x9Ecf zIDp4UAH{tI9WfO|#;4PiTh;*o=#aJBQy0 z8ekw6;|QK2<9A&5=!B`*j;lyGm+uyhF$BwS6wi@)9%EK?#x!ilH6)zRe}hICjHUP; z(a5-f_lXXejIFqYxC^;fP#^uV0Q>O(sTc8_!PkhuTAao^n1Bs9gSW`Ol6xOIV+yw5JYuZkdq*{NLnOB05@N6B zcZHhhi5b{|D~P*>>l?Mu6H(ZSD~P+6{~v0hCuU#=E+h6j{!G+BcTC4NTtv+EycSeN z7fiusoW(n2+rStL9WVjw@h4s)(?*_2XoGO9#8Es!>PB z*5U-7BF%QjOlXQhn2%p^9dUQ?not#;F%j!<0#A{8Cu3GL!2ry~&$xnEKXHAd5<)Qs zD{vV1k@RPNXQ+!_h{R@`!Aqpy#XS&TVG!nF7p@@YFI;1&fVLQgrPz;~NboDy1gfDU z#$gqX;69S>W~__a=!VHyhvRsR6nprdP!ByZ6&vseo+8y=K5x`VFHFNmoWv8P+{Zl> z_0SVjupTGy2+4ory`wg|VItPxciczf{eeJ!R7XcdU^x!pCgL67J4Hpb!Eh|ZFSv{# zG9To(i>By@nb?X`h(@YIyhqeQS4_Z49KtQcJIvpK3TTa?n1`J>hnGlmgl917qX#Bo z6%OGR;{DEe2cM%Q24N1i;WVO=;wa+>)IdjsV+nTSGTtG>F|IRwiJq8*RXB(nh<%(f zGd@F8^g$%n<0$SR{s~?S%A+~@V+JI&?jX)N z{!V;?dgzSNSb%LfiTj9up6>>wP#;|ojs@6`lemX?7x>KZ3F@H}Mq)0u;y7+2)7GOrOOQ5PLB470Ev2XPgzkm3sW z7L-R5^gsj_;zu0EEx7QT7KQKynj;M3u^2n>2ks!|H9m8Eh%e9rVHl6^u^lIH3qhp5 z&ig_IG(~rWV;(l)Fs|YylK#y#jZaY*?J*dUSb<-02KN#BAFfG!h)VbhJrIt$*nk7L zglHtZ!FPt@sD@Su!#FI!W*o*9JVT@H`B4V-&<=eu4)d`A`|ua;B8U|CxCfyGs-Zc$V>qT`Ikw|>T)`8>z0Y-od?<}t zXpLSNg_&4^oj8gsc!D?&xIZHgN}&c?pgV?P8kS%S4&WT_;vJGc7An zg`GHz^SFyQNc4nz1M;C1s-X!wpfAQC3d^tszu^@AMl@nS<@!N36h>LpLUVM+0F1>f zEXP*t!=Jd0$A}Tl=ZDP5k5Z_DMrem#7>3E1hgH~)12~Pp@dPoR@#i2D@}UGOp*~up zD+VG0QCN%(*oEJ54mS~vSkHNmAQST9V^lyLG)G7D!AMNOT&%!W?8R|h#BDrBtQY)w z$cWr1hH|KZCTNEq7=#E+$3m>eHtfR*T)-_vBgRXL?>1{PoiHenYI;v_EOCLZGr z;=JbjLONte0Tf4BR7E{BLpyXsUkt}ML}D(MVI8(%HxA=ZT*M7L#7o3{!*2ztkP$gi z5XDgjl~Egw(F*O+1N|@zV=)!8u?Q=$0o$<~hwul^;TrDXFMp>3ga*pv#az z&f+4j;U@0l5uV`{f`}Dl{DVYDhEzz0On4tTkp~4(7{%}jN~0VqqB5$ZHtL}vnxZ*c zqb=H_6S|@&!q683Fa*Of3S%%16EOvmn1NaN4)d`Pi?IwpU=`M2JvL$se#CbCgkAU* zd$14taS(^`JC5N5PT~~K;4hrR1zf~sT){P5$3M7%TeyuoxQqLEfQNX5$9RILh{iKK z|DPB9^Ce#W&ujjvH~;gNf9l=;5#;~=|KtDu8UOvc|NTAx{k{La4qub6?Z4OQYxcGK z9(*spC*Paz(f8_m_PzTV_*wXw_}Taw`C0jy`Pumy`dRv!`q}y!`&s*$``P;)_+9v& z_}%y&`Ca*)`Q7;)`d$8n?f4OX*Bh}OUIShWKfr6lYs711KE8w3&dUgtf}d!P3}?}f48 zz0rH5_e$@X-aEaAdN1{!>b=!_toK^)x!!xd2YWB}p6tEZd$jjz@7dnFyTN<8_jK>= z-s8R3d(ZdY?=!$>fzJe=4L&1$RwRJW4xb@DOMIsIZ1EZ6v&LtR&mNyaK8sdh5oTj5 z#=>Wp&oG~5KGS@*HG@jmObBOM$AI2LeB;Ml-1f@1~842~Tf zLpYXjOyStVF@|Fe#~hA59D_I(aZKXa#4(Cv6~`>u;TR?fV!^SEV;sjij(HsWI0kYo z2Mj?EmSIaYJb=Ge_KoMSo1 zbdK#D<2lxI%;(t8F`#2X$Apdz9V0qcbj;}3(J`cBNyn6qEgfSz)^yD2*wZnnV^PPX z&EXi;v8rQM$F7cH9m_hVb!_Vx*Rif+UdO(UfgKAwCU$J>7}>G1V`j(Bj-ee(JEnGQ z?HJp!wqtI`-j2Z?i#sNFZ0;D{vASb+$L@~d9m_kWcWmz%-?6@9e#idK0h|jsCva}y z9KpGQa|Y)Q&LNyjIHz!K;T*%chI0<*9?n6Wi#R87ZsHupxr%cZ=Pu4+oXa?;ac<)r z$GMJk9_K#Jft(9DCvtA&9Lc$obEY{MhXLq>uTUMI!a0|7FXv#+#hjBlH*=2WT+KO~ zb2sO3&gGobIk$6;=UmS@pL0LwfX)S-6FN6^j_6#`Iiqt&=a9}Nol`oubdKp<(>bSe zPv@Y{MV*s6H+7EcT-7>s;44uXA7Lz|Mu86FWC{j_h37IkR(T z=g`ijol`rvc8={_+c~#$Z|C67#hsHoH+PQiT-`alb9d+P&gGrcJGXa^?_A$GzjJ@r z09*@jO~AE*{747a3|u>K4Z*bp*A!e^aE-yW2G<;1dvFcHwFuWFT$^x>!nF$5EL^*A z4a2nz*EC$)aE-&Y4%a+f`*01!wGh`tTpMwX#I+LFOk6v04aKz-*Hm0vagD{b7S~){ zdvOiMwHViAT$^!?#4ac<{*K}OlagE2d9@l)r&t`WIbH zmup|Hfw>munwV>2u93M`=9-ynXRe{Smgbt8Yiq8txz^^In`>{b!MPUanw)EMuF<(x z=bD{scdp^NmiGl*+jEW2wLaJUT>Enk(6vC<1YH|+jnK71*9=`de07)PygxK`_$t!uZg;g*7Hy7A!}?@74k>)Nktz@6cmuxrCv z;99Y3#;zT^hU{AMFu1nt8nbK7>EPP4YtXJmyC&`0v}@F^Rl8>G+O=!gu4SKtYum1I zyVmWRw`18hYwfPNyY}uHyle5U$rnR< zxK@7+uHCza?^?cV`mXJ}#_w9cYyPhNy9eN2fO`V&4Y)_(UV(cC?j5*?;9i1z3hphq z$KYOrdk*eBB*eWFd{%I8!aWN2D%`Vh@4`I{_cGkmaBsss4);3T^KkFOJrMUo+!Jwc z#61%CO58JX@5DV6_fp(bac{*v7WZ1*a|wrgFz&^;C*$6Xdo=FVxMwp5?%}wXWQ8CF%7*? zA4QQ0?j5>^=w9Mf^h7ZRxgYL@j)Hrm?vZAPd!|=*aWBC<48m6^h4gs&Gxr*7MkIQnE(#+l+_OE56&M5e zbluy{0rz^Z?EH7{*F9kOg548#Z`eIz_ln&!UJLh--Ai^)*}Y}=n6Gc=UJv)4-Ggol z_oUOnJ!+l71H?tQxl?q0Zi;_i*RNA6y^d*<$)yNB*x z`l~Hm*Km*By>|E9-Fwds_u}1?{~7MlyI1d?y?giW;osTB=Zx>+9)B~q=kMOXH2~HE zSQB7vfHeZv3Rp8>?SM4|))H7#U~Pdl2G$z9ThDz6)*x7m&;`~eSfgOAf;9`)E=)&v zSkqu_gEbD;I#}~y?SnNC)ZHYA|)|yyzvIN$kSc_s!inS@Xm;GC-V$F)RE7q`B%VJH7wJp}TSnFcVi?uJ= zVJ(a`F>7IsOdD7;W9^JJG}h8AhP5@;*jQ^50&8!q!Lb&nJFLyIM#owmYj&*Nv4+Q5 z9&386?Rl_}YXR2$So>oQPytvIWNnZ&Le>gdGi2?MHAK<#xzAuJhM*A&BO$KM<2_*- zI-?>o4VAT2)>LhVHCEPI zeTLMq2FqG3YqG*n6S-i`mbF{fa9PXM4Ayp8<7KUvHDA_#Sp#M*m^ERKXK=rTwPMzc zSvzJ8nYCorlv!J5jhVG(MPTjO)kwxruqMsgG;7qXRkLQz+O^r}0c+ZFk!ba*iF-!=PgnK=fpg(FM2VM;3I>rKop(--t z$sopLu-3I3tbI+3y8{^qz}i@AWJ|-E*?$IbPlL6z*3=e9LRf2S&8@Y!O<*lF*cxGLg{>KGi6V#xYl^Kcw#L|6 zV{4Aze9Lbf3t>&NwaFP_t+F-C)-Jb)wam$Itta0d#=x3qYoBAmT4-yct&Oe)Yo)E3 zwszVY>Mke;YpboX-Ue%~t-ZDeI}WVLwl>=uZELl&!`f|YxUJ>3rrX+XYrOyN%zX~l zep>@>Ex0w|)`nXn-VfG{TRUzIxwYie5ejR}tu?pid>yPow-()+bZgU(z*=={)~#K) zhW%zJuNmWDjk~q(*1VtmhHD4b!dHd0@z%&&D{sxbwe!VcExk4M*4A5NZ>_yG_txH9 zgFge-jlQ+|*6drmZwIq|$Xe=cUCBR)l9cxGWOJi|~I+2PrSeVBnzc=jP7&Nbz|!Lt#bk;sCF zP54c~GZdbs@JxkgD^4}$J3=2+foCx8G~#{^&t`Z=qaZx9;n|G^@GOUCIy~EP?MvU(So*h{M&yrL}CfuvXGZz!k0-izfEQ)7R7Qizq<=~l> zt92M#z_Tp%;Mta^wRtYUGcTTf@eE8HcqYcPF=42L)VNWT`#wg)Gc_OJc@17GJagmO zn-Yi*&*XSE$1^&f)k%w+)ws?P4$t)DhG%>{>l1}glz?Y}PFLY`MQ?n8H1O<@XNX4O zOXNcI7hH3QL|YU^Y@( zjq;3?XQezd<=H9EP{kZdF3s-~W6%g6;Mu4AnTSLi6oF^UJYzN=o;fQ6&!Bl0Z6!RL<{355s(EH@ z3r3*F{@NA`LEIn%(otyhIrogk91>xCD&uDs9GZe)U8-L{DI)G<8 zJ>%(F&$BuCy}&b|6_FBGbMU_5S<&k7?5Jl*x4<){o-Or^>BDT?127IvkO!Vg-HWMc z1<$O$%gT5Qvk;2nh>bt8@Osb{r4fR^GV?v)TXc-g*Yt zv$)$34$tW3z~l7%&M*O<<;{y1>A3%58a(ss+240*d5!Q)uxEokBkWn>6KS~4;TdAj z5+}r&)LfJ3g>py=&mgaWXOcae{2s2Q;yT4()PQH1J=5&j<`Jlithk?o`!+la-4LFQ zev+K~K0Gts44$F(EcHHkw%Rk+AHp-&hu|4(&tiKfI~I;5VJ?79D2e#^Gcjvr=#DZ- zjB|o`v^Jyl3M*BVPobnLiqv-zz%6GxeUWKN*W_2VLRW zd(Yr|7T+`Zp3V1+zGw9>#Nc~@XZStK@0tFq0mkeYh-%>LXU{zt+=St%gUq=5E*RW~ za5O-6JbD`p{)BO8g4~FH6AbRgWVAqjym}oB?#Fa|jlu}N3I>m07D7=Rv2pxmFgOpL zQ3~;K>P0a4J$j%F664(SU~oCYPys1$`B^Zy3jI+9X>dI{7+jAbsELfY^)wjVf|00) z_i_J8Ft`H|XoQ@2@;Dg$1ryN>`S9XVFt`uX&>A1&?ZaU35N6^V6hkZ=dk_rHMJJR* ze4M%;3@$=qaM(M0}k3o9_TUP!>sW{(3O@ z1Nxv6QsL^gU~nx4p*qsz=G91SI&ozPmsDd>3`&=-%5yMa$nQ`xIFt{CK&S4>6=6u_G^ydFg18x%t-U3>A?QSO4Vm!64K?2HZXw z3~t3}G(ZkK{)1}|6VV*`@%ltCcmPpohhm6@6UX_x(G{N|5zZasnnE8`!h5)Wl-~k| zqBb()-tS!Zh(KfH#ntjvnT^Kqr(!Li}|o7+i`lR75IVI~WYE!w}R$ zX52f#--EGeg4}q%pWhN9(FR2j6UTq!{h%v8Lt>oY7YwdIKU6_l+}O)?g^{R_?0CF~ z&kd8&5{2+?H}@LMK}VEALj3hB-w%4D5>n&uU${0f9QBY5k9P5AViH=QAm07VpNTo> zgi;8>*`N5_(HE7G7B_bCIxz|jkP}aL@O#8me2tF~6UVm)gA33dWswY*wgrQ0Fc>wF z8TWqV_k;2H3ip78!y)L-y;g4D1ij{YaO2n`l1Tb;r3ddCx}23%JX;Ws#>k7;i}_yg9lD?l zlHtnt{2nnJ^^p_L7V)|eh4v_kM7Xe!=K%(zHnQQ#0LV9k%;esQIp~72NP+87{5}wlCdiL>Gq}fNA$p+_(&F}Xu6s;GD-=N-oQdRn z!vNI6`*<>q*M=x`Kxrh!)u~($7=^~jhj&x>`>+UMsEiD_H#r#Gg{f$Z5(vSCNxVM{ z$Ct>B*As)m-?0F_PzmX9cLLWcrl2iKAOsi3bDzWrG(=v!9mngyBJ@U8WWvL-d?%QJ z4k(RexE{gvh6prAVZ_FnG2AaP7^B&k0OI zTYQ4VxH^Pu1QBR~B8ZRkgZT_G5=~GLv2c12*ENQs0rDa^Fc|y;%Px-jCli79b4OkQLE=d9Rp{Z&4Ll5Z#CW9rN%lsv--bd-K0z zKE6dYWW}>E?t@r>FjPl2JpY#0gGK0rn#h4yy?8cbG5VtpKET_ayk9KGU^GBJ#K7qu z{FX5sjZp}3aK1bDB!r_miXjA7x^bOh0@|PylH*2K?v0p+jwpw;c+iD01m>Uzsvs+# zb>=7`MWUzZSg5m;&um~%ZNfZe1Xh}ZqMI=@6jLi zkQXuVS17+*grfyMMiTt<4fhp9qBANWBc8V7yTBs!M?K_4Oq^}YXN3r~MoFZ=?Kb?L zFbh3U71{9WYknhGf#GP1B1nYmt-0Csafx zJZr&!gJl?s#`p*!xZa%iiRtKy%6K2InsKdQB}SqJN+3CIf5meK-ysaOkq0qxzA1A> zOhhQkBLkwFa4*C%3`0{CLsHyq%;$qS2t#e;K`dNo#CMI!=!lBQf|m_>eOQHXw8p1M zjfY?I9bqvBqX~*432rsuK7+aFgL)`{__$i1&jL~Cff~q-Sh!G+dmpBv3#uRof_3@c zu?dsV5tZ;h-qhiDh4mPZ_Nag?cvYM08|yG0q4*qG@TwO7Kdi%ev_}PG#haS^nb?4d z=zvPdhIci1E!d1H=z^-qg_yWloiP@sqX%jtAL8L!HJ(YBgFdK_!bptURe5e=5eDNc zl)!s=R7GoG%;xlB#tIv5&*oY)fy;L)dyb+Hzc&=s{%2uX0i6yFn8VLUpc1_~e%?v~{9#!8Gs zCsap%B*wi@xQAjD#-j^rq9BsuK?&YF)?yO6qYgeo3OxBZ7(9uMh(s6~;$x)6%i?^e z*p4|Eh~_ARtcZcD#rW-FF-GAVR7PHe;BHagBi3LtdZ0dvAq`#@;qSl>%*9}|!sp0| zc)0Zu&tR;?1aw0^6hj)kEX;TZJ24-_&<2%|2O+rsA>#*ZKqUI02}&a?V&d;YTxa+J z-cXz(+`pHy<#@z&@t1uOP(E^o_A1Ux6JKqiVVkM@a4_cri@*^c)X5%x%Z&-zC z=#SQ@j6z6_x9{^D#v!c7ObkURYN0qXAr9_l<++FLScnL8M`M&lZY0I?EWAeS!)i>& zAhbhG6h~&n#r@3OTksQ>U;@I>9ABUi(jb7FnYhp3M=V4HdZH;Rpa9;(yNrD1IEEjw z01@bkuTT*Mkp?kvI|J_vJFplN(Fd(i4MmX|@$e`;*EIHEHD+KaI-&tSLvEzNn{-^0 zIEHQb9uv?9tx*FX<9&qSSz5j`9K~I$Hpc|T@3W^~clHhep{u`XcFIa_H7>!xsLEBc4HmpVjTM68#KV@D2yyfjMvGyKjSp^VgnXnA_k!onxG0kMoy$c zOgu`;_k`cE6Du$aW6&Gz&;S)s1lf=r0X#^;*a=6l11m8b5$KEdXoSl67#|=t;vzaR z_j>$^J=lovF%2Wo3vEyjpQ9*pAQfWcX(H|o_!E1v2}>{?qtP3oXoM>G1bLAGA$SwQ z^@EEzid|TTg_w#F_!jNZ5S399`H&Gw5KPGDhpRY=z1V_fn1yi|gsy0XdZ>txkp~%& z7{LVmIk<*Du^-#85_2&b!x4s1G(`=R#Yf1AvDmNP`drU=gA)9zzj^j%bMnsDjT>1i6t3DG?uU<1qHd4P3wp{Dz%ak7f7{QxT3q=!N!Z zfd;6CvM7%H$cD5?g4lQ+o9_!ZZ~=edAa-FhR$&olVG>4TAbO!ATB8wap%O}?DDoj2 z(jgh*;ax0#E4Yj6IENEBh+nW3Yq1pbFar}X3WM-1I-@O`p#f^3B1)qe3Lqyk;XNcl zT)d0P^AZnm1DA0I$8iw9VmmfsB^F~YW?&M=U?}>b7doRITA&f?pc*QmG(N_M$cyaA zgw#le1c-&VF?daQh}*c1i#UT5IE;PRg>Be`HCT>Cn2RV(#dw5c7zUy@dZ07DL2EQa zBh*7pR6zxlK}i(DhscLq$c9Wvi&RL8gouk6cpG5+gQs|iySRaCxP)^!jX!V{hwvMA z<7aHgR&2yttip0E#zM@+Y|OwkOvZRbU^GTx2nM1bdZQP*qYFBsJ=)=Gv_x|>MI(HP zdZ>e%sE#W50u@mnW$_t4MM;!EaTLWz_z(q=A9;}*A0Q{PBO9_J3o;=i(jy(xAT{1Y zDx^SiBtudpL1H9A2ofT}|HS8?@euca;_y$!{-0R{r5V3&AxWugYU)nDgM!Z&9 z;%j&fwTIVK7j#E2c+K^L*WeI%O^!wcyjCY;8fL(2c`g=WF_vQ$)?y>JVmth8?8a|6 z1b;jJhEC%gF5wz(;4U8GDf}(Ig}+UIqyAR?&HCH*H|%fO-?YDNf8+kv{muK^_a5NA zzE#aSY^G$T5*)BgaUNl^ioUc5)2mSjsV#V=Kp4j$6}7j z9Gf{tbFAi=&9R$fILC61=^WcR#&fLan9s4FV?f7(jtLzbI!1J?=$O&5qhm)6&Yu47%typDYx13MOWOzha$ zF|uQ2$IOnM9YZ^oc1-Qq+A+3cZO7b>y&Z!)7I#eU*xWI?V|B;uj@=!@JC=7$@7Ufk zzGHpI{Eq#d12`9OPT<_YIf8Qq=M2sroI^O5a8BXe!a0U>4d)!rJ)DC$7jaJF+{8JG za~0<-&Rv|tIG1rwbSePv@Y{MV*s6H+7EcT-7>s;44uXA7L zz|Mu86FWC{j_h37IkR(T=g`ijol`rvc8={_+c~#$Z|C67#hsHoH+PQiT-`alb9d+P z&gGrcJGXa^?_A$GzjJ@r09*@jO~AE*lehub3|u>K4Z*bp*A!e^aE-yW2G<;1dvFcH zwFuWFT$^x>!nF$5EL^*A4a2nz*EC$)aE-&Y4%a+f`*01!wGh`tTpMwX#I+LFOk6v0 z4aKz-*Hm0vagD{b7S~){dvOiMwHViAT$^!?#4ac<{*K}OlagE2d9@l&# zFb}Q;9fE5^t`WIbHmup|Hfw>munwV>2u93M`=9-ynXRe{Smgbt8Yiq8txz^^I zn`>{b!MPUanw)EMuF<(x=bD{scdp^NmgkzDYkRKoxz^{JpKE`v0lF6GnxJcgt`WLc z=$fHxhjFv~yOvm-f7V1h48nA*#y(uab0p2m=K$9#8>2fSun^nv2ks)y`#b|s9IkEtm*tKBSgk2kUjo7tf*Nj~|t^(JRT~l^#*)?X@nq6~t?b$VG*P>mMc5T`< zYS*e=vv%#;HSB~RaF2j%+pclD*6o_NYu~PcyB6-6xNGCCk-JtNis5h#-L>?SaBbZ+ zcGud=!?kzU;9ZM%P2RP6*XUiVcg^0ld)M$?%XdxRwf#MCt=~0&*Z$oDa4*0;0rv*n zBXF<4Jp=a+Qsrmt1@{!(TX2uTy$1Ij+Dk76X;vvBXiJq-6U+|zJx z!#xi7I^6Sc@54P1_d?tgac{&u68B2F!MzjrP~1y#PsP0z_gLI(anHrQ7x!S?i*ZlJ zy&3mt+^ccV#=V31QZ_PcnlW@3Kh&b>MJ=-jJw&(6I&_wd}y z>jL-o+~YffdvNd1JwW#YJ7FAF;xO*Qy+ij9-An9@@mK}-8t=osNB1CWp)1^*bdS=# z%13bT(mhP~GTqa3Z__l2VfY?<@eksa z-&)>a&YXGbTXaH*i=D=D3YX+hnuqqV2EtkhYa*+H5JxYSYu(W zg*6w}UYv%t7}jK1n_-Q{Bv`Xy?S?fR)^b?WVQq&s9$R6}hqWKpfLIIC1J;IEBVw(H zH6zxJSVLkhi8UqGmfV81Cf1xZfi)=BqF9q+ZA$8ftUJJ(6>C?lVX>CQnigwYtZ}i{ zWiqUNxd3ZntckHU#u^!GWvrR8b|z^Pt_fIEV{MH!HrCoi!`d5baID3#Cdb+wYjmvD zv1Z5G9cy^3<*}y6+MeXixE5f|kF`J609gz43f2Z$BV?_RHAB`8Swj@kg8K{_VYrpnqXYpkrbvgXR#D{HW<#j+;L+N||BiQ>U zov zSethP*6NjlwR_g^ZGkm?A>FxdVXdDvf9qilptXS31X>$tji9xH)(l!ZXbqvYgw_;V zTWF1;wT9LlT6<^>qP2+DBwCx8<6G_*ux8QPMQa$XWy}`Fy$jYjTI*=d<1<(TX)WZp zur|^f$!J(JY3-ynl-5!nL^QJW<$s4Ym)2fdgJ~_MHJR3CTBB*LrZt<^Zd$`x9oBSO z+i8ubwVu{|TKj1YsI{QhguaF~qIF@-sI{ZkkXlP>O=$yIV`{CbHK(x#bFW7ejKn&e zNBkjN$7q2FY{pe29?F;u*1B5rx&t?nY8c}HSQ~4N>~2^yn|?U=G+0Y(P3=KEL$(oo z#^{G7uom|n@{atsMt3;>v}X4ltl=#JYkJ4Q8sF=%=GWR^Yk;i<{uTFOjj*-CytdVV{EPQ8CZL44RUi>le`7iC|j#+&2k6KfVIpIVQsTD&el3F!ZEx>-tj#1 zU~Tj|TtvtO?o+Ua+FELBs_!D*L_QN(b8YRlHQ3f-Ta*1ItkGTzYqqW3wuakUZfm-& z?Y73-T5oHGWw+7x?cx&RVjZZO?F%PVnpM!&V0c+~5t+&SB zT6=5mt-ZGfzdfwUw>IAz{cLl%_ruzKYxu3@x2E6Perxm_|DFNxEWki`HsCB0 z%;P?Yc9@3Uc!aF;`FAl8D{&497Vzid8$@CcJgbmxA+G_0u^JcP*#^%zv_}*?`|u1o zzvsPSI6NcaSqaZfcy^)-zQbX7w!$+Op0x;vXD|N2drP^W!?PKS;8~3r%b2&IB|OXV z6Ye3?a_$H4%!g+`JOkockP--mXGDI(GkA8yGbElR@l44zc*dkWdSC&L!?P$Kq6Ivo z@-sZMl6f^_3wV}g6+GLLXbsN=c;>~kFNfe+n7nJb77zi?%G^e}b=>#S7t8S%60GO7 zA{0^Bj~B?jfoCnku@yJr*&WaD^uaQmLHtep_Xve&fA-@gJQLIyWAGzx!Lvi2A@VHI z4>*gEtz2{Hh}k%dH^~1Z<4br(X(v3h{LE`YC(OYSyhFiVd?%QMU+@Uoe&IO| z&wOpf-$?x{zfbhWGW>;vyZJNG5p&>~vUe!7hieL+Ir{~V;aRl$@NAlA)I6)^nYAkD zhZQ)F#J@3bfM?z2<2YjO=bnK!h=gb4JS&&`0M`RNL$?E-sq<`IEqK;$9j?JMc%H=z z!%}!g&$D`-+3SS4I1117dA9Fsc-C((Jp1Pvz{ZGxX9GPWnE5EbU3hlTGlbXi-Z7pF z@QmSdc;?Wvhn_+7ETU%;J)7tmMb9dFX3?{Y2k;u6X>1P9IR1ji$Z?YI7oLUmOyq53 z_>P+JcH?3%oFfzW-)kHGYX#F^bBW#zqk(I+0I|# zSx?V=diK*Zpq>T2hs@`A-|(!cXGT3cn&v#;BRo^;+0u(hc7b~UdciZOr;*?ypE*3E zIuFMX^Ah7Nv_ljQ;tdL1=JjAQb|V@eT;Y4bSnPynX0u-9nTrwFjGIV*jr$h{!Lzql zk?K0vFFc$31I{DK-~7(d1D@qQh4}w)|Al9LJ@b1Eo&okOa63f7Gs2z~E_jpc98<6d z&yf2T*CfW{XFNjo+kA%@jURCbneK3%VkkD?A9$wOv&{pr8kdpc9`|kZ#&UQzI`Mt( z`{<5EI0esAmqaJb#W8s1x)?lzJrjrE*=)~fdsf>s+xzeW`5v=YhKcZu_Y>rN!uO8| z?0{#&Jsa*B@sZeqTks6IXUT`av*p*}S@SCB56_@qMvCXW7kEb9v+C!N_yz9;J>Z%4 zQ;7eP>ja%K569tIc+bR#!ZY$m5QJyvzlLY&_rtUG`QLEuz%%!rz4r{hXYoCg@7a9M z=s$vI_C32Fj%~P$%>1!+FdUn}*UX;#zgYlg0+{V=BAAU} zMuJ%hW+s@OV1|NO3T7&ptzgE2Sqo+^n7v>IgINq_GMLR^MuS-mW;U4JV1|QP4rV%- z?O?`(Sr2ADnEhY|gjo<~LYNIelgc%cNO_(`h_JkP}W>J_) zVK#*s6=qeKSz&gC85U+)m}z0Qg&7xSU6^@c_JtW3W?`6#VK#;t8D?denPGN@85(A3 zn5kj5h8Y`XZJ4=X_J$c8W^wd{*&JqcnAKrshuIxwc$np3ria-cW_-+onIC3!QA!de{9b$%vSt4eNm@Q()h*=|Mj+i}S28mfDW|Ejqk~Y)7StVwcm|bFq ziCHFQnwV{3#)(-cW}cXRVg`y?C}yI>&iZdgN+|!Fg~NCUvs7BaY!x$B%vv#X#q1R` zSj=KElf`TnGg{1QF|)<&7BgJTaxv4zY!@?L%z82N#q1X|V9bIs6UJ;9Gh)n&F*C;O z7&Bzdk}*@pY#B3V%$hNC#_Sn0Xw0H9lg4ZsGitWO%o?+6%&;-b#!MTtZOpha>&DC* zvv176F$>2`9J6uE$T2I&%p9|G%+N7Q$4niwb&MI=vwzG0G7HE|AhUtY2r?_k%pkLa%n&k5$V?%#h0GW- zYiK*n9x{Wd7R)3vo5+kJvx>|tGP}qOBeRUmG&0-Bj3cv-%sevt$P6U2kjz9f8_A3$ zvy#kAGCRo(C9{;$mHanb$&4klmM*~TB{P`JVltD-Y$h|B%xW^T$?PUGoXm1E)5&Zn zGoH+PGV{soCo`bTf-)1zY$!9L%!)EI%IqjJq+*uk&qNzc!(K$g>?t#-%%U=r%4{k# zs?4e~v&!r$Gpx+AGSkXzD>JUlx-#?1>?<>{Cc;cCv$4#`GAql>EVHw&!YnN_wanHs zW6P{9Gq=p%GK0%3E;G5JVMdo(U1oM|h8bRFd70^Dw%1aehM8Yxf0+Sh7MPh}FJMNP zSz+NYJM1RR5;Ie*56l=l12f0W9t%Yj_QPy4Gs?^=GqY?9Zon)vGtJC4TY^(C^UUls zGtkUJGZW2hG&9o7N*fNd)67saOU+C*v(?O4Gi%MvHM7^uU^9!&Og6LGGS%lhg_&(; zx0&H)mYbPwX1kg3X4adTZ)U%l0cRGRnQ&&qnGt7JoSAX+VTPPpa%RezEoa8u4wyNY zzVW{qbY{_+Np}in)R|RhW}Vq}X4si!m!&DcN0@PE)}5JmX5X2CXBM8BcxL07k!Mz( znR#aCnW1+PX6l)(XU3jcduHyLy=MlWS$t;lt%DhTNn3JnMJLR{0X&D8O|!c54NUH;98G%M1~ zNV6l&kTgruOi8mP&6qT6GETRDvnS1g(qO0y}=s5Gn6%u2H>&9F4f(o9RUEzP(z z>(b0ivoFoSGz-&AOtUes!K}>EFgw!>O|vx3)HGYujLk5ZxoP(18<@puCa2k)W^|r| znVm&phNoGcW_p_KX~ySgF!R&wPcuLtz)VoHLCpv?E7Z(TvqQ}gtphVfe}EaIW{sLT z+5~2hnnijMW|NvxYF4S4rDm6!VQQACnWko&nsI8@shOu~2l1SMS*WvNHmVt^W~G{$ zYIdp_s%EL0shVTRzZt9j`R5Xxz+0HbY9_1MtY);Hf|;#mx0>N6)o)wyqhwX6-hG*}G=& zn#F4-ui3n2^qSSX0%rG`;cJ$!nZ9QGn(=Ga?`xR-YX-1cz-9uQ4QxhmmWfo6T_U z12dh?b~fWVeH8Zd#;% zwb|5WR9}Rd)n-?lVQrSRnbu}on{l1-JFa_lgc;aoVVj9$?EV{_YR6!1u#!a5KWq z3O6%60%nMtC2pp;+2Ur5n>8MVpYS)#A~%z~5e8u~4#DhlGtA90Z-WS!aef+ak$oxO zHM+t~bhFXTNH;6p%yhHU`@k&qJ-Cfz%lVFA=DOMIX0V&ZZYF!0AGpuJtadZo&2Bft z-7NRaEBGv6#`^@A`F;jwz?%hcCcN43X2hEnZ)UvN@ws4@d^eaazYUjQ=DgYSX3(2O zZzjFj^k&q@UBmSav+H}pEPFHUufUA^yfE|L?0Ylt&B8YmKlVEQOqi8#X8v?+gIW4! z>gR$P`&}>vX78VcS^Q@5o6T=Vzghk3VRpY6{+TxNOoG||;aCYX|IPll2S7>K6Tsd8 z-@{%3_6)FhfIS54C16hhdkffO;40#5;kg5Q5ZH^ro&@$LIE`1xwDsTK1@@=*c-wg5%!801A9l~S(2_B^roi9Jy4g_8Lozg;wiy;AI%vK97Fv6qTHRqU-&2VG&$m6bRI zd$HJ)#ojFTXlV_5w#>s$oWo1l+r=I)_Ik1Bi@jg$0dolUgo*b%;~jhsd&by1#vU@; zU{4u)%cMBU_yILw?-_g0EP*{~>`n6y8IEzC!QM6Yu$csV+SuF19yj*7vFDAwZ<@kh zIFYb7jy-bhl@tF2uLbtdX%2hpM8O_A$8it#-pPgXuqRJ{*rUf@J@)Lm3w!wFggt%i z?b8>Lu;-7xf9wHdFCcpY*&E0nLG}u=XOO*veuKS)>?ve#A$ttffjx)pJ!B7}pI}cS zdlT8CC_n63WbdMZu$PfNjqGh?k0W~>+4IQWNA^In7m_`ZmcSlK=U~qydnegL$zDqK zRI;~{J(ldXWX~mgF9l&QrlPPnQz-1!WX~phH`&AK4(#ca1@?Hd*ONV;M#COZ_JTSI zdqdeH%3e|SjIwuB7uZY6o>KOfIthDC#k;^~hEHHGDtl7dn`$oXRb|gAdso@R%3fB* zU~j8$Fc?v=_tinz3+pxPjb)E4du2687$#yV?5SmMEqiPwxXf!rNz_FL48ts}$3a}h zE7-%!US8$V1U(Reg|PRRJ-}|kg{-_o_OKg)nXtFrudvsh zJ@4#&XAeAk;e85wZ_R6(qZaQR#y>vgp z=ctKB_!^zj2lnE%C$GJE7r!%`xy56wdZeAq(gSt6WHFsWnr&i zdj>Z{JJ?IOFNVV&!;zQ^dk@=#cpG-(F#g0v*t6K)#V--_4ZjuG+t?n*IZ+VBQ3m!x zt_^!5+atL>dY~VM!CuN!VQ=L{Sb+_&_wsHW!XG$?Yq*2Qcm;bn+soOW&go!}XL~){ z^V#0dpP?e`32krarm$CZdvt?6qz7XZ#=#!b_L^RRWmtnv*n!y}$S25RSp#;Ae3W*KiYeVK4D#c!ePBHEz%GL`a5INQX?YM|n=< zfxXM^VQw$;Pf!}=P!aY#ua4TV7kWe38{Ho1_DXMy_UHtAsrN(}`odo8LtyXqQU70E z_tK$Y7=U58zaMQ!M6?;&j7TIg(jqNJGLn)~W=uNhT+>yGF-bhG3HZM@)=t{NwZ`1;5cWAQ@dv%2OkPe+3rekKF(hIr6^=hAUnxpeYEtv9(jt#x0 zwQk(drEhQPl86Z1zUrwRP)v8twmH2OfW~Z99I@f=M5=`J#_nJI5#OwC|I~miVk4 zj(pbFMZV~zZC|uWy03b2##gNn{Z)P2e$y7Gzv*8YziZH_@A~-ecRiagQooFg)RDI% zbynsdTA|+$-Es7Xe)jy()HQ!%L)b{V>L@$tSxt zf2do_&2wvoC2lRY%B|@)xb@Fgw_e)q)>#J`Bh0NoPP=v9Ik)Dx;?_Mk-CE$DTh~2y ztMjE>yS;Pk(a&!6M7p*7Z@2y%@784=w;mCXUUhl&et<_Gr1a>GG#)*X!K3T4dURAS zkJiiY(cmH;y<5VgGs}9kbR~~ItLD*>wLR)z-=oVLdo+Cuk1lQFQCCNg4*r|5dU&)* zUyn}i@6nrsJ(^;KM;nas=)?&g-8;pj_hxuBYPLsx7I-x65|3tE;n5sxJeqlfM}xL_ z)ZXsVkGnm3dA~+t8Xf7;9#PC0iHnA zdQy9}csj3k&FIy+S-g5AyH_9N@@jNmulg1AYUUzdEmYjAWlDLqQdzH7uHe-QmAzWB znpgAI^lI8VUN!1@^;-k4-fHaCz0JHjqor3nwef1v_FlC*di81-uP*53)#g3DnzD~q zZ}ju(l>S~VJ;d`4)tv}tXFG9Wg&up*8&-LoW z1zt55dv)4U=3L>`@vFQVyT+@1)_e8IMz7Y{;$^=R(@w9B-OaUo8TWu!mmczJQwwdWhJPJZXrH6N(iCvyDa)o0(RLnN{OB)({`2E=$Z=#N)Z$9pwRB0t^y z?d5TsJjBuj!_w~#OJBGwz2R%=aeqrUC$n^Rprr#-TG}Gm(o(4{O&wxsbXrSqrnhu! zMoY(LwzO$hOLJzoG$x0o=W|&)KaZvD@>!a_fTdpwTDrF|V-&TtXmLw_maueBDNB2m zu{3KrOCOcDbWTM}t5&u&s*0r>t6AE(hNVe08Narrjp{P)UzV<{Z)v55mcDFc>G&p= zW^6``%`I)-lDJ!0I;D-J>Dn<@drPZyv^1iVrJcK28u_=SqqSyWte~6*Kr5grVnt70=s|J(bP~snEscVF#<3^I}C`)^evGnm+OaB_roD(d~J;~C= zlP&d5v9$Lz=9+G4nNUmD&a_l#S=xP$rI+UNy7`t)TVUzCg~YVj($!1IWvQhNms`4J zg{6rr$#u1*o7V97T1#uKw{+9JF-kAxjS(roKlkEpUvO!^rPAai6d>@RX&sPg^?XjHUa}TKYDeJpbdd zbCwQ2Pb?QKy?fD8>k`*qv9#4yODA5lbjNjayJ2bEP3m>q(&`bG_PAqd=v_;9-Lv%i zeM`SRu+;C7rFkC{{}W69eo9X~vvm1$OAovt{+E`%er4&O*OmsnwKUs1;(c#v-4EpS z(b9gOi0dHi`b|A(ayep>qKm!+Sg$Ss<2ep~8}q0X_EI^!($ zjc42hOOq#(XOg8U-2C+Lx0lB)VzYUjS}J0z2XTh2zf4=dIBb36wDqye)>}TdUhuW` zgrBW@{cYVCVC&*!woXfK>xe*Gd!?|obxK=n2iaOG*w*Z+Yz<6pYZ5-CvGs0ThC;(bwhSrC+D!Wdrn@H%hp`EZFS_a z^*zqz<@tQJj>~Ur+XBp2(AE@%Z2g9dg>79|gz<~o+PIjld5YWW#r+bt?kZ{Pgi^M) zC~a%rGPb&Lt1P*cvvo*$TWeOZHC09Cz>!L}PN{5b(<-)RscP$YoTx^=)opE3!`2Kn zZT*M?wQL<#+t%uJZ1t~8o>=vltzGIdcYUtIp$4`NZ)j`zM#O^u8rwRx3AJi!t4}jq zuVPknTkEy3)wiXsS1_X$dAGLJXk+XDFs`kw<=WXAiv#U#?bm^tbhPyqR(7(rS!Y}Q zx=WsO)ZLQMB)+lW5YirAX)c7A;kD=$k zwx;P%Ju!3u>p9TY2#gzKYyQEuKEjkCwiX#`>oZIrW^2*m{En$3Y%M&J*I?o(YCW23 zF=C9Zna46F`i*1F$1@i?Ot96))`_oRET4d`MR9;N17`TM> zSV}*j+%jTD@8z~ySh|8fUCG*^^(tb-q}9}D4Sj{mYgv2rTxV+n=By{z4XhQaZ>0aw zV-r1&>6>j0*<$N>pVZv>jHZQJudP* zx?QsMExKH`^(8u7p)b+;D)mCsYwSmoQ`G7lorrRAxXU`!Dog(RZl>I?(Bjr!_EWV)SFU}Ga zjAA}Ww5|W*F{=M&?GX|~U*j8E#!_$O{lk7jBD%#fK1#;32jNIy3_L)^MCOEl5`By( zsOqNAk=(;Mf~TnJv z)p2-*>MqqSaQdkB!F3e$Rh@@lXzHhW2*LiUL-7Em15}qG4y}_hCPI>{j=&?74&-^n zqD2bT!w5>LItaH>I7oF4zM^ih>Nc3Eh#BF?m|Ar-9-(9!)rE*ey%1gtBduy@oI*%C z)gicr{OMJv;SDNgP+g8_G|b2m0m-D=0bxj)S+yT7B1;z4k+_QjSs4Q_Q6`(}0(?QW z?5e8}jruuMHzNtnbE@u!kxR8b4#6)sx!^bg^QiX38Klaq+86&JB%f-3TtNE#ssnKe z846G*Tt>!%{Eo}WScuSwoygiejqWaUA}|RXgDj93@oS zU^l#IT9RDw2X#xSu0|xPl&0=@hf-x!XW%jNmsK5uo5);_IPgEDEU(%XhhbJ=-LVz1 zs8x}Ez(q&giBkgKxlFkC>YDyrRZ2vU`}umL|&sT%!)XUJEbwZbK&s=+ll2n!8s zvPSrf(zR5l;0`j^rbajcXC2lI8xV=|b%_V}kmWC4hvP8o5d+rZ3rf{jorD`m+ko0) zKaxH(PA#zqv8d8Q zH57M{p(S%;FaDrPE1p9H(zWI~?80wUXrnq6H;}3=y^k&Uj*{(E$KnF~+Y={N;VtrY zpx1C57HW6o48eV5=)@e@iJvIlnX?4v;oC*E36|kGvj0s#Vn1S0zN_j)TtYxM>Wk%g zj_lo4dtncvQKkp+;XgQgvgfb>_mQp_{ftfcgaWgH!`K|F9Czk$I467i`7{gfWZt!#JEoJW9=09gO|>j(l^N1Do&)8Rx3D#Zufw;5>Q{p|}7= zrTMC(a17BXynr6UHoQZYh1?6U9QTlX5piKU&cTavi^&}a@f~@WaF$^`o+8as)(3NN z8OAcz${2+s_=)_>ReN9qo*~T&uE88!f}-L|`WXlD9l2L=Z^CLkM2gkqhiM2$B1*1d z9k2s$k#VhRD=fe@nCplIBXJ1dk$XM&WURzJ1ZP5OAsn$NaEAWFa@>S*mc4-f*n+1>5zbf`gM;{pjQ=Aa%)n_xBhP=tg+;gm4@#V4 z-LV$;;B%fFF$6pC0znseuY++ogipwLk?}Ddr|=88E~&P|d|W^R3SXv%Sb>|6E8MHl z4;%0Repgu!48ab(K+0?E2aLgfyhpn0^erai7``FP4Qhr^oIweiyYLc0_sIdH zu@7&N`T>27aX5$%Nc)icBqrhrJ|V*+?kAXxWB7thkEuDP;yAt`%M;#Xl0MHF(r z<}u8|8T>-dH#~+}ID=ov@s@cp6Q}VL+20W#LU9T|knKHrU56ViXCe=re;@BwMRaHe7$4&WWqe5Kzp2K(?D!QWU*jKprd zM2hdMIfh{eo*`Kz_fQPRRy>B^5B3xWU=!}c^^?BBKUj}DFn`g1=!4a`1sf%z=u@n~ zHF!`YnsWh5a0v-0@SF1y3vdpx$P>d}z#N1l3OQn_KSFU5k;w9gx?(Dh;R`avQ7=ry zA@FsnhQ!mq7=wLyji3b1AB+HBbLw*>PoxguYfIgVNAOGHyheX)#639NoCoNOHMj)@ zU$-e=qv>++RhPO^*h|jfD<8dxIOMl@e~NkdANXimbJ_G9X5lpWm_oBMY;#OQ7+7Y_ zAR-Mh0SECOX$+CN7>&Jng_Nd9O$^5lJVi2xNEHmiW;}$CQ=~lp#d_R<;Swp0-dKg3 zuu#lLq#Ksu3KCJsSEMr*;sX94ub)VJ%tbh&k<(wK6=vcTB9S#fq#33n3}28TnMfl{ z#36h@NOF;S7>j*)gWy1sS{R94c!9taBGoYz+wcVbDMcz{AU5LxTtVcFf3O~RUp8Kxo(Uy&)JNMlUKQG7;*Ow&JnjZ`^!45P6ZuMwP6q&7xl zFJ2=!7mr~y_Tn{yb8{_5V=rDKRURJ0Xzarqq|Qq$7=!(Ii!}L&8Dnt(?+}uo+%O&o z@d4=yPyZnX;Ty7+6KRQ=IE&xNQ=Y!XLR>^5ic}Ekj+M9xsYq=2 z2OIGa{*^?kVinQO9En2B)2Ab%~9E?9=^P?V|7xrfbog1|bQDHx3d_=t>k84EKJ zj#w1!{977~>G@v)I0GHuG$%d>iHsUb?8xaG>;xN7< zYh#htn1_pSqeK&tzSxMz2yDup!Z;kkH{@u>d{~Gpuu-PDNPld@3#4knp1>5GLNxNX zBtNXg9r(0jT`>{|@CjL36BFj+GAxv7BQgNn@d_brIh!y8;fO<#cHCF60gsWgJ^g`6 zIEiQ!=)ipjYj7XQI+7d4BMiTguM;t1749LRGj+pwgy9$Rb>W=GYTQS%zu6O*h!coL z!LFR|Sck_**-fNArs6E(QLH;_gspgqv_0rw%)uqtDBqL&4))rH%^ zfD?#8;Xd4Nuo*9rwlBSgxwrzOANMYd#9{nEzJJ(u?U@zKQWTi9aYdn7x6W_<(FfL^@#=9wKNcX9;HGGK^vL2*%~pNcbEF?H(gw@$0KpSPnqod~Az&iwjhVOt=Ooq%Q*j=Os*_naoP`G!r*Qwl zNhG4&RO*c|#G&*w&SD%zEJ{tMH*f?oC^>__aTvc*GL*k@1TiQ%lQR@Y5sT8Z7!P5H zN7>mTqi_;Qs4#~b;S9W}I+y)~b1>$S52oWXeCJbR%)u=LE}$M*j0Xr=$omtl#tURw z#6H6od_>;G>;>$_FO*n9tT>J&R9?#5xB%BO=E7V=Ab2@v23FxEvaO&mu>;>xbR~5_ z7?MzB73+X22v|*@V+o!h(;9jS+wcuV*K#dRz>6B|L_%>BLF?%Oti@a8-9T^SFyc{p zBlF-I0yoiRLh7x&&&4KuLD6j@BXAau?VPJvjAzKcgYy~( zk$|c@i4PG-yNjO14*Wvd-JDUlij;de!?6k9P;xJ8jf+UWk9uMQzM#Z@p2I~19w29I z!grK9NDt#Gf)DY21ltjfiihbB+(CvTya&NSxKaBk_YXWr-eV#|a2CE{)Cn8#9c7Pm z55a9@IKh1zhhd|^N!~x>1B#vEoWV7OoaQXUL3mOB46nmSlsHQt;TAH6Q#*vg@juQk zY{V~A{*PY9GvqtRKE!3DKF`=V47tF0jg9z)su$V+c!eUDhzGZj`7&#ba0FiA{X7o9 zM$@a*62DRN8t?t_5oNA(cHk)r-r)X=+sJm4THzu>ZgEE8G?L$DKjAQ(5$rYWfrVyw z=w~FL!Ch*L7}UK-KjJ59-lsSOTA`*XnZ)cQ#-u+ixkYk~Ap^a~!MR5W!$G@AZq-4Pf=Kj0P$#d3}z5)J=w z{vlZ$&*K(~#PhxmQD~OH`XW^#=QbXrToP*tFS@unn~}@IS%R-<=%uDeWpRe!8LHUC z43~1|;xmfX2?2Zscy&yWUE15$XS!MK;Bx0>_(y5h8#f2I);RyLS5>L z8h_C*XjspXyJ%gXx}$3YLq20bL-NAdM&#L;d=cElkk!c1)Q~+W*^K(5PIE)MBz)N3GPpvxd~z>>l2sUfT(dJHv0v33|eKirUq7&3w!M;fvZ4MrLA z4Kqg@l5Y(4MenhO_>7}}P;WfXW9|gToJids&H#va4U>4sF9 zL9Un^O3X9qQB0jhth0#$6X!7BT>1^;=kfb|Lmpt#0$#I_y@DBwxOTB2Z?RwrV=kpe zShtM1mUCWU{|ZC?UdcMIGUOsgtfnv581fQ}*3!f43`xR)_3V`mhGg7m$UV&3#9rA< zy>MuYA^o-*l4~3L04uk%Ryzy{+)1r5V;5)1Zt95eJ?x*ohE&|gxH!4rkkJR|(Sxi9 z&K@GS!>rvAL!3uh1I#;SNV70%cAS32mJ^2bJ;~ai;vB@e)AZ^Y<~+-u2{+^mj{MJ% z3I7>V{~Xtyr$=%40`<9QNRvzS-DS=t{O^h(3$M}-*Vv!eS+^V18V7FD%eUyO+w@-q zvD{&99KFk$+@luvspkXE(uan`;PxZ--eYoo!g=tN+B{>9=hPUlUXb@o_UtRp!`D3j zhW+!FI=`dG-?Qd;@`2ZUWFLQG{Ll2r7w!XJ4XOK$JioK|BMtHW!C8p6Kbh|rwThz8 zqv_4xToXgSv4#x#LoRXb?RaueFr;cCc_neayV=7Y&T_B8{mGDIw!uA#^IlBxBGE9p z_n7j>VM?silo%I3efZm#$Nfx6^5=B{rWnah@d-2~c?wff^8t0HAX9P$n^Gi|Ddkd| zQZtPyO+rlRnAVhj=}Z}w-jtacOj()HlwFxjIi1;*+gVI`mz8m|F-{Isa^^IpVlGo! zzY!q zo+&-*o3gF}*ES@##-=oDV#9P*W=NVgH5^)OnOCy+)gIa}2qSGiA+qQ=Ah`88nI4O*W;$R8vk(Bkmc*9BN9? zEasnWif4`~!{(9ad{g=^H2HtelqE?$Sa;+)4&Xn03ObOX! z%GS-ywUwB*nbLZPDc^RQGHJIdY4@6Pa36CXFy-?>lm9bJ$$iw6%f}e&IJulO<=81x zI-fDcH=JDmM{UlT5`4jw3m2K^vMGhG62mo9*4{9s%`NI1VaokG)bk#5JuoH1BU9c! zrk+oU|G6pEUNY`$Q(nI@heSIZ^3&yzAHMwb=Wo7Di4Sy$rgTU^Du<*?#{rKY)*%~%Iy#{pF;{1a7fES4w+QMAqR^&7R+Dmui;mo$G>bIAM}4tZ3|Avx+gWKcbaoNvHu8#`n`Q-@q@?vOmK91_~bA>Z3M zqn4|K@XA;db|AuC35?HGrg7{|O59rAgy zL#9o0NbXR^o#l|ha~+a-fkSRBBF?1_DZ9cUzgH3OT89kXK#rRo;@IYp`#T)6ZMQ?l z?Q=-$gAOTuggS&V=Lupu?U3i;4v9GDkgFHT>xx4zUw6pOTjX@dAs_EMB=M0$f}S~~ z$V+PW#vvo#J7m)*hur<@5Z4cfREu)R#2ANMh;vA?B!{&3I%J>noWm(S_~Pn{zf+0@ zI{6OADM4wRGCQ49{4zOZepaWX%juLId7M(YfK#3pcFLIIPRUW)DG}wIGPM$qS96ND zrc*A|b;`m9PU+snDV16{C3736m>rxF-Py_e1*g2}<&<~*obr8uQ`|$G5hR@O&yn>_jF1gpoC07TyOUmqHj3X}T zaLOf)b1pe^#U)d3yQJMimsESf^Y2|!>6=ShM7v~cf=iCrF7dd0q)ng?--G!`^~^qU zHX<0FYqU-^{6SAM4V zl~lQWrDYLc-ZT43;hMg3rID{pZ0jqnyZcI`0lv~>w6Cn4<|}^|_{yL)z7n{@S8gBX z_2Is9`ns=tedH^Z-up_}FJEcn@s+H8ei9VoCk1o*$)I9>^18C0jBVg2HQM<}x!!)# zVYr{{pXw*Y7yHSljec_T06+Qiv*c|*346&kKm4St*H30A_m`iU{bgsXN`D&%^xX(k_4b_l&=EjPRFb@BAfwoWFea3lP670W!KofK;p-zivR7fEOx~Gsqb5qFHuoM#UHiaZ5Pbq~erj&twQ_Ag? zDW%1=l;Yz~DG7yxq*%8gIlCfAcHa&XAD>`(S1nlTj|-M2CxgXE2$qrMQc3ujRPr-C zm1K0LmJW?l%heUBW%P&CGN)u3$uK>Q)P9;q3Kk2I=vg6h>Qe~c)uffAU1{ZN%5+k1 zY&tpeHJw!Mm|k+;PcJ(fXOO^%46?0dMmhUBqZ}NRNgn%TmfhPj%aZz8Bq%mRBxk$}g|G6_B>!1*J>z!ZIMd zh;-~zjQ{0I$bf96B)m)+2^~{T9!FP@H(M%8g;mw0>WiAZzppC`CN+?`j;4I~+De{$ zYcJ-~uA+_lN@ABGQm)W==~ZR6eCfVg_CDS(gO*>DsKB4%EB119 IeP1l#|Ce3i+5i9m literal 0 HcmV?d00001 diff --git a/data/lumps/tantoang.lmp b/data/lumps/tantoang.lmp new file mode 100644 index 0000000000000000000000000000000000000000..2d97fb77dd55f9d76eb434be48144ca5c7184f06 GIT binary patch literal 8196 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 literal 0 HcmV?d00001 diff --git a/data/lumps/watermap.lmp b/data/lumps/watermap.lmp new file mode 100644 index 0000000000000000000000000000000000000000..1b62be95b327f3ad4fd4fa6567e1062834b4fe3a GIT binary patch literal 8704 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 literal 0 HcmV?d00001 diff --git a/data/prboom.txt b/data/prboom.txt new file mode 100644 index 00000000..de5c8a46 --- /dev/null +++ b/data/prboom.txt @@ -0,0 +1,187 @@ +# PWAD creation directives for prboom.wad +# Initially generated by DeuTex 4.4.0 + +# List of data Lumps +[lumps] +SWITCHES +ANIMATED +CRBRICK +CRTAN +CRGRAY +CRGREEN +CRBROWN +CRGOLD +CRRED +CRBLUE +CRBLUE2 +CRORANGE +CRYELLOW + +C_START +WATERMAP +C_END + +# PrBoom internal data lumps (large data lumps removed from the source code) +B_START +SINETABL +TANGTABL +TANTOANG +GAMMATBL +B_END + +# List of Sounds +[sounds] +# MBF dog sounds +DSDGSIT +DSDGATK +DSDGACT +DSDGDTH +DSDGPAIN + +# List of Pictures (with insertion point) +[graphics] +DIG0 0 0 +DIG1 0 0 +DIG2 0 0 +DIG3 0 0 +DIG4 0 0 +DIG5 0 0 +DIG6 0 0 +DIG7 0 0 +DIG8 0 0 +DIG9 0 0 +DIGA 0 0 +DIGB 0 0 +DIGC 0 0 +DIGD 0 0 +DIGE 0 0 +DIGF 0 0 +DIGG 0 0 +DIGH 0 0 +DIGI 0 0 +DIGJ 0 0 +DIGK 0 0 +DIGL 0 0 +DIGM 0 0 +DIGN 0 0 +DIGO 0 0 +DIGP 0 0 +DIGQ 0 0 +DIGR 0 0 +DIGS 0 0 +DIGT 0 0 +DIGU 0 0 +DIGV 0 0 +DIGW 0 0 +DIGX 0 0 +DIGY 0 0 +DIGZ 0 0 +DIG45 0 0 +DIG47 0 0 +DIG58 0 0 +DIG91 0 0 +DIG93 0 0 +STBR123 0 0 +STBR124 0 0 +STBR125 0 0 +STBR126 0 0 +STBR127 0 0 +BOXUL 0 0 +BOXUC 0 0 +BOXUR 0 0 +BOXCL 0 0 +BOXCC 0 0 +BOXCR 0 0 +BOXLL 0 0 +BOXLC 0 0 +BOXLR 0 0 +STKEYS6 0 0 +STKEYS7 0 0 +STKEYS8 0 0 +STCFN096 0 0 +M_BUTT1 0 0 +M_BUTT2 0 0 +M_COLORS 0 0 +M_PALNO 0 0 +M_PALSEL 0 0 +M_VBOX 0 0 +M_GENERL 0 0 +M_SETUP 0 0 +M_AUTO 0 0 +M_KEYBND 0 0 +M_CHAT 0 0 +M_COMPAT 0 0 +M_ENEM 0 0 +M_MESS 0 0 +M_STAT 0 0 +M_WEAP 0 0 +M_HORSEN 0 0 +M_VERSEN 0 0 +PRBOOM 0 0 + +# List of Sprites +[sprites] +# Empty sprite +TNT1A0 0 0 + +# MBF dog sprites +DOGSA1 10 30 +DOGSA2A8 14 29 +DOGSA3A7 15 29 +DOGSA4A6 12 29 +DOGSA5 10 30 +DOGSB1 9 30 +DOGSB2B8 13 29 +DOGSB3B7 14 29 +DOGSB4B6 12 29 +DOGSB5 9 29 +DOGSC1 10 30 +DOGSC2C8 11 29 +DOGSC3C7 14 29 +DOGSC4C6 12 29 +DOGSC5 10 30 +DOGSD1 10 30 +DOGSD2D8 12 29 +DOGSD3D7 14 29 +DOGSD4D6 13 29 +DOGSD5 9 29 +DOGSE1 10 30 +DOGSE2 14 29 +DOGSE3 14 29 +DOGSE4 13 29 +DOGSE5 9 29 +DOGSE6 13 29 +DOGSE7 14 29 +DOGSE8 14 29 +DOGSF1 10 30 +DOGSF2 13 29 +DOGSF3 16 29 +DOGSF4 13 29 +DOGSF5 9 28 +DOGSF6 13 29 +DOGSF7 16 29 +DOGSF8 13 29 +DOGSG1 9 30 +DOGSG2 12 29 +DOGSG3 13 29 +DOGSG4 11 29 +DOGSG5 8 28 +DOGSG6 11 29 +DOGSG7 13 29 +DOGSG8 12 29 +DOGSH1 9 25 +DOGSH2 10 26 +DOGSH3 11 26 +DOGSH4 11 26 +DOGSH5 9 26 +DOGSH6 11 26 +DOGSH7 11 26 +DOGSH8 10 26 +DOGSI0 10 30 +DOGSJ0 10 30 +DOGSK0 11 27 +DOGSL0 11 20 +DOGSM0 12 15 +DOGSN0 13 15 + +# End of extraction diff --git a/data/rd_graphic.c b/data/rd_graphic.c new file mode 100644 index 00000000..97054f51 --- /dev/null +++ b/data/rd_graphic.c @@ -0,0 +1,166 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Convert portable pixmap to Doom patch format + +#include "config.h" + +#include +#include +#include +#include + +#include "rd_util.h" +#include "rd_palette.h" +#include "rd_graphic.h" + +// +// parseppm +// +// try to read a ppm file header and get width/height of image +// + +static unsigned char *parseppm(char *ppm, size_t size, const char *file, + int *width, int *height) +{ + int maxcol, numpixels; + char *pos = ppm; + + if (size < 2 || !(*pos++ == 'P' && *pos++ == '6')) + die("Not a PPM: %s\n", file); + + numpixels = *width = strtol(pos, &pos, 0); + numpixels *= *height = strtol(pos, &pos, 0); + maxcol = strtol(pos, &pos, 0); + + if (maxcol != 255 || !isspace(*pos++)) + die("Invalid PPM header: %s\n", file); + + if (!numpixels || numpixels > (size-(ppm-pos))/3) + die("Invalid PPM size: %s\n", file); + + return (unsigned char *)pos; +} + +// +// column_pixels_to_colours +// +// make an array of colour indices from a column of raw pixel data +// (random access to which is easier while building a patch column) +// + +static void pixels_to_colours(int *colours, unsigned char *pixels, + int width, int height, int x) +{ + int i = height; + int *colour = colours; + unsigned char *rgb = &pixels[3*x]; + + while (--i>=0) + { + *colour = palette_getindex(rgb); + colour++; + rgb += 3*width; + } +} + +// +// createcolumn +// +// make a doom patch column from an array of colour indices +// + +static size_t createcolumn(unsigned char **column, int *colours, int height) +{ + size_t size = 256, length = 0; + unsigned char *data = xmalloc(size); + int i, y, top, transparent, opaque; + + for (y = 0; y < height; ) + { + // we are at the start of a new post + top = y; + + // count transparent pixels above post and opaque pixels in it + transparent = opaque = 0; + for (; y < height && colours[y] == -1; y++) transparent++; + for (; y < height && colours[y] >= 0; y++) opaque++; + + if (opaque > 0) // this post has pixels + { + while (size < length + opaque + 8) + size *= 2, data = xrealloc(data, size); + + data[length++] = top + transparent; // column offset + data[length++] = opaque; // length of column + data[length++] = 0; // padding + + for (i = 0; i < opaque; i++) + data[length++] = colours[top + transparent + i]; + + data[length++] = 0; // more padding + } + } + data[length++] = 0xff; // end of column + + *column = data; + return length; +} + +// +// ppm_to_patch +// +// convert an 8-bit portable pixmap to doom's patch format +// insert_x and insert_y are the graphic/sprite insertion point +// + +size_t ppm_to_patch(void **lumpdata, const char *filename, + int insert_x, int insert_y) +{ + void *data; + size_t size = read_or_die(&data, filename); + + int i, width, height, *column_colours; + unsigned char *pixels, **columns, *patch; + size_t *columnsizes, totalcolumnsize, offset; + + pixels = parseppm(data, size, filename, &width, &height); + columns = xmalloc(width * sizeof(*columns)); + columnsizes = xmalloc(width * sizeof(*columnsizes)); + column_colours = xmalloc(height * sizeof(*column_colours)); + + for (totalcolumnsize = i = 0; i < width; i++) + { + pixels_to_colours(column_colours, pixels, width, height, i); + columnsizes[i] = createcolumn(&columns[i], column_colours, height); + totalcolumnsize += columnsizes[i]; + } + + patch = xmalloc(8+4*width+totalcolumnsize); + + ((short *)patch)[0] = SHORT(width); + ((short *)patch)[1] = SHORT(height); + ((short *)patch)[2] = SHORT(insert_x); + ((short *)patch)[3] = SHORT(insert_y); + + for (offset = 8+4*width, i = 0; i < width; i++) + { + ((int *)(patch+8))[i] = LONG(offset); + offset += columnsizes[i]; + } + + for (offset = 8+4*width, i = 0; i < width; i++) + { + memmove(patch + offset, columns[i], columnsizes[i]); + offset += columnsizes[i]; + free(columns[i]); + } + + free(column_colours); + free(columnsizes); + free(columns); + free(data); + + *lumpdata = patch; + return 8+4*width+totalcolumnsize; +} diff --git a/data/rd_graphic.h b/data/rd_graphic.h new file mode 100644 index 00000000..45e620dc --- /dev/null +++ b/data/rd_graphic.h @@ -0,0 +1,8 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Convert portable pixmap to Doom format + +// convert ppm to doom patch format, with insertion point +size_t ppm_to_patch(void **lumpdata, const char *filename, + int insert_x, int insert_y); diff --git a/data/rd_main.c b/data/rd_main.c new file mode 100644 index 00000000..e79dc8fa --- /dev/null +++ b/data/rd_main.c @@ -0,0 +1,162 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Main program, parse command line arguments + +#include "config.h" + +#include +#include +#include +#include + +#include "rd_util.h" +#include "rd_output.h" +#include "rd_sound.h" +#include "rd_palette.h" +#include "rd_graphic.h" + +enum argtype +{ + ARG_NONE, + ARG_OUTPUT, + ARG_INCLUDE, + ARG_PALETTE, + ARG_MARKER, + ARG_LUMP, + ARG_GRAPHIC, + ARG_SOUND, + ARG_SPRITE, +}; + +static void ATTR((noreturn)) usage(int exitcode) +{ + FILE *f = exitcode ? stderr : stdout; + fprintf(f, "Usage: rdatawad \n" + " -o \n" + " -I \n" + " -palette \n" + " -marker \n" + " -lumps ...\n" + " -graphics ...\n" + " -sounds ...\n" + " -sprites ...\n"); + exit(exitcode); +} + +int main(int argc, char **argv) +{ + enum argtype argtype = ARG_NONE; + const char *output = NULL; + + if (argc <= 1) + usage(0); + + while (*(++argv)) + { + char *arg = *argv; + + if (*arg == '-') + { + if (*(arg+1) == '-') // allow both -switch and --switch + arg++; + + if (!strcmp(arg, "-o")) + argtype = ARG_OUTPUT; + else if (!strcmp(arg, "-I")) + argtype = ARG_INCLUDE; + else if (!strcmp(arg, "-palette")) + argtype = ARG_PALETTE; + else if (!strcmp(arg, "-marker")) + argtype = ARG_MARKER; + else if (!strcmp(arg, "-lumps")) + argtype = ARG_LUMP; + else if (!strcmp(arg, "-graphics")) + argtype = ARG_GRAPHIC; + else if (!strcmp(arg, "-sounds")) + argtype = ARG_SOUND; + else if (!strcmp(arg, "-sprites")) + argtype = ARG_SPRITE; + else if (!strcmp(arg, "-help") || !strcmp(arg, "-version")) + usage(0); + else + usage(1); + } + else + switch (argtype) + { + case ARG_NONE: + usage(1); + break; + + case ARG_OUTPUT: + output = arg; + argtype = ARG_NONE; + break; + + case ARG_INCLUDE: + search_path(arg); + break; + + case ARG_PALETTE: + palette_init(arg); + argtype = ARG_NONE; + break; + + case ARG_MARKER: + output_add(arg, NULL, 0); + break; + + case ARG_LUMP: + { + void *data = NULL; + size_t size = read_or_die(&data, arg); + output_add(arg, data, size); + } + break; + + case ARG_GRAPHIC: + { + void *data = NULL; + size_t size = ppm_to_patch(&data, arg, 0, 0); + output_add(arg, data, size); + } + break; + + case ARG_SOUND: + { + void *data = NULL; + size_t size = wav_to_doom(&data, arg); + output_add(arg, data, size); + } + break; + + case ARG_SPRITE: + { + void *data = NULL; + char *pos = arg; + size_t size; + int x, y; + + x = strtol(pos, &pos, 0); + if (*pos == ',') pos++; + else if (!isspace(*pos) && !isdigit(*pos)) usage(1); + + y = strtol(pos, &pos, 0); + if (*pos == ',') pos++; + else if (!isspace(*pos) && !isdigit(*pos)) usage(1); + + size = ppm_to_patch(&data, pos, x, y); + output_add(pos, data, size); + } + break; + } + } + + if (output) + output_write(output); + else + die("No output file specified\n"); + + return 0; +} diff --git a/data/rd_output.c b/data/rd_output.c new file mode 100644 index 00000000..c70fc370 --- /dev/null +++ b/data/rd_output.c @@ -0,0 +1,143 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Output wad construction - add lump data, build wad directory + +#include "config.h" + +#include +#include +#include +#include + +#include "rd_util.h" +#include "rd_output.h" + +struct lump +{ + const char *name; + const void *data; + size_t size; + unsigned int offset; +}; + +static unsigned int numlumps, dirsize; +static struct lump *dir; + +// +// extract_lumpname +// + +static char *extract_lumpname(const char *filename) +{ + const char *base; + char *lumpname, *suffix, *c; + + // strip off directory prefix + base = strrchr(filename, '/'); + if (!base) + base = filename; + else + base += 1; + + if (!*base) + die("Empty lumpname: %s\n", filename); + + // copy the name + lumpname = xstrdup(base); + + suffix = strrchr(lumpname, '.'); + if (suffix) + *suffix = '\0'; + + for (c = lumpname; *c; c++) + *c = toupper(*c); + + return lumpname; +} + +// +// output_add - add lump to wad +// + +void output_add(const char *filename, const void *data, size_t size) +{ + struct lump *newlump; + + if (numlumps >= dirsize) + { + dirsize = dirsize ? 2*dirsize : 256; + dir = xrealloc(dir, dirsize * sizeof(*dir)); + } + + newlump = &dir[numlumps++]; + + newlump->name = extract_lumpname(filename); + newlump->data = data; + newlump->size = size; +} + +// +// write* - serialisation functions +// + +// write a uint32_t, byteswapping if necessary +static void write_u32(FILE *f, unsigned int n) +{ + n = LONG(n); + + fwrite(&n, 4, 1, f); +} + +// write a lump name (8 byte string) +static void write_ch8(FILE *f, const char *s) +{ + char buffer[9]; + + memset(buffer, 0, sizeof(buffer)); + snprintf(buffer, sizeof(buffer), "%s", s); + + fwrite(buffer, 8, 1, f); +} + +// +// output_write - write wad to file +// + +void output_write(const char *filename) +{ + unsigned int i; + struct lump *lump; + unsigned int pos = 12; + FILE *out; + + // calculate wad directory offsets + for (i = numlumps, lump = dir; i; i--, lump++) + { + lump->offset = pos; + pos += lump->size; + } + + out = fopen(filename, "wb"); + if (!out) + die("Cannot open %s\n", filename); + + // write wad header + fwrite("PWAD", 4, 1, out); + write_u32(out, numlumps); + write_u32(out, pos); + + // write lumps + for (i = numlumps, lump = dir; i; i--, lump++) + fwrite(lump->data, lump->size, 1, out); + + // write wad directory + for (i = numlumps, lump = dir; i; i--, lump++) + { + write_u32(out, lump->offset); + write_u32(out, lump->size); + write_ch8(out, lump->name); + } + + fclose(out); +} diff --git a/data/rd_output.h b/data/rd_output.h new file mode 100644 index 00000000..bcd6bec1 --- /dev/null +++ b/data/rd_output.h @@ -0,0 +1,10 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Output wad construction - add lump data, build wad directory + +// append lump to output wad +void output_add(const char *filename, const void *data, size_t size); + +// write output file to filename +void output_write(const char *filename); diff --git a/data/rd_palette.c b/data/rd_palette.c new file mode 100644 index 00000000..25205cae --- /dev/null +++ b/data/rd_palette.c @@ -0,0 +1,93 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Chained hash lookup to convert rgb triples to palette indices + +#include "config.h" + +#include +#include +#include + +#include "rd_util.h" +#include "rd_palette.h" + +static unsigned char *palette_data; + +#define PAL_SIZE 256 + +static struct { + unsigned char rgb[3]; + int first, next; +} hash[PAL_SIZE]; + +#define HASH(c) ((int)((c)[0])+(int)((c)[1])+(int)((c)[2])) + +// +// make_hash +// +// killough-style chained hash +// + +static void make_hash(void) +{ + int i; + unsigned char *rgb; + + for (i = PAL_SIZE, rgb = &palette_data[3*i]; rgb -= 3, --i >= 0; ) + { + memmove(hash[i].rgb, rgb, 3); + hash[i].next = hash[i].first = PAL_SIZE; + } + + for (i = PAL_SIZE, rgb = &palette_data[3*i]; rgb -= 3, --i >= 0; ) + { + int h = HASH(rgb) % PAL_SIZE; + hash[i].next = hash[h].first; + hash[h].first = i; + } +} + +// +// loadpal +// + +static void loadpal(const char *filename) +{ + void *data = NULL; + size_t size = read_or_die(&data, filename); + + if (size != 3*PAL_SIZE) + die("Bad palette: %s\n", filename); + + palette_data = data; +} + +// +// palette_init +// + +void palette_init(const char *filename) +{ + loadpal(filename); + make_hash(); +} + +// +// palette_getindex +// + +int palette_getindex(const unsigned char *rgb) +{ + int i; + + if (!palette_data) + die("No palette loaded - please specify one with -palette\n"); + + i = hash[HASH(rgb) % PAL_SIZE].first; + + while (i < PAL_SIZE && memcmp(hash[i].rgb, rgb, 3) != 0) + i = hash[i].next; + + return i < PAL_SIZE ? i : -1; +} diff --git a/data/rd_palette.h b/data/rd_palette.h new file mode 100644 index 00000000..db40ca9b --- /dev/null +++ b/data/rd_palette.h @@ -0,0 +1,10 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Chained hash lookup to convert rgb triples to palette indices + +// load raw palette, create hash table etc. +void palette_init(const char *filename); + +// lookup colour index of rgb triple +int palette_getindex(const unsigned char *rgb); diff --git a/data/rd_sound.c b/data/rd_sound.c new file mode 100644 index 00000000..524e106c --- /dev/null +++ b/data/rd_sound.c @@ -0,0 +1,72 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Convert WAVE files to Doom sound format + +#include "config.h" + +#include +#include +#include + +#include "rd_util.h" +#include "rd_sound.h" + +// +// wav_to_doom +// +// loads a wav and converts it into Doom's sound format +// makes lots of assumptions about the format of a wav file. +// + +struct wav_header +{ + char riff[4]; + unsigned int size; + char wave[4]; + char fmt[4]; + unsigned int fmtlen; + unsigned short fmttag; + unsigned short channels; + unsigned int samplerate; + unsigned int byterate; + unsigned short blockalign; + unsigned short bits; + char data[4]; + unsigned int datalen; + char samples[1]; +} ATTR((packed)); + +struct doom_sound_header +{ + unsigned short log2bits; + unsigned short rate; + unsigned int length; + char samples[1]; +} ATTR((packed)); + +size_t wav_to_doom(void **lumpdata, const char *filename) +{ + void *data; + size_t size = read_or_die(&data, filename); + struct wav_header *header = data; + struct doom_sound_header *out; + + if (size < sizeof(*header) - 1 + || memcmp(header->riff, "RIFF", 4) != 0 + || memcmp(header->wave, "WAVE", 4) != 0) + die("Invalid WAV file: %s\n", filename); + + size = sizeof(*out) - 1 + LONG(header->datalen); + out = xmalloc(size); + + out->log2bits = 3; + out->rate = SHORT(LONG(header->samplerate)); + out->length = header->datalen; + memmove(out->samples, header->samples, LONG(header->datalen)); + + free(data); + + *lumpdata = out; + return size; +} diff --git a/data/rd_sound.h b/data/rd_sound.h new file mode 100644 index 00000000..555d70fc --- /dev/null +++ b/data/rd_sound.h @@ -0,0 +1,7 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Convert WAVE files to Doom sound format + +// convert wav file to doom sound format +size_t wav_to_doom(void **lumpdata, const char *filename); diff --git a/data/rd_util.c b/data/rd_util.c new file mode 100644 index 00000000..e4baf9c7 --- /dev/null +++ b/data/rd_util.c @@ -0,0 +1,113 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Useful utility functions + +#include "config.h" + +#include +#include +#include +#include + +#include "rd_util.h" + +void ATTR((noreturn)) die(const char *error, ...) +{ + va_list args; + va_start(args, error); + vfprintf(stderr, error, args); + va_end(args); + + exit(1); +} + +void *xmalloc(size_t size) +{ + void *ptr = malloc(size); + + if (!ptr) + die("Failure to allocate %lu bytes\n", (unsigned long)size); + + return ptr; +} + +void *xrealloc(void *ptr, size_t size) +{ + ptr = realloc(ptr, size); + + if (!ptr) + die("Failure to reallocate %lu bytes\n", (unsigned long)size); + + return ptr; +} + +void *xcalloc(size_t n, size_t size) +{ + void *ptr = xmalloc(n * size); + memset(ptr, 0, n * size); + return ptr; +} + +char *xstrdup(const char *s) +{ + size_t size = strlen(s)+1; + void *ptr = xmalloc(size); + memcpy(ptr, s, size); + return ptr; +} + +static const char **search_paths = NULL; +static size_t num_search_paths = 0; + +// slurp an entire file into memory or kill yourself +size_t read_or_die(void **ptr, const char *file) +{ + size_t size = 0, length = 0; + void *buffer = NULL, *pos = buffer; + FILE *f; + + f = fopen(file, "rb"); + if (!f) + { + int i; + size_t s; + char *path; + + for (i = 0; i < num_search_paths; i++) + { + s = strlen(search_paths[i]) + 1 + strlen(file) + 1; + path = xmalloc(s); + snprintf(path, s, "%s/%s", search_paths[i], file); + f = fopen(path, "rb"); + free(path); + } + } + if (!f) + die("Cannot open %s\n", file); + + while (!feof(f)) + { + size_t size_read; + + if (size >= length) + { + size += 4096; + buffer = xrealloc(buffer, size); + } + + pos = (char *)buffer + length; // don't assume sizeof(void)==1 + size_read = fread(pos, 1, 4096, f); + length += size_read; + } + + *ptr = buffer; + return length; +} + +void search_path(const char *path) +{ + search_paths = xrealloc(search_paths, + (++num_search_paths)*sizeof(*search_paths)); + search_paths[num_search_paths-1] = xstrdup(path); +} diff --git a/data/rd_util.h b/data/rd_util.h new file mode 100644 index 00000000..59a9cd0e --- /dev/null +++ b/data/rd_util.h @@ -0,0 +1,38 @@ +// Copyright (c) 1993-2011 PrBoom developers (see AUTHORS) +// Licence: GPLv2 or later (see COPYING) + +// Useful utility functions + +#ifdef __GNUC__ +#define ATTR(x) __attribute__(x) +#else +#define ATTR(x) +#endif + +#ifdef WORDS_BIGENDIAN +# ifdef __GNUC__ +#define LONG(x) __builtin_bswap32((x)) +#define SHORT(x) (__builtin_bswap32((x))>>16) +# else +#define LONG(x) ( (((x) & 0x000000FF) << 24) \ + +(((x) & 0x0000FF00) << 8) \ + +(((x) & 0x00FF0000) >> 8) \ + +(((x) & 0xFF000000) >> 24) ) +#define SHORT(x) ( (((x) & 0x00FF) << 8) \ + +(((x) & 0xFF00) >> 8) ) +# endif +#else +#define LONG(x) (x) +#define SHORT(x) (x) +#endif + +void ATTR((noreturn)) die(const char *error, ...); + +void *xmalloc(size_t size); +void *xrealloc(void *ptr, size_t size); +void *xcalloc(size_t n, size_t size); +char *xstrdup(const char *s); + +// slurp an entire file into memory or kill yourself +size_t read_or_die(void **ptr, const char *file); +void search_path(const char *path); diff --git a/data/sprites/dogsa1.ppm b/data/sprites/dogsa1.ppm new file mode 100644 index 0000000000000000000000000000000000000000..abdbafd96a28d8efdc9a0297328f4fa33d562005 GIT binary patch literal 1723 zcmaLXt&U-8$bx5z4}M7!M%)_%XYWt<9DZ8w_@%zQpytyb&xTE*bXQz5G@B1*sCAB{%S z>Ga>-hQpyqg{-!Su!F&1Hk%~_1h;cH&o1zb5q0GLcBs{Fhg zu83`?(|JOaCtzg3>-BnwDvrlv>vLfNmptsF#x0?*ne4a7a(q0^CQk5WDeP6Q4NwkcA^ubirq;iPyed%B3N3hcpBt@<$ zgH}~6fXWH=1T!(j(ZiElh&-gAd<sSUdpPoKibF0f5hK z>&mIpD4aX>9GzIB7M3G31Pqo!k;=-@txm1ET=reJ4=~KXLHr@aT44VHQ*O?5 literal 0 HcmV?d00001 diff --git a/data/sprites/dogsa2a8.ppm b/data/sprites/dogsa2a8.ppm new file mode 100644 index 0000000000000000000000000000000000000000..46d332e523f85250cb8cd47d189553dc5e8be796 GIT binary patch literal 2362 zcma*ouZ~(#5XbR45|F$=-++W<+cZcdf;t2ei9jM(Adv_p5>bgjA`wU=0*T}i=x3RB za&zy(GTEHWoip=2zd3){-#_Z@pWoZR>g~6;`nOi=`QLuOKb=m`b6%NUtyY)I<#xLn z!#SVN$K&yp+G@^ryS-koI3Rc4KgTDv>-D-?@+cx1jj+Sv5R$0kZ#Em`SY@}{Ef$MM zgpBw5eKZ2XaF`AJ^WQ*VBPc$b&9e4WXSrOCMk8VJTTY27V#3jsgl4npP*^0G!f|07hh{kpiUx&p%odj} z`Nb7u$}=P;4mTf*epyO#$FxjaiRLqd(U(pYN(MAC5JB>^P%W=Uq+v1~XKjoZM}tna zEP__r;VLI0Xp(a_w0YV(1*DlgtGq(PQJyG{zBEW4ntlN#I0aTrd*l~|$x#au%|@rG z%@utFDaHZyOO>DvRGu{2Y@I3Zy3^?tt6ucgqA}X?_FBR%bwyu)f)Tor`` z1B!6GaXy`r188UB8kCqpHfm5)y1Tb^%%5mLf>l`p0S(H?muaf{W|L|4V1h`FG5Epx zIIug-a*<#m%9BuXVxuUrZWy1HRxza!CXV(5=q=bTn`vEYp^Qw#sGMa`gn%z^dob-M ogJp3v#t8af#(|RgB`tQqF97p2^up=&dY+PU=9G;-#Y@}u7kSpvivR!s literal 0 HcmV?d00001 diff --git a/data/sprites/dogsa3a7.ppm b/data/sprites/dogsa3a7.ppm new file mode 100644 index 0000000000000000000000000000000000000000..d5e1aaff86b5a98b5436b573601666575cc01a96 GIT binary patch literal 2536 zcmbu=A&z1}5XSMPt6kqG2Uj==tQ zzHFxhZ(d-gLOS)jtN#5}b-%~YAD#Zw_x@9-|M=MX_VCdD+wFFT!@<}dcH=6}>2&(@ zZgI}%vnN{^Z-mrg5^-Sn`~C5FLg}etuShP3QBuFywl> z8q(-X2^LDC5o$7-{Q7TvWHBkhzQSg+StHG4bQwrdIOt06I5+;##7@ay$dakHju(AS?d#%9?j*$t_ zRciBCZ{6YxZd}bt#5r;{Ui{O)s5L6M6m|PgI zcB5L8c4Usen3Rt!kyZi4x4$o9#^bRV&V^+!s~k&W_?*OlB(=LeG1kgkVpjbBm#*fc{=*Y?MsUjOxX|Gn3Ld+Ysrd3pRh91h3h@pL*__xt^0!qdohyFH)J z2-eHxVr6Jg3zIM$4mqpUDg=7SP!4syUacZOM2*Me)GA?LmBo*+ZdHFPv)5v=P`cag zwDwsUd3C$^kp7VLt2k{A4d*VWp5Nv> zSf-Gk+x`6)$IKK684lmdTtwTxjd4?#!^sWeFp?UR+k&oGJ1(67GoU=2w*0oZrW`Cw zBRQFW?FgI_^Jb-r6=vJMjd8<65036)LDANj-xxG4Y85(UZv|i(#h-B0%Fp0|M3RTg zd+7Gjj8HDFarefc3xZM(Qr`K{0PUq^)>vB)+Sl$fDfFBMRlTgY#b+LEEreOGt^01- z1>xA;5gEuLtj^T6pCs6{US9W29hB~(iZFdyk#mDt6Ooqps!k|=P!T3GRz@O@poC0& nDZFzAgF*hG9G7G>Nxzll(-=rd+FkAAr_7Sfv2}JY!BX-K&0FAy literal 0 HcmV?d00001 diff --git a/data/sprites/dogsa5.ppm b/data/sprites/dogsa5.ppm new file mode 100644 index 0000000000000000000000000000000000000000..e8aa14960f0ba63c897c50e1c6af85ea27aea8ba GIT binary patch literal 1723 zcmajftCAW~5C-6AUIDXjKrXeZ$`fGY5J)5fi9{fg2qY4LL?Dp}Br*cYYvhA}t80qM zWV^P`_VoOhK7G#c`)BXlueTpxd*9#RdtU~FuD|Q``sW#vF3d~)d_Ldr_sivSI2=OX z@Aog5_T%YvLc|ic+s&hdf@p)AmECR!qerF*IvhVGP9~GpYLyNVIbtmqi%+}dC=kOs z9*^twdODqMx7%i8?R>dhrd8caHk*x#9;}+N5^1wk=JUDsI124?*JtSeHzlq;13|~~ zNQy$%7caZ0ie?bQLi9=neLNl)CW$t{s%Q)J1xg$d+4cKkS7$Bro0H;Zn(P*U7M7DFV*=z=Bvm#t=o3r^iqc$ZK`{D(IC81+Q z%v*P$i^w95Ou?|MhFthAF5+-FWS4Fc1z)X(+x3|c#9}JJeexbt3f_c$p5g#Y%#h?bs0oL?TT(1$DE^ z0Tmt5WYV5BgMAu49O1EO5@)URBG&a#6wZs3)c}P&YEdRzM2WY9!2rt;+?K>s=Ao0s YCG-o>WL1vqEv_EK$0(owLN8>JzZ>VNH~;_u literal 0 HcmV?d00001 diff --git a/data/sprites/dogsb1.ppm b/data/sprites/dogsb1.ppm new file mode 100644 index 0000000000000000000000000000000000000000..f60a70f6f796fadade25e390e9b9093d7cc19376 GIT binary patch literal 1543 zcmZ|Pt*%-@6b0aFEE3a%2e|#oZ65%Eqz-{ZB9KS~5{W<}5lBQOvZ4}MfkfU%pkK~P zCMOJM=VtCYd)C@}?a2(ke)PWn{QUh(@7w!(@6+2`{dc`y|1|;ByLIM#KL2UrZxdAh z*R+zs-|zRs;SlX|xmbTU0T<2_I3ACO!(p@85Ej1VY&LshmQTAHO(v81d`>u>PFJhd zXf%Qgrww$+R8VZUTV$qSLZ5ABusT|d$K%Cfp)5JR~=eV1n!dXV33>uZ~XYwwClJGk}gKz$LgRI z(FnFH`Lb6CI6`quAQgc7{r;rlA-lOPPUCjFS=A}bTvWDswgqIx6dxZSv}so)fMlkC z0TLMo9*+kJp(|&iwxo^m)zq2!aR3r6@D&$Hwv7e5 zkR3xk;`yZ-_Bm1>y) literal 0 HcmV?d00001 diff --git a/data/sprites/dogsb2b8.ppm b/data/sprites/dogsb2b8.ppm new file mode 100644 index 0000000000000000000000000000000000000000..488ac4d0a3002c8cbd164647df1c22c8ca310620 GIT binary patch literal 2188 zcma*np{|-i5C!1si{u5GAdnMV_WS+$e7;;R);P!G zaj{su%&h{}>-Fh$l72WGLSnsMuV_WsbULl#AN}EQn4pT1h49zSw%aWRs+c31$K&xM zLy4ebtJMmU3O63mza#Ubx_?zXr2`ZKQOqKpBa%oQqc z{aJNYLL$pnrRlRdXO47cDs!N77}%(?dzV!OS@fHsYT$RD;qr(@b*GWDN8a zKa-FiRugM{s`8ECp^8s1N&lm#O6yW}NslC}hf`N|JgWE_U$5k%DqV`Cmje(^O^Bm} z{6Q5-8l`pBm(8=ft7H{na?{CtJ^&)9VmY-2uhJr0ClMB&;+Q&xK$20YAoWe0Dzo86 z09(Ez=9FJZs*W2SVS`~fkhn3ITC1Hb!ZOck2s}=9 zOWNUy3|qlm3i(tLLNAWZ2ODjrt|TTHDoT(5`)4ak?i@#93)mWy_}5(l#0<}8f2U|s o0Z3%<`D^9)JQL*D8a7CFr{r!6pi+dzBr>!<_I&R*ntwdM0oJOvc>n+a literal 0 HcmV?d00001 diff --git a/data/sprites/dogsb3b7.ppm b/data/sprites/dogsb3b7.ppm new file mode 100644 index 0000000000000000000000000000000000000000..ecebe0dbfebb80e440fa971fee0d517e16e29f54 GIT binary patch literal 2362 zcmbu=p>AS96vpw@yaHz5ph=VNW;GAcbt@{7h)N^^i9{eP5l93QS%IwN0gyaG_t$>e z$;qYMHrbPA^4+=L{Lh&)bLsCNgWI3qZ+{JL@9qZQZf^Sj4u`|>czk|-?zesV7Uz6E zpH3${&yNw9VC~V;xb2^<`0{HawwApMpg*r`oT&-3T&F6C>#e>VMNaHo8A^|M{ zVYASPHknNR{0o{vE)e1Cr*-s9sVgQOAx@(7Q!*{s72x?88rk9us`)eDikDi={LK)N6N&Z~M4g24=xSC{4*k8lZOXW;ur9$-q}CBN5syhb zb+|^g2?ev&{qoyXF9v6ORBE6K#Z~= zG;!IJO6bu6`h-(tuxOq0>LGMqX&ss`(fNkvs)&y5i;p>0z#Oh>T@tj~lP{v!G~YH; z9$-$&W=EsZ*CEyEi@*UC$bh~+t^3dN&|tv{JwQeB;J7DjoiLxb#(a-xqF$=n!>W0F RZVd=Un-uvM7EWd?{0rCLtMUK< literal 0 HcmV?d00001 diff --git a/data/sprites/dogsb4b6.ppm b/data/sprites/dogsb4b6.ppm new file mode 100644 index 0000000000000000000000000000000000000000..c3535ee4e3bdc6fca7a392ceccefd75451dfb270 GIT binary patch literal 2014 zcmb8vp^_Rw6a~;|z9N6HRirjm(Fi1Q2qcmZKq8Ql2qY4LL?BnLh>RS8MDhu{hq<*~ zDVUy3Zc(qA*YDoB?{&}o{5}|d`#StF7(PD_zC1lW{!XXU?++XfhsT1>%yc>}dF;#O za=l*PK0veV_Altn=W`0^=kxhbi%5x0)AZA`OJu*_r-_#qWc(F5;!Uom69aa4yPfv2 zNLd_@M^Ph0*Z>R3nvO0Oi^XgMi7XL$@o+_lV$nt{M$8#vT!fA9P+6KurWuJ+7E3(ac{NL}t=#O$00A%rg)X5nV=P zMpMifzJ6;?l2>nm4i=#&X4=$J)1y-gBH~ z<|$?}85c8m^*w)cZ$%=^z-?0P>G!biw({1v8;q4xBXU>P^Vey*6?u=$lMU1OF9qVc zpG4S*2!($fY4L_j0SqK3O9V>6rwc?!yR9L?Dp}Bocu{B9I6q5`jdnKqC1B=45X2 z(s_@074lMDeebPX-Oay0o!`B;kH4Ld&(F@U_xIZGa=Bcu*Z<#9+j_Y?pU;oS<9IyY z?|0wTYPH+#ULsS3LD3~~I-THKDC6;XzuyBK4hLMmd{q)ZHQH=8D3H3`ZZ}=F+igTz zD9r1EQgxvtexj_`>*;hFS(MD8(Q>&YV$3G7+wIQh^LS&7L%9};O>io<7K=qFx)?l^ z7GwQ>9}9p!8}P!C+m*?NP1q?5~<#)-=-UL%~H~@C3`Knk}B9Kw>=;U?7r1sEm46^gCcE*&~;MEFGPC8w;5UERysoi>;U@5>|%K~^EDl~+@- zaz5;3UQUEOe}M{CCS-*Z%Z<2q_c)u)u!JrpIJqK9h7Az~J(g6O6ac#c=V{5^zD5$e OO^S-TfO)xF7QX;t7E3_@ literal 0 HcmV?d00001 diff --git a/data/sprites/dogsc1.ppm b/data/sprites/dogsc1.ppm new file mode 100644 index 0000000000000000000000000000000000000000..5c558f26a3db207fb4a41b13fde242f48352d7b1 GIT binary patch literal 1723 zcmZ|Pt#V>P5C!09EE2PCke{t=)fSQmAP#{9pJJ zLOvdkA2{{&a5xa|_j^KMyWPUm=~R)LTxKw71wLWZH0;*vwR}mdF`AgoW~0$)u~^LK zb5o-VHMz_nkQiz4|9uCo|0S))%jL4y>tV7Z?{>RJdsJ;QnUKS^N@lfMS;9#btvZBu z`lBS0_#6rKIT#E=&d9VrA1+x+s~mBFe!m}m5;9Fr01Y_yOmc}ml7|ZkENPmw1WetX z`c`4VF}5Znj|prxo0OD*d(fzm#c()8b~Pof#`Z$u&2aZhy*gTtEgnP7FEh3i5A|}n z+-|q~{jLI2=b&@AB$t?}Bjx1-AvF57@*-SQNiMOj^hOwtp-mxXo0emIhP?Edrg>#; z-=`# zBq5+Z`Fu9Y>d>xLb`F*hOsS0cyo|QY&gF`@M-}26l#K%nn$$U#Q=ktEQZ$0`k2>bQ*b-iA* zaxRz4YPI?im;}jyr_-sB#6jz5yw>SR-RR9`hNJFdvV{*=+V5iTuT4@$&Mr z+wF$KA%D4C@^Kmnp3vGlY9-Mr{`q_k&Iu1%%D3As5&|f}^WB6o16pCN;R!7aD*8NxKjeKZ8n=`BZ4wPFfc$2&(F^h6NdC6lR2u9 z=uw(U0uoOKLh!+0AWmUM8Vw3bs4-9&;KRcMY_HcV&1RUcLlr!>A0HoRs|*N599ikN zRh{7!fd%v`jFBTanM|tis-K%G2{=XasPe9CaW-2hB$AZ?6wwRc-33bk!{E&rSAn!T zfZ%GuG9OpIue9S-gRQ`7(1mI_pF`lNY)NvJWyn=X)6X-$*S~$>1f&JNN7c-)MRWn1 z7Odc@t_5=zspU7fGE-f$0fKulxo$1kHX+rBDx?-vG1}t0r`UPa_=5w~@W&tr6QQmezRUOH9$+r(d zF9=m+@pa)r@}=FHX=_IiHYl}u?`2F4NRNUOHMcTH&{Q1P`cDpqJWvj$!G?E4$9t@n;zlDDQ C;zW4> literal 0 HcmV?d00001 diff --git a/data/sprites/dogsc3c7.ppm b/data/sprites/dogsc3c7.ppm new file mode 100644 index 0000000000000000000000000000000000000000..56aa3b197a01e570af0d77018e3ec1390961b202 GIT binary patch literal 2362 zcmbu=p^hR!5XSM-yaMJD9$=GA_Ad7X2n6R4E0G8!5`jb_kjNEC1QLlrA`!@wJOcM) zzGOmXa`5AxZQ5Q z3X5~SUd=w;SWln_0q}f2gYe^^S^6vHa=HBY_&6L6X!rZwh`<6g4b!wP<2mwPSZK%N z(JrcR_@#jq?8hKsx7$4v>b`{8Y_?vnIVpC(-*2~Df|+NDV_NbEBdbFy5!(X{6L2z_ zj7Fp3aA;gC7FN+5h$X=Jd|n}x(`YlY&1OT7Erj7P*ztI56h~vx+yHchf%kenOkx;F zfR>;{aM0-In93|A{mGHj?RM49Nu^~b%|Yd402>r$7a4J!Rpsfq)}U6am4ucG0}Kt{ z#zeHYTcEI{SuGDLISpe8l;e)bV*z>u6RecbK&vcKqREZ9cM2U{du5Px`F1@s<>Ta9bs&OK!%&!pl%NWu zCvw>H_KNSslEcZ@0K5dcXv#HGab1u9ES6>vw9(V+K<5@(x&@F?sGq$lR%v6%ad5)N zlvxL+jWL-7@(4>yJ4rz@7$;BkEVZNIi&ucA2rM5K4oFEeeaI~?oeyQC85xZuvkpX% z$q0+DSjM+KW=VnwMfLZao|h&4ibc-EQ}I zJP@pZE939?`qnJxScJf% zQA<&6dSrI>lXxI}gc2zn4{qTJDFgO&mrkH=r_$G06hdSD1vz@Dv}uk!UetaM_nVE@mtnco#LnVdKk1@cuz zIbxhlhN6hlQrEt%apU#XnJ_EOa9;i*LMv(cfg5t^M<;xJ(ra|ByVrx|@&WOk7)Zve z89t@r&e0|5E{Y-733b1IOqonLP%QV7-u(Pswl2xs19d`tV=3PrLK(8&o+GPF=Pq0S zPoUgOtzBhbD~Q_S@aZxi(zJol&&f>cEzuGtIr%?=V@ecZIw0|;E|H*88(^BGDQ97Z hYKwVnh)S zrxUW7ojqsf%Tb7NE|(3m z*}`u&8!(*`TonZyWNl}&nL^g&hg4m6Dq7{Cq`-{>01vQQtvISnk|`mIH2Ch;DO1{t zg)19FwCy4rUw6~#)LKMbbM97@!L}i)g_w%_{T>~LiF7MA*J?8IxV4h1Un~?FG|pj7 zt=c*Yv6_wS`FxIEuvZ5xgGGmA$upTuUcrgT>F!JeF?f-fEvdc4ARVb#Ca7^Mx}o(F zE2zgLNi7PiXkp6np365zs!~O{@p%0Ia;_$$wuXqyC`9B`=3d9kjMQ*5PbA4;7R1wHC%5v5wMyAmY`SXf>^#M4LvOL0mvLwBsDq-_m)|a=p>hF9$UoMy1?RLFhtGi7rsK?{6 z-|vC^(P*^YZkxopemESC$0L1=@bP&35)^#3S`nj+^s&O(?RFWw))@|m-EMa_n@y)v zqp5MdUcUsF%jIM;k#|0y4+aBZu-0o6GgULxD}yZmfGob;!tpokpWpLMQJL&bYz@pk@lLZS-VQ&oDwT3OuBx* zAO7idN=?ZT+%B1PJ~xvh93RoTv4ofs!VJ*&f zyX{V_?j8<@+wCUzdc76~giKs;wOUp2?|h9&ftU5?WvA20=iirM23RZ>@6yGm)9HS{ z*A%e6;H8RC$QQ>dVwcmQO8W`{0H?dzY_{9&P=&`DomL{>Nx~q-x?C`KEt0ZyYe!p)w4gP#tz-UP#1C_! z!g7a8M-JU#=}SB*Ri2uUsTxs5BXZr(+@G9FYg4tv@~Oexz|V*Ac;IAowobTY<&yYR z5gwQl19?85NOF@ia8xC$)sbs@E*{4`gAfNrb9=o$9#= zJ3is_#v=xgPa0}R(7})`4}|`Z7!Jq8JTw>lzqt~FaiBThBF1ni(W$e`@x5bRJq~s=Ga0COt=w6OS8}S4=k3eyAYrRvF?8W~6FiFk}t^c)ZQ5p>ks(!`9@AS96vpw@yaHz5pb1I4S=|R<-3lZUfxHrdtgJvH5l93Qi9jNG03?sV{>qn~ zOeh!9o-~tl=YI1)-xQV=i`Cbz@ku;1^8Hy)1(6jcb2MtGb|CRa?})cwFUO}E=M zl0_L*(PzGxSDI(~B1n=GN$xEOpAz=uwV0(G`o-NUHam+p{8k0J8xJKRt z!({dHtGozq${c}P4Vc3s#?-|Xm!Pv_Wg*~)^;`R0Q zORkF87vWghb5D@jBylxhD=~vU=?ix(j;iIbp?Wx6E}J#{DV-MlBNx49+1Yrm7vPC38yS1yHGy{waR>3 Nk~RyInKotS{{h3ydQ1QS literal 0 HcmV?d00001 diff --git a/data/sprites/dogsd4d6.ppm b/data/sprites/dogsd4d6.ppm new file mode 100644 index 0000000000000000000000000000000000000000..8c8e2e84c81efa689c6643dafd1480782ccda66e GIT binary patch literal 2275 zcmbW&A&z1}5XSMdL2>}rA&^K8fJCfBB9KS~5`jb_kVpg)IY(fB;Y(`C zGV}KBR3@F8>gs=gRo$=o`PS*aeeeG0bYEXP-(Fsx{|<-4`Fy@yE*UqQ&2vseWH=nw z?D2SfzuWbCz1?o{1uLPA12`U!Pp1<|EW(OxstqSOXey#1w%hI0%lsQStY9-RlH{yb ztI)RFEvF1&zu#k`8O=5W6*!qpMx&91>-BoESj^}1<#LH{6k&jL0-{Z)(`KMq1WTY` zXS3O_y9tVfi6g;|X8uV7u}Y@eNhMf>3?^s_jXVx(rXg4bVoWEY!W3Z$>LrZB8fcbh zR)HjiW@I>mM)Fimo@R0yXfT7Z?NK*AWBG2N)y|8;c9E;vTb zWV+E3q9^R$=|A`F5v330wWL*Au(}j&QI(dq*H$!++ofr+P=mn$%E*7rY3aKRsW+;O zp|xE8R~l7lImDp8*0Er5LMzml%S??rmRnIB6R#4yrQXjxfE9dpa+<~zTiRTVOd;UZ zo80`0W{JDO^5LgOSWqHabo%-e zROj=#Pb(Sx{eEv`fk{N03Wd{v-EJ32EKJFbo6V+SmZ?>tEDJ{X0$;LlYM_VJYE>k5 zfp5BAum9@Ob)8kCRTd%=MHY)iUAe}{2;1$}b|L{3k*sMo)=#3#5s8Xjd M>1XiMBzac90mUm&rT_o{ literal 0 HcmV?d00001 diff --git a/data/sprites/dogse1.ppm b/data/sprites/dogse1.ppm new file mode 100644 index 0000000000000000000000000000000000000000..bf3bdafb11e9a285cba970862d6a46121b85c5a0 GIT binary patch literal 1723 zcmaLXEpMVh6vgp%Bt;@I`wiHabepCUfn;3;5{W<}5lG|;Boa|sQCW#VA}cD9d<6EF zJ;`M9>;n@rxifSA_uP4T`2Fkd`{UQ2-|l|g-`{;14C>$W^RwaS=jX@A$MJYP91ivB zjd-)!tk-MEe!qWxeLbJgGThLYC3>v3SS;S(-#Kb;Z*N7AlFBf_%gYN8GMmkIyB%xo z&%d@>t<22l^9EAD?RE=XE|-(Z1SNn73_tL77cy^K1})!+QeCCjXvX^&CyBImLSDc$-xobl&WI`NH(!@w zBT}7CCzSjGV%w}Wt#RrJs8m5pZ57v(HcNcDT=X2VK1^(_spGN>=c7n_)Q4>xLQRPS zqR1Rb91B>|mZ0EH0OF_?DVXiKRt?j;^}mHs2l hC`($6^ zx7+D-S}vEbl*<{7M)Uc6JRbA6+wJ*$20;kQ6gf;mnXh2~ewyUnZihf2-$oqf;7}|Z z$^Dk8)`?_Jr&EQPv)RlPissnia1dv(ujm{OhYDfW>$Pz(7>Ixf>Z)D9sSQv(EviWs zWK=PdpnAQY2tt4&;#tba3N!qNpDABN2|XaJ*=)wQG?`2i%r4Y^^z?Y-=(b=@VWtWx zB%s^vDksv`OdPq@YAMuDeJdgzpD*|jn4ncOwT(tYGOTH*)9Ls7B|!(sqfHO_WPAm* zBmoVCBydbY*is^a0s1b0tJUf&h2mgpjbFXu#``Y_R$plgB+Bj)cdCq{QaxBq*U;Z#c zl9I(@p`W4YTTpo>)aFe+EPoOt11jTQGBeuf9+ik>mM%$-!E%hn zvZ;OJR6ATF=PghonIXqfh!#nSXKUjVdEo8_rkvy>m%=#Nf=`t+$^NWrs%ldS(tP)n z7-P!+yB~iMF;Bkw&sjrd>W^BQa;D0=PHiGydZsR?%x~p|Db3kkq+-e$c#L18{R7lL B*#iIo literal 0 HcmV?d00001 diff --git a/data/sprites/dogse3.ppm b/data/sprites/dogse3.ppm new file mode 100644 index 0000000000000000000000000000000000000000..4b0c171e63db57adb08d5066cfff4794ded5bd3d GIT binary patch literal 2449 zcmbW&p^_Rw5XSLno+58>Ra9Lnr+EP45J)5fi9{eH5lG|-Bm#*NpWpj0z5era@7vSUhyPBe)Af4gKddYTK#P}pdaA;}A zj*_&&YJsJu4QrJGSP%g#n#+>p(>S1kWca0^9t22gfYGMDUL!VJGju?J;+$z?`}8nC zs|0|yxTAT*p%Bm{t1JAPULGs!naBfdWCGd|(>$FNV0y0;+Tu1KX6Bj3bGsUg+sGv! z4(}!yT>{sS=C1*f!}N~3jSM+JgDU&}Z-4{nv*g4BbRQnC#OJYQn9m*WxM|it;Fr73 z<;j=Lac7>!I2a5*uj;OnF!FTGXOa}Q-QK&kjgNJan1(>7H`n*%u5p#=#lYR$Mp}|y zJvvitjC8t?`5M$u9MD+GA*xZ_hE;4|3N2AdC$o`xx=UH8CiA)7Zm=HZ^HU=hhql}w z^2C;VMD~;A0xxMqCT}?Tsx$&671&XYg$N82xfqT(J}p?nG}6JOhZ%ce!rB6=@CATj zDkh_Y8L25#Nr2cq!{@=74AYK+G~WG+ISX%1yxkdiF@zH5)Oa7^ZYHUiP%e+k)H^RR ilh3c&R7L@--?X;D$}96eR;0f)e4?$^J5yu7`CfBR{CfBE+MTK#P{o8$5L=bu$`s=?7{G#n0%Y&;$x z4u|vke7#;Tmy3~|PN(g5Tdn;Z1fgU)o#G&?q@MxwNiAKnnRrK4wH3`HxVElZ0bMV&W_o3x8*`nNcJ_3ZeY%rV*w} zrm<-nO=G`X1B4>dLTLPMR0^%AP}3%p3Aiydah4u(9-)+&RYDI_o{K@7DP{^^4$Zp9 zkyfQiR+}j~`I`|W!rJXlZH@7DvWhA}jytT+8gvl^6^LUpPK(_e3z-%3ZR%FEmOyRcV(KWVE%d z`uHw-?oL|DNkUt#R>&-!_iE)C%qXWDt58ncu#fxTFLMx3C={5C!0?c?C@0pt()j=4vE@>kvpJ0*OQ*kq9IbfkYsY2qdxsiR2OJm!4$v zXZh1fvzeVe=ggVipZ@>o{QZ1+``3AUfA9QxeXah^=kw)q`TB|KsM;#p7AOX1laHQ^!c(95sIx8#r1ma6jGP# z^_nhvk-T@=N9J|B%n6;XY)T1_UCctc5-&1OTVb^O_E*6nsp8gGno zC@1Bp($7|1YX(BmC4+~eQ7fz0>mkC>_dvM9wiR3&p%A3+5`cfF1Z)o4#Q;CLoD>P;7ww7 zf>uNo6nPUb_tR4}k(F~!gu!!rm7mfD5{7e{R`2LtqOV@HumwN~D@YX|%yc?Uy_G_c W8ayPttz76Ro+)V*GhnTNQv3$D`A=8? literal 0 HcmV?d00001 diff --git a/data/sprites/dogse6.ppm b/data/sprites/dogse6.ppm new file mode 100644 index 0000000000000000000000000000000000000000..4f5dd13375bd603551745155f830688eec23af45 GIT binary patch literal 2188 zcmb8vt&Sp55QgE^a}|3BlT0$3>{YN1fkYyZD-kVQB9KS~60s7ASh9ejm6h6}CFXj45 zYo$9?*|Ll{k&(}PNg}2wlj1N+L_QTliT4t^L4~Yopsu0#6n;VpKI*HB%9<>fOZh=z zb`!??Wo?`Gs>~I`2x8Pn+A`*~r+Pl)cYkvrfUI20hawP(ZL?97JBm#*~_1|Y&QQI>jdt0J9=q)7-LqJ%jJANkH|Qcu=Z3uF|3hxB1Q^1DrKwH z%Av?%8pq?Y@o!_A+D655-fRxqW|lN#kpnNFuh zDX<(jBF31h6_(wnOp!R`DVgBtERlC9jns5QFylL-#5_7BM)_*bMLVS(scOP za@LM%``L|D7oBSYZ^y(>Kg$R%*Ys&5Ak)&6kE{bvv6`Q5JaVcP$LS9gZ20Lw6=wQW z)niyz1giy*R7Y@}VkG!gO6Pi*O2f&w>c>c}X=m=6fMAJSEEYbh`%`kg>Z+c+$?Dw7 zr%0{F`T*z*wlncf0I9($nZmo!#{9(IWOZ&0heMY|s;+vbbQM)AIy*o^>QZ2J3Z|B_ zlpmdf-!s)EKv-@Tzl~Kppbv>omzAT9j(F7pK#@|EgULuO!FCmHH*b^?3Q!Zs@aeWs zFTND7N?xJdf)tK)SsV~!48$ppaIRfW;k2DF?dBIPS3J$e;&X~lPF^`@z4}dIYM1iG z=vdN?#VItx1Tc||3}ahf@xx3-!k7Y+8;B_j#L-MlrUg#Hl|9q{ardgRN^F3#z6>XF@+XIG-8u#rTYTx>YSU(aT+ z@9*zcwFGX+Tdh`M2pd8?a<$1g(ucz#{_%MH{QR6PQkh6ws)(Wt6+P)x@a5$N&tx*; z<3aoQ_$VUISL@ojh^tc61&IOf|bBSM&K<0|q^AUdV9kW*C)li1K!9>LN$67QO~7$>RXbyksG60bQK3w$Ptlew03 z;#38>+|<2BQpZin*weL^(dJWQs(f?H%Zsqiy6e+PBxamNBtOHvn|=}C;Jhh0?ubOl zv~UrBH|UZOiPMQAq?3bR@DnWaSS+kzpW!O(t$VR_%Bpo*o08&ZO9pig(3p&bdQ VFQ?a^A;Hhw4J0w0g0*w&$UmcS*E#?I literal 0 HcmV?d00001 diff --git a/data/sprites/dogsf1.ppm b/data/sprites/dogsf1.ppm new file mode 100644 index 0000000000000000000000000000000000000000..b0da06bf669511ba7233537dfad6da329a0e9a7f GIT binary patch literal 1723 zcmaLXEw5TZ5C-5n5|Btt{{j8TExEb^NgV=-L?Dp}Bocu{B9I6q5`jb_kVyVQpY}~R zC+9*ZWHV>>{g|DzhhIOMU*A7}|I+;S_SXE=?UvtcHd`*29&fkX^?JQrF6Z;P?EWV~ zT(8%-D5ukDzu!L|4lug;sv8fJV;#A z==b|S|3;_NL8qn-?uAtnTma$#SnYPZ)oNKopH8QgSrmLVA&6^+l18HuE@E2JNflhn zb*n4w!N=E>GAP7Xj+)FdW6C#nh$yeR#manQip3hmqx+Ksnt-#^nzRKhC&Nt}+>-0W=RnGdP*@9huK->pys!juM#tmPnr2>6QfK5=3wYy2k%(6;XJ=s- z(HVXFp{wgNTTi*!G7$eZD2NH8K+Ii*p;MzP!l>&F1(yPA6kFy)6D9gJu1Dh4K8+{` zL^LJJPl<|POmn)JdJOj|CDs5m(_vPRj51j7)SLR)qzZ8X1~24T&Pb4*>sP&{RRsru z2@=cw$=k=W<&(5l@g+AmTVQ!{F$&9;tFGGut|r;F0$5AJOa7|QDA*OmAzdNo8Vm+1 I)~bd4H}o2xBLDyZ literal 0 HcmV?d00001 diff --git a/data/sprites/dogsf2.ppm b/data/sprites/dogsf2.ppm new file mode 100644 index 0000000000000000000000000000000000000000..90c0d303ffb217e97fb951cfa4c7b66f8d01dad2 GIT binary patch literal 2275 zcmbW&p^jof5QX8@SYBY?fP`d*tmXk&hZu=OAd!fbNCXmzKq8Px#7HCpiR2O3Z#YSZ z^ktc3E0a!jch%pgs{1m(-&@`HpWR=r?%P}IN2l}c-{o?-UazOqX}8tf%nBgqcs%07(C`_UxrvvK&jNJq ze2akU)x(VRKb7YQG`XqNm@=cS-|xHMXtPBgyO;je*h*edjut>eZj&B@@tu-*Te(?R zpn-zLG_}csl9+m>r>?SsMjI4Kc_9>%D(tDSA|Q#%jH;~TP?FB)D=V#N(yA2%QVU-# zBtiKK)Tr~OZh^LDnH5S_wL(=mR57ZoXujI3$mUcl^p>__^f)3YlAy@T9W~QmtY{q+ zF~+FUKrJ=07l}`|aMZ{m%x&rz&EK}GNz-JH>JgPU_8%y44GfJW+HSX-?gi+1=XBHN ztrcN*bvRRfPNh(QOkz4IK;P0&>-3kbf{LR+SMLFcFWRKI2wD0j(Y59U;)f=(;#AYn z#Nz~I^g?hbbSNL6iJ{XG72$D;mR!M!H_9c$x-kx)IYRtndga@V$#V2P`)f!uk>iJB zq_!eFPL)IaYPHJI;vWqvQ!&gVeEp$$SLsjME`>wwJ$oI;caaY;Kc|mkntpyQmB5lE xolkHazGvyz2CbQ1t+d;1krYBn^a2^bjQRgqe!Hslr&GbH+9$yDCo2{!&R-UgvdsVh literal 0 HcmV?d00001 diff --git a/data/sprites/dogsf3.ppm b/data/sprites/dogsf3.ppm new file mode 100644 index 0000000000000000000000000000000000000000..ac3a33ef3f3c363c912f09e36d2f51e1cd27b3a6 GIT binary patch literal 2710 zcmb`|p^hR!5XSM-SS04&U~_xfOD;$rfORX7NCa{v0(laFL?V!gl`9cQBw{7BCj3yV(?KjXGaXWiLXNNM8S$Jf<`>++>0Ag42RrB76ykF zxFHcf8jZvX1XL8H>ZM;S`Ot5QY#tm~K>a2G29qkkp{;mdaJ)G<-l!2)TyM64^r}DO)bif2g;RO?=D$0!d4|E_b1ilJ;Z(6#-tOheF!)}<{S%Px>xL&1P%l}3YQc$8gg zzNOW6pnM8^y)q}3#`{a)>eS?XtQZDB@n1rA1+5O*kZ5(j&n=IpcI%dA1h@C zJ{%5PqBM;v7LP+DlUBXAQEz9A8-?cOpyFKxHU zLElVNrnClWTIz$UM5zi^|A~wS#z#Xsw3MC7^G4Wj0#^3{6(-yp1ju#~ONJE4vdfEX zQ{Mb3@T@|b>{1B`6ahyjNkPXzcEq1_hpSNiq`*N4e$gSvQ%}0XVHIP-MY**l2$73+ zqu3nchJ5=+(A1gFKZE5m?~Xu*EF|e-(|l;B$UY8DwA%iaz2tB9z!^r7j*+Cvo=xyF Xr8QL3@^h^J|4#)HoqamY_<;Na^rP{w literal 0 HcmV?d00001 diff --git a/data/sprites/dogsf4.ppm b/data/sprites/dogsf4.ppm new file mode 100644 index 0000000000000000000000000000000000000000..530665f82ddc8c07125eb38cf2232cc06933e938 GIT binary patch literal 2275 zcmbW%p>9Gk6vpvso?>sXgk($h6q`dJkq9Jm0*OQ*kq9ILi9}2yc>v@df&IdlHOtN2 zrQ_tLIc-n>{myA`-(PzDm&g8VumAkqdw6>CjYgyKc&sg5&UU*!9*>_trkmNCb}$$$ z7K`O_2|J(94~N6~e7;;RUsvO>-ELQdN(^nA%?4_}-(#TB#!jb`E}E8K2}GlCyaR?s`wnV9Ez3& zC~@+Lx8q;5vV~QV$0@XMjC07d9eGg5WnrrWlG1S)V8Q~Fa%edkgRPLppfJqegD!~# zp+eINsHFYo6kmdt<51|h8S!Pd#>vsBxhm17P4!ZQ|I)G}PBJYt^HR@tIbv&ZnA0h0?IN-pnEz2&Xu*;}kJfk&S_-jlnpr1^ncy(#A<~HJN4!~1 wB`rX!EODBwNb)X0F^=(@?`M>|u(@NHdD*m4rDZm{nPVd$KrMLmGy-(L0cNuo#{d8T literal 0 HcmV?d00001 diff --git a/data/sprites/dogsf5.ppm b/data/sprites/dogsf5.ppm new file mode 100644 index 0000000000000000000000000000000000000000..9932f429634a07683bf28ea1165844f2e4fc0d62 GIT binary patch literal 1441 zcmajet!_d=5C!0BUIEiLXxgMdo(G@~fkYyZNW_&$1QLlrB9KS~5?O&n@(TUhlWcDG zHtFPMGdpw6oY}d%pKrbA_s5ry-plK2@8RjmHdlaV?OiwyrSr>-|x!? zW-W~gb|;o0kH=%`%0?(F^{7-5w(1N91L`mpr7KbBsu@dq!{S7u0NRd&tIrBW1ceE< zk$iQ`lQSF+odv~lAjLVU!0)mWQ4q|kc`O6xb2N$TH-Q;cMMt4E8jXNM0Y(8cb1Ao? zqG%5oMV6_V-YQso3#Jnkz3~kJD=KYR+m~hO-^%_~$;&cB7pz=M#CF-@EAD8J*XxzT zJc0bmKPdp=!gyR* literal 0 HcmV?d00001 diff --git a/data/sprites/dogsf6.ppm b/data/sprites/dogsf6.ppm new file mode 100644 index 0000000000000000000000000000000000000000..3b723040b0c2c3fd494d40badb24276a16c82766 GIT binary patch literal 2275 zcmbW&t&Sp55QgD(BwWSb!6YOz$zBEP5J)5fi9{f|03;HDL?DqXkjNECB$qJz&|juQ zIJ<|=R!FD1PStz9IzP?NkN)7}``|}^@c!Qa_V!l)?RLA<>GZ3y^ZD#m{&YG`6=yP; zw6As4@p%0FvfJ&(y`OVB1K!gLpT*~C1JikqT@JVq#yCOL)^T5^H{F?yH#il$sURLS>bTF!-P zCk2t#zE&KW)`tf9P@b@r{Q8hi(+-Xvtzovqk;sDA%b!MRLt~FLonk&^c6<#DR;Sve z%KdTzJC>JefL1T^sDg3=S((`-LfCLPw1$y~7GRg!G6yP4hi36Z`@hZ2x@AK*n@v>Z znwRsI5i8@{81u=EDoR*xSq=$uS^E@G;_C|K6YYscjB@6-#;m_rnNPLlYkL4311LZr zT|n_>8}A&BA!v1Hu_(j#P3VfERLh~xP64R98OS9U>6#c zOjC2fNy=5iX=c0sX|g)XsAMO>CkU+tMr1x~_-5&XYUfIcDnMi=;+aUJ>$R|!_UO1x NmkM6-XqH!2Oml z>5!RO4mQ^zo$8|MUtibs^!)kN`uXGgr&1Q2r9Jbr-?J3tI-_HZ{ zbUGc6#~7--y}hyhS<;`MpNGTYXfzrO2JGVh%#gm{@69pTXt5RA?RH8m7K?tr|N8nm zolYl{3HuhZ+@SQ~EdQM+&UjP-UoMyUv)K%`-EQAPUaeMg!!%<8>IOd`BZFG-!lz$} zf3^h*#5jxHNtbBX^ZA@kW4&H06s%>6%Hr2{CCrW89*@Tf3*VNJUtV5p+359pY?{wS z7O5!4k{$7$Y$KTyJi%nxK8|3UH6y14VTr+H*aA`qd}GwTaZgnQ4)AI zo-jS3fz638pafS8vZD%FR-PuK5{6?aDmd}@QZ8jwQnD0WTB_~%W{LG+B0tch5ksxm zw#=vp$D?C;I%aJL7py;x@%}0gqA{T2%zjBVG(((h0hGg*w%6a;l67g=vX>>#ORkodM6$gj zu2~g@ML7-PZxk=f07N#*t~qtvJ%9sFxpxXV)r){jofTVpPMY9!6&%VkG)_ir<*#8*)DW#HK_ zF^0ybEtx+quk-gEQ<#B&OHb5t9`gN?C8aW#rQKy{OGaI1qlb9>at+GO#|X_|mm?=r aKqKcfrz|6Pe!2O1@Ut%^u74aG$$tU%Q1ui5 literal 0 HcmV?d00001 diff --git a/data/sprites/dogsf8.ppm b/data/sprites/dogsf8.ppm new file mode 100644 index 0000000000000000000000000000000000000000..e3eeb4599b68dac82165a2f818b5937235698760 GIT binary patch literal 2275 zcmbW&p^hR!5XSM-yaMLlfP`c>IgLPa4uM1>kS7sHBm#*maFB&ZWW(4Tl+*lyJnepgglk zn0N*ot8^`lp3nsIvtm~5b{kNZ66RoNITR}uN##|RbR5D$lUsJp4a8vSq0m#OD)K9; zl7dD+v^iF1Dp!1V$pBQ2l?FK~dX}QaAzmRg7lY->5oxgiC1!>ti}R{q!NlaoYQ#5{ zBSt9catpsK);eg@=``X^8!ZY6*5=ogT#v6SwLX+wVhqjb0AZt0Eknx<^sG&PUIb)0 z1=;{JNqp$hI7D>S?1dN`VArgCy+F#)(-4RV+vt@rk?4{t%%hyoXO|PjRl(9r(HY(F^75E4WKiP%qofN%+~kvf zB)k#S0!xt9LNs9cRu{1A^$H)KOc={CjhrBG>cWy}>EhMUgco@WLu)9jn1fS0MRLWP zEd?(nQEY`Ee(eb&UQKomcFJ+nHpJ@+#WSWfg<7{6yOClYmS!# za}F#Ce7FjHI!n$1;FByf!ZupOL3GK%k<&35nrQ`g5OsLNFDdQ`fTto@=#aKnPj6BP c3IJiM*1ylV{-R5KRD4$cVbUszKbaE$3zFwvtpET3 literal 0 HcmV?d00001 diff --git a/data/sprites/dogsg2.ppm b/data/sprites/dogsg2.ppm new file mode 100644 index 0000000000000000000000000000000000000000..edbc052cddf7f6d593b27eb1b8ca903d7b7e4645 GIT binary patch literal 2014 zcma*op^hR!5XSM-@Dle1BwY3`rx8fbtw16XNF)M@L?BlpkO(9afkYyZNFL$t$9zd= zGVH=tHl3R4ul`ll)6D+)+3NoI-u>O`zP+`+bvmE^9S(={`TXhD%i#5Tm3z5dZnqmh z>*;iQnOFqGAxP&J4klZ#*G2j0jK^ca#bP1xcswGQh3)tI-EIdf9xTmcdLkgh;ZVwE zvk5`gWHRx%-ELQ_6<|8l&wM`riW9LyG<<~Vbh=zFb;8heh@%8&G#WKH5@)kna;@>n z;(J7+$#fY_&)TbVh>auIsg9-J?>qRLgkTk>B=OaW=3QnxuZkL9&BujM%VjRt2vIeAJjc+I% z1n-C}?-qZ}YYa`j&Z`Kk?Y<$_g9 zx}nnl-^%=s5(hW+lLnwzb!-dAsEu%n?-ATlKY?UREnP8#luv;~Q#kgQ=@H~@DvDqf z4xsk_pOC}Ue;_!yqQ-pQgDX@7AP7u4gEELU*QT^8!d!roNZ@B{WId;oXh0l`>2hi5 zKR!Nk{Zxs@doLUgj+58C#&S2T#AHmNl)&MON8`9)>l_ez5L;x{I3kpC^~oZjRFQ$# zAs&s%Y)QX&P#IqYHgS(Q%qB8@9l}p7b!qHY+PkCdvSjX#8zT=n$qMl zC`{x>8r4K?HXG>@jdDRa-s}^4Bx<#1XQe%NsnVICM@4V3uQ_z%N-EI5gc+HyP0P6Mp)|=2V0ZwzjwPiu8y7h fU7Om?7{@2zTVP#DWd$d{cZniHvlz=(8I$!Ff*@-T literal 0 HcmV?d00001 diff --git a/data/sprites/dogsg4.ppm b/data/sprites/dogsg4.ppm new file mode 100644 index 0000000000000000000000000000000000000000..00fe4005b41925337aafc7b5d6b6965fddc896c9 GIT binary patch literal 1927 zcma*np>8Tc6vpwZd5Zf6O-Oo^J_U6MBocvKiCDQ3fkYyZ2xKLq@=648blWolgH7dvC0}G{)cUb{~!1@AuEf zZnqoe7_As`@iF@&g=0J(kHe7pe7;;RFPBR)e`_p%$%Cq@Y5=m?Yy^;;&u2Lw4hNeW z{k>kVx?)5fAjo0A-%}|h2FSMCEe!LRxdfEpHn|8*1)Cfa$&4iA1m+gB zXCPIKBv8pgCLk^XpiE}78OTTil>^*<&D;eL1g}Kiz>H#kWad014VmPrNpC%XErG9B zg1Zo55-@2;+N1%M7e+jJgt_cSOI<>IUrpxZF(_X)51lIb=2IFc&)PRgUIxj8kTGe< zhqgWEvWk{x U;*Nm-sO?UIupB2&wB3OCU$7;|uK)l5 literal 0 HcmV?d00001 diff --git a/data/sprites/dogsg5.ppm b/data/sprites/dogsg5.ppm new file mode 100644 index 0000000000000000000000000000000000000000..14f087c1e6cda9fa7ce38c2f7a559ece8d2f0984 GIT binary patch literal 1357 zcmaLWp^hR!5XSLyB)kG<6CPl*o9yP^0l_&05{W<}5lAEgi9CTsAdv_p5`jF)BXGav zOFA8v*~+9-UHz}Gs%vKce74^{U*3PW-#Q->+N^Z-A$J5dcEFmw@oSG&*!tOCESIKw2Um{s(4*hJ|tzqdY@mL{F zaYTPPJjR6;!uwK0ZJ#jOzIetR@52 riTFbbOgyk|y;(2IO*2gK2OgsYgYm6NL8j;fPl_R};GFXDf;Idf6v`T| literal 0 HcmV?d00001 diff --git a/data/sprites/dogsg6.ppm b/data/sprites/dogsg6.ppm new file mode 100644 index 0000000000000000000000000000000000000000..4637d61f1bc59d1d705200a8646186c6e7dedcd8 GIT binary patch literal 1927 zcmb8vA#Q>}5C!0BPSG2XkhDopK^+2#L?Dp}Bocu{B9I7VC883^0V;b0`t^V5<`2-N zoseaAcix*fv-|JsbEnsP?7eh)PfwkPZujo*y|L5jbUYrn+wI->c7S-m^ZESQ*hgcl z)v6s+@xkE&2PMRa(70Z&E5K|m7K`LH!X6HXx5iQtcQhIe27?NZm=vyd{${fw$++9? z_WM0TDQ2_TZwxAhN?s__<#GwaiH}Vo6%A0}a>>Y_&*#!jr&ErRVL-^KXn<96GB%*8 z!a0-4WH=mJuP{U{28D=BUT{FhVTJRi1`JTdM}JWcsBktOk13&JdMov68f>b-b{$q(Ny&j@(9awzC4Z!9y8hjEBsvD}0=OQPq!(M! zpl@N2I-drri^$3}Bt})Rd{F=Dr|kWH-^r?ixm`Z7-1AD__Lf$Z%cFaxkUw@36#*Dz zcMwb!nn1&E3{rS`nD@l!WxxO~o?*yoHe@%RI5BF>^A!@luNy7PIYxk~;C8X`X^TiG zPN+hXS3*FjsPfcMVp@2L!l<?EDQsjIu;HGv}=jJ59NGC#KY(@M~T!coV6 literal 0 HcmV?d00001 diff --git a/data/sprites/dogsg7.ppm b/data/sprites/dogsg7.ppm new file mode 100644 index 0000000000000000000000000000000000000000..9b4f6b608d83be8310e1aa0d4c3fffcc22624904 GIT binary patch literal 2188 zcmbW&p^hR!6ouh+B=ZWGeS=9RnN3#n0IWl-L?Vz#1d<0pA`wUg5{W<}5lAGDz<$G- zR65~_2B^?EoQUa4=^oKC0n z`Fy!tK!oIWyJ1R<&H866>YKG2v)NdOf~jh2IU$KqEY|DwY&N4p12M&sFPF=~U|?F4 zm+5pmg(BfWq8?{78c~hM;~WZBSm}1V4k$13Y67J`$3atuGaL^4{XQ+BDR^?zs!6~FciZ}N1k07I|xHG;vSus9?`~&HjYJLC! literal 0 HcmV?d00001 diff --git a/data/sprites/dogsg8.ppm b/data/sprites/dogsg8.ppm new file mode 100644 index 0000000000000000000000000000000000000000..11e969ad98150867ea881ea75502df7081cc5d78 GIT binary patch literal 2101 zcma*nA&**75C!16Cc8fYQ-|DS(=Dk+Al9ryAdv_p5`jb_kVpg)u@Z?uA`!^S{sj7! zlfK;WUI`O&Gk5NpGiT;r-p7xp*YDq6e?Gmwy*+(-d3o@++wC4i)?v%#@^m^~E|*{b z?07ti91e#%xG^%D&7`p3S3952*sKhXb-i9UCYHg;WU}3ENk|qpTN?KJeIj~HoA2-M zWn$@50*@s6F?@c0ZZ;bNMx&8*M>xU81nKfC+OXJ9=W)Dg77ec?_|}FTrNqdG?gi(yT-RyrJI9V){HT6 zU6_NhV91noN1?6*fUg)hPQmcN;HidStBbT&O*(C7fE3!_X5XLL^LF6^=rwRkp*IB97#G{@0OGS(vLT99=8HZg^}-Qa1oA zLU@KP6_E5Cza`EE3a%4`@n4`vC|fbqM521QLlrA`wU=0*OE(5i3_#Ad&q9de})` zHoL%tyv&<-@64TH_UEhh`TgGOw|f14>+R#?;jbu)-_kgn&E_E{1<&X6^?J?o{BSq~ zaWa{lPNxK@Ii679ff;|XSgckn^cpf<$K&yCw*!ciq~r0pTrT(feZtfnuY# z!x@c6&KK>_0GwqRhjlug^?DtVXYGJ>yWQz@Dl4TYg3v(Ycwk&|+O zR0#1Ig=V`!vqC74m&+vz$zvciq*|Jl=XSfvEJ!dqeDos7NUipQk~)o#1qOvl<&{b0 zDcX1C2PI^a!GKY`mczJzK514W;?jbJ{=1Gmm&dk*PL1d>QQ&p$HMlU$m2rSg!wmh! yiNcYANUw2n!U7B*D_W(*Q;ZkD6@|kjhiCS)O5v9~d;fj|X?r;(HnaTvD*hkU_$Y(` literal 0 HcmV?d00001 diff --git a/data/sprites/dogsh2.ppm b/data/sprites/dogsh2.ppm new file mode 100644 index 0000000000000000000000000000000000000000..00c67422a2fddd952d09cd5cf8c68f6e16528962 GIT binary patch literal 1573 zcmZ|Op{`<43~?{>SL&*yj5cX_P){r-47_ym8S_6}cp zDERC38df|V`PFJwunNZGu~_MFIG9u4)9EysOfHv;m{CaU&1O@q3Pz*Ra5!8pmvrXy zIlove(5ZzU1!|JTN^m?L*XuPFx@r`5yB&r7es36)p!*hat2+v$u?K@eDM&I&%D7m@ z5T`XS1|Wsf?RIhbUayCusR2~UA~G|hD>cUk07C>5GoQfs`+dl8(_O4flPe#vNtCA3 zDNLu+p=KmiADgjBvEt@Mz;fH$?G_6ifJZcHp2@(J^S!FBu|zzR*zn0f>Yi=MCFd;) z-wd>HOh*c25iM1ggeMjKCM&VwjTEbnS?uhF$y<;6DuQkeST-32*BFMO`HHU$VtU0> z@GU5Lh=RQa_95Sxj@tQ5X4JYvYOh4gpoIymBN(ELlGY4MwZ-*n9wkkA8|dm7ZXPIf z`4-X9O6Z~mkD>*s7`h{_>te50EeXQ}bO8R3_%ut1nExZSl$zaLW9}32xw9)7FfkUJ z&1Rk;I((Cxd{~egRqUwBEPUd+^C(n4Fdi&M$I)f_zt~tNX!aFTi-nlJA<7F{_|~zw M#z+e~f)>p2ABU}z4iNF`__+-w=eJSuYSkl@q9iX4u@9-PxHIo?sPg`uh+}v za=YCC0o?ERr}eGmX0vf3iqk2OvI=Vc(Gsn4jwZU@&-9+|A?aK=qJ^@p$}s12K=T z|N53IP%z=$v$`4excQDsK?6VOLAP_LZyEhDL4RKX1-p`>du}tMb>6<>L}u88dx<95 z7JC^IRt;*C3v0V~m#-V-=mL?HHTf#g WpYEiED4+kRh@_H808Jp+xxWGVQN4Qr literal 0 HcmV?d00001 diff --git a/data/sprites/dogsh4.ppm b/data/sprites/dogsh4.ppm new file mode 100644 index 0000000000000000000000000000000000000000..a9cf9ea019629c4e1261a89a6c61574358d7b2ab GIT binary patch literal 1729 zcma*nA#wsi5CzcaoFX?!6;;UvH~?`GNF)Ne5`jb_kVpg)fs8~TkqBgDu8_z63YD78 zK)OW20&BLp9ou&%;)o(lF4M!G);hbw%hG;xm>T; zR(Yt*gssNlJPfnh3`#HC>2zujbAdCtV(etMpdeWvIN`76hGNOb4a8VnCW`QSDSZ$Gj3o?Yn;SB~qX%R_s#R97B ze6d&HtTA-Y4Q`_J&2;?fmeo!oo5|@r7z~hDYxTuB*~|wQ`obpuKSFHR9Xt^YMWfo* z)zI4bI)eZ7zx=O3igONli!Q!54OF^IuoacXHjm7q#A{q=$YBBLZbNc~=jDus%*7u! z)JGuEWyneV|7{>Do*0TSGy34T?5?W2_9uzTco>pcaWSQ7_~SJT0~xt%T@oD=c@T0W z^yiN2?vsVYI2fFF8Pp5FXt1(Ev8PNqpw$+kiE8gyNTMOPp2_N2Bx1N#USl9EB>jFr ZdUzP11du10t~>dv)F?=&lDMDT>%#%y5>UZR7sKPbm}%*yWI{Q%TDNaswm#?_xVuZx7#gg z);YdgV5N9MEEK_Jvq1rnskJ0k)49Ah7%r3N5m9PpcL_(s}6$iy6&A@}%=esI@4fWbnvQ0Z^PNh{Doj8A^bn zvV6e=$$Qw$bkqwaT}h&lz+$%*w5=(Wif&C2OWGxem|ej$K$cPxR%{)Hn-dD@p$}H z#$niMWo?g#!$DsV=m24_*DD|GcV%r@@zrXT?m^KXJDpA(Df}I9DZR8#y<+3+b~`6O zYq5~|d_J8{8+jCK4bJ%VESJm0VgbU(!)9$M>a1ly4jST4mZeHy? z&SoT3N?q7@T3C$}E)p|6&ICR6>K-M}~!T^nFeFoYZ58MMHwhq}Xdc-(Nw@tu${m zQ-Z-lyp&gx5^m4#n(OT1=Tjsb1Tf!WhH_d(t+hulT2lhTfNi~AV_<=k5eA%sB?Gum zj(8;bPeWp4aac0Kz=@R%mP|Pa9b0}8jVIy)&@^p86y%;I(FB;W`6;Y9#_Hu literal 0 HcmV?d00001 diff --git a/data/sprites/dogsh7.ppm b/data/sprites/dogsh7.ppm new file mode 100644 index 0000000000000000000000000000000000000000..8c6e2f689035b513feebd2a6e7c4d24f28b5db07 GIT binary patch literal 1729 zcma*np^hR!6a~#y55`jb_kVpg)i9ohMA`wU=0y&aTV9)eT zDxL0~&b~}4uV3AJ-+fiZ{QmVe8vPtuKR(`myubJST`rgV{eHb(dobT^yq)S)9f<(0Hj=NYaX0w^5KCx_3I3ADxq_0*hTj>Z7xn8fQ)9GX~ zv8vOF0UnaHbSR)e>JqDCBh+4VY|q_O1wwBLy~y5m@rZ ze0GsN73WsWco=LDDF@O+Y;KLmV|{ZkM4SeZ!C=tD^yj%g2U5DK;(;DRiu)% z6v*-htf@)CP~>i;*ib0}P!!6rjHwe53|MjPnkD}?Qmpsag)U(=jr=!55kA3k5fntm XDFA{^g^0i-1}_1Yr-I%LEn4)y7%;si literal 0 HcmV?d00001 diff --git a/data/sprites/dogsh8.ppm b/data/sprites/dogsh8.ppm new file mode 100644 index 0000000000000000000000000000000000000000..e0b18b3a1be8b1154e1b7270a3f5f68aa4da91d4 GIT binary patch literal 1573 zcmZ|Op>9G^5CqWbB9WN>L7OJ+42$hYujhO0_j@`` zaNU_qCNOu#?LVDV>-D+@*Y#{RGZ73O{^u`D>~uP^Cw&FVXf&#c>fLS^76q8iX5*2d z0PgquvYNN?cwBQeQKC!)_xmrF%Ow`@a5#jK=ks|@RJ)5oSayX7K;Iy_u!wcLT}MO; zT}L0?=nP#f7U6;c6YTYR?RFcobfUl#Zsy`rU=?LB7|1+33Q7@9d5H>gUH%& zJSU@@t}etBM)QelU0#!devVqLR#t??vn3c*(>15lDZ{uTI|pE12IibXq14kIWv*|4 z?S$!B2uhS;3cVCCOaf!wzQyxd?cz+DZcx3VLR26^QeZoyFP94uO@fwIgCA-b9XB$9 zPUtZ}#Nz|LezY8qww;${U4@S8_o(l*rlf`hP`i=2boi8oBh#Jg`0(7lNzaFYm~Fd) ziwl$}hrU{^G;2Bvt|lUH#;M)flFgF&7Yu;aM0gzmX3rv0Y07zw0vkGoj_bV}Cv3xe zj6yu5FLO(rA9dks`gNdVS%s`dhlxmXL>#eExC(gTlNMfaxN?aAh$w2}Lmk_hQfBc( Slf4IU^AU`KKuOjSP2?}<5y#H} literal 0 HcmV?d00001 diff --git a/data/sprites/dogsi0.ppm b/data/sprites/dogsi0.ppm new file mode 100644 index 0000000000000000000000000000000000000000..4d8633627cb5aa530eceef6a37733f5632f5ef0f GIT binary patch literal 1723 zcmaLXuZ|i(5C-sSEE3a%2e@mSYto!VBB(2f|27?@r$K&aAs;{2K>-Bmx8f`Y4 zKey$$TrRU0wnyx0wOT9|-EMa{98RZG5XJp|e>@()zrV{o!vd;Nsb(^nTrQW7+rlF$ zd;~_q-s|;XTdfueK%O1t+wC?W!bhKaa-~7EOzw6&KmjT{s%Jn{u7YYdn;A|R^F~l&o?RM+ds!?^Uo(B-Yt=FrQ|J1{32lM%SrsAOj3P1p# zO|>Wz^1NXIc@vaoOU+%NeNP&H`pO%i#jV~sxvS4HgB8OPQV(wDmLi{@TCK-h6j==% z55>Y<>t@c$08#MI^C&CG!XKw+zE#3s|0Yd4;ksNR2xK+#s6j8}s$*V_b7h{Xn$WbJ%GI?cg z^&V_&2Mg|(2d(-j+A|_%ta21!Q(yjY!lu|fLvH6GgA(h-N_RC7GwfC17KpL21VRN{ zGLi))@a<5q#?nq8P?EmR_ByVCMrY+#nfGb85Q;s#EW^K3MB?g(?x)5RRVEJBkB{FL URJB|kE6DOQ@-MvpcPwRp0j*@<=>Px# literal 0 HcmV?d00001 diff --git a/data/sprites/dogsj0.ppm b/data/sprites/dogsj0.ppm new file mode 100644 index 0000000000000000000000000000000000000000..059a1404599049b9dd3a9221924c71c041a7ff79 GIT binary patch literal 1813 zcmZ|Pp^lS7-+;+%W)Vmvf^j^+N(2&#Kq3)HBm#**A`wU=55QxvU-o1h z@)K?*O>cWnPjCM+-@n_RA3uM6v_HST+CO@|cYnLx?p^9RJ{%6~I-k$~{5vAYnd zNN?<`t_X_XpmhSKsFaAMA}5j!%w!sL1&t9GOwX~@y_%o>L?;?>qkvVw1$II#3-C%z#NJLOlc+}**wsi1q%{oTyxM`tj28w@*x_xH}Prg{0d-ELpz|24T>E{hes+wK0wVZYzk zwBzwuwyV_&wjK_LlB|I$r_-qv&CyFTW-Sa>TGU6j=kwWVOgPK5{EN?Kvq3|U(FV-Q z7M95%ad0%7&5E#q`FxJgM+WC|xljS3dio8AhOo70%o_u|{i zi!e)3K-6udL&+2^&NYZ6%&h5Wy-7p?8aXOJKdxXp zox)T`NX0lveqH4EnzSSqERPr&wglQLxMI4;s7~c_` zy2vmq)w4sxy|4&(7S84@(B zEtNp9x?)N4T(Kw3UpUo98nfj|Xe;7l#S!U5^=`L|CX-nJ4*9KdK(MCn*C+US4*)Ay z;|Q6Fo&jXRAtpTKtsW14no)u+j48<~84r~Ma^$s%G0li+5L)YSIPCZP*|OqKNG=d+ z2iX6a(P%{CNG&}pt>@4rZLPEbAJKBibXH9YAR_~UFrSeyRxNqG#nexPt(AN-nbhR} G$A17G>!Az) literal 0 HcmV?d00001 diff --git a/data/sprites/dogsl0.ppm b/data/sprites/dogsl0.ppm new file mode 100644 index 0000000000000000000000000000000000000000..b736f59d1591dfcc7a948d2860700abfe2642763 GIT binary patch literal 1333 zcmdtgA&vr35CqWGoMLalW)k)SBM_K%8~}+xA`wU=0*OQ*5y&|J5{cj_voERa&(FXK zwnI8S)z#J2Km2@;n&z|l8a0#2=wm$oXTSA&y<9He9;GGTwrcQnI@PqrVxe0!H=7N@ ze!s6r>Kfs2IGEb)b~35iY=)ptAp?nsdN6fbX6N%c1ex0LcqHpn5fF|8tgS`?C^DGh zd_I?5m&=8qE-A(9I1$S36$n0!qlGL}DC4WuDoY2Mf_rmvJ)4?H3C4h8ps?L;nb*aX zd9XK3ctBfRk&9jxY)8nH@!YEr#O3Gn8L8Xt7C%M?HsF-;6}mD(vuZgG*(08l2R7mX zsEdESUP;-jFdL-nx`?BBbIjtwJ5Q%mh!vh1t?zpVgc>eL#edtj_%_xa{EL8EDe_!- z3L-o(!v?j-)KSR`g|K$a|vK(0W7;{Zqm5{W=^fRV^Kz#IUHKq5H|`=FA}>}-PA zAJX|U27hm>Xw8Gzo!L{3FyS4a_V)2!RpoJ>lOKyIo;Rwjxl2*~0QDf0ivR!s literal 0 HcmV?d00001 diff --git a/data/sprites/dogsn0.ppm b/data/sprites/dogsn0.ppm new file mode 100644 index 0000000000000000000000000000000000000000..db12c5c3317353fade206391a7548e57ba07a450 GIT binary patch literal 1138 zcma*lA#OrZ6olbwEE3ZlytD~LAfG@WsAB<01QLlrvH&C!fkafY03?fG8T5k*H{_Af zo-{YZ+&TX}=cV6Yt=iOc0;{nxa~WU^c?wRl=yqn3=9_Tg|y zxK*dqDYP;`18x#w!yq6-m6}ecv)L@;$=EkEu?ECXNrWtgkQwxb>2+7i747!tRsx&` z4aZbo?{qr8tt~VfAcFzYG9XSSM~2qYK&53g8VS=HmG2Lz#Uxc#an9$n?Zh}}IGLf) zTnz?;e!oxDb?qFF$1+f`P&lTAWgd^m5_3z3gX~sG27*cO?F&UD&!>Hym@>+X1yu$k zTzOai8meo2U6MiZHD)8G;;44JElD5<#1SJBz7Bf(A9EYBGX(qEF>7+H^EeO9!EqvF kwcCfk9=ePd6!P_rdu5CUs{AxCoh;_PXoG)VJsOJ-_F79R2f~<9IcT<3Y6*SUPoTC}ZQ_nk;2 z^6dvXBW3G$AJV#eTG<*^YgE1Anrq9x(Y#U9vNzNy+p2A~IxX*L-Zavx?X}^9Y&_(- zX4N9$b3TYacWwMR9>kw(&@A>k9>kt&-Zu7}4}$0ThNi)L!w2!_TEw5@LGWDNhAmqM z&xH?y=jyd<7(N$12%c-uvUdC6b>V}o=c;)uK36ULyr%V9*KXo-;e+6HEgH6{7rZxo z5IonaN$tA9bK!&Vxz@E?Ht@Zzf(NnZnsYva=X{X$+_mxNu08u)z4&wW;?K2g9h{d| zt$h%@w@#Bb;jx4dvh$W|cQhqj-y1#%o@?Nt_+0oPe6CgN=E3JR2p$B_-Pt_$-ta;2 zT$6^p%=d;5g6CS*Zr#46&xH@d=NdN*U)L&l5IlEx!)D>}g%5(~+BR<@B;JFSBsaC!>d=Nb6*{B)B7CZ=^Yh90z^SSUr_<6Nk)(M_#8axQ!+qg}WAdXhS zgB-l+Uw)dkXXr#&St>v6JT z4`NT(sn20}W`hUuGnlPa{ORC9*3)g{PX`aOo~~P`W$lK|_>}NL?3_gTiByPWWcFN> zD|bSk5hF7S3?DIa&6>4q+jY37Q({uruHCx#=$WHe?>>bJ7d~|8@ZrPRvS-h}Cj7a( zKR*Ko4jMFQ@Q@*?sYBC-4I9q8N2dG!te1+GJEd# zMy6-RK4!2VGL#SD^HS4>4Ikle|IObA4oMr{jIUkWF>!3Um8({*Uj5cvS6x;0YJNDL zwQJYCw|>L>8~OQQ)0V9s9&hpU($dlzHRhij>wtlc8#ie>d}MmY)(^KGIeP3wWI+FQ z8#cVZCFy^}Z}=TLC5G|$?AhyIm)}f8Ks>{9*WsUAT~Xgw;Q#U%zv!e^CF= zhHrHr9#ng-q~JH#fw2Vjp98zc_$~dj)t~s+#pC~A)21ySu31anOPTdvf)A z`RB6u>=o4xN%>@pJXZE`Q^HFLwQV`G3gq zamogH@pJVzZXEoZ6GHLF;y)5x|AQ$1_ut<#B$An#{I~dH;l~@l-i5MSKZ~F5v+&#T zi9xlEUALB85Pt@L4}OTA${#j<-MaTQ{*-w9w0^PXkNC4{e-Qr&s(%nazyC-4iCxFW zF8;9fqxs7oRR8rG;>{oNQ#6Bu_zk~F7=QnPgx_pLy5)c5sO?`6|CS*B-fMHy{*TPa zC_sVq4=#R-zXy$f?^yV>{$cU+eb#+yvBw>@{Wkt#^{0M?(OWBH7k{ky&&D4s{;>Ja z49{ZN_*2}*&u5Js88id@+xj1tfB$=ux^|D({&?|+@ozBx4`StS@ei`}WAO*a8^q7y z1@Rv{8-FV0ACJF40V;6+{+*KeiP!$D;*W(NkKga0)jFs4Tl{C^H+tH>@GcTI{+53( z>c8)#_+#z4607}9 zDE}~iioe<4fGChlNk& z=i(18q~PXf_0JW%`fdHa{4?Wie^~oRj0~@O;t#7o@zWp-Z@E)FMcliF#dr93IDM12hAVlKX~B(CjKz|ZmB(oriHcN#*grWt3Mw9 z04}k3>4&vHEdH?m2jP3?v+c7PO%8AVVfOb)h3*)Ey6aQ85 z#xDrp_&Wuce^C92-^+h6@pJJHZ_=b$P+kMiiQndr_=gOn{oAr-Xe2X%`hT|kgYc8O zar)C@@gEA~_hj?q=YPX^_0^g+{S(;t+6c<)c?Yy4sD_g%grGcy)` zEdG%rgX+(j37!}+q94^ibyzdXf9-&zZryW)@!R#Y^XKu<#Y#F`{Vo3ilnn9HA~)f_ z!SNFQ+42vXf8$T0`48exiN&93{kQTb+4x$l!)W^{eoH@A{Ko%3!jConVf^vJk5~Sn z_`UfB)!+DS{NwQ_gg20&_6O(CN1mCi_y==8H2!r1x%_)k`sdbu>Nc>^( z2k}$sJzl~el2!kM=Ko*f&y`UiPZ)o2NFL#^VL|xk)c)+**Aac#_60Zo;8ouHIOZV! zc;Qp`!^&^%55u?ka|g9QX#FVU^vpbdikEuq1~SstKNf%Rp@RmedPazUP*V4v#vk7L zW5u7{;_vzaKeT={e@8e^bRpByIHuur8;F0vAi^j9!`Zn0Gbw&H~e&)myCG3AJqMB-QyQOeV@Ml z!uaFGACEsCes~UJ#h>oCds6u#D+HfAXh@G7z5f}0ro|tRf9uw5M|fRU{Dj|Z9p{6P zg7^u~___SE;vW{t%+oiBpT8M@tW^uc_Xz%0`!h2;QGoFqe?0o|$kV<2Gr8*p4WE~O zEPP7e+8-2uZfk#V^;^i9l>gtuPmdKp@ly}_j?6_<7RKLc@Q|Lp`V@|ret7j~&kVY% zLHS$!#NUZ^^2eI~c=)+;=MKW>ok4raSN=Wxk;EVTCByJX z^dtVD`ln~COC+k^#2>bOvGB8VJd7?k`4fx3v*BC)Eq&te92{?&7xB6Ar=?o|!}vRA z#h;Opbv+uthcYrftL@9=CovrG^0FZO@GS8vF6JP9qPObD3O~H_5k8F{wcq%?u^4_( z{f*z6?pFohmCmD}Au;~G#P8j2_=xn3&cvVAyHMeOgrDh1rAI7$%iq^If{2BGxO33K zNK3N?KR5nisa>{g+0t(~<=-!n_#?S96Y}r^>tpPYV&NNBhS$I0XC`&A&u61${6XP+ z;PLSJyM-TA{dnOA;amF&Keyq>i{JVmt3yHben{}8e4}TC@N*dprz#_{3q7Mkg%5@C zdpQ1ZJltV>o-O{PiAfwB2Nsrowro^aPe?5O%*VOBg?GKt^Uz_#6_^2Nf|0>umU$UAtL9;;-`9VukN%;uKNz^!OM%#iNgfA1nTZ zSm_6!P4VZ+I@5hE{ahJ-p5K`ghM$`|nZ=*cr5m-f@S($&e>{9^B;ofA;x~N8c3n76 zRGA=tTdpoNXjHi29NpjIAD-5o%Rg5Tf7c*>+rW&>toj%pNWAc4rJtGEt$QqZ4?HaV zpza4R<-?6XC?8sXoBzo0;XRChq~+h; z_^FHD1A^*!Hm=}pv9|^n0Ks{R{uzBd`~-uy=~@hm7Jp`sWUsZXTM=g=JfdJ?B_2L~8;U2amci{P zBcn&pAp9VHzc46%;vX?Qnae+SI^~}`If&mnK>QqDtm+448k~Xft9*MNN;a(cS?B27 z79zpF2L*3j1{RiMR`>~LKR)=lJb#ZqcV(K+#l$CQU}OG){V zA1;3THXfdDi62#1Tq8z=g_w{P|Hu)&ss6e9{Ud&k@1JF#b?F;^9)pX;5Egy>i##6c z(AnT)(FcVeFa3mbK0k~ zzwhS9@*wI=Ru6+W`h)~NGWZ>~ z76}Ok?K6bw*BO8CUjGvukFDzwe~t#Xc3u~}B|J3W7uINB@Aq5!6n`XRq{W|*Vf=kv zm5kMacwvOc;k$w#zsjFT5E-|se+D0ZdYhVOIPZhM$mW8K8Qx-iOYGHtUi>BDbNnm# zTl{x$j(NuO8N4@E@V+SPc(|$gV{GTV&X@Z>3#&lzp}d2a_%@EiZ_bmz!#q}k;D^8e z{b<5>h2IpuopH4hk9}qq`_Z5D^IqwNR@PB+r?4x4uqgxhyN$hj8J}o?$ z;Pdzd0}p@V=QHbtM9FC=VEnFgKz$m8MZ#PIu#Vof9=SKr>r z-MfaL$@?NSB^*=yjlqlfPtShIzN>TA3Hkr)gKXJzz*PN|z~nX}R(hDptCz^5s>osLIbZ)vm2xQ$UyuBD^w{weCr|zU$De7H`!C~f(X#d3xd->pY-@05Q3W&D|$ zRWtu3{>=X}{(oLS|7H9WCsv*KFY!O21u|2zL>{7aTDdv*D1udjIH&6TTGzxDPz|7HA}H&@;K zFY#~wFXJcKiFA+m$#-}33+}^ge1_aPBavF@jc2h9*_dFcjvknXP528} zGw|+$Nm!3lsKSW+9!$U*97RP&Z*4ISt8oZr^OE_FF<610a7jM0_;Ej$;d@+^KN4w( z5m=0GP^18<;7G+n>_owWk;rWrfVub_c?v}$HzEZy@ey(qjzq3QPdtsyIE`!0i$uC& zGB)58s+=E*bizce!BJE!LM(U$t8oZrFJLPG9>nX|kBf^&B2AHrrPzxLFN{PQU>Fu* z7YY}PL~3I&=3@u)T@;DjjDDDfPmr^CBvKQ}n2s$tgKMY&T`?IOa0*o}rVKCv@8UO9 zxFiy}8)LB&2T{6YB+>$-upB?4c&SLF5k}%=e2eo-MG)?6sQ!5+=}}!8=oTQWgHKZ@iaE$G^$pPM3V3%*5NoRT^@s!5UC!CR6|!x#(JEITxW7`svUHa-*mF&m#C z`|W%tx?wWb<2WkS=Dg!!yn+2FQHSHia4f=36u5)y5q$*0tU31+q+&j{ zBUcOBQFO-?yoY0`a2MAwM&niNL6MfUaTtKv_z00!oIfOD0^Y_iDAk%W#Yil|PULG7 ziQIr*n1+owiAr~Kt}q76u@@J#Oi^R zUOb64_!VV3ay=p)FJUM0-NSJq8B?(V$56f#_X9kDCD@IEovB0UgQu|(Cs8pm5^0Um zSc-2^IEl6uDVUBAa1xce@LG(jP<+kI#@jnKmAsF&-h|7gMxiHHYDRItiutMPN6(wjWSfRFJfDomhFLK@~^GmfLoMCus^Vg}aZFiJd5+lUlQ!8_QG^Piv(fo_<9 z75EnUo+Jj`gE4poJCJP>eN42%NX)~BIEk{8`ECrvGk6cbpx6}d%SguKcoTb&|0%u$ z_h1YbV>_a_Y%13+(l8qz;5S@6jdlfnFbS*iJqkQcS)dcfU@^Ai3@T0MTp<-Ru>prr z>=~{n^uPqXj@`&TgKGqBkdArSieo7GEcG5Kn1of>gM2e7TeQdhScq*niPF!}XGLF3 z#wzSZzFE8n?U0ED_z=fYayD_IH=e*7_y)P>P*>0zBQP5qaTvwsa-YGy7>6bJ0%uU+ zdB%zufN6LeKOp}++CsEL2IgTij-dE_&JVg_9G2hw&U=9}LPtD+`PhQraM6pjGw6!3Sd33`3Z)luE|81~cojQv8s%T2?L%KYi52)7 z*%njoXpH`tf|d9NIbY_wLNg4)RIJ8#$o&fS8_h8Y)9@C)L+&N~4b3qKQ?VM~BIi;* z6HPGyPhll?Bl|Lr1&z=TPvUiag)=DkDt#>U#snEvxCa?{4)5YSWM4xa zLtS*oLs)=~*pIwxsS{|7J{XV1*os3aypFyuTA)9kz*2mS-%#W|$_cG72$S$CKE*K< zUC;SOYYfI@EXQX!iVHT-hN2Y);YlpRCpd!h-zRpoKz~fcEBFwHP;g@;awVFe560mI ze1QE(_<%A+J#@op%)uIbizrHO;@U+=49C-W1KV*NMK=>8?!tYTfS0ftKOtcY-+{X5 ziU;r<-oj4&j*GT(4@PSY#N$|u&G-okAJX5!9Y{h3W?&`0z;P7WM*jg#(FbEOAM5Zf z&ft=dsAsqvgYY;OV-xlv=g0J!aVt6^4O6fT+we2;e8RDy4mx85reZlh#xKbCDKVoC z5-|c(u^b=aAo6@hy+&viU2=8Mr&fwzhw7s|sDHw~n zcpG2gIL`Zmx_}1gigZlFtN0KHkaGup4BUt|=#NJ+A8W7^Cvg6kk;oOOk0gx16fD6e z{D3G*d`106GbCdap2Z4$jGvKfC*O%1&n6hh0pK{ z5_WOUa06N)1!FK1Z{TAbK#twqhjAU6p(ipi4NI{Jd+{fVe#3na_0Soqn1}^<7dvnS zdB5ctaU)uyH%4JPmSGe2;13k}jZcXn}4RhQ}})ui*pi z!V%>9nRAb;P#dk0jFEU8bFl)Ou^Yc3*FoNcD{&iIp$CRxJZ9llY`~W|h$xEuLK}oy zXpGJnfYEpgFJKiu#CJG~+=r+~xB|DJIl5vn9>UXDgtxE_-{B~79j1N8<+uq=k%$2p zg~^zY75D%p^ZT~R7YL3Mt7v*VNAtB zynzq!B@W;Z3bKa|FmxCsr>4m~g! zqwoZt!(yz$2lxU%;3%@4;B!z4SD+T^qcytXKBQwDrePjl#k=?rJFy?fk>e!y5tKp| zT#vfA3!TviX&8eiF%ygM2G-+a?8X6{M2=H@9!jDzu0w4!MF;f2KxAMXp28fwjFs4c zPp}I=;W)DWPWhoY%HwL>g!;G(osf({$iz5I!7MDo3arIe?7&|9f>X%x2YqjpKzUTf z4Y&i%&<^*aFVZjy<1rPpu?Vl>U2MW<*p2=84SylmpOhC$pggWZP27$~XoXJbf&LhV z(HM^@n2Cj0ij`Q0E%+R}u@ArEcVz#IYXwD75*1MuHE|p6L<_V>7xcnF496%uib;3| zbFm1oVine5Gd{&m?8VRc4Syi}Y3`ReA0<#0m2ov{;#Sl{6SPK0bVV=p$53SAA&kc) zOvh|2#4C6WtFaCr;3I6uF6_kt{ECw}jT~n$SU z2*zU~p2QSP#dJK2S(t-)Scn&~7_VR%mSY9p#45avcd-`lVFNZ|6Sm+(e1uQ%8Mb2w zzQj)K!ft$v@30r&<45enPdI>|aS(@a7{B5Oe#225!*QIz$(U38{vChB{K;?q6?2;3 zBC|6K|DUtS|BZ7$BmTMZ?}>kJ{5U)&k1c+j9<#@848~$i#%7GhYRty&Iq+O~PCPfB zBhQuR%yZ{C^jvyQJ-41?&$Z{=b8i_~7M6)+V;NaimYHQ|8CsTaV-?&e7 zya?-tb;Pog9W#SXb{u3VOl1+!dYC5pB^5Ezksa zq8_|9Zov(>4qiK6Lzkl>E=4JnKvA5B0`OYQ0k2K3QLk06S+8BMVXtMcX|HXsaj$i+ zd9Quj0NVoF1ltDN2-^zV4BHOd5Ze;l6x){3$bfB*ZI5kGGHjDeR+2+~y*#_Dc=74RZZKQ3bZKiFfZK!RjZK`dnZLDprZLV#vZLn>zZL)2% zZM1E*ZMJQ9f7q7WrrWmL#@p80=G*pr5Aa^#J;8f}_XzJ5dEvdodx-ZE?Ndt`oi&-C8u zJ=A-t_f+q#-ebMjde8OV>pj?evG-)}&EBKES9{O)-t9fyd%5>?@9o~>z1NpV3D^g) zFJPa*zJYxN`wI3M>^s@(SSvJYin%088SEBjdXwd`{hhkY>nV)n`G zo7qRRuV$akzMFkG`*QZ_?AzJLv#)2L&%U31K>LFB3GEx&N3^eKpV7XfeMtL~_9^XK z+Q+o7X`j=+r+rZSqV`EU!#=8gRr{>=UG2l#m$gr8-_|~^eO>#!_I>RG+ZVP^Y~R>E zvVCRy%=Vq_L)(|OPi^1YKDK>r``q@u?StDFw@+^0+&;Q}b^Gl0-R;BMm$y%E-`+mH zeSQ1<_Wd0LI2LeB;Ml-1f@1~842~TfLpYXjOyStVF@|Fe#~hA59D_I(aZKXa#4(Cv z6~`=&T^z$WmT^qu*v2uAV;#plj(r>hITmtE} zj)ff)J2rNV>{!_`vtwt+(2k`YQ#-bHjO|$4F}GuH$KZ~|9g{mYcZ}{>-7&jkcgOIK zu;2eZ= z5za|CH{l$Ga}~~6ICtS3hI1LtX*jpx9EWoq&UrZZ;T(u_AsW`Xd9E)=;&bc`E;v9@~G0w?2H{%?Qb2ZM{ICtY5j&nKA={UFJ9FKE7&iSOG z3!Dq83Fn5KBXX|DIV0zeoI`Rh$vGwGmYid9uE{wk=boH{axTg_Dd(n~qjIjwIVoWpZ2uK}FfbB@osKIi0ZI|*>D$2A|7u3fo?xd!H1m}_FL zjk!kVTA6ERuARAt=31I-YObxh#^zd^Yi_Q+xd!K2oNIEf&ACSBTAgcluHCtYw+v6h zwY^rTjtk)0pKE~2F&Tr=8Z}T9t{u9D=vv|=3_>fo*65m}Ymcr$F2xgYZPGPL*D77J zbnVhLOxH49({ydqHBQ$$UGsG9b0b^}eF(0Nx<*~i zQ+93HHRkVE(bmJYXV;)R!ZqoO;2O1S)vj5)cI_JWop4Rtwe9cTxohUGox6taTKXR=sMm0f-L-bt++BOG4A?Hm@8(^n7L!-keN$nPMNu7=9rmlRvqS^?U_O!3g)Dln`VxhxoYOD znY;E32Ev>+bKA^uGuO?WH*??2fioA*oH%piUV*uC=FC-xIdtaIt$?|8=Gff?bME#( z&NUBn@ls%JUI`qY80PHF;P*h7%V$oXxqVwO2j={l`)3Z|pO4XwV*$(&G*{4^L30Pq zAvBlJoI-O8%`waYa}LctGzZaKL~|0&P5gQs?FGzPGX*9Rd97l5<&3QEU z(Hux~As>agk>*I|hdGnxPMSk$E~Pn@=2n_x`8pnjxtBGOALeA5n>h}NFlW=;O>;QS zHBHx zF&`t)92IaXlX{HVNJS%*#?cJ=WH8sYKg@kCj)Up+17L2fIkLCFoY@~o(x$;&T61b^ zA|K4PHRsmc+YT@nH#@cu4|83 z%|$mS-Q4u8Fjw82b#vFvVc(z3F=IT;aW~i9ocGUqQg>i3d?T0}Z;rgV^5)E&J6{v# z(wkFnZoN77=GvQcZ|=Q0_)}p{zPb74=$or=&c3<(=J1=#Z%+S4nB(6Ex1bQ*1K?hO z(Qt1-C7ev69mI5`;1(3dw~4$KqtO}_ak4X?i>J{CH=`ijv# z`;ZSiI}#h*8{r;_GWfLv*96={;a&>&RJgZddwa?VX=n)dVEo*U_8jibaF0e+xM#z? z8?)hF4)=7pw`1?!veBA3Z4aukl_X2&< zkn@V6Xn>30-XZr8J%qba3C9~y=P(7`Q3E;Qo+S4sxku>^T!20GXs6&_CigVCx9Qiq z)Ni=w$-Pfk!@W@MiE?k0d!*be<(?_`PPvE5y;P0h-YWN4x!3Apw1s=Hj^EBPVKTa* zIa3#Lavrrl1?D!@XthF`EVVoZSZZ zpt%?ACAc@uJ!ut%DHE5K8Bz! zE`WRM++%k?nxPa9)}Sv1_vE=Z&pmo4s&njc@1A@3s=+;d+pgst!@Yj)`O63Q0J;~@ zJ%R2GbdR8W1((3RgUwJ9KVQu`f_n_xq9Tq}B_6mJF$wNXbdTa@xMwjL*CQ9))9Btt z_c-2$LiqX$$`|g1bWdb4xJU9;xM$M6lkTB(FXbw@x6(b9?zKE#nRXdZ!o8SP;oeO5 zXu4N388wj;pH`wCz`dRB@pP}}R~5Nl;2zL=xBz=95I5W_+63+$br0zZxTn;;rS38P zwH$2##-jr+hkH`jV-mW+J*#KR(%-^#B%>yB;?pu55AMS)$d4~eQw|sc_sX6R_s+hI zk!XbCaF6YBjDmY_-Gl31+?5y$_vlu@Zx?f&VFKLCTLq^|(EegF-1F<+-!sKIMz|-~ zy}|AgcCYX!7g5jQ9%A+j4Tf zqBq=A@80^)a!_}0AKZKI9(?!WyC>hh`R>toul}xVlndO$?_PfQ^zVt#XGc03gW_k+ zJsN!tqj48X1gzAj7JAl#__+R(RFwNiMSGf{27gI#8bEz)e!w7 z8r_QNNJdTM#K*r!qtBr)ZbBYxKNXEWkAb)i1+nvFH2MNkQ5Qw{Pp12M<@X_IDbS6@8Bl2MTp=fk22H-Xn#Li!c1w&C6MeyyxX!K=_L?aZ(k3UDF zui^nTM=AV#AR2uW52FprkVEYf00S4lB6vEfvN24zy4fRnJd-g`7OOSykxETBQM5C|cL9|3!9R4mEeG8AG zJubuXZ>c+Y0*SZ^e|^I-Vj6m&2D0O$-P9BG#ZAbIFLp(v^D!88a2|GlO`X69G{i;t zVP`bD9HY#0{5gVrdI-`?ZA;Rz(+YMfa|TZ0+sgPV{KU#_KmFckGs3_q-) zZeTQ8p&X99%V*+oB%&(Lyu)W=22yYn^5d(wIqw*TJ5e0_-r_j%5Za(3j;-c;#3bB{ zYmprvuZl)zV*qZ)dH7~!H2MlM(F~>W%bQ$3cntU8O8oUkG`a=Ppbu_Betflp>lDM$ z2qo~->$Dqq1nqG-et(U28q<)BTF8SP%Q;U-!<{IO{jXAIF&6Dm8K;)F799Y8r1mn;FRq*F4w4-viMR$NZACwR ziSvhnsDlfzcM)ZZ2hj!df?NJ4P&ZAuLEc)R#oQLn8=X%6wv_?gon9Fft8hYVI6u_=I+z*h6 z7AS|Kv#CRvf}W^_yx2J_8hr`rXpXWt@*Hg|CZh+gM_zn2lh-00%~2M=JH6G;JqlpdW5W5qv+5>jPua0axP8 zRN8pV!C=%!aU6JxdXI_dg6hbH9aAVbj6`!>ildV`HcUfr+=9Z`Gl}|uhtM8Z;LMYJ zALb$zcj6NK@Kg4VdMtzjP!3oq=Jc;h8h5Xq4 z80{n;L|atB>G2!~=3*!sp%i|7lrq6o^u{eX58scYzF{0XqZ)Ez$0M|t$V5w2!tY~g zvoH&T(Eyj=(8Jv4@DzIC7MzdoAL1IvcqE}Z60ma&?GZ-fZdAdU2e}?FAH&fM<#1v& zWsaE`i2Aq~her_)rlJpSLs9I1fcpd%AV>VLJ7-eyM z7_nj&hM*D3;CLF(<2ejLBb34Mp*)XS7=p$qixa7|gP4s}G(kC>9K!KnF4E8p74XMk z?#-Bo5x5JN;q)Nl#S6$p8(e{G_+lW}GDf34u0}3=J%Ba|W6>GcAwRzBPd&o~bi++J zAN%g3ZNy~sL2VSr;ePZXFarb85M^R7>BO75k+vI8`l%2V;~x#9R9eMYXplh8XZv`1@L`W>OH2QFY2K* zPIRGcu>kj@9jYNO_9W3~#U!Ml9!lXvBJaUMJb?DN7WwggXX3Y@z(XhHqPQapxa z+<`Lqqd9-WQapxUsEe}rs~Mk(S1}R2Q6J@SrYXmQ*YPC!p%E$}J9amrkAS8Q5t{V$$bZ};Yr+wCb%5A zv9|&DQap>{Xp0&sg2VM`Q}H4mMmN+!8Jw<1oOlCMFbFMB6$S89UHVLzhfzqvttg2< z?%N7K-EKEz}9ThA9|~R;Z35IC3+6T`a-lxDU;7 zH45R-O_UQB<1zF_Q(T1tICvxH8!urzQqTlfq9A^`fi@J2@fiA{8LFZ%4%Z@fEWzU# zfV*%lis0z=(dcJbjwwh*TU?Lg_`N1&idC3_bacjTD2r^^a~;<%=HVgq#GR;u{5V*H z81V|8z(BOdb+`z>SLZvh8Z(iJF1P~~k$|7B6ITA8|lH!%~Vk&LFe7RB(_W%Mzy0gLcBhM)s( zLq+7n;Y!iy=U9nZ7=u1&ftt7k*|4u7^&87D4H>u>4RJLt#Ge)TPP~uBn1o?SM15R| zA~;o^vcY=1geQ@PMAXBTxB$PGUp1#e?MCLk4^(EwND zB1Evi6zvSk#nAaq1sT!jm9rX=SaAK^{R#yAYZJ*bDOxCq&B;1c4(YRtn#q@fEM zqXtSN0Y@&TPUAhijH!44eb5HCqB4r$uM*Tre1w&F9uts;u4szuaVhfSL~+UtoA4T* z!#E5^A{wCv${-((UBr3DCcK7OcoeDVf+n~g08!3;c%K}bYnT#xc7j6aKV?(r$!!AqEi2hks$&=A+5917vj3wRGc!x}8c zbUci~NJ3NGh|5qE*>I=`Z4fr%HO#?8q@y?5q8_eA85G1H=TndHIo4q*W@0>sqbJ(n z4pc)K6vUtB(LUn~tjDXEjfu!W3Ob-6u17@_MRpu1OgZ60yoE)Wjz=&QJF@j`7IAedvN#sE6xuIW9p#MDy}F*o)7x5i9W$X5tBqMk+g& z4qnB4OveO_LMnQrGg_iPZbVg-$3@7GXl}|6`>_ijV?9>l70ksnJcb95iatm}8#KhN zsE*1gg$s}er*qNw#y;%ACwL!kVHpK^;iLU5`c4&oWXo$MF4L9OCT!Sl6 z3FS}<#ZeT6kq^0%4W}dYe{c-H;vn{6FTTM}e1T7~4V&>k*5Mtj!W&qQC0L9XFb{Ju z6VG5OCgTY_hH-cp58{5LV>pIlFb1F>`k)tj;9ex5GdiLj?nW!zg=T1ihG>9#sDs;a zD{jV(sD+xSfopLMs^UsiL1kQqiYSkAD2p;Eg_5`!C2$dn;X)Ke5uA_nP#A?!5CxDQ z`H(jz55E(TJ0=&ul`|#>zm@$Q*?9ba@Tt?K$_{TLzYeWn$S_MwXRjX4zSW zmSr8(LjzdWP0$S10qeruux?mKtSd>l7uKO(uuk>E01Sq8ZaA!i_rp5*Fvh{U`UECp zDy+*hF$eST0v2Nlmg5bqg4f16ypPTB+VL9t0z2^y_F^9n;#VAl*Wzh-ZF-G*t$NLR z?RpJ+EqhISZF`M-t$WRT?b`;}7T6}(HrPhkR@i3PcG!m4me{7)wxnYeY;$aTY=fr3 zHp#ZhHp;fjHp{llHf#fI(`?&zz_!jd&$iDt(6;b2Y#VJOZ7XdvZ98p4ZA)!aZCh<) zZEJ0FZF_BlZHsM_ZJTYQZL4jwZMz?bZMki_ZM$u}ZM|*2ZNK*b?*-lyyf=7{@Lq8e z-aEX9crWpu;=RRtjQ1MvIo^A`2YD}QgHGsb=x^s`pm!vEFOF=X&q;9_+o?d$RXt@6q0?y=Qyx_8#uN z+vv%v>;u>ruuov$z&?U~1^W#49qdEcm#|M^-@-nIeGU5@_C4%_*cY)+ zV&B9*ihULPEW2PI<~N*%eH;5Y_I2#@*!QsyWM9ZWk$ofkNcNTNGud~t4`pA~rmheK7lC_Q~v<*+;XlW}nTzn|(O@a`x%$+u6snuVgg|?ZeuawNGo`);_L% zUHiQDeeDC=7q(Aq-`GB~eP#R1_MPoR+n2UaZQt5Hwta2;-1fcggWDIkPj27bKDvE% z`|S4J?Zexbw@+{1-afv4ef#|O{T%~17H~}9*uXJ@V+F?yjvX9BIF@is;n>14hGPxK z9F9F4gE$s(OybzYF^Xdq$1ILr9K$%4aZKab#xag#9mhP5eH;Th7IIAF*vK)GV9K$)5b4=&h&M}^2J;!{G{Tu^2 z7IaML*w8VeV@1b|jvXCCI+k=y>DbaSrejUVoQ^#mgE|&X5+gF6;? zOzzm+F}h=Q$Lx;X9m6}8cTDft-Z8#oeaHNc{hb4FF2Fef=LSB*J~(IK+<|il&Ludf z;M{_949+z;=iuCfa}dr&I49xUgmVLWM z&gD3#JT(xu7&Rsi)o$oT*2spRx9Jh1b&Uric?Hsss;m(OWH|`v{ zbLGhx4d>9EOaBbctvko=Tzefj_wF3LbMel}J2&qfy>s=>**kad9KLh;&gnb1{~ny{ zch29rf7bw93vf-qwE@=%Tq|(Rz_o*-SJL-_YYMI{xW?dGgKG}1J-7zpT7+v7u1&Z` zF$S(#xOU+hhHDwFX}GrG8i#8gu6elj;Tni*A+CwIHsTtIYbE{R+KFo@uBEu9;@XO9 zEUvY<=Hl9mYcQ_GxF+M;jB7Nm)wpKk+RZ4qmgAbvPPoS7T90c!uKl=5T&r`<&b2$&@LbF52iNvo6)f%o33%X*6Et3YoD%xwuft?u8q1z>RPF5 zre$uUEkPHI!t+>XHIPl9X8t}VO9>{_#H&Pf=Bd2mhoClsj1d(i@}UAu?p?#buo34OuI;6J2#9WYpFgL^;5pzY%88LUn91?R$%qcOqH^I9G55zDAajBKfVn~D2$?Hn z&XBo7<`Csiq@6)~Jb;DRh7%~2L`+D*B&@<7u1j2GMEEsE}%Jq<_4N0 zXs)0+gXRvJLuf9cIfdpHnqz3Lp*e@<9-4z_E}}V!<|bAcLVE#o7R_BWhtXWda;dak zFvrncM{^!ez#K?(A&0=+NOL5Q!<DrB#5lZ;Jt+79eKMHqYR>Cw>_^d2^aEgStU0plV9xBt zqiNG%F0DDWn{Wc<9^@QjIOfA#+%u>$Cd|M*BwJk0U^9_IX-`)dxc zxxj022<8ZzD?AwH4sV6I#O4&YggM6M8t;I)$L1h+hB?VAV2-l6%H}Nh##ETg{430D zHpkgq=ehU@r%~lG?s+gbdMS1z{{-48m_uzYwK>%XQDP$J1m;|udu&iEdfLvAj4Zd1Fel&Kd~@{6&7kdvx%=kuo6B!bzq$S9_)md3 z|K|R?2f)1m>2PnrSIGMu?I3z!GS=Y;%Fg1oNXJXqiM+G63HM64XTrS`{qQWdz`Ygjv2d@&Sh)A%M_jmo_8jib zm<#u6WLwC%1xau($J_V?rCy*tfO|gN`{5oC_kz?yGTbBbK2E^BBkmz_FNu3f_QE|T zbubXK@iE+sat#vU9+h|Co|V!s)3<33UPE;9i*n zD6y1wAH(nhzC_+-94nGB4I6O^m0#svi?MhE`{CXl_wb})A$B0oYrG%HaPQAX{0{d7 zwZ|iP6FHLB zlY5;8z`ai&Bl~LZiMSW;k$Mlu;NB_sP`Q`tb-1^x_}jz;_guO6>MP`bhhsttW?(DM zpz6Dn2_DB996`A?+{fXbujTjw#ny6tVkj2kOXOR}XQB^gz&&MWaP@oCDY)ls4Ss`r z(OSd3Y3@;TubO+-8e%vW;cFCppK$~J7j<{}9@Q1MeShseK1m?BLvV*caW7DySc4S` z6xS9g#jTLy4n>0pifeF98{8pSa0ng<5)wjeX4dol-My~kc;4K9!2RNL1R^tguf6u3 zf$VjjveqpeH(*}int?u83Rxp3YvoE^WIjOF(4By+sgt#JKS0*n?Zpeo8a!ExHws%I zYxHETo~+p$gtfR1S<@$L`+7sx`kjTW{gX9-KVddxZJ?|XEO?!B7qWIx))2l#j%Z#N zAZrY_LDn3~+Cy1`C~FaAO`@z#lr@U7R#Db0%G$+?$bhVA>;_rmcoK;ycAMi1Sqmv^ zBHtn39nKj{gsh!>j%+dPBV=u*tg(CmS#wzzvIbMuV%~(T&8!Mps~HAayD4ip%im`n zK-PAif~@tFHJ`HfQ`UgWTF?(D_>ldEtQD0tqq251*CUQ2WKF58Eq#K($6Nz20UV+Pywb`;pTh?mJn(cE)McG9DuM7(y zYrKfMg0twt!>|NY;R44oLQZWDrOefn*X$Hi2XmNLGPl7D#r1WEe=6fn*v; zwt-|ENY;U59!U0qWFSZuf@C5{HiBd%NLGSmCP;RIWGG0Mf@CU4wt{3VNY;X6E=cx* zWH3k;gJd#DHiKj|NLGVnHb{1ZWH?BcgJe2Lwu59mNY;a7K1lY1WI#w3gk(ZUHiTqE zNLEA)Bs)SfBqU2hG9@HiLNX>KYeF(7BzrB)dW~EF{Z9 zGA$(ALNYES>q0UwB>O@#FeD2@GBG3@LozZXD?>6fBs)VgG$czyGBqSyLozlbYeOI3$}xGCCxyLoz!gyF)TOB+ElGJtW&hGCo#9GCw5yLoz@l3q&$OBpW0Z zk`*GEA(9;;86uJ;BAFtREg~5sk~JckBa%HL86=WLBAFzTO_IC7e_17xSt8jbl3^lQ zCX#6)*(Q>4B3UPrc_P^-l7S*wD3Xa{4f-!5WdI*n;tG6_ER~*+Y!%5^k*pQTT#@V* z$zYKz7Rh9hY!=CAk*pTUY?161$#9V@7s+&yY!}IRk*pWVe39%I$$*h87|Dc@Y#7Oi zk*pZWjFIdZ$&isO8OfB9Y#GUzk*pcXoRRDq$)J%e8p))QY#Pa^IS$FJk?b1Du#qeq z$+VGd8_BqltQ*O^k?b4Ez>zE*$;6Rt9LdO$tQ^VAk?b7F(2*=1$<&c-9m&{{tR2bR zk?bAG;E^mI$>fo29?9sDtRBhik?bDH@R2MZ$@GzIAIbQUtRKnzk?bGI0Fo>q$pn&Y zAjt@ltRTq@lI$SK5Rxn*$rO@oA;}n$tfAwO>>$jB-uogQ6yPKl366#MUr78 zSw@m+B-uuiaU@wsl6fTAN0NagSxAzJB-u!kktA73l9?pgNs^%?SxR|o{g*$*PjfD#@;r3@gd9 zl1wYfwvvo1$-0uvE6Ki+46Fr^Of1R9l8h|L%96}1$oBj4jF9lFTj1 z-jWP1$>Nesu4G6?mt=KGX4fG|hL>b{Nv4-%du_p8NamMhe@OLja9GV3I}PBQEy%dSut&PPbbon+lf=AC5U zNd}%|;YlW*WaCLjo@C`oW}al{Nrv7NNT!}->q*9*WbH}jo@DPy2A^c{NhaT3NJd}g z9$Z^72rF?BDUfVG$@rTB$^4V-Kgj@;EWlqO*?^J}C|QA$87SF-k|8Krf|4mH*@BWW zC|QG&IVjnKl0hh0gdvb@LdhtUtU}2wl=3&~=XOh(COl#E8nYLv`I$!?Sk zN6B)OOh?Ifl#EBodX&sZ$$pd!$Rm(U$ZSLY%ZQY$NXd+p>`2Lwlq^Zfl$2~q$(WR^ zN!!r>vL_{jQnDx|lTxxNC8JWZDkZa0vMVLSQnD;1(^9f6CF4@EE+z9)vM(hAQ?f86 z6H~G=UqG@l>q4?KB|}rPG$m70vNa`Ra}*?VQ?fTbkStEg@9t$?U8G$?%jc zPs#L@Y){GftOv>bl`=)NZ3D>^-44kZm8?<89PJFr zAeAiACy;DX$tabqQpqfp>{7`vl`K=qG?i>q$vBm)Q^`EdJ(1T5NEYfUNH(fuq)Jw* zWTr}Xs${52ma1f`77P6^V|6?qH{&L}kStcoWR+}I$!Lv%WVT9nt7N!JmaAmCK7eGr zO4h4nzDoA1WWY)mtYpGUHmqdC&VppdN_MPd$V!&1WXejmtYpke)~saCO7^T|(9Xdj zNH(ox)Jj%uPe^vHWY|7~-%LJ_9+(5kyp4rq;7S&*Wa7?-WaK`9WadhCu4L#+mab&# zO17?K>`K;d7fAN5WbjHBuVnH{Hm_v#N>=X&V4gv;gC#@wEF@D{vV|K$vW6vdSh9yDgIKbNC6idPi6x_0vWg|M z*u98-fMgj+ZWFbo?vScGmM)LQN%;Z&&3}wkumP}>IR+fxq z$y%1oWyxNa3}(q(mqJ0v}8+EjONO;%Sxct1 zWLrzdb^bNXd;AK?z?Lj*$;6gy>`IWVY{|@)>}<)~6{MmMm|{^gaT~_?E2i@{sKB@sKR=3y^GZ$q1LMaLEjx4apFfEOE&c zmuzv#7?-T^F#Lm8kSub^B=3ZY*o4cF>~hI4mn`!>m<`D|zY8ylZsAztH%KPBWTQ() zx@4tGX1ZjjkAY;VpTRo>ZsQn1GS?-0T{74ui(N9=b8Y831IcQa%y!9cmkf8waxb`p zv4CW}&xd5b--BepOBTFj!b>*1WW-BWyky2pc6B{P2+jzO~Y zB~!lyBx8RF7D2N2A3(DBC6iyW`6Z)YvikQyvil{&zrX=rlOWmtGqDqr`7hc3(g9El z(g`5l02?7)0n!;D-2u`eAYB5|DInbf(lPKHw!^&cKspGdi$FRFq?_O_(ox{Zf87Pr zVIW-w+i(TaaggOG*HlRNfpj297lL#mNH>CXBuH0+bS6l5f^;ZImx6REBqP^x-ZMcu z7o>YZIvAvj;U%P-K{^_wt3f&&7GN)=%RxFFq}xF{9y&uhAEf)?Uq}~(bV5isgmgqm zSHvtxcZ76ENSB0kN=UbabWBLsgmg|E!aYbAMcyc`f#`~fScfP)gL#rU2I;ntj*ID# z&I{?jkPZy#!jMjk7Wfr&A)Ogl@e$IcA)Ol1tuYh}A)Oo2y^)BlXE;ufZVu__Sd9I+ z1?ljRE{`f`j}cgc1Go+80LgZaYbe@bIHW5?Izw(kIz+OZXWt>+B15qNdm!B-(m^6! zB+^OJ64Fs3T_xLa5z=8IT_z#q6?%eMLJWCKsr>U zOGP?Wq+6v8euH$b?8IeA7mIYVNH>dgwDf{>wyZ-W9wH6W?IIm7()A*pFVg)Y9Wa+6 zoiL7Tyx&11NN0?6$4H0FF-WJ3bjxJD&ieeD}c?RjOkq(=M zkWL%vwvmn->AI258|l930_nn83hBm?jvVRAao%KGARRj0Ae}m4kdB=j_yFnNDS?KN zPM-0Qjvndikn2YAn68@jv(m@lFlIM4muC% z5|U0K=@ycXp*E1tA?Y5H4x)b`okY@2BppTNAe}|hUGxW}%Sbwnq}xb3j-=~II*+9L zNIHn7{YC^iGq?1azsn$Zes-&|@x~rtaO1iA7Lb|Q`V-mt3-B*_&U04~AZY=4@lCG?7 z7=;Dc0_oI}ZY}B9a>cTZsD-xp1(UH7`)~=*kq+tbk}j`?=!{{QjSZ0QFX;e#3;B;< z=?1HarjX9CQJ4qm5<3Cu7L$%K=^D#}U^GQ{NGF+elSxO}aomJ-m$6{!|8$v^f62KC z={S?Fvzd_YvxAT>H0eZ>ZZzpgldiNnknXgBkS?{QkZv{USd*?b>0GnEW==x7*qT7P z*@j^zq_b^5q{Hngk|Eu0L6ELD>3r)6>41|ixb-*$>4jLSpn~D{XZo5;Et~=?xlkPj|z>_Y#I*@KW>Bt+4 zd64eBgOD!0$M_6eJY$dY_!iQ^CtZ9Kun^MGCtZEg+4lsAkS@PGkZwQe_>-HHfD z=>QCebON5keY^(?%>GYzpmYe#H4M%Ve()IZg(*0>9Gk%ax&~lKDQ0WTo zgnp0?(aDfb(RJ8`(9Fk#4;dKPkomvLcrP>&6ARVi}Vhp5vRXSMLVh4^uI$Cew3En|ET&2rZI$fpP zRXSd!>s2~mrTeurqzhI$VWk^ZI%1_ORyt#M;~36CI%OZ@Es|lRar}@QMIoKE(oHKJ zwQcY-q`P)FCP6xFrQ0?f(se7Hx2GT-xcBe^(v6!6>B^PP+&m}>>C&xLpNGGs#12=$l1xsh}&*%r~ z5*~{wkdEP{SPSVMmJZ@$IE^c~gC~&AV(BhUgX!U1fpi;7$8m8~KsD5dbRma8x{;+L zc_4;i9414$lovy~mH);L?1ywOpT=d}!b7}3JQ9%(>2Q`VXX$j#1L=5{u4n0dmhR_z z_zuzuE#1&vAYIV|F%;4vJqgn>7t%2;UDNBa6?jKWw**Loa?6QPHN9@JxJ9Mz|aMe6hYqjce)PwO(7&*^c~FX%>> zF6qUwSM;K5*Yu{aXuY`MO}*jzExoPb9bIH?jIRCiu6~sBzCKd@fo|RGq5jIvHqYul z)`g2d(L2>s-Sfs%?Fft29XdSI39jdQ2sgrX?D9gFNqeE&EPSaO7k;I??tG<_D!ta7 z_r2Cn%D>TPHonoVbH(W+)8cf^ggEW!_*S<)_EtB|`%VXrey3xi-{})2b)l$_Iz8>9?pXPg zZanalzQN5Xu3MjUx-CKPuAZR38IYhCEl<#Q&L(I_Qi85r=(GO0`Dgvth|l`&n$Nl# zH>4c;@L6}yo~WzUO4N2E6}>3*$~^oPMo`oY{J zU1>)W_k<tULEk*5k(}>u(n%>pdHj^|_CwZY2&Y~Vfd=o4E(B(ag)p9d0%zebzk-5h_8C($*+3k&96G!i?4cg(pMdBNzs30 zOVKTgrs!*xQ*^$DDY|MKKK4k_(L+*nyFXI&<~b?);))dg@0Juj{a}hNa+;rsPSG)s zQ*^?+6#YCUMQ^vJ>K0j2_1OZcI(L~=-K1Ko{-I&2{-$-RPWmZT|J65D`~RM*`%XyJ zYi6YC1B+7ijy0)z^43&cYk#V~9m)4!NY%S;rs|K6QgyaBsXE{1RPD@2)%UGwdSRwC zT`qT;-cvYD=PsM3`&UWRYwM=z$R=s}%nxaLf2TA(r)QdO`AeEk8OEQ-r)k&JG(Bu? z8u$FBY1^7KU2#*I{%%*AZgwzDSBp&3SAGp> zbbYW#y3W}@T@M(Xu6GVk*DuDVYyZ%6U21x|u0A(i*IbmYD~6@(+-uYIm%r2XnQiHM z<{lp7V7h*PEM3oyO4o(YrR(jN({3Vfcy0$(_*FB%5>;13Obw+%;uJJit|N1pu zFUd&P`?!Lfw`J&??hJh^bB4Z@JwqSPlc84^%+RBYX6R<6GIW;m89JI9l4exP&{b+@ z===3E^r*%e+Vy>gUfL={=Wd&!7yp={ZCx_-(C$1IH!hVQkfB2dW$4R8Gjyhr8Qiy@ zp(p;4p?Cb5p>Isf(4S^yX#4yOopVu!F2qevg;!?i{A)9G;D!wC+mxYSZp+YTcV+0c zdo%R!2QzfNqZ!&7nW4`_@i=EPbY*UedU}~{x}Kp+-Qx2x8M^a>4E^?Th93SbL#MuE z`{OcnLVSkq{fXyH%+R&IX6Q9(89Kw0q1$uS++y+QWV=V#b9?mU0FOQy=+PP3JUTd+ zNB79<(Q^uT^u8dEzE#wt6H0irvy4aQFYnQ1gFU+HHy&NPnn%~E>CrXocy#6Z9$liL zN9X*`qm5=B{kFMBUuoshJ3>5qT04*K`lClz_{pQaT|D}HH; zkN$IzM_2jHqhJ2+(F;d-bdAv-{b;O5|1rU%^G@>U{eOD&_ftLk;dGDwWtK;Oo#W9H z=XeM2d`WyhF8C{c=bb@S6^~?^&yv6ulMunSpi->B(qny&EnPHWb^85 zIlMX{rz= zUR@#Bt3Ooo>h0fnb>FI9U9h@W->Ko%vuk;Ey*gg~sjgSAtMApVzV+&~hCF^_uWs3d z$8GA>%fI*P+AX~LQA@9$(Aul>hOmuoyt++0w!6Jo|M{a==jz0B{p8hkyLk1ru3r6f zH?NNG?$x7vdbQD;=jr3sS^Du<{k=NhK(Ai&3)?WrtJe

    ioZXb=Xk$Zy4J@+^cOP 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 +#endif +#include +#include +#include +#include +#include + +#ifdef HAVE_NET + +#include "SDL.h" +#include "SDL_net.h" + +#include "protocol.h" +#include "i_network.h" +#include "lprintf.h" +//#include "doomstat.h" + +/* cph - + * Each client will either use the IPv4 socket or the IPv6 socket + * Each server will use whichever or both that are available + */ +UDP_CHANNEL sentfrom; +IPaddress sentfrom_addr; +UDP_SOCKET udp_socket; + +/* Statistics */ +size_t sentbytes, recvdbytes; + +UDP_PACKET *udp_packet; + +/* I_ShutdownNetwork + * + * Shutdown the network code + */ +void I_ShutdownNetwork(void) +{ + SDLNet_FreePacket(udp_packet); + SDLNet_Quit(); +} + +/* I_InitNetwork + * + * Sets up the network code + */ +void I_InitNetwork(void) +{ + SDLNet_Init(); + udp_packet = SDLNet_AllocPacket(10000); +} + +UDP_PACKET *I_AllocPacket(int size) +{ + return(SDLNet_AllocPacket(size)); +} + +void I_FreePacket(UDP_PACKET *packet) +{ + SDLNet_FreePacket(packet); +} + + +/* cph - I_WaitForPacket - use select(2) via SDL_net's interface + * No more I_uSleep loop kludge */ + +void I_WaitForPacket(int ms) +{ + SDLNet_SocketSet ss = SDLNet_AllocSocketSet(1); + SDLNet_UDP_AddSocket(ss, udp_socket); + SDLNet_CheckSockets(ss,ms); + SDLNet_FreeSocketSet(ss); +} + +/* I_ConnectToServer + * + * Connect to a server + */ +IPaddress serverIP; + +int I_ConnectToServer(const char *serv) +{ + char server[500], *p; + Uint16 port; + + /* Split serv into address and port */ + if (strlen(serv)>500) return 0; + strcpy(server,serv); + p = strchr(server, ':'); + if(p) + { + *p++ = '\0'; + port = atoi(p); + } + else + port = 5030; /* Default server port */ + + SDLNet_ResolveHost(&serverIP, server, port); + if ( serverIP.host == INADDR_NONE ) + return -1; + + if (SDLNet_UDP_Bind(udp_socket, 0, &serverIP) == -1) + return -1; + + return 0; +} + +/* I_Disconnect + * + * Disconnect from server + */ +void I_Disconnect(void) +{ +/* int i; + UDP_PACKET *packet; + packet_header_t *pdata = (packet_header_t *)packet->data; + packet = I_AllocPacket(sizeof(packet_header_t) + 1); + + packet->data[sizeof(packet_header_t)] = consoleplayer; + pdata->type = PKT_QUIT; pdata->tic = gametic; + + for (i=0; i<4; i++) { + I_SendPacket(packet); + I_uSleep(10000); + } + I_FreePacket(packet);*/ + SDLNet_UDP_Unbind(udp_socket, 0); +} + +/* + * I_Socket + * + * Sets the given socket non-blocking, binds to the given port, or first + * available if none is given + */ +UDP_SOCKET I_Socket(Uint16 port) +{ + if(port) + return (SDLNet_UDP_Open(port)); + else { + UDP_SOCKET sock; + port = IPPORT_RESERVED; + while( (sock = SDLNet_UDP_Open(port)) == NULL ) + port++; + return sock; + } +} + +void I_CloseSocket(UDP_SOCKET sock) +{ + SDLNet_UDP_Close(sock); +} + +UDP_CHANNEL I_RegisterPlayer(IPaddress *ipaddr) +{ + static int freechannel; + return(SDLNet_UDP_Bind(udp_socket, freechannel++, ipaddr)); +} + +void I_UnRegisterPlayer(UDP_CHANNEL channel) +{ + SDLNet_UDP_Unbind(udp_socket, channel); +} + +/* + * ChecksumPacket + * + * Returns the checksum of a given network packet + */ +static byte ChecksumPacket(const packet_header_t* buffer, size_t len) +{ + const byte* p = (const void*)buffer; + byte sum = 0; + + if (len==0) + return 0; + + while (p++, --len) + sum += *p; + + return sum; +} + +size_t I_GetPacket(packet_header_t* buffer, size_t buflen) +{ + int checksum; + size_t len; + int status; + + status = SDLNet_UDP_Recv(udp_socket, udp_packet); + len = udp_packet->len; + if (buflen0) ) + memcpy(buffer, udp_packet->data, len); + sentfrom=udp_packet->channel; +#ifndef SDL_NET_UDP_PACKET_SRC + sentfrom_addr=udp_packet->address; +#else + sentfrom_addr=udp_packet->src; /* cph - allow for old SDL_net library */ +#endif + checksum=buffer->checksum; + buffer->checksum=0; + if ( (status!=0) && (len>0)) { + byte psum = ChecksumPacket(buffer, udp_packet->len); +/* fprintf(stderr, "recvlen = %u, stolen = %u, csum = %u, psum = %u\n", + udp_packet->len, len, checksum, psum); */ + if (psum == checksum) return len; + } + return 0; +} + +void I_SendPacket(packet_header_t* packet, size_t len) +{ + packet->checksum = ChecksumPacket(packet, len); + memcpy(udp_packet->data, packet, udp_packet->len = len); + SDLNet_UDP_Send(udp_socket, 0, udp_packet); +} + +void I_SendPacketTo(packet_header_t* packet, size_t len, UDP_CHANNEL *to) +{ + packet->checksum = ChecksumPacket(packet, len); + memcpy(udp_packet->data, packet, udp_packet->len = len); + SDLNet_UDP_Send(udp_socket, *to, udp_packet); +} + +void I_PrintAddress(FILE* fp, UDP_CHANNEL *addr) +{ +/* + char *addy; + Uint16 port; + IPaddress *address; + + address = SDLNet_UDP_GetPeerAddress(udp_socket, player); + +//FIXME: if it cant resolv it may freeze up + addy = SDLNet_ResolveIP(address); + port = address->port; + + if(addy != NULL) + fprintf(fp, "%s:%d", addy, port); + else + fprintf(fp, "Error"); +*/ +} + +#endif /* HAVE_NET */ diff --git a/src/SDL/i_sound.c b/src/SDL/i_sound.c new file mode 100644 index 00000000..16a102a4 --- /dev/null +++ b/src/SDL/i_sound.c @@ -0,0 +1,722 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System interface for sound. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_LIBSDL_MIXER +#define HAVE_MIDI_MIXER +#endif +#include + +#include "SDL.h" +#include "SDL_audio.h" +#include "SDL_mutex.h" +#include "SDL_byteorder.h" +#include "SDL_version.h" +#ifdef HAVE_MIDI_MIXER +#include "SDL_mixer.h" +#endif + +#include "z_zone.h" + +#include "m_swap.h" +#include "i_sound.h" +#include "m_argv.h" +#include "m_misc.h" +#include "w_wad.h" +#include "lprintf.h" +#include "s_sound.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "doomtype.h" + +#include "d_main.h" + +// The number of internal mixing channels, +// the samples calculated for each mixing step, +// the size of the 16bit, 2 hardware channel (stereo) +// mixing buffer, and the samplerate of the raw data. + +// Variables used by Boom from Allegro +// created here to avoid changes to core Boom files +int snd_card = 1; +int mus_card = 1; +int detect_voices = 0; // God knows + +static boolean sound_inited = false; +static boolean first_sound_init = true; + +// Needed for calling the actual sound output. +static int SAMPLECOUNT= 512; +#define MAX_CHANNELS 32 + +// MWM 2000-01-08: Sample rate in samples/second +int snd_samplerate=11025; + +// The actual output device. +int audio_fd; + +typedef struct { + // SFX id of the playing sound effect. + // Used to catch duplicates (like chainsaw). + int id; +// The channel step amount... + unsigned int step; +// ... and a 0.16 bit remainder of last step. + unsigned int stepremainder; + unsigned int samplerate; +// The channel data pointers, start and end. + const unsigned char* data; + const unsigned char* enddata; + // Hardware left and right channel volume lookup. + int *leftvol_lookup; + int *rightvol_lookup; +} channel_info_t; + +channel_info_t channelinfo[MAX_CHANNELS]; + +// Pitch to stepping lookup, unused. +int steptable[256]; + +// Volume lookups. +int vol_lookup[128*256]; + +/* cph + * stopchan + * Stops a sound, unlocks the data + */ + +static void stopchan(int i) +{ + if (channelinfo[i].data) /* cph - prevent excess unlocks */ + { + channelinfo[i].data=NULL; + W_UnlockLumpNum(S_sfx[channelinfo[i].id].lumpnum); + } +} + +// +// This function adds a sound to the +// list of currently active sounds, +// which is maintained as a given number +// (eight, usually) of internal channels. +// Returns a handle. +// +static int addsfx(int sfxid, int channel, const unsigned char* data, size_t len) +{ + stopchan(channel); + + channelinfo[channel].data = data; + /* Set pointer to end of raw data. */ + channelinfo[channel].enddata = channelinfo[channel].data + len - 1; + channelinfo[channel].samplerate = (channelinfo[channel].data[3]<<8)+channelinfo[channel].data[2]; + channelinfo[channel].data += 8; /* Skip header */ + + channelinfo[channel].stepremainder = 0; + + // Preserve sound SFX id, + // e.g. for avoiding duplicates of chainsaw. + channelinfo[channel].id = sfxid; + + return channel; +} + +static void updateSoundParams(int handle, int volume, int seperation, int pitch) +{ + int slot = handle; + int rightvol; + int leftvol; + int step = steptable[pitch]; + + // Set stepping + // MWM 2000-12-24: Calculates proportion of channel samplerate + // to global samplerate for mixing purposes. + // Patched to shift left *then* divide, to minimize roundoff errors + // as well as to use SAMPLERATE as defined above, not to assume 11025 Hz + if (pitched_sounds) + channelinfo[slot].step = step + (((channelinfo[slot].samplerate<<16)/snd_samplerate)-65536); + else + channelinfo[slot].step = ((channelinfo[slot].samplerate<<16)/snd_samplerate); + + // Separation, that is, orientation/stereo. + // range is: 1 - 256 + seperation += 1; + + // Per left/right channel. + // x^2 seperation, + // adjust volume properly. + leftvol = volume - ((volume*seperation*seperation) >> 16); + seperation = seperation - 257; + rightvol= volume - ((volume*seperation*seperation) >> 16); + + // Sanity check, clamp volume. + if (rightvol < 0 || rightvol > 127) + I_Error("rightvol out of bounds"); + + if (leftvol < 0 || leftvol > 127) + I_Error("leftvol out of bounds"); + + // Get the proper lookup table piece + // for this volume level??? + channelinfo[slot].leftvol_lookup = &vol_lookup[leftvol*256]; + channelinfo[slot].rightvol_lookup = &vol_lookup[rightvol*256]; +} + +void I_UpdateSoundParams(int handle, int volume, int seperation, int pitch) +{ + SDL_LockAudio(); + updateSoundParams(handle, volume, seperation, pitch); + SDL_UnlockAudio(); +} + +// +// SFX API +// Note: this was called by S_Init. +// However, whatever they did in the +// old DPMS based DOS version, this +// were simply dummies in the Linux +// version. +// See soundserver initdata(). +// +void I_SetChannels(void) +{ + // Init internal lookups (raw data, mixing buffer, channels). + // This function sets up internal lookups used during + // the mixing process. + int i; + int j; + + int* steptablemid = steptable + 128; + + // Okay, reset internal mixing channels to zero. + for (i=0; iname); + return W_GetNumForName(namebuf); +} + +// +// Starting a sound means adding it +// to the current list of active sounds +// in the internal channels. +// As the SFX info struct contains +// e.g. a pointer to the raw data, +// it is ignored. +// As our sound handling does not handle +// priority, it is ignored. +// Pitching (that is, increased speed of playback) +// is set, but currently not used by mixing. +// +int I_StartSound(int id, int channel, int vol, int sep, int pitch, int priority) +{ + const unsigned char* data; + int lump; + size_t len; + + if ((channel < 0) || (channel >= MAX_CHANNELS)) + return -1; + + lump = S_sfx[id].lumpnum; + + // We will handle the new SFX. + // Set pointer to raw data. + len = W_LumpLength(lump); + + // e6y: Crash with zero-length sounds. + // Example wad: dakills (http://www.doomworld.com/idgames/index.php?id=2803) + // The entries DSBSPWLK, DSBSPACT, DSSWTCHN and DSSWTCHX are all zero-length sounds + if (len<=8) return -1; + + /* Find padded length */ + len -= 8; + // do the lump caching outside the SDL_LockAudio/SDL_UnlockAudio pair + // use locking which makes sure the sound data is in a malloced area and + // not in a memory mapped one + data = W_LockLumpNum(lump); + + SDL_LockAudio(); + + // Returns a handle (not used). + addsfx(id, channel, data, len); + updateSoundParams(channel, vol, sep, pitch); + + SDL_UnlockAudio(); + + + return channel; +} + + + +void I_StopSound (int handle) +{ + SDL_LockAudio(); + stopchan(handle); + SDL_UnlockAudio(); +} + + +boolean I_SoundIsPlaying(int handle) +{ + return channelinfo[handle].data != NULL; +} + + +boolean I_AnySoundStillPlaying(void) +{ + boolean result = false; + int i; + + for (i=0; i> 16; + + // Add left and right part + // for this channel (sound) + // to the current data. + // Adjust volume accordingly. + dl += channelinfo[chan].leftvol_lookup[sample]; + dr += channelinfo[chan].rightvol_lookup[sample]; + // Increment index ??? + channelinfo[chan].stepremainder += channelinfo[chan].step; + // MSB is next sample??? + channelinfo[chan].data += channelinfo[chan].stepremainder >> 16; + // Limit to LSB??? + channelinfo[chan].stepremainder &= 0xffff; + + // Check whether we are done. + if (channelinfo[chan].data >= channelinfo[chan].enddata) + stopchan(chan); + } + } + + // Clamp to range. Left hardware channel. + // Has been char instead of short. + // if (dl > 127) *leftout = 127; + // else if (dl < -128) *leftout = -128; + // else *leftout = dl; + + if (dl > SHRT_MAX) + *leftout = SHRT_MAX; + else if (dl < SHRT_MIN) + *leftout = SHRT_MIN; + else + *leftout = (signed short)dl; + + // Same for right hardware channel. + if (dr > SHRT_MAX) + *rightout = SHRT_MAX; + else if (dr < SHRT_MIN) + *rightout = SHRT_MIN; + else + *rightout = (signed short)dr; + + // Increment current pointers in stream + leftout += step; + rightout += step; + } +} + +void I_ShutdownSound(void) +{ + if (sound_inited) { + lprintf(LO_INFO, "I_ShutdownSound: "); +#ifdef HAVE_MIDI_MIXER + Mix_CloseAudio(); +#else + SDL_CloseAudio(); +#endif + lprintf(LO_INFO, "\n"); + sound_inited = false; + } +} + +//static SDL_AudioSpec audio; + +void I_InitSound(void) +{ +#ifdef HAVE_MIDI_MIXER + int audio_rate; + Uint16 audio_format; + int audio_channels; + int audio_buffers; + + if (sound_inited) + I_ShutdownSound(); + + // Secure and configure sound device first. + lprintf(LO_INFO,"I_InitSound: "); + + /* Initialize variables */ + audio_rate = snd_samplerate; +#if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) + audio_format = AUDIO_S16MSB; +#else + audio_format = AUDIO_S16LSB; +#endif + audio_channels = 2; + SAMPLECOUNT = 512; + audio_buffers = SAMPLECOUNT*snd_samplerate/11025; + + if (Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers) < 0) { + lprintf(LO_INFO,"couldn't open audio with desired format\n"); + return; + } + sound_inited = true; + SAMPLECOUNT = audio_buffers; + Mix_SetPostMix(I_UpdateSound, NULL); + lprintf(LO_INFO," configured audio device with %d samples/slice\n", SAMPLECOUNT); +#else + SDL_AudioSpec audio; + + // Secure and configure sound device first. + lprintf(LO_INFO,"I_InitSound: "); + + // Open the audio device + audio.freq = snd_samplerate; +#if ( SDL_BYTEORDER == SDL_BIG_ENDIAN ) + audio.format = AUDIO_S16MSB; +#else + audio.format = AUDIO_S16LSB; +#endif + audio.channels = 2; + audio.samples = SAMPLECOUNT*snd_samplerate/11025; + audio.callback = I_UpdateSound; + if ( SDL_OpenAudio(&audio, NULL) < 0 ) { + lprintf(LO_INFO,"couldn't open audio with desired format\n"); + return; + } + SAMPLECOUNT = audio.samples; + lprintf(LO_INFO," configured audio device with %d samples/slice\n", SAMPLECOUNT); +#endif + + if (first_sound_init) { + first_sound_init = false; + } + + if (!nomusicparm) + I_InitMusic(); + + // Finished initialization. + lprintf(LO_INFO,"I_InitSound: sound module ready\n"); +#ifndef HAVE_MIDI_MIXER + SDL_PauseAudio(0); +#endif +} + + + + +// +// MUSIC API. +// + +#ifndef HAVE_OWN_MUSIC + +#ifdef HAVE_MIDI_MIXER +typedef struct _Mix_Music Mix_Music; +#include "mmus2mid.h" + +static Mix_Music *music[2] = { NULL, NULL }; + +char* music_tmp = NULL; /* cph - name of music temporary file */ + +#endif + +void I_ShutdownMusic(void) +{ +#ifdef HAVE_MIDI_MIXER + if (music_tmp) { + unlink(music_tmp); + lprintf(LO_DEBUG, "I_ShutdownMusic: removing %s\n", music_tmp); + free(music_tmp); + music_tmp = NULL; + } +#endif +} + +void I_InitMusic(void) +{ +#ifdef HAVE_MIDI_MIXER + if (!music_tmp) { +#ifndef _WIN32 + music_tmp = strdup("/tmp/prboom-music-XXXXXX"); + { + int fd = mkstemp(music_tmp); + if (fd<0) { + lprintf(LO_ERROR, "I_InitMusic: failed to create music temp file %s", music_tmp); + free(music_tmp); return; + } else + close(fd); + } +#else /* !_WIN32 */ + music_tmp = strdup("doom.tmp"); +#endif + } +#endif +} + +void I_PlaySong(int handle, int looping) +{ +#ifdef HAVE_MIDI_MIXER + if ( music[handle] ) { + Mix_FadeInMusic(music[handle], looping ? -1 : 0, 500); + } +#endif +} + +extern int mus_pause_opt; // From m_misc.c + +void I_PauseSong (int handle) +{ +#ifdef HAVE_MIDI_MIXER + switch(mus_pause_opt) { + case 0: + I_StopSong(handle); + break; + case 1: + Mix_PauseMusic(); + break; + } +#endif + // Default - let music continue +} + +void I_ResumeSong (int handle) +{ +#ifdef HAVE_MIDI_MIXER + switch(mus_pause_opt) { + case 0: + I_PlaySong(handle,1); + break; + case 1: + Mix_ResumeMusic(); + break; + } +#endif + /* Otherwise, music wasn't stopped */ +} + +void I_StopSong(int handle) +{ +#ifdef HAVE_MIDI_MIXER + Mix_FadeOutMusic(500); +#endif +} + +void I_UnRegisterSong(int handle) +{ +#ifdef HAVE_MIDI_MIXER + if ( music[handle] ) { + Mix_FreeMusic(music[handle]); + music[handle] = NULL; + } +#endif +} + +int I_RegisterSong(const void *data, size_t len) +{ +#ifdef HAVE_MIDI_MIXER + MIDI *mididata; + FILE *midfile; + + if ( len < 32 ) + return 0; // the data should at least as big as the MUS header + if ( music_tmp == NULL ) + return 0; + midfile = fopen(music_tmp, "wb"); + if ( midfile == NULL ) { + lprintf(LO_ERROR,"Couldn't write MIDI to %s\n", music_tmp); + return 0; + } + /* Convert MUS chunk to MIDI? */ + if ( memcmp(data, "MUS", 3) == 0 ) + { + UBYTE *mid; + int midlen; + + mididata = malloc(sizeof(MIDI)); + mmus2mid(data, mididata, 89, 0); + MIDIToMidi(mididata,&mid,&midlen); + M_WriteFile(music_tmp,mid,midlen); + free(mid); + free_mididata(mididata); + free(mididata); + } else { + fwrite(data, len, 1, midfile); + } + fclose(midfile); + + music[0] = Mix_LoadMUS(music_tmp); + if ( music[0] == NULL ) { + lprintf(LO_ERROR,"Couldn't load MIDI from %s: \n", music_tmp); + } +#endif + return (0); +} + +// cournia - try to load a music file into SDL_Mixer +// returns true if could not load the file +int I_RegisterMusic( const char* filename, musicinfo_t *song ) +{ +#ifdef HAVE_MIDI_MIXER + if (!filename) return 1; + if (!song) return 1; + music[0] = Mix_LoadMUS(filename); + if (music[0] == NULL) + { + lprintf(LO_WARN,"Couldn't load music from %s: \nAttempting to load default MIDI music.\n", filename); + return 1; + } + else + { + song->data = 0; + song->handle = 0; + song->lumpnum = 0; + return 0; + } +#else + return 1; +#endif +} + +void I_SetMusicVolume(int volume) +{ +#ifdef HAVE_MIDI_MIXER + Mix_VolumeMusic(volume*8); +#endif +} + +#endif /* HAVE_OWN_MUSIC */ + diff --git a/src/SDL/i_system.c b/src/SDL/i_system.c new file mode 100644 index 00000000..0cb2016b --- /dev/null +++ b/src/SDL/i_system.c @@ -0,0 +1,434 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Misc system stuff needed by Doom, implemented for Linux. + * Mainly timer handling, and ENDOOM/ENDBOOM. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#include +#include +#include +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#include +#include +#else +#include +#endif +#include + +#include "SDL.h" + +#include +#include + +#include "d_main.h" +#include "lprintf.h" +#include "doomtype.h" +#include "doomdef.h" +#include "lprintf.h" +#ifndef PRBOOM_SERVER +#include "m_fixed.h" +#include "r_fps.h" +#include "m_argv.h" +#endif +#include "i_system.h" + +#ifdef __GNUG_ +#pragma implementation "i_system.h" +#endif + + +static struct timeval start; +static boolean InDisplay = false; + +boolean I_StartDisplay(void) +{ + if (InDisplay) + return false; + + InDisplay = true; + return true; +} + +void I_EndDisplay(void) +{ + InDisplay = false; +} + +static uint32_t GetTicks(void) +{ + uint32_t ticks; + struct timeval now; + gettimeofday(&now, NULL); + ticks = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_usec - start.tv_usec) / 1000; + return (ticks); +} + +static void Delay(uint32_t ms) +{ + int was_error; + + struct timeval tv; + uint32_t then, now, elapsed; + + /* Set the timeout interval */ + then = GetTicks(); + do { + errno = 0; + + /* Calculate the time interval left (in case of interrupt) */ + now = GetTicks(); + elapsed = (now - then); + then = now; + if (elapsed >= ms) { + break; + } + ms -= elapsed; + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; + + was_error = select(0, NULL, NULL, NULL, &tv); + } + while (was_error && (errno == EINTR)); +} + +void I_uSleep(unsigned long usecs) +{ + Delay(usecs/1000); +} + + +static uint32_t StartTicks(void) +{ + gettimeofday(&start, NULL); +} + +int ms_to_next_tick; + +#include + +int I_GetTime_RealTime (void) +{ + struct timeval tp; + struct timezone tzp; + int newtics; + static int basetime=0; + + gettimeofday(&tp, &tzp); + if (!basetime) + basetime = tp.tv_sec; + newtics = (tp.tv_sec-basetime)*TICRATE + tp.tv_usec*TICRATE/1000000; + return newtics; +} + +/* + * I_GetRandomTimeSeed + * + * CPhipps - extracted from G_ReloadDefaults because it is O/S based + */ +unsigned long I_GetRandomTimeSeed(void) +{ + /* killough 3/26/98: shuffle random seed, use the clock */ + struct timeval tv; + struct timezone tz; + gettimeofday(&tv,&tz); + return (tv.tv_sec*1000ul + tv.tv_usec/1000ul); +} + +/* cphipps - I_GetVersionString + * Returns a version string in the given buffer + */ +const char* I_GetVersionString(char* buf, size_t sz) +{ +#ifdef HAVE_SNPRINTF + snprintf(buf,sz,"%s v%s (http://prboom.sourceforge.net/)",PACKAGE,VERSION); +#else + sprintf(buf,"%s v%s (http://prboom.sourceforge.net/)",PACKAGE,VERSION); +#endif + return buf; +} + +#ifdef PRBOOM_SERVER + +/* cphipps - I_SigString + * Returns a string describing a signal number + */ +const char* I_SigString(char* buf, size_t sz, int signum) +{ +#ifdef HAVE_SNPRINTF + snprintf(buf,sz,"signal %d",signum); +#else + sprintf(buf,"signal %d",signum); +#endif + return buf; +} + +#else + +fixed_t I_GetTimeFrac (void) +{ + unsigned long now; + fixed_t frac; + + now = GetTicks(); + + if (tic_vars.step == 0) + return FRACUNIT; + else + { + frac = (fixed_t)((now - tic_vars.start) * FRACUNIT / tic_vars.step); + if (frac < 0) + frac = 0; + if (frac > FRACUNIT) + frac = FRACUNIT; + return frac; + } +} + +void I_GetTime_SaveMS(void) +{ + if (!movement_smooth) + return; + + tic_vars.start = GetTicks(); + tic_vars.next = (unsigned int) ((tic_vars.start * tic_vars.msec + 1.0f) / tic_vars.msec); + tic_vars.step = tic_vars.next - tic_vars.start; +} +// Return the path where the executable lies -- Lee Killough +// proff_fs 2002-07-04 - moved to i_system +#ifdef _WIN32 +const char *I_DoomExeDir(void) +{ + static const char current_dir_dummy[] = {"."}; // proff - rem extra slash 8/21/03 + static char *base; + if (!base) // cache multiple requests + { + size_t len = strlen(*myargv); + char *p = (base = malloc(len+1)) + len - 1; + strcpy(base,*myargv); + while (p > base && *p!='/' && *p!='\\') + *p--=0; + if (*p=='/' || *p=='\\') + *p--=0; + if (strlen(base)<2) + { + free(base); + base = malloc(1024); + if (!getcwd(base,1024)) + strcpy(base, current_dir_dummy); + } + } + return base; +} +#elif defined(MACOSX) + +/* Defined elsewhere */ + +#else +// cph - V.Aguilar (5/30/99) suggested return ~/.lxdoom/, creating +// if non-existant +static const char prboom_dir[] = {"/.prboom"}; // Mead rem extra slash 8/21/03 + +const char *I_DoomExeDir(void) +{ + static char *base; + if (!base) // cache multiple requests + { + char *home = getenv("HOME"); + size_t len = strlen(home); + + base = malloc(len + strlen(prboom_dir) + 1); + strcpy(base, home); + // I've had trouble with trailing slashes before... + if (base[len-1] == '/') base[len-1] = 0; + strcat(base, prboom_dir); + mkdir(base, S_IRUSR | S_IWUSR | S_IXUSR); // Make sure it exists + } + return base; +} +#endif + +/* + * HasTrailingSlash + * + * cphipps - simple test for trailing slash on dir names + */ + +boolean HasTrailingSlash(const char* dn) +{ + return ( (dn[strlen(dn)-1] == '/')); +} + +/* + * I_FindFile + * + * proff_fs 2002-07-04 - moved to i_system + * + * cphipps 19/1999 - writen to unify the logic in FindIWADFile and the WAD + * autoloading code. + * Searches the standard dirs for a named WAD file + * The dirs are listed at the start of the function + */ + +char* I_FindFile(const char* wfname, const char* ext) +{ +fprintf(stderr, "wfname: %s\n", wfname); + // lookup table of directories to search + static const struct { + const char *dir; // directory + const char *sub; // subdirectory + const char *env; // environment variable + const char *(*func)(void); // for I_DoomExeDir + } search[] = { + {NULL}, // current working directory + {NULL, NULL, "DOOMWADDIR"}, // run-time $DOOMWADDIR + {DOOMWADDIR}, // build-time configured DOOMWADDIR + {NULL, "doom", "HOME"}, // ~/doom + {NULL, NULL, "HOME"}, // ~ + {NULL, NULL, NULL, I_DoomExeDir}, // config directory + {"/usr/local/share/games/doom"}, + {"/usr/share/games/doom"}, + {"/usr/local/share/doom"}, + {"/usr/share/doom"}, + }; + + int i; + /* Precalculate a length we will need in the loop */ + size_t pl = strlen(wfname) + strlen(ext) + 4; + + for (i = 0; i < sizeof(search)/sizeof(*search); i++) { + char * p; + const char * d = NULL; + const char * s = NULL; + /* Each entry in the switch sets d to the directory to look in, + * and optionally s to a subdirectory of d */ + // switch replaced with lookup table + if (search[i].env) { + if (!(d = getenv(search[i].env))) + continue; + } else if (search[i].func) + d = search[i].func(); + else + d = search[i].dir; + s = search[i].sub; + + p = malloc((d ? strlen(d) : 0) + (s ? strlen(s) : 0) + pl); + sprintf(p, "%s%s%s%s%s", d ? d : "", (d && !HasTrailingSlash(d)) ? "/" : "", + s ? s : "", (s && !HasTrailingSlash(s)) ? "/" : "", + wfname); + + if (access(p,F_OK)) + strcat(p, ext); + if (!access(p,F_OK)) { + lprintf(LO_INFO, " found %s\n", p); + return p; + } + free(p); + } + return NULL; +} + +#endif + + +/* + * I_Filelength + * + * Return length of an open file. + */ + +int I_Filelength(int handle) +{ + struct stat fileinfo; + if (fstat(handle,&fileinfo) == -1) + I_Error("I_Filelength: %s",strerror(errno)); + return fileinfo.st_size; +} + +static int I_GetTime_Error(void) +{ + I_Error("I_GetTime_Error: GetTime() used before initialization"); + return 0; +} + +int (*I_GetTime)(void) = I_GetTime_Error; + +void I_Init(void) +{ + I_GetTime = I_GetTime_RealTime; + + { + /* killough 2/21/98: avoid sound initialization if no sound & no music */ + if (!(nomusicparm && nosfxparm)) + I_InitSound(); + } + + R_InitInterpolation(); +} + +// killough 2/22/98: Add support for ENDBOOM, which is PC-specific + +static void PrintVer(void) +{ + char vbuf[200]; + lprintf(LO_INFO,"%s\n",I_GetVersionString(vbuf,200)); +} + +extern void D_Doom_Deinit(void); + +int main(int argc, char **argv) +{ + myargc = argc; + myargv = (const char * const *) argv; + + /* Version info */ + lprintf(LO_INFO,"\n"); + PrintVer(); + + StartTicks(); + Z_Init(); /* 1/18/98 killough: start up memory stuff first */ + + /* cphipps - call to video specific startup code */ + I_PreInitGraphics(); + + D_DoomMainSetup(); + D_DoomLoop(); + D_DoomDeinit(); + return 0; +} diff --git a/src/SDL/i_video.c b/src/SDL/i_video.c new file mode 100644 index 00000000..ecd15075 --- /dev/null +++ b/src/SDL/i_video.c @@ -0,0 +1,384 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM graphics stuff for SDL + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "SDL.h" + +#include "m_argv.h" +#include "doomstat.h" +#include "doomdef.h" +#include "doomtype.h" +#include "v_video.h" +#include "r_draw.h" +#include "d_main.h" +#include "d_event.h" +#include "i_video.h" +#include "z_zone.h" +#include "s_sound.h" +#include "sounds.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "lprintf.h" + +extern void M_QuitDOOM(int choice); +int use_doublebuffer = 1; // Included not to break m_misc, but not relevant to SDL +static SDL_Surface *screen; + +//////////////////////////////////////////////////////////////////////////// +// Input code +int leds_always_off = 0; // Expected by m_misc, not relevant + +// Mouse handling +extern int usemouse; // config file var +static boolean mouse_enabled; // usemouse, but can be overriden by -nomouse +static boolean mouse_currently_grabbed; + +///////////////////////////////////////////////////////////////////////////////// +// Keyboard handling + +// +// Translates the key currently in key +// + +static int I_TranslateKey(SDL_keysym* key) +{ + int rc = 0; + + switch (key->sym) { + case SDLK_LEFT: rc = KEYD_LEFTARROW; break; + case SDLK_RIGHT: rc = KEYD_RIGHTARROW; break; + case SDLK_DOWN: rc = KEYD_DOWNARROW; break; + case SDLK_UP: rc = KEYD_UPARROW; break; + case SDLK_ESCAPE: rc = KEYD_ESCAPE; break; + case SDLK_RETURN: rc = KEYD_ENTER; break; + case SDLK_TAB: rc = KEYD_TAB; break; + case SDLK_F1: rc = KEYD_F1; break; + case SDLK_F2: rc = KEYD_F2; break; + case SDLK_F3: rc = KEYD_F3; break; + case SDLK_F4: rc = KEYD_F4; break; + case SDLK_F5: rc = KEYD_F5; break; + case SDLK_F6: rc = KEYD_F6; break; + case SDLK_F7: rc = KEYD_F7; break; + case SDLK_F8: rc = KEYD_F8; break; + case SDLK_F9: rc = KEYD_F9; break; + case SDLK_F10: rc = KEYD_F10; break; + case SDLK_F11: rc = KEYD_F11; break; + case SDLK_F12: rc = KEYD_F12; break; + case SDLK_BACKSPACE: rc = KEYD_BACKSPACE; break; + case SDLK_DELETE: rc = KEYD_DEL; break; + case SDLK_INSERT: rc = KEYD_INSERT; break; + case SDLK_PAGEUP: rc = KEYD_PAGEUP; break; + case SDLK_PAGEDOWN: rc = KEYD_PAGEDOWN; break; + case SDLK_HOME: rc = KEYD_HOME; break; + case SDLK_END: rc = KEYD_END; break; + case SDLK_PAUSE: rc = KEYD_PAUSE; break; + case SDLK_EQUALS: rc = KEYD_EQUALS; break; + case SDLK_MINUS: rc = KEYD_MINUS; break; + case SDLK_KP0: rc = KEYD_KEYPAD0; break; + case SDLK_KP1: rc = KEYD_KEYPAD1; break; + case SDLK_KP2: rc = KEYD_KEYPAD2; break; + case SDLK_KP3: rc = KEYD_KEYPAD3; break; + case SDLK_KP4: rc = KEYD_KEYPAD4; break; + case SDLK_KP5: rc = KEYD_KEYPAD5; break; + case SDLK_KP6: rc = KEYD_KEYPAD6; break; + case SDLK_KP7: rc = KEYD_KEYPAD7; break; + case SDLK_KP8: rc = KEYD_KEYPAD8; break; + case SDLK_KP9: rc = KEYD_KEYPAD9; break; + case SDLK_KP_PLUS: rc = KEYD_KEYPADPLUS; break; + case SDLK_KP_MINUS: rc = KEYD_KEYPADMINUS; break; + case SDLK_KP_DIVIDE: rc = KEYD_KEYPADDIVIDE; break; + case SDLK_KP_MULTIPLY: rc = KEYD_KEYPADMULTIPLY; break; + case SDLK_KP_ENTER: rc = KEYD_KEYPADENTER; break; + case SDLK_KP_PERIOD: rc = KEYD_KEYPADPERIOD; break; + case SDLK_LSHIFT: + case SDLK_RSHIFT: rc = KEYD_RSHIFT; break; + case SDLK_LCTRL: + case SDLK_RCTRL: rc = KEYD_RCTRL; break; + case SDLK_LALT: + case SDLK_LMETA: + case SDLK_RALT: + case SDLK_RMETA: rc = KEYD_RALT; break; + case SDLK_CAPSLOCK: rc = KEYD_CAPSLOCK; break; + default: rc = key->sym; break; + } + + return rc; + +} + +///////////////////////////////////////////////////////////////////////////////// +// Main input code + +/* cph - pulled out common button code logic */ +static int I_SDLtoDoomMouseState(Uint8 buttonstate) +{ + return 0 + | (buttonstate & SDL_BUTTON(1) ? 1 : 0) + | (buttonstate & SDL_BUTTON(2) ? 2 : 0) + | (buttonstate & SDL_BUTTON(3) ? 4 : 0); +} + +static void I_GetEvent(SDL_Event *Event) +{ + event_t event; + + switch (Event->type) { + case SDL_KEYDOWN: + event.type = ev_keydown; + event.data1 = I_TranslateKey(&Event->key.keysym); + D_PostEvent(&event); + break; + + case SDL_KEYUP: + { + event.type = ev_keyup; + event.data1 = I_TranslateKey(&Event->key.keysym); + D_PostEvent(&event); + } + break; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if (mouse_enabled) // recognise clicks even if the pointer isn't grabbed + { + event.type = ev_mouse; + event.data1 = I_SDLtoDoomMouseState(SDL_GetMouseState(NULL, NULL)); + event.data2 = event.data3 = 0; + D_PostEvent(&event); + } + break; + + case SDL_MOUSEMOTION: + if (mouse_currently_grabbed) { + event.type = ev_mouse; + event.data1 = I_SDLtoDoomMouseState(Event->motion.state); + event.data2 = Event->motion.xrel << 5; + event.data3 = -Event->motion.yrel << 5; + D_PostEvent(&event); + } + break; + + + case SDL_QUIT: + S_StartSound(NULL, sfx_swtchn); + M_QuitDOOM(0); + + default: + break; + } +} + + +// +// I_StartTic +// + +void I_StartTic (void) +{ + SDL_Event Event; + { + boolean should_be_grabbed = mouse_enabled && + !(paused || (gamestate != GS_LEVEL) || demoplayback || menuactive); + + if (mouse_currently_grabbed != should_be_grabbed) + SDL_WM_GrabInput((mouse_currently_grabbed = should_be_grabbed) + ? SDL_GRAB_ON : SDL_GRAB_OFF); + } + + while ( SDL_PollEvent(&Event) ) + I_GetEvent(&Event); +} + +// +// I_InitInputs +// + +static void I_InitInputs(void) +{ + int nomouse_parm = M_CheckParm("-nomouse"); + + // check if the user wants to use the mouse + mouse_enabled = usemouse && !nomouse_parm; + + // e6y: fix for turn-snapping bug on fullscreen in software mode + if (!nomouse_parm) + SDL_WarpMouse((unsigned short)(SCREENWIDTH/2), (unsigned short)(SCREENHEIGHT/2)); +} + +////////////////////////////////////////////////////////////////////////////// +// Graphics API + +// +// I_FinishUpdate +// +#define NO_PALETTE_CHANGE 1000 + +void I_FinishUpdate (void) +{ +#ifdef MONITOR_VISIBILITY + if (!(SDL_GetAppState()&SDL_APPACTIVE)) { + return; + } +#endif + + SDL_Flip(screen); +} + +// +// I_SetPalette +// +void I_SetPalette (int pal) +{ +} + +// I_PreInitGraphics + +void I_PreInitGraphics(void) +{ + static const union { + const char *c; + char *s; + } window_pos = {"SDL_VIDEO_WINDOW_POS=center"}; + + unsigned int flags = 0; + + putenv(window_pos.s); + + // Initialize SDL + if (!(M_CheckParm("-nodraw") && M_CheckParm("-nosound"))) + flags = SDL_INIT_VIDEO; + + if ( SDL_Init(flags) < 0 ) + I_Error("Could not initialize SDL [%s]", SDL_GetError()); + +} + +#define PIXEL_DEPTH 2 +#define PIXEL_DEPTH_32BPP 4 + +// CPhipps - +// I_SetRes +// Sets the screen resolution +void I_SetRes(void) +{ + int i; + + // set first three to standard values + for (i=0; i<3; i++) + screens[i].height = SCREENHEIGHT; + + // statusbar + screens[4].height = (ST_SCALED_HEIGHT+1); + + lprintf(LO_INFO,"I_SetRes: Using resolution %dx%d\n", SCREENWIDTH, SCREENHEIGHT); +} + +static void I_UpdateVideoMode(void) +{ + int init_flags; + int i; + + lprintf(LO_INFO, "I_UpdateVideoMode: %dx%d (%s)\n", SCREENWIDTH, SCREENHEIGHT, "nofullscreen"); + + V_InitMode(); + V_DestroyUnusedTrueColorPalettes(); + V_FreeScreens(); + + I_SetRes(); + + // Initialize SDL with this graphics mode + if (use_doublebuffer) + init_flags = SDL_DOUBLEBUF; + else + init_flags = SDL_SWSURFACE; + init_flags |= SDL_HWPALETTE; + + screen = SDL_SetVideoMode(SCREENWIDTH, SCREENHEIGHT, 15, init_flags); + + if(screen == NULL) { + I_Error("Couldn't set %dx%d video mode [%s]", SCREENWIDTH, SCREENHEIGHT, SDL_GetError()); + } + + lprintf(LO_INFO, "I_UpdateVideoMode: 0x%x, %s, %s\n", init_flags, screen->pixels ? "SDL buffer" : "own buffer", SDL_MUSTLOCK(screen) ? "lock-and-copy": "direct access"); + + mouse_currently_grabbed = false; + + // Get the info needed to render to the display + if (!SDL_MUSTLOCK(screen)) + { + screens[0].not_on_heap = true; + screens[0].data = (unsigned char *) (screen->pixels); + } + else + screens[0].not_on_heap = false; + + V_AllocScreens(); + + // Hide pointer while over this window + SDL_ShowCursor(0); + + R_InitBuffer(SCREENWIDTH, SCREENHEIGHT); + +} + +void I_InitGraphics(void) +{ + char titlebuffer[2048]; + static int firsttime=1; + + if (firsttime) + { + firsttime = 0; + + lprintf(LO_INFO, "I_InitGraphics: %dx%d\n", SCREENWIDTH, SCREENHEIGHT); + + /* Set the video mode */ + I_UpdateVideoMode(); + + /* Setup the window title */ + strcpy(titlebuffer,PACKAGE); + strcat(titlebuffer," "); + strcat(titlebuffer,VERSION); + SDL_WM_SetCaption(titlebuffer, titlebuffer); + + /* Initialize the input system */ + I_InitInputs(); + } +} + diff --git a/src/am_map.c b/src/am_map.c new file mode 100644 index 00000000..040c40bb --- /dev/null +++ b/src/am_map.c @@ -0,0 +1,1585 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * the automap code + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "st_stuff.h" +#include "r_main.h" +#include "p_setup.h" +#include "p_maputl.h" +#include "w_wad.h" +#include "v_video.h" +#include "p_spec.h" +#include "am_map.h" +#include "dstrings.h" +#include "d_deh.h" // Ty 03/27/98 - externalizations +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "g_game.h" + +//jff 1/7/98 default automap colors added +int mapcolor_back; // map background +int mapcolor_grid; // grid lines color +int mapcolor_wall; // normal 1s wall color +int mapcolor_fchg; // line at floor height change color +int mapcolor_cchg; // line at ceiling height change color +int mapcolor_clsd; // line at sector with floor=ceiling color +int mapcolor_rkey; // red key color +int mapcolor_bkey; // blue key color +int mapcolor_ykey; // yellow key color +int mapcolor_rdor; // red door color (diff from keys to allow option) +int mapcolor_bdor; // blue door color (of enabling one but not other ) +int mapcolor_ydor; // yellow door color +int mapcolor_tele; // teleporter line color +int mapcolor_secr; // secret sector boundary color +int mapcolor_exit; // jff 4/23/98 add exit line color +int mapcolor_unsn; // computer map unseen line color +int mapcolor_flat; // line with no floor/ceiling changes +int mapcolor_sprt; // general sprite color +int mapcolor_item; // item sprite color +int mapcolor_frnd; // friendly sprite color +int mapcolor_enemy; // enemy sprite color +int mapcolor_hair; // crosshair color +int mapcolor_sngl; // single player arrow color +int mapcolor_plyr[4] = { 112, 88, 64, 32 }; // colors for player arrows in multiplayer + +//jff 3/9/98 add option to not show secret sectors until entered +int map_secret_after; +//jff 4/3/98 add symbols for "no-color" for disable and "black color" for black +#define NC 0 +#define BC 247 + +// drawing stuff +#define FB 0 + +// scale on entry +#define INITSCALEMTOF (.2*FRACUNIT) +// how much the automap moves window per tic in frame-buffer coordinates +// moves 140 pixels in 1 second +#define F_PANINC 4 +// how much zoom-in per tic +// goes to 2x in 1 second +#define M_ZOOMIN ((int) (1.02*FRACUNIT)) +// how much zoom-out per tic +// pulls out to 0.5x in 1 second +#define M_ZOOMOUT ((int) (FRACUNIT/1.02)) + +#define PLAYERRADIUS (16*(1<>16) +// translates between frame-buffer and map coordinates +#define CXMTOF(x) (f_x + MTOF((x)-m_x)) +#define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y))) + +typedef struct +{ + mpoint_t a, b; +} mline_t; + +// +// The vector graphics for the automap. +// A line drawing of the player pointing right, +// starting from the middle. +// +#define R ((8*PLAYERRADIUS)/7) +mline_t player_arrow[] = +{ + { { -R+R/8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R-R/2, R/4 } }, // -----> + { { R, 0 }, { R-R/2, -R/4 } }, + { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >----> + { { -R+R/8, 0 }, { -R-R/8, -R/4 } }, + { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>---> + { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } } +}; +#undef R +#define NUMPLYRLINES (sizeof(player_arrow)/sizeof(mline_t)) + +#define R ((8*PLAYERRADIUS)/7) +mline_t cheat_player_arrow[] = +{ // killough 3/22/98: He's alive, Jim :) + { { -R+R/8, 0 }, { R, 0 } }, // ----- + { { R, 0 }, { R-R/2, R/4 } }, // -----> + { { R, 0 }, { R-R/2, -R/4 } }, + { { -R+R/8, 0 }, { -R-R/8, R/4 } }, // >----> + { { -R+R/8, 0 }, { -R-R/8, -R/4 } }, + { { -R+3*R/8, 0 }, { -R+R/8, R/4 } }, // >>---> + { { -R+3*R/8, 0 }, { -R+R/8, -R/4 } }, + { { -R/10-R/6, R/4}, {-R/10-R/6, -R/4} }, // J + { { -R/10-R/6, -R/4}, {-R/10-R/6-R/8, -R/4} }, + { { -R/10-R/6-R/8, -R/4}, {-R/10-R/6-R/8, -R/8} }, + { { -R/10, R/4}, {-R/10, -R/4}}, // F + { { -R/10, R/4}, {-R/10+R/8, R/4}}, + { { -R/10+R/4, R/4}, {-R/10+R/4, -R/4}}, // F + { { -R/10+R/4, R/4}, {-R/10+R/4+R/8, R/4}}, +}; +#undef R +#define NUMCHEATPLYRLINES (sizeof(cheat_player_arrow)/sizeof(mline_t)) + +#define R (FRACUNIT) +mline_t triangle_guy[] = +{ +{ { (fixed_t)(-.867*R), (fixed_t)(-.5*R) }, { (fixed_t)( .867*R), (fixed_t)(-.5*R) } }, +{ { (fixed_t)( .867*R), (fixed_t)(-.5*R) }, { (fixed_t)(0 ), (fixed_t)( R) } }, +{ { (fixed_t)(0 ), (fixed_t)( R) }, { (fixed_t)(-.867*R), (fixed_t)(-.5*R) } } +}; +#undef R +#define NUMTRIANGLEGUYLINES (sizeof(triangle_guy)/sizeof(mline_t)) + +//jff 1/5/98 new symbol for keys on automap +#define R (FRACUNIT) +mline_t cross_mark[] = +{ + { { -R, 0 }, { R, 0} }, + { { 0, -R }, { 0, R } }, +}; +#undef R +#define NUMCROSSMARKLINES (sizeof(cross_mark)/sizeof(mline_t)) +//jff 1/5/98 end of new symbol + +#define R (FRACUNIT) +mline_t thintriangle_guy[] = +{ +{ { (fixed_t)(-.5*R), (fixed_t)(-.7*R) }, { (fixed_t)( R), (fixed_t)( 0) } }, +{ { (fixed_t)( R), (fixed_t)( 0) }, { (fixed_t)(-.5*R), (fixed_t)( .7*R) } }, +{ { (fixed_t)(-.5*R), (fixed_t)( .7*R) }, { (fixed_t)(-.5*R), (fixed_t)(-.7*R) } } +}; +#undef R +#define NUMTHINTRIANGLEGUYLINES (sizeof(thintriangle_guy)/sizeof(mline_t)) + +int ddt_cheating = 0; // killough 2/7/98: make global, rename to ddt_* + +static int leveljuststarted = 1; // kluge until AM_LevelInit() is called + +enum automapmode_e automapmode; // Mode that the automap is in + +// location of window on screen +static int f_x; +static int f_y; + +// size of window on screen +static int f_w; +static int f_h; + +static mpoint_t m_paninc; // how far the window pans each tic (map coords) +static fixed_t mtof_zoommul; // how far the window zooms each tic (map coords) +static fixed_t ftom_zoommul; // how far the window zooms each tic (fb coords) + +static fixed_t m_x, m_y; // LL x,y window location on the map (map coords) +static fixed_t m_x2, m_y2; // UR x,y window location on the map (map coords) + +// +// width/height of window on map (map coords) +// +static fixed_t m_w; +static fixed_t m_h; + +// based on level size +static fixed_t min_x; +static fixed_t min_y; +static fixed_t max_x; +static fixed_t max_y; + +static fixed_t max_w; // max_x-min_x, +static fixed_t max_h; // max_y-min_y + +// based on player size +static fixed_t min_w; +static fixed_t min_h; + + +static fixed_t min_scale_mtof; // used to tell when to stop zooming out +static fixed_t max_scale_mtof; // used to tell when to stop zooming in + +// old stuff for recovery later +static fixed_t old_m_w, old_m_h; +static fixed_t old_m_x, old_m_y; + +// old location used by the Follower routine +static mpoint_t f_oldloc; + +// used by MTOF to scale from map-to-frame-buffer coords +static fixed_t scale_mtof = (fixed_t)INITSCALEMTOF; +// used by FTOM to scale from frame-buffer-to-map coords (=1/scale_mtof) +static fixed_t scale_ftom; + +static player_t *plr; // the player represented by an arrow + +// killough 2/22/98: Remove limit on automap marks, +// and make variables external for use in savegames. + +mpoint_t *markpoints = NULL; // where the points are +int markpointnum = 0; // next point to be assigned (also number of points now) +int markpointnum_max = 0; // killough 2/22/98 + +static boolean stopped = true; + +// +// AM_activateNewScale() +// +// Changes the map scale after zooming or translating +// +// Passed nothing, returns nothing +// +static void AM_activateNewScale(void) +{ + m_x += m_w/2; + m_y += m_h/2; + m_w = FTOM(f_w); + m_h = FTOM(f_h); + m_x -= m_w/2; + m_y -= m_h/2; + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + +// +// AM_saveScaleAndLoc() +// +// Saves the current center and zoom +// Affects the variables that remember old scale and loc +// +// Passed nothing, returns nothing +// +static void AM_saveScaleAndLoc(void) +{ + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; +} + +// +// AM_restoreScaleAndLoc() +// +// restores the center and zoom from locally saved values +// Affects global variables for location and scale +// +// Passed nothing, returns nothing +// +static void AM_restoreScaleAndLoc(void) +{ + m_w = old_m_w; + m_h = old_m_h; + if (!(automapmode & am_follow)) + { + m_x = old_m_x; + m_y = old_m_y; + } + else + { + m_x = (plr->mo->x >> FRACTOMAPBITS) - m_w/2;//e6y + m_y = (plr->mo->y >> FRACTOMAPBITS) - m_h/2;//e6y + } + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + + // Change the scaling multipliers + scale_mtof = FixedDiv(f_w<= markpointnum_max) + markpoints = realloc(markpoints, + (markpointnum_max = markpointnum_max ? + markpointnum_max*2 : 16) * sizeof(*markpoints)); + + markpoints[markpointnum].x = m_x + m_w/2; + markpoints[markpointnum].y = m_y + m_h/2; + markpointnum++; +} + +// +// AM_findMinMaxBoundaries() +// +// Determines bounding box of all vertices, +// sets global variables controlling zoom range. +// +// Passed nothing, returns nothing +// +static void AM_findMinMaxBoundaries(void) +{ + int i; + fixed_t a; + fixed_t b; + + min_x = min_y = INT_MAX; + max_x = max_y = -INT_MAX; + + for (i=0;i max_x) + max_x = vertexes[i].x; + + if (vertexes[i].y < min_y) + min_y = vertexes[i].y; + else if (vertexes[i].y > max_y) + max_y = vertexes[i].y; + } + + max_w = (max_x >>= FRACTOMAPBITS) - (min_x >>= FRACTOMAPBITS);//e6y + max_h = (max_y >>= FRACTOMAPBITS) - (min_y >>= FRACTOMAPBITS);//e6y + + min_w = 2*PLAYERRADIUS; // const? never changed? + min_h = 2*PLAYERRADIUS; + + a = FixedDiv(f_w< max_x) + m_x = max_x - m_w/2; + else if (m_x + m_w/2 < min_x) + m_x = min_x - m_w/2; + + if (m_y + m_h/2 > max_y) + m_y = max_y - m_h/2; + else if (m_y + m_h/2 < min_y) + m_y = min_y - m_h/2; + + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; +} + + +// +// AM_initVariables() +// +// Initialize the variables for the automap +// +// Affects the automap global variables +// Status bar is notified that the automap has been entered +// Passed nothing, returns nothing +// +static void AM_initVariables(void) +{ + int pnum; + static event_t st_notify = { ev_keyup, AM_MSGENTERED, 0, 0 }; + + automapmode |= am_active; + + f_oldloc.x = INT_MAX; + + m_paninc.x = m_paninc.y = 0; + ftom_zoommul = FRACUNIT; + mtof_zoommul = FRACUNIT; + + m_w = FTOM(f_w); + m_h = FTOM(f_h); + + // find player to center on initially + if (!playeringame[pnum = consoleplayer]) + for (pnum=0;pnummo->x >> FRACTOMAPBITS) - m_w/2;//e6y + m_y = (plr->mo->y >> FRACTOMAPBITS) - m_h/2;//e6y + AM_changeWindowLoc(); + + // for saving & restoring + old_m_x = m_x; + old_m_y = m_y; + old_m_w = m_w; + old_m_h = m_h; + + // inform the status bar of the change + ST_Responder(&st_notify); +} + +// +// AM_loadPics() +// +static void AM_loadPics(void) +{ + // cph - mark numbers no longer needed cached +} + +// +// AM_unloadPics() +// +static void AM_unloadPics(void) +{ + // cph - mark numbers no longer needed cached +} + +// +// AM_clearMarks() +// +// Sets the number of marks to 0, thereby clearing them from the display +// +// Affects the global variable markpointnum +// Passed nothing, returns nothing +// +void AM_clearMarks(void) +{ + markpointnum = 0; +} + +// +// AM_LevelInit() +// +// Initialize the automap at the start of a new level +// should be called at the start of every level +// +// Passed nothing, returns nothing +// Affects automap's global variables +// +// CPhipps - get status bar height from status bar code +static void AM_LevelInit(void) +{ + leveljuststarted = 0; + + f_x = f_y = 0; + f_w = SCREENWIDTH; // killough 2/7/98: get rid of finit_ vars + f_h = SCREENHEIGHT-ST_SCALED_HEIGHT;// to allow runtime setting of width/height + + AM_findMinMaxBoundaries(); + scale_mtof = FixedDiv(min_scale_mtof, (int) (0.7*FRACUNIT)); + if (scale_mtof > max_scale_mtof) + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); +} + +// +// AM_Stop() +// +// Cease automap operations, unload patches, notify status bar +// +// Passed nothing, returns nothing +// +void AM_Stop (void) +{ + static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED, 0 }; + + AM_unloadPics(); + automapmode &= ~am_active; + ST_Responder(&st_notify); + stopped = true; +} + +// +// AM_Start() +// +// Start up automap operations, +// if a new level, or game start, (re)initialize level variables +// init map variables +// load mark patches +// +// Passed nothing, returns nothing +// +void AM_Start(void) +{ + static int lastlevel = -1, lastepisode = -1; + + if (!stopped) + AM_Stop(); + stopped = false; + if (lastlevel != gamemap || lastepisode != gameepisode) + { + AM_LevelInit(); + lastlevel = gamemap; + lastepisode = gameepisode; + } + AM_initVariables(); + AM_loadPics(); +} + +// +// AM_minOutWindowScale() +// +// Set the window scale to the maximum size +// +// Passed nothing, returns nothing +// +static void AM_minOutWindowScale(void) +{ + scale_mtof = min_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + +// +// AM_maxOutWindowScale(void) +// +// Set the window scale to the minimum size +// +// Passed nothing, returns nothing +// +static void AM_maxOutWindowScale(void) +{ + scale_mtof = max_scale_mtof; + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + AM_activateNewScale(); +} + +// +// AM_Responder() +// +// Handle events (user inputs) in automap mode +// +// Passed an input event, returns true if its handled +// +boolean AM_Responder +( event_t* ev ) +{ + int rc; + static int cheatstate=0; + static int bigstate=0; + int ch; // phares + + rc = false; + + if (!(automapmode & am_active)) + { + if (ev->type == ev_keydown && ev->data1 == key_map) // phares + { + AM_Start (); + rc = true; + } + } + else if (ev->type == ev_keydown) + { + rc = true; + ch = ev->data1; // phares + if (ch == key_map_right) // | + if (!(automapmode & am_follow)) // V + m_paninc.x = FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_left) + if (!(automapmode & am_follow)) + m_paninc.x = -FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_up) + if (!(automapmode & am_follow)) + m_paninc.y = FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_down) + if (!(automapmode & am_follow)) + m_paninc.y = -FTOM(F_PANINC); + else + rc = false; + else if (ch == key_map_zoomout) + { + mtof_zoommul = M_ZOOMOUT; + ftom_zoommul = M_ZOOMIN; + } + else if (ch == key_map_zoomin) + { + mtof_zoommul = M_ZOOMIN; + ftom_zoommul = M_ZOOMOUT; + } + else if (ch == key_map) + { + bigstate = 0; + AM_Stop (); + } + else if (ch == key_map_gobig) + { + bigstate = !bigstate; + if (bigstate) + { + AM_saveScaleAndLoc(); + AM_minOutWindowScale(); + } + else + AM_restoreScaleAndLoc(); + } + else if (ch == key_map_follow) + { + automapmode ^= am_follow; // CPhipps - put all automap mode stuff into one enum + f_oldloc.x = INT_MAX; + // Ty 03/27/98 - externalized + plr->message = (automapmode & am_follow) ? s_AMSTR_FOLLOWON : s_AMSTR_FOLLOWOFF; + } + else if (ch == key_map_grid) + { + automapmode ^= am_grid; // CPhipps + // Ty 03/27/98 - *not* externalized + plr->message = (automapmode & am_grid) ? s_AMSTR_GRIDON : s_AMSTR_GRIDOFF; + } + else if (ch == key_map_mark) + { + /* Ty 03/27/98 - *not* externalized + * cph 2001/11/20 - use doom_printf so we don't have our own buffer */ + doom_printf("%s %d", s_AMSTR_MARKEDSPOT, markpointnum); + AM_addMark(); + } + else if (ch == key_map_clear) + { + AM_clearMarks(); // Ty 03/27/98 - *not* externalized + plr->message = s_AMSTR_MARKSCLEARED; // ^ + } // | + else if (ch == key_map_rotate) { + automapmode ^= am_rotate; + plr->message = (automapmode & am_rotate) ? s_AMSTR_ROTATEON : s_AMSTR_ROTATEOFF; + } + else if (ch == key_map_overlay) { + automapmode ^= am_overlay; + plr->message = (automapmode & am_overlay) ? s_AMSTR_OVERLAYON : s_AMSTR_OVERLAYOFF; + } + else // phares + { + cheatstate=0; + rc = false; + } + } + else if (ev->type == ev_keyup) + { + rc = false; + ch = ev->data1; + if (ch == key_map_right) + { + if (!(automapmode & am_follow)) + m_paninc.x = 0; + } + else if (ch == key_map_left) + { + if (!(automapmode & am_follow)) + m_paninc.x = 0; + } + else if (ch == key_map_up) + { + if (!(automapmode & am_follow)) + m_paninc.y = 0; + } + else if (ch == key_map_down) + { + if (!(automapmode & am_follow)) + m_paninc.y = 0; + } + else if ((ch == key_map_zoomout) || (ch == key_map_zoomin)) + { + mtof_zoommul = FRACUNIT; + ftom_zoommul = FRACUNIT; + } + } + return rc; +} + +// +// AM_rotate() +// +// Rotation in 2D. +// Used to rotate player arrow line character. +// +// Passed the coordinates of a point, and an angle +// Returns the coordinates rotated by the angle +// +// CPhipps - made static & enhanced for automap rotation + +static void AM_rotate(fixed_t* x, fixed_t* y, angle_t a, fixed_t xorig, fixed_t yorig) +{ + fixed_t tmpx; + + //e6y + xorig>>=FRACTOMAPBITS; + yorig>>=FRACTOMAPBITS; + + tmpx = + FixedMul(*x - xorig,finecosine[a>>ANGLETOFINESHIFT]) + - FixedMul(*y - yorig,finesine[a>>ANGLETOFINESHIFT]); + + *y = yorig + + FixedMul(*x - xorig,finesine[a>>ANGLETOFINESHIFT]) + + FixedMul(*y - yorig,finecosine[a>>ANGLETOFINESHIFT]); + + *x = tmpx + xorig; +} + +// +// AM_changeWindowScale() +// +// Automap zooming +// +// Passed nothing, returns nothing +// +static void AM_changeWindowScale(void) +{ + // Change the scaling multipliers + scale_mtof = FixedMul(scale_mtof, mtof_zoommul); + scale_ftom = FixedDiv(FRACUNIT, scale_mtof); + + if (scale_mtof < min_scale_mtof) + AM_minOutWindowScale(); + else if (scale_mtof > max_scale_mtof) + AM_maxOutWindowScale(); + else + AM_activateNewScale(); +} + +// +// AM_doFollowPlayer() +// +// Turn on follow mode - the map scrolls opposite to player motion +// +// Passed nothing, returns nothing +// +static void AM_doFollowPlayer(void) +{ + if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y) + { + m_x = FTOM(MTOF(plr->mo->x >> FRACTOMAPBITS)) - m_w/2;//e6y + m_y = FTOM(MTOF(plr->mo->y >> FRACTOMAPBITS)) - m_h/2;//e6y + m_x2 = m_x + m_w; + m_y2 = m_y + m_h; + f_oldloc.x = plr->mo->x; + f_oldloc.y = plr->mo->y; + } +} + +// +// AM_Ticker() +// +// Updates on gametic - enter follow mode, zoom, or change map location +// +// Passed nothing, returns nothing +// +void AM_Ticker (void) +{ + if (!(automapmode & am_active)) + return; + + if (automapmode & am_follow) + AM_doFollowPlayer(); + + // Change the zoom if necessary + if (ftom_zoommul != FRACUNIT) + AM_changeWindowScale(); + + // Change x,y location + if (m_paninc.x || m_paninc.y) + AM_changeWindowLoc(); +} + +// +// AM_clipMline() +// +// Automap clipping of lines. +// +// Based on Cohen-Sutherland clipping algorithm but with a slightly +// faster reject and precalculated slopes. If the speed is needed, +// use a hash algorithm to handle the common cases. +// +// Passed the line's coordinates on map and in the frame buffer performs +// clipping on them in the lines frame coordinates. +// Returns true if any part of line was not clipped +// +static boolean AM_clipMline +( mline_t* ml, + fline_t* fl ) +{ + enum + { + LEFT =1, + RIGHT =2, + BOTTOM =4, + TOP =8 + }; + + register int outcode1 = 0; + register int outcode2 = 0; + register int outside; + + fpoint_t tmp; + int dx; + int dy; + + +#define DOOUTCODE(oc, mx, my) \ + (oc) = 0; \ + if ((my) < 0) (oc) |= TOP; \ + else if ((my) >= f_h) (oc) |= BOTTOM; \ + if ((mx) < 0) (oc) |= LEFT; \ + else if ((mx) >= f_w) (oc) |= RIGHT; + + + // do trivial rejects and outcodes + if (ml->a.y > m_y2) + outcode1 = TOP; + else if (ml->a.y < m_y) + outcode1 = BOTTOM; + + if (ml->b.y > m_y2) + outcode2 = TOP; + else if (ml->b.y < m_y) + outcode2 = BOTTOM; + + if (outcode1 & outcode2) + return false; // trivially outside + + if (ml->a.x < m_x) + outcode1 |= LEFT; + else if (ml->a.x > m_x2) + outcode1 |= RIGHT; + + if (ml->b.x < m_x) + outcode2 |= LEFT; + else if (ml->b.x > m_x2) + outcode2 |= RIGHT; + + if (outcode1 & outcode2) + return false; // trivially outside + + // transform to frame-buffer coordinates. + fl->a.x = CXMTOF(ml->a.x); + fl->a.y = CYMTOF(ml->a.y); + fl->b.x = CXMTOF(ml->b.x); + fl->b.y = CYMTOF(ml->b.y); + + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + + if (outcode1 & outcode2) + return false; + + while (outcode1 | outcode2) + { + // may be partially inside box + // find an outside point + if (outcode1) + outside = outcode1; + else + outside = outcode2; + + // clip to each side + if (outside & TOP) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx*(fl->a.y))/dy; + tmp.y = 0; + } + else if (outside & BOTTOM) + { + dy = fl->a.y - fl->b.y; + dx = fl->b.x - fl->a.x; + tmp.x = fl->a.x + (dx*(fl->a.y-f_h))/dy; + tmp.y = f_h-1; + } + else if (outside & RIGHT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy*(f_w-1 - fl->a.x))/dx; + tmp.x = f_w-1; + } + else if (outside & LEFT) + { + dy = fl->b.y - fl->a.y; + dx = fl->b.x - fl->a.x; + tmp.y = fl->a.y + (dy*(-fl->a.x))/dx; + tmp.x = 0; + } + + if (outside == outcode1) + { + fl->a = tmp; + DOOUTCODE(outcode1, fl->a.x, fl->a.y); + } + else + { + fl->b = tmp; + DOOUTCODE(outcode2, fl->b.x, fl->b.y); + } + + if (outcode1 & outcode2) + return false; // trivially outside + } + + return true; +} +#undef DOOUTCODE + +// +// AM_drawMline() +// +// Clip lines, draw visible parts of lines. +// +// Passed the map coordinates of the line, and the color to draw it +// Color -1 is special and prevents drawing. Color 247 is special and +// is translated to black, allowing Color 0 to represent feature disable +// in the defaults file. +// Returns nothing. +// +static void AM_drawMline +( mline_t* ml, + int color ) +{ + static fline_t fl; + + if (color==-1) // jff 4/3/98 allow not drawing any sort of line + return; // by setting its color to -1 + if (color==247) // jff 4/3/98 if color is 247 (xparent), use black + color=0; + + if (AM_clipMline(ml, &fl)) + V_DrawLine(&fl, color); // draws it on frame buffer using fb coords +} + +// +// AM_drawGrid() +// +// Draws blockmap aligned grid lines. +// +// Passed the color to draw the grid lines +// Returns nothing +// +static void AM_drawGrid(int color) +{ + fixed_t x, y; + fixed_t start, end; + mline_t ml; + + // Figure out start of vertical gridlines + start = m_x; + if ((start-bmaporgx)%(MAPBLOCKUNITS<> LockedKeyShift; + if (!type || type==7) + return 3; //any or all keys + else return (type-1)%3; + } + switch (type) // closed keyed door + { + case 26: case 32: case 99: case 133: + /*bluekey*/ + return 1; + case 27: case 34: case 136: case 137: + /*yellowkey*/ + return 2; + case 28: case 33: case 134: case 135: + /*redkey*/ + return 0; + default: + return -1; //not a keyed door + } +} + +// +// Determines visible lines, draws them. +// This is LineDef based, not LineSeg based. +// +// jff 1/5/98 many changes in this routine +// backward compatibility not needed, so just changes, no ifs +// addition of clauses for: +// doors opening, keyed door id, secret sectors, +// teleports, exit lines, key things +// ability to suppress any of added features or lines with no height changes +// +// support for gamma correction in automap abandoned +// +// jff 4/3/98 changed mapcolor_xxxx=0 as control to disable feature +// jff 4/3/98 changed mapcolor_xxxx=-1 to disable drawing line completely +// +static void AM_drawWalls(void) +{ + int i; + static mline_t l; + + // draw the unclipped visible portions of all lines + for (i=0;ix >> FRACTOMAPBITS;//e6y + l.a.y = lines[i].v1->y >> FRACTOMAPBITS;//e6y + l.b.x = lines[i].v2->x >> FRACTOMAPBITS;//e6y + l.b.y = lines[i].v2->y >> FRACTOMAPBITS;//e6y + + if (automapmode & am_rotate) { + AM_rotate(&l.a.x, &l.a.y, ANG90-plr->mo->angle, plr->mo->x, plr->mo->y); + AM_rotate(&l.b.x, &l.b.y, ANG90-plr->mo->angle, plr->mo->x, plr->mo->y); + } + + // if line has been seen or IDDT has been used + if (ddt_cheating || (lines[i].flags & ML_MAPPED)) + { + if ((lines[i].flags & ML_DONTDRAW) && !ddt_cheating) + continue; + { + /* cph - show keyed doors and lines */ + int amd; + if ((mapcolor_bdor || mapcolor_ydor || mapcolor_rdor) && + !(lines[i].flags & ML_SECRET) && /* non-secret */ + (amd = AM_DoorColor(lines[i].special)) != -1 + ) + { + { + switch (amd) /* closed keyed door */ + { + case 1: + /*bluekey*/ + AM_drawMline(&l, + mapcolor_bdor? mapcolor_bdor : mapcolor_cchg); + continue; + case 2: + /*yellowkey*/ + AM_drawMline(&l, + mapcolor_ydor? mapcolor_ydor : mapcolor_cchg); + continue; + case 0: + /*redkey*/ + AM_drawMline(&l, + mapcolor_rdor? mapcolor_rdor : mapcolor_cchg); + continue; + case 3: + /*any or all*/ + AM_drawMline(&l, + mapcolor_clsd? mapcolor_clsd : mapcolor_cchg); + continue; + } + } + } + } + if /* jff 4/23/98 add exit lines to automap */ + ( + mapcolor_exit && + ( + lines[i].special==11 || + lines[i].special==52 || + lines[i].special==197 || + lines[i].special==51 || + lines[i].special==124 || + lines[i].special==198 + ) + ) { + AM_drawMline(&l, mapcolor_exit); /* exit line */ + continue; + } + + if (!lines[i].backsector) + { + // jff 1/10/98 add new color for 1S secret sector boundary + if (mapcolor_secr && //jff 4/3/98 0 is disable + ( + ( + map_secret_after && + P_WasSecret(lines[i].frontsector) && + !P_IsSecret(lines[i].frontsector) + ) + || + ( + !map_secret_after && + P_WasSecret(lines[i].frontsector) + ) + ) + ) + AM_drawMline(&l, mapcolor_secr); // line bounding secret sector + else //jff 2/16/98 fixed bug + AM_drawMline(&l, mapcolor_wall); // special was cleared + } + else /* now for 2S lines */ + { + // jff 1/10/98 add color change for all teleporter types + if + ( + mapcolor_tele && !(lines[i].flags & ML_SECRET) && + (lines[i].special == 39 || lines[i].special == 97 || + lines[i].special == 125 || lines[i].special == 126) + ) + { // teleporters + AM_drawMline(&l, mapcolor_tele); + } + else if (lines[i].flags & ML_SECRET) // secret door + { + AM_drawMline(&l, mapcolor_wall); // wall color + } + else if + ( + mapcolor_clsd && + !(lines[i].flags & ML_SECRET) && // non-secret closed door + ((lines[i].backsector->floorheight==lines[i].backsector->ceilingheight) || + (lines[i].frontsector->floorheight==lines[i].frontsector->ceilingheight)) + ) + { + AM_drawMline(&l, mapcolor_clsd); // non-secret closed door + } //jff 1/6/98 show secret sector 2S lines + else if + ( + mapcolor_secr && //jff 2/16/98 fixed bug + ( // special was cleared after getting it + (map_secret_after && + ( + (P_WasSecret(lines[i].frontsector) + && !P_IsSecret(lines[i].frontsector)) || + (P_WasSecret(lines[i].backsector) + && !P_IsSecret(lines[i].backsector)) + ) + ) + || //jff 3/9/98 add logic to not show secret til after entered + ( // if map_secret_after is true + !map_secret_after && + (P_WasSecret(lines[i].frontsector) || + P_WasSecret(lines[i].backsector)) + ) + ) + ) + { + AM_drawMline(&l, mapcolor_secr); // line bounding secret sector + } //jff 1/6/98 end secret sector line change + else if (lines[i].backsector->floorheight != + lines[i].frontsector->floorheight) + { + AM_drawMline(&l, mapcolor_fchg); // floor level change + } + else if (lines[i].backsector->ceilingheight != + lines[i].frontsector->ceilingheight) + { + AM_drawMline(&l, mapcolor_cchg); // ceiling level change + } + else if (mapcolor_flat && ddt_cheating) + { + AM_drawMline(&l, mapcolor_flat); //2S lines that appear only in IDDT + } + } + } // now draw the lines only visible because the player has computermap + else if (plr->powers[pw_allmap]) // computermap visible lines + { + if (!(lines[i].flags & ML_DONTDRAW)) // invisible flag lines do not show + { + if + ( + mapcolor_flat + || + !lines[i].backsector + || + lines[i].backsector->floorheight + != lines[i].frontsector->floorheight + || + lines[i].backsector->ceilingheight + != lines[i].frontsector->ceilingheight + ) + AM_drawMline(&l, mapcolor_unsn); + } + } + } +} + +// +// AM_drawLineCharacter() +// +// Draws a vector graphic according to numerous parameters +// +// Passed the structure defining the vector graphic shape, the number +// of vectors in it, the scale to draw it at, the angle to draw it at, +// the color to draw it with, and the map coordinates to draw it at. +// Returns nothing +// +static void AM_drawLineCharacter +( mline_t* lineguy, + int lineguylines, + fixed_t scale, + angle_t angle, + int color, + fixed_t x, + fixed_t y ) +{ + int i; + mline_t l; + + if (automapmode & am_rotate) angle -= plr->mo->angle - ANG90; // cph + + for (i=0;imo->angle, + mapcolor_sngl, //jff color + plr->mo->x >> FRACTOMAPBITS,//e6y + plr->mo->y >> FRACTOMAPBITS//e6y + ); + else + AM_drawLineCharacter + ( + player_arrow, + NUMPLYRLINES, + 0, + plr->mo->angle, + mapcolor_sngl, //jff color + plr->mo->x >> FRACTOMAPBITS,//e6y + plr->mo->y >> FRACTOMAPBITS);//e6y + return; + } + + for (i=0;imo->x >> FRACTOMAPBITS, y = p->mo->y >> FRACTOMAPBITS;//e6y + if (automapmode & am_rotate) + AM_rotate(&x, &y, ANG90-plr->mo->angle, plr->mo->x, plr->mo->y); + + AM_drawLineCharacter (player_arrow, NUMPLYRLINES, 0, p->mo->angle, + p->powers[pw_invisibility] ? 246 /* *close* to black */ + : mapcolor_plyr[i], //jff 1/6/98 use default color + x, y); + } + } +} + +// +// AM_drawThings() +// +// Draws the things on the automap in double IDDT cheat mode +// +// Passed colors and colorrange, no longer used +// Returns nothing +// +static void AM_drawThings(void) +{ + int i; + mobj_t* t; + + // for all sectors + for (i=0;ix >> FRACTOMAPBITS, y = t->y >> FRACTOMAPBITS;//e6y + + if (automapmode & am_rotate) + AM_rotate(&x, &y, ANG90-plr->mo->angle, plr->mo->x, plr->mo->y); + + //jff 1/5/98 case over doomednum of thing being drawn + if (mapcolor_rkey || mapcolor_ykey || mapcolor_bkey) + { + switch(t->info->doomednum) + { + //jff 1/5/98 treat keys special + case 38: case 13: //jff red key + AM_drawLineCharacter + ( + cross_mark, + NUMCROSSMARKLINES, + 16<angle, + mapcolor_rkey!=-1? mapcolor_rkey : mapcolor_sprt, + x, y + ); + t = t->snext; + continue; + case 39: case 6: //jff yellow key + AM_drawLineCharacter + ( + cross_mark, + NUMCROSSMARKLINES, + 16<angle, + mapcolor_ykey!=-1? mapcolor_ykey : mapcolor_sprt, + x, y + ); + t = t->snext; + continue; + case 40: case 5: //jff blue key + AM_drawLineCharacter + ( + cross_mark, + NUMCROSSMARKLINES, + 16<angle, + mapcolor_bkey!=-1? mapcolor_bkey : mapcolor_sprt, + x, y + ); + t = t->snext; + continue; + default: + break; + } + } + //jff 1/5/98 end added code for keys + //jff previously entire code + AM_drawLineCharacter + ( + thintriangle_guy, + NUMTHINTRIANGLEGUYLINES, + 16<angle, + t->flags & MF_FRIEND && !t->player ? mapcolor_frnd : + /* cph 2006/07/30 - Show count-as-kills in red. */ + ((t->flags & (MF_COUNTKILL | MF_CORPSE)) == MF_COUNTKILL) ? mapcolor_enemy : + /* bbm 2/28/03 Show countable items in yellow. */ + t->flags & MF_COUNTITEM ? mapcolor_item : mapcolor_sprt, + x, y + ); + t = t->snext; + } + } +} + +// +// AM_drawMarks() +// +// Draw the marked locations on the automap +// +// Passed nothing, returns nothing +// +// killough 2/22/98: +// Rewrote AM_drawMarks(). Removed limit on marks. +// +static void AM_drawMarks(void) +{ + int i; + for (i=0;imo->angle, plr->mo->x, plr->mo->y); + + fx = CXMTOF(fx); fy = CYMTOF(fy); + + do + { + int d = j % 10; + if (d==1) // killough 2/22/98: less spacing for '1' + fx++; + + if (fx >= f_x && fx < f_w - w && fy >= f_y && fy < f_h - h) { + // cph - construct patch name and draw marker + char namebuf[] = { 'A', 'M', 'M', 'N', 'U', 'M', '0'+d, 0 }; + + V_DrawNamePatch(fx, fy, FB, namebuf, CR_DEFAULT, VPT_NONE); + } + fx -= w-1; // killough 2/22/98: 1 space backwards + j /= 10; + } + while (j>0); + } +} + +// +// AM_drawCrosshair() +// +// Draw the single point crosshair representing map center +// +// Passed the color to draw the pixel with +// Returns nothing +// +// CPhipps - made static inline, and use the general pixel plotter function + +inline static void AM_drawCrosshair(int color) +{ + fline_t line; + + line.a.x = (f_w/2)-1; + line.a.y = (f_h/2); + line.b.x = (f_w/2)+1; + line.b.y = (f_h/2); + V_DrawLine(&line, color); + + line.a.x = (f_w/2); + line.a.y = (f_h/2)-1; + line.b.x = (f_w/2); + line.b.y = (f_h/2)+1; + V_DrawLine(&line, color); +} + +// +// AM_Drawer() +// +// Draws the entire automap +// +// Passed nothing, returns nothing +// +void AM_Drawer (void) +{ + // CPhipps - all automap modes put into one enum + if (!(automapmode & am_active)) return; + + if (!(automapmode & am_overlay)) // cph - If not overlay mode, clear background for the automap + V_FillRect(FB, f_x, f_y, f_w, f_h, (byte)mapcolor_back); //jff 1/5/98 background default color + if (automapmode & am_grid) + AM_drawGrid(mapcolor_grid); //jff 1/7/98 grid default color + AM_drawWalls(); + AM_drawPlayers(); + if (ddt_cheating==2) + AM_drawThings(); //jff 1/5/98 default double IDDT sprite + AM_drawCrosshair(mapcolor_hair); //jff 1/7/98 default crosshair color + + AM_drawMarks(); +} diff --git a/src/am_map.h b/src/am_map.h new file mode 100644 index 00000000..850c9ce5 --- /dev/null +++ b/src/am_map.h @@ -0,0 +1,111 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * AutoMap module. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __AMMAP_H__ +#define __AMMAP_H__ + +#include "d_event.h" + +#define MAPBITS 12 +#define FRACTOMAPBITS (FRACBITS-MAPBITS) + +// Used by ST StatusBar stuff. +#define AM_MSGHEADER (('a'<<24)+('m'<<16)) +#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8)) +#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8)) + +// Called by main loop. +boolean AM_Responder (event_t* ev); + +// Called by main loop. +void AM_Ticker (void); + +// Called by main loop, +// called instead of view drawer if automap active. +void AM_Drawer (void); + +// Called to force the automap to quit +// if the level is completed while it is up. +void AM_Stop (void); + +// killough 2/22/98: for saving automap information in savegame: + +extern void AM_Start(void); + +//jff 4/16/98 make externally available + +extern void AM_clearMarks(void); + +typedef struct +{ + fixed_t x,y; +} mpoint_t; + +extern mpoint_t *markpoints; +extern int markpointnum, markpointnum_max; + +// end changes -- killough 2/22/98 + +// killough 5/2/98: moved from m_misc.c + +//jff 1/7/98 automap colors added +extern int mapcolor_back; // map background +extern int mapcolor_grid; // grid lines color +extern int mapcolor_wall; // normal 1s wall color +extern int mapcolor_fchg; // line at floor height change color +extern int mapcolor_cchg; // line at ceiling height change color +extern int mapcolor_clsd; // line at sector with floor=ceiling color +extern int mapcolor_rkey; // red key color +extern int mapcolor_bkey; // blue key color +extern int mapcolor_ykey; // yellow key color +extern int mapcolor_rdor; // red door color (diff from keys to allow option) +extern int mapcolor_bdor; // blue door color (of enabling one not other) +extern int mapcolor_ydor; // yellow door color +extern int mapcolor_tele; // teleporter line color +extern int mapcolor_secr; // secret sector boundary color +//jff 4/23/98 +extern int mapcolor_exit; // exit line +extern int mapcolor_unsn; // computer map unseen line color +extern int mapcolor_flat; // line with no floor/ceiling changes +extern int mapcolor_sprt; // general sprite color +extern int mapcolor_item; // item sprite color +extern int mapcolor_enemy; // enemy sprite color +extern int mapcolor_frnd; // friendly sprite color +extern int mapcolor_hair; // crosshair color +extern int mapcolor_sngl; // single player arrow color +extern int mapcolor_plyr[4]; // colors for players in multiplayer +extern int mapcolor_me; // consoleplayer's chosen colour +//jff 3/9/98 +extern int map_secret_after; // secrets do not appear til after bagged + +#endif diff --git a/src/d_client.c b/src/d_client.c new file mode 100644 index 00000000..c134605a --- /dev/null +++ b/src/d_client.c @@ -0,0 +1,533 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Network client. Passes information to/from server, staying + * synchronised. + * Contains the main wait loop, waiting for network input or + * time before doing the next tic. + * Rewritten for LxDoom, but based around bits of the old code. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include + +#ifdef USE_SDL_NET + #include "SDL.h" +#endif + +#include "doomtype.h" +#include "doomstat.h" +#include "d_net.h" +#include "z_zone.h" + +#include "d_main.h" +#include "g_game.h" +#include "m_menu.h" +#include "p_checksum.h" + +#include "protocol.h" +#include "i_network.h" +#include "i_system.h" +#include "i_main.h" +#include "i_video.h" +#include "m_argv.h" +#include "r_fps.h" +#include "lprintf.h" + +static boolean server; +static int remotetic; // Tic expected from the remote +static int remotesend; // Tic expected by the remote +ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS]; +static ticcmd_t* localcmds; +static unsigned numqueuedpackets; +static packet_header_t** queuedpacket; +int maketic; +int ticdup = 1; +static int xtratics = 0; +int wanted_player_number; + +static boolean isExtraDDisplay = false; + +#ifdef HAVE_NET +void D_CheckNetGame(void) +{ + packet_header_t *packet = Z_Malloc(sizeof(packet_header_t)+1, PU_STATIC, NULL); + + if (server) { + lprintf(LO_INFO, "D_CheckNetGame: waiting for server to signal game start\n"); + do { + while (!I_GetPacket(packet, sizeof(packet_header_t)+1)) { + packet_set(packet, PKT_GO, 0); + *(byte*)(packet+1) = consoleplayer; + I_SendPacket(packet, sizeof(packet_header_t)+1); + I_uSleep(100000); + } + } while (packet->type != PKT_GO); + } + Z_Free(packet); +} + +boolean D_NetGetWad(const char* name) +{ +#if defined(HAVE_WAIT_H) + size_t psize = sizeof(packet_header_t) + strlen(name) + 500; + packet_header_t *packet; + boolean done = false; + + if (!server || strchr(name, '/')) return false; // If it contains path info, reject + + do { + // Send WAD request to remote + packet = Z_Malloc(psize, PU_STATIC, NULL); + packet_set(packet, PKT_WAD, 0); + *(byte*)(packet+1) = consoleplayer; + strcpy(1+(byte*)(packet+1), name); + I_SendPacket(packet, sizeof(packet_header_t) + strlen(name) + 2); + + I_uSleep(10000); + } while (!I_GetPacket(packet, psize) || (packet->type != PKT_WAD)); + Z_Free(packet); + + if (!strcasecmp((void*)(packet+1), name)) { + pid_t pid; + int rv; + byte *p = (byte*)(packet+1) + strlen(name) + 1; + + /* Automatic wad file retrieval using wget (supports http and ftp, using URLs) + * Unix systems have all these commands handy, this kind of thing is easy + * Any windo$e port will have some awkward work replacing these. + */ + /* cph - caution here. This is data from an untrusted source. + * Don't pass it via a shell. */ + if ((pid = fork()) == -1) + perror("fork"); + else if (!pid) { + /* Child chains to wget, does the download */ + execlp("wget", "wget", p, NULL); + } + /* This is the parent, i.e. main LxDoom process */ + wait(&rv); + if (!(done = !access(name, R_OK))) { + if (!strcmp(p+strlen(p)-4, ".zip")) { + p = strrchr(p, '/')+1; + if ((pid = fork()) == -1) + perror("fork"); + else if (!pid) { + /* Child executes decompressor */ + execlp("unzip", "unzip", p, name, NULL); + } + /* Parent waits for the file */ + wait(&rv); + done = !!access(name, R_OK); + } + /* Add more decompression protocols here as desired */ + } + Z_Free(buffer); + } + return done; +#else /* HAVE_WAIT_H */ + return false; +#endif +} + +void NetUpdate(void) +{ + static int lastmadetic; + if (isExtraDDisplay) + return; + if (server) { // Receive network packets + size_t recvlen; + packet_header_t *packet = Z_Malloc(10000, PU_STATIC, NULL); + while ((recvlen = I_GetPacket(packet, 10000))) { + switch(packet->type) { + case PKT_TICS: + { + byte *p = (void*)(packet+1); + int tics = *p++; + unsigned long ptic = doom_ntohl(packet->tic); + if (ptic > (unsigned)remotetic) { // Missed some + packet_set(packet, PKT_RETRANS, remotetic); + *(byte*)(packet+1) = consoleplayer; + I_SendPacket(packet, sizeof(*packet)+1); + } else { + if (ptic + tics <= (unsigned)remotetic) break; // Will not improve things + remotetic = ptic; + while (tics--) { + int players = *p++; + while (players--) { + int n = *p++; + RawToTic(&netcmds[n][remotetic%BACKUPTICS], p); + p += sizeof(ticcmd_t); + } + remotetic++; + } + } + } + break; + case PKT_RETRANS: // Resend request + remotesend = doom_ntohl(packet->tic); + break; + case PKT_DOWN: // Server downed + { + int j; + for (j=0; j 0 ? newtics : 0); + lastmadetic += newtics; + if (ffmap) newtics++; + while (newtics--) { + I_StartTic(); + if (maketic - gametic > BACKUPTICS/2) break; + G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]); + maketic++; + } + if (server && maketic > remotesend) { // Send the tics to the server + int sendtics; + remotesend -= xtratics; + if (remotesend < 0) remotesend = 0; + sendtics = maketic - remotesend; + { + size_t pkt_size = sizeof(packet_header_t) + 2 + sendtics * sizeof(ticcmd_t); + packet_header_t *packet = Z_Malloc(pkt_size, PU_STATIC, NULL); + + packet_set(packet, PKT_TICC, maketic - sendtics); + *(byte*)(packet+1) = sendtics; + *(((byte*)(packet+1))+1) = consoleplayer; + { + void *tic = ((byte*)(packet+1)) +2; + while (sendtics--) { + TicToRaw(tic, &localcmds[remotesend++%BACKUPTICS]); + tic = (byte *)tic + sizeof(ticcmd_t); + } + } + I_SendPacket(packet, pkt_size); + Z_Free(packet); + } + } + } +} + +/* cph - data passed to this must be in the Doom (little-) endian */ +void D_NetSendMisc(netmisctype_t type, size_t len, void* data) +{ + if (server) { + size_t size = sizeof(packet_header_t) + 3*sizeof(int) + len; + packet_header_t *packet = Z_Malloc(size, PU_STATIC, NULL); + int *p = (void*)(packet+1); + + packet_set(packet, PKT_EXTRA, gametic); + *p++ = LONG(type); *p++ = LONG(consoleplayer); *p++ = LONG(len); + memcpy(p, data, len); + I_SendPacket(packet, size); + + Z_Free(packet); + } +} + +static void CheckQueuedPackets(void) +{ + int i; + for (i=0; (unsigned)itic) <= gametic) + switch (queuedpacket[i]->type) { + case PKT_QUIT: // Player quit the game + { + int pn = *(byte*)(queuedpacket[i]+1); + playeringame[pn] = false; + doom_printf("Player %d left the game\n", pn); + } + break; + case PKT_EXTRA: + { + int *p = (int*)(queuedpacket[i]+1); + size_t len = LONG(*(p+2)); + switch (LONG(*p)) { + case nm_plcolour: + G_ChangedPlayerColour(LONG(*(p+1)), LONG(*(p+3))); + break; + case nm_savegamename: + if (len < SAVEDESCLEN) { + memcpy(savedescription, p+3, len); + // Force terminating 0 in case + savedescription[len] = 0; + } + break; + } + } + break; + default: // Should not be queued + break; + } + + { // Requeue remaining packets + int newnum = 0; + packet_header_t **newqueue = NULL; + + for (i=0; (unsigned)itic) > gametic) { + newqueue = Z_Realloc(newqueue, ++newnum * sizeof *newqueue, + PU_STATIC, NULL); + newqueue[newnum-1] = queuedpacket[i]; + } else Z_Free(queuedpacket[i]); + + Z_Free(queuedpacket); + numqueuedpackets = newnum; queuedpacket = newqueue; + } +} + +void D_QuitNetGame (void) +{ + byte buf[1 + sizeof(packet_header_t)]; + packet_header_t *packet = (void*)buf; + int i; + + if (!server) return; + buf[sizeof(packet_header_t)] = consoleplayer; + packet_set(packet, PKT_QUIT, gametic); + + for (i=0; i<4; i++) { + I_SendPacket(packet, 1 + sizeof(packet_header_t)); + I_uSleep(10000); + } +} + +void D_InitNetGame (void) +{ + int i; + int numplayers = 1; + + i = M_CheckParm("-net"); + if (i && i < myargc-1) i++; + + if (!(netgame = server = !!i)) { + playeringame[consoleplayer = 0] = true; + // e6y + // for play, recording or playback using "single-player coop" mode. + // Equivalent to using prboom_server with -N 1 + netgame = M_CheckParm("-solo-net") || M_CheckParm("-net1"); + } else { + // Get game info from server + packet_header_t *packet = Z_Malloc(1000, PU_STATIC, NULL); + struct setup_packet_s *sinfo = (void*)(packet+1); + struct { packet_header_t head; short pn; } PACKEDATTR initpacket; + + I_InitNetwork(); + udp_socket = I_Socket(0); + I_ConnectToServer(myargv[i]); + + do + { + do { + // Send init packet + initpacket.pn = doom_htons(wanted_player_number); + packet_set(&initpacket.head, PKT_INIT, 0); + I_SendPacket(&initpacket.head, sizeof(initpacket)); + I_WaitForPacket(5000); + } while (!I_GetPacket(packet, 1000)); + if (packet->type == PKT_DOWN) I_Error("Server aborted the game"); + } while (packet->type != PKT_SETUP); + + // Get info from the setup packet + consoleplayer = sinfo->yourplayer; + compatibility_level = sinfo->complevel; + G_Compatibility(); + startskill = sinfo->skill; + deathmatch = sinfo->deathmatch; + startmap = sinfo->level; + startepisode = sinfo->episode; + ticdup = sinfo->ticdup; + xtratics = sinfo->extratic; + G_ReadOptions(sinfo->game_options); + + lprintf(LO_INFO, "\tjoined game as player %d/%d; %d WADs specified\n", + consoleplayer+1, numplayers = sinfo->players, sinfo->numwads); + { + char *p = sinfo->wadnames; + int i = sinfo->numwads; + + while (i--) { + D_AddFile(p, source_net); + p += strlen(p) + 1; + } + } + Z_Free(packet); + } + localcmds = netcmds[displayplayer = consoleplayer]; + for (i=0; i 10) { + if (server) { + char buf[sizeof(packet_header_t)+1]; + remotesend--; + packet_set((packet_header_t *)buf, PKT_RETRANS, remotetic); + buf[sizeof(buf)-1] = consoleplayer; + I_SendPacket((packet_header_t *)buf, sizeof buf); + } + M_Ticker(); return; + } + { + WasRenderedInTryRunTics = true; + if (movement_smooth && gamestate==wipegamestate) + { + isExtraDDisplay = true; + D_Display(); + isExtraDDisplay = false; + } + } + } else break; + } + + while (runtics--) { + if (server) CheckQueuedPackets(); + if (advancedemo) + D_DoAdvanceDemo (); + M_Ticker (); + I_GetTime_SaveMS(); + G_Ticker (); + P_Checksum(gametic); + gametic++; + NetUpdate(); // Keep sending our tics to avoid stalling remote nodes + } +} +#else +doomcom_t* doomcom; + +void D_InitNetGame (void) +{ + int i; + + doomcom = Z_Malloc(sizeof *doomcom, PU_STATIC, NULL); + doomcom->consoleplayer = 0; + doomcom->numnodes = 0; doomcom->numplayers = 1; + localcmds = netcmds[consoleplayer]; + netgame = (M_CheckParm("-solo-net") != 0) || (M_CheckParm("-net1") != 0); + + for (i=0; inumplayers; i++) + playeringame[i] = true; + for (; iconsoleplayer; +} + +void D_BuildNewTiccmds(void) +{ + static int lastmadetic; + int newtics = I_GetTime() - lastmadetic; + lastmadetic += newtics; + while (newtics--) + { + I_StartTic(); + if (maketic - gametic > BACKUPTICS/2) break; + G_BuildTiccmd(&localcmds[maketic%BACKUPTICS]); + maketic++; + } +} + +void TryRunTics (void) +{ + int runtics; + + // Wait for tics to run + while (1) { + D_BuildNewTiccmds(); + runtics = maketic - gametic; + if (runtics) + break; + + WasRenderedInTryRunTics = true; + if (movement_smooth && gamestate==wipegamestate) + { + isExtraDDisplay = true; + D_Display(); + isExtraDDisplay = false; + } + } + + if (advancedemo) + D_DoAdvanceDemo (); + M_Ticker (); + I_GetTime_SaveMS(); + G_Ticker (); + P_Checksum(gametic); + gametic++; +} +#endif diff --git a/src/d_deh.c b/src/d_deh.c new file mode 100644 index 00000000..98702337 --- /dev/null +++ b/src/d_deh.c @@ -0,0 +1,3118 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Dehacked file support + * New for the TeamTNT "Boom" engine + * + * Author: Ty Halderman, TeamTNT + * + *--------------------------------------------------------------------*/ + +// killough 5/2/98: fixed headers, removed rendunant external declarations: +#include "doomdef.h" +#include "doomtype.h" +#include "doomstat.h" +#include "d_deh.h" +#include "sounds.h" +#include "info.h" +#include "m_cheat.h" +#include "p_inter.h" +#include "p_enemy.h" +#include "g_game.h" +#include "d_think.h" +#include "w_wad.h" + +// CPhipps - modify to use logical output routine +#include "lprintf.h" + +#define TRUE 1 +#define FALSE 0 + +#ifndef HAVE_STRLWR +#include + +static char* strlwr(char* str) +{ + char* p; + for (p=str; *p; p++) *p = tolower(*p); + return str; +} +#endif + +// killough 10/98: new functions, to allow processing DEH files in-memory +// (e.g. from wads) + +typedef struct { + /* cph 2006/08/06 - + * if lump != NULL, lump is the start of the lump, + * inp is the current read pos. */ + const byte *inp, *lump; + long size; + /* else, !lump, and f is the file being read */ + FILE* f; +} DEHFILE; + +// killough 10/98: emulate IO whether input really comes from a file or not + +static char *dehfgets(char *buf, size_t n, DEHFILE *fp) +{ + if (!fp->lump) // If this is a real file, + return (fgets)(buf, n, fp->f); // return regular fgets + if (!n || !*fp->inp || fp->size<=0) // If no more characters + return NULL; + if (n==1) + fp->size--, *buf = *fp->inp++; + else + { // copy buffer + char *p = buf; + while (n>1 && *fp->inp && fp->size && + (n--, fp->size--, *p++ = *fp->inp++) != '\n') + ; + *p = 0; + } + return buf; // Return buffer pointer +} + +static int dehfeof(DEHFILE *fp) +{ + return !fp->lump ? feof(fp->f) : !*fp->inp || fp->size<=0; +} + +static int dehfgetc(DEHFILE *fp) +{ + return !fp->lump ? fgetc(fp->f) : fp->size > 0 ? + fp->size--, *fp->inp++ : EOF; +} + +// haleyjd 9/22/99 +int HelperThing = -1; // in P_SpawnMapThing to substitute helper thing + +// variables used in other routines +boolean deh_pars = FALSE; // in wi_stuff to allow pars in modified games + +// #include "d_deh.h" -- we don't do that here but we declare the +// variables. This externalizes everything that there is a string +// set for in the language files. See d_deh.h for detailed comments, +// original English values etc. These are set to the macro values, +// which are set by D_ENGLSH.H or D_FRENCH.H(etc). BEX files are a +// better way of changing these strings globally by language. + +// ==================================================================== +// Any of these can be changed using the bex extensions +#include "dstrings.h" // to get the initial values +/* cph - const's + * - removed redundant "can't XXX in a netgame" strings. + */ +const char *s_D_DEVSTR = D_DEVSTR; +const char *s_D_CDROM = D_CDROM; +const char *s_PRESSKEY = PRESSKEY; +const char *s_PRESSYN = PRESSYN; +const char *s_QUITMSG = QUITMSG; +const char *s_QSAVESPOT = QSAVESPOT; // PRESSKEY; +const char *s_SAVEDEAD = SAVEDEAD; // PRESSKEY; // remove duplicate y/n +const char *s_QSPROMPT = QSPROMPT; // PRESSYN; +const char *s_QLPROMPT = QLPROMPT; // PRESSYN; +const char *s_NEWGAME = NEWGAME; // PRESSKEY; +const char *s_RESTARTLEVEL= RESTARTLEVEL; // PRESSYN; +const char *s_NIGHTMARE = NIGHTMARE; // PRESSYN; +const char *s_SWSTRING = SWSTRING; // PRESSKEY; +const char *s_MSGOFF = MSGOFF; +const char *s_MSGON = MSGON; +const char *s_NETEND = NETEND; // PRESSKEY; +const char *s_ENDGAME = ENDGAME; // PRESSYN; // killough 4/4/98: end +const char *s_DOSY = DOSY; +const char *s_DETAILHI = DETAILHI; +const char *s_DETAILLO = DETAILLO; +const char *s_GAMMALVL0 = GAMMALVL0; +const char *s_GAMMALVL1 = GAMMALVL1; +const char *s_GAMMALVL2 = GAMMALVL2; +const char *s_GAMMALVL3 = GAMMALVL3; +const char *s_GAMMALVL4 = GAMMALVL4; +const char *s_EMPTYSTRING = EMPTYSTRING; +const char *s_GOTARMOR = GOTARMOR; +const char *s_GOTMEGA = GOTMEGA; +const char *s_GOTHTHBONUS = GOTHTHBONUS; +const char *s_GOTARMBONUS = GOTARMBONUS; +const char *s_GOTSTIM = GOTSTIM; +const char *s_GOTMEDINEED = GOTMEDINEED; +const char *s_GOTMEDIKIT = GOTMEDIKIT; +const char *s_GOTSUPER = GOTSUPER; +const char *s_GOTBLUECARD = GOTBLUECARD; +const char *s_GOTYELWCARD = GOTYELWCARD; +const char *s_GOTREDCARD = GOTREDCARD; +const char *s_GOTBLUESKUL = GOTBLUESKUL; +const char *s_GOTYELWSKUL = GOTYELWSKUL; +const char *s_GOTREDSKULL = GOTREDSKULL; +const char *s_GOTINVUL = GOTINVUL; +const char *s_GOTBERSERK = GOTBERSERK; +const char *s_GOTINVIS = GOTINVIS; +const char *s_GOTSUIT = GOTSUIT; +const char *s_GOTMAP = GOTMAP; +const char *s_GOTVISOR = GOTVISOR; +const char *s_GOTMSPHERE = GOTMSPHERE; +const char *s_GOTCLIP = GOTCLIP; +const char *s_GOTCLIPBOX = GOTCLIPBOX; +const char *s_GOTROCKET = GOTROCKET; +const char *s_GOTROCKBOX = GOTROCKBOX; +const char *s_GOTCELL = GOTCELL; +const char *s_GOTCELLBOX = GOTCELLBOX; +const char *s_GOTSHELLS = GOTSHELLS; +const char *s_GOTSHELLBOX = GOTSHELLBOX; +const char *s_GOTBACKPACK = GOTBACKPACK; +const char *s_GOTBFG9000 = GOTBFG9000; +const char *s_GOTCHAINGUN = GOTCHAINGUN; +const char *s_GOTCHAINSAW = GOTCHAINSAW; +const char *s_GOTLAUNCHER = GOTLAUNCHER; +const char *s_GOTPLASMA = GOTPLASMA; +const char *s_GOTSHOTGUN = GOTSHOTGUN; +const char *s_GOTSHOTGUN2 = GOTSHOTGUN2; +const char *s_PD_BLUEO = PD_BLUEO; +const char *s_PD_REDO = PD_REDO; +const char *s_PD_YELLOWO = PD_YELLOWO; +const char *s_PD_BLUEK = PD_BLUEK; +const char *s_PD_REDK = PD_REDK; +const char *s_PD_YELLOWK = PD_YELLOWK; +const char *s_PD_BLUEC = PD_BLUEC; +const char *s_PD_REDC = PD_REDC; +const char *s_PD_YELLOWC = PD_YELLOWC; +const char *s_PD_BLUES = PD_BLUES; +const char *s_PD_REDS = PD_REDS; +const char *s_PD_YELLOWS = PD_YELLOWS; +const char *s_PD_ANY = PD_ANY; +const char *s_PD_ALL3 = PD_ALL3; +const char *s_PD_ALL6 = PD_ALL6; +const char *s_GGSAVED = GGSAVED; +const char *s_HUSTR_MSGU = HUSTR_MSGU; +const char *s_HUSTR_E1M1 = HUSTR_E1M1; +const char *s_HUSTR_E1M2 = HUSTR_E1M2; +const char *s_HUSTR_E1M3 = HUSTR_E1M3; +const char *s_HUSTR_E1M4 = HUSTR_E1M4; +const char *s_HUSTR_E1M5 = HUSTR_E1M5; +const char *s_HUSTR_E1M6 = HUSTR_E1M6; +const char *s_HUSTR_E1M7 = HUSTR_E1M7; +const char *s_HUSTR_E1M8 = HUSTR_E1M8; +const char *s_HUSTR_E1M9 = HUSTR_E1M9; +const char *s_HUSTR_E2M1 = HUSTR_E2M1; +const char *s_HUSTR_E2M2 = HUSTR_E2M2; +const char *s_HUSTR_E2M3 = HUSTR_E2M3; +const char *s_HUSTR_E2M4 = HUSTR_E2M4; +const char *s_HUSTR_E2M5 = HUSTR_E2M5; +const char *s_HUSTR_E2M6 = HUSTR_E2M6; +const char *s_HUSTR_E2M7 = HUSTR_E2M7; +const char *s_HUSTR_E2M8 = HUSTR_E2M8; +const char *s_HUSTR_E2M9 = HUSTR_E2M9; +const char *s_HUSTR_E3M1 = HUSTR_E3M1; +const char *s_HUSTR_E3M2 = HUSTR_E3M2; +const char *s_HUSTR_E3M3 = HUSTR_E3M3; +const char *s_HUSTR_E3M4 = HUSTR_E3M4; +const char *s_HUSTR_E3M5 = HUSTR_E3M5; +const char *s_HUSTR_E3M6 = HUSTR_E3M6; +const char *s_HUSTR_E3M7 = HUSTR_E3M7; +const char *s_HUSTR_E3M8 = HUSTR_E3M8; +const char *s_HUSTR_E3M9 = HUSTR_E3M9; +const char *s_HUSTR_E4M1 = HUSTR_E4M1; +const char *s_HUSTR_E4M2 = HUSTR_E4M2; +const char *s_HUSTR_E4M3 = HUSTR_E4M3; +const char *s_HUSTR_E4M4 = HUSTR_E4M4; +const char *s_HUSTR_E4M5 = HUSTR_E4M5; +const char *s_HUSTR_E4M6 = HUSTR_E4M6; +const char *s_HUSTR_E4M7 = HUSTR_E4M7; +const char *s_HUSTR_E4M8 = HUSTR_E4M8; +const char *s_HUSTR_E4M9 = HUSTR_E4M9; +const char *s_HUSTR_1 = HUSTR_1; +const char *s_HUSTR_2 = HUSTR_2; +const char *s_HUSTR_3 = HUSTR_3; +const char *s_HUSTR_4 = HUSTR_4; +const char *s_HUSTR_5 = HUSTR_5; +const char *s_HUSTR_6 = HUSTR_6; +const char *s_HUSTR_7 = HUSTR_7; +const char *s_HUSTR_8 = HUSTR_8; +const char *s_HUSTR_9 = HUSTR_9; +const char *s_HUSTR_10 = HUSTR_10; +const char *s_HUSTR_11 = HUSTR_11; +const char *s_HUSTR_12 = HUSTR_12; +const char *s_HUSTR_13 = HUSTR_13; +const char *s_HUSTR_14 = HUSTR_14; +const char *s_HUSTR_15 = HUSTR_15; +const char *s_HUSTR_16 = HUSTR_16; +const char *s_HUSTR_17 = HUSTR_17; +const char *s_HUSTR_18 = HUSTR_18; +const char *s_HUSTR_19 = HUSTR_19; +const char *s_HUSTR_20 = HUSTR_20; +const char *s_HUSTR_21 = HUSTR_21; +const char *s_HUSTR_22 = HUSTR_22; +const char *s_HUSTR_23 = HUSTR_23; +const char *s_HUSTR_24 = HUSTR_24; +const char *s_HUSTR_25 = HUSTR_25; +const char *s_HUSTR_26 = HUSTR_26; +const char *s_HUSTR_27 = HUSTR_27; +const char *s_HUSTR_28 = HUSTR_28; +const char *s_HUSTR_29 = HUSTR_29; +const char *s_HUSTR_30 = HUSTR_30; +const char *s_HUSTR_31 = HUSTR_31; +const char *s_HUSTR_32 = HUSTR_32; +const char *s_PHUSTR_1 = PHUSTR_1; +const char *s_PHUSTR_2 = PHUSTR_2; +const char *s_PHUSTR_3 = PHUSTR_3; +const char *s_PHUSTR_4 = PHUSTR_4; +const char *s_PHUSTR_5 = PHUSTR_5; +const char *s_PHUSTR_6 = PHUSTR_6; +const char *s_PHUSTR_7 = PHUSTR_7; +const char *s_PHUSTR_8 = PHUSTR_8; +const char *s_PHUSTR_9 = PHUSTR_9; +const char *s_PHUSTR_10 = PHUSTR_10; +const char *s_PHUSTR_11 = PHUSTR_11; +const char *s_PHUSTR_12 = PHUSTR_12; +const char *s_PHUSTR_13 = PHUSTR_13; +const char *s_PHUSTR_14 = PHUSTR_14; +const char *s_PHUSTR_15 = PHUSTR_15; +const char *s_PHUSTR_16 = PHUSTR_16; +const char *s_PHUSTR_17 = PHUSTR_17; +const char *s_PHUSTR_18 = PHUSTR_18; +const char *s_PHUSTR_19 = PHUSTR_19; +const char *s_PHUSTR_20 = PHUSTR_20; +const char *s_PHUSTR_21 = PHUSTR_21; +const char *s_PHUSTR_22 = PHUSTR_22; +const char *s_PHUSTR_23 = PHUSTR_23; +const char *s_PHUSTR_24 = PHUSTR_24; +const char *s_PHUSTR_25 = PHUSTR_25; +const char *s_PHUSTR_26 = PHUSTR_26; +const char *s_PHUSTR_27 = PHUSTR_27; +const char *s_PHUSTR_28 = PHUSTR_28; +const char *s_PHUSTR_29 = PHUSTR_29; +const char *s_PHUSTR_30 = PHUSTR_30; +const char *s_PHUSTR_31 = PHUSTR_31; +const char *s_PHUSTR_32 = PHUSTR_32; +const char *s_THUSTR_1 = THUSTR_1; +const char *s_THUSTR_2 = THUSTR_2; +const char *s_THUSTR_3 = THUSTR_3; +const char *s_THUSTR_4 = THUSTR_4; +const char *s_THUSTR_5 = THUSTR_5; +const char *s_THUSTR_6 = THUSTR_6; +const char *s_THUSTR_7 = THUSTR_7; +const char *s_THUSTR_8 = THUSTR_8; +const char *s_THUSTR_9 = THUSTR_9; +const char *s_THUSTR_10 = THUSTR_10; +const char *s_THUSTR_11 = THUSTR_11; +const char *s_THUSTR_12 = THUSTR_12; +const char *s_THUSTR_13 = THUSTR_13; +const char *s_THUSTR_14 = THUSTR_14; +const char *s_THUSTR_15 = THUSTR_15; +const char *s_THUSTR_16 = THUSTR_16; +const char *s_THUSTR_17 = THUSTR_17; +const char *s_THUSTR_18 = THUSTR_18; +const char *s_THUSTR_19 = THUSTR_19; +const char *s_THUSTR_20 = THUSTR_20; +const char *s_THUSTR_21 = THUSTR_21; +const char *s_THUSTR_22 = THUSTR_22; +const char *s_THUSTR_23 = THUSTR_23; +const char *s_THUSTR_24 = THUSTR_24; +const char *s_THUSTR_25 = THUSTR_25; +const char *s_THUSTR_26 = THUSTR_26; +const char *s_THUSTR_27 = THUSTR_27; +const char *s_THUSTR_28 = THUSTR_28; +const char *s_THUSTR_29 = THUSTR_29; +const char *s_THUSTR_30 = THUSTR_30; +const char *s_THUSTR_31 = THUSTR_31; +const char *s_THUSTR_32 = THUSTR_32; +const char *s_HUSTR_CHATMACRO1 = HUSTR_CHATMACRO1; +const char *s_HUSTR_CHATMACRO2 = HUSTR_CHATMACRO2; +const char *s_HUSTR_CHATMACRO3 = HUSTR_CHATMACRO3; +const char *s_HUSTR_CHATMACRO4 = HUSTR_CHATMACRO4; +const char *s_HUSTR_CHATMACRO5 = HUSTR_CHATMACRO5; +const char *s_HUSTR_CHATMACRO6 = HUSTR_CHATMACRO6; +const char *s_HUSTR_CHATMACRO7 = HUSTR_CHATMACRO7; +const char *s_HUSTR_CHATMACRO8 = HUSTR_CHATMACRO8; +const char *s_HUSTR_CHATMACRO9 = HUSTR_CHATMACRO9; +const char *s_HUSTR_CHATMACRO0 = HUSTR_CHATMACRO0; +const char *s_HUSTR_TALKTOSELF1 = HUSTR_TALKTOSELF1; +const char *s_HUSTR_TALKTOSELF2 = HUSTR_TALKTOSELF2; +const char *s_HUSTR_TALKTOSELF3 = HUSTR_TALKTOSELF3; +const char *s_HUSTR_TALKTOSELF4 = HUSTR_TALKTOSELF4; +const char *s_HUSTR_TALKTOSELF5 = HUSTR_TALKTOSELF5; +const char *s_HUSTR_MESSAGESENT = HUSTR_MESSAGESENT; +const char *s_HUSTR_PLRGREEN = HUSTR_PLRGREEN; +const char *s_HUSTR_PLRINDIGO = HUSTR_PLRINDIGO; +const char *s_HUSTR_PLRBROWN = HUSTR_PLRBROWN; +const char *s_HUSTR_PLRRED = HUSTR_PLRRED; +const char *s_AMSTR_FOLLOWON = AMSTR_FOLLOWON; +const char *s_AMSTR_FOLLOWOFF = AMSTR_FOLLOWOFF; +const char *s_AMSTR_GRIDON = AMSTR_GRIDON; +const char *s_AMSTR_GRIDOFF = AMSTR_GRIDOFF; +const char *s_AMSTR_MARKEDSPOT = AMSTR_MARKEDSPOT; +const char *s_AMSTR_MARKSCLEARED = AMSTR_MARKSCLEARED; +// CPhipps - automap rotate & overlay +const char* s_AMSTR_ROTATEON = AMSTR_ROTATEON; +const char* s_AMSTR_ROTATEOFF = AMSTR_ROTATEOFF; +const char* s_AMSTR_OVERLAYON = AMSTR_OVERLAYON; +const char* s_AMSTR_OVERLAYOFF = AMSTR_OVERLAYOFF; +const char *s_STSTR_MUS = STSTR_MUS; +const char *s_STSTR_NOMUS = STSTR_NOMUS; +const char *s_STSTR_DQDON = STSTR_DQDON; +const char *s_STSTR_DQDOFF = STSTR_DQDOFF; +const char *s_STSTR_KFAADDED = STSTR_KFAADDED; +const char *s_STSTR_FAADDED = STSTR_FAADDED; +const char *s_STSTR_NCON = STSTR_NCON; +const char *s_STSTR_NCOFF = STSTR_NCOFF; +const char *s_STSTR_BEHOLD = STSTR_BEHOLD; +const char *s_STSTR_BEHOLDX = STSTR_BEHOLDX; +const char *s_STSTR_CHOPPERS = STSTR_CHOPPERS; +const char *s_STSTR_CLEV = STSTR_CLEV; +const char *s_STSTR_COMPON = STSTR_COMPON; +const char *s_STSTR_COMPOFF = STSTR_COMPOFF; +const char *s_E1TEXT = E1TEXT; +const char *s_E2TEXT = E2TEXT; +const char *s_E3TEXT = E3TEXT; +const char *s_E4TEXT = E4TEXT; +const char *s_C1TEXT = C1TEXT; +const char *s_C2TEXT = C2TEXT; +const char *s_C3TEXT = C3TEXT; +const char *s_C4TEXT = C4TEXT; +const char *s_C5TEXT = C5TEXT; +const char *s_C6TEXT = C6TEXT; +const char *s_P1TEXT = P1TEXT; +const char *s_P2TEXT = P2TEXT; +const char *s_P3TEXT = P3TEXT; +const char *s_P4TEXT = P4TEXT; +const char *s_P5TEXT = P5TEXT; +const char *s_P6TEXT = P6TEXT; +const char *s_T1TEXT = T1TEXT; +const char *s_T2TEXT = T2TEXT; +const char *s_T3TEXT = T3TEXT; +const char *s_T4TEXT = T4TEXT; +const char *s_T5TEXT = T5TEXT; +const char *s_T6TEXT = T6TEXT; +const char *s_CC_ZOMBIE = CC_ZOMBIE; +const char *s_CC_SHOTGUN = CC_SHOTGUN; +const char *s_CC_HEAVY = CC_HEAVY; +const char *s_CC_IMP = CC_IMP; +const char *s_CC_DEMON = CC_DEMON; +const char *s_CC_LOST = CC_LOST; +const char *s_CC_CACO = CC_CACO; +const char *s_CC_HELL = CC_HELL; +const char *s_CC_BARON = CC_BARON; +const char *s_CC_ARACH = CC_ARACH; +const char *s_CC_PAIN = CC_PAIN; +const char *s_CC_REVEN = CC_REVEN; +const char *s_CC_MANCU = CC_MANCU; +const char *s_CC_ARCH = CC_ARCH; +const char *s_CC_SPIDER = CC_SPIDER; +const char *s_CC_CYBER = CC_CYBER; +const char *s_CC_HERO = CC_HERO; +// Ty 03/30/98 - new substitutions for background textures +// during int screens +const char *bgflatE1 = "FLOOR4_8"; // end of DOOM Episode 1 +const char *bgflatE2 = "SFLR6_1"; // end of DOOM Episode 2 +const char *bgflatE3 = "MFLR8_4"; // end of DOOM Episode 3 +const char *bgflatE4 = "MFLR8_3"; // end of DOOM Episode 4 +const char *bgflat06 = "SLIME16"; // DOOM2 after MAP06 +const char *bgflat11 = "RROCK14"; // DOOM2 after MAP11 +const char *bgflat20 = "RROCK07"; // DOOM2 after MAP20 +const char *bgflat30 = "RROCK17"; // DOOM2 after MAP30 +const char *bgflat15 = "RROCK13"; // DOOM2 going MAP15 to MAP31 +const char *bgflat31 = "RROCK19"; // DOOM2 going MAP31 to MAP32 +const char *bgcastcall = "BOSSBACK"; // Panel behind cast call + +const char *startup1 = ""; // blank lines are default and are not printed +const char *startup2 = ""; +const char *startup3 = ""; +const char *startup4 = ""; +const char *startup5 = ""; + +/* Ty 05/03/98 - externalized + * cph - updated for prboom */ +const char *savegamename = "prbmsav"; + +// end d_deh.h variable declarations +// ==================================================================== + +// Do this for a lookup--the pointer (loaded above) is cross-referenced +// to a string key that is the same as the define above. We will use +// strdups to set these new values that we read from the file, orphaning +// the original value set above. + +// CPhipps - make strings pointed to const +typedef struct { + const char **ppstr; // doubly indirect pointer to string + const char *lookup; // pointer to lookup string name + const char *orig; // if modified, this is the original, unmodified string +} deh_strs; + +/* CPhipps - const, static + * - removed redundant "Can't XXX in a netgame" strings + */ +static deh_strs deh_strlookup[] = { // not const any more, because of orig. + {&s_D_DEVSTR,"D_DEVSTR"}, + {&s_D_CDROM,"D_CDROM"}, + {&s_PRESSKEY,"PRESSKEY"}, + {&s_PRESSYN,"PRESSYN"}, + {&s_QUITMSG,"QUITMSG"}, + {&s_QSAVESPOT,"QSAVESPOT"}, + {&s_SAVEDEAD,"SAVEDEAD"}, + /* cph - disabled to prevent format string attacks in WAD files + {&s_QSPROMPT,"QSPROMPT"}, + {&s_QLPROMPT,"QLPROMPT"},*/ + {&s_NEWGAME,"NEWGAME"}, + {&s_RESTARTLEVEL,"RESTARTLEVEL"}, + {&s_NIGHTMARE,"NIGHTMARE"}, + {&s_SWSTRING,"SWSTRING"}, + {&s_MSGOFF,"MSGOFF"}, + {&s_MSGON,"MSGON"}, + {&s_NETEND,"NETEND"}, + {&s_ENDGAME,"ENDGAME"}, + {&s_DOSY,"DOSY"}, + {&s_DETAILHI,"DETAILHI"}, + {&s_DETAILLO,"DETAILLO"}, + {&s_GAMMALVL0,"GAMMALVL0"}, + {&s_GAMMALVL1,"GAMMALVL1"}, + {&s_GAMMALVL2,"GAMMALVL2"}, + {&s_GAMMALVL3,"GAMMALVL3"}, + {&s_GAMMALVL4,"GAMMALVL4"}, + {&s_EMPTYSTRING,"EMPTYSTRING"}, + {&s_GOTARMOR,"GOTARMOR"}, + {&s_GOTMEGA,"GOTMEGA"}, + {&s_GOTHTHBONUS,"GOTHTHBONUS"}, + {&s_GOTARMBONUS,"GOTARMBONUS"}, + {&s_GOTSTIM,"GOTSTIM"}, + {&s_GOTMEDINEED,"GOTMEDINEED"}, + {&s_GOTMEDIKIT,"GOTMEDIKIT"}, + {&s_GOTSUPER,"GOTSUPER"}, + {&s_GOTBLUECARD,"GOTBLUECARD"}, + {&s_GOTYELWCARD,"GOTYELWCARD"}, + {&s_GOTREDCARD,"GOTREDCARD"}, + {&s_GOTBLUESKUL,"GOTBLUESKUL"}, + {&s_GOTYELWSKUL,"GOTYELWSKUL"}, + {&s_GOTREDSKULL,"GOTREDSKULL"}, + {&s_GOTINVUL,"GOTINVUL"}, + {&s_GOTBERSERK,"GOTBERSERK"}, + {&s_GOTINVIS,"GOTINVIS"}, + {&s_GOTSUIT,"GOTSUIT"}, + {&s_GOTMAP,"GOTMAP"}, + {&s_GOTVISOR,"GOTVISOR"}, + {&s_GOTMSPHERE,"GOTMSPHERE"}, + {&s_GOTCLIP,"GOTCLIP"}, + {&s_GOTCLIPBOX,"GOTCLIPBOX"}, + {&s_GOTROCKET,"GOTROCKET"}, + {&s_GOTROCKBOX,"GOTROCKBOX"}, + {&s_GOTCELL,"GOTCELL"}, + {&s_GOTCELLBOX,"GOTCELLBOX"}, + {&s_GOTSHELLS,"GOTSHELLS"}, + {&s_GOTSHELLBOX,"GOTSHELLBOX"}, + {&s_GOTBACKPACK,"GOTBACKPACK"}, + {&s_GOTBFG9000,"GOTBFG9000"}, + {&s_GOTCHAINGUN,"GOTCHAINGUN"}, + {&s_GOTCHAINSAW,"GOTCHAINSAW"}, + {&s_GOTLAUNCHER,"GOTLAUNCHER"}, + {&s_GOTPLASMA,"GOTPLASMA"}, + {&s_GOTSHOTGUN,"GOTSHOTGUN"}, + {&s_GOTSHOTGUN2,"GOTSHOTGUN2"}, + {&s_PD_BLUEO,"PD_BLUEO"}, + {&s_PD_REDO,"PD_REDO"}, + {&s_PD_YELLOWO,"PD_YELLOWO"}, + {&s_PD_BLUEK,"PD_BLUEK"}, + {&s_PD_REDK,"PD_REDK"}, + {&s_PD_YELLOWK,"PD_YELLOWK"}, + {&s_PD_BLUEC,"PD_BLUEC"}, + {&s_PD_REDC,"PD_REDC"}, + {&s_PD_YELLOWC,"PD_YELLOWC"}, + {&s_PD_BLUES,"PD_BLUES"}, + {&s_PD_REDS,"PD_REDS"}, + {&s_PD_YELLOWS,"PD_YELLOWS"}, + {&s_PD_ANY,"PD_ANY"}, + {&s_PD_ALL3,"PD_ALL3"}, + {&s_PD_ALL6,"PD_ALL6"}, + {&s_GGSAVED,"GGSAVED"}, + {&s_HUSTR_MSGU,"HUSTR_MSGU"}, + {&s_HUSTR_E1M1,"HUSTR_E1M1"}, + {&s_HUSTR_E1M2,"HUSTR_E1M2"}, + {&s_HUSTR_E1M3,"HUSTR_E1M3"}, + {&s_HUSTR_E1M4,"HUSTR_E1M4"}, + {&s_HUSTR_E1M5,"HUSTR_E1M5"}, + {&s_HUSTR_E1M6,"HUSTR_E1M6"}, + {&s_HUSTR_E1M7,"HUSTR_E1M7"}, + {&s_HUSTR_E1M8,"HUSTR_E1M8"}, + {&s_HUSTR_E1M9,"HUSTR_E1M9"}, + {&s_HUSTR_E2M1,"HUSTR_E2M1"}, + {&s_HUSTR_E2M2,"HUSTR_E2M2"}, + {&s_HUSTR_E2M3,"HUSTR_E2M3"}, + {&s_HUSTR_E2M4,"HUSTR_E2M4"}, + {&s_HUSTR_E2M5,"HUSTR_E2M5"}, + {&s_HUSTR_E2M6,"HUSTR_E2M6"}, + {&s_HUSTR_E2M7,"HUSTR_E2M7"}, + {&s_HUSTR_E2M8,"HUSTR_E2M8"}, + {&s_HUSTR_E2M9,"HUSTR_E2M9"}, + {&s_HUSTR_E3M1,"HUSTR_E3M1"}, + {&s_HUSTR_E3M2,"HUSTR_E3M2"}, + {&s_HUSTR_E3M3,"HUSTR_E3M3"}, + {&s_HUSTR_E3M4,"HUSTR_E3M4"}, + {&s_HUSTR_E3M5,"HUSTR_E3M5"}, + {&s_HUSTR_E3M6,"HUSTR_E3M6"}, + {&s_HUSTR_E3M7,"HUSTR_E3M7"}, + {&s_HUSTR_E3M8,"HUSTR_E3M8"}, + {&s_HUSTR_E3M9,"HUSTR_E3M9"}, + {&s_HUSTR_E4M1,"HUSTR_E4M1"}, + {&s_HUSTR_E4M2,"HUSTR_E4M2"}, + {&s_HUSTR_E4M3,"HUSTR_E4M3"}, + {&s_HUSTR_E4M4,"HUSTR_E4M4"}, + {&s_HUSTR_E4M5,"HUSTR_E4M5"}, + {&s_HUSTR_E4M6,"HUSTR_E4M6"}, + {&s_HUSTR_E4M7,"HUSTR_E4M7"}, + {&s_HUSTR_E4M8,"HUSTR_E4M8"}, + {&s_HUSTR_E4M9,"HUSTR_E4M9"}, + {&s_HUSTR_1,"HUSTR_1"}, + {&s_HUSTR_2,"HUSTR_2"}, + {&s_HUSTR_3,"HUSTR_3"}, + {&s_HUSTR_4,"HUSTR_4"}, + {&s_HUSTR_5,"HUSTR_5"}, + {&s_HUSTR_6,"HUSTR_6"}, + {&s_HUSTR_7,"HUSTR_7"}, + {&s_HUSTR_8,"HUSTR_8"}, + {&s_HUSTR_9,"HUSTR_9"}, + {&s_HUSTR_10,"HUSTR_10"}, + {&s_HUSTR_11,"HUSTR_11"}, + {&s_HUSTR_12,"HUSTR_12"}, + {&s_HUSTR_13,"HUSTR_13"}, + {&s_HUSTR_14,"HUSTR_14"}, + {&s_HUSTR_15,"HUSTR_15"}, + {&s_HUSTR_16,"HUSTR_16"}, + {&s_HUSTR_17,"HUSTR_17"}, + {&s_HUSTR_18,"HUSTR_18"}, + {&s_HUSTR_19,"HUSTR_19"}, + {&s_HUSTR_20,"HUSTR_20"}, + {&s_HUSTR_21,"HUSTR_21"}, + {&s_HUSTR_22,"HUSTR_22"}, + {&s_HUSTR_23,"HUSTR_23"}, + {&s_HUSTR_24,"HUSTR_24"}, + {&s_HUSTR_25,"HUSTR_25"}, + {&s_HUSTR_26,"HUSTR_26"}, + {&s_HUSTR_27,"HUSTR_27"}, + {&s_HUSTR_28,"HUSTR_28"}, + {&s_HUSTR_29,"HUSTR_29"}, + {&s_HUSTR_30,"HUSTR_30"}, + {&s_HUSTR_31,"HUSTR_31"}, + {&s_HUSTR_32,"HUSTR_32"}, + {&s_PHUSTR_1,"PHUSTR_1"}, + {&s_PHUSTR_2,"PHUSTR_2"}, + {&s_PHUSTR_3,"PHUSTR_3"}, + {&s_PHUSTR_4,"PHUSTR_4"}, + {&s_PHUSTR_5,"PHUSTR_5"}, + {&s_PHUSTR_6,"PHUSTR_6"}, + {&s_PHUSTR_7,"PHUSTR_7"}, + {&s_PHUSTR_8,"PHUSTR_8"}, + {&s_PHUSTR_9,"PHUSTR_9"}, + {&s_PHUSTR_10,"PHUSTR_10"}, + {&s_PHUSTR_11,"PHUSTR_11"}, + {&s_PHUSTR_12,"PHUSTR_12"}, + {&s_PHUSTR_13,"PHUSTR_13"}, + {&s_PHUSTR_14,"PHUSTR_14"}, + {&s_PHUSTR_15,"PHUSTR_15"}, + {&s_PHUSTR_16,"PHUSTR_16"}, + {&s_PHUSTR_17,"PHUSTR_17"}, + {&s_PHUSTR_18,"PHUSTR_18"}, + {&s_PHUSTR_19,"PHUSTR_19"}, + {&s_PHUSTR_20,"PHUSTR_20"}, + {&s_PHUSTR_21,"PHUSTR_21"}, + {&s_PHUSTR_22,"PHUSTR_22"}, + {&s_PHUSTR_23,"PHUSTR_23"}, + {&s_PHUSTR_24,"PHUSTR_24"}, + {&s_PHUSTR_25,"PHUSTR_25"}, + {&s_PHUSTR_26,"PHUSTR_26"}, + {&s_PHUSTR_27,"PHUSTR_27"}, + {&s_PHUSTR_28,"PHUSTR_28"}, + {&s_PHUSTR_29,"PHUSTR_29"}, + {&s_PHUSTR_30,"PHUSTR_30"}, + {&s_PHUSTR_31,"PHUSTR_31"}, + {&s_PHUSTR_32,"PHUSTR_32"}, + {&s_THUSTR_1,"THUSTR_1"}, + {&s_THUSTR_2,"THUSTR_2"}, + {&s_THUSTR_3,"THUSTR_3"}, + {&s_THUSTR_4,"THUSTR_4"}, + {&s_THUSTR_5,"THUSTR_5"}, + {&s_THUSTR_6,"THUSTR_6"}, + {&s_THUSTR_7,"THUSTR_7"}, + {&s_THUSTR_8,"THUSTR_8"}, + {&s_THUSTR_9,"THUSTR_9"}, + {&s_THUSTR_10,"THUSTR_10"}, + {&s_THUSTR_11,"THUSTR_11"}, + {&s_THUSTR_12,"THUSTR_12"}, + {&s_THUSTR_13,"THUSTR_13"}, + {&s_THUSTR_14,"THUSTR_14"}, + {&s_THUSTR_15,"THUSTR_15"}, + {&s_THUSTR_16,"THUSTR_16"}, + {&s_THUSTR_17,"THUSTR_17"}, + {&s_THUSTR_18,"THUSTR_18"}, + {&s_THUSTR_19,"THUSTR_19"}, + {&s_THUSTR_20,"THUSTR_20"}, + {&s_THUSTR_21,"THUSTR_21"}, + {&s_THUSTR_22,"THUSTR_22"}, + {&s_THUSTR_23,"THUSTR_23"}, + {&s_THUSTR_24,"THUSTR_24"}, + {&s_THUSTR_25,"THUSTR_25"}, + {&s_THUSTR_26,"THUSTR_26"}, + {&s_THUSTR_27,"THUSTR_27"}, + {&s_THUSTR_28,"THUSTR_28"}, + {&s_THUSTR_29,"THUSTR_29"}, + {&s_THUSTR_30,"THUSTR_30"}, + {&s_THUSTR_31,"THUSTR_31"}, + {&s_THUSTR_32,"THUSTR_32"}, + {&s_HUSTR_CHATMACRO1,"HUSTR_CHATMACRO1"}, + {&s_HUSTR_CHATMACRO2,"HUSTR_CHATMACRO2"}, + {&s_HUSTR_CHATMACRO3,"HUSTR_CHATMACRO3"}, + {&s_HUSTR_CHATMACRO4,"HUSTR_CHATMACRO4"}, + {&s_HUSTR_CHATMACRO5,"HUSTR_CHATMACRO5"}, + {&s_HUSTR_CHATMACRO6,"HUSTR_CHATMACRO6"}, + {&s_HUSTR_CHATMACRO7,"HUSTR_CHATMACRO7"}, + {&s_HUSTR_CHATMACRO8,"HUSTR_CHATMACRO8"}, + {&s_HUSTR_CHATMACRO9,"HUSTR_CHATMACRO9"}, + {&s_HUSTR_CHATMACRO0,"HUSTR_CHATMACRO0"}, + {&s_HUSTR_TALKTOSELF1,"HUSTR_TALKTOSELF1"}, + {&s_HUSTR_TALKTOSELF2,"HUSTR_TALKTOSELF2"}, + {&s_HUSTR_TALKTOSELF3,"HUSTR_TALKTOSELF3"}, + {&s_HUSTR_TALKTOSELF4,"HUSTR_TALKTOSELF4"}, + {&s_HUSTR_TALKTOSELF5,"HUSTR_TALKTOSELF5"}, + {&s_HUSTR_MESSAGESENT,"HUSTR_MESSAGESENT"}, + {&s_HUSTR_PLRGREEN,"HUSTR_PLRGREEN"}, + {&s_HUSTR_PLRINDIGO,"HUSTR_PLRINDIGO"}, + {&s_HUSTR_PLRBROWN,"HUSTR_PLRBROWN"}, + {&s_HUSTR_PLRRED,"HUSTR_PLRRED"}, + //{c_HUSTR_KEYGREEN,"HUSTR_KEYGREEN"}, + //{c_HUSTR_KEYINDIGO,"HUSTR_KEYINDIGO"}, + //{c_HUSTR_KEYBROWN,"HUSTR_KEYBROWN"}, + //{c_HUSTR_KEYRED,"HUSTR_KEYRED"}, + {&s_AMSTR_FOLLOWON,"AMSTR_FOLLOWON"}, + {&s_AMSTR_FOLLOWOFF,"AMSTR_FOLLOWOFF"}, + {&s_AMSTR_GRIDON,"AMSTR_GRIDON"}, + {&s_AMSTR_GRIDOFF,"AMSTR_GRIDOFF"}, + {&s_AMSTR_MARKEDSPOT,"AMSTR_MARKEDSPOT"}, + {&s_AMSTR_MARKSCLEARED,"AMSTR_MARKSCLEARED"}, + {&s_STSTR_MUS,"STSTR_MUS"}, + {&s_STSTR_NOMUS,"STSTR_NOMUS"}, + {&s_STSTR_DQDON,"STSTR_DQDON"}, + {&s_STSTR_DQDOFF,"STSTR_DQDOFF"}, + {&s_STSTR_KFAADDED,"STSTR_KFAADDED"}, + {&s_STSTR_FAADDED,"STSTR_FAADDED"}, + {&s_STSTR_NCON,"STSTR_NCON"}, + {&s_STSTR_NCOFF,"STSTR_NCOFF"}, + {&s_STSTR_BEHOLD,"STSTR_BEHOLD"}, + {&s_STSTR_BEHOLDX,"STSTR_BEHOLDX"}, + {&s_STSTR_CHOPPERS,"STSTR_CHOPPERS"}, + {&s_STSTR_CLEV,"STSTR_CLEV"}, + {&s_STSTR_COMPON,"STSTR_COMPON"}, + {&s_STSTR_COMPOFF,"STSTR_COMPOFF"}, + {&s_E1TEXT,"E1TEXT"}, + {&s_E2TEXT,"E2TEXT"}, + {&s_E3TEXT,"E3TEXT"}, + {&s_E4TEXT,"E4TEXT"}, + {&s_C1TEXT,"C1TEXT"}, + {&s_C2TEXT,"C2TEXT"}, + {&s_C3TEXT,"C3TEXT"}, + {&s_C4TEXT,"C4TEXT"}, + {&s_C5TEXT,"C5TEXT"}, + {&s_C6TEXT,"C6TEXT"}, + {&s_P1TEXT,"P1TEXT"}, + {&s_P2TEXT,"P2TEXT"}, + {&s_P3TEXT,"P3TEXT"}, + {&s_P4TEXT,"P4TEXT"}, + {&s_P5TEXT,"P5TEXT"}, + {&s_P6TEXT,"P6TEXT"}, + {&s_T1TEXT,"T1TEXT"}, + {&s_T2TEXT,"T2TEXT"}, + {&s_T3TEXT,"T3TEXT"}, + {&s_T4TEXT,"T4TEXT"}, + {&s_T5TEXT,"T5TEXT"}, + {&s_T6TEXT,"T6TEXT"}, + {&s_CC_ZOMBIE,"CC_ZOMBIE"}, + {&s_CC_SHOTGUN,"CC_SHOTGUN"}, + {&s_CC_HEAVY,"CC_HEAVY"}, + {&s_CC_IMP,"CC_IMP"}, + {&s_CC_DEMON,"CC_DEMON"}, + {&s_CC_LOST,"CC_LOST"}, + {&s_CC_CACO,"CC_CACO"}, + {&s_CC_HELL,"CC_HELL"}, + {&s_CC_BARON,"CC_BARON"}, + {&s_CC_ARACH,"CC_ARACH"}, + {&s_CC_PAIN,"CC_PAIN"}, + {&s_CC_REVEN,"CC_REVEN"}, + {&s_CC_MANCU,"CC_MANCU"}, + {&s_CC_ARCH,"CC_ARCH"}, + {&s_CC_SPIDER,"CC_SPIDER"}, + {&s_CC_CYBER,"CC_CYBER"}, + {&s_CC_HERO,"CC_HERO"}, + {&bgflatE1,"BGFLATE1"}, + {&bgflatE2,"BGFLATE2"}, + {&bgflatE3,"BGFLATE3"}, + {&bgflatE4,"BGFLATE4"}, + {&bgflat06,"BGFLAT06"}, + {&bgflat11,"BGFLAT11"}, + {&bgflat20,"BGFLAT20"}, + {&bgflat30,"BGFLAT30"}, + {&bgflat15,"BGFLAT15"}, + {&bgflat31,"BGFLAT31"}, + {&bgcastcall,"BGCASTCALL"}, + // Ty 04/08/98 - added 5 general purpose startup announcement + // strings for hacker use. See m_menu.c + {&startup1,"STARTUP1"}, + {&startup2,"STARTUP2"}, + {&startup3,"STARTUP3"}, + {&startup4,"STARTUP4"}, + {&startup5,"STARTUP5"}, + {&savegamename,"SAVEGAMENAME"}, // Ty 05/03/98 +}; + +static int deh_numstrlookup = +sizeof(deh_strlookup)/sizeof(deh_strlookup[0]); + +const char *deh_newlevel = "NEWLEVEL"; // CPhipps - const + +// DOOM shareware/registered/retail (Ultimate) names. +// CPhipps - const**const +const char **const mapnames[] = +{ + &s_HUSTR_E1M1, + &s_HUSTR_E1M2, + &s_HUSTR_E1M3, + &s_HUSTR_E1M4, + &s_HUSTR_E1M5, + &s_HUSTR_E1M6, + &s_HUSTR_E1M7, + &s_HUSTR_E1M8, + &s_HUSTR_E1M9, + + &s_HUSTR_E2M1, + &s_HUSTR_E2M2, + &s_HUSTR_E2M3, + &s_HUSTR_E2M4, + &s_HUSTR_E2M5, + &s_HUSTR_E2M6, + &s_HUSTR_E2M7, + &s_HUSTR_E2M8, + &s_HUSTR_E2M9, + + &s_HUSTR_E3M1, + &s_HUSTR_E3M2, + &s_HUSTR_E3M3, + &s_HUSTR_E3M4, + &s_HUSTR_E3M5, + &s_HUSTR_E3M6, + &s_HUSTR_E3M7, + &s_HUSTR_E3M8, + &s_HUSTR_E3M9, + + &s_HUSTR_E4M1, + &s_HUSTR_E4M2, + &s_HUSTR_E4M3, + &s_HUSTR_E4M4, + &s_HUSTR_E4M5, + &s_HUSTR_E4M6, + &s_HUSTR_E4M7, + &s_HUSTR_E4M8, + &s_HUSTR_E4M9, + + &deh_newlevel, // spares? Unused. + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel, + &deh_newlevel +}; + +// CPhipps - const**const +const char **const mapnames2[] = // DOOM 2 map names. +{ + &s_HUSTR_1, + &s_HUSTR_2, + &s_HUSTR_3, + &s_HUSTR_4, + &s_HUSTR_5, + &s_HUSTR_6, + &s_HUSTR_7, + &s_HUSTR_8, + &s_HUSTR_9, + &s_HUSTR_10, + &s_HUSTR_11, + + &s_HUSTR_12, + &s_HUSTR_13, + &s_HUSTR_14, + &s_HUSTR_15, + &s_HUSTR_16, + &s_HUSTR_17, + &s_HUSTR_18, + &s_HUSTR_19, + &s_HUSTR_20, + + &s_HUSTR_21, + &s_HUSTR_22, + &s_HUSTR_23, + &s_HUSTR_24, + &s_HUSTR_25, + &s_HUSTR_26, + &s_HUSTR_27, + &s_HUSTR_28, + &s_HUSTR_29, + &s_HUSTR_30, + &s_HUSTR_31, + &s_HUSTR_32, +}; + +// CPhipps - const**const +const char **const mapnamesp[] = // Plutonia WAD map names. +{ + &s_PHUSTR_1, + &s_PHUSTR_2, + &s_PHUSTR_3, + &s_PHUSTR_4, + &s_PHUSTR_5, + &s_PHUSTR_6, + &s_PHUSTR_7, + &s_PHUSTR_8, + &s_PHUSTR_9, + &s_PHUSTR_10, + &s_PHUSTR_11, + + &s_PHUSTR_12, + &s_PHUSTR_13, + &s_PHUSTR_14, + &s_PHUSTR_15, + &s_PHUSTR_16, + &s_PHUSTR_17, + &s_PHUSTR_18, + &s_PHUSTR_19, + &s_PHUSTR_20, + + &s_PHUSTR_21, + &s_PHUSTR_22, + &s_PHUSTR_23, + &s_PHUSTR_24, + &s_PHUSTR_25, + &s_PHUSTR_26, + &s_PHUSTR_27, + &s_PHUSTR_28, + &s_PHUSTR_29, + &s_PHUSTR_30, + &s_PHUSTR_31, + &s_PHUSTR_32, +}; + +// CPhipps - const**const +const char **const mapnamest[] = // TNT WAD map names. +{ + &s_THUSTR_1, + &s_THUSTR_2, + &s_THUSTR_3, + &s_THUSTR_4, + &s_THUSTR_5, + &s_THUSTR_6, + &s_THUSTR_7, + &s_THUSTR_8, + &s_THUSTR_9, + &s_THUSTR_10, + &s_THUSTR_11, + + &s_THUSTR_12, + &s_THUSTR_13, + &s_THUSTR_14, + &s_THUSTR_15, + &s_THUSTR_16, + &s_THUSTR_17, + &s_THUSTR_18, + &s_THUSTR_19, + &s_THUSTR_20, + + &s_THUSTR_21, + &s_THUSTR_22, + &s_THUSTR_23, + &s_THUSTR_24, + &s_THUSTR_25, + &s_THUSTR_26, + &s_THUSTR_27, + &s_THUSTR_28, + &s_THUSTR_29, + &s_THUSTR_30, + &s_THUSTR_31, + &s_THUSTR_32, +}; + +// Function prototypes +void lfstrip(char *); // strip the \r and/or \n off of a line +void rstrip(char *); // strip trailing whitespace +char * ptr_lstrip(char *); // point past leading whitespace +boolean deh_GetData(char *, char *, uint_64_t *, char **, FILE *); +boolean deh_procStringSub(char *, char *, char *, FILE *); +char * dehReformatStr(char *); + +// Prototypes for block processing functions +// Pointers to these functions are used as the blocks are encountered. + +static void deh_procThing(DEHFILE *fpin, FILE* fpout, char *line); +static void deh_procFrame(DEHFILE *, FILE*, char *); +static void deh_procPointer(DEHFILE *, FILE*, char *); +static void deh_procSounds(DEHFILE *, FILE*, char *); +static void deh_procAmmo(DEHFILE *, FILE*, char *); +static void deh_procWeapon(DEHFILE *, FILE*, char *); +static void deh_procSprite(DEHFILE *, FILE*, char *); +static void deh_procCheat(DEHFILE *, FILE*, char *); +static void deh_procMisc(DEHFILE *, FILE*, char *); +static void deh_procText(DEHFILE *, FILE*, char *); +static void deh_procPars(DEHFILE *, FILE*, char *); +static void deh_procStrings(DEHFILE *, FILE*, char *); +static void deh_procError(DEHFILE *, FILE*, char *); +static void deh_procBexCodePointers(DEHFILE *, FILE*, char *); +static void deh_procHelperThing(DEHFILE *, FILE *, char *); // haleyjd 9/22/99 +// haleyjd: handlers to fully deprecate the DeHackEd text section +static void deh_procBexSounds(DEHFILE *, FILE *, char *); +static void deh_procBexMusic(DEHFILE *, FILE *, char *); +static void deh_procBexSprites(DEHFILE *, FILE *, char *); + +// Structure deh_block is used to hold the block names that can +// be encountered, and the routines to use to decipher them + +typedef struct +{ + const char *key; // a mnemonic block code name // CPhipps - const* + void (*const fptr)(DEHFILE *, FILE*, char *); // handler +} deh_block; + +#define DEH_BUFFERMAX 1024 // input buffer area size, hardcodedfor now +// killough 8/9/98: make DEH_BLOCKMAX self-adjusting +#define DEH_BLOCKMAX (sizeof deh_blocks/sizeof*deh_blocks) // size of array +#define DEH_MAXKEYLEN 32 // as much of any key as we'll look at +#define DEH_MOBJINFOMAX 24 // number of ints in the mobjinfo_t structure (!) + +// Put all the block header values, and the function to be called when that +// one is encountered, in this array: +static const deh_block deh_blocks[] = { // CPhipps - static const + /* 0 */ {"Thing",deh_procThing}, + /* 1 */ {"Frame",deh_procFrame}, + /* 2 */ {"Pointer",deh_procPointer}, + /* 3 */ {"Sound",deh_procSounds}, // Ty 03/16/98 corrected from "Sounds" + /* 4 */ {"Ammo",deh_procAmmo}, + /* 5 */ {"Weapon",deh_procWeapon}, + /* 6 */ {"Sprite",deh_procSprite}, + /* 7 */ {"Cheat",deh_procCheat}, + /* 8 */ {"Misc",deh_procMisc}, + /* 9 */ {"Text",deh_procText}, // -- end of standard "deh" entries, + + // begin BOOM Extensions (BEX) + + /* 10 */ {"[STRINGS]",deh_procStrings}, // new string changes + /* 11 */ {"[PARS]",deh_procPars}, // alternative block marker + /* 12 */ {"[CODEPTR]",deh_procBexCodePointers}, // bex codepointers by mnemonic + /* 13 */ {"[HELPER]", deh_procHelperThing}, // helper thing substitution haleyjd 9/22/99 + /* 14 */ {"[SPRITES]", deh_procBexSprites}, // bex style sprites + /* 15 */ {"[SOUNDS]", deh_procBexSounds}, // bex style sounds + /* 16 */ {"[MUSIC]", deh_procBexMusic}, // bex style music + /* 17 */ {"", deh_procError} // dummy to handle anything else +}; + +// flag to skip included deh-style text, used with INCLUDE NOTEXT directive +static boolean includenotext = false; + +// MOBJINFO - Dehacked block name = "Thing" +// Usage: Thing nn (name) +// These are for mobjinfo_t types. Each is an integer +// within the structure, so we can use index of the string in this +// array to offset by sizeof(int) into the mobjinfo_t array at [nn] +// * things are base zero but dehacked considers them to start at #1. *** +// CPhipps - static const + +static const char *deh_mobjinfo[DEH_MOBJINFOMAX] = +{ + "ID #", // .doomednum + "Initial frame", // .spawnstate + "Hit points", // .spawnhealth + "First moving frame", // .seestate + "Alert sound", // .seesound + "Reaction time", // .reactiontime + "Attack sound", // .attacksound + "Injury frame", // .painstate + "Pain chance", // .painchance + "Pain sound", // .painsound + "Close attack frame", // .meleestate + "Far attack frame", // .missilestate + "Death frame", // .deathstate + "Exploding frame", // .xdeathstate + "Death sound", // .deathsound + "Speed", // .speed + "Width", // .radius + "Height", // .height + "Mass", // .mass + "Missile damage", // .damage + "Action sound", // .activesound + "Bits", // .flags + "Bits2", // .flags + "Respawn frame" // .raisestate +}; + +// Strings that are used to indicate flags ("Bits" in mobjinfo) +// This is an array of bit masks that are related to p_mobj.h +// values, using the smae names without the MF_ in front. +// Ty 08/27/98 new code +// +// killough 10/98: +// +// Convert array to struct to allow multiple values, make array size variable + +#define DEH_MOBJFLAGMAX (sizeof deh_mobjflags/sizeof*deh_mobjflags) + +struct deh_mobjflags_s { + const char *name; // CPhipps - const* + uint_64_t value; +}; + +// CPhipps - static const +static const struct deh_mobjflags_s deh_mobjflags[] = { + {"SPECIAL", MF_SPECIAL}, // call P_Specialthing when touched + {"SOLID", MF_SOLID}, // block movement + {"SHOOTABLE", MF_SHOOTABLE}, // can be hit + {"NOSECTOR", MF_NOSECTOR}, // invisible but touchable + {"NOBLOCKMAP", MF_NOBLOCKMAP}, // inert but displayable + {"AMBUSH", MF_AMBUSH}, // deaf monster + {"JUSTHIT", MF_JUSTHIT}, // will try to attack right back + {"JUSTATTACKED", MF_JUSTATTACKED}, // take at least 1 step before attacking + {"SPAWNCEILING", MF_SPAWNCEILING}, // initially hang from ceiling + {"NOGRAVITY", MF_NOGRAVITY}, // don't apply gravity during play + {"DROPOFF", MF_DROPOFF}, // can jump from high places + {"PICKUP", MF_PICKUP}, // will pick up items + {"NOCLIP", MF_NOCLIP}, // goes through walls + {"SLIDE", MF_SLIDE}, // keep info about sliding along walls + {"FLOAT", MF_FLOAT}, // allow movement to any height + {"TELEPORT", MF_TELEPORT}, // don't cross lines or look at heights + {"MISSILE", MF_MISSILE}, // don't hit same species, explode on block + {"DROPPED", MF_DROPPED}, // dropped, not spawned (like ammo clip) + {"SHADOW", MF_SHADOW}, // use fuzzy draw like spectres + {"NOBLOOD", MF_NOBLOOD}, // puffs instead of blood when shot + {"CORPSE", MF_CORPSE}, // so it will slide down steps when dead + {"INFLOAT", MF_INFLOAT}, // float but not to target height + {"COUNTKILL", MF_COUNTKILL}, // count toward the kills total + {"COUNTITEM", MF_COUNTITEM}, // count toward the items total + {"SKULLFLY", MF_SKULLFLY}, // special handling for flying skulls + {"NOTDMATCH", MF_NOTDMATCH}, // do not spawn in deathmatch + + // killough 10/98: TRANSLATION consists of 2 bits, not 1: + + {"TRANSLATION", MF_TRANSLATION1}, // for Boom bug-compatibility + {"TRANSLATION1", MF_TRANSLATION1}, // use translation table for color (players) + {"TRANSLATION2", MF_TRANSLATION2}, // use translation table for color (players) + {"UNUSED1", MF_TRANSLATION2}, // unused bit # 1 -- For Boom bug-compatibility + {"UNUSED2", MF_UNUSED2}, // unused bit # 2 -- For Boom compatibility + {"UNUSED3", MF_UNUSED3}, // unused bit # 3 -- For Boom compatibility + {"UNUSED4", MF_TRANSLUCENT}, // unused bit # 4 -- For Boom compatibility + {"TRANSLUCENT", MF_TRANSLUCENT}, // apply translucency to sprite (BOOM) + {"TOUCHY", MF_TOUCHY}, // dies on contact with solid objects (MBF) + {"BOUNCES", MF_BOUNCES}, // bounces off floors, ceilings and maybe walls (MBF) + {"FRIEND", MF_FRIEND}, // a friend of the player(s) (MBF) +}; + +// STATE - Dehacked block name = "Frame" and "Pointer" +// Usage: Frame nn +// Usage: Pointer nn (Frame nn) +// These are indexed separately, for lookup to the actual +// function pointers. Here we'll take whatever Dehacked gives +// us and go from there. The (Frame nn) after the pointer is the +// real place to put this value. The "Pointer" value is an xref +// that Dehacked uses and is useless to us. +// * states are base zero and have a dummy #0 (TROO) + +static const char *deh_state[] = // CPhipps - static const* +{ + "Sprite number", // .sprite (spritenum_t) // an enum + "Sprite subnumber", // .frame (long) + "Duration", // .tics (long) + "Next frame", // .nextstate (statenum_t) + // This is set in a separate "Pointer" block from Dehacked + "Codep Frame", // pointer to first use of action (actionf_t) + "Unknown 1", // .misc1 (long) + "Unknown 2" // .misc2 (long) +}; + +// SFXINFO_STRUCT - Dehacked block name = "Sounds" +// Sound effects, typically not changed (redirected, and new sfx put +// into the pwad, but not changed here. Can you tell that Gregdidn't +// know what they were for, mostly? Can you tell that I don't either? +// Mostly I just put these into the same slots as they are in the struct. +// This may not be supported in our -deh option if it doesn't make sense by then. + +// * sounds are base zero but have a dummy #0 + +static const char *deh_sfxinfo[] = // CPhipps - static const* +{ + "Offset", // pointer to a name string, changed in text + "Zero/One", // .singularity (int, one at a time flag) + "Value", // .priority + "Zero 1", // .link (sfxinfo_t*) referenced sound if linked + "Zero 2", // .pitch + "Zero 3", // .volume + "Zero 4", // .data (SAMPLE*) sound data + "Neg. One 1", // .usefulness + "Neg. One 2" // .lumpnum +}; + +// MUSICINFO is not supported in Dehacked. Ignored here. +// * music entries are base zero but have a dummy #0 + +// SPRITE - Dehacked block name = "Sprite" +// Usage = Sprite nn +// Sprite redirection by offset into the text area - unsupported by BOOM +// * sprites are base zero and dehacked uses it that way. + +// static const char *deh_sprite[] = // CPhipps - static const* +// { +// "Offset" // supposed to be the offset into the text section +// }; + +// AMMO - Dehacked block name = "Ammo" +// usage = Ammo n (name) +// Ammo information for the few types of ammo + +static const char *deh_ammo[] = // CPhipps - static const* +{ + "Max ammo", // maxammo[] + "Per ammo" // clipammo[] +}; + +// WEAPONS - Dehacked block name = "Weapon" +// Usage: Weapon nn (name) +// Basically a list of frames and what kind of ammo (see above)it uses. + +static const char *deh_weapon[] = // CPhipps - static const* +{ + "Ammo type", // .ammo + "Deselect frame", // .upstate + "Select frame", // .downstate + "Bobbing frame", // .readystate + "Shooting frame", // .atkstate + "Firing frame" // .flashstate +}; + +// CHEATS - Dehacked block name = "Cheat" +// Usage: Cheat 0 +// Always uses a zero in the dehacked file, for consistency. No meaning. +// These are just plain funky terms compared with id's +// +// killough 4/18/98: integrated into main cheat table now (see st_stuff.c) + +// MISC - Dehacked block name = "Misc" +// Usage: Misc 0 +// Always uses a zero in the dehacked file, for consistency. No meaning. + +static const char *deh_misc[] = // CPhipps - static const* +{ + "Initial Health", // initial_health + "Initial Bullets", // initial_bullets + "Max Health", // maxhealth + "Max Armor", // max_armor + "Green Armor Class", // green_armor_class + "Blue Armor Class", // blue_armor_class + "Max Soulsphere", // max_soul + "Soulsphere Health", // soul_health + "Megasphere Health", // mega_health + "God Mode Health", // god_health + "IDFA Armor", // idfa_armor + "IDFA Armor Class", // idfa_armor_class + "IDKFA Armor", // idkfa_armor + "IDKFA Armor Class", // idkfa_armor_class + "BFG Cells/Shot", // BFGCELLS + "Monsters Infight" // Unknown--not a specific number it seems, but + // the logic has to be here somewhere or + // it'd happen always +}; + +// TEXT - Dehacked block name = "Text" +// Usage: Text fromlen tolen +// Dehacked allows a bit of adjustment to the length (why?) + +// BEX extension [CODEPTR] +// Usage: Start block, then each line is: +// FRAME nnn = PointerMnemonic + +typedef struct { + actionf_t cptr; // actual pointer to the subroutine + const char *lookup; // mnemonic lookup string to be specified in BEX + // CPhipps - const* +} deh_bexptr; + +static const deh_bexptr deh_bexptrs[] = // CPhipps - static const +{ + {A_Light0, "A_Light0"}, + {A_WeaponReady, "A_WeaponReady"}, + {A_Lower, "A_Lower"}, + {A_Raise, "A_Raise"}, + {A_Punch, "A_Punch"}, + {A_ReFire, "A_ReFire"}, + {A_FirePistol, "A_FirePistol"}, + {A_Light1, "A_Light1"}, + {A_FireShotgun, "A_FireShotgun"}, + {A_Light2, "A_Light2"}, + {A_FireShotgun2, "A_FireShotgun2"}, + {A_CheckReload, "A_CheckReload"}, + {A_OpenShotgun2, "A_OpenShotgun2"}, + {A_LoadShotgun2, "A_LoadShotgun2"}, + {A_CloseShotgun2, "A_CloseShotgun2"}, + {A_FireCGun, "A_FireCGun"}, + {A_GunFlash, "A_GunFlash"}, + {A_FireMissile, "A_FireMissile"}, + {A_Saw, "A_Saw"}, + {A_FirePlasma, "A_FirePlasma"}, + {A_BFGsound, "A_BFGsound"}, + {A_FireBFG, "A_FireBFG"}, + {A_BFGSpray, "A_BFGSpray"}, + {A_Explode, "A_Explode"}, + {A_Pain, "A_Pain"}, + {A_PlayerScream, "A_PlayerScream"}, + {A_Fall, "A_Fall"}, + {A_XScream, "A_XScream"}, + {A_Look, "A_Look"}, + {A_Chase, "A_Chase"}, + {A_FaceTarget, "A_FaceTarget"}, + {A_PosAttack, "A_PosAttack"}, + {A_Scream, "A_Scream"}, + {A_SPosAttack, "A_SPosAttack"}, + {A_VileChase, "A_VileChase"}, + {A_VileStart, "A_VileStart"}, + {A_VileTarget, "A_VileTarget"}, + {A_VileAttack, "A_VileAttack"}, + {A_StartFire, "A_StartFire"}, + {A_Fire, "A_Fire"}, + {A_FireCrackle, "A_FireCrackle"}, + {A_Tracer, "A_Tracer"}, + {A_SkelWhoosh, "A_SkelWhoosh"}, + {A_SkelFist, "A_SkelFist"}, + {A_SkelMissile, "A_SkelMissile"}, + {A_FatRaise, "A_FatRaise"}, + {A_FatAttack1, "A_FatAttack1"}, + {A_FatAttack2, "A_FatAttack2"}, + {A_FatAttack3, "A_FatAttack3"}, + {A_BossDeath, "A_BossDeath"}, + {A_CPosAttack, "A_CPosAttack"}, + {A_CPosRefire, "A_CPosRefire"}, + {A_TroopAttack, "A_TroopAttack"}, + {A_SargAttack, "A_SargAttack"}, + {A_HeadAttack, "A_HeadAttack"}, + {A_BruisAttack, "A_BruisAttack"}, + {A_SkullAttack, "A_SkullAttack"}, + {A_Metal, "A_Metal"}, + {A_SpidRefire, "A_SpidRefire"}, + {A_BabyMetal, "A_BabyMetal"}, + {A_BspiAttack, "A_BspiAttack"}, + {A_Hoof, "A_Hoof"}, + {A_CyberAttack, "A_CyberAttack"}, + {A_PainAttack, "A_PainAttack"}, + {A_PainDie, "A_PainDie"}, + {A_KeenDie, "A_KeenDie"}, + {A_BrainPain, "A_BrainPain"}, + {A_BrainScream, "A_BrainScream"}, + {A_BrainDie, "A_BrainDie"}, + {A_BrainAwake, "A_BrainAwake"}, + {A_BrainSpit, "A_BrainSpit"}, + {A_SpawnSound, "A_SpawnSound"}, + {A_SpawnFly, "A_SpawnFly"}, + {A_BrainExplode, "A_BrainExplode"}, + {A_Detonate, "A_Detonate"}, // killough 8/9/98 + {A_Mushroom, "A_Mushroom"}, // killough 10/98 + {A_Die, "A_Die"}, // killough 11/98 + {A_Spawn, "A_Spawn"}, // killough 11/98 + {A_Turn, "A_Turn"}, // killough 11/98 + {A_Face, "A_Face"}, // killough 11/98 + {A_Scratch, "A_Scratch"}, // killough 11/98 + {A_PlaySound, "A_PlaySound"}, // killough 11/98 + {A_RandomJump, "A_RandomJump"}, // killough 11/98 + {A_LineEffect, "A_LineEffect"}, // killough 11/98 + + // This NULL entry must be the last in the list + {NULL, "A_NULL"}, // Ty 05/16/98 +}; + +// to hold startup code pointers from INFO.C +// CPhipps - static +static actionf_t deh_codeptr[NUMSTATES]; + +// haleyjd: support for BEX SPRITES, SOUNDS, and MUSIC +char *deh_spritenames[NUMSPRITES + 1]; +char *deh_musicnames[NUMMUSIC + 1]; +char *deh_soundnames[NUMSFX + 1]; + +void D_BuildBEXTables(void) +{ + int i; + + // moved from ProcessDehFile, then we don't need the static int i + for (i = 0; i < NUMSTATES; i++) // remember what they start as for deh xref + deh_codeptr[i] = states[i].action; + + for(i = 0; i < NUMSPRITES; i++) + deh_spritenames[i] = strdup(sprnames[i]); + deh_spritenames[NUMSPRITES] = NULL; + + for(i = 1; i < NUMMUSIC; i++) + deh_musicnames[i] = strdup(S_music[i].name); + deh_musicnames[0] = deh_musicnames[NUMMUSIC] = NULL; + + for(i = 1; i < NUMSFX; i++) + deh_soundnames[i] = strdup(S_sfx[i].name); + deh_soundnames[0] = deh_soundnames[NUMSFX] = NULL; +} + +// ==================================================================== +// ProcessDehFile +// Purpose: Read and process a DEH or BEX file +// Args: filename -- name of the DEH/BEX file +// outfilename -- output file (DEHOUT.TXT), appended to here +// Returns: void +// +// killough 10/98: +// substantially modified to allow input from wad lumps instead of .deh files. + +void ProcessDehFile(const char *filename, const char *outfilename, int lumpnum) +{ + static FILE *fileout; // In case -dehout was used + DEHFILE infile, *filein = &infile; // killough 10/98 + char inbuffer[DEH_BUFFERMAX]; // Place to put the primary infostring + const char *file_or_lump; + + // Open output file if we're writing output + if (outfilename && *outfilename && !fileout) + { + static boolean firstfile = true; // to allow append to output log + if (!strcmp(outfilename, "-")) + fileout = stdout; + else + if (!(fileout=fopen(outfilename, firstfile ? "wt" : "at"))) + { + lprintf(LO_WARN, "Could not open -dehout file %s\n... using stdout.\n", + outfilename); + fileout = stdout; + } + firstfile = false; + } + + // killough 10/98: allow DEH files to come from wad lumps + + if (filename) + { + if (!(infile.f = fopen(filename,"rt"))) + { + lprintf(LO_WARN, "-deh file %s not found\n",filename); + return; // should be checked up front anyway + } + infile.lump = NULL; + file_or_lump = "file"; + } + else // DEH file comes from lump indicated by third argument + { + infile.size = W_LumpLength(lumpnum); + infile.inp = infile.lump = W_CacheLumpNum(lumpnum); + filename = lumpinfo[lumpnum].wadfile->name; + file_or_lump = "lump from"; + } + + lprintf(LO_INFO, "Loading DEH %s %s\n",file_or_lump,filename); + if (fileout) fprintf(fileout,"\nLoading DEH %s %s\n\n",file_or_lump,filename); + + // move deh_codeptr initialisation to D_BuildBEXTables + + // loop until end of file + + while (dehfgets(inbuffer,sizeof(inbuffer),filein)) + { + unsigned i; + + lfstrip(inbuffer); + if (fileout) fprintf(fileout,"Line='%s'\n",inbuffer); + if (!*inbuffer || *inbuffer == '#' || *inbuffer == ' ') + continue; /* Blank line or comment line */ + + // -- If DEH_BLOCKMAX is set right, the processing is independently + // -- handled based on data in the deh_blocks[] structure array + + // killough 10/98: INCLUDE code rewritten to allow arbitrary nesting, + // and to greatly simplify code, fix memory leaks, other bugs + + if (!strnicmp(inbuffer,"INCLUDE",7)) // include a file + { + // preserve state while including a file + // killough 10/98: moved to here + + char *nextfile; + boolean oldnotext = includenotext; // killough 10/98 + + // killough 10/98: exclude if inside wads (only to discourage + // the practice, since the code could otherwise handle it) + + if (infile.lump) + { + if (fileout) + fprintf(fileout, + "No files may be included from wads: %s\n",inbuffer); + continue; + } + + // check for no-text directive, used when including a DEH + // file but using the BEX format to handle strings + + if (!strnicmp(nextfile = ptr_lstrip(inbuffer+7),"NOTEXT",6)) + includenotext = true, nextfile = ptr_lstrip(nextfile+6); + + if (fileout) + fprintf(fileout,"Branching to include file %s...\n", nextfile); + + // killough 10/98: + // Second argument must be NULL to prevent closing fileout too soon + + ProcessDehFile(nextfile,NULL,0); // do the included file + + includenotext = oldnotext; + if (fileout) fprintf(fileout,"...continuing with %s\n",filename); + continue; + } + + for (i=0; i= NUMSTATES) + { + if (fpout) fprintf(fpout,"Bad pointer number %d of %d\n", + indexnum, NUMSTATES); + return; // killough 10/98: fix SegViol + } + strcpy(key,"A_"); // reusing the key area to prefix the mnemonic + strcat(key,ptr_lstrip(mnemonic)); + + found = FALSE; + i= -1; // incremented to start at zero at the top of the loop + do // Ty 05/16/98 - fix loop logic to look for null ending entry + { + ++i; + if (!stricmp(key,deh_bexptrs[i].lookup)) + { // Ty 06/01/98 - add to states[].action for new djgcc version + states[indexnum].action = deh_bexptrs[i].cptr; // assign + if (fpout) fprintf(fpout, + " - applied %s from codeptr[%d] to states[%d]\n", + deh_bexptrs[i].lookup,i,indexnum); + found = TRUE; + } + } while (!found && (deh_bexptrs[i].cptr != NULL)); + + if (!found) + if (fpout) fprintf(fpout, + "Invalid frame pointer mnemonic '%s' at %d\n", + mnemonic, indexnum); + } + return; +} + +//--------------------------------------------------------------------------- +// To be on the safe, compatible side, we manually convert DEH bitflags +// to prboom types - POPE +//--------------------------------------------------------------------------- +static uint_64_t getConvertedDEHBits(uint_64_t bits) { + static const uint_64_t bitMap[32] = { + /* cf linuxdoom-1.10 p_mobj.h */ + MF_SPECIAL, // 0 Can be picked up - When touched the thing can be picked up. + MF_SOLID, // 1 Obstacle - The thing is solid and will not let you (or others) pass through it + MF_SHOOTABLE, // 2 Shootable - Can be shot. + MF_NOSECTOR, // 3 Total Invisibility - Invisible, but can be touched + MF_NOBLOCKMAP, // 4 Don't use the blocklinks (inert but displayable) + MF_AMBUSH, // 5 Semi deaf - The thing is a deaf monster + MF_JUSTHIT, // 6 In pain - Will try to attack right back after being hit + MF_JUSTATTACKED, // 7 Steps before attack - Will take at least one step before attacking + MF_SPAWNCEILING, // 8 Hangs from ceiling - When the level starts, this thing will be at ceiling height. + MF_NOGRAVITY, // 9 No gravity - Gravity does not affect this thing + MF_DROPOFF, // 10 Travels over cliffs - Monsters normally do not walk off ledges/steps they could not walk up. With this set they can walk off any height of cliff. Usually only used for flying monsters. + MF_PICKUP, // 11 Pick up items - The thing can pick up gettable items. + MF_NOCLIP, // 12 No clipping - Thing can walk through walls. + MF_SLIDE, // 13 Slides along walls - Keep info about sliding along walls (don't really know much about this one). + MF_FLOAT, // 14 Floating - Thing can move to any height + MF_TELEPORT, // 15 Semi no clipping - Don't cross lines or look at teleport heights. (don't really know much about this one either). + MF_MISSILE, // 16 Projectiles - Behaves like a projectile, explodes when hitting something that blocks movement + MF_DROPPED, // 17 Disappearing weapon - Dropped, not spawned (like an ammo clip) I have not had much success in using this one. + MF_SHADOW, // 18 Partial invisibility - Drawn like a spectre. + MF_NOBLOOD, // 19 Puffs (vs. bleeds) - If hit will spawn bullet puffs instead of blood splats. + MF_CORPSE, // 20 Sliding helpless - Will slide down steps when dead. + MF_INFLOAT, // 21 No auto levelling - float but not to target height (?) + MF_COUNTKILL, // 22 Affects kill % - counted as a killable enemy and affects percentage kills on level summary. + MF_COUNTITEM, // 23 Affects item % - affects percentage items gathered on level summary. + MF_SKULLFLY, // 24 Running - special handling for flying skulls. + MF_NOTDMATCH, // 25 Not in deathmatch - do not spawn in deathmatch (like keys) + MF_TRANSLATION1, // 26 Color 1 (grey / red) + MF_TRANSLATION2, // 27 Color 2 (brown / red) + // Convert bit 28 to MF_TOUCHY, not (MF_TRANSLATION1|MF_TRANSLATION2) + // fixes bug #1576151 (part 1) + MF_TOUCHY, // 28 - explodes on contact (MBF) + MF_BOUNCES, // 29 - bounces off walls and floors (MBF) + MF_FRIEND, // 30 - friendly monster helps players (MBF) + MF_TRANSLUCENT // e6y: Translucency via dehacked/bex doesn't work without it + }; + int i; + uint_64_t shiftBits = bits; + uint_64_t convertedBits = 0; + for (i=0; i<32; i++) { + if (shiftBits & 0x1) convertedBits |= bitMap[i]; + shiftBits >>= 1; + } + return convertedBits; +} + +//--------------------------------------------------------------------------- +// See usage below for an explanation of this function's existence - POPE +//--------------------------------------------------------------------------- +static void setMobjInfoValue(int mobjInfoIndex, int keyIndex, uint_64_t value) { + mobjinfo_t *mi; + if (mobjInfoIndex >= NUMMOBJTYPES || mobjInfoIndex < 0) return; + mi = &mobjinfo[mobjInfoIndex]; + switch (keyIndex) { + case 0: mi->doomednum = (int)value; return; + case 1: mi->spawnstate = (int)value; return; + case 2: mi->spawnhealth = (int)value; return; + case 3: mi->seestate = (int)value; return; + case 4: mi->seesound = (int)value; return; + case 5: mi->reactiontime = (int)value; return; + case 6: mi->attacksound = (int)value; return; + case 7: mi->painstate = (int)value; return; + case 8: mi->painchance = (int)value; return; + case 9: mi->painsound = (int)value; return; + case 10: mi->meleestate = (int)value; return; + case 11: mi->missilestate = (int)value; return; + case 12: mi->deathstate = (int)value; return; + case 13: mi->xdeathstate = (int)value; return; + case 14: mi->deathsound = (int)value; return; + case 15: mi->speed = (int)value; return; + case 16: mi->radius = (int)value; return; + case 17: mi->height = (int)value; return; + case 18: mi->mass = (int)value; return; + case 19: mi->damage = (int)value; return; + case 20: mi->activesound = (int)value; return; + case 21: mi->flags = value; return; + case 22: return; // "Bits2", unused + case 23: mi->raisestate = (int)value; return; + default: return; + } +} + +// ==================================================================== +// deh_procThing +// Purpose: Handle DEH Thing block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +// Ty 8/27/98 - revised to also allow mnemonics for +// bit masks for monster attributes +// + +static void deh_procThing(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + int ix; + char *strval; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + if (fpout) fprintf(fpout,"Thing line: '%s'\n",inbuffer); + + // killough 8/98: allow hex numbers in input: + ix = sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"count=%d, Thing %d\n",ix, indexnum); + + // Note that the mobjinfo[] array is base zero, but object numbers + // in the dehacked file start with one. Grumble. + --indexnum; + + // now process the stuff + // Note that for Things we can look up the key and use its offset + // in the array of key strings as an int offset in the structure + + // get a line until a blank or end of file--it's not + // blank now because it has our incoming key in it + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + int bGetData; + + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); // toss the end of line + + // killough 11/98: really bail out on blank lines (break != continue) + if (!*inbuffer) break; // bail out with blank line between sections + + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + bGetData = deh_GetData(inbuffer,key,&value,&strval,fpout); + if (!bGetData) + // Old code: if (!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + for (ix=0; ix>32) & 0xffffffff, + (unsigned long)deh_mobjflags[iy].value & 0xffffffff, strval + ); + } + value |= deh_mobjflags[iy].value; + break; + } + if (iy >= DEH_MOBJFLAGMAX && fpout) { + fprintf(fpout, "Could not find bit mnemonic %s\n", strval); + } + } + + // Don't worry about conversion -- simply print values + if (fpout) { + fprintf(fpout, + "Bits = 0x%08lX%08lX\n", + (unsigned long)(value>>32) & 0xffffffff, + (unsigned long)value & 0xffffffff + ); + } + mobjinfo[indexnum].flags = value; // e6y + } + } + if (fpout) { + fprintf(fpout, + "Assigned 0x%08lx%08lx to %s(%d) at index %d\n", + (unsigned long)(value>>32) & 0xffffffff, + (unsigned long)value & 0xffffffff, key, indexnum, ix + ); + } + } + } + return; +} + +// ==================================================================== +// deh_procFrame +// Purpose: Handle DEH Frame block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procFrame(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Frame at index %d: %s\n",indexnum,key); + if (indexnum < 0 || indexnum >= NUMSTATES) + if (fpout) fprintf(fpout,"Bad frame number %d of %d\n",indexnum, NUMSTATES); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!strcasecmp(key,deh_state[0])) // Sprite number + { + if (fpout) fprintf(fpout," - sprite = %lld\n",value); + states[indexnum].sprite = (spritenum_t)value; + } + else + if (!strcasecmp(key,deh_state[1])) // Sprite subnumber + { + if (fpout) fprintf(fpout," - frame = %lld\n",value); + states[indexnum].frame = (long)value; // long + } + else + if (!strcasecmp(key,deh_state[2])) // Duration + { + if (fpout) fprintf(fpout," - tics = %lld\n",value); + states[indexnum].tics = (long)value; // long + } + else + if (!strcasecmp(key,deh_state[3])) // Next frame + { + if (fpout) fprintf(fpout," - nextstate = %lld\n",value); + states[indexnum].nextstate = (statenum_t)value; + } + else + if (!strcasecmp(key,deh_state[4])) // Codep frame (not set in Frame deh block) + { + if (fpout) fprintf(fpout," - codep, should not be set in Frame section!\n"); + /* nop */ ; + } + else + if (!strcasecmp(key,deh_state[5])) // Unknown 1 + { + if (fpout) fprintf(fpout," - misc1 = %lld\n",value); + states[indexnum].misc1 = (long)value; // long + } + else + if (!strcasecmp(key,deh_state[6])) // Unknown 2 + { + if (fpout) fprintf(fpout," - misc2 = %lld\n",value); + states[indexnum].misc2 = (long)value; // long + } + else + if (fpout) fprintf(fpout,"Invalid frame string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procPointer +// Purpose: Handle DEH Code pointer block, can use BEX [CODEPTR] instead +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procPointer(DEHFILE *fpin, FILE* fpout, char *line) // done +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + int i; // looper + + strncpy(inbuffer,line,DEH_BUFFERMAX); + // NOTE: different format from normal + + // killough 8/98: allow hex numbers in input, fix error case: + if (sscanf(inbuffer,"%*s %*i (%s %i)",key, &indexnum) != 2) + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + return; + } + + if (fpout) fprintf(fpout,"Processing Pointer at index %d: %s\n",indexnum, key); + if (indexnum < 0 || indexnum >= NUMSTATES) + { + if (fpout) + fprintf(fpout,"Bad pointer number %d of %d\n",indexnum, NUMSTATES); + return; + } + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + + if (value >= NUMSTATES) + { + if (fpout) + fprintf(fpout,"Bad pointer number %lld of %d\n",value, NUMSTATES); + return; + } + + if (!strcasecmp(key,deh_state[4])) // Codep frame (not set in Frame deh block) + { + states[indexnum].action = deh_codeptr[value]; + if (fpout) fprintf(fpout," - applied from codeptr[%lld] to states[%d]\n", + value,indexnum); + // Write BEX-oriented line to match: + // for (i=0;i FRAME %d = %s\n", + indexnum, &deh_bexptrs[i].lookup[2]); + break; + } + if (deh_bexptrs[i].cptr == NULL) // stop at null entry + break; + } + } + else + if (fpout) fprintf(fpout,"Invalid frame pointer index for '%s' at %lld\n", + key, value); + } + return; +} + +// ==================================================================== +// deh_procSounds +// Purpose: Handle DEH Sounds block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procSounds(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Sounds at index %d: %s\n", + indexnum, key); + if (indexnum < 0 || indexnum >= NUMSFX) + if (fpout) fprintf(fpout,"Bad sound number %d of %d\n", + indexnum, NUMSFX); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!strcasecmp(key,deh_sfxinfo[0])) // Offset + /* nop */ ; // we don't know what this is, I don't think + else + if (!strcasecmp(key,deh_sfxinfo[1])) // Zero/One + S_sfx[indexnum].singularity = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[2])) // Value + S_sfx[indexnum].priority = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[3])) // Zero 1 + ; // .link - don't set pointers from DeHackEd + else + if (!strcasecmp(key,deh_sfxinfo[4])) // Zero 2 + S_sfx[indexnum].pitch = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[5])) // Zero 3 + S_sfx[indexnum].volume = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[6])) // Zero 4 + ; // .data - don't set pointers from DeHackEd + else + if (!strcasecmp(key,deh_sfxinfo[7])) // Neg. One 1 + S_sfx[indexnum].usefulness = (int)value; + else + if (!strcasecmp(key,deh_sfxinfo[8])) // Neg. One 2 + S_sfx[indexnum].lumpnum = (int)value; + else + if (fpout) fprintf(fpout, + "Invalid sound string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procAmmo +// Purpose: Handle DEH Ammo block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procAmmo(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Ammo at index %d: %s\n", + indexnum, key); + if (indexnum < 0 || indexnum >= NUMAMMO) + if (fpout) fprintf(fpout,"Bad ammo number %d of %d\n", + indexnum,NUMAMMO); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!strcasecmp(key,deh_ammo[0])) // Max ammo + maxammo[indexnum] = (int)value; + else + if (!strcasecmp(key,deh_ammo[1])) // Per ammo + clipammo[indexnum] = (int)value; + else + if (fpout) fprintf(fpout,"Invalid ammo string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procWeapon +// Purpose: Handle DEH Weapon block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procWeapon(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + int indexnum; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout,"Processing Weapon at index %d: %s\n", + indexnum, key); + if (indexnum < 0 || indexnum >= NUMWEAPONS) + if (fpout) fprintf(fpout,"Bad weapon number %d of %d\n", + indexnum, NUMAMMO); + + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + if (!strcasecmp(key,deh_weapon[0])) // Ammo type + weaponinfo[indexnum].ammo = (ammotype_t)value; + else + if (!strcasecmp(key,deh_weapon[1])) // Deselect frame + weaponinfo[indexnum].upstate = (int)value; + else + if (!strcasecmp(key,deh_weapon[2])) // Select frame + weaponinfo[indexnum].downstate = (int)value; + else + if (!strcasecmp(key,deh_weapon[3])) // Bobbing frame + weaponinfo[indexnum].readystate = (int)value; + else + if (!strcasecmp(key,deh_weapon[4])) // Shooting frame + weaponinfo[indexnum].atkstate = (int)value; + else + if (!strcasecmp(key,deh_weapon[5])) // Firing frame + weaponinfo[indexnum].flashstate = (int)value; + else + if (fpout) fprintf(fpout,"Invalid weapon string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procSprite +// Purpose: Dummy - we do not support the DEH Sprite block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procSprite(DEHFILE *fpin, FILE* fpout, char *line) // Not supported +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + int indexnum; + + // Too little is known about what this is supposed to do, and + // there are better ways of handling sprite renaming. Not supported. + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout, + "Ignoring Sprite offset change at index %d: %s\n",indexnum, key); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + // ignore line + if (fpout) fprintf(fpout,"- %s\n",inbuffer); + } + return; +} + +// ==================================================================== +// deh_procPars +// Purpose: Handle BEX extension for PAR times +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procPars(DEHFILE *fpin, FILE* fpout, char *line) // extension +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + int indexnum; + int episode, level, partime, oldpar; + + // new item, par times + // usage: After [PARS] Par 0 section identifier, use one or more of these + // lines: + // par 3 5 120 + // par 14 230 + // The first would make the par for E3M5 be 120 seconds, and the + // second one makes the par for MAP14 be 230 seconds. The number + // of parameters on the line determines which group of par values + // is being changed. Error checking is done based on current fixed + // array sizes of[4][10] and [32] + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + // killough 8/98: allow hex numbers in input: + sscanf(inbuffer,"%s %i",key, &indexnum); + if (fpout) fprintf(fpout, + "Processing Par value at index %d: %s\n",indexnum, key); + // indexnum is a dummy entry + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(strlwr(inbuffer)); // lowercase it + if (!*inbuffer) break; // killough 11/98 + if (3 != sscanf(inbuffer,"par %i %i %i",&episode, &level, &partime)) + { // not 3 + if (2 != sscanf(inbuffer,"par %i %i",&level, &partime)) + { // not 2 + if (fpout) fprintf(fpout,"Invalid par time setting string: %s\n",inbuffer); + } + else + { // is 2 + // Ty 07/11/98 - wrong range check, not zero-based + if (level < 1 || level > 32) // base 0 array (but 1-based parm) + { + if (fpout) fprintf(fpout,"Invalid MAPnn value MAP%d\n",level); + } + else + { + oldpar = cpars[level-1]; + if (fpout) fprintf(fpout,"Changed par time for MAP%02d from %d to %d\n",level,oldpar,partime); + cpars[level-1] = partime; + deh_pars = TRUE; + } + } + } + else + { // is 3 + // note that though it's a [4][10] array, the "left" and "top" aren't used, + // effectively making it a base 1 array. + // Ty 07/11/98 - level was being checked against max 3 - dumb error + // Note that episode 4 does not have par times per original design + // in Ultimate DOOM so that is not supported here. + if (episode < 1 || episode > 3 || level < 1 || level > 9) + { + if (fpout) fprintf(fpout, + "Invalid ExMx values E%dM%d\n",episode, level); + } + else + { + oldpar = pars[episode][level]; + pars[episode][level] = partime; + if (fpout) fprintf(fpout, + "Changed par time for E%dM%d from %d to %d\n", + episode,level,oldpar,partime); + deh_pars = TRUE; + } + } + } + return; +} + +// ==================================================================== +// deh_procCheat +// Purpose: Handle DEH Cheat block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procCheat(DEHFILE *fpin, FILE* fpout, char *line) // done +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char ch = 0; // CPhipps - `writable' null string to initialise... + char *strval = &ch; // pointer to the value area + int ix, iy; // array indices + char *p; // utility pointer + + if (fpout) fprintf(fpout,"Processing Cheat: %s\n",line); + + strncpy(inbuffer,line,DEH_BUFFERMAX); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // Otherwise we got a (perhaps valid) cheat name, + // so look up the key in the array + + // killough 4/18/98: use main cheat code table in st_stuff.c now + for (ix=0; cheat[ix].cheat; ix++) + if (cheat[ix].deh_cheat) // killough 4/18/98: skip non-deh + { + if (!stricmp(key,cheat[ix].deh_cheat)) // found the cheat, ignored case + { + // replace it but don't overflow it. Use current length as limit. + // Ty 03/13/98 - add 0xff code + // Deal with the fact that the cheats in deh files are extended + // with character 0xFF to the original cheat length, which we don't do. + for (iy=0; strval[iy]; iy++) + strval[iy] = (strval[iy]==(char)0xff) ? '\0' : strval[iy]; + + iy = ix; // killough 4/18/98 + + // Ty 03/14/98 - skip leading spaces + p = strval; + while (*p == ' ') ++p; + // Ty 03/16/98 - change to use a strdup and orphan the original + // Also has the advantage of allowing length changes. + // strncpy(cheat[iy].cheat,p,strlen(cheat[iy].cheat)); +#if 0 + { // killough 9/12/98: disable cheats which are prefixes of this one + int i; + for (i=0; cheat[i].cheat; i++) + if (cheat[i].when & not_deh && + !strncasecmp(cheat[i].cheat, + cheat[iy].cheat, + strlen(cheat[i].cheat)) && i != iy) + cheat[i].deh_modified = true; + } +#endif + cheat[iy].cheat = strdup(p); + if (fpout) fprintf(fpout, + "Assigned new cheat '%s' to cheat '%s'at index %d\n", + p, cheat[ix].deh_cheat, iy); // killough 4/18/98 + } + } + if (fpout) fprintf(fpout,"- %s\n",inbuffer); + } + return; +} + +// ==================================================================== +// deh_procMisc +// Purpose: Handle DEH Misc block +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procMisc(DEHFILE *fpin, FILE* fpout, char *line) // done +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + + strncpy(inbuffer,line,DEH_BUFFERMAX); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // Otherwise it's ok + if (fpout) fprintf(fpout,"Processing Misc item '%s'\n", key); + + if (!strcasecmp(key,deh_misc[0])) // Initial Health + initial_health = (int)value; + else + if (!strcasecmp(key,deh_misc[1])) // Initial Bullets + initial_bullets = (int)value; + else + if (!strcasecmp(key,deh_misc[2])) // Max Health + maxhealth = (int)value; + else + if (!strcasecmp(key,deh_misc[3])) // Max Armor + max_armor = (int)value; + else + if (!strcasecmp(key,deh_misc[4])) // Green Armor Class + green_armor_class = (int)value; + else + if (!strcasecmp(key,deh_misc[5])) // Blue Armor Class + blue_armor_class = (int)value; + else + if (!strcasecmp(key,deh_misc[6])) // Max Soulsphere + max_soul = (int)value; + else + if (!strcasecmp(key,deh_misc[7])) // Soulsphere Health + soul_health = (int)value; + else + if (!strcasecmp(key,deh_misc[8])) // Megasphere Health + mega_health = (int)value; + else + if (!strcasecmp(key,deh_misc[9])) // God Mode Health + god_health = (int)value; + else + if (!strcasecmp(key,deh_misc[10])) // IDFA Armor + idfa_armor = (int)value; + else + if (!strcasecmp(key,deh_misc[11])) // IDFA Armor Class + idfa_armor_class = (int)value; + else + if (!strcasecmp(key,deh_misc[12])) // IDKFA Armor + idkfa_armor = (int)value; + else + if (!strcasecmp(key,deh_misc[13])) // IDKFA Armor Class + idkfa_armor_class = (int)value; + else + if (!strcasecmp(key,deh_misc[14])) // BFG Cells/Shot + bfgcells = (int)value; + else + if (!strcasecmp(key,deh_misc[15])) { // Monsters Infight + // e6y: Dehacked support - monsters infight + if (value == 202) monsters_infight = 0; + else if (value == 221) monsters_infight = 1; + else if (fpout) fprintf(fpout, + "Invalid value for 'Monsters Infight': %i", (int)value); + + /* No such switch in DOOM - nop */ //e6y ; + } else + if (fpout) fprintf(fpout, + "Invalid misc item string index for '%s'\n",key); + } + return; +} + +// ==================================================================== +// deh_procText +// Purpose: Handle DEH Text block +// Notes: We look things up in the current information and if found +// we replace it. At the same time we write the new and +// improved BEX syntax to the log file for future use. +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procText(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX*2]; // can't use line -- double size buffer too. + int i; // loop variable + int fromlen, tolen; // as specified on the text block line + int usedlen; // shorter of fromlen and tolen if not matched + boolean found = FALSE; // to allow early exit once found + char* line2 = NULL; // duplicate line for rerouting + + // e6y + // Correction for DEHs which swap the values of two strings. For example: + // Text 4 4 Text 4 4; Text 6 6 Text 6 6 + // BOSSBOS2 BOS2BOSS; RUNNINSTALKS STALKSRUNNIN + // It corrects buggy behaviour on "All Hell is Breaking Loose" TC + // http://www.doomworld.com/idgames/index.php?id=6480 + static boolean sprnames_state[NUMSPRITES+1]; + static boolean S_sfx_state[NUMSFX]; + static boolean S_music_state[NUMMUSIC]; + + // Ty 04/11/98 - Included file may have NOTEXT skip flag set + if (includenotext) // flag to skip included deh-style text + { + if (fpout) fprintf(fpout, + "Skipped text block because of notext directive\n"); + strcpy(inbuffer,line); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + dehfgets(inbuffer, sizeof(inbuffer), fpin); // skip block + // Ty 05/17/98 - don't care if this fails + return; // ************** Early return + } + + // killough 8/98: allow hex numbers in input: + sscanf(line,"%s %i %i",key,&fromlen,&tolen); + if (fpout) fprintf(fpout, + "Processing Text (key=%s, from=%d, to=%d)\n", + key, fromlen, tolen); + + // killough 10/98: fix incorrect usage of feof + { + int c, totlen = 0; + while (totlen < fromlen + tolen && (c = dehfgetc(fpin)) != EOF) + if (c != '\r') + inbuffer[totlen++] = c; + inbuffer[totlen]='\0'; + } + + // if the from and to are 4, this may be a sprite rename. Check it + // against the array and process it as such if it matches. Remember + // that the original names are (and should remain) uppercase. + // Future: this will be from a separate [SPRITES] block. + if (fromlen==4 && tolen==4) + { + i=0; + while (sprnames[i]) // null terminated list in info.c //jff 3/19/98 + { //check pointer + if (!strnicmp(sprnames[i],inbuffer,fromlen) && !sprnames_state[i]) //not first char + { + if (fpout) fprintf(fpout, + "Changing name of sprite at index %d from %s to %*s\n", + i,sprnames[i],tolen,&inbuffer[fromlen]); + // Ty 03/18/98 - not using strdup because length is fixed + + // killough 10/98: but it's an array of pointers, so we must + // use strdup unless we redeclare sprnames and change all else + { + // CPhipps - fix constness problem + char *s; + sprnames[i] = s = strdup(sprnames[i]); + + //e6y: flag the sprite as changed + sprnames_state[i] = true; + + strncpy(s,&inbuffer[fromlen],tolen); + } + found = TRUE; + break; // only one will match--quit early + } + ++i; // next array element + } + } + else + if (fromlen < 7 && tolen < 7) // lengths of music and sfx are 6 or shorter + { + usedlen = (fromlen < tolen) ? fromlen : tolen; + if (fromlen != tolen) + if (fpout) fprintf(fpout, + "Warning: Mismatched lengths from=%d, to=%d, used %d\n", + fromlen, tolen, usedlen); + // Try sound effects entries - see sounds.c + for (i=1; i 12) ? "..." : "",fromlen,tolen); + if ((size_t)fromlen <= strlen(inbuffer)) + { + line2 = strdup(&inbuffer[fromlen]); + inbuffer[fromlen] = '\0'; + } + + deh_procStringSub(NULL, inbuffer, line2, fpout); + } + free(line2); // may be NULL, ignored by free() + return; +} + +static void deh_procError(DEHFILE *fpin, FILE* fpout, char *line) +{ + char inbuffer[DEH_BUFFERMAX]; + + strncpy(inbuffer,line,DEH_BUFFERMAX); + if (fpout) fprintf(fpout,"Unmatched Block: '%s'\n",inbuffer); + return; +} + +// ==================================================================== +// deh_procStrings +// Purpose: Handle BEX [STRINGS] extension +// Args: fpin -- input file stream +// fpout -- output file stream (DEHOUT.TXT) +// line -- current line in file to process +// Returns: void +// +static void deh_procStrings(DEHFILE *fpin, FILE* fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + static int maxstrlen = 128; // maximum string length, bumped 128 at + // a time as needed + // holds the final result of the string after concatenation + static char *holdstring = NULL; + boolean found = false; // looking for string continuation + + if (fpout) fprintf(fpout,"Processing extended string substitution\n"); + + if (!holdstring) holdstring = malloc(maxstrlen*sizeof(*holdstring)); + + *holdstring = '\0'; // empty string to start with + strncpy(inbuffer,line,DEH_BUFFERMAX); + // Ty 04/24/98 - have to allow inbuffer to start with a blank for + // the continuations of C1TEXT etc. + while (!dehfeof(fpin) && *inbuffer) /* && (*inbuffer != ' ') */ + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + if (*inbuffer == '#') continue; // skip comment lines + lfstrip(inbuffer); + if (!*inbuffer) break; // killough 11/98 + if (!*holdstring) // first one--get the key + { + if (!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + } + while (strlen(holdstring) + strlen(inbuffer) > (size_t)maxstrlen) // Ty03/29/98 - fix stupid error + { + // killough 11/98: allocate enough the first time + maxstrlen += strlen(holdstring) + strlen(inbuffer) - maxstrlen; + if (fpout) fprintf(fpout, + "* increased buffer from to %d for buffer size %d\n", + maxstrlen,(int)strlen(inbuffer)); + holdstring = realloc(holdstring,maxstrlen*sizeof(*holdstring)); + } + // concatenate the whole buffer if continuation or the value iffirst + strcat(holdstring,ptr_lstrip(((*holdstring) ? inbuffer : strval))); + rstrip(holdstring); + // delete any trailing blanks past the backslash + // note that blanks before the backslash will be concatenated + // but ones at the beginning of the next line will not, allowing + // indentation in the file to read well without affecting the + // string itself. + if (holdstring[strlen(holdstring)-1] == '\\') + { + holdstring[strlen(holdstring)-1] = '\0'; + continue; // ready to concatenate + } + if (*holdstring) // didn't have a backslash, trap above would catch that + { + // go process the current string + found = deh_procStringSub(key, NULL, holdstring, fpout); // supply keyand not search string + + if (!found) + if (fpout) fprintf(fpout, + "Invalid string key '%s', substitution skipped.\n",key); + + *holdstring = '\0'; // empty string for the next one + } + } + return; +} + +// ==================================================================== +// deh_procStringSub +// Purpose: Common string parsing and handling routine for DEH and BEX +// Args: key -- place to put the mnemonic for the string if found +// lookfor -- original value string to look for +// newstring -- string to put in its place if found +// fpout -- file stream pointer for log file (DEHOUT.TXT) +// Returns: boolean: True if string found, false if not +// +boolean deh_procStringSub(char *key, char *lookfor, char *newstring, FILE *fpout) +{ + boolean found; // loop exit flag + int i; // looper + + found = false; + for (i=0;i '%s'\n",key,newstring); + + if (!key) + if (fpout) fprintf(fpout, + "Assigned '%.12s%s' to'%.12s%s' at key %s\n", + lookfor, (strlen(lookfor) > 12) ? "..." : "", + newstring, (strlen(newstring) > 12) ? "..." :"", + deh_strlookup[i].lookup); + + if (!key) // must have passed an old style string so showBEX + if (fpout) fprintf(fpout, + "*BEX FORMAT:\n%s = %s\n*END BEX\n", + deh_strlookup[i].lookup, + dehReformatStr(newstring)); + + break; + } + } + if (!found) + if (fpout) fprintf(fpout, + "Could not find '%.12s'\n",key ? key: lookfor); + + return found; +} + +//======================================================================== +// haleyjd 9/22/99 +// +// deh_procHelperThing +// +// Allows handy substitution of any thing for helper dogs. DEH patches +// are being made frequently for this purpose and it requires a complete +// rewiring of the DOG thing. I feel this is a waste of effort, and so +// have added this new [HELPER] BEX block + +static void deh_procHelperThing(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + + strncpy(inbuffer,line,DEH_BUFFERMAX); + while (!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if (!dehfgets(inbuffer, sizeof(inbuffer), fpin)) break; + lfstrip(inbuffer); + if (!*inbuffer) break; + if (!deh_GetData(inbuffer,key,&value,NULL,fpout)) // returns TRUE if ok + { + if (fpout) fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // Otherwise it's ok + if (fpout) + { + fprintf(fpout,"Processing Helper Thing item '%s'\n", key); + fprintf(fpout,"value is %i", (int)value); + } + if (!strncasecmp(key, "type", 4)) + HelperThing = (int)value; + } + return; +} + +// +// deh_procBexSprites +// +// Supports sprite name substitutions without requiring use +// of the DeHackEd Text block +// +static void deh_procBexSprites(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + char candidate[5]; + int rover; + + if(fpout) + fprintf(fpout,"Processing sprite name substitution\n"); + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + while(!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if(!dehfgets(inbuffer, sizeof(inbuffer), fpin)) + break; + if(*inbuffer == '#') + continue; // skip comment lines + lfstrip(inbuffer); + if(!*inbuffer) + break; // killough 11/98 + if(!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if(fpout) + fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // do it + memset(candidate, 0, sizeof(candidate)); + strncpy(candidate, ptr_lstrip(strval), 4); + if(strlen(candidate) != 4) + { + if(fpout) + fprintf(fpout, "Bad length for sprite name '%s'\n", + candidate); + continue; + } + + rover = 0; + while(deh_spritenames[rover]) + { + if(!strncasecmp(deh_spritenames[rover], key, 4)) + { + if(fpout) + fprintf(fpout, "Substituting '%s' for sprite '%s'\n", + candidate, deh_spritenames[rover]); + + sprnames[rover] = strdup(candidate); + break; + } + rover++; + } + } +} + +// ditto for sound names +static void deh_procBexSounds(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + char candidate[7]; + int rover, len; + + if(fpout) + fprintf(fpout,"Processing sound name substitution\n"); + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + while(!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if(!dehfgets(inbuffer, sizeof(inbuffer), fpin)) + break; + if(*inbuffer == '#') + continue; // skip comment lines + lfstrip(inbuffer); + if(!*inbuffer) + break; // killough 11/98 + if(!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if(fpout) + fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // do it + memset(candidate, 0, 7); + strncpy(candidate, ptr_lstrip(strval), 6); + len = strlen(candidate); + if(len < 1 || len > 6) + { + if(fpout) + fprintf(fpout, "Bad length for sound name '%s'\n", + candidate); + continue; + } + + rover = 1; + while(deh_soundnames[rover]) + { + if(!strncasecmp(deh_soundnames[rover], key, 6)) + { + if(fpout) + fprintf(fpout, "Substituting '%s' for sound '%s'\n", + candidate, deh_soundnames[rover]); + + S_sfx[rover].name = strdup(candidate); + break; + } + rover++; + } + } +} + +// ditto for music names +static void deh_procBexMusic(DEHFILE *fpin, FILE *fpout, char *line) +{ + char key[DEH_MAXKEYLEN]; + char inbuffer[DEH_BUFFERMAX]; + uint_64_t value; // All deh values are ints or longs + char *strval; // holds the string value of the line + char candidate[7]; + int rover, len; + + if(fpout) + fprintf(fpout,"Processing music name substitution\n"); + + strncpy(inbuffer,line,DEH_BUFFERMAX); + + while(!dehfeof(fpin) && *inbuffer && (*inbuffer != ' ')) + { + if(!dehfgets(inbuffer, sizeof(inbuffer), fpin)) + break; + if(*inbuffer == '#') + continue; // skip comment lines + lfstrip(inbuffer); + if(!*inbuffer) + break; // killough 11/98 + if(!deh_GetData(inbuffer,key,&value,&strval,fpout)) // returns TRUE if ok + { + if(fpout) + fprintf(fpout,"Bad data pair in '%s'\n",inbuffer); + continue; + } + // do it + memset(candidate, 0, 7); + strncpy(candidate, ptr_lstrip(strval), 6); + len = strlen(candidate); + if(len < 1 || len > 6) + { + if(fpout) + fprintf(fpout, "Bad length for music name '%s'\n", + candidate); + continue; + } + + rover = 1; + while(deh_musicnames[rover]) + { + if(!strncasecmp(deh_musicnames[rover], key, 6)) + { + if(fpout) + fprintf(fpout, "Substituting '%s' for music '%s'\n", + candidate, deh_musicnames[rover]); + + S_music[rover].name = strdup(candidate); + break; + } + rover++; + } + } +} + +// ==================================================================== +// General utility function(s) +// ==================================================================== + +// ==================================================================== +// dehReformatStr +// Purpose: Convert a string into a continuous string with embedded +// linefeeds for "\n" sequences in the source string +// Args: string -- the string to convert +// Returns: the converted string (converted in a static buffer) +// +char *dehReformatStr(char *string) +{ + static char buff[DEH_BUFFERMAX]; // only processing the changed string, + // don't need double buffer + char *s, *t; + + s = string; // source + t = buff; // target + // let's play... + + while (*s) + { + if (*s == '\n') + ++s, *t++ = '\\', *t++ = 'n', *t++ = '\\', *t++='\n'; + else + *t++ = *s++; + } + *t = '\0'; + return buff; +} + +// ==================================================================== +// lfstrip +// Purpose: Strips CR/LF off the end of a string +// Args: s -- the string to work on +// Returns: void -- the string is modified in place +// +// killough 10/98: only strip at end of line, not entire string + +void lfstrip(char *s) // strip the \r and/or \n off of a line +{ + char *p = s+strlen(s); + while (p > s && (*--p=='\r' || *p=='\n')) + *p = 0; +} + +// ==================================================================== +// rstrip +// Purpose: Strips trailing blanks off a string +// Args: s -- the string to work on +// Returns: void -- the string is modified in place +// +void rstrip(char *s) // strip trailing whitespace +{ + char *p = s+strlen(s); // killough 4/4/98: same here + while (p > s && isspace(*--p)) // break on first non-whitespace + *p='\0'; +} + +// ==================================================================== +// ptr_lstrip +// Purpose: Points past leading whitespace in a string +// Args: s -- the string to work on +// Returns: char * pointing to the first nonblank character in the +// string. The original string is not changed. +// +char *ptr_lstrip(char *p) // point past leading whitespace +{ + while (isspace(*p)) + p++; + return p; +} + +// e6y: Correction of wrong processing of Bits parameter if its value is equal to zero +// No more desync on HACX demos. +// FIXME!!! (lame) +static boolean StrToInt(char *s, long *l) +{ + return ( + (sscanf(s, " 0x%lx", l) == 1) || + (sscanf(s, " 0X%lx", l) == 1) || + (sscanf(s, " 0%lo", l) == 1) || + (sscanf(s, " %ld", l) == 1) + ); +} + +// ==================================================================== +// deh_GetData +// Purpose: Get a key and data pair from a passed string +// Args: s -- the string to be examined +// k -- a place to put the key +// l -- pointer to a long integer to store the number +// strval -- a pointer to the place in s where the number +// value comes from. Pass NULL to not use this. +// fpout -- stream pointer to output log (DEHOUT.TXT) +// Notes: Expects a key phrase, optional space, equal sign, +// optional space and a value, mostly an int but treated +// as a long just in case. The passed pointer to hold +// the key must be DEH_MAXKEYLEN in size. + +boolean deh_GetData(char *s, char *k, uint_64_t *l, char **strval, FILE *fpout) +{ + char *t; // current char + long val; // to hold value of pair + char buffer[DEH_MAXKEYLEN]; // to hold key in progress + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + boolean okrc = 1; // assume good unless we have problems + int i; // iterator + + *buffer = '\0'; + val = 0; // defaults in case not otherwise set + for (i=0, t=s; *t && i < DEH_MAXKEYLEN; t++, i++) + { + if (*t == '=') break; + buffer[i] = *t; // copy it + } + buffer[--i] = '\0'; // terminate the key before the '=' + if (!*t) // end of string with no equal sign + { + okrc = FALSE; + } + else + { + if (!*++t) + { + val = 0; // in case "thiskey =" with no value + okrc = FALSE; + } + // we've incremented t + // e6y: Correction of wrong processing of Bits parameter if its value is equal to zero + // No more desync on HACX demos. + // Old code: e6y val = strtol(t,NULL,0); // killough 8/9/98: allow hex or octal input + if (!StrToInt(t,&val)) + { + val = 0; + okrc = 2; + } + } + + // go put the results in the passed pointers + *l = val; // may be a faked zero + + // if spaces between key and equal sign, strip them + strcpy(k,ptr_lstrip(buffer)); // could be a zero-length string + + if (strval != NULL) // pass NULL if you don't want this back + *strval = t; // pointer, has to be somewhere in s, + // even if pointing at the zero byte. + + return(okrc); +} diff --git a/src/d_deh.h b/src/d_deh.h new file mode 100644 index 00000000..02cdffaf --- /dev/null +++ b/src/d_deh.h @@ -0,0 +1,1118 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Dehacked file support + * New for the TeamTNT "Boom" engine + * + * Author: Ty Halderman, TeamTNT + * + * Description: This file translates the #defined string constants + * to named variables to externalize them for deh/bex changes. + * Should be able to compile with D_FRENCH (for example) and still + * work (untested). + * + */ + +#ifndef __D_DEH__ +#define __D_DEH__ + +void ProcessDehFile(const char *filename, const char *outfilename, int lumpnum); + +// +// Ty 03/22/98 - note that we are keeping the english versions and +// comments in this file +// New string names all start with an extra s_ to avoid conflicts, +// but are otherwise identical to the original including uppercase. +// This is partly to keep the changes simple and partly for easier +// identification of the locations in which they're used. +// +// Printed strings for translation +// + +// +// D_Main.C +// +//#define D_DEVSTR "Development mode ON.\n" +extern const char *s_D_DEVSTR; // = D_DEVSTR; +//#define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n" +extern const char *s_D_CDROM; // = D_CDROM; + +// +// M_Menu.C +// +//#define PRESSKEY "press a key." +extern const char *s_PRESSKEY; // = PRESSKEY; +//#define PRESSYN "press y or n." +extern const char *s_PRESSYN; // = PRESSYN; +//#define QUITMSG "are you sure you want to\nquit this great game?" +extern const char *s_QUITMSG; // = QUITMSG; +//#define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY +extern const char *s_LOADNET; // = LOADNET; +//#define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY +extern const char *s_QLOADNET; // = QLOADNET; +//#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY +extern const char *s_QSAVESPOT; // = QSAVESPOT; +//#define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY +extern const char *s_SAVEDEAD; // = SAVEDEAD; +//#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN +extern const char *s_QSPROMPT; // = QSPROMPT; +//#define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n"PRESSYN +extern const char *s_QLPROMPT; // = QLPROMPT; + +/* +#define NEWGAME \ +"you can't start a new game\n"\ +"while in a network game.\n\n"PRESSKEY +*/ +extern const char *s_NEWGAME; // = NEWGAME; + +// CPhipps - message given when asked if to restart the level +extern const char *s_RESTARTLEVEL; + +/* +#define NIGHTMARE \ +"are you sure? this skill level\n"\ +"isn't even remotely fair.\n\n"PRESSYN +*/ +extern const char *s_NIGHTMARE; // = NIGHTMARE; + +/* +#define SWSTRING \ +"this is the shareware version of doom.\n\n"\ +"you need to order the entire trilogy.\n\n"PRESSKEY +*/ +extern const char *s_SWSTRING; // = SWSTRING; + +//#define MSGOFF "Messages OFF" +extern const char *s_MSGOFF; // = MSGOFF; +//#define MSGON "Messages ON" +extern const char *s_MSGON; // = MSGON; +//#define NETEND "you can't end a netgame!\n\n"PRESSKEY +extern const char *s_NETEND; // = NETEND; +//#define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN +extern const char *s_ENDGAME; // = ENDGAME; + +//#define DOSY "(press y to quit)" +extern const char *s_DOSY; // = DOSY; + +//#define DETAILHI "High detail" +extern const char *s_DETAILHI; // = DETAILHI; +//#define DETAILLO "Low detail" +extern const char *s_DETAILLO; // = DETAILLO; +//#define GAMMALVL0 "Gamma correction OFF" +extern const char *s_GAMMALVL0; // = GAMMALVL0; +//#define GAMMALVL1 "Gamma correction level 1" +extern const char *s_GAMMALVL1; // = GAMMALVL1; +//#define GAMMALVL2 "Gamma correction level 2" +extern const char *s_GAMMALVL2; // = GAMMALVL2; +//#define GAMMALVL3 "Gamma correction level 3" +extern const char *s_GAMMALVL3; // = GAMMALVL3; +//#define GAMMALVL4 "Gamma correction level 4" +extern const char *s_GAMMALVL4; // = GAMMALVL4; +//#define EMPTYSTRING "empty slot" +extern const char *s_EMPTYSTRING; // = EMPTYSTRING; + +// +// P_inter.C +// +//#define GOTARMOR "Picked up the armor." +extern const char *s_GOTARMOR; // = GOTARMOR; +//#define GOTMEGA "Picked up the MegaArmor!" +extern const char *s_GOTMEGA; // = GOTMEGA; +//#define GOTHTHBONUS "Picked up a health bonus." +extern const char *s_GOTHTHBONUS; // = GOTHTHBONUS; +//#define GOTARMBONUS "Picked up an armor bonus." +extern const char *s_GOTARMBONUS; // = GOTARMBONUS; +//#define GOTSTIM "Picked up a stimpack." +extern const char *s_GOTSTIM; // = GOTSTIM; +//#define GOTMEDINEED "Picked up a medikit that you REALLY need!" +extern const char *s_GOTMEDINEED; // = GOTMEDINEED; +//#define GOTMEDIKIT "Picked up a medikit." +extern const char *s_GOTMEDIKIT; // = GOTMEDIKIT; +//#define GOTSUPER "Supercharge!" +extern const char *s_GOTSUPER; // = GOTSUPER; + +//#define GOTBLUECARD "Picked up a blue keycard." +extern const char *s_GOTBLUECARD; // = GOTBLUECARD; +//#define GOTYELWCARD "Picked up a yellow keycard." +extern const char *s_GOTYELWCARD; // = GOTYELWCARD; +//#define GOTREDCARD "Picked up a red keycard." +extern const char *s_GOTREDCARD; // = GOTREDCARD; +//#define GOTBLUESKUL "Picked up a blue skull key." +extern const char *s_GOTBLUESKUL; // = GOTBLUESKUL; +//#define GOTYELWSKUL "Picked up a yellow skull key." +extern const char *s_GOTYELWSKUL; // = GOTYELWSKUL; +//#define GOTREDSKULL "Picked up a red skull key." +extern const char *s_GOTREDSKULL; // = GOTREDSKULL; + +//#define GOTINVUL "Invulnerability!" +extern const char *s_GOTINVUL; // = GOTINVUL; +//#define GOTBERSERK "Berserk!" +extern const char *s_GOTBERSERK; // = GOTBERSERK; +//#define GOTINVIS "Partial Invisibility" +extern const char *s_GOTINVIS; // = GOTINVIS; +//#define GOTSUIT "Radiation Shielding Suit" +extern const char *s_GOTSUIT; // = GOTSUIT; +//#define GOTMAP "Computer Area Map" +extern const char *s_GOTMAP; // = GOTMAP; +//#define GOTVISOR "Light Amplification Visor" +extern const char *s_GOTVISOR; // = GOTVISOR; +//#define GOTMSPHERE "MegaSphere!" +extern const char *s_GOTMSPHERE; // = GOTMSPHERE; + +//#define GOTCLIP "Picked up a clip." +extern const char *s_GOTCLIP; // = GOTCLIP; +//#define GOTCLIPBOX "Picked up a box of bullets." +extern const char *s_GOTCLIPBOX; // = GOTCLIPBOX; +//#define GOTROCKET "Picked up a rocket." +extern const char *s_GOTROCKET; // = GOTROCKET; +//#define GOTROCKBOX "Picked up a box of rockets." +extern const char *s_GOTROCKBOX; // = GOTROCKBOX; +//#define GOTCELL "Picked up an energy cell." +extern const char *s_GOTCELL; // = GOTCELL; +//#define GOTCELLBOX "Picked up an energy cell pack." +extern const char *s_GOTCELLBOX; // = GOTCELLBOX; +//#define GOTSHELLS "Picked up 4 shotgun shells." +extern const char *s_GOTSHELLS; // = GOTSHELLS; +//#define GOTSHELLBOX "Picked up a box of shotgun shells." +extern const char *s_GOTSHELLBOX; // = GOTSHELLBOX; +//#define GOTBACKPACK "Picked up a backpack full of ammo!" +extern const char *s_GOTBACKPACK; // = GOTBACKPACK; + +//#define GOTBFG9000 "You got the BFG9000! Oh, yes." +extern const char *s_GOTBFG9000; // = GOTBFG9000; +//#define GOTCHAINGUN "You got the chaingun!" +extern const char *s_GOTCHAINGUN; // = GOTCHAINGUN; +//#define GOTCHAINSAW "A chainsaw! Find some meat!" +extern const char *s_GOTCHAINSAW; // = GOTCHAINSAW; +//#define GOTLAUNCHER "You got the rocket launcher!" +extern const char *s_GOTLAUNCHER; // = GOTLAUNCHER; +//#define GOTPLASMA "You got the plasma gun!" +extern const char *s_GOTPLASMA; // = GOTPLASMA; +//#define GOTSHOTGUN "You got the shotgun!" +extern const char *s_GOTSHOTGUN; // = GOTSHOTGUN; +//#define GOTSHOTGUN2 "You got the super shotgun!" +extern const char *s_GOTSHOTGUN2; // = GOTSHOTGUN2; + +// +// P_Doors.C +// +//#define PD_BLUEO "You need a blue key to activate this object" +extern const char *s_PD_BLUEO; // = PD_BLUEO; +//#define PD_REDO "You need a red key to activate this object" +extern const char *s_PD_REDO; // = PD_REDO; +//#define PD_YELLOWO "You need a yellow key to activate this object" +extern const char *s_PD_YELLOWO; // = PD_YELLOWO; +//#define PD_BLUEK "You need a blue key to open this door" +extern const char *s_PD_BLUEK; // = PD_BLUEK; +//#define PD_REDK "You need a red key to open this door" +extern const char *s_PD_REDK; // = PD_REDK; +//#define PD_YELLOWK "You need a yellow key to open this door" +extern const char *s_PD_YELLOWK; // = PD_YELLOWK; +//jff 02/05/98 Create messages specific to card and skull keys +//#define PD_BLUEC "You need a blue card to open this door" +extern const char *s_PD_BLUEC; // = PD_BLUEC; +//#define PD_REDC "You need a red card to open this door" +extern const char *s_PD_REDC; // = PD_REDC; +//#define PD_YELLOWC "You need a yellow card to open this door" +extern const char *s_PD_YELLOWC; // = PD_YELLOWC; +//#define PD_BLUES "You need a blue skull to open this door" +extern const char *s_PD_BLUES; // = PD_BLUES; +//#define PD_REDS "You need a red skull to open this door" +extern const char *s_PD_REDS; // = PD_REDS; +//#define PD_YELLOWS "You need a yellow skull to open this door" +extern const char *s_PD_YELLOWS; // = PD_YELLOWS; +//#define PD_ANY "Any key will open this door" +extern const char *s_PD_ANY; // = PD_ANY; +//#define PD_ALL3 "You need all three keys to open this door" +extern const char *s_PD_ALL3; // = PD_ALL3; +//#define PD_ALL6 "You need all six keys to open this door" +extern const char *s_PD_ALL6; // = PD_ALL6; + +// +// G_game.C +// +//#define GGSAVED "game saved." +extern const char *s_GGSAVED; // = GGSAVED; + +// +// HU_stuff.C +// +//#define HUSTR_MSGU "[Message unsent]" +extern const char *s_HUSTR_MSGU; // = HUSTR_MSGU; + +//#define HUSTR_E1M1 "E1M1: Hangar" +extern const char *s_HUSTR_E1M1; // = HUSTR_E1M1; +//#define HUSTR_E1M2 "E1M2: Nuclear Plant" +extern const char *s_HUSTR_E1M2; // = HUSTR_E1M2; +//#define HUSTR_E1M3 "E1M3: Toxin Refinery" +extern const char *s_HUSTR_E1M3; // = HUSTR_E1M3; +//#define HUSTR_E1M4 "E1M4: Command Control" +extern const char *s_HUSTR_E1M4; // = HUSTR_E1M4; +//#define HUSTR_E1M5 "E1M5: Phobos Lab" +extern const char *s_HUSTR_E1M5; // = HUSTR_E1M5; +//#define HUSTR_E1M6 "E1M6: Central Processing" +extern const char *s_HUSTR_E1M6; // = HUSTR_E1M6; +//#define HUSTR_E1M7 "E1M7: Computer Station" +extern const char *s_HUSTR_E1M7; // = HUSTR_E1M7; +//#define HUSTR_E1M8 "E1M8: Phobos Anomaly" +extern const char *s_HUSTR_E1M8; // = HUSTR_E1M8; +//#define HUSTR_E1M9 "E1M9: Military Base" +extern const char *s_HUSTR_E1M9; // = HUSTR_E1M9; + +//#define HUSTR_E2M1 "E2M1: Deimos Anomaly" +extern const char *s_HUSTR_E2M1; // = HUSTR_E2M1; +//#define HUSTR_E2M2 "E2M2: Containment Area" +extern const char *s_HUSTR_E2M2; // = HUSTR_E2M2; +//#define HUSTR_E2M3 "E2M3: Refinery" +extern const char *s_HUSTR_E2M3; // = HUSTR_E2M3; +//#define HUSTR_E2M4 "E2M4: Deimos Lab" +extern const char *s_HUSTR_E2M4; // = HUSTR_E2M4; +//#define HUSTR_E2M5 "E2M5: Command Center" +extern const char *s_HUSTR_E2M5; // = HUSTR_E2M5; +//#define HUSTR_E2M6 "E2M6: Halls of the Damned" +extern const char *s_HUSTR_E2M6; // = HUSTR_E2M6; +//#define HUSTR_E2M7 "E2M7: Spawning Vats" +extern const char *s_HUSTR_E2M7; // = HUSTR_E2M7; +//#define HUSTR_E2M8 "E2M8: Tower of Babel" +extern const char *s_HUSTR_E2M8; // = HUSTR_E2M8; +//#define HUSTR_E2M9 "E2M9: Fortress of Mystery" +extern const char *s_HUSTR_E2M9; // = HUSTR_E2M9; + +//#define HUSTR_E3M1 "E3M1: Hell Keep" +extern const char *s_HUSTR_E3M1; // = HUSTR_E3M1; +//#define HUSTR_E3M2 "E3M2: Slough of Despair" +extern const char *s_HUSTR_E3M2; // = HUSTR_E3M2; +//#define HUSTR_E3M3 "E3M3: Pandemonium" +extern const char *s_HUSTR_E3M3; // = HUSTR_E3M3; +//#define HUSTR_E3M4 "E3M4: House of Pain" +extern const char *s_HUSTR_E3M4; // = HUSTR_E3M4; +//#define HUSTR_E3M5 "E3M5: Unholy Cathedral" +extern const char *s_HUSTR_E3M5; // = HUSTR_E3M5; +//#define HUSTR_E3M6 "E3M6: Mt. Erebus" +extern const char *s_HUSTR_E3M6; // = HUSTR_E3M6; +//#define HUSTR_E3M7 "E3M7: Limbo" +extern const char *s_HUSTR_E3M7; // = HUSTR_E3M7; +//#define HUSTR_E3M8 "E3M8: Dis" +extern const char *s_HUSTR_E3M8; // = HUSTR_E3M8; +//#define HUSTR_E3M9 "E3M9: Warrens" +extern const char *s_HUSTR_E3M9; // = HUSTR_E3M9; + +//#define HUSTR_E4M1 "E4M1: Hell Beneath" +extern const char *s_HUSTR_E4M1; // = HUSTR_E4M1; +//#define HUSTR_E4M2 "E4M2: Perfect Hatred" +extern const char *s_HUSTR_E4M2; // = HUSTR_E4M2; +//#define HUSTR_E4M3 "E4M3: Sever The Wicked" +extern const char *s_HUSTR_E4M3; // = HUSTR_E4M3; +//#define HUSTR_E4M4 "E4M4: Unruly Evil" +extern const char *s_HUSTR_E4M4; // = HUSTR_E4M4; +//#define HUSTR_E4M5 "E4M5: They Will Repent" +extern const char *s_HUSTR_E4M5; // = HUSTR_E4M5; +//#define HUSTR_E4M6 "E4M6: Against Thee Wickedly" +extern const char *s_HUSTR_E4M6; // = HUSTR_E4M6; +//#define HUSTR_E4M7 "E4M7: And Hell Followed" +extern const char *s_HUSTR_E4M7; // = HUSTR_E4M7; +//#define HUSTR_E4M8 "E4M8: Unto The Cruel" +extern const char *s_HUSTR_E4M8; // = HUSTR_E4M8; +//#define HUSTR_E4M9 "E4M9: Fear" +extern const char *s_HUSTR_E4M9; // = HUSTR_E4M9; + +//#define HUSTR_1 "level 1: entryway" +extern const char *s_HUSTR_1; // = HUSTR_1; +//#define HUSTR_2 "level 2: underhalls" +extern const char *s_HUSTR_2; // = HUSTR_2; +//#define HUSTR_3 "level 3: the gantlet" +extern const char *s_HUSTR_3; // = HUSTR_3; +//#define HUSTR_4 "level 4: the focus" +extern const char *s_HUSTR_4; // = HUSTR_4; +//#define HUSTR_5 "level 5: the waste tunnels" +extern const char *s_HUSTR_5; // = HUSTR_5; +//#define HUSTR_6 "level 6: the crusher" +extern const char *s_HUSTR_6; // = HUSTR_6; +//#define HUSTR_7 "level 7: dead simple" +extern const char *s_HUSTR_7; // = HUSTR_7; +//#define HUSTR_8 "level 8: tricks and traps" +extern const char *s_HUSTR_8; // = HUSTR_8; +//#define HUSTR_9 "level 9: the pit" +extern const char *s_HUSTR_9; // = HUSTR_9; +//#define HUSTR_10 "level 10: refueling base" +extern const char *s_HUSTR_10; // = HUSTR_10; +//#define HUSTR_11 "level 11: 'o' of destruction!" +extern const char *s_HUSTR_11; // = HUSTR_11; + +//#define HUSTR_12 "level 12: the factory" +extern const char *s_HUSTR_12; // = HUSTR_12; +//#define HUSTR_13 "level 13: downtown" +extern const char *s_HUSTR_13; // = HUSTR_13; +//#define HUSTR_14 "level 14: the inmost dens" +extern const char *s_HUSTR_14; // = HUSTR_14; +//#define HUSTR_15 "level 15: industrial zone" +extern const char *s_HUSTR_15; // = HUSTR_15; +//#define HUSTR_16 "level 16: suburbs" +extern const char *s_HUSTR_16; // = HUSTR_16; +//#define HUSTR_17 "level 17: tenements" +extern const char *s_HUSTR_17; // = HUSTR_17; +//#define HUSTR_18 "level 18: the courtyard" +extern const char *s_HUSTR_18; // = HUSTR_18; +//#define HUSTR_19 "level 19: the citadel" +extern const char *s_HUSTR_19; // = HUSTR_19; +//#define HUSTR_20 "level 20: gotcha!" +extern const char *s_HUSTR_20; // = HUSTR_20; + +//#define HUSTR_21 "level 21: nirvana" +extern const char *s_HUSTR_21; // = HUSTR_21; +//#define HUSTR_22 "level 22: the catacombs" +extern const char *s_HUSTR_22; // = HUSTR_22; +//#define HUSTR_23 "level 23: barrels o' fun" +extern const char *s_HUSTR_23; // = HUSTR_23; +//#define HUSTR_24 "level 24: the chasm" +extern const char *s_HUSTR_24; // = HUSTR_24; +//#define HUSTR_25 "level 25: bloodfalls" +extern const char *s_HUSTR_25; // = HUSTR_25; +//#define HUSTR_26 "level 26: the abandoned mines" +extern const char *s_HUSTR_26; // = HUSTR_26; +//#define HUSTR_27 "level 27: monster condo" +extern const char *s_HUSTR_27; // = HUSTR_27; +//#define HUSTR_28 "level 28: the spirit world" +extern const char *s_HUSTR_28; // = HUSTR_28; +//#define HUSTR_29 "level 29: the living end" +extern const char *s_HUSTR_29; // = HUSTR_29; +//#define HUSTR_30 "level 30: icon of sin" +extern const char *s_HUSTR_30; // = HUSTR_30; + +//#define HUSTR_31 "level 31: wolfenstein" +extern const char *s_HUSTR_31; // = HUSTR_31; +//#define HUSTR_32 "level 32: grosse" +extern const char *s_HUSTR_32; // = HUSTR_32; + +//#define PHUSTR_1 "level 1: congo" +extern const char *s_PHUSTR_1; // = PHUSTR_1; +//#define PHUSTR_2 "level 2: well of souls" +extern const char *s_PHUSTR_2; // = PHUSTR_2; +//#define PHUSTR_3 "level 3: aztec" +extern const char *s_PHUSTR_3; // = PHUSTR_3; +//#define PHUSTR_4 "level 4: caged" +extern const char *s_PHUSTR_4; // = PHUSTR_4; +//#define PHUSTR_5 "level 5: ghost town" +extern const char *s_PHUSTR_5; // = PHUSTR_5; +//#define PHUSTR_6 "level 6: baron's lair" +extern const char *s_PHUSTR_6; // = PHUSTR_6; +//#define PHUSTR_7 "level 7: caughtyard" +extern const char *s_PHUSTR_7; // = PHUSTR_7; +//#define PHUSTR_8 "level 8: realm" +extern const char *s_PHUSTR_8; // = PHUSTR_8; +//#define PHUSTR_9 "level 9: abattoire" +extern const char *s_PHUSTR_9; // = PHUSTR_9; +//#define PHUSTR_10 "level 10: onslaught" +extern const char *s_PHUSTR_10; // = PHUSTR_10; +//#define PHUSTR_11 "level 11: hunted" +extern const char *s_PHUSTR_11; // = PHUSTR_11; + +//#define PHUSTR_12 "level 12: speed" +extern const char *s_PHUSTR_12; // = PHUSTR_12; +//#define PHUSTR_13 "level 13: the crypt" +extern const char *s_PHUSTR_13; // = PHUSTR_13; +//#define PHUSTR_14 "level 14: genesis" +extern const char *s_PHUSTR_14; // = PHUSTR_14; +//#define PHUSTR_15 "level 15: the twilight" +extern const char *s_PHUSTR_15; // = PHUSTR_15; +//#define PHUSTR_16 "level 16: the omen" +extern const char *s_PHUSTR_16; // = PHUSTR_16; +//#define PHUSTR_17 "level 17: compound" +extern const char *s_PHUSTR_17; // = PHUSTR_17; +//#define PHUSTR_18 "level 18: neurosphere" +extern const char *s_PHUSTR_18; // = PHUSTR_18; +//#define PHUSTR_19 "level 19: nme" +extern const char *s_PHUSTR_19; // = PHUSTR_19; +//#define PHUSTR_20 "level 20: the death domain" +extern const char *s_PHUSTR_20; // = PHUSTR_20; + +//#define PHUSTR_21 "level 21: slayer" +extern const char *s_PHUSTR_21; // = PHUSTR_21; +//#define PHUSTR_22 "level 22: impossible mission" +extern const char *s_PHUSTR_22; // = PHUSTR_22; +//#define PHUSTR_23 "level 23: tombstone" +extern const char *s_PHUSTR_23; // = PHUSTR_23; +//#define PHUSTR_24 "level 24: the final frontier" +extern const char *s_PHUSTR_24; // = PHUSTR_24; +//#define PHUSTR_25 "level 25: the temple of darkness" +extern const char *s_PHUSTR_25; // = PHUSTR_25; +//#define PHUSTR_26 "level 26: bunker" +extern const char *s_PHUSTR_26; // = PHUSTR_26; +//#define PHUSTR_27 "level 27: anti-christ" +extern const char *s_PHUSTR_27; // = PHUSTR_27; +//#define PHUSTR_28 "level 28: the sewers" +extern const char *s_PHUSTR_28; // = PHUSTR_28; +//#define PHUSTR_29 "level 29: odyssey of noises" +extern const char *s_PHUSTR_29; // = PHUSTR_29; +//#define PHUSTR_30 "level 30: the gateway of hell" +extern const char *s_PHUSTR_30; // = PHUSTR_30; + +//#define PHUSTR_31 "level 31: cyberden" +extern const char *s_PHUSTR_31; // = PHUSTR_31; +//#define PHUSTR_32 "level 32: go 2 it" +extern const char *s_PHUSTR_32; // = PHUSTR_32; + +//#define THUSTR_1 "level 1: system control" +extern const char *s_THUSTR_1; // = THUSTR_1; +//#define THUSTR_2 "level 2: human bbq" +extern const char *s_THUSTR_2; // = THUSTR_2; +//#define THUSTR_3 "level 3: power control" +extern const char *s_THUSTR_3; // = THUSTR_3; +//#define THUSTR_4 "level 4: wormhole" +extern const char *s_THUSTR_4; // = THUSTR_4; +//#define THUSTR_5 "level 5: hanger" +extern const char *s_THUSTR_5; // = THUSTR_5; +//#define THUSTR_6 "level 6: open season" +extern const char *s_THUSTR_6; // = THUSTR_6; +//#define THUSTR_7 "level 7: prison" +extern const char *s_THUSTR_7; // = THUSTR_7; +//#define THUSTR_8 "level 8: metal" +extern const char *s_THUSTR_8; // = THUSTR_8; +//#define THUSTR_9 "level 9: stronghold" +extern const char *s_THUSTR_9; // = THUSTR_9; +//#define THUSTR_10 "level 10: redemption" +extern const char *s_THUSTR_10; // = THUSTR_10; +//#define THUSTR_11 "level 11: storage facility" +extern const char *s_THUSTR_11; // = THUSTR_11; + +//#define THUSTR_12 "level 12: crater" +extern const char *s_THUSTR_12; // = THUSTR_12; +//#define THUSTR_13 "level 13: nukage processing" +extern const char *s_THUSTR_13; // = THUSTR_13; +//#define THUSTR_14 "level 14: steel works" +extern const char *s_THUSTR_14; // = THUSTR_14; +//#define THUSTR_15 "level 15: dead zone" +extern const char *s_THUSTR_15; // = THUSTR_15; +//#define THUSTR_16 "level 16: deepest reaches" +extern const char *s_THUSTR_16; // = THUSTR_16; +//#define THUSTR_17 "level 17: processing area" +extern const char *s_THUSTR_17; // = THUSTR_17; +//#define THUSTR_18 "level 18: mill" +extern const char *s_THUSTR_18; // = THUSTR_18; +//#define THUSTR_19 "level 19: shipping/respawning" +extern const char *s_THUSTR_19; // = THUSTR_19; +//#define THUSTR_20 "level 20: central processing" +extern const char *s_THUSTR_20; // = THUSTR_20; + +//#define THUSTR_21 "level 21: administration center" +extern const char *s_THUSTR_21; // = THUSTR_21; +//#define THUSTR_22 "level 22: habitat" +extern const char *s_THUSTR_22; // = THUSTR_22; +//#define THUSTR_23 "level 23: lunar mining project" +extern const char *s_THUSTR_23; // = THUSTR_23; +//#define THUSTR_24 "level 24: quarry" +extern const char *s_THUSTR_24; // = THUSTR_24; +//#define THUSTR_25 "level 25: baron's den" +extern const char *s_THUSTR_25; // = THUSTR_25; +//#define THUSTR_26 "level 26: ballistyx" +extern const char *s_THUSTR_26; // = THUSTR_26; +//#define THUSTR_27 "level 27: mount pain" +extern const char *s_THUSTR_27; // = THUSTR_27; +//#define THUSTR_28 "level 28: heck" +extern const char *s_THUSTR_28; // = THUSTR_28; +//#define THUSTR_29 "level 29: river styx" +extern const char *s_THUSTR_29; // = THUSTR_29; +//#define THUSTR_30 "level 30: last call" +extern const char *s_THUSTR_30; // = THUSTR_30; + +//#define THUSTR_31 "level 31: pharaoh" +extern const char *s_THUSTR_31; // = THUSTR_31; +//#define THUSTR_32 "level 32: caribbean" +extern const char *s_THUSTR_32; // = THUSTR_32; + +//#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +extern const char *s_HUSTR_CHATMACRO1; // = HUSTR_CHATMACRO1; +//#define HUSTR_CHATMACRO2 "I'm OK." +extern const char *s_HUSTR_CHATMACRO2; // = HUSTR_CHATMACRO2; +//#define HUSTR_CHATMACRO3 "I'm not looking too good!" +extern const char *s_HUSTR_CHATMACRO3; // = HUSTR_CHATMACRO3; +//#define HUSTR_CHATMACRO4 "Help!" +extern const char *s_HUSTR_CHATMACRO4; // = HUSTR_CHATMACRO4; +//#define HUSTR_CHATMACRO5 "You suck!" +extern const char *s_HUSTR_CHATMACRO5; // = HUSTR_CHATMACRO5; +//#define HUSTR_CHATMACRO6 "Next time, scumbag..." +extern const char *s_HUSTR_CHATMACRO6; // = HUSTR_CHATMACRO6; +//#define HUSTR_CHATMACRO7 "Come here!" +extern const char *s_HUSTR_CHATMACRO7; // = HUSTR_CHATMACRO7; +//#define HUSTR_CHATMACRO8 "I'll take care of it." +extern const char *s_HUSTR_CHATMACRO8; // = HUSTR_CHATMACRO8; +//#define HUSTR_CHATMACRO9 "Yes" +extern const char *s_HUSTR_CHATMACRO9; // = HUSTR_CHATMACRO9; +//#define HUSTR_CHATMACRO0 "No" +extern const char *s_HUSTR_CHATMACRO0; // = HUSTR_CHATMACRO0; + +//#define HUSTR_TALKTOSELF1 "You mumble to yourself" +extern const char *s_HUSTR_TALKTOSELF1; // = HUSTR_TALKTOSELF1; +//#define HUSTR_TALKTOSELF2 "Who's there?" +extern const char *s_HUSTR_TALKTOSELF2; // = HUSTR_TALKTOSELF2; +//#define HUSTR_TALKTOSELF3 "You scare yourself" +extern const char *s_HUSTR_TALKTOSELF3; // = HUSTR_TALKTOSELF3; +//#define HUSTR_TALKTOSELF4 "You start to rave" +extern const char *s_HUSTR_TALKTOSELF4; // = HUSTR_TALKTOSELF4; +//#define HUSTR_TALKTOSELF5 "You've lost it..." +extern const char *s_HUSTR_TALKTOSELF5; // = HUSTR_TALKTOSELF5; + +//#define HUSTR_MESSAGESENT "[Message Sent]" +extern const char *s_HUSTR_MESSAGESENT; // = HUSTR_MESSAGESENT; + +// The following should NOT be changed unless it seems +// just AWFULLY necessary + +//#define HUSTR_PLRGREEN "Green: " +extern const char *s_HUSTR_PLRGREEN; // = HUSTR_PLRGREEN; +//#define HUSTR_PLRINDIGO "Indigo: " +extern const char *s_HUSTR_PLRINDIGO; // = HUSTR_PLRINDIGO; +//#define HUSTR_PLRBROWN "Brown: " +extern const char *s_HUSTR_PLRBROWN; // = HUSTR_PLRBROWN; +//#define HUSTR_PLRRED "Red: " +extern const char *s_HUSTR_PLRRED; // = HUSTR_PLRRED; + +// +// AM_map.C +// + +//#define AMSTR_FOLLOWON "Follow Mode ON" +extern const char* s_AMSTR_FOLLOWON; // = AMSTR_FOLLOWON; +//#define AMSTR_FOLLOWOFF "Follow Mode OFF" +extern const char* s_AMSTR_FOLLOWOFF; // = AMSTR_FOLLOWOFF; + +//#define AMSTR_GRIDON "Grid ON" +extern const char* s_AMSTR_GRIDON; // = AMSTR_GRIDON; +//#define AMSTR_GRIDOFF "Grid OFF" +extern const char* s_AMSTR_GRIDOFF; // = AMSTR_GRIDOFF; + +//#define AMSTR_MARKEDSPOT "Marked Spot" +extern const char* s_AMSTR_MARKEDSPOT; // = AMSTR_MARKEDSPOT; +//#define AMSTR_MARKSCLEARED "All Marks Cleared" +extern const char* s_AMSTR_MARKSCLEARED; // = AMSTR_MARKSCLEARED; + +// CPhipps - automap rotate & overlay +extern const char* s_AMSTR_ROTATEON; +extern const char* s_AMSTR_ROTATEOFF; +extern const char* s_AMSTR_OVERLAYON; +extern const char* s_AMSTR_OVERLAYOFF; + +// +// ST_stuff.C +// + +//#define STSTR_MUS "Music Change" +extern const char* s_STSTR_MUS; // = STSTR_MUS; +//#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +extern const char* s_STSTR_NOMUS; // = STSTR_NOMUS; +//#define STSTR_DQDON "Degreelessness Mode On" +extern const char* s_STSTR_DQDON; // = STSTR_DQDON; +//#define STSTR_DQDOFF "Degreelessness Mode Off" +extern const char* s_STSTR_DQDOFF; // = STSTR_DQDOFF; + +//#define STSTR_KFAADDED "Very Happy Ammo Added" +extern const char* s_STSTR_KFAADDED; // = STSTR_KFAADDED; +//#define STSTR_FAADDED "Ammo (no keys) Added" +extern const char* s_STSTR_FAADDED; // = STSTR_FAADDED; + +//#define STSTR_NCON "No Clipping Mode ON" +extern const char* s_STSTR_NCON; // = STSTR_NCON; +//#define STSTR_NCOFF "No Clipping Mode OFF" +extern const char* s_STSTR_NCOFF; // = STSTR_NCOFF; + +//#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +extern const char* s_STSTR_BEHOLD; // = STSTR_BEHOLD; +//#define STSTR_BEHOLDX "Power-up Toggled" +extern const char* s_STSTR_BEHOLDX; // = STSTR_BEHOLDX; + +//#define STSTR_CHOPPERS "... doesn't suck - GM" +extern const char* s_STSTR_CHOPPERS; // = STSTR_CHOPPERS; +//#define STSTR_CLEV "Changing Level..." +extern const char* s_STSTR_CLEV; // = STSTR_CLEV; + +// +// F_Finale.C +// +/* +#define E1TEXT \ +"Once you beat the big badasses and\n"\ +"clean out the moon base you're supposed\n"\ +"to win, aren't you? Aren't you? Where's\n"\ +"your fat reward and ticket home? What\n"\ +"the hell is this? It's not supposed to\n"\ +"end this way!\n"\ +"\n" \ +"It stinks like rotten meat, but looks\n"\ +"like the lost Deimos base. Looks like\n"\ +"you're stuck on The Shores of Hell.\n"\ +"The only way out is through.\n"\ +"\n"\ +"To continue the DOOM experience, play\n"\ +"The Shores of Hell and its amazing\n"\ +"sequel, Inferno!\n" +*/ +extern const char* s_E1TEXT; // = E1TEXT; + + +/* +#define E2TEXT \ +"You've done it! The hideous cyber-\n"\ +"demon lord that ruled the lost Deimos\n"\ +"moon base has been slain and you\n"\ +"are triumphant! But ... where are\n"\ +"you? You clamber to the edge of the\n"\ +"moon and look down to see the awful\n"\ +"truth.\n" \ +"\n"\ +"Deimos floats above Hell itself!\n"\ +"You've never heard of anyone escaping\n"\ +"from Hell, but you'll make the bastards\n"\ +"sorry they ever heard of you! Quickly,\n"\ +"you rappel down to the surface of\n"\ +"Hell.\n"\ +"\n" \ +"Now, it's on to the final chapter of\n"\ +"DOOM! -- Inferno." +*/ +extern const char* s_E2TEXT; // = E2TEXT; + + +/* +#define E3TEXT \ +"The loathsome spiderdemon that\n"\ +"masterminded the invasion of the moon\n"\ +"bases and caused so much death has had\n"\ +"its ass kicked for all time.\n"\ +"\n"\ +"A hidden doorway opens and you enter.\n"\ +"You've proven too tough for Hell to\n"\ +"contain, and now Hell at last plays\n"\ +"fair -- for you emerge from the door\n"\ +"to see the green fields of Earth!\n"\ +"Home at last.\n" \ +"\n"\ +"You wonder what's been happening on\n"\ +"Earth while you were battling evil\n"\ +"unleashed. It's good that no Hell-\n"\ +"spawn could have come through that\n"\ +"door with you ..." +*/ +extern const char* s_E3TEXT; // = E3TEXT; + + +/* +#define E4TEXT \ +"the spider mastermind must have sent forth\n"\ +"its legions of hellspawn before your\n"\ +"final confrontation with that terrible\n"\ +"beast from hell. but you stepped forward\n"\ +"and brought forth eternal damnation and\n"\ +"suffering upon the horde as a true hero\n"\ +"would in the face of something so evil.\n"\ +"\n"\ +"besides, someone was gonna pay for what\n"\ +"happened to daisy, your pet rabbit.\n"\ +"\n"\ +"but now, you see spread before you more\n"\ +"potential pain and gibbitude as a nation\n"\ +"of demons run amok among our cities.\n"\ +"\n"\ +"next stop, hell on earth!" +*/ +extern const char* s_E4TEXT; // = E4TEXT; + + +// after level 6, put this: + +/* +#define C1TEXT \ +"YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \ +"STARPORT. BUT SOMETHING IS WRONG. THE\n" \ +"MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \ +"WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \ +"IS BEING SUBVERTED BY THEIR PRESENCE.\n" \ +"\n"\ +"AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \ +"FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \ +"YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \ +"OF THE STARBASE AND FIND THE CONTROLLING\n" \ +"SWITCH WHICH HOLDS EARTH'S POPULATION\n" \ +"HOSTAGE." +*/ +extern const char* s_C1TEXT; // = C1TEXT; + +// After level 11, put this: + +/* +#define C2TEXT \ +"YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \ +"HUMANKIND TO EVACUATE EARTH AND ESCAPE\n"\ +"THE NIGHTMARE. NOW YOU ARE THE ONLY\n"\ +"HUMAN LEFT ON THE FACE OF THE PLANET.\n"\ +"CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n"\ +"AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n"\ +"YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n"\ +"THAT YOU HAVE SAVED YOUR SPECIES.\n"\ +"\n"\ +"BUT THEN, EARTH CONTROL BEAMS DOWN A\n"\ +"MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n"\ +"THE SOURCE OF THE ALIEN INVASION. IF YOU\n"\ +"GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n"\ +"ENTRY. THE ALIEN BASE IS IN THE HEART OF\n"\ +"YOUR OWN HOME CITY, NOT FAR FROM THE\n"\ +"STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n"\ +"UP AND RETURN TO THE FRAY." +*/ +extern const char* s_C2TEXT; // = C2TEXT; + + +// After level 20, put this: + +/* +#define C3TEXT \ +"YOU ARE AT THE CORRUPT HEART OF THE CITY,\n"\ +"SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n"\ +"YOU SEE NO WAY TO DESTROY THE CREATURES'\n"\ +"ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n"\ +"TEETH AND PLUNGE THROUGH IT.\n"\ +"\n"\ +"THERE MUST BE A WAY TO CLOSE IT ON THE\n"\ +"OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n"\ +"GOT TO GO THROUGH HELL TO GET TO IT?" +*/ +extern const char* s_C3TEXT; // = C3TEXT; + + +// After level 29, put this: + +/* +#define C4TEXT \ +"THE HORRENDOUS VISAGE OF THE BIGGEST\n"\ +"DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n"\ +"YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n"\ +"HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n"\ +"UP AND DIES, ITS THRASHING LIMBS\n"\ +"DEVASTATING UNTOLD MILES OF HELL'S\n"\ +"SURFACE.\n"\ +"\n"\ +"YOU'VE DONE IT. THE INVASION IS OVER.\n"\ +"EARTH IS SAVED. HELL IS A WRECK. YOU\n"\ +"WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n"\ +"DIE, NOW. WIPING THE SWEAT FROM YOUR\n"\ +"FOREHEAD YOU BEGIN THE LONG TREK BACK\n"\ +"HOME. REBUILDING EARTH OUGHT TO BE A\n"\ +"LOT MORE FUN THAN RUINING IT WAS.\n" +*/ +extern const char* s_C4TEXT; // = C4TEXT; + + + +// Before level 31, put this: + +/* +#define C5TEXT \ +"CONGRATULATIONS, YOU'VE FOUND THE SECRET\n"\ +"LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n"\ +"HUMANS, RATHER THAN DEMONS. YOU WONDER\n"\ +"WHO THE INMATES OF THIS CORNER OF HELL\n"\ +"WILL BE." +*/ +extern const char* s_C5TEXT; // = C5TEXT; + + +// Before level 32, put this: + +/* +#define C6TEXT \ +"CONGRATULATIONS, YOU'VE FOUND THE\n"\ +"SUPER SECRET LEVEL! YOU'D BETTER\n"\ +"BLAZE THROUGH THIS ONE!\n" +*/ +extern const char* s_C6TEXT; // = C6TEXT; + + +// after map 06 + +/* +#define P1TEXT \ +"You gloat over the steaming carcass of the\n"\ +"Guardian. With its death, you've wrested\n"\ +"the Accelerator from the stinking claws\n"\ +"of Hell. You relax and glance around the\n"\ +"room. Damn! There was supposed to be at\n"\ +"least one working prototype, but you can't\n"\ +"see it. The demons must have taken it.\n"\ +"\n"\ +"You must find the prototype, or all your\n"\ +"struggles will have been wasted. Keep\n"\ +"moving, keep fighting, keep killing.\n"\ +"Oh yes, keep living, too." +*/ +extern const char* s_P1TEXT; // = P1TEXT; + + +// after map 11 + +/* +#define P2TEXT \ +"Even the deadly Arch-Vile labyrinth could\n"\ +"not stop you, and you've gotten to the\n"\ +"prototype Accelerator which is soon\n"\ +"efficiently and permanently deactivated.\n"\ +"\n"\ +"You're good at that kind of thing." +*/ +extern const char* s_P2TEXT; // = P2TEXT; + + +// after map 20 + +/* +#define P3TEXT \ +"You've bashed and battered your way into\n"\ +"the heart of the devil-hive. Time for a\n"\ +"Search-and-Destroy mission, aimed at the\n"\ +"Gatekeeper, whose foul offspring is\n"\ +"cascading to Earth. Yeah, he's bad. But\n"\ +"you know who's worse!\n"\ +"\n"\ +"Grinning evilly, you check your gear, and\n"\ +"get ready to give the bastard a little Hell\n"\ +"of your own making!" +*/ +extern const char* s_P3TEXT; // = P3TEXT; + +// after map 30 + +/* +#define P4TEXT \ +"The Gatekeeper's evil face is splattered\n"\ +"all over the place. As its tattered corpse\n"\ +"collapses, an inverted Gate forms and\n"\ +"sucks down the shards of the last\n"\ +"prototype Accelerator, not to mention the\n"\ +"few remaining demons. You're done. Hell\n"\ +"has gone back to pounding bad dead folks \n"\ +"instead of good live ones. Remember to\n"\ +"tell your grandkids to put a rocket\n"\ +"launcher in your coffin. If you go to Hell\n"\ +"when you die, you'll need it for some\n"\ +"final cleaning-up ..." +*/ +extern const char* s_P4TEXT; // = P4TEXT; + +// before map 31 + +/* +#define P5TEXT \ +"You've found the second-hardest level we\n"\ +"got. Hope you have a saved game a level or\n"\ +"two previous. If not, be prepared to die\n"\ +"aplenty. For master marines only." +*/ +extern const char* s_P5TEXT; // = P5TEXT; + +// before map 32 + +/* +#define P6TEXT \ +"Betcha wondered just what WAS the hardest\n"\ +"level we had ready for ya? Now you know.\n"\ +"No one gets out alive." +*/ +extern const char* s_P6TEXT; // = P6TEXT; + + +/* +#define T1TEXT \ +"You've fought your way out of the infested\n"\ +"experimental labs. It seems that UAC has\n"\ +"once again gulped it down. With their\n"\ +"high turnover, it must be hard for poor\n"\ +"old UAC to buy corporate health insurance\n"\ +"nowadays..\n"\ +"\n"\ +"Ahead lies the military complex, now\n"\ +"swarming with diseased horrors hot to get\n"\ +"their teeth into you. With luck, the\n"\ +"complex still has some warlike ordnance\n"\ +"laying around." +*/ +extern const char* s_T1TEXT; // = T1TEXT; + + +/* +#define T2TEXT \ +"You hear the grinding of heavy machinery\n"\ +"ahead. You sure hope they're not stamping\n"\ +"out new hellspawn, but you're ready to\n"\ +"ream out a whole herd if you have to.\n"\ +"They might be planning a blood feast, but\n"\ +"you feel about as mean as two thousand\n"\ +"maniacs packed into one mad killer.\n"\ +"\n"\ +"You don't plan to go down easy." +*/ +extern const char* s_T2TEXT; // = T2TEXT; + + +/* +#define T3TEXT \ +"The vista opening ahead looks real damn\n"\ +"familiar. Smells familiar, too -- like\n"\ +"fried excrement. You didn't like this\n"\ +"place before, and you sure as hell ain't\n"\ +"planning to like it now. The more you\n"\ +"brood on it, the madder you get.\n"\ +"Hefting your gun, an evil grin trickles\n"\ +"onto your face. Time to take some names." +*/ +extern const char* s_T3TEXT; // = T3TEXT; + +/* +#define T4TEXT \ +"Suddenly, all is silent, from one horizon\n"\ +"to the other. The agonizing echo of Hell\n"\ +"fades away, the nightmare sky turns to\n"\ +"blue, the heaps of monster corpses start \n"\ +"to evaporate along with the evil stench \n"\ +"that filled the air. Jeeze, maybe you've\n"\ +"done it. Have you really won?\n"\ +"\n"\ +"Something rumbles in the distance.\n"\ +"A blue light begins to glow inside the\n"\ +"ruined skull of the demon-spitter." +*/ +extern const char* s_T4TEXT; // = T4TEXT; + + +/* +#define T5TEXT \ +"What now? Looks totally different. Kind\n"\ +"of like King Tut's condo. Well,\n"\ +"whatever's here can't be any worse\n"\ +"than usual. Can it? Or maybe it's best\n"\ +"to let sleeping gods lie.." +*/ +extern const char* s_T5TEXT; // = T5TEXT; + + +/* +#define T6TEXT \ +"Time for a vacation. You've burst the\n"\ +"bowels of hell and by golly you're ready\n"\ +"for a break. You mutter to yourself,\n"\ +"Maybe someone else can kick Hell's ass\n"\ +"next time around. Ahead lies a quiet town,\n"\ +"with peaceful flowing water, quaint\n"\ +"buildings, and presumably no Hellspawn.\n"\ +"\n"\ +"As you step off the transport, you hear\n"\ +"the stomp of a cyberdemon's iron shoe." +*/ +extern const char* s_T6TEXT; // = T6TEXT; + +// +// Character cast strings F_FINALE.C +// +//#define CC_ZOMBIE "ZOMBIEMAN" +extern const char* s_CC_ZOMBIE; // = CC_ZOMBIE; +//#define CC_SHOTGUN "SHOTGUN GUY" +extern const char* s_CC_SHOTGUN; // = CC_SHOTGUN; +//#define CC_HEAVY "HEAVY WEAPON DUDE" +extern const char* s_CC_HEAVY; // = CC_HEAVY; +//#define CC_IMP "IMP" +extern const char* s_CC_IMP; // = CC_IMP; +//#define CC_DEMON "DEMON" +extern const char* s_CC_DEMON; // = CC_DEMON; +//#define CC_LOST "LOST SOUL" +extern const char* s_CC_LOST; // = CC_LOST; +//#define CC_CACO "CACODEMON" +extern const char* s_CC_CACO; // = CC_CACO; +//#define CC_HELL "HELL KNIGHT" +extern const char* s_CC_HELL; // = CC_HELL; +//#define CC_BARON "BARON OF HELL" +extern const char* s_CC_BARON; // = CC_BARON; +//#define CC_ARACH "ARACHNOTRON" +extern const char* s_CC_ARACH; // = CC_ARACH; +//#define CC_PAIN "PAIN ELEMENTAL" +extern const char* s_CC_PAIN; // = CC_PAIN; +//#define CC_REVEN "REVENANT" +extern const char* s_CC_REVEN; // = CC_REVEN; +//#define CC_MANCU "MANCUBUS" +extern const char* s_CC_MANCU; // = CC_MANCU; +//#define CC_ARCH "ARCH-VILE" +extern const char* s_CC_ARCH; // = CC_ARCH; +//#define CC_SPIDER "THE SPIDER MASTERMIND" +extern const char* s_CC_SPIDER; // = CC_SPIDER; +//#define CC_CYBER "THE CYBERDEMON" +extern const char* s_CC_CYBER; // = CC_CYBER; +//#define CC_HERO "OUR HERO" +extern const char* s_CC_HERO; // = CC_HERO; + +// Ty 03/30/98 - new substitutions for background textures during int screens +// char* bgflatE1 = "FLOOR4_8"; +extern const char* bgflatE1; +// char* bgflatE2 = "SFLR6_1"; +extern const char* bgflatE2; +// char* bgflatE3 = "MFLR8_4"; +extern const char* bgflatE3; +// char* bgflatE4 = "MFLR8_3"; +extern const char* bgflatE4; + +// char* bgflat06 = "SLIME16"; +extern const char* bgflat06; +// char* bgflat11 = "RROCK14"; +extern const char* bgflat11; +// char* bgflat20 = "RROCK07"; +extern const char* bgflat20; +// char* bgflat30 = "RROCK17"; +extern const char* bgflat30; +// char* bgflat15 = "RROCK13"; +extern const char* bgflat15; +// char* bgflat31 = "RROCK19"; +extern const char* bgflat31; + +// char* bgcastcall = "BOSSBACK"; // panel behind cast call +extern const char* bgcastcall; + +// ignored if blank, general purpose startup announcements +// char* startup1 = ""; +extern const char* startup1; +// char* startup2 = ""; +extern const char* startup2; +// char* startup3 = ""; +extern const char* startup3; +// char* startup4 = ""; +extern const char* startup4; +// char* startup5 = ""; +extern const char* startup5; + +// from g_game.c, prefix for savegame name like "boomsav" +extern const char* savegamename; + +void D_BuildBEXTables(void); + +#endif diff --git a/src/d_englsh.h b/src/d_englsh.h new file mode 100644 index 00000000..86e74164 --- /dev/null +++ b/src/d_englsh.h @@ -0,0 +1,707 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Printed strings for translation. + * English language support (default). + * See dstrings.h for suggestions about foreign language BEX support + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_ENGLSH__ +#define __D_ENGLSH__ + +/* d_main.c */ +#define D_DEVSTR "Development mode ON.\n" +#define D_CDROM "CD-ROM Version: default.cfg from c:\\doomdata\n" + +/* m_menu.c */ +#define PRESSKEY "press a key." +#define PRESSYN "press y or n." +#define QUITMSG "are you sure you want to\nquit this great game?" +#define LOADNET "you can't do load while in a net game!\n\n"PRESSKEY +#define QLOADNET "you can't quickload during a netgame!\n\n"PRESSKEY +#define QSAVESPOT "you haven't picked a quicksave slot yet!\n\n"PRESSKEY +#define SAVEDEAD "you can't save if you aren't playing!\n\n"PRESSKEY +#define QSPROMPT "quicksave over your game named\n\n'%s'?\n\n"PRESSYN +#define QLPROMPT "do you want to quickload the game named\n\n'%s'?\n\n"PRESSYN + +#define NEWGAME \ + "you can't start a new game\n"\ + "while in a network game.\n\n"PRESSKEY + +#define NIGHTMARE \ + "are you sure? this skill level\n"\ + "isn't even remotely fair.\n\n"PRESSYN + +#define SWSTRING \ + "this is the shareware version of doom.\n\n"\ + "you need to order the entire trilogy.\n\n"PRESSKEY + +#define MSGOFF "Messages OFF" +#define MSGON "Messages ON" +#define NETEND "you can't end a netgame!\n\n"PRESSKEY +#define ENDGAME "are you sure you want to end the game?\n\n"PRESSYN +#define RESTARTLEVEL "restart the level?\n\n"PRESSYN + +#define DOSY "(press y to quit)" + +#define DETAILHI "High detail" +#define DETAILLO "Low detail" +#define GAMMALVL0 "Gamma correction OFF" +#define GAMMALVL1 "Gamma correction level 1" +#define GAMMALVL2 "Gamma correction level 2" +#define GAMMALVL3 "Gamma correction level 3" +#define GAMMALVL4 "Gamma correction level 4" +#define EMPTYSTRING "empty slot" + +/* p_inter.c */ +#define GOTARMOR "Picked up the armor." +#define GOTMEGA "Picked up the MegaArmor!" +#define GOTHTHBONUS "Picked up a health bonus." +#define GOTARMBONUS "Picked up an armor bonus." +#define GOTSTIM "Picked up a stimpack." +#define GOTMEDINEED "Picked up a medikit that you REALLY need!" +#define GOTMEDIKIT "Picked up a medikit." +#define GOTSUPER "Supercharge!" + +#define GOTBLUECARD "Picked up a blue keycard." +#define GOTYELWCARD "Picked up a yellow keycard." +#define GOTREDCARD "Picked up a red keycard." +#define GOTBLUESKUL "Picked up a blue skull key." +#define GOTYELWSKUL "Picked up a yellow skull key." +#define GOTREDSKULL "Picked up a red skull key." + +#define GOTINVUL "Invulnerability!" +#define GOTBERSERK "Berserk!" +#define GOTINVIS "Partial Invisibility" +#define GOTSUIT "Radiation Shielding Suit" +#define GOTMAP "Computer Area Map" +#define GOTVISOR "Light Amplification Visor" +#define GOTMSPHERE "MegaSphere!" + +#define GOTCLIP "Picked up a clip." +#define GOTCLIPBOX "Picked up a box of bullets." +#define GOTROCKET "Picked up a rocket." +#define GOTROCKBOX "Picked up a box of rockets." +#define GOTCELL "Picked up an energy cell." +#define GOTCELLBOX "Picked up an energy cell pack." +#define GOTSHELLS "Picked up 4 shotgun shells." +#define GOTSHELLBOX "Picked up a box of shotgun shells." +#define GOTBACKPACK "Picked up a backpack full of ammo!" + +#define GOTBFG9000 "You got the BFG9000! Oh, yes." +#define GOTCHAINGUN "You got the chaingun!" +#define GOTCHAINSAW "A chainsaw! Find some meat!" +#define GOTLAUNCHER "You got the rocket launcher!" +#define GOTPLASMA "You got the plasma gun!" +#define GOTSHOTGUN "You got the shotgun!" +#define GOTSHOTGUN2 "You got the super shotgun!" + +/* p_doors.c */ +#define PD_BLUEO "You need a blue key to activate this object" +#define PD_REDO "You need a red key to activate this object" +#define PD_YELLOWO "You need a yellow key to activate this object" +#define PD_BLUEK "You need a blue key to open this door" +#define PD_REDK "You need a red key to open this door" +#define PD_YELLOWK "You need a yellow key to open this door" +/* jff 02/05/98 Create messages specific to card and skull keys */ +#define PD_BLUEC "You need a blue card to open this door" +#define PD_REDC "You need a red card to open this door" +#define PD_YELLOWC "You need a yellow card to open this door" +#define PD_BLUES "You need a blue skull to open this door" +#define PD_REDS "You need a red skull to open this door" +#define PD_YELLOWS "You need a yellow skull to open this door" +#define PD_ANY "Any key will open this door" +#define PD_ALL3 "You need all three keys to open this door" +#define PD_ALL6 "You need all six keys to open this door" + +/* g_game.c */ +#define GGSAVED "game saved." + +/* hu_stuff.c */ +#define HUSTR_MSGU "[Message unsent]" + +#define HUSTR_E1M1 "E1M1: Hangar" +#define HUSTR_E1M2 "E1M2: Nuclear Plant" +#define HUSTR_E1M3 "E1M3: Toxin Refinery" +#define HUSTR_E1M4 "E1M4: Command Control" +#define HUSTR_E1M5 "E1M5: Phobos Lab" +#define HUSTR_E1M6 "E1M6: Central Processing" +#define HUSTR_E1M7 "E1M7: Computer Station" +#define HUSTR_E1M8 "E1M8: Phobos Anomaly" +#define HUSTR_E1M9 "E1M9: Military Base" + +#define HUSTR_E2M1 "E2M1: Deimos Anomaly" +#define HUSTR_E2M2 "E2M2: Containment Area" +#define HUSTR_E2M3 "E2M3: Refinery" +#define HUSTR_E2M4 "E2M4: Deimos Lab" +#define HUSTR_E2M5 "E2M5: Command Center" +#define HUSTR_E2M6 "E2M6: Halls of the Damned" +#define HUSTR_E2M7 "E2M7: Spawning Vats" +#define HUSTR_E2M8 "E2M8: Tower of Babel" +#define HUSTR_E2M9 "E2M9: Fortress of Mystery" + +#define HUSTR_E3M1 "E3M1: Hell Keep" +#define HUSTR_E3M2 "E3M2: Slough of Despair" +#define HUSTR_E3M3 "E3M3: Pandemonium" +#define HUSTR_E3M4 "E3M4: House of Pain" +#define HUSTR_E3M5 "E3M5: Unholy Cathedral" +#define HUSTR_E3M6 "E3M6: Mt. Erebus" +#define HUSTR_E3M7 "E3M7: Limbo" +#define HUSTR_E3M8 "E3M8: Dis" +#define HUSTR_E3M9 "E3M9: Warrens" + +#define HUSTR_E4M1 "E4M1: Hell Beneath" +#define HUSTR_E4M2 "E4M2: Perfect Hatred" +#define HUSTR_E4M3 "E4M3: Sever The Wicked" +#define HUSTR_E4M4 "E4M4: Unruly Evil" +#define HUSTR_E4M5 "E4M5: They Will Repent" +#define HUSTR_E4M6 "E4M6: Against Thee Wickedly" +#define HUSTR_E4M7 "E4M7: And Hell Followed" +#define HUSTR_E4M8 "E4M8: Unto The Cruel" +#define HUSTR_E4M9 "E4M9: Fear" + +#define HUSTR_1 "level 1: entryway" +#define HUSTR_2 "level 2: underhalls" +#define HUSTR_3 "level 3: the gantlet" +#define HUSTR_4 "level 4: the focus" +#define HUSTR_5 "level 5: the waste tunnels" +#define HUSTR_6 "level 6: the crusher" +#define HUSTR_7 "level 7: dead simple" +#define HUSTR_8 "level 8: tricks and traps" +#define HUSTR_9 "level 9: the pit" +#define HUSTR_10 "level 10: refueling base" +#define HUSTR_11 "level 11: 'o' of destruction!" + +#define HUSTR_12 "level 12: the factory" +#define HUSTR_13 "level 13: downtown" +#define HUSTR_14 "level 14: the inmost dens" +#define HUSTR_15 "level 15: industrial zone" +#define HUSTR_16 "level 16: suburbs" +#define HUSTR_17 "level 17: tenements" +#define HUSTR_18 "level 18: the courtyard" +#define HUSTR_19 "level 19: the citadel" +#define HUSTR_20 "level 20: gotcha!" + +#define HUSTR_21 "level 21: nirvana" +#define HUSTR_22 "level 22: the catacombs" +#define HUSTR_23 "level 23: barrels o' fun" +#define HUSTR_24 "level 24: the chasm" +#define HUSTR_25 "level 25: bloodfalls" +#define HUSTR_26 "level 26: the abandoned mines" +#define HUSTR_27 "level 27: monster condo" +#define HUSTR_28 "level 28: the spirit world" +#define HUSTR_29 "level 29: the living end" +#define HUSTR_30 "level 30: icon of sin" + +#define HUSTR_31 "level 31: wolfenstein" +#define HUSTR_32 "level 32: grosse" + +#define PHUSTR_1 "level 1: congo" +#define PHUSTR_2 "level 2: well of souls" +#define PHUSTR_3 "level 3: aztec" +#define PHUSTR_4 "level 4: caged" +#define PHUSTR_5 "level 5: ghost town" +#define PHUSTR_6 "level 6: baron's lair" +#define PHUSTR_7 "level 7: caughtyard" +#define PHUSTR_8 "level 8: realm" +#define PHUSTR_9 "level 9: abattoire" +#define PHUSTR_10 "level 10: onslaught" +#define PHUSTR_11 "level 11: hunted" + +#define PHUSTR_12 "level 12: speed" +#define PHUSTR_13 "level 13: the crypt" +#define PHUSTR_14 "level 14: genesis" +#define PHUSTR_15 "level 15: the twilight" +#define PHUSTR_16 "level 16: the omen" +#define PHUSTR_17 "level 17: compound" +#define PHUSTR_18 "level 18: neurosphere" +#define PHUSTR_19 "level 19: nme" +#define PHUSTR_20 "level 20: the death domain" + +#define PHUSTR_21 "level 21: slayer" +#define PHUSTR_22 "level 22: impossible mission" +#define PHUSTR_23 "level 23: tombstone" +#define PHUSTR_24 "level 24: the final frontier" +#define PHUSTR_25 "level 25: the temple of darkness" +#define PHUSTR_26 "level 26: bunker" +#define PHUSTR_27 "level 27: anti-christ" +#define PHUSTR_28 "level 28: the sewers" +#define PHUSTR_29 "level 29: odyssey of noises" +#define PHUSTR_30 "level 30: the gateway of hell" + +#define PHUSTR_31 "level 31: cyberden" +#define PHUSTR_32 "level 32: go 2 it" + +#define THUSTR_1 "level 1: system control" +#define THUSTR_2 "level 2: human bbq" +#define THUSTR_3 "level 3: power control" +#define THUSTR_4 "level 4: wormhole" +#define THUSTR_5 "level 5: hanger" +#define THUSTR_6 "level 6: open season" +#define THUSTR_7 "level 7: prison" +#define THUSTR_8 "level 8: metal" +#define THUSTR_9 "level 9: stronghold" +#define THUSTR_10 "level 10: redemption" +#define THUSTR_11 "level 11: storage facility" + +#define THUSTR_12 "level 12: crater" +#define THUSTR_13 "level 13: nukage processing" +#define THUSTR_14 "level 14: steel works" +#define THUSTR_15 "level 15: dead zone" +#define THUSTR_16 "level 16: deepest reaches" +#define THUSTR_17 "level 17: processing area" +#define THUSTR_18 "level 18: mill" +#define THUSTR_19 "level 19: shipping/respawning" +#define THUSTR_20 "level 20: central processing" + +#define THUSTR_21 "level 21: administration center" +#define THUSTR_22 "level 22: habitat" +#define THUSTR_23 "level 23: lunar mining project" +#define THUSTR_24 "level 24: quarry" +#define THUSTR_25 "level 25: baron's den" +#define THUSTR_26 "level 26: ballistyx" +#define THUSTR_27 "level 27: mount pain" +#define THUSTR_28 "level 28: heck" +#define THUSTR_29 "level 29: river styx" +#define THUSTR_30 "level 30: last call" + +#define THUSTR_31 "level 31: pharaoh" +#define THUSTR_32 "level 32: caribbean" + +#define HUSTR_CHATMACRO1 "I'm ready to kick butt!" +#define HUSTR_CHATMACRO2 "I'm OK." +#define HUSTR_CHATMACRO3 "I'm not looking too good!" +#define HUSTR_CHATMACRO4 "Help!" +#define HUSTR_CHATMACRO5 "You suck!" +#define HUSTR_CHATMACRO6 "Next time, scumbag..." +#define HUSTR_CHATMACRO7 "Come here!" +#define HUSTR_CHATMACRO8 "I'll take care of it." +#define HUSTR_CHATMACRO9 "Yes" +#define HUSTR_CHATMACRO0 "No" + +#define HUSTR_TALKTOSELF1 "You mumble to yourself" +#define HUSTR_TALKTOSELF2 "Who's there?" +#define HUSTR_TALKTOSELF3 "You scare yourself" +#define HUSTR_TALKTOSELF4 "You start to rave" +#define HUSTR_TALKTOSELF5 "You've lost it..." + +#define HUSTR_MESSAGESENT "[Message Sent]" + +/* The following should NOT be changed unless it seems + * just AWFULLY necessary */ + +#define HUSTR_PLRGREEN "Player 1: " +#define HUSTR_PLRINDIGO "Player 2: " +#define HUSTR_PLRBROWN "Player 3: " +#define HUSTR_PLRRED "Player 4: " + +#define HUSTR_KEYGREEN 'g' +#define HUSTR_KEYINDIGO 'i' +#define HUSTR_KEYBROWN 'b' +#define HUSTR_KEYRED 'r' + +/* am_map.c */ + +#define AMSTR_FOLLOWON "Follow Mode ON" +#define AMSTR_FOLLOWOFF "Follow Mode OFF" + +#define AMSTR_GRIDON "Grid ON" +#define AMSTR_GRIDOFF "Grid OFF" + +#define AMSTR_MARKEDSPOT "Marked Spot" +#define AMSTR_MARKSCLEARED "All Marks Cleared" + +#define AMSTR_ROTATEON "Rotate Mode ON" +#define AMSTR_ROTATEOFF "Rotate Mode OFF" + +#define AMSTR_OVERLAYON "Overlay Mode ON" +#define AMSTR_OVERLAYOFF "Overlay Mode OFF" + +/* st_stuff.c */ + +#define STSTR_MUS "Music Change" +#define STSTR_NOMUS "IMPOSSIBLE SELECTION" +#define STSTR_DQDON "Degreelessness Mode On" +#define STSTR_DQDOFF "Degreelessness Mode Off" + +#define STSTR_KFAADDED "Very Happy Ammo Added" +#define STSTR_FAADDED "Ammo (no keys) Added" + +#define STSTR_NCON "No Clipping Mode ON" +#define STSTR_NCOFF "No Clipping Mode OFF" + +#define STSTR_BEHOLD "inVuln, Str, Inviso, Rad, Allmap, or Lite-amp" +#define STSTR_BEHOLDX "Power-up Toggled" + +#define STSTR_CHOPPERS "... doesn't suck - GM" +#define STSTR_CLEV "Changing Level..." + +#define STSTR_COMPON "Compatibility Mode On" /* phares */ +#define STSTR_COMPOFF "Compatibility Mode Off" /* phares */ + +/* f_finale.c */ + +#define E1TEXT \ + "Once you beat the big badasses and\n"\ + "clean out the moon base you're supposed\n"\ + "to win, aren't you? Aren't you? Where's\n"\ + "your fat reward and ticket home? What\n"\ + "the hell is this? It's not supposed to\n"\ + "end this way!\n"\ + "\n" \ + "It stinks like rotten meat, but looks\n"\ + "like the lost Deimos base. Looks like\n"\ + "you're stuck on The Shores of Hell.\n"\ + "The only way out is through.\n"\ + "\n"\ + "To continue the DOOM experience, play\n"\ + "The Shores of Hell and its amazing\n"\ + "sequel, Inferno!\n" + + +#define E2TEXT \ + "You've done it! The hideous cyber-\n"\ + "demon lord that ruled the lost Deimos\n"\ + "moon base has been slain and you\n"\ + "are triumphant! But ... where are\n"\ + "you? You clamber to the edge of the\n"\ + "moon and look down to see the awful\n"\ + "truth.\n" \ + "\n"\ + "Deimos floats above Hell itself!\n"\ + "You've never heard of anyone escaping\n"\ + "from Hell, but you'll make the bastards\n"\ + "sorry they ever heard of you! Quickly,\n"\ + "you rappel down to the surface of\n"\ + "Hell.\n"\ + "\n" \ + "Now, it's on to the final chapter of\n"\ + "DOOM! -- Inferno." + + +#define E3TEXT \ + "The loathsome spiderdemon that\n"\ + "masterminded the invasion of the moon\n"\ + "bases and caused so much death has had\n"\ + "its ass kicked for all time.\n"\ + "\n"\ + "A hidden doorway opens and you enter.\n"\ + "You've proven too tough for Hell to\n"\ + "contain, and now Hell at last plays\n"\ + "fair -- for you emerge from the door\n"\ + "to see the green fields of Earth!\n"\ + "Home at last.\n" \ + "\n"\ + "You wonder what's been happening on\n"\ + "Earth while you were battling evil\n"\ + "unleashed. It's good that no Hell-\n"\ + "spawn could have come through that\n"\ + "door with you ..." + + +#define E4TEXT \ + "the spider mastermind must have sent forth\n"\ + "its legions of hellspawn before your\n"\ + "final confrontation with that terrible\n"\ + "beast from hell. but you stepped forward\n"\ + "and brought forth eternal damnation and\n"\ + "suffering upon the horde as a true hero\n"\ + "would in the face of something so evil.\n"\ + "\n"\ + "besides, someone was gonna pay for what\n"\ + "happened to daisy, your pet rabbit.\n"\ + "\n"\ + "but now, you see spread before you more\n"\ + "potential pain and gibbitude as a nation\n"\ + "of demons run amok among our cities.\n"\ + "\n"\ + "next stop, hell on earth!" + + +/* after level 6, put this: */ + +#define C1TEXT \ + "YOU HAVE ENTERED DEEPLY INTO THE INFESTED\n" \ + "STARPORT. BUT SOMETHING IS WRONG. THE\n" \ + "MONSTERS HAVE BROUGHT THEIR OWN REALITY\n" \ + "WITH THEM, AND THE STARPORT'S TECHNOLOGY\n" \ + "IS BEING SUBVERTED BY THEIR PRESENCE.\n" \ + "\n"\ + "AHEAD, YOU SEE AN OUTPOST OF HELL, A\n" \ + "FORTIFIED ZONE. IF YOU CAN GET PAST IT,\n" \ + "YOU CAN PENETRATE INTO THE HAUNTED HEART\n" \ + "OF THE STARBASE AND FIND THE CONTROLLING\n" \ + "SWITCH WHICH HOLDS EARTH'S POPULATION\n" \ + "HOSTAGE." + +/* After level 11, put this: */ + +#define C2TEXT \ + "YOU HAVE WON! YOUR VICTORY HAS ENABLED\n" \ + "HUMANKIND TO EVACUATE EARTH AND ESCAPE\n"\ + "THE NIGHTMARE. NOW YOU ARE THE ONLY\n"\ + "HUMAN LEFT ON THE FACE OF THE PLANET.\n"\ + "CANNIBAL MUTATIONS, CARNIVOROUS ALIENS,\n"\ + "AND EVIL SPIRITS ARE YOUR ONLY NEIGHBORS.\n"\ + "YOU SIT BACK AND WAIT FOR DEATH, CONTENT\n"\ + "THAT YOU HAVE SAVED YOUR SPECIES.\n"\ + "\n"\ + "BUT THEN, EARTH CONTROL BEAMS DOWN A\n"\ + "MESSAGE FROM SPACE: \"SENSORS HAVE LOCATED\n"\ + "THE SOURCE OF THE ALIEN INVASION. IF YOU\n"\ + "GO THERE, YOU MAY BE ABLE TO BLOCK THEIR\n"\ + "ENTRY. THE ALIEN BASE IS IN THE HEART OF\n"\ + "YOUR OWN HOME CITY, NOT FAR FROM THE\n"\ + "STARPORT.\" SLOWLY AND PAINFULLY YOU GET\n"\ + "UP AND RETURN TO THE FRAY." + + +/* After level 20, put this: */ + +#define C3TEXT \ + "YOU ARE AT THE CORRUPT HEART OF THE CITY,\n"\ + "SURROUNDED BY THE CORPSES OF YOUR ENEMIES.\n"\ + "YOU SEE NO WAY TO DESTROY THE CREATURES'\n"\ + "ENTRYWAY ON THIS SIDE, SO YOU CLENCH YOUR\n"\ + "TEETH AND PLUNGE THROUGH IT.\n"\ + "\n"\ + "THERE MUST BE A WAY TO CLOSE IT ON THE\n"\ + "OTHER SIDE. WHAT DO YOU CARE IF YOU'VE\n"\ + "GOT TO GO THROUGH HELL TO GET TO IT?" + + +/* After level 29, put this: */ + +#define C4TEXT \ + "THE HORRENDOUS VISAGE OF THE BIGGEST\n"\ + "DEMON YOU'VE EVER SEEN CRUMBLES BEFORE\n"\ + "YOU, AFTER YOU PUMP YOUR ROCKETS INTO\n"\ + "HIS EXPOSED BRAIN. THE MONSTER SHRIVELS\n"\ + "UP AND DIES, ITS THRASHING LIMBS\n"\ + "DEVASTATING UNTOLD MILES OF HELL'S\n"\ + "SURFACE.\n"\ + "\n"\ + "YOU'VE DONE IT. THE INVASION IS OVER.\n"\ + "EARTH IS SAVED. HELL IS A WRECK. YOU\n"\ + "WONDER WHERE BAD FOLKS WILL GO WHEN THEY\n"\ + "DIE, NOW. WIPING THE SWEAT FROM YOUR\n"\ + "FOREHEAD YOU BEGIN THE LONG TREK BACK\n"\ + "HOME. REBUILDING EARTH OUGHT TO BE A\n"\ + "LOT MORE FUN THAN RUINING IT WAS.\n" + +/* Before level 31, put this: */ + +#define C5TEXT \ + "CONGRATULATIONS, YOU'VE FOUND THE SECRET\n"\ + "LEVEL! LOOKS LIKE IT'S BEEN BUILT BY\n"\ + "HUMANS, RATHER THAN DEMONS. YOU WONDER\n"\ + "WHO THE INMATES OF THIS CORNER OF HELL\n"\ + "WILL BE." + + +/* Before level 32, put this: */ + +#define C6TEXT \ + "CONGRATULATIONS, YOU'VE FOUND THE\n"\ + "SUPER SECRET LEVEL! YOU'D BETTER\n"\ + "BLAZE THROUGH THIS ONE!\n" + +/*** Plutonia ***/ +/* after map 06 */ + +#define P1TEXT \ + "You gloat over the steaming carcass of the\n"\ + "Guardian. With its death, you've wrested\n"\ + "the Accelerator from the stinking claws\n"\ + "of Hell. You relax and glance around the\n"\ + "room. Damn! There was supposed to be at\n"\ + "least one working prototype, but you can't\n"\ + "see it. The demons must have taken it.\n"\ + "\n"\ + "You must find the prototype, or all your\n"\ + "struggles will have been wasted. Keep\n"\ + "moving, keep fighting, keep killing.\n"\ + "Oh yes, keep living, too." + + +/* after map 11 */ + +#define P2TEXT \ + "Even the deadly Arch-Vile labyrinth could\n"\ + "not stop you, and you've gotten to the\n"\ + "prototype Accelerator which is soon\n"\ + "efficiently and permanently deactivated.\n"\ + "\n"\ + "You're good at that kind of thing." + + +/* after map 20 */ + +#define P3TEXT \ + "You've bashed and battered your way into\n"\ + "the heart of the devil-hive. Time for a\n"\ + "Search-and-Destroy mission, aimed at the\n"\ + "Gatekeeper, whose foul offspring is\n"\ + "cascading to Earth. Yeah, he's bad. But\n"\ + "you know who's worse!\n"\ + "\n"\ + "Grinning evilly, you check your gear, and\n"\ + "get ready to give the bastard a little Hell\n"\ + "of your own making!" + +/* after map 30 */ + +#define P4TEXT \ + "The Gatekeeper's evil face is splattered\n"\ + "all over the place. As its tattered corpse\n"\ + "collapses, an inverted Gate forms and\n"\ + "sucks down the shards of the last\n"\ + "prototype Accelerator, not to mention the\n"\ + "few remaining demons. You're done. Hell\n"\ + "has gone back to pounding bad dead folks \n"\ + "instead of good live ones. Remember to\n"\ + "tell your grandkids to put a rocket\n"\ + "launcher in your coffin. If you go to Hell\n"\ + "when you die, you'll need it for some\n"\ + "final cleaning-up ..." + +/* before map 31 */ + +#define P5TEXT \ + "You've found the second-hardest level we\n"\ + "got. Hope you have a saved game a level or\n"\ + "two previous. If not, be prepared to die\n"\ + "aplenty. For master marines only." + +/* before map 32 */ + +#define P6TEXT \ + "Betcha wondered just what WAS the hardest\n"\ + "level we had ready for ya? Now you know.\n"\ + "No one gets out alive." + +/*** TNT: Evilution ***/ + +#define T1TEXT \ + "You've fought your way out of the infested\n"\ + "experimental labs. It seems that UAC has\n"\ + "once again gulped it down. With their\n"\ + "high turnover, it must be hard for poor\n"\ + "old UAC to buy corporate health insurance\n"\ + "nowadays..\n"\ + "\n"\ + "Ahead lies the military complex, now\n"\ + "swarming with diseased horrors hot to get\n"\ + "their teeth into you. With luck, the\n"\ + "complex still has some warlike ordnance\n"\ + "laying around." + + +#define T2TEXT \ + "You hear the grinding of heavy machinery\n"\ + "ahead. You sure hope they're not stamping\n"\ + "out new hellspawn, but you're ready to\n"\ + "ream out a whole herd if you have to.\n"\ + "They might be planning a blood feast, but\n"\ + "you feel about as mean as two thousand\n"\ + "maniacs packed into one mad killer.\n"\ + "\n"\ + "You don't plan to go down easy." + + +#define T3TEXT \ + "The vista opening ahead looks real damn\n"\ + "familiar. Smells familiar, too -- like\n"\ + "fried excrement. You didn't like this\n"\ + "place before, and you sure as hell ain't\n"\ + "planning to like it now. The more you\n"\ + "brood on it, the madder you get.\n"\ + "Hefting your gun, an evil grin trickles\n"\ + "onto your face. Time to take some names." + +#define T4TEXT \ + "Suddenly, all is silent, from one horizon\n"\ + "to the other. The agonizing echo of Hell\n"\ + "fades away, the nightmare sky turns to\n"\ + "blue, the heaps of monster corpses start \n"\ + "to evaporate along with the evil stench \n"\ + "that filled the air. Jeeze, maybe you've\n"\ + "done it. Have you really won?\n"\ + "\n"\ + "Something rumbles in the distance.\n"\ + "A blue light begins to glow inside the\n"\ + "ruined skull of the demon-spitter." + + +#define T5TEXT \ + "What now? Looks totally different. Kind\n"\ + "of like King Tut's condo. Well,\n"\ + "whatever's here can't be any worse\n"\ + "than usual. Can it? Or maybe it's best\n"\ + "to let sleeping gods lie.." + + +#define T6TEXT \ + "Time for a vacation. You've burst the\n"\ + "bowels of hell and by golly you're ready\n"\ + "for a break. You mutter to yourself,\n"\ + "Maybe someone else can kick Hell's ass\n"\ + "next time around. Ahead lies a quiet town,\n"\ + "with peaceful flowing water, quaint\n"\ + "buildings, and presumably no Hellspawn.\n"\ + "\n"\ + "As you step off the transport, you hear\n"\ + "the stomp of a cyberdemon's iron shoe." + + + +/* + * Character cast strings F_FINALE.C + */ +#define CC_ZOMBIE "ZOMBIEMAN" +#define CC_SHOTGUN "SHOTGUN GUY" +#define CC_HEAVY "HEAVY WEAPON DUDE" +#define CC_IMP "IMP" +#define CC_DEMON "DEMON" +#define CC_LOST "LOST SOUL" +#define CC_CACO "CACODEMON" +#define CC_HELL "HELL KNIGHT" +#define CC_BARON "BARON OF HELL" +#define CC_ARACH "ARACHNOTRON" +#define CC_PAIN "PAIN ELEMENTAL" +#define CC_REVEN "REVENANT" +#define CC_MANCU "MANCUBUS" +#define CC_ARCH "ARCH-VILE" +#define CC_SPIDER "THE SPIDER MASTERMIND" +#define CC_CYBER "THE CYBERDEMON" +#define CC_HERO "OUR HERO" + + +#endif diff --git a/src/d_event.h b/src/d_event.h new file mode 100644 index 00000000..f0b0fe36 --- /dev/null +++ b/src/d_event.h @@ -0,0 +1,127 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Event information structures. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_EVENT__ +#define __D_EVENT__ + + +#include "doomtype.h" + + +// +// Event handling. +// + +// Input event types. +typedef enum +{ +#ifdef __LIBRETRO__ + ev_none, +#endif + ev_keydown, + ev_keyup, + ev_mouse, +} evtype_t; + +// Event structure. +typedef struct +{ + evtype_t type; + int data1; // keys / mouse/joystick buttons + int data2; // mouse/joystick x move + int data3; // mouse/joystick y move +} event_t; + + +typedef enum +{ + ga_nothing, + ga_loadlevel, + ga_newgame, + ga_loadgame, + ga_savegame, + ga_playdemo, + ga_completed, + ga_victory, + ga_worlddone, +} gameaction_t; + + + +// +// Button/action code definitions. +// +typedef enum +{ + // Press "Fire". + BT_ATTACK = 1, + + // Use button, to open doors, activate switches. + BT_USE = 2, + + // Flag: game events, not really buttons. + BT_SPECIAL = 128, + BT_SPECIALMASK = 3, + + // Flag, weapon change pending. + // If true, the next 4 bits hold weapon num. + BT_CHANGE = 4, + + // The 4bit weapon mask and shift, convenience. +//BT_WEAPONMASK = (8+16+32), + BT_WEAPONMASK = (8+16+32+64), // extended to pick up SSG // phares + BT_WEAPONSHIFT = 3, + + // Special events + BTS_LOADGAME = 0, // Loads a game + // Pause the game. + BTS_PAUSE = 1, + // Save the game at each console. + BTS_SAVEGAME = 2, + BTS_RESTARTLEVEL= 3, // Restarts the current level + + // Savegame slot numbers occupy the second byte of buttons. + BTS_SAVEMASK = (4+8+16), + BTS_SAVESHIFT = 2, + +} buttoncode_t; + + +// +// GLOBAL VARIABLES +// + +extern gameaction_t gameaction; + +#endif diff --git a/src/d_ipxgate.c b/src/d_ipxgate.c new file mode 100644 index 00000000..aad28b6b --- /dev/null +++ b/src/d_ipxgate.c @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "protocol.h" + +#define BACKUPTICS 12 +#define NCMD_EXIT 0x80000000 +#define NCMD_RETRANSMIT 0x40000000 +#define NCMD_SETUP 0x20000000 +#define NCMD_KILL 0x10000000 // kill game +#define NCMD_CHECKSUM 0x0fffffff + +typedef struct +{ + short gameid; // so multiple games can setup at once + short drone; + short nodesfound; + short nodeswanted; +} setupdata_t; + +typedef struct +a +{ + // High bit is retransmit request. + unsigned checksum; + // Only valid if NCMD_RETRANSMIT. + byte retransmitfrom; + + byte starttic; + byte player; + byte numtics; + ticcmd_t cmds[BACKUPTICS]; +} doomdata_t; + +typedef struct +{ + signed int tic; + union altu { + setupdata_t s; + unsigned char data[100]; + doomdata_t d; + } u; +} ipxpacket_t; + +int nodes; + +unsigned short port = 0x869b; + +int ipx_socket(void) { + int s = socket(PF_IPX,SOCK_DGRAM,0); + struct sockaddr_ipx sa; + if (s == -1) { + fprintf(stderr,"socket(PF_PIPX): %s\n",strerror(errno)); + exit(-1); + } + memset(&sa,0,sizeof(sa)); + memset(sa.sipx_node,0xff,sizeof(sa.sipx_node)); + sa.sipx_port = htons(port); + if (bind(s,(struct sockaddr*)&sa,sizeof(sa)) == -1) { + fprintf(stderr,"bind(%d): %s\n",port,strerror(errno)); + exit(-1); + } + return s; +} + +int udp_socket(const char* ip) { + struct sockaddr_in sa; + int s = socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP); + + if (s == -1) { + fprintf(stderr,"socket(PF_INET): %s\n", strerror(errno)); + exit(-1); + } + sa.sin_family=PF_INET; + inet_aton(ip,&sa.sin_addr); + sa.sin_port = htons(5030); + + if (connect(s,(struct sockaddr*)&sa,sizeof sa) == -1) { + fprintf(stderr,"connect(%s:%d): %s\n", ip, 5030, strerror(errno)); + exit(-1); + } + return s; +} + +static byte ChecksumPacket(const packet_header_t* buffer, size_t len) +{ + const byte* p = (void*)buffer; + byte sum = 0; + + if (len==0) + return 0; + + while (p++, --len) + sum += *p; + + return sum; +} + +// +// Checksum +// +unsigned NetbufferChecksum (void* p, size_t l) +{ + unsigned c; + + c = 0x1234567; + + l = l/4; + for (int i=0 ; i= -64 && delta <= 64) + return (maketic&~0xff) + low; + if (delta > 64) + return (maketic&~0xff) - 256 + low; + if (delta < -64) + return (maketic&~0xff) + 256 + low; + fprintf(stderr,"ExpandTics strange value %i at maketic %i\n",low,maketic); + exit(-2); +} + +void send_udp_packet(enum packet_type_e type, unsigned tic, void* data, size_t len) { + packet_header_t* p = calloc(sizeof(packet_header_t)+len+1,1); + p->tic = doom_htonl(basetic = tic); p->type = type; + if (!data) { + data = (void*)&consoleplayer; len = 1; + } + memcpy(((char*)p)+sizeof(*p),data,len); + p->checksum = ChecksumPacket(p,sizeof(packet_header_t)+len); + write(udps,p,sizeof(packet_header_t)+len+1); +} + +int connected; +int ipxcounter; + +void ipx_receive(int s) { + ipxpacket_t buf; + int rc; + struct sockaddr from; + size_t sl = sizeof(from); + rc = recvfrom(s,&buf,sizeof buf,0,&from,&sl); + if (rc == -1) { + fprintf(stderr,"read(ipx): %s\n", strerror(errno)); + exit(-2); + } + if (rc > 0) { + if (buf.tic == -1) { + // Setup packet + if (!connected++) { + connect(s,&from,sl); + send_udp_packet(PKT_INIT,0,NULL,0); + } + } else { + if (buf.u.d.checksum & NCMD_SETUP) { + printf("setup packet, dropped\n"); + } else if (buf.u.d.checksum & NCMD_EXIT) { + send_udp_packet(PKT_QUIT,buf.u.d.starttic,NULL,0); + exit(0); + } else if ((buf.u.d.checksum & NCMD_CHECKSUM) == buf.u.d.checksum) { + // No flags, normal game packet + char outbuf[100]; + int tics; + outbuf[0] = tics = buf.u.d.numtics; + outbuf[1] = buf.u.d.player; + for (int i=0; i< tics; i++) + TicToRaw(outbuf+2+i*sizeof(ticcmd_t),&buf.u.d.cmds[i]); + send_udp_packet(PKT_TICC, ExpandTics(buf.u.d.starttic, basetic), outbuf, 2+tics*sizeof(ticcmd_t)); + } + } + } +} + +void udp_receive(int s) { + size_t len = 1024; + packet_header_t *p = malloc(len); + int rc; + + rc = read(s,p,len); + if (rc < 0) { + fprintf(stderr,"read(udp): %s\n", strerror(errno)); + exit(-2); + } + if (rc > 0) { + switch (p->type) { + case PKT_SETUP: + { + struct setup_packet_s *sinfo = (void*)(p+1); + consoleplayer = sinfo->yourplayer; + send_udp_packet(PKT_GO,0,NULL,0); + write(ipxs,"\xff\xff\xff\xff\x00\x00\x00\x00\x02\x00\x02\x00\x00\x00\x00\x00",16); + } + break; + case PKT_GO: + { + ipxpacket_t pkt; + memset(&pkt,0,sizeof(pkt)); + pkt.tic = ipxcounter++; + pkt.u.d.player = consoleplayer^1; + pkt.u.d.starttic = 0; + pkt.u.d.numtics = 0; + pkt.u.d.retransmitfrom = 0; + pkt.u.d.checksum = NetbufferChecksum(&pkt.u.d.retransmitfrom, 4); + write(ipxs,&pkt,16); + } + break; + case PKT_TICS: + { + ipxpacket_t pkt; + int tic = doom_ntohl(p->tic); + byte *pp = (void*)(p+1); + int tics = *pp++; + memset(&pkt,0,sizeof(pkt)); + size_t len; + + pkt.tic = ipxcounter++; + pkt.u.d.starttic = tic; + pkt.u.d.player = (consoleplayer == 0 ? 1 : 0); + pkt.u.d.numtics = tics; + + for (int t=0; t0) { + if (FD_ISSET(ipxs,&fds)) + ipx_receive(ipxs); + if (FD_ISSET(udps,&fds)) + udp_receive(udps); + } + } +} + +int main(int argc, char**argv) { + ipxs = ipx_socket(); + udps = udp_socket(argv[1]); + loop(ipxs,udps); + return 0; +} + diff --git a/src/d_ipxgate.o b/src/d_ipxgate.o new file mode 100644 index 0000000000000000000000000000000000000000..0d271110ad80dc938149360691bed2948340491d GIT binary patch literal 9248 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 literal 0 HcmV?d00001 diff --git a/src/d_items.c b/src/d_items.c new file mode 100644 index 00000000..5adc28df --- /dev/null +++ b/src/d_items.c @@ -0,0 +1,140 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Something to do with weapon sprite frames. Don't ask me. + * + *----------------------------------------------------------------------------- + */ + +// We are referring to sprite numbers. +#include "doomtype.h" +#include "info.h" + +#ifdef __GNUG__ +#pragma implementation "d_items.h" +#endif +#include "d_items.h" + + +// +// PSPRITE ACTIONS for waepons. +// This struct controls the weapon animations. +// +// Each entry is: +// ammo/amunition type +// upstate +// downstate +// readystate +// atkstate, i.e. attack/fire/hit frame +// flashstate, muzzle flash +// +weaponinfo_t weaponinfo[NUMWEAPONS] = +{ + { + // fist + am_noammo, + S_PUNCHUP, + S_PUNCHDOWN, + S_PUNCH, + S_PUNCH1, + S_NULL + }, + { + // pistol + am_clip, + S_PISTOLUP, + S_PISTOLDOWN, + S_PISTOL, + S_PISTOL1, + S_PISTOLFLASH + }, + { + // shotgun + am_shell, + S_SGUNUP, + S_SGUNDOWN, + S_SGUN, + S_SGUN1, + S_SGUNFLASH1 + }, + { + // chaingun + am_clip, + S_CHAINUP, + S_CHAINDOWN, + S_CHAIN, + S_CHAIN1, + S_CHAINFLASH1 + }, + { + // missile launcher + am_misl, + S_MISSILEUP, + S_MISSILEDOWN, + S_MISSILE, + S_MISSILE1, + S_MISSILEFLASH1 + }, + { + // plasma rifle + am_cell, + S_PLASMAUP, + S_PLASMADOWN, + S_PLASMA, + S_PLASMA1, + S_PLASMAFLASH1 + }, + { + // bfg 9000 + am_cell, + S_BFGUP, + S_BFGDOWN, + S_BFG, + S_BFG1, + S_BFGFLASH1 + }, + { + // chainsaw + am_noammo, + S_SAWUP, + S_SAWDOWN, + S_SAW, + S_SAW1, + S_NULL + }, + { + // super shotgun + am_shell, + S_DSGUNUP, + S_DSGUNDOWN, + S_DSGUN, + S_DSGUN1, + S_DSGUNFLASH1 + }, +}; diff --git a/src/d_items.h b/src/d_items.h new file mode 100644 index 00000000..8da4df29 --- /dev/null +++ b/src/d_items.h @@ -0,0 +1,59 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Items: key cards, artifacts, weapon, ammunition. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_ITEMS__ +#define __D_ITEMS__ + +#include "doomdef.h" + +#ifdef __GNUG__ +#pragma interface +#endif + + +/* Weapon info: sprite frames, ammunition use. */ +typedef struct +{ + ammotype_t ammo; + int upstate; + int downstate; + int readystate; + int atkstate; + int flashstate; + +} weaponinfo_t; + +extern weaponinfo_t weaponinfo[NUMWEAPONS]; + +#endif diff --git a/src/d_main.c b/src/d_main.c new file mode 100644 index 00000000..6b4baf12 --- /dev/null +++ b/src/d_main.c @@ -0,0 +1,1567 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM main program (D_DoomMain), + * plus functions to determine game mode (shareware, registered), + * parse command line parameters, configure game parameters (turbo), + * and call the startup functions. + * + *----------------------------------------------------------------------------- + */ + + +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#include +#include +#else +#include +#endif +#include +#include +#include + +#include "doomdef.h" +#include "doomtype.h" +#include "doomstat.h" +#include "d_net.h" +#include "dstrings.h" +#include "sounds.h" +#include "z_zone.h" +#include "w_wad.h" +#include "s_sound.h" +#include "v_video.h" +#include "f_finale.h" +#include "f_wipe.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_menu.h" +#include "p_checksum.h" +#include "i_main.h" +#include "i_system.h" +#include "i_sound.h" +#include "i_video.h" +#include "g_game.h" +#include "hu_stuff.h" +#include "wi_stuff.h" +#include "st_stuff.h" +#include "am_map.h" +#include "p_setup.h" +#include "r_draw.h" +#include "r_main.h" +#include "r_fps.h" +#include "d_main.h" +#include "d_deh.h" // Ty 04/08/98 - Externalizations +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "am_map.h" + +void GetFirstMap(int *ep, int *map); // Ty 08/29/98 - add "-warp x" functionality +static void D_PageDrawer(void); + +// CPhipps - removed wadfiles[] stuff + +// jff 1/24/98 add new versions of these variables to remember command line +boolean clnomonsters; // checkparm of -nomonsters +boolean clrespawnparm; // checkparm of -respawn +boolean clfastparm; // checkparm of -fast +// jff 1/24/98 end definition of command line version of play mode switches + +boolean nomonsters; // working -nomonsters +boolean respawnparm; // working -respawn +boolean fastparm; // working -fast + +//jff 1/22/98 parms for disabling music and sound +boolean nosfxparm; +boolean nomusicparm; + +//jff 4/18/98 +extern boolean inhelpscreens; + +skill_t startskill; +int startepisode; +int startmap; +boolean autostart; +int ffmap; + +boolean advancedemo; + +char wadfile[PATH_MAX+1]; // primary wad file +char mapdir[PATH_MAX+1]; // directory of development maps +char baseiwad[PATH_MAX+1]; // jff 3/23/98: iwad directory +char basesavegame[PATH_MAX+1]; // killough 2/16/98: savegame directory + +//jff 4/19/98 list of standard IWAD names +const char *const standard_iwads[]= +{ + "doom2f.wad", + "doom2.wad", + "plutonia.wad", + "tnt.wad", + "doom.wad", + "doom1.wad", + "doomu.wad", /* CPhipps - alow doomu.wad */ + "freedoom.wad", /* wart@kobold.org: added freedoom for Fedora Extras */ +}; +static const int nstandard_iwads = sizeof standard_iwads/sizeof*standard_iwads; + +/* + * D_PostEvent - Event handling + * + * Called by I/O functions when an event is received. + * Try event handlers for each code area in turn. + * cph - in the true spirit of the Boom source, let the + * short ciruit operator madness begin! + */ + +void D_PostEvent(event_t *ev) +{ + /* cph - suppress all input events at game start + * FIXME: This is a lousy kludge */ + if (gametic < 3) return; + M_Responder(ev) || + (gamestate == GS_LEVEL && ( + HU_Responder(ev) || + ST_Responder(ev) || + AM_Responder(ev) + ) + ) || + G_Responder(ev); +} + +// +// D_Wipe +// +// CPhipps - moved the screen wipe code from D_Display to here +// The screens to wipe between are already stored, this just does the timing +// and screen updating + +static void D_Wipe(void) +{ + boolean done; + + do + { + I_uSleep(20000); // CPhipps - don't thrash cpu in this loop + done = wipe_ScreenWipe(1); + M_Drawer(); // menu is drawn even on top of wipes + I_FinishUpdate(); // page flip or blit buffer + } + while (!done); +} + +// +// D_Display +// draw current display, possibly wiping it from the previous +// + +// wipegamestate can be set to -1 to force a wipe on the next draw +gamestate_t wipegamestate = GS_DEMOSCREEN; +extern boolean setsizeneeded; +extern int showMessages; + +void D_Display (void) +{ + static boolean isborderstate = false; + static boolean borderwillneedredraw = false; + static gamestate_t oldgamestate = -1; + boolean wipe; + boolean viewactive = false, isborder = false; + + if (!I_StartDisplay()) + return; + + // save the current screen if about to wipe + if ((wipe = gamestate != wipegamestate)) + wipe_StartScreen(); + + if (gamestate != GS_LEVEL) { // Not a level + switch (oldgamestate) { + case -1: + case GS_LEVEL: + V_SetPalette(0); // cph - use default (basic) palette + default: + break; + } + + switch (gamestate) { + case GS_INTERMISSION: + WI_Drawer(); + break; + case GS_FINALE: + F_Drawer(); + break; + case GS_DEMOSCREEN: + D_PageDrawer(); + break; + default: + break; + } + } else if (gametic != basetic) { // In a level + boolean redrawborderstuff; + + HU_Erase(); + + if (setsizeneeded) { // change the view size if needed + R_ExecuteSetViewSize(); + oldgamestate = -1; // force background redraw + } + + // Work out if the player view is visible, and if there is a border + viewactive = (!(automapmode & am_active) || (automapmode & am_overlay)) && !inhelpscreens; + isborder = viewactive ? (viewheight != SCREENHEIGHT) : (!inhelpscreens && (automapmode & am_active)); + + if (oldgamestate != GS_LEVEL) { + redrawborderstuff = isborder; + } else { + // CPhipps - + // If there is a border, and either there was no border last time, + // or the border might need refreshing, then redraw it. + redrawborderstuff = isborder && (!isborderstate || borderwillneedredraw); + // The border may need redrawing next time if the border surrounds the screen, + // and there is a menu being displayed + borderwillneedredraw = viewactive + ? (menuactive && isborder) + : (!inhelpscreens && menuactive == mnact_full); + } + + // Now do the drawing + if (viewactive) + R_RenderPlayerView (&players[displayplayer]); + if (automapmode & am_active) + AM_Drawer(); + ST_Drawer( + ((viewheight != SCREENHEIGHT) + || ((automapmode & am_active) && !(automapmode & am_overlay))), + redrawborderstuff, + (menuactive == mnact_full)); + HU_Drawer(); + } + + isborderstate = isborder; + oldgamestate = wipegamestate = gamestate; + + // draw pause pic + if (paused && (menuactive != mnact_full)) { + // Simplified the "logic" here and no need for x-coord caching - POPE + V_DrawNamePatch((320 - V_NamePatchWidth("M_PAUSE"))/2, 4, + 0, "M_PAUSE", CR_DEFAULT, VPT_NONE); + } + + // menus go directly to the screen + M_Drawer(); // menu is drawn even on top of everything +#ifdef HAVE_NET + NetUpdate(); // send out any new accumulation +#else + D_BuildNewTiccmds(); +#endif + + // normal update + if (!wipe) + I_FinishUpdate (); // page flip or blit buffer + else { + // wipe update + wipe_EndScreen(); + D_Wipe(); + } + + I_EndDisplay(); + + //e6y: don't thrash cpu during pausing + if (paused) { + I_uSleep(1000); + } +} + +int has_exited; + +/* I_SafeExit + * This function is called instead of exit() by functions that might be called + * during the exit process (i.e. after exit() has already been called) + * Prevent infinitely recursive exits -- killough + */ + +void I_SafeExit(int rc) +{ + if (!has_exited) /* If it hasn't exited yet, exit now -- killough */ + has_exited=rc ? 2 : 1; +} + +// +// DEMO LOOP +// + +static int demosequence; // killough 5/2/98: made static +static int pagetic; +static const char *pagename; // CPhipps - const + +// +// D_PageTicker +// Handles timing for warped projection +// +void D_PageTicker(void) +{ + if (--pagetic < 0) + D_AdvanceDemo(); +} + +// +// D_PageDrawer +// +static void D_PageDrawer(void) +{ + // proff/nicolas 09/14/98 -- now stretchs bitmaps to fullscreen! + // CPhipps - updated for new patch drawing + // proff - added M_DrawCredits + if (pagename) + { + V_DrawNamePatch(0, 0, 0, pagename, CR_DEFAULT, VPT_NONE); + } + else + M_DrawCredits(); +} + +// +// D_AdvanceDemo +// Called after each demo or intro demosequence finishes +// +void D_AdvanceDemo (void) +{ + advancedemo = true; +} + +/* killough 11/98: functions to perform demo sequences + * cphipps 10/99: constness fixes + */ + +static void D_SetPageName(const char *name) +{ + pagename = name; +} + +static void D_DrawTitle1(const char *name) +{ + S_StartMusic(mus_intro); + pagetic = (TICRATE*170)/35; + D_SetPageName(name); +} + +static void D_DrawTitle2(const char *name) +{ + S_StartMusic(mus_dm2ttl); + D_SetPageName(name); +} + +/* killough 11/98: tabulate demo sequences + */ + +static struct +{ + void (*func)(const char *); + const char *name; +} const demostates[][4] = + { + { + {D_DrawTitle1, "TITLEPIC"}, + {D_DrawTitle1, "TITLEPIC"}, + {D_DrawTitle2, "TITLEPIC"}, + {D_DrawTitle1, "TITLEPIC"}, + }, + + { + {G_DeferedPlayDemo, "demo1"}, + {G_DeferedPlayDemo, "demo1"}, + {G_DeferedPlayDemo, "demo1"}, + {G_DeferedPlayDemo, "demo1"}, + }, + { + {D_SetPageName, NULL}, + {D_SetPageName, NULL}, + {D_SetPageName, NULL}, + {D_SetPageName, NULL}, + }, + + { + {G_DeferedPlayDemo, "demo2"}, + {G_DeferedPlayDemo, "demo2"}, + {G_DeferedPlayDemo, "demo2"}, + {G_DeferedPlayDemo, "demo2"}, + }, + + { + {D_SetPageName, "HELP2"}, + {D_SetPageName, "HELP2"}, + {D_SetPageName, "CREDIT"}, + {D_DrawTitle1, "TITLEPIC"}, + }, + + { + {G_DeferedPlayDemo, "demo3"}, + {G_DeferedPlayDemo, "demo3"}, + {G_DeferedPlayDemo, "demo3"}, + {G_DeferedPlayDemo, "demo3"}, + }, + + { + {NULL}, + {NULL}, + {NULL}, + {D_SetPageName, "CREDIT"}, + }, + + { + {NULL}, + {NULL}, + {NULL}, + {G_DeferedPlayDemo, "demo4"}, + }, + + { + {NULL}, + {NULL}, + {NULL}, + {NULL}, + } + }; + +/* + * This cycles through the demo sequences. + * killough 11/98: made table-driven + */ + +void D_DoAdvanceDemo(void) +{ + players[consoleplayer].playerstate = PST_LIVE; /* not reborn */ + advancedemo = usergame = paused = false; + gameaction = ga_nothing; + + pagetic = TICRATE * 11; /* killough 11/98: default behavior */ + gamestate = GS_DEMOSCREEN; + + if (netgame && !demoplayback) { + demosequence = 0; + } else + if (!demostates[++demosequence][gamemode].func) + demosequence = 0; + demostates[demosequence][gamemode].func + (demostates[demosequence][gamemode].name); +} + +// +// D_StartTitle +// +void D_StartTitle (void) +{ + gameaction = ga_nothing; + demosequence = -1; + D_AdvanceDemo(); +} + +// +// D_AddFile +// +// Rewritten by Lee Killough +// +// Ty 08/29/98 - add source parm to indicate where this came from +// CPhipps - static, const char* parameter +// - source is an enum +// - modified to allocate & use new wadfiles array +void D_AddFile (const char *file, wad_source_t source) +{ + char *gwa_filename=NULL; + + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = + AddDefaultExtension(strcpy(malloc(strlen(file)+5), file), ".wad"); + wadfiles[numwadfiles].src = source; // Ty 08/29/98 + numwadfiles++; + // proff: automatically try to add the gwa files + // proff - moved from w_wad.c + gwa_filename=AddDefaultExtension(strcpy(malloc(strlen(file)+5), file), ".wad"); + if (strlen(gwa_filename)>4) + if (!strcasecmp(gwa_filename+(strlen(gwa_filename)-4),".wad")) + { + char *ext; + ext = &gwa_filename[strlen(gwa_filename)-4]; + ext[1] = 'g'; ext[2] = 'w'; ext[3] = 'a'; + wadfiles = realloc(wadfiles, sizeof(*wadfiles)*(numwadfiles+1)); + wadfiles[numwadfiles].name = gwa_filename; + wadfiles[numwadfiles].src = source; // Ty 08/29/98 + numwadfiles++; + } +} + +// killough 10/98: support -dehout filename +// cph - made const, don't cache results +static const char *D_dehout(void) +{ + int p = M_CheckParm("-dehout"); + if (!p) + p = M_CheckParm("-bexout"); + return (p && ++p < myargc ? myargv[p] : NULL); +} + +// +// CheckIWAD +// +// Verify a file is indeed tagged as an IWAD +// Scan its lumps for levelnames and return gamemode as indicated +// Detect missing wolf levels in DOOM II +// +// The filename to check is passed in iwadname, the gamemode detected is +// returned in gmode, hassec returns the presence of secret levels +// +// jff 4/19/98 Add routine to test IWAD for validity and determine +// the gamemode from it. Also note if DOOM II, whether secret levels exist +// CPhipps - const char* for iwadname, made static +static void CheckIWAD(const char *iwadname,GameMode_t *gmode,boolean *hassec) +{ + if ( !access (iwadname,R_OK) ) + { + int ud=0,rg=0,sw=0,cm=0,sc=0; + FILE* fp; + + // Identify IWAD correctly + if ((fp = fopen(iwadname, "rb"))) + { + wadinfo_t header; + + // read IWAD header + if (fread(&header, sizeof(header), 1, fp) == 1 && !strncmp(header.identification, "IWAD", 4)) + { + size_t length; + filelump_t *fileinfo; + + // read IWAD directory + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps; + fileinfo = malloc(length*sizeof(filelump_t)); + if (fseek (fp, header.infotableofs, SEEK_SET) || + fread (fileinfo, sizeof(filelump_t), length, fp) != length || + fclose(fp)) + I_Error("CheckIWAD: failed to read directory %s",iwadname); + + // scan directory for levelname lumps + while (length--) + if (fileinfo[length].name[0] == 'E' && + fileinfo[length].name[2] == 'M' && + fileinfo[length].name[4] == 0) + { + if (fileinfo[length].name[1] == '4') + ++ud; + else if (fileinfo[length].name[1] == '3') + ++rg; + else if (fileinfo[length].name[1] == '2') + ++rg; + else if (fileinfo[length].name[1] == '1') + ++sw; + } + else if (fileinfo[length].name[0] == 'M' && + fileinfo[length].name[1] == 'A' && + fileinfo[length].name[2] == 'P' && + fileinfo[length].name[5] == 0) + { + ++cm; + if (fileinfo[length].name[3] == '3') + if (fileinfo[length].name[4] == '1' || + fileinfo[length].name[4] == '2') + ++sc; + } + + free(fileinfo); + } + else // missing IWAD tag in header + I_Error("CheckIWAD: IWAD tag %s not present", iwadname); + } + else // error from open call + I_Error("CheckIWAD: Can't open IWAD %s", iwadname); + + // Determine game mode from levels present + // Must be a full set for whichever mode is present + // Lack of wolf-3d levels also detected here + + *gmode = indetermined; + *hassec = false; + if (cm>=30) + { + *gmode = commercial; + *hassec = sc>=2; + } + else if (ud>=9) + *gmode = retail; + else if (rg>=18) + *gmode = registered; + else if (sw>=9) + *gmode = shareware; + } + else // error from access call + I_Error("CheckIWAD: IWAD %s not readable", iwadname); +} + + + +// NormalizeSlashes +// +// Remove trailing slashes, translate backslashes to slashes +// The string to normalize is passed and returned in str +// +// jff 4/19/98 Make killoughs slash fixer a subroutine +// +static void NormalizeSlashes(char *str) +{ + int l; + + // killough 1/18/98: Neater / \ handling. + // Remove trailing / or \ to prevent // /\ \/ \\, and change \ to / + + if (!str || !(l = strlen(str))) + return; + if (str[--l]=='/' || str[l]=='\\') // killough 1/18/98 + str[l]=0; + while (l--) + if (str[l]=='\\') + str[l]='/'; +} + +/* + * FindIWADFIle + * + * Search for one of the standard IWADs + * CPhipps - static, proper prototype + * - 12/1999 - rewritten to use I_FindFile + */ +static char *FindIWADFile(void) +{ + int i; + char * iwad = NULL; + + i = M_CheckParm("-iwad"); + lprintf(LO_ALWAYS, "i: %d\n", i); + + for(int x = 0; x < 32; x++) + lprintf(LO_ALWAYS, "myargv[%d]: %s\n", x, myargv[x]); + + if (i && (++i < myargc)) { + iwad = I_FindFile(myargv[i], ".wad"); + } else { + for (i=0; !iwad && i PATH_MAX-12) p = NULL; + + strcpy(basesavegame,(p == NULL) ? I_DoomExeDir() : p); + } + if ((i=M_CheckParm("-save")) && i=10 && !strnicmp(iwad+i-10,"doom2f.wad",10)) + language=french; + else if (i>=7 && !strnicmp(iwad+i-7,"tnt.wad",7)) + gamemission = pack_tnt; + else if (i>=12 && !strnicmp(iwad+i-12,"plutonia.wad",12)) + gamemission = pack_plut; + break; + default: + gamemission = none; + break; + } + if (gamemode == indetermined) + //jff 9/3/98 use logical output routine + lprintf(LO_WARN,"Unknown Game Version, may not work\n"); + D_AddFile(iwad,source_iwad); + free(iwad); + } + else + I_Error("IdentifyVersion: IWAD not found\n"); +} + + + +// killough 5/3/98: old code removed +// +// Find a Response File +// + +#define MAXARGVS 100 + +static void FindResponseFile (void) +{ + int i; + + for (i = 1;i < myargc;i++) + if (myargv[i][0] == '@') + { + int size; + int index; + int indexinfile; + byte *file = NULL; + const char **moreargs = malloc(myargc * sizeof(const char*)); + const char **newargv; + // proff 04/05/2000: Added for searching responsefile + char fname[PATH_MAX+1]; + + strcpy(fname,&myargv[i][1]); + AddDefaultExtension(fname,".rsp"); + + // READ THE RESPONSE FILE INTO MEMORY + // proff 04/05/2000: changed for searching responsefile + // cph 2002/08/09 - use M_ReadFile for simplicity + size = M_ReadFile(fname, &file); + // proff 04/05/2000: Added for searching responsefile + if (size < 0) + { + strcat(strcpy(fname,I_DoomExeDir()),&myargv[i][1]); + AddDefaultExtension(fname,".rsp"); + size = M_ReadFile(fname, &file); + } + if (size < 0) + { + /* proff 04/05/2000: Changed from LO_FATAL + * proff 04/05/2000: Simply removed the exit(1); + * cph - made fatal, don't drop through and SEGV + */ + I_Error("No such response file: %s",fname); + } + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Found response file %s\n",fname); + // proff 04/05/2000: Added check for empty rsp file + if (size<=0) + { + int k; + lprintf(LO_ERROR,"\nResponse file empty!\n"); + + newargv = calloc(sizeof(char *),MAXARGVS); + newargv[0] = myargv[0]; + for (k = 1,index = 1;k < myargc;k++) + { + if (i!=k) + newargv[index++] = myargv[k]; + } + myargc = index; myargv = newargv; + return; + } + + // KEEP ALL CMDLINE ARGS FOLLOWING @RESPONSEFILE ARG + memcpy((void *)moreargs,&myargv[i+1],(index = myargc - i - 1) * sizeof(myargv[0])); + + { + const char *firstargv = myargv[0]; + newargv = calloc(sizeof(char *),MAXARGVS); + newargv[0] = firstargv; + } + + { + byte *infile = file; + indexinfile = 0; + indexinfile++; // SKIP PAST ARGV[0] (KEEP IT) + do { + while (size > 0 && isspace(*infile)) { infile++; size--; } + if (size > 0) { + char *s = malloc(size+1); + char *p = s; + int quoted = 0; + + while (size > 0) { + // Whitespace terminates the token unless quoted + if (!quoted && isspace(*infile)) break; + if (*infile == '\"') { + // Quotes are removed but remembered + infile++; size--; quoted ^= 1; + } else { + *p++ = *infile++; size--; + } + } + if (quoted) I_Error("Runaway quoted string in response file"); + + // Terminate string, realloc and add to argv + *p = 0; + newargv[indexinfile++] = realloc(s,strlen(s)+1); + } + } while(size > 0); + } + free(file); + + memcpy((void *)&newargv[indexinfile],moreargs,index*sizeof(moreargs[0])); + free((void *)moreargs); + + myargc = indexinfile+index; myargv = newargv; + + // DISPLAY ARGS + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"%d command-line args:\n",myargc); + for (index=1;index 0) + { + tmyargv[tmyargc++] = strdup("-file"); // put the switch in + for (i=0;i 0) + { + tmyargv[tmyargc++] = strdup("-deh"); + for (i=0;i7 ? 7 : viewangleoffset); + viewangleoffset = (8-viewangleoffset) * ANG45; + } + + // init subsystems + + G_ReloadDefaults(); // killough 3/4/98: set defaults just loaded. + // jff 3/24/98 this sets startskill if it was -1 + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"V_Init: allocate screens.\n"); + V_Init(); + + // CPhipps - autoloading of wads + // Designed to be general, instead of specific to boomlump.wad + // Some people might find this useful + // cph - support MBF -noload parameter + if (!M_CheckParm("-noload")) { + // only autoloaded wads here - autoloaded patches moved down below W_Init + int i; + + for (i=0; i= 0; ) + // Split loading DEHACKED lumps into IWAD/autoload and PWADs/others + if (lumpinfo[p].source == source_iwad + || lumpinfo[p].source == source_pre + || lumpinfo[p].source == source_auto_load) + ProcessDehFile(NULL, D_dehout(), p); // cph - add dehacked-in-a-wad support + + if (!M_CheckParm("-noload")) { + // now do autoloaded dehacked patches, after IWAD patches but before PWAD + int i; + + for (i=0; i= 0; ) + if (!(lumpinfo[p].source == source_iwad + || lumpinfo[p].source == source_pre + || lumpinfo[p].source == source_auto_load)) + ProcessDehFile(NULL, D_dehout(), p); + + // Load command line dehacked patches after WAD dehacked patches + + // e6y: DEH files preloaded in wrong order + // http://sourceforge.net/tracker/index.php?func=detail&aid=1418158&group_id=148658&atid=772943 + + // ty 03/09/98 do dehacked stuff + // Using -deh in BOOM, others use -dehacked. + // Ty 03/18/98 also allow .bex extension. .bex overrides if both exist. + + p = M_CheckParm ("-deh"); + if (p) + { + char file[PATH_MAX+1]; // cph - localised + // the parms after p are deh/bex file names, + // until end of parms or another - preceded parm + // Ty 04/11/98 - Allow multiple -deh files in a row + + while (++p != myargc && *myargv[p] != '-') + { + AddDefaultExtension(strcpy(file, myargv[p]), ".bex"); + if (access(file, F_OK)) // nope + { + AddDefaultExtension(strcpy(file, myargv[p]), ".deh"); + if (access(file, F_OK)) // still nope + I_Error("D_DoomMainSetup: Cannot find .deh or .bex file named %s", + myargv[p]); + } + // during the beta we have debug output to dehout.txt + ProcessDehFile(file,D_dehout(),0); + } + } + + V_InitColorTranslation(); //jff 4/24/98 load color translation lumps + + // killough 2/22/98: copyright / "modified game" / SPA banners removed + + // Ty 04/08/98 - Add 5 lines of misc. data, only if nonblank + // The expectation is that these will be set in a .bex file + //jff 9/3/98 use logical output routine + if (*startup1) lprintf(LO_INFO,"%s",startup1); + if (*startup2) lprintf(LO_INFO,"%s",startup2); + if (*startup3) lprintf(LO_INFO,"%s",startup3); + if (*startup4) lprintf(LO_INFO,"%s",startup4); + if (*startup5) lprintf(LO_INFO,"%s",startup5); + // End new startup strings + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"M_Init: Init miscellaneous info.\n"); + M_Init(); + +#ifdef HAVE_NET + // CPhipps - now wait for netgame start + D_CheckNetGame(); +#endif + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"R_Init: Init DOOM refresh daemon - "); + R_Init(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"\nP_Init: Init Playloop state.\n"); + P_Init(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"I_Init: Setting up machine state.\n"); + I_Init(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"S_Init: Setting up sound.\n"); + S_Init(snd_SfxVolume /* *8 */, snd_MusicVolume /* *8*/ ); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"HU_Init: Setting up heads up display.\n"); + HU_Init(); + + if (!(M_CheckParm("-nodraw") && M_CheckParm("-nosound"))) + I_InitGraphics(); + + //jff 9/3/98 use logical output routine + lprintf(LO_INFO,"ST_Init: Init status bar.\n"); + ST_Init(); + + idmusnum = -1; //jff 3/17/98 insure idmus number is blank + + // start the apropriate game based on parms + + // killough 12/98: + + if ((p = M_CheckParm ("-checksum")) && ++p < myargc) + { + P_RecordChecksum (myargv[p]); + } + + if (slot && ++slot < myargc) + { + slot = atoi(myargv[slot]); // killough 3/16/98: add slot info + G_LoadGame(slot, true); // killough 5/15/98: add command flag // cph - no filename + } + else + if (autostart || netgame) + { + // sets first map and first episode if unknown + GetFirstMap(&startepisode, &startmap); + G_InitNew(startskill, startepisode, startmap); + } + else + D_StartTitle(); // start up intro loop +} + +// +// D_DoomMain +// +extern void p_checksum_cleanup(void); +#ifdef HAVE_NET +extern void D_QuitNetGame (void); +#endif + +void D_DoomLoop(void) +{ + //Doom loop + do + { + WasRenderedInTryRunTics = false; + + if (ffmap == gamemap) ffmap = 0; + + TryRunTics (); // will run at least one tic + + // killough 3/16/98: change consoleplayer to displayplayer + if (players[displayplayer].mo) // cph 2002/08/10 + S_UpdateSounds(players[displayplayer].mo);// move positional sounds + + if (!movement_smooth || !WasRenderedInTryRunTics || gamestate != wipegamestate) + { + // Update display, next frame, with current state. + D_Display(); + } + }while(!has_exited); +} + +void D_DoomDeinit(void) +{ + lprintf(LO_INFO,"D_DoomDeinit:\n"); + //Deinit + M_QuitDOOM(0); + Z_Close(); +#ifdef HAVE_NET + D_QuitNetGame(); + I_ShutdownNetwork(); +#endif + M_SaveDefaults (); + I_ShutdownSound(); + I_ShutdownMusic(); +#ifdef HAVE_SDL + SDL_Quit(); +#endif + p_checksum_cleanup(); +} + +// +// GetFirstMap +// +// Ty 08/29/98 - determine first available map from the loaded wads and run it +// + +void GetFirstMap(int *ep, int *map) +{ + int i,j; // used to generate map name + boolean done = false; // Ty 09/13/98 - to exit inner loops + char test[6]; // MAPxx or ExMx plus terminator for testing + char name[6]; // MAPxx or ExMx plus terminator for display + boolean newlevel = false; // Ty 10/04/98 - to test for new level + int ix; // index for lookup + + strcpy(name,""); // initialize + if (*map == 0) // unknown so go search for first changed one + { + *ep = 1; + *map = 1; // default E1M1 or MAP01 + if (gamemode == commercial) + { + for (i=1;!done && i<33;i++) // Ty 09/13/98 - add use of !done + { + sprintf(test,"MAP%02d",i); + ix = W_CheckNumForName(test); + if (ix != -1) // Ty 10/04/98 avoid -1 subscript + { + if (lumpinfo[ix].source == source_pwad) + { + *map = i; + strcpy(name,test); // Ty 10/04/98 + done = true; // Ty 09/13/98 + newlevel = true; // Ty 10/04/98 + } + else + { + if (!*name) // found one, not pwad. First default. + strcpy(name,test); + } + } + } + } + else // one of the others + { + strcpy(name,"E1M1"); // Ty 10/04/98 - default for display + for (i=1;!done && i<5;i++) // Ty 09/13/98 - add use of !done + { + for (j=1;!done && j<10;j++) // Ty 09/13/98 - add use of !done + { + sprintf(test,"E%dM%d",i,j); + ix = W_CheckNumForName(test); + if (ix != -1) // Ty 10/04/98 avoid -1 subscript + { + if (lumpinfo[ix].source == source_pwad) + { + *ep = i; + *map = j; + strcpy(name,test); // Ty 10/04/98 + done = true; // Ty 09/13/98 + newlevel = true; // Ty 10/04/98 + } + else + { + if (!*name) // found one, not pwad. First default. + strcpy(name,test); + } + } + } + } + } + //jff 9/3/98 use logical output routine + lprintf(LO_CONFIRM,"Auto-warping to first %slevel: %s\n", + newlevel ? "new " : "", name); // Ty 10/04/98 - new level test + } +} diff --git a/src/d_main.h b/src/d_main.h new file mode 100644 index 00000000..d6b9b6e1 --- /dev/null +++ b/src/d_main.h @@ -0,0 +1,80 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Main startup and splash screenstuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_MAIN__ +#define __D_MAIN__ + +#include "d_event.h" +#include "w_wad.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* CPhipps - removed wadfiles[] stuff to w_wad.h */ + +extern char basesavegame[]; // killough 2/16/98: savegame path + +//jff 1/24/98 make command line copies of play modes available +extern boolean clnomonsters; // checkparm of -nomonsters +extern boolean clrespawnparm; // checkparm of -respawn +extern boolean clfastparm; // checkparm of -fast +//jff end of external declaration of command line playmode + +extern boolean nosfxparm; +extern boolean nomusicparm; +extern int ffmap; + +// Called by IO functions when input is detected. +void D_PostEvent(event_t* ev); + +// Demo stuff +extern boolean advancedemo; +void D_AdvanceDemo(void); +void D_DoAdvanceDemo (void); + +// +// BASE LEVEL +// + +void D_Display(void); +void D_PageTicker(void); +void D_StartTitle(void); +void D_DoomMain(void); +void D_AddFile (const char *file, wad_source_t source); + +/* cph - MBF-like wad/deh/bex autoload code */ +#define MAXLOADFILES 2 +extern const char *wad_files[MAXLOADFILES], *deh_files[MAXLOADFILES]; + +#endif diff --git a/src/d_net.h b/src/d_net.h new file mode 100644 index 00000000..fcdeb6c4 --- /dev/null +++ b/src/d_net.h @@ -0,0 +1,214 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Networking stuff. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_NET__ +#define __D_NET__ + +#include "d_player.h" + + +#ifdef __GNUG__ +#pragma interface +#endif + + +// +// Network play related stuff. +// There is a data struct that stores network +// communication related stuff, and another +// one that defines the actual packets to +// be transmitted. +// + +#define DOOMCOM_ID 0x12345678l + +// Max computers/players in a game. +#define MAXNETNODES 8 + + +typedef enum +{ + CMD_SEND = 1, + CMD_GET = 2 + +} command_t; + + +// +// Network packet data. +// +typedef struct +{ + // High bit is retransmit request. + unsigned checksum; + // Only valid if NCMD_RETRANSMIT. + byte retransmitfrom; + + byte starttic; + byte player; + byte numtics; + ticcmd_t cmds[BACKUPTICS]; + +} doomdata_t; + +// +// Startup packet difference +// SG: 4/12/98 +// Added so we can send more startup data to synch things like +// bobbing, recoil, etc. +// this is just mapped over the ticcmd_t array when setup packet is sent +// +// Note: the original code takes care of startskill, deathmatch, nomonsters +// respawn, startepisode, startmap +// Note: for phase 1 we need to add monsters_remember, variable_friction, +// weapon_recoil, allow_pushers, over_under, player_bobbing, +// fastparm, demo_insurance, and the rngseed +//Stick all options into bytes so we don't need to mess with bitfields +//WARNING: make sure this doesn't exceed the size of the ticcmds area! +//sizeof(ticcmd_t)*BACKUPTICS +//This is the current length of our extra stuff +// +//killough 5/2/98: this should all be replaced by calls to G_WriteOptions() +//and G_ReadOptions(), which were specifically designed to set up packets. +//By creating a separate struct and functions to read/write the options, +//you now have two functions and data to maintain instead of just one. +//If the array in g_game.c which G_WriteOptions()/G_ReadOptions() operates +//on, is too large (more than sizeof(ticcmd_t)*BACKUPTICS), it can +//either be shortened, or the net code needs to divide it up +//automatically into packets. The STARTUPLEN below is non-portable. +//There's a portable way to do it without having to know the sizes. + +#define STARTUPLEN 12 +typedef struct +{ + byte monsters_remember; + byte variable_friction; + byte weapon_recoil; + byte allow_pushers; + byte over_under; + byte player_bobbing; + byte fastparm; + byte demo_insurance; + unsigned long rngseed; + char filler[sizeof(ticcmd_t)*BACKUPTICS-STARTUPLEN]; +} startup_t; + +typedef enum { + // Leave space, so low values corresponding to normal netgame setup packets can be ignored + nm_plcolour = 3, + nm_savegamename = 4, +} netmisctype_t; + +typedef struct +{ + netmisctype_t type; + size_t len; + byte value[sizeof(ticcmd_t)*BACKUPTICS - sizeof(netmisctype_t) - sizeof(size_t)]; +} netmisc_t; + +typedef struct +{ + // Supposed to be DOOMCOM_ID? + long id; + + // DOOM executes an int to execute commands. + short intnum; + // Communication between DOOM and the driver. + // Is CMD_SEND or CMD_GET. + short command; + // Is dest for send, set by get (-1 = no packet). + short remotenode; + + // Number of bytes in doomdata to be sent + short datalength; + + // Info common to all nodes. + // Console is allways node 0. + short numnodes; + // Flag: 1 = no duplication, 2-5 = dup for slow nets. + short ticdup; + // Flag: 1 = send a backup tic in every packet. + short extratics; + // Flag: 1 = deathmatch. + short deathmatch; + // Flag: -1 = new game, 0-5 = load savegame + short savegame; + short episode; // 1-3 + short map; // 1-9 + short skill; // 1-5 + + // Info specific to this node. + short consoleplayer; + short numplayers; + + // These are related to the 3-display mode, + // in which two drones looking left and right + // were used to render two additional views + // on two additional computers. + // Probably not operational anymore. + // 1 = left, 0 = center, -1 = right + short angleoffset; + // 1 = drone + short drone; + + // The packet data to be sent. + doomdata_t data; + +} doomcom_t; + +// Create any new ticcmds and broadcast to other players. +#ifdef HAVE_NET +void NetUpdate (void); +#else +void D_BuildNewTiccmds(void); +#endif + +//? how many ticks to run? +void TryRunTics (void); + +// CPhipps - move to header file +void D_InitNetGame (void); // This does the setup +void D_CheckNetGame(void); // This waits for game start + +// CPhipps - misc info broadcast +void D_NetSendMisc(netmisctype_t type, size_t len, void* data); + +// CPhipps - ask server for a wad file we need +boolean D_NetGetWad(const char* name); + +// Netgame stuff (buffers and pointers, i.e. indices). +extern doomcom_t *doomcom; +extern doomdata_t *netbuffer; // This points inside doomcom. + +#endif diff --git a/src/d_player.h b/src/d_player.h new file mode 100644 index 00000000..a45abe8c --- /dev/null +++ b/src/d_player.h @@ -0,0 +1,234 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Player state structure. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __D_PLAYER__ +#define __D_PLAYER__ + + +// The player data structure depends on a number +// of other structs: items (internal inventory), +// animation states (closely tied to the sprites +// used to represent them, unfortunately). +#include "d_items.h" +#include "p_pspr.h" + +// In addition, the player is just a special +// case of the generic moving object/actor. +#include "p_mobj.h" + +// Finally, for odd reasons, the player input +// is buffered within the player data struct, +// as commands per game tick. +#include "d_ticcmd.h" + +#ifdef __GNUG__ +#pragma interface +#endif + + +// +// Player states. +// +typedef enum +{ + // Playing or camping. + PST_LIVE, + // Dead on the ground, view follows killer. + PST_DEAD, + // Ready to restart/respawn??? + PST_REBORN + +} playerstate_t; + + +// +// Player internal flags, for cheats and debug. +// +typedef enum +{ + // No clipping, walk through barriers. + CF_NOCLIP = 1, + // No damage, no health loss. + CF_GODMODE = 2, + // Not really a cheat, just a debug aid. + CF_NOMOMENTUM = 4 + +} cheat_t; + + +// +// Extended player object info: player_t +// +typedef struct player_s +{ + mobj_t* mo; + playerstate_t playerstate; + ticcmd_t cmd; + + // Determine POV, + // including viewpoint bobbing during movement. + // Focal origin above r.z + fixed_t viewz; + // Base height above floor for viewz. + fixed_t viewheight; + // Bob/squat speed. + fixed_t deltaviewheight; + // bounded/scaled total momentum. + fixed_t bob; + + /* killough 10/98: used for realistic bobbing (i.e. not simply overall speed) + * mo->momx and mo->momy represent true momenta experienced by player. + * This only represents the thrust that the player applies himself. + * This avoids anomolies with such things as Boom ice and conveyors. + */ + fixed_t momx, momy; // killough 10/98 + + // This is only used between levels, + // mo->health is used during levels. + int health; + int armorpoints; + // Armor type is 0-2. + int armortype; + + // Power ups. invinc and invis are tic counters. + int powers[NUMPOWERS]; + boolean cards[NUMCARDS]; + boolean backpack; + + // Frags, kills of other players. + int frags[MAXPLAYERS]; + weapontype_t readyweapon; + + // Is wp_nochange if not changing. + weapontype_t pendingweapon; + + boolean weaponowned[NUMWEAPONS]; + int ammo[NUMAMMO]; + int maxammo[NUMAMMO]; + + // True if button down last tic. + int attackdown; + int usedown; + + // Bit flags, for cheats and debug. + // See cheat_t, above. + int cheats; + + // Refired shots are less accurate. + int refire; + + // For intermission stats. + int killcount; + int itemcount; + int secretcount; + + // Hint messages. // CPhipps - const + const char* message; + + // For screen flashing (red or bright). + int damagecount; + int bonuscount; + + // Who did damage (NULL for floors/ceilings). + mobj_t* attacker; + + // So gun flashes light up areas. + int extralight; + + // Current PLAYPAL, ??? + // can be set to REDCOLORMAP for pain, etc. + int fixedcolormap; + + // Player skin colorshift, + // 0-3 for which color to draw player. + int colormap; + + // Overlay view sprites (gun, etc). + pspdef_t psprites[NUMPSPRITES]; + + // True if secret level has been done. + boolean didsecret; + +} player_t; + + +// +// INTERMISSION +// Structure passed e.g. to WI_Start(wb) +// +typedef struct +{ + boolean in; // whether the player is in game + + // Player stats, kills, collected items etc. + int skills; + int sitems; + int ssecret; + int stime; + int frags[4]; + int score; // current score on entry, modified on return + +} wbplayerstruct_t; + +typedef struct +{ + int epsd; // episode # (0-2) + + // if true, splash the secret level + boolean didsecret; + + // previous and next levels, origin 0 + int last; + int next; + + int maxkills; + int maxitems; + int maxsecret; + int maxfrags; + + // the par time + int partime; + + // index of this player in game + int pnum; + + wbplayerstruct_t plyr[MAXPLAYERS]; + + // CPhipps - total game time for completed levels so far + int totaltimes; + +} wbstartstruct_t; + + +#endif diff --git a/src/d_server.c b/src/d_server.c new file mode 100644 index 00000000..d909e857 --- /dev/null +++ b/src/d_server.c @@ -0,0 +1,737 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Network game server code + * New for LxDoom, but drawing ideas and code fragments from the + * earlier net code + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_SDL_NET + #include "SDL.h" +#endif + +#include "doomtype.h" +#include "protocol.h" +#include "i_network.h" +#ifndef PRBOOM_SERVER +#include "m_fixed.h" +#endif +#include "i_system.h" +#include "m_swap.h" + +#ifndef HAVE_GETOPT +/* The following code for getopt is from the libc-source of FreeBSD, + * it might be changed a little bit. + * Florian Schulze (florian.schulze@gmx.net) + */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)getopt.c 8.3 (Berkeley) 4/27/95"; +#endif +static const char rcsid[] = "$FreeBSD$"; +#endif /* LIBC_SCCS and not lint */ + +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +char *__progname="prboom_server"; + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +getopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + extern char *__progname; + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + int ret; + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (optopt == (int)'-') + return (-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname, optopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + ret = BADARG; + else + ret = BADCH; + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + __progname, optopt); + return (ret); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return (optopt); /* dump back option letter */ +} +#else +#include +#endif + +#define MAXPLAYERS 4 +#define BACKUPTICS 12 + +// Dummies to forfill l_udp.c unused client stuff +int M_CheckParm(const char* p) { p = NULL; return 1; } +int myargc; +char** myargv; + +void NORETURN I_Error(const char *error, ...) // killough 3/20/98: add const +{ + va_list argptr; + va_start(argptr,error); + vfprintf(stderr,error,argptr); + va_end(argptr); + exit(-1); +} + +int playerjoingame[MAXPLAYERS], playerleftgame[MAXPLAYERS]; +UDP_CHANNEL remoteaddr[MAXPLAYERS]; +enum { pc_unused, pc_connected, pc_ready, pc_confirmedready, pc_playing, pc_quit } playerstate[MAXPLAYERS]; +int displaycounter; + +boolean n_players_in_state(int n, int ps) { + int i,j; + for (i=j=0;itic); +} + + + +void read_config_file(FILE* fp, struct setup_packet_s* sp) +{ + byte* gameopt = sp->game_options; + + while (!feof(fp)) { + char def[80]; + char strparm[100]; + if (fscanf (fp, "%79s %99[^\n]\n", def, strparm) == 2) { + int v = atoi(strparm); + if (!strcmp(def,"default_skill")) { + if (verbose) printf("config file sets default_skill to %d\n",v); + sp->skill = v-1; + } else if (!strcmp(def,"default_compatibility_level")) { + if (verbose) printf("config file sets compatibility_level to %d\n",v); + if (v == -1) v = MAX_COMPATIBILITY_LEVEL-1; //e6y: -1 => maxcompat + sp->complevel = v; + } else { + int i; + for (i=0; i= MAXPLAYERS); } + +int main(int argc, char** argv) +{ +#ifndef USE_SDL_NET + int localport = 5030; +#else + Uint16 localport = 5030; +#endif + int numplayers = 2, xtratics = 0, ticdup = 1; + int exectics = 0; // gametics completed + struct setup_packet_s setupinfo = { 2, 0, 1, 1, 1, 0, best_compatibility, 0, 0}; + char**wadname = NULL; + char**wadget = NULL; + int numwads = 0; + { + int opt; + byte *gameopt = setupinfo.game_options; + + memcpy(gameopt, &def_game_options, sizeof (setupinfo.game_options)); + while ((opt = getopt(argc, argv, "c:t:x:p:e:l:adrfns:N:vw:")) != EOF) + switch (opt) { + case 'c': + { + FILE *cf = fopen(optarg,"r"); + if (!cf) { perror("fopen"); return -1; } + read_config_file(cf,&setupinfo); + fclose(cf); + } + break; + case 't': + if (optarg) ticdup = atoi(optarg); + break; + case 'x': + if (optarg) xtratics = atoi(optarg); + break; + case 'p': + if (optarg) localport = atoi(optarg); + break; + case 'e': + if (optarg) setupinfo.episode = atoi(optarg); + break; + case 'l': + if (optarg) setupinfo.level = atoi(optarg); + break; + case 'a': + setupinfo.deathmatch = 2; + break; + case 'd': + setupinfo.deathmatch = 1; + break; + case 'r': + setupinfo.game_options[6] = 1; + break; + case 'f': + setupinfo.game_options[7] = 1; + break; + case 'n': + setupinfo.game_options[8] = 1; + break; + case 's': + if (optarg) setupinfo.skill = atoi(optarg)-1; + break; + case 'N': + if (optarg) setupinfo.players = numplayers = atoi(optarg); + break; + case 'v': + verbose++; + break; + case 'w': + if (optarg) { + char *p; + wadname = realloc(wadname, ++numwads * sizeof *wadname); + wadget = realloc(wadget , numwads * sizeof *wadget ); + wadname[numwads-1] = strdup(optarg); + if ((p = strchr(wadname[numwads-1], ','))) { + *p++ = 0; wadget[numwads-1] = p; + } else wadget[numwads-1] = NULL; + } + break; + } + } + + setupinfo.ticdup = ticdup; setupinfo.extratic = xtratics; + { /* Random number seed + * Mirrors the corresponding code in G_ReadOptions */ + int rngseed = (int)time(NULL); + setupinfo.game_options[13] = rngseed & 0xff; + rngseed >>= 8; + setupinfo.game_options[12] = rngseed & 0xff; + rngseed >>= 8; + setupinfo.game_options[11] = rngseed & 0xff; + rngseed >>= 8; + setupinfo.game_options[10] = rngseed & 0xff; + } + I_InitSockets(localport); + + printf("Listening on port %d, waiting for %d players\n", localport, numplayers); + + { // no players initially + int i; + for (i=0; i2) printf("Received packet:"); + switch (packet->type) { + case PKT_INIT: + if (!ingame) { + { + int n; + struct setup_packet_s *sinfo = (void*)(packet+1); + + /* Find player number and add to the game */ + n = *(short*)(packet+1); + + if (badplayer(n) || playerstate[n] != pc_unused) + for (n=0; nyourplayer = n; + sinfo->numwads = numwads; + for (i=0; iwadnames + extrabytes, wadname[i]); + extrabytes += strlen(wadname[i]) + 1; + } + I_SendPacketTo(packet, sizeof *packet + sizeof setupinfo + extrabytes, + remoteaddr+n); + I_uSleep(10000); + I_SendPacketTo(packet, sizeof *packet + sizeof setupinfo + extrabytes, + remoteaddr+n); + } + } + } + break; + case PKT_GO: + if (!ingame) { + int from = *(byte*)(packet+1); + + if (badplayer(from) || playerstate[from] == pc_unused) break; + if (confirming) { + if (playerstate[from] != pc_confirmedready) curplayers++; + playerstate[from] = pc_confirmedready; + } else + playerstate[from] = pc_ready; + } + break; + case PKT_TICC: + { + byte tics = *(byte*)(packet+1); + int from = *(((byte*)(packet+1))+1); + + if (badplayer(from)) break; + + if (verbose>2) + printf("tics %ld - %ld from %d\n", ptic(packet), ptic(packet) + tics - 1, from); + if (ptic(packet) > remoteticfrom[from]) { + // Missed tics, so request a resend + packet_set(packet, PKT_RETRANS, remoteticfrom[from]); + I_SendPacketTo(packet, sizeof *packet, remoteaddr+from); + } else { + ticcmd_t *newtic = (void*)(((byte*)(packet+1))+2); + if (ptic(packet) + tics < remoteticfrom[from]) break; // Won't help + remoteticfrom[from] = ptic(packet); + while (tics--) + netcmds[from][remoteticfrom[from]++%BACKUPTICS] = *newtic++; + } + } + break; + case PKT_RETRANS: + { + int from = *(byte*)(packet+1); + if (badplayer(from)) break; + + if (verbose>2) printf("%d requests resend from %ld\n", from, ptic(packet)); + remoteticto[from] = ptic(packet); + } + break; + case PKT_QUIT: + { + int from = *(byte*)(packet+1); + if (badplayer(from)) break; + + if (!ingame && playerstate[from] != pc_unused) { + // If we already got a PKT_GO, we have to remove this player frmo the count of ready players. And we then flag this player slot as vacant. + printf("player %d pulls out\n", from); + if (playerstate[from] == pc_confirmedready) curplayers--; + playerstate[from] = pc_unused; + } else + if (playerleftgame[from] == INT_MAX) { // In the game + playerleftgame[from] = ptic(packet); + --curplayers; + if (verbose) printf("%d quits at %ld (%d left)\n", from, ptic(packet),curplayers); + if (ingame && !curplayers) exit(0); // All players have exited + } + } + // Fall through and broadcast it + case PKT_EXTRA: + BroadcastPacket(packet, len); + if (packet->type == PKT_EXTRA) { + if (verbose>2) printf("misc from %d\n", *(((byte*)(packet+1))+1)); + } + break; + case PKT_WAD: + { + int i; + int from = *(byte*)(packet+1); + char *name = 1 + (char*)(packet+1); + size_t size = sizeof(packet_header_t); + packet_header_t *reply; + + if (badplayer(from) || playerstate[from] != pc_unused) break; + + if (verbose) printf("Request for %s ", name); + for (i=0; itype); + break; + } + } + free(packet); + if (!ingame && n_players_in_state(numplayers,pc_confirmedready)) { + int i; + packet_header_t gopacket; + packet = &gopacket; + ingame=true; + printf("All players joined, beginning game.\n"); + for (i=0; i1) printf("%d new tics can be run\n", lowtic - exectics); + + if (lowtic > exectics) + exectics = lowtic; // count exec'ed tics + // Now send all tics up to lowtic + for (i=0; i1) printf("sending %d tics to %d\n", tics, i); + while (tics--) { + int j, playersthistic = 0; + byte *q = p++; + for (j=0; j remoteticto[i])) { + *p++ = j; + memcpy(p, &netcmds[j][remoteticto[i]%BACKUPTICS], sizeof(ticcmd_t)); + p += sizeof(ticcmd_t); + playersthistic++; + } + *q = playersthistic; + remoteticto[i]++; + } + I_SendPacketTo(packet, p - ((byte*)packet), remoteaddr+i); + free(packet); + } + { + if (remoteticfrom[i] == remoteticto[i]) { + backoffcounter[i] = 0; + } else if (remoteticfrom[i] > remoteticto[i]+1) { + if ((backoffcounter[i] += remoteticfrom[i] - remoteticto[i] - 1) > 35) { + packet_header_t *packet = malloc(sizeof(packet_header_t)); + packet_set(packet, PKT_BACKOFF, remoteticto[i]); + I_SendPacketTo(packet,sizeof *packet,remoteaddr+i); + backoffcounter[i] = 0; + if (verbose) printf("telling client %d to back off\n",i); + free(packet); + } + } + } + } + } + if (!((ingame ? 0xff : 0xf) & displaycounter++)) { + int i; + fprintf(stderr,"Player states: ["); + for (i=0;i +#include +#include +#include +#include + +// this should go here, not in makefile/configure.ac -- josh +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#include "m_swap.h" +#include "version.h" + +// Game mode handling - identify IWAD version +// to handle IWAD dependend animations etc. +typedef enum { + shareware, // DOOM 1 shareware, E1, M9 + registered, // DOOM 1 registered, E3, M27 + commercial, // DOOM 2 retail, E1 M34 (DOOM 2 german edition not handled) + retail, // DOOM 1 retail, E4, M36 + indetermined // Well, no IWAD found. +} GameMode_t; + +// Mission packs - might be useful for TC stuff? +typedef enum { + doom, // DOOM 1 + doom2, // DOOM 2 + pack_tnt, // TNT mission pack + pack_plut, // Plutonia pack + none +} GameMission_t; + +// Identify language to use, software localization. +typedef enum { + english, + french, + german, + unknown +} Language_t; + +// +// For resize of screen, at start of game. +// + +#define BASE_WIDTH 320 + +// It is educational but futile to change this +// scaling e.g. to 2. Drawing of status bar, +// menues etc. is tied to the scale implied +// by the graphics. + +#define INV_ASPECT_RATIO 0.625 /* 0.75, ideally */ + +// killough 2/8/98: MAX versions for maximum screen sizes +// allows us to avoid the overhead of dynamic allocation +// when multiple screen sizes are supported + +// proff 08/17/98: Changed for high-res +#define MAX_SCREENWIDTH 320 +#define MAX_SCREENHEIGHT 200 + +// The maximum number of players, multiplayer/networking. +#define MAXPLAYERS 4 + +// phares 5/14/98: +// DOOM Editor Numbers (aka doomednum in mobj_t) + +#define DEN_PLAYER5 4001 +#define DEN_PLAYER6 4002 +#define DEN_PLAYER7 4003 +#define DEN_PLAYER8 4004 + +// State updates, number of tics / second. +#define TICRATE 35 + +// The current state of the game: whether we are playing, gazing +// at the intermission screen, the game final animation, or a demo. + +typedef enum { + GS_LEVEL, + GS_INTERMISSION, + GS_FINALE, + GS_DEMOSCREEN +} gamestate_t; + +// +// Difficulty/skill settings/filters. +// +// These are Thing flags + +// Skill flags. +#define MTF_EASY 1 +#define MTF_NORMAL 2 +#define MTF_HARD 4 +// Deaf monsters/do not react to sound. +#define MTF_AMBUSH 8 + +/* killough 11/98 */ +#define MTF_NOTSINGLE 16 +#define MTF_NOTDM 32 +#define MTF_NOTCOOP 64 +#define MTF_FRIEND 128 +#define MTF_RESERVED 256 + +typedef enum { + sk_none=-1, //jff 3/24/98 create unpicked skill setting + sk_baby=0, + sk_easy, + sk_medium, + sk_hard, + sk_nightmare +} skill_t; + +// +// Key cards. +// + +typedef enum { + it_bluecard, + it_yellowcard, + it_redcard, + it_blueskull, + it_yellowskull, + it_redskull, + NUMCARDS +} card_t; + +// The defined weapons, including a marker +// indicating user has not changed weapon. +typedef enum { + wp_fist, + wp_pistol, + wp_shotgun, + wp_chaingun, + wp_missile, + wp_plasma, + wp_bfg, + wp_chainsaw, + wp_supershotgun, + + NUMWEAPONS, + wp_nochange // No pending weapon change. +} weapontype_t; + +// Ammunition types defined. +typedef enum { + am_clip, // Pistol / chaingun ammo. + am_shell, // Shotgun / double barreled shotgun. + am_cell, // Plasma rifle, BFG. + am_misl, // Missile launcher. + NUMAMMO, + am_noammo // Unlimited for chainsaw / fist. +} ammotype_t; + +// Power up artifacts. +typedef enum { + pw_invulnerability, + pw_strength, + pw_invisibility, + pw_ironfeet, + pw_allmap, + pw_infrared, + NUMPOWERS +} powertype_t; + +// Power up durations (how many seconds till expiration). +typedef enum { + INVULNTICS = (30*TICRATE), + INVISTICS = (60*TICRATE), + INFRATICS = (120*TICRATE), + IRONTICS = (60*TICRATE) +} powerduration_t; + +// DOOM keyboard definition. +// This is the stuff configured by Setup.Exe. +// Most key data are simple ascii (uppercased). + +#define KEYD_RIGHTARROW 0xae +#define KEYD_LEFTARROW 0xac +#define KEYD_UPARROW 0xad +#define KEYD_DOWNARROW 0xaf +#define KEYD_ESCAPE 27 +#define KEYD_ENTER 13 +#define KEYD_TAB 9 +#define KEYD_F1 (0x80+0x3b) +#define KEYD_F2 (0x80+0x3c) +#define KEYD_F3 (0x80+0x3d) +#define KEYD_F4 (0x80+0x3e) +#define KEYD_F5 (0x80+0x3f) +#define KEYD_F6 (0x80+0x40) +#define KEYD_F7 (0x80+0x41) +#define KEYD_F8 (0x80+0x42) +#define KEYD_F9 (0x80+0x43) +#define KEYD_F10 (0x80+0x44) +#define KEYD_F11 (0x80+0x57) +#define KEYD_F12 (0x80+0x58) +#define KEYD_BACKSPACE 127 +#define KEYD_PAUSE 0xff +#define KEYD_EQUALS 0x3d +#define KEYD_MINUS 0x2d +#define KEYD_RSHIFT (0x80+0x36) +#define KEYD_RCTRL (0x80+0x1d) +#define KEYD_RALT (0x80+0x38) +#define KEYD_LALT KEYD_RALT +#define KEYD_CAPSLOCK 0xba // phares + +// phares 3/2/98: +#define KEYD_INSERT 0xd2 +#define KEYD_HOME 0xc7 +#define KEYD_PAGEUP 0xc9 +#define KEYD_PAGEDOWN 0xd1 +#define KEYD_DEL 0xc8 +#define KEYD_END 0xcf +#define KEYD_SCROLLLOCK 0xc6 +#define KEYD_SPACEBAR 0x20 +// phares 3/2/98 + +#define KEYD_NUMLOCK 0xC5 // killough 3/6/98 + +// cph - Add the numeric keypad keys, as suggested by krose 4/22/99: +// The way numbers are assigned to keys is a mess, but it's too late to +// change that easily. At least these additions are don neatly. +// Codes 0x100-0x200 are reserved for number pad + +#define KEYD_KEYPAD0 (0x100 + '0') +#define KEYD_KEYPAD1 (0x100 + '1') +#define KEYD_KEYPAD2 (0x100 + '2') +#define KEYD_KEYPAD3 (0x100 + '3') +#define KEYD_KEYPAD4 (0x100 + '4') +#define KEYD_KEYPAD5 (0x100 + '5') +#define KEYD_KEYPAD6 (0x100 + '6') +#define KEYD_KEYPAD7 (0x100 + '7') +#define KEYD_KEYPAD8 (0x100 + '8') +#define KEYD_KEYPAD9 (0x100 + '9') +#define KEYD_KEYPADENTER (0x100 + KEYD_ENTER) +#define KEYD_KEYPADDIVIDE (0x100 + '/') +#define KEYD_KEYPADMULTIPLY (0x100 + '*') +#define KEYD_KEYPADMINUS (0x100 + '-') +#define KEYD_KEYPADPLUS (0x100 + '+') +#define KEYD_KEYPADPERIOD (0x100 + '.') + +// phares 4/19/98: +// Defines Setup Screen groups that config variables appear in. +// Used when resetting the defaults for every item in a Setup group. + +typedef enum { + ss_none, + ss_keys, + ss_weap, + ss_stat, + ss_auto, + ss_enem, + ss_mess, + ss_chat, + ss_gen, /* killough 10/98 */ + ss_comp, /* killough 10/98 */ + ss_max +} ss_types; + +// phares 3/20/98: +// +// Player friction is variable, based on controlling +// linedefs. More friction can create mud, sludge, +// magnetized floors, etc. Less friction can create ice. + +#define MORE_FRICTION_MOMENTUM 15000 // mud factor based on momentum +#define ORIG_FRICTION 0xE800 // original value +#define ORIG_FRICTION_FACTOR 2048 // original value + +#endif // __DOOMDEF__ diff --git a/src/doomstat.c b/src/doomstat.c new file mode 100644 index 00000000..c1303325 --- /dev/null +++ b/src/doomstat.c @@ -0,0 +1,100 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Put all global state variables here. + * + *----------------------------------------------------------------------------- + */ + +#ifdef __GNUG__ +#pragma implementation "doomstat.h" +#endif +#include "doomstat.h" + +// Game Mode - identify IWAD as shareware, retail etc. +GameMode_t gamemode = indetermined; +GameMission_t gamemission = doom; + +// Language. +Language_t language = english; + +// Set if homebrew PWAD stuff has been added. +boolean modifiedgame; + +//----------------------------------------------------------------------------- + +// CPhipps - compatibility vars +complevel_t compatibility_level, default_compatibility_level; + +int comp[COMP_TOTAL], default_comp[COMP_TOTAL]; // killough 10/98 + +// v1.1-like pitched sounds +int pitched_sounds; // killough + +int demo_insurance, default_demo_insurance; // killough 1/16/98 + +int allow_pushers = 1; // MT_PUSH Things // phares 3/10/98 +int default_allow_pushers; // killough 3/1/98: make local to each game + +int variable_friction = 1; // ice & mud // phares 3/10/98 +int default_variable_friction; // killough 3/1/98: make local to each game + +int weapon_recoil; // weapon recoil // phares +int default_weapon_recoil; // killough 3/1/98: make local to each game + +int player_bobbing; // whether player bobs or not // phares 2/25/98 +int default_player_bobbing; // killough 3/1/98: make local to each game + +int monsters_remember; // killough 3/1/98 +int default_monsters_remember; + +int monster_infighting=1; // killough 7/19/98: monster<=>monster attacks +int default_monster_infighting=1; + +int monster_friction=1; // killough 10/98: monsters affected by friction +int default_monster_friction=1; + +// killough 8/8/98: distance friends tend to move towards players +int distfriend = 128, default_distfriend = 128; + +// killough 9/8/98: whether monsters are allowed to strafe or retreat +int monster_backing, default_monster_backing; + +// killough 9/9/98: whether monsters are able to avoid hazards (e.g. crushers) +int monster_avoid_hazards, default_monster_avoid_hazards; + +// killough 9/9/98: whether monsters help friends +int help_friends, default_help_friends; + +int flashing_hom; // killough 10/98 + +int doom_weapon_toggles; // killough 10/98 + +int monkeys, default_monkeys; + diff --git a/src/doomstat.h b/src/doomstat.h new file mode 100644 index 00000000..472089f8 --- /dev/null +++ b/src/doomstat.h @@ -0,0 +1,313 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * All the global variables that store the internal state. + * Theoretically speaking, the internal state of the engine + * should be found by looking at the variables collected + * here, and every relevant module will have to include + * this header file. + * In practice, things are a bit messy. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __D_STATE__ +#define __D_STATE__ + +// We need the playr data structure as well. +#include "d_player.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// ------------------------ +// Command line parameters. +// + +extern boolean nomonsters; // checkparm of -nomonsters +extern boolean respawnparm; // checkparm of -respawn +extern boolean fastparm; // checkparm of -fast + +// ----------------------------------------------------- +// Game Mode - identify IWAD as shareware, retail etc. +// + +extern GameMode_t gamemode; +extern GameMission_t gamemission; + +// Set if homebrew PWAD stuff has been added. +extern boolean modifiedgame; + +// CPhipps - new compatibility handling +extern complevel_t compatibility_level, default_compatibility_level; + +// CPhipps - old compatibility testing flags aliased to new handling +#define compatibility (compatibility_level<=boom_compatibility_compatibility) +#define demo_compatibility (compatibility_level < boom_compatibility_compatibility) +#define mbf_features (compatibility_level>=mbf_compatibility) + +// v1.1-like pitched sounds +extern int pitched_sounds; // killough + +extern int demo_insurance, default_demo_insurance; // killough 4/5/98 + +// ------------------------------------------- +// killough 10/98: compatibility vector + +enum { + comp_telefrag, + comp_dropoff, + comp_vile, + comp_pain, + comp_skull, + comp_blazing, + comp_doorlight, + comp_model, + comp_god, + comp_falloff, + comp_floors, + comp_skymap, + comp_pursuit, + comp_doorstuck, + comp_staylift, + comp_zombie, + comp_stairs, + comp_infcheat, + comp_zerotags, + comp_moveblock, + comp_respawn, /* cph - this is the inverse of comp_respawnfix from eternity */ + comp_sound, + comp_666, + comp_soul, + comp_maskedanim, + COMP_NUM, /* cph - should be last in sequence */ + COMP_TOTAL=32 // Some extra room for additional variables +}; + +extern int comp[COMP_TOTAL], default_comp[COMP_TOTAL]; + +// ------------------------------------------- +// Language. +extern Language_t language; + +// ------------------------------------------- +// Selected skill type, map etc. +// + +// Defaults for menu, methinks. +extern skill_t startskill; +extern int startepisode; +extern int startmap; + +extern boolean autostart; + +// Selected by user. +extern skill_t gameskill; +extern int gameepisode; +extern int gamemap; + +// Nightmare mode flag, single player. +extern boolean respawnmonsters; + +// Netgame? Only true if >1 player. +extern boolean netgame; + +// Flag: true only if started as net deathmatch. +// An enum might handle altdeath/cooperative better. +extern boolean deathmatch; + +// ------------------------------------------ +// Internal parameters for sound rendering. +// These have been taken from the DOS version, +// but are not (yet) supported with Linux +// (e.g. no sound volume adjustment with menu. + +// These are not used, but should be (menu). +// From m_menu.c: +// Sound FX volume has default, 0 - 15 +// Music volume has default, 0 - 15 +// These are multiplied by 8. +extern int snd_SfxVolume; // maximum volume for sound +extern int snd_MusicVolume; // maximum volume for music + +// CPhipps - screen parameters +extern unsigned int desired_screenwidth, desired_screenheight; + +// ------------------------- +// Status flags for refresh. +// + +enum automapmode_e { + am_active = 1, // currently shown + am_overlay= 2, // covers the screen, i.e. not overlay mode + am_rotate = 4, // rotates to the player facing direction + am_follow = 8, // keep the player centred + am_grid =16, // show grid +}; +extern enum automapmode_e automapmode; // Mode that the automap is in + +enum menuactive_e { + mnact_inactive, // no menu + mnact_float, // doom-style large font menu, doesn't overlap anything + mnact_full, // boom-style small font menu, may overlap status bar +}; +extern enum menuactive_e menuactive; // Type of menu overlaid, if any + +extern boolean paused; // Game Pause? + +// This one is related to the 3-screen display mode. +// ANG90 = left side, ANG270 = right +extern int viewangleoffset; + +// Player taking events, and displaying. +extern int consoleplayer; +extern int displayplayer; + +// ------------------------------------- +// Scores, rating. +// Statistics on a given map, for intermission. +// +extern int totalkills, totallive; +extern int totalitems; +extern int totalsecret; + +// Timer, for scores. +extern int basetic; /* killough 9/29/98: levelstarttic, adjusted */ +extern int leveltime; // tics in game play for par + +// -------------------------------------- +// DEMO playback/recording related stuff. + +extern boolean usergame; +extern boolean demoplayback; +extern int demover; + +extern gamestate_t gamestate; + +//----------------------------- +// Internal parameters, fixed. +// These are set by the engine, and not changed +// according to user inputs. Partly load from +// WAD, partly set at startup time. + +extern int gametic; + + +// Bookkeeping on players - state. +extern player_t players[MAXPLAYERS]; + +// Alive? Disconnected? +extern boolean playeringame[MAXPLAYERS]; +extern boolean realplayeringame[MAXPLAYERS]; + +extern mapthing_t *deathmatchstarts; // killough +extern size_t num_deathmatchstarts; // killough + +extern mapthing_t *deathmatch_p; + +// Player spawn spots. +extern mapthing_t playerstarts[]; + +// Intermission stats. +// Parameters for world map / intermission. +extern wbstartstruct_t wminfo; + +//----------------------------------------- +// Internal parameters, used for engine. +// + +// if true, load all graphics at level load +extern boolean precache; + +// wipegamestate can be set to -1 +// to force a wipe on the next draw +extern gamestate_t wipegamestate; + +extern int mouseSensitivity_horiz; // killough +extern int mouseSensitivity_vert; + +extern int bodyqueslot; + +// Needed to store the number of the dummy sky flat. +// Used for rendering, as well as tracking projectiles etc. + +extern int skyflatnum; + +extern int maketic; + +// Networking and tick handling related. +#define BACKUPTICS 12 + +extern ticcmd_t netcmds[][BACKUPTICS]; +extern int ticdup; + +//----------------------------------------------------------------------------- + +extern int allow_pushers; // MT_PUSH Things // phares 3/10/98 +extern int default_allow_pushers; + +extern int variable_friction; // ice & mud // phares 3/10/98 +extern int default_variable_friction; + +extern int monsters_remember; // killough 3/1/98 +extern int default_monsters_remember; + +extern int weapon_recoil; // weapon recoil // phares +extern int default_weapon_recoil; + +extern int player_bobbing; // whether player bobs or not // phares 2/25/98 +extern int default_player_bobbing; // killough 3/1/98: make local to each game + +/* killough 8/8/98: distance friendly monsters tend to stay from player */ +extern int distfriend, default_distfriend; + +/* killough 9/8/98: whether monsters are allowed to strafe or retreat */ +extern int monster_backing, default_monster_backing; + +/* killough 9/9/98: whether monsters intelligently avoid hazards */ +extern int monster_avoid_hazards, default_monster_avoid_hazards; + +/* killough 10/98: whether monsters are affected by friction */ +extern int monster_friction, default_monster_friction; + +/* killough 9/9/98: whether monsters help friends */ +extern int help_friends, default_help_friends; + +extern int flashing_hom; // killough 10/98 + +extern int doom_weapon_toggles; // killough 10/98 + +/* killough 7/19/98: whether monsters should fight against each other */ +extern int monster_infighting, default_monster_infighting; + +extern int monkeys, default_monkeys; + +extern int HelperThing; // type of thing to use for helper + +#endif diff --git a/src/doomtype.h b/src/doomtype.h new file mode 100644 index 00000000..0b93aac9 --- /dev/null +++ b/src/doomtype.h @@ -0,0 +1,132 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2006 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Simple basic typedefs, isolated here to make it easier + * separating modules. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __DOOMTYPE__ +#define __DOOMTYPE__ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef __BYTEBOOL__ +#define __BYTEBOOL__ +/* Fixed to use builtin bool type with C++. */ +#ifdef __cplusplus +typedef bool boolean; +#else +typedef enum {false, true} boolean; +#endif +typedef unsigned char byte; +#endif + +//e6y +#ifndef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) +#endif +#ifndef MIN +#define MIN(a,b) ((a)<(b)?(a):(b)) +#endif + +/* cph - Wrapper for the long long type, as Win32 used a different name. + * Except I don't know what to test as it's compiler specific + * Proff - I fixed it */ +#ifndef _MSC_VER +typedef signed long long int_64_t; +typedef unsigned long long uint_64_t; +// define compiled-specific long-long contstant notation here +#define LONGLONG(num) (uint_64_t)num ## ll +#else +typedef __int64 int_64_t; +typedef unsigned __int64 uint_64_t; +// define compiled-specific long-long contstant notation here +#define LONGLONG(num) (uint_64_t)num +#undef PATH_MAX +#define PATH_MAX 1024 +#define strcasecmp _stricmp +#define strncasecmp _strnicmp +#define S_ISDIR(x) (((sbuf.st_mode & S_IFDIR)==S_IFDIR)?1:0) +#endif + +#ifdef __GNUC__ +#define CONSTFUNC __attribute__((const)) +#define PUREFUNC __attribute__((pure)) +#define NORETURN __attribute__ ((noreturn)) +#else +#define CONSTFUNC +#define PUREFUNC +#define NORETURN +#endif + +/* CPhipps - use limits.h instead of depreciated values.h */ +#include + +// In case limits.h does not define PATH_MAX +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +/* cph - move compatibility levels here so we can use them in d_server.c */ +typedef enum { + doom_12_compatibility, /* Doom v1.2 */ + doom_1666_compatibility, /* Doom v1.666 */ + doom2_19_compatibility, /* Doom & Doom 2 v1.9 */ + ultdoom_compatibility, /* Doom 2 v1.9 */ + finaldoom_compatibility, /* Final & Ultimate Doom v1.9, and Doom95 */ + dosdoom_compatibility, /* Early dosdoom & tasdoom */ + tasdoom_compatibility, /* Early dosdoom & tasdoom */ + boom_compatibility_compatibility, /* Boom's compatibility mode */ + boom_201_compatibility, /* Compatible with Boom v2.01 */ + boom_202_compatibility, /* Compatible with Boom v2.01 */ + lxdoom_1_compatibility, /* LxDoom v1.3.2+ */ + mbf_compatibility, /* MBF */ + prboom_1_compatibility, /* PrBoom 2.03beta? */ + prboom_2_compatibility, /* PrBoom 2.1.0-2.1.1 */ + prboom_3_compatibility, /* PrBoom 2.2.x */ + prboom_4_compatibility, /* PrBoom 2.3.x */ + prboom_5_compatibility, /* PrBoom 2.4.0 */ + prboom_6_compatibility, /* Latest PrBoom */ + MAX_COMPATIBILITY_LEVEL, /* Must be last entry */ + /* Aliases follow */ + boom_compatibility = boom_201_compatibility, /* Alias used by G_Compatibility */ + best_compatibility = prboom_6_compatibility, +} complevel_t; + +/* cph - from v_video.h, needed by gl_struct.h */ +enum patch_translation_e { + VPT_NONE = 0, // Normal + VPT_FLIP = 1, // Flip image horizontally + VPT_TRANS = 2, // Translate image via a translation table +}; + +#endif diff --git a/src/dstrings.c b/src/dstrings.c new file mode 100644 index 00000000..4e27ea5e --- /dev/null +++ b/src/dstrings.c @@ -0,0 +1,85 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Globally defined strings. + * + *----------------------------------------------------------------------------- + */ + +#ifdef __GNUG__ +#pragma implementation "dstrings.h" +#endif +#include "dstrings.h" + + +// killough 1/18/98: remove hardcoded limit, add const: +const char *const endmsg[]= +{ + // DOOM1 + QUITMSG, + "please don't leave, there's more\ndemons to toast!", + "let's beat it -- this is turning\ninto a bloodbath!", + "i wouldn't leave if i were you.\ndos is much worse.", + "you're trying to say you like dos\nbetter than me, right?", + "don't leave yet -- there's a\ndemon around that corner!", + "ya know, next time you come in here\ni'm gonna toast ya.", + "go ahead and leave. see if i care.", // 1/15/98 killough + + // QuitDOOM II messages + "you want to quit?\nthen, thou hast lost an eighth!", + "don't go now, there's a \ndimensional shambler waiting\nat the dos prompt!", + "get outta here and go back\nto your boring programs.", + "if i were your boss, i'd \n deathmatch ya in a minute!", + "look, bud. you leave now\nand you forfeit your body count!", + "just leave. when you come\nback, i'll be waiting with a bat.", + "you're lucky i don't smack\nyou for thinking about leaving.", // 1/15/98 killough + + // FinalDOOM? + +// Note that these ending "bad taste" strings were commented out +// in the original id code as the #else case of an #if 1 +// Obviously they were internal playthings before the release of +// DOOM2 and were not intended for public use. +// +// Following messages commented out for now. Bad taste. // phares + +// "fuck you, pussy!\nget the fuck out!", +// "you quit and i'll jizz\nin your cystholes!", +// "if you leave, i'll make\nthe lord drink my jizz.", +// "hey, ron! can we say\n'fuck' in the game?", +// "i'd leave: this is just\nmore monsters and levels.\nwhat a load.", +// "suck it down, asshole!\nyou're a fucking wimp!", +// "don't quit now! we're \nstill spending your money!", + + // Internal debug. Different style, too. + "THIS IS NO MESSAGE!\nPage intentionally left blank.", // 1/15/98 killough +}; + +// killough 1/18/98: remove hardcoded limit and replace with var (silly hack): +const size_t NUM_QUITMESSAGES = sizeof(endmsg)/sizeof(*endmsg) - 1; diff --git a/src/dstrings.h b/src/dstrings.h new file mode 100644 index 00000000..abb77f81 --- /dev/null +++ b/src/dstrings.h @@ -0,0 +1,80 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * DOOM strings, by language. + * Note: In BOOM, some new strings hav ebeen defined that are + * not found in the French version. A better approach is + * to create a BEX text-replacement file for other + * languages since any language can be supported that way + * without recompiling the program. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __DSTRINGS__ +#define __DSTRINGS__ + +/* All important printed strings. + * Language selection (message strings). + * Use -DFRENCH etc. + */ + +#ifdef FRENCH +#include "d_french.h" +#else +#include "d_englsh.h" +#endif + +/* Note this is not externally modifiable through DEH/BEX + * Misc. other strings. + * #define SAVEGAMENAME "boomsav" * killough 3/22/98 * + * Ty 05/04/98 - replaced with a modifiable string, see d_deh.c + */ + +/* + * File locations, + * relative to current position. + * Path names are OS-sensitive. + */ +#define DEVMAPS "devmaps" +#define DEVDATA "devdata" + + +/* Not done in french? + * QuitDOOM messages * + * killough 1/18/98: + * replace hardcoded limit with extern var (silly hack, I know) + */ + +#include + +extern const size_t NUM_QUITMESSAGES; /* Calculated in dstrings.c */ + +extern const char* const endmsg[]; /* killough 1/18/98 const added */ + +#endif diff --git a/src/f_finale.c b/src/f_finale.c new file mode 100644 index 00000000..55ef7f76 --- /dev/null +++ b/src/f_finale.c @@ -0,0 +1,668 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Game completion, final screen animation. + * + *----------------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "d_event.h" +#include "v_video.h" +#include "w_wad.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_deh.h" // Ty 03/22/98 - externalizations +#include "f_finale.h" // CPhipps - hmm... + +// Stage of animation: +// 0 = text, 1 = art screen, 2 = character cast +static int finalestage; // cph - +static int finalecount; // made static +static const char* finaletext; // cph - +static const char* finaleflat; // made static const + +// defines for the end mission display text // phares + +#define TEXTSPEED 3 // original value // phares +#define TEXTWAIT 250 // original value // phares +#define NEWTEXTSPEED 0.01f // new value // phares +#define NEWTEXTWAIT 1000 // new value // phares + +// CPhipps - removed the old finale screen text message strings; +// they were commented out for ages already +// Ty 03/22/98 - ... the new s_WHATEVER extern variables are used +// in the code below instead. + +void F_StartCast (void); +void F_CastTicker (void); +boolean F_CastResponder (event_t *ev); +void F_CastDrawer (void); + +void WI_checkForAccelerate(void); // killough 3/28/98: used to +extern int acceleratestage; // accelerate intermission screens +static int midstage; // whether we're in "mid-stage" + +// +// F_StartFinale +// +void F_StartFinale (void) +{ + gameaction = ga_nothing; + gamestate = GS_FINALE; + automapmode &= ~am_active; + + // killough 3/28/98: clear accelerative text flags + acceleratestage = midstage = 0; + + // Okay - IWAD dependend stuff. + // This has been changed severly, and + // some stuff might have changed in the process. + switch ( gamemode ) + { + // DOOM 1 - E1, E3 or E4, but each nine missions + case shareware: + case registered: + case retail: + { + S_ChangeMusic(mus_victor, true); + + switch (gameepisode) + { + case 1: + finaleflat = bgflatE1; // Ty 03/30/98 - new externalized bg flats + finaletext = s_E1TEXT; // Ty 03/23/98 - Was e1text variable. + break; + case 2: + finaleflat = bgflatE2; + finaletext = s_E2TEXT; // Ty 03/23/98 - Same stuff for each + break; + case 3: + finaleflat = bgflatE3; + finaletext = s_E3TEXT; + break; + case 4: + finaleflat = bgflatE4; + finaletext = s_E4TEXT; + break; + default: + // Ouch. + break; + } + break; + } + + // DOOM II and missions packs with E1, M34 + case commercial: + { + S_ChangeMusic(mus_read_m, true); + + // Ty 08/27/98 - added the gamemission logic + switch (gamemap) + { + case 6: + finaleflat = bgflat06; + finaletext = (gamemission==pack_tnt) ? s_T1TEXT : + (gamemission==pack_plut) ? s_P1TEXT : s_C1TEXT; + break; + case 11: + finaleflat = bgflat11; + finaletext = (gamemission==pack_tnt) ? s_T2TEXT : + (gamemission==pack_plut) ? s_P2TEXT : s_C2TEXT; + break; + case 20: + finaleflat = bgflat20; + finaletext = (gamemission==pack_tnt) ? s_T3TEXT : + (gamemission==pack_plut) ? s_P3TEXT : s_C3TEXT; + break; + case 30: + finaleflat = bgflat30; + finaletext = (gamemission==pack_tnt) ? s_T4TEXT : + (gamemission==pack_plut) ? s_P4TEXT : s_C4TEXT; + break; + case 15: + finaleflat = bgflat15; + finaletext = (gamemission==pack_tnt) ? s_T5TEXT : + (gamemission==pack_plut) ? s_P5TEXT : s_C5TEXT; + break; + case 31: + finaleflat = bgflat31; + finaletext = (gamemission==pack_tnt) ? s_T6TEXT : + (gamemission==pack_plut) ? s_P6TEXT : s_C6TEXT; + break; + default: + // Ouch. + break; + } + break; + // Ty 08/27/98 - end gamemission logic + } + + // Indeterminate. + default: // Ty 03/30/98 - not externalized + S_ChangeMusic(mus_read_m, true); + finaleflat = "F_SKY1"; // Not used anywhere else. + finaletext = s_C1TEXT; // FIXME - other text, music? + break; + } + + finalestage = 0; + finalecount = 0; +} + + + +boolean F_Responder (event_t *event) +{ + if (finalestage == 2) + return F_CastResponder (event); + + return false; +} + +// Get_TextSpeed() returns the value of the text display speed // phares +// Rewritten to allow user-directed acceleration -- killough 3/28/98 + +static float Get_TextSpeed(void) +{ + return midstage ? NEWTEXTSPEED : (midstage=acceleratestage) ? + acceleratestage=0, NEWTEXTSPEED : TEXTSPEED; +} + + +// +// F_Ticker +// +// killough 3/28/98: almost totally rewritten, to use +// player-directed acceleration instead of constant delays. +// Now the player can accelerate the text display by using +// the fire/use keys while it is being printed. The delay +// automatically responds to the user, and gives enough +// time to read. +// +// killough 5/10/98: add back v1.9 demo compatibility +// + +void F_Ticker(void) +{ + int i; + if (!demo_compatibility) + WI_checkForAccelerate(); // killough 3/28/98: check for acceleration + else + if (gamemode == commercial && finalecount > 50) // check for skipping + for (i=0; i strlen(finaletext)*speed + + (midstage ? NEWTEXTWAIT : TEXTWAIT) || + (midstage && acceleratestage)) { + if (gamemode != commercial) // Doom 1 / Ultimate Doom episode end + { // with enough time, it's automatic + finalecount = 0; + finalestage = 1; + wipegamestate = -1; // force a wipe + if (gameepisode == 3) + S_StartMusic(mus_bunny); + } + else // you must press a button to continue in Doom 2 + if (!demo_compatibility && midstage) + { + next_level: + if (gamemap == 30) + F_StartCast(); // cast of Doom 2 characters + else + gameaction = ga_worlddone; // next level, e.g. MAP07 + } + } + } +} + +// +// F_TextWrite +// +// This program displays the background and text at end-mission // phares +// text time. It draws both repeatedly so that other displays, // | +// like the main menu, can be drawn over it dynamically and // V +// erased dynamically. The TEXTSPEED constant is changed into +// the Get_TextSpeed function so that the speed of writing the // ^ +// text can be increased, and there's still time to read what's // | +// written. // phares +// CPhipps - reformatted + +#include "hu_stuff.h" +extern patchnum_t hu_font[HU_FONTSIZE]; + + +static void F_TextWrite (void) +{ + V_DrawBackground(finaleflat, 0); + { // draw some of the text onto the screen + int cx = 10; + int cy = 10; + const char* ch = finaletext; // CPhipps - const + int count = (int)((float)(finalecount - 10)/Get_TextSpeed()); // phares + int w; + + if (count < 0) + count = 0; + + for ( ; count ; count-- ) { + int c = *ch++; + + if (!c) + break; + if (c == '\n') { + cx = 10; + cy += 11; + continue; + } + + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) { + cx += 4; + continue; + } + + w = hu_font[c].width; + if (cx+w > SCREENWIDTH) + break; + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_NONE); + cx+=w; + } + } +} + +// +// Final DOOM 2 animation +// Casting by id Software. +// in order of appearance +// +typedef struct +{ + const char **name; // CPhipps - const** + mobjtype_t type; +} castinfo_t; + +#define MAX_CASTORDER 18 /* Ty - hard coded for now */ +static const castinfo_t castorder[] = { // CPhipps - static const, initialised here + { &s_CC_ZOMBIE, MT_POSSESSED }, + { &s_CC_SHOTGUN, MT_SHOTGUY }, + { &s_CC_HEAVY, MT_CHAINGUY }, + { &s_CC_IMP, MT_TROOP }, + { &s_CC_DEMON, MT_SERGEANT }, + { &s_CC_LOST, MT_SKULL }, + { &s_CC_CACO, MT_HEAD }, + { &s_CC_HELL, MT_KNIGHT }, + { &s_CC_BARON, MT_BRUISER }, + { &s_CC_ARACH, MT_BABY }, + { &s_CC_PAIN, MT_PAIN }, + { &s_CC_REVEN, MT_UNDEAD }, + { &s_CC_MANCU, MT_FATSO }, + { &s_CC_ARCH, MT_VILE }, + { &s_CC_SPIDER, MT_SPIDER }, + { &s_CC_CYBER, MT_CYBORG }, + { &s_CC_HERO, MT_PLAYER }, + { NULL, 0} + }; + +int castnum; +int casttics; +state_t* caststate; +boolean castdeath; +int castframes; +int castonmelee; +boolean castattacking; + + +// +// F_StartCast +// + +void F_StartCast (void) +{ + wipegamestate = -1; // force a screen wipe + castnum = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + casttics = caststate->tics; + castdeath = false; + finalestage = 2; + castframes = 0; + castonmelee = 0; + castattacking = false; + S_ChangeMusic(mus_evil, true); +} + + +// +// F_CastTicker +// +void F_CastTicker (void) +{ + int st; + int sfx; + + if (--casttics > 0) + return; // not time to change state yet + + if (caststate->tics == -1 || caststate->nextstate == S_NULL) + { + // switch from deathstate to next monster + castnum++; + castdeath = false; + if (castorder[castnum].name == NULL) + castnum = 0; + if (mobjinfo[castorder[castnum].type].seesound) + S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound); + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + castframes = 0; + } + else + { + // just advance to next state in animation + if (caststate == &states[S_PLAY_ATK1]) + goto stopattack; // Oh, gross hack! + st = caststate->nextstate; + caststate = &states[st]; + castframes++; + + // sound hacks.... + switch (st) + { + case S_PLAY_ATK1: sfx = sfx_dshtgn; break; + case S_POSS_ATK2: sfx = sfx_pistol; break; + case S_SPOS_ATK2: sfx = sfx_shotgn; break; + case S_VILE_ATK2: sfx = sfx_vilatk; break; + case S_SKEL_FIST2: sfx = sfx_skeswg; break; + case S_SKEL_FIST4: sfx = sfx_skepch; break; + case S_SKEL_MISS2: sfx = sfx_skeatk; break; + case S_FATT_ATK8: + case S_FATT_ATK5: + case S_FATT_ATK2: sfx = sfx_firsht; break; + case S_CPOS_ATK2: + case S_CPOS_ATK3: + case S_CPOS_ATK4: sfx = sfx_shotgn; break; + case S_TROO_ATK3: sfx = sfx_claw; break; + case S_SARG_ATK2: sfx = sfx_sgtatk; break; + case S_BOSS_ATK2: + case S_BOS2_ATK2: + case S_HEAD_ATK2: sfx = sfx_firsht; break; + case S_SKULL_ATK2: sfx = sfx_sklatk; break; + case S_SPID_ATK2: + case S_SPID_ATK3: sfx = sfx_shotgn; break; + case S_BSPI_ATK2: sfx = sfx_plasma; break; + case S_CYBER_ATK2: + case S_CYBER_ATK4: + case S_CYBER_ATK6: sfx = sfx_rlaunc; break; + case S_PAIN_ATK3: sfx = sfx_sklatk; break; + default: sfx = 0; break; + } + + if (sfx) + S_StartSound (NULL, sfx); + } + + if (castframes == 12) + { + // go into attack frame + castattacking = true; + if (castonmelee) + caststate=&states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate=&states[mobjinfo[castorder[castnum].type].missilestate]; + castonmelee ^= 1; + if (caststate == &states[S_NULL]) + { + if (castonmelee) + caststate= + &states[mobjinfo[castorder[castnum].type].meleestate]; + else + caststate= + &states[mobjinfo[castorder[castnum].type].missilestate]; + } + } + + if (castattacking) + { + if (castframes == 24 + || caststate == &states[mobjinfo[castorder[castnum].type].seestate] ) + { + stopattack: + castattacking = false; + castframes = 0; + caststate = &states[mobjinfo[castorder[castnum].type].seestate]; + } + } + + casttics = caststate->tics; + if (casttics == -1) + casttics = 15; +} + + +// +// F_CastResponder +// + +boolean F_CastResponder (event_t* ev) +{ + if (ev->type != ev_keydown) + return false; + + if (castdeath) + return true; // already in dying frames + + // go into death frame + castdeath = true; + caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; + casttics = caststate->tics; + castframes = 0; + castattacking = false; + if (mobjinfo[castorder[castnum].type].deathsound) + S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound); + + return true; +} + + +static void F_CastPrint (const char* text) // CPhipps - static, const char* +{ + const char* ch; // CPhipps - const + int c; + int cx; + int w; + int width; + + // find width + ch = text; + width = 0; + + while (ch) + { + c = *ch++; + if (!c) + break; + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + width += 4; + continue; + } + + w = hu_font[c].width; + width += w; + } + + // draw it + cx = 160-width/2; + ch = text; + while (ch) + { + c = *ch++; + if (!c) + break; + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += 4; + continue; + } + + w = hu_font[c].width; + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, 180, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_NONE); + cx+=w; + } +} + + +// +// F_CastDrawer +// + +void F_CastDrawer (void) +{ + spritedef_t* sprdef; + spriteframe_t* sprframe; + int lump; + boolean flip; + + // erase the entire screen to a background + // CPhipps - patch drawing updated + V_DrawNamePatch(0,0,0, bgcastcall, CR_DEFAULT, VPT_NONE); // Ty 03/30/98 bg texture extern + + F_CastPrint (*(castorder[castnum].name)); + + // draw the current frame in the middle of the screen + sprdef = &sprites[caststate->sprite]; + sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK]; + lump = sprframe->lump[0]; + flip = (boolean)sprframe->flip[0]; + + // CPhipps - patch drawing updated + V_DrawNumPatch(160, 170, 0, lump+firstspritelump, CR_DEFAULT, + (flip ? VPT_FLIP : 0)); +} + +// +// F_BunnyScroll +// +static const char pfub2[] = { "PFUB2" }; +static const char pfub1[] = { "PFUB1" }; + +static void F_BunnyScroll (void) +{ + char name[10]; + int stage; + static int laststage; + + { + int scrolled = 320 - (finalecount-230)/2; + if (scrolled <= 0) { + V_DrawNamePatch(0, 0, 0, pfub2, CR_DEFAULT, VPT_NONE); + } else if (scrolled >= 320) { + V_DrawNamePatch(0, 0, 0, pfub1, CR_DEFAULT, VPT_NONE); + } else { + V_DrawNamePatch(320-scrolled, 0, 0, pfub1, CR_DEFAULT, VPT_NONE); + V_DrawNamePatch(-scrolled, 0, 0, pfub2, CR_DEFAULT, VPT_NONE); + } + } + + if (finalecount < 1130) + return; + if (finalecount < 1180) + { + // CPhipps - patch drawing updated + V_DrawNamePatch((320-13*8)/2, (200-8*8)/2,0, "END0", CR_DEFAULT, VPT_NONE); + laststage = 0; + return; + } + + stage = (finalecount-1180) / 5; + if (stage > 6) + stage = 6; + if (stage > laststage) + { + S_StartSound (NULL, sfx_pistol); + laststage = stage; + } + + sprintf (name,"END%i",stage); + // CPhipps - patch drawing updated + V_DrawNamePatch((320-13*8)/2, (200-8*8)/2, 0, name, CR_DEFAULT, VPT_NONE); +} + + +// +// F_Drawer +// +void F_Drawer (void) +{ + if (finalestage == 2) + { + F_CastDrawer (); + return; + } + + if (!finalestage) + F_TextWrite (); + else + { + switch (gameepisode) + { + // CPhipps - patch drawing updated + case 1: + if ( gamemode == retail ) + V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_NONE); + else + V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_NONE); + break; + case 2: + V_DrawNamePatch(0, 0, 0, "VICTORY2", CR_DEFAULT, VPT_NONE); + break; + case 3: + F_BunnyScroll (); + break; + case 4: + V_DrawNamePatch(0, 0, 0, "ENDPIC", CR_DEFAULT, VPT_NONE); + break; + } + } +} diff --git a/src/f_finale.h b/src/f_finale.h new file mode 100644 index 00000000..6214f38f --- /dev/null +++ b/src/f_finale.h @@ -0,0 +1,56 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Related to f_finale.c, which is called at the end of a level + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __F_FINALE__ +#define __F_FINALE__ + +#include "doomtype.h" +#include "d_event.h" + +/* + * FINALE + */ + +/* Called by main loop. */ +boolean F_Responder (event_t* ev); + +/* Called by main loop. */ +void F_Ticker (void); + +/* Called by main loop. */ +void F_Drawer (void); + +void F_StartFinale (void); + +#endif diff --git a/src/f_wipe.c b/src/f_wipe.c new file mode 100644 index 00000000..2cbf659d --- /dev/null +++ b/src/f_wipe.c @@ -0,0 +1,195 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Mission begin melt/wipe screen special effect. + * + *----------------------------------------------------------------------------- + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "z_zone.h" +#include "doomdef.h" +#include "i_video.h" +#include "v_video.h" +#include "m_random.h" +#include "f_wipe.h" + +// +// SCREEN WIPE PACKAGE +// + +// Parts re-written to support true-color video modes. Column-major +// formatting removed. - POPE + +// CPhipps - macros for the source and destination screens +#define SRC_SCR 2 +#define DEST_SCR 3 + +static screeninfo_t wipe_scr_start; +static screeninfo_t wipe_scr_end; +static screeninfo_t wipe_scr; + +static int y_lookup[MAX_SCREENWIDTH]; + + +static int wipe_initMelt(int ticks) +{ + int i; + + // copy start screen to main screen + for(i=0;i not ready to scroll yet) + y_lookup[0] = -(M_Random()%16); + for (i=1;i 0) + y_lookup[i] = 0; + else + if (y_lookup[i] == -16) + y_lookup[i] = -15; + } + return 0; +} + +static int wipe_doMelt(int ticks) +{ + boolean done = true; + int i; + + while (ticks--) { + for (i=0;i<(SCREENWIDTH);i++) { + if (y_lookup[i]<0) { + y_lookup[i]++; + done = false; + continue; + } + if (y_lookup[i] < SCREENHEIGHT) { + byte *s, *d; + int j, dy; + + /* cph 2001/07/29 - + * The original melt rate was 8 pixels/sec, i.e. 25 frames to melt + * the whole screen, so make the melt rate depend on SCREENHEIGHT + * so it takes no longer in high res + */ + dy = (y_lookup[i] < 16) ? y_lookup[i]+1 : SCREENHEIGHT/25; + if (y_lookup[i]+dy >= SCREENHEIGHT) + dy = SCREENHEIGHT - y_lookup[i]; + + s = wipe_scr_end.data + (y_lookup[i] * SURFACE_BYTE_PITCH +(i * SURFACE_PIXEL_DEPTH)); + d = wipe_scr.data + (y_lookup[i] * SURFACE_BYTE_PITCH +(i * SURFACE_PIXEL_DEPTH)); + for (j=dy;j;j--) { + + d[0] = s[0]; + d[1] = s[1]; + + d += SURFACE_BYTE_PITCH; + s += SURFACE_BYTE_PITCH; + } + y_lookup[i] += dy; + s = wipe_scr_start.data + (i * SURFACE_PIXEL_DEPTH); + d = wipe_scr.data + (y_lookup[i] * SURFACE_BYTE_PITCH +(i * SURFACE_PIXEL_DEPTH)); + for (j=SCREENHEIGHT-y_lookup[i];j;j--) { + + d[0] = s[0]; + d[1] = s[1]; + + d += SURFACE_BYTE_PITCH; + s += SURFACE_BYTE_PITCH; + } + done = false; + } + } + } + return done; +} + +// CPhipps - modified to allocate and deallocate screens[2 to 3] as needed, saving memory + +static int wipe_exitMelt(int ticks) +{ + V_FreeScreen(&wipe_scr_start); + wipe_scr_start.height = 0; + V_FreeScreen(&wipe_scr_end); + wipe_scr_end.height = 0; + // Paranoia + screens[SRC_SCR] = wipe_scr_start; + screens[DEST_SCR] = wipe_scr_end; + return 0; +} + +int wipe_StartScreen(void) +{ + wipe_scr_start.height = SCREENHEIGHT; + wipe_scr_start.not_on_heap = false; + V_AllocScreen(&wipe_scr_start); + screens[SRC_SCR] = wipe_scr_start; + V_CopyRect(0, 0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0, SRC_SCR, VPT_NONE ); // Copy start screen to buffer + return 0; +} + +int wipe_EndScreen(void) +{ + wipe_scr_end.height = SCREENHEIGHT; + wipe_scr_end.not_on_heap = false; + V_AllocScreen(&wipe_scr_end); + screens[DEST_SCR] = wipe_scr_end; + V_CopyRect(0, 0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0, DEST_SCR, VPT_NONE); // Copy end screen to buffer + V_CopyRect(0, 0, SRC_SCR, SCREENWIDTH, SCREENHEIGHT, 0, 0, 0 , VPT_NONE); // restore start screen + return 0; +} + +// killough 3/5/98: reformatted and cleaned up +int wipe_ScreenWipe(int ticks) +{ + static boolean go; // when zero, stop the wipe + if (!go) // initial stuff + { + go = 1; + wipe_scr = screens[0]; + wipe_initMelt(ticks); + } + // do a piece of wipe-in + if (wipe_doMelt(ticks)) // final stuff + { + wipe_exitMelt(ticks); + go = 0; + } + return !go; +} diff --git a/src/f_wipe.h b/src/f_wipe.h new file mode 100644 index 00000000..d991a50c --- /dev/null +++ b/src/f_wipe.h @@ -0,0 +1,45 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Mission start screen wipe/melt, special effects. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __F_WIPE_H__ +#define __F_WIPE_H__ + +/* + * SCREEN WIPE PACKAGE + */ + +int wipe_ScreenWipe (int ticks); +int wipe_StartScreen(void); +int wipe_EndScreen (void); + +#endif diff --git a/src/g_game.c b/src/g_game.c new file mode 100644 index 00000000..f4c9e83e --- /dev/null +++ b/src/g_game.c @@ -0,0 +1,2625 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: none + * The original Doom description was none, basically because this file + * has everything. This ties up the game logic, linking the menu and + * input code to the underlying game by creating & respawning players, + * building game tics, calling the underlying thing logic. + * + *----------------------------------------------------------------------------- + */ + +#include +#include +#include +#ifdef _MSC_VER +#define F_OK 0 /* Check for file existence */ +#define W_OK 2 /* Check for write permission */ +#define R_OK 4 /* Check for read permission */ +#include +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "d_net.h" +#include "f_finale.h" +#include "m_argv.h" +#include "m_misc.h" +#include "m_menu.h" +#include "m_random.h" +#include "p_setup.h" +#include "p_saveg.h" +#include "p_tick.h" +#include "p_map.h" +#include "p_checksum.h" +#include "d_main.h" +#include "wi_stuff.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "am_map.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_draw.h" +#include "p_map.h" +#include "s_sound.h" +#include "dstrings.h" +#include "sounds.h" +#include "r_data.h" +#include "r_sky.h" +#include "d_deh.h" // Ty 3/27/98 deh declarations +#include "p_inter.h" +#include "g_game.h" +#include "lprintf.h" +#include "i_main.h" +#include "i_system.h" +#include "r_demo.h" +#include "r_fps.h" + +#define SAVEGAMESIZE 0x20000 +#define SAVESTRINGSIZE 24 + +static size_t savegamesize = SAVEGAMESIZE; // killough +static boolean netdemo; +static const byte *demobuffer; /* cph - only used for playback */ +static int demolength; // check for overrun (missing DEMOMARKER) +static FILE *demofp; /* cph - record straight to file */ +static const byte *demo_p; +static short consistancy[MAXPLAYERS][BACKUPTICS]; + +gameaction_t gameaction; +gamestate_t gamestate; +skill_t gameskill; +boolean respawnmonsters; +int gameepisode; +int gamemap; +boolean paused; +// CPhipps - moved *_loadgame vars here +static boolean forced_loadgame = false; +static boolean command_loadgame = false; + +boolean usergame; // ok to save / end game +boolean deathmatch; // only if started as net death +boolean netgame; // only true if packets are broadcast +boolean playeringame[MAXPLAYERS]; +player_t players[MAXPLAYERS]; +int consoleplayer; // player taking events and displaying +int displayplayer; // view being displayed +int gametic; +int basetic; /* killough 9/29/98: for demo sync */ +int totalkills, totallive, totalitems, totalsecret; // for intermission +boolean demoplayback; +int demover; +wbstartstruct_t wminfo; // parms for world map / intermission +boolean haswolflevels = false;// jff 4/18/98 wolf levels present +static byte *savebuffer; // CPhipps - static +int autorun = false; // always running? // phares +int totalleveltimes; // CPhipps - total time for all completed levels +int longtics; + +// +// controls (have defaults) +// + +int key_right; +int key_left; +int key_up; +int key_down; +int key_menu_right; // phares 3/7/98 +int key_menu_left; // | +int key_menu_up; // V +int key_menu_down; +int key_menu_backspace; // ^ +int key_menu_escape; // | +int key_menu_enter; // phares 3/7/98 +int key_strafeleft; +int key_straferight; +int key_fire; +int key_use; +int key_strafe; +int key_speed; +int key_escape = KEYD_ESCAPE; // phares 4/13/98 +int key_savegame; // phares +int key_loadgame; // | +int key_autorun; // V +int key_reverse; +int key_zoomin; +int key_zoomout; +int key_chat; +int key_backspace; +int key_enter; +int key_map_right; +int key_map_left; +int key_map_up; +int key_map_down; +int key_map_zoomin; +int key_map_zoomout; +int key_map; +int key_map_gobig; +int key_map_follow; +int key_map_mark; +int key_map_clear; +int key_map_grid; +int key_map_overlay; // cph - map overlay +int key_map_rotate; // cph - map rotation +int key_help = KEYD_F1; // phares 4/13/98 +int key_soundvolume; +int key_hud; +int key_quicksave; +int key_endgame; +int key_messages; +int key_quickload; +int key_quit; +int key_gamma; +int key_spy; +int key_pause; +int key_setup; +int destination_keys[MAXPLAYERS]; +int key_weapontoggle; +int key_weaponcycleup; +int key_weaponcycledown; +int key_weapon1; +int key_weapon2; +int key_weapon3; +int key_weapon4; +int key_weapon5; +int key_weapon6; +int key_weapon7; // ^ +int key_weapon8; // | +int key_weapon9; // phares + +int key_screenshot; // killough 2/22/98: screenshot key +int mousebfire; +int mousebstrafe; +int mousebforward; +int mousebbackward; + +#define MAXPLMOVE (forwardmove[1]) +#define TURBOTHRESHOLD 0x32 +#define SLOWTURNTICS 6 +#define QUICKREVERSE (short)32768 // 180 degree reverse // phares +#define NUMKEYS 512 + +fixed_t forwardmove[2] = {0x19, 0x32}; +fixed_t sidemove[2] = {0x18, 0x28}; +fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn + +// CPhipps - made lots of key/button state vars static +static boolean gamekeydown[NUMKEYS]; +static int turnheld; // for accelerative turning + +static boolean mousearray[4]; +static boolean *mousebuttons = &mousearray[1]; // allow [-1] + +// mouse values are used once +static int mousex; +static int mousey; + +// Game events info +static buttoncode_t special_event; // Event triggered by local player, to send +static byte savegameslot; // Slot to load if gameaction == ga_loadgame +char savedescription[SAVEDESCLEN]; // Description to save in savegame if gameaction == ga_savegame + +//jff 3/24/98 define defaultskill here +int defaultskill; //note 1-based + +// killough 2/8/98: make corpse queue variable in size +int bodyqueslot, bodyquesize; // killough 2/8/98 +mobj_t **bodyque = 0; // phares 8/10/98 + +static void G_DoSaveGame (boolean menu); +static const byte* G_ReadDemoHeader(const byte* demo_p, size_t size, boolean failonerror); + +// +// G_BuildTiccmd +// Builds a ticcmd from all of the available inputs +// or reads it from the demo buffer. +// If recording a demo, write it out +// +static inline signed char fudgef(signed char b) +{ + static int c; + if (!b || !demo_compatibility || longtics) return b; + if (++c & 0x1f) return b; + b |= 1; if (b>2) b-=2; + return b; +} + +static inline signed short fudgea(signed short b) +{ + if (!b || !demo_compatibility || !longtics) return b; + b |= 1; if (b>2) b-=2; + return b; +} + + +void G_BuildTiccmd(ticcmd_t* cmd) +{ + boolean strafe; + boolean bstrafe; + int speed; + int tspeed; + int forward; + int side; + int newweapon; // phares + /* cphipps - remove needless I_BaseTiccmd call, just set the ticcmd to zero */ + memset(cmd,0,sizeof*cmd); + cmd->consistancy = consistancy[consoleplayer][maketic%BACKUPTICS]; + + strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]; + //e6y: the "RUN" key inverts the autorun state + speed = (gamekeydown[key_speed] ? !autorun : autorun); // phares + + forward = side = 0; + + // use two stage accelerative turning + // on the keyboard + if (gamekeydown[key_right] || gamekeydown[key_left]) + turnheld += ticdup; + else + turnheld = 0; + + if (turnheld < SLOWTURNTICS) + tspeed = 2; // slow turn + else + tspeed = speed; + + // turn 180 degrees in one keystroke? // phares + // | + if (gamekeydown[key_reverse]) // V + { + cmd->angleturn += QUICKREVERSE; // ^ + gamekeydown[key_reverse] = false; // | + } // phares + + // let movement keys cancel each other out + + if (strafe) + { + if (gamekeydown[key_right]) + side += sidemove[speed]; + if (gamekeydown[key_left]) + side -= sidemove[speed]; + } + else + { + if (gamekeydown[key_right]) + cmd->angleturn -= angleturn[tspeed]; + if (gamekeydown[key_left]) + cmd->angleturn += angleturn[tspeed]; + } + + if (gamekeydown[key_up]) + forward += forwardmove[speed]; + if (gamekeydown[key_down]) + forward -= forwardmove[speed]; + if (gamekeydown[key_straferight]) + side += sidemove[speed]; + if (gamekeydown[key_strafeleft]) + side -= sidemove[speed]; + + // buttons + cmd->chatchar = HU_dequeueChatChar(); + + if (gamekeydown[key_fire] || mousebuttons[mousebfire]) + cmd->buttons |= BT_ATTACK; + + if (gamekeydown[key_use] || mousebuttons[mousebforward]) + { + cmd->buttons |= BT_USE; + } + + // Toggle between the top 2 favorite weapons. // phares + // If not currently aiming one of these, switch to // phares + // the favorite. Only switch if you possess the weapon. // phares + + // killough 3/22/98: + // + // Perform automatic weapons switch here rather than in p_pspr.c, + // except in demo_compatibility mode. + // + // killough 3/26/98, 4/2/98: fix autoswitch when no weapons are left + + if ((!demo_compatibility && players[consoleplayer].attackdown && // killough + !P_CheckAmmo(&players[consoleplayer])) || gamekeydown[key_weapontoggle]) + { + newweapon = P_SwitchWeapon(&players[consoleplayer]); // phares + } + else if (gamekeydown[key_weaponcycleup] || gamekeydown[key_weaponcycledown]) + { + static weapontype_t weapons_list[] = {wp_fist, wp_chainsaw, wp_pistol, wp_shotgun, wp_supershotgun, wp_chaingun, wp_missile, wp_plasma, wp_bfg}; + int i; + weapontype_t origweapon = players[consoleplayer].readyweapon; + int oldweapon = 0; + + for(i = 0; i != 9; i ++) + { + if(weapons_list[i] == players[consoleplayer].readyweapon) + { + oldweapon = i; + break; + } + } + + for(i = 0; i != 9; i ++) + { + if(gamekeydown[key_weaponcycleup]) + oldweapon ++; + else + oldweapon --; + if(oldweapon < 0) + oldweapon = 8; + if(oldweapon > 8) + oldweapon = 0; + + // I SHOULD just write a P_CheckAmmo that takes the weapon as an argument... This will die if multithreaded... + players[consoleplayer].readyweapon = weapons_list[oldweapon]; + + if(players[consoleplayer].weaponowned[players[consoleplayer].readyweapon] && P_CheckAmmo(&players[consoleplayer])) + { + newweapon = players[consoleplayer].readyweapon; + break; + } + } + players[consoleplayer].readyweapon = origweapon; + } + else + { // phares 02/26/98: Added gamemode checks + newweapon = + gamekeydown[key_weapon1] ? wp_fist : // killough 5/2/98: reformatted + gamekeydown[key_weapon2] ? wp_pistol : + gamekeydown[key_weapon3] ? wp_shotgun : + gamekeydown[key_weapon4] ? wp_chaingun : + gamekeydown[key_weapon5] ? wp_missile : + gamekeydown[key_weapon6] && gamemode != shareware ? wp_plasma : + gamekeydown[key_weapon7] && gamemode != shareware ? wp_bfg : + gamekeydown[key_weapon8] ? wp_chainsaw : + (!demo_compatibility && gamekeydown[key_weapon9] && gamemode == commercial) ? wp_supershotgun : + wp_nochange; + + // killough 3/22/98: For network and demo consistency with the + // new weapons preferences, we must do the weapons switches here + // instead of in p_user.c. But for old demos we must do it in + // p_user.c according to the old rules. Therefore demo_compatibility + // determines where the weapons switch is made. + + // killough 2/8/98: + // Allow user to switch to fist even if they have chainsaw. + // Switch to fist or chainsaw based on preferences. + // Switch to shotgun or SSG based on preferences. + + if (!demo_compatibility) + { + const player_t *player = &players[consoleplayer]; + + // only select chainsaw from '1' if it's owned, it's + // not already in use, and the player prefers it or + // the fist is already in use, or the player does not + // have the berserker strength. + + if (newweapon==wp_fist && player->weaponowned[wp_chainsaw] && + player->readyweapon!=wp_chainsaw && + (player->readyweapon==wp_fist || + !player->powers[pw_strength] || + P_WeaponPreferred(wp_chainsaw, wp_fist))) + newweapon = wp_chainsaw; + + // Select SSG from '3' only if it's owned and the player + // does not have a shotgun, or if the shotgun is already + // in use, or if the SSG is not already in use and the + // player prefers it. + + if (newweapon == wp_shotgun && gamemode == commercial && + player->weaponowned[wp_supershotgun] && + (!player->weaponowned[wp_shotgun] || + player->readyweapon == wp_shotgun || + (player->readyweapon != wp_supershotgun && + P_WeaponPreferred(wp_supershotgun, wp_shotgun)))) + newweapon = wp_supershotgun; + } + // killough 2/8/98, 3/22/98 -- end of weapon selection changes + } + + if (newweapon != wp_nochange) + { + cmd->buttons |= BT_CHANGE; + cmd->buttons |= newweapon<angleturn -= mousex; /* mead now have enough dynamic range 2-10-00 */ + + mousex = mousey = 0; + + if (forward > MAXPLMOVE) + forward = MAXPLMOVE; + else if (forward < -MAXPLMOVE) + forward = -MAXPLMOVE; + if (side > MAXPLMOVE) + side = MAXPLMOVE; + else if (side < -MAXPLMOVE) + side = -MAXPLMOVE; + + cmd->forwardmove += fudgef((signed char)forward); + cmd->sidemove += side; + cmd->angleturn = fudgea(cmd->angleturn); + + // CPhipps - special events (game new/load/save/pause) + if (special_event & BT_SPECIAL) { + cmd->buttons = special_event; + special_event = 0; + } +} + +// +// G_RestartLevel +// + +void G_RestartLevel(void) +{ + special_event = BT_SPECIAL | (BTS_RESTARTLEVEL & BT_SPECIALMASK); +} + +#include "z_bmalloc.h" +// +// G_DoLoadLevel +// + +static void G_DoLoadLevel (void) +{ + int i; + + // Set the sky map. + // First thing, we have a dummy sky texture name, + // a flat. The data is in the WAD only because + // we look for an actual index, instead of simply + // setting one. + + skyflatnum = R_FlatNumForName ( SKYFLATNAME ); + + // DOOM determines the sky texture to be used + // depending on the current episode, and the game version. + if (gamemode == commercial) + // || gamemode == pack_tnt //jff 3/27/98 sorry guys pack_tnt,pack_plut + // || gamemode == pack_plut) //aren't gamemodes, this was matching retail + { + skytexture = R_TextureNumForName ("SKY3"); + if (gamemap < 12) + skytexture = R_TextureNumForName ("SKY1"); + else + if (gamemap < 21) + skytexture = R_TextureNumForName ("SKY2"); + } + else //jff 3/27/98 and lets not forget about DOOM and Ultimate DOOM huh? + switch (gameepisode) + { + case 1: + skytexture = R_TextureNumForName ("SKY1"); + break; + case 2: + skytexture = R_TextureNumForName ("SKY2"); + break; + case 3: + skytexture = R_TextureNumForName ("SKY3"); + break; + case 4: // Special Edition sky + skytexture = R_TextureNumForName ("SKY4"); + break; + }//jff 3/27/98 end sky setting fix + + /* cph 2006/07/31 - took out unused levelstarttic variable */ + + if (!demo_compatibility && !mbf_features) // killough 9/29/98 + basetic = gametic; + + if (wipegamestate == GS_LEVEL && (gameaction == ga_newgame || gameaction == ga_completed)) + wipegamestate = -1; // force a wipe + + gamestate = GS_LEVEL; + + for (i=0 ; idata1 == key_spy && netgame && (demoplayback || !deathmatch) && + gamestate == GS_LEVEL) + { + if (ev->type == ev_keyup) + gamekeydown[key_spy] = false; + if (ev->type == ev_keydown && !gamekeydown[key_spy]) + { + gamekeydown[key_spy] = true; + do // spy mode + if (++displayplayer >= MAXPLAYERS) + displayplayer = 0; + while (!playeringame[displayplayer] && displayplayer!=consoleplayer); + + ST_Start(); // killough 3/7/98: switch status bar views too + HU_Start(); + S_UpdateSounds(players[displayplayer].mo); + R_ActivateSectorInterpolations(); + R_SmoothPlaying_Reset(NULL); + } + return true; + } + + // any other key pops up menu if in demos + // + // killough 8/2/98: enable automap in -timedemo demos + // + // killough 9/29/98: make any key pop up menu regardless of + // which kind of demo, and allow other events during playback + + if (gameaction == ga_nothing && (demoplayback || gamestate == GS_DEMOSCREEN)) + { + // killough 9/29/98: allow user to pause demos during playback + if (ev->type == ev_keydown && ev->data1 == key_pause) + { + if (paused ^= 2) + S_PauseSound(); + else + S_ResumeSound(); + return true; + } + + // killough 10/98: + // Don't pop up menu, if paused in middle + // of demo playback, or if automap active. + // Don't suck up keys, which may be cheats + + return gamestate == GS_DEMOSCREEN && + !(paused & 2) && !(automapmode & am_active) && + ((ev->type == ev_keydown) || + (ev->type == ev_mouse && ev->data1)) ? + M_StartControlPanel(), true : false; + } + + if (gamestate == GS_FINALE && F_Responder(ev)) + return true; // finale ate the event + + switch (ev->type) + { + case ev_keydown: + if (ev->data1 == key_pause) // phares + { + special_event = BT_SPECIAL | (BTS_PAUSE & BT_SPECIALMASK); + return true; + } + if (ev->data1 data1] = true; + return true; // eat key down events + + case ev_keyup: + if (ev->data1 data1] = false; + return false; // always let key up events filter down + + case ev_mouse: + mousebuttons[0] = ev->data1 & 1; + mousebuttons[1] = ev->data1 & 2; + mousebuttons[2] = ev->data1 & 4; + /* + * bmead@surfree.com + * Modified by Barry Mead after adding vastly more resolution + * to the Mouse Sensitivity Slider in the options menu 1-9-2000 + * Removed the mouseSensitivity "*4" to allow more low end + * sensitivity resolution especially for lsdoom users. + */ + mousex += (ev->data2*(mouseSensitivity_horiz))/10; /* killough */ + mousey += (ev->data3*(mouseSensitivity_vert))/10; /*Mead rm *4 */ + return true; // eat events + default: + break; + } + return false; +} + +// +// G_Ticker +// Make ticcmd_ts for the players. +// + +void G_Ticker (void) +{ + int i; + static gamestate_t prevgamestate; + + // CPhipps - player colour changing + if (!demoplayback && mapcolor_plyr[consoleplayer] != mapcolor_me) { + // Changed my multiplayer colour - Inform the whole game + int net_cl = LONG(mapcolor_me); +#ifdef HAVE_NET + D_NetSendMisc(nm_plcolour, sizeof(net_cl), &net_cl); +#endif + G_ChangedPlayerColour(consoleplayer, mapcolor_me); + } + P_MapStart(); + // do player reborns if needed + for (i=0 ; iforwardmove > TURBOTHRESHOLD && + !(gametic&31) && ((gametic>>5)&3) == i ) + { + extern char *player_names[]; + /* cph - don't use sprintf, use doom_printf */ + doom_printf ("%s is turbo!", player_names[i]); + } + + if (netgame && !netdemo && !(gametic%ticdup) ) + { + if (gametic > BACKUPTICS + && consistancy[i][buf] != cmd->consistancy) + I_Error("G_Ticker: Consistency failure (%i should be %i)", + cmd->consistancy, consistancy[i][buf]); + if (players[i].mo) + consistancy[i][buf] = players[i].mo->x; + else + consistancy[i][buf] = 0; // killough 2/14/98 + } + } + } + + // check for special buttons + for (i=0; i>BTS_SAVESHIFT; + gameaction = ga_savegame; + break; + + // CPhipps - remote loadgame request + case BTS_LOADGAME: + savegameslot = + (players[i].cmd.buttons & BTS_SAVEMASK)>>BTS_SAVESHIFT; + gameaction = ga_loadgame; + forced_loadgame = netgame; // Force if a netgame + command_loadgame = false; + break; + + // CPhipps - Restart the level + case BTS_RESTARTLEVEL: + if (demoplayback || (compatibility_level < lxdoom_1_compatibility)) + break; // CPhipps - Ignore in demos or old games + gameaction = ga_loadlevel; + break; + } + players[i].cmd.buttons = 0; + } + } + } + } + + // cph - if the gamestate changed, we may need to clean up the old gamestate + if (gamestate != prevgamestate) { + switch (prevgamestate) { + case GS_LEVEL: + // This causes crashes at level end - Neil Stevens + // The crash is because the sounds aren't stopped before freeing them + // the following is a possible fix + // This fix does avoid the crash wowever, with this fix in, the exit + // switch sound is cut off + // S_Stop(); + // Z_FreeTags(PU_LEVEL, PU_PURGELEVEL-1); + break; + case GS_INTERMISSION: + WI_End(); + default: + break; + } + prevgamestate = gamestate; + } + + // e6y + // do nothing if a pause has been pressed during playback + // pausing during intermission can cause desynchs without that + if (paused & 2 && gamestate != GS_LEVEL) + return; + + // do main actions + switch (gamestate) + { + case GS_LEVEL: + P_Ticker (); + ST_Ticker (); + AM_Ticker (); + HU_Ticker (); + break; + + case GS_INTERMISSION: + WI_Ticker (); + break; + + case GS_FINALE: + F_Ticker (); + break; + + case GS_DEMOSCREEN: + D_PageTicker (); + break; + } +} + +// +// PLAYER STRUCTURE FUNCTIONS +// also see P_SpawnPlayer in P_Things +// + +// +// G_PlayerFinishLevel +// Can when a player completes a level. +// + +static void G_PlayerFinishLevel(int player) +{ + player_t *p = &players[player]; + memset(p->powers, 0, sizeof p->powers); + memset(p->cards, 0, sizeof p->cards); + p->mo = NULL; // cph - this is allocated PU_LEVEL so it's gone + p->extralight = 0; // cancel gun flashes + p->fixedcolormap = 0; // cancel ir gogles + p->damagecount = 0; // no palette changes + p->bonuscount = 0; +} + +// CPhipps - G_SetPlayerColour +// Player colours stuff +// +// G_SetPlayerColour + +#include "r_draw.h" + +void G_ChangedPlayerColour(int pn, int cl) +{ + int i; + + if (!netgame) return; + + mapcolor_plyr[pn] = cl; + + // Rebuild colour translation tables accordingly + R_InitTranslationTables(); + // Change translations on existing player mobj's + for (i=0; iflags &= ~MF_TRANSLATION; + players[i].mo->flags |= playernumtotrans[i] << MF_TRANSSHIFT; + } + } +} + +// +// G_PlayerReborn +// Called after a player dies +// almost everything is cleared and initialized +// + +void G_PlayerReborn (int player) +{ + player_t *p; + int i; + int frags[MAXPLAYERS]; + int killcount; + int itemcount; + int secretcount; + + memcpy (frags, players[player].frags, sizeof frags); + killcount = players[player].killcount; + itemcount = players[player].itemcount; + secretcount = players[player].secretcount; + + p = &players[player]; + + // killough 3/10/98,3/21/98: preserve cheats across idclev + { + int cheats = p->cheats; + memset (p, 0, sizeof(*p)); + p->cheats = cheats; + } + + memcpy(players[player].frags, frags, sizeof(players[player].frags)); + players[player].killcount = killcount; + players[player].itemcount = itemcount; + players[player].secretcount = secretcount; + + p->usedown = p->attackdown = true; // don't do anything immediately + p->playerstate = PST_LIVE; + p->health = initial_health; // Ty 03/12/98 - use dehacked values + p->readyweapon = p->pendingweapon = wp_pistol; + p->weaponowned[wp_fist] = true; + p->weaponowned[wp_pistol] = true; + p->ammo[am_clip] = initial_bullets; // Ty 03/12/98 - use dehacked values + + for (i=0 ; imaxammo[i] = maxammo[i]; +} + +// +// G_CheckSpot +// Returns false if the player cannot be respawned +// at the given mapthing_t spot +// because something is occupying it +// + +static boolean G_CheckSpot(int playernum, mapthing_t *mthing) +{ + fixed_t x,y; + subsector_t *ss; + int i; + + if (!players[playernum].mo) + { + // first spawn of level, before corpses + for (i=0 ; ix == mthing->x << FRACBITS + && players[i].mo->y == mthing->y << FRACBITS) + return false; + return true; + } + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + // killough 4/2/98: fix bug where P_CheckPosition() uses a non-solid + // corpse to detect collisions with other players in DM starts + // + // Old code: + // if (!P_CheckPosition (players[playernum].mo, x, y)) + // return false; + + players[playernum].mo->flags |= MF_SOLID; + i = P_CheckPosition(players[playernum].mo, x, y); + players[playernum].mo->flags &= ~MF_SOLID; + if (!i) + return false; + + // flush an old corpse if needed + // killough 2/8/98: make corpse queue have an adjustable limit + // killough 8/1/98: Fix bugs causing strange crashes + + if (bodyquesize > 0) + { + static int queuesize; + if (queuesize < bodyquesize) + { + bodyque = realloc(bodyque, bodyquesize*sizeof*bodyque); + memset(bodyque+queuesize, 0, + (bodyquesize-queuesize)*sizeof*bodyque); + queuesize = bodyquesize; + } + if (bodyqueslot >= bodyquesize) + P_RemoveMobj(bodyque[bodyqueslot % bodyquesize]); + bodyque[bodyqueslot++ % bodyquesize] = players[playernum].mo; + } + else + if (!bodyquesize) + P_RemoveMobj(players[playernum].mo); + + // spawn a teleport fog + ss = R_PointInSubsector (x,y); + { // Teleport fog at respawn point + fixed_t xa,ya; + int an; + mobj_t *mo; + +/* BUG: an can end up negative, because mthing->angle is (signed) short. + * We have to emulate original Doom's behaviour, deferencing past the start + * of the array, into the previous array (finetangent) */ + an = ( ANG45 * ((signed)mthing->angle/45) ) >> ANGLETOFINESHIFT; + xa = finecosine[an]; + ya = finesine[an]; + + if (compatibility_level <= finaldoom_compatibility || compatibility_level == prboom_4_compatibility) + switch (an) { + case -4096: xa = finetangent[2048]; // finecosine[-4096] + ya = finetangent[0]; // finesine[-4096] + break; + case -3072: xa = finetangent[3072]; // finecosine[-3072] + ya = finetangent[1024]; // finesine[-3072] + break; + case -2048: xa = finesine[0]; // finecosine[-2048] + ya = finetangent[2048]; // finesine[-2048] + break; + case -1024: xa = finesine[1024]; // finecosine[-1024] + ya = finetangent[3072]; // finesine[-1024] + break; + case 1024: + case 2048: + case 3072: + case 4096: + case 0: break; /* correct angles set above */ + default: I_Error("G_CheckSpot: unexpected angle %d\n",an); + } + + mo = P_SpawnMobj(x+20*xa, y+20*ya, ss->sector->floorheight, MT_TFOG); + + if (players[consoleplayer].viewz != 1) + S_StartSound(mo, sfx_telept); // don't start sound on first frame + } + + return true; +} + + +// G_DeathMatchSpawnPlayer +// Spawns a player at one of the random death match spots +// called at level load and each death +// +void G_DeathMatchSpawnPlayer (int playernum) +{ + int j, selections = deathmatch_p - deathmatchstarts; + + if (selections < MAXPLAYERS) + I_Error("G_DeathMatchSpawnPlayer: Only %i deathmatch spots, %d required", + selections, MAXPLAYERS); + + for (j=0 ; j<20 ; j++) + { + int i = P_Random(pr_dmspawn) % selections; + if (G_CheckSpot (playernum, &deathmatchstarts[i]) ) + { + deathmatchstarts[i].type = playernum+1; + P_SpawnPlayer (playernum, &deathmatchstarts[i]); + return; + } + } + + // no good spot, so the player will probably get stuck + P_SpawnPlayer (playernum, &playerstarts[playernum]); +} + +// +// G_DoReborn +// + +void G_DoReborn (int playernum) +{ + if (!netgame) + gameaction = ga_loadlevel; // reload the level from scratch + else + { // respawn at the start + int i; + + // first dissasociate the corpse + players[playernum].mo->player = NULL; + + // spawn at random spot if in death match + if (deathmatch) + { + G_DeathMatchSpawnPlayer (playernum); + return; + } + + if (G_CheckSpot (playernum, &playerstarts[playernum]) ) + { + P_SpawnPlayer (playernum, &playerstarts[playernum]); + return; + } + + // try to spawn at one of the other players spots + for (i=0 ; i lump); + return s; +} + +static uint_64_t G_Signature(void) +{ + static uint_64_t s = 0; + static boolean computed = false; + char name[9]; + int episode, map; + + if (!computed) { + computed = true; + if (gamemode == commercial) + for (map = haswolflevels ? 32 : 30; map; map--) + sprintf(name, "map%02d", map), s = G_UpdateSignature(s, name); + else + for (episode = gamemode==retail ? 4 : + gamemode==shareware ? 1 : 3; episode; episode--) + for (map = 9; map; map--) + sprintf(name, "E%dM%d", episode, map), s = G_UpdateSignature(s, name); + } + return s; +} + +// +// killough 5/15/98: add forced loadgames, which allow user to override checks +// + +void G_ForcedLoadGame(void) +{ + // CPhipps - net loadgames are always forced, so we only reach here + // in single player + gameaction = ga_loadgame; + forced_loadgame = true; +} + +// killough 3/16/98: add slot info +// killough 5/15/98: add command-line +void G_LoadGame(int slot, boolean command) +{ + if (!demoplayback && !command) { + // CPhipps - handle savegame filename in G_DoLoadGame + // - Delay load so it can be communicated in net game + // - store info in special_event + special_event = BT_SPECIAL | (BTS_LOADGAME & BT_SPECIALMASK) | + ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK); + forced_loadgame = netgame; // CPhipps - always force load netgames + } else { + // Do the old thing, immediate load + gameaction = ga_loadgame; + forced_loadgame = false; + savegameslot = slot; + demoplayback = false; + // Don't stay in netgame state if loading single player save + // while watching multiplayer demo + netgame = false; + } + command_loadgame = command; + R_SmoothPlaying_Reset(NULL); // e6y +} + +// killough 5/15/98: +// Consistency Error when attempting to load savegame. + +static void G_LoadGameErr(const char *msg) +{ + Z_Free(savebuffer); // Free the savegame buffer + M_ForcedLoadGame(msg); // Print message asking for 'Y' to force + if (command_loadgame) // If this was a command-line -loadgame + { + D_StartTitle(); // Start the title screen + gamestate = GS_DEMOSCREEN; // And set the game state accordingly + } +} + +// CPhipps - size of version header +#define VERSIONSIZE 16 + +const char * comp_lev_str[MAX_COMPATIBILITY_LEVEL] = +{ "doom v1.2", "doom v1.666", "doom/doom2 v1.9", "ultimate doom", "final doom", + "dosdoom compatibility", "tasdoom compatibility", "\"boom compatibility\"", "boom v2.01", "boom v2.02", "lxdoom v1.3.2+", + "MBF", "PrBoom 2.03beta", "PrBoom v2.1.0-2.1.1", "PrBoom v2.1.2-v2.2.6", + "PrBoom v2.3.x", "PrBoom 2.4.0", "Current PrBoom" }; + +// comp_options_by_version removed - see G_Compatibility + +static byte map_old_comp_levels[] = +{ 0, 1, 2, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; + +static const struct { + int comp_level; + const char* ver_printf; + int version; +} version_headers[] = { + /* cph - we don't need a new version_header for prboom_3_comp/v2.1.1, since + * the file format is unchanged. */ + { prboom_3_compatibility, "PrBoom %d", 210}, + { prboom_5_compatibility, "PrBoom %d", 211}, + { prboom_6_compatibility, "PrBoom %d", 212} +}; + +static const size_t num_version_headers = sizeof(version_headers) / sizeof(version_headers[0]); + +void G_DoLoadGame(void) +{ + int length, i; + // CPhipps - do savegame filename stuff here + char name[PATH_MAX+1]; // killough 3/22/98 + int savegame_compatibility = -1; + + G_SaveGameName(name,sizeof(name),savegameslot, demoplayback); + + gameaction = ga_nothing; + + length = M_ReadFile(name, &savebuffer); + if (length<=0) + I_Error("Couldn't read file %s: %s", name, "(Unknown Error)"); + save_p = savebuffer + SAVESTRINGSIZE; + + // CPhipps - read the description field, compare with supported ones + for (i=0; (size_t)i= prboom_4_compatibility) ? *save_p : savegame_compatibility; + if (savegame_compatibility < prboom_6_compatibility) + compatibility_level = map_old_comp_levels[compatibility_level]; + save_p++; + + gameskill = *save_p++; + gameepisode = *save_p++; + gamemap = *save_p++; + + for (i=0 ; i= prboom_2_compatibility) { + memcpy(&totalleveltimes, save_p, sizeof totalleveltimes); + save_p += sizeof totalleveltimes; + } + else totalleveltimes = 0; + + // killough 11/98: load revenant tracer state + basetic = gametic - *save_p++; + + // dearchive all the modifications + P_MapStart(); + P_UnArchivePlayers (); + P_UnArchiveWorld (); + P_UnArchiveThinkers (); + P_UnArchiveSpecials (); + P_UnArchiveRNG (); // killough 1/18/98: load RNG information + P_UnArchiveMap (); // killough 1/22/98: load automap information + P_MapEnd(); + R_SmoothPlaying_Reset(NULL); // e6y + + if (*save_p != 0xe6) + I_Error ("G_DoLoadGame: Bad savegame"); + + // done + Z_Free (savebuffer); + + if (setsizeneeded) + R_ExecuteSetViewSize (); +} + +// +// G_SaveGame +// Called by the menu task. +// Description is a 24 byte text string +// + +void G_SaveGame(int slot, char *description) +{ + strcpy(savedescription, description); + if (demoplayback) { + /* cph - We're doing a user-initiated save game while a demo is + * running so, go outside normal mechanisms + */ + savegameslot = slot; + G_DoSaveGame(true); + } + // CPhipps - store info in special_event + special_event = BT_SPECIAL | (BTS_SAVEGAME & BT_SPECIALMASK) | + ((slot << BTS_SAVESHIFT) & BTS_SAVEMASK); +#ifdef HAVE_NET + D_NetSendMisc(nm_savegamename, strlen(savedescription)+1, savedescription); +#endif +} + +// Check for overrun and realloc if necessary -- Lee Killough 1/22/98 +void (CheckSaveGame)(size_t size, const char* file, int line) +{ + size_t pos = save_p - savebuffer; + + size += 1024; // breathing room + if (pos+size > savegamesize) + save_p = (savebuffer = realloc(savebuffer, + savegamesize += (size+1023) & ~1023)) + pos; +} + +/* killough 3/22/98: form savegame name in one location + * (previously code was scattered around in multiple places) + * cph - Avoid possible buffer overflow problems by passing + * size to this function and using snprintf */ + +void G_SaveGameName(char *name, size_t size, int slot, boolean demoplayback) +{ + const char* sgn = demoplayback ? "demosav" : savegamename; +#ifdef HAVE_SNPRINTF + snprintf (name, size, "%s/%s%d.dsg", basesavegame, sgn, slot); +#else + sprintf (name, "%s/%s%d.dsg", basesavegame, sgn, slot); +#endif +} + +static void G_DoSaveGame (boolean menu) +{ + char name[PATH_MAX+1]; + char name2[VERSIONSIZE]; + char *description; + int length, i; + + gameaction = ga_nothing; // cph - cancel savegame at top of this function, + // in case later problems cause a premature exit + + G_SaveGameName(name,sizeof(name),savegameslot, demoplayback && !menu); + + description = savedescription; + + save_p = savebuffer = malloc(savegamesize); + + CheckSaveGame(SAVESTRINGSIZE+VERSIONSIZE+sizeof(uint_64_t)); + memcpy (save_p, description, SAVESTRINGSIZE); + save_p += SAVESTRINGSIZE; + memset (name2,0,sizeof(name2)); + + // CPhipps - scan for the version header + for (i=0; (size_t)i= prboom_2_compatibility) { + memcpy(save_p, &totalleveltimes, sizeof totalleveltimes); + save_p += sizeof totalleveltimes; + } + else totalleveltimes = 0; + + // killough 11/98: save revenant tracer state + *save_p++ = (gametic-basetic) & 255; + + // killough 3/22/98: add Z_CheckHeap after each call to ensure consistency + Z_CheckHeap(); + P_ArchivePlayers(); + Z_CheckHeap(); + + // phares 9/13/98: Move mobj_t->index out of P_ArchiveThinkers so the + // indices can be used by P_ArchiveWorld when the sectors are saved. + // This is so we can save the index of the mobj_t of the thinker that + // caused a sound, referenced by sector_t->soundtarget. + P_ThinkerToIndex(); + + P_ArchiveWorld(); + Z_CheckHeap(); + P_ArchiveThinkers(); + + // phares 9/13/98: Move index->mobj_t out of P_ArchiveThinkers, simply + // for symmetry with the P_ThinkerToIndex call above. + + P_IndexToThinker(); + + Z_CheckHeap(); + P_ArchiveSpecials(); + P_ArchiveRNG(); // killough 1/18/98: save RNG information + Z_CheckHeap(); + P_ArchiveMap(); // killough 1/22/98: save automap information + + *save_p++ = 0xe6; // consistancy marker + + length = save_p - savebuffer; + + Z_CheckHeap(); + doom_printf( "%s", M_WriteFile(name, savebuffer, length) + ? s_GGSAVED /* Ty - externalised */ + : "Game save failed!"); // CPhipps - not externalised + + free(savebuffer); // killough + savebuffer = save_p = NULL; + + savedescription[0] = 0; +} + +static skill_t d_skill; +static int d_episode; +static int d_map; + +void G_DeferedInitNew(skill_t skill, int episode, int map) +{ + d_skill = skill; + d_episode = episode; + d_map = map; + gameaction = ga_newgame; +} + +/* cph - + * G_Compatibility + * + * Initialises the comp[] array based on the compatibility_level + * For reference, MBF did: + * for (i=0; i < COMP_TOTAL; i++) + * comp[i] = compatibility; + * + * Instead, we have a lookup table showing at what version a fix was + * introduced, and made optional (replaces comp_options_by_version) + */ + +void G_Compatibility(void) +{ + static const struct { + complevel_t fix; // level at which fix/change was introduced + complevel_t opt; // level at which fix/change was made optional + } levels[] = { + // comp_telefrag - monsters used to telefrag only on MAP30, now they do it for spawners only + { mbf_compatibility, mbf_compatibility }, + // comp_dropoff - MBF encourages things to drop off of overhangs + { mbf_compatibility, mbf_compatibility }, + // comp_vile - original Doom archville bugs like ghosts + { boom_compatibility, mbf_compatibility }, + // comp_pain - original Doom limits Pain Elementals from spawning too many skulls + { boom_compatibility, mbf_compatibility }, + // comp_skull - original Doom let skulls be spit through walls by Pain Elementals + { boom_compatibility, mbf_compatibility }, + // comp_blazing - original Doom duplicated blazing door sound + { boom_compatibility, mbf_compatibility }, + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + // comp_doorlight - MBF made door lighting changes more gradual + { boom_compatibility, mbf_compatibility }, + // comp_model - improvements to the game physics + { boom_compatibility, mbf_compatibility }, + // comp_god - fixes to God mode + { boom_compatibility, mbf_compatibility }, + // comp_falloff - MBF encourages things to drop off of overhangs + { mbf_compatibility, mbf_compatibility }, + // comp_floors - fixes for moving floors bugs + { boom_compatibility_compatibility, mbf_compatibility }, + // comp_skymap + { boom_compatibility, mbf_compatibility }, + // comp_pursuit - MBF AI change, limited pursuit? + { mbf_compatibility, mbf_compatibility }, + // comp_doorstuck - monsters stuck in doors fix + { boom_202_compatibility, mbf_compatibility }, + // comp_staylift - MBF AI change, monsters try to stay on lifts + { mbf_compatibility, mbf_compatibility }, + // comp_zombie - prevent dead players triggering stuff + { lxdoom_1_compatibility, mbf_compatibility }, + // comp_stairs - see p_floor.c + { boom_202_compatibility, mbf_compatibility }, + // comp_infcheat - FIXME + { mbf_compatibility, mbf_compatibility }, + // comp_zerotags - allow zero tags in wads */ + { boom_compatibility, mbf_compatibility }, + // comp_moveblock - enables keygrab and mancubi shots going thru walls + { lxdoom_1_compatibility, prboom_2_compatibility }, + // comp_respawn - objects which aren't on the map at game start respawn at (0,0) + { prboom_2_compatibility, prboom_2_compatibility }, + // comp_sound - see s_sound.c + { boom_compatibility_compatibility, prboom_3_compatibility }, + // comp_666 - enables tag 666 in non-ExM8 levels + { ultdoom_compatibility, prboom_4_compatibility }, + // comp_soul - enables lost souls bouncing (see P_ZMovement) + { prboom_4_compatibility, prboom_4_compatibility }, + // comp_maskedanim - 2s mid textures don't animate + { doom_1666_compatibility, prboom_4_compatibility }, + }; + int i; + + if (sizeof(levels)/sizeof(*levels) != COMP_NUM) + I_Error("G_Compatibility: consistency error"); + + for (i = 0; i < sizeof(levels)/sizeof(*levels); i++) + if (compatibility_level < levels[i].opt) + comp[i] = (compatibility_level < levels[i].fix); + + if (!mbf_features) { + monster_infighting = 1; + monster_backing = 0; + monster_avoid_hazards = 0; + monster_friction = 0; + help_friends = 0; + + monkeys = 0; + } + + if (demo_compatibility) { + allow_pushers = 0; + variable_friction = 0; + monsters_remember = 0; + weapon_recoil = 0; + player_bobbing = 1; + } +} + +// killough 3/1/98: function to reload all the default parameter +// settings before a new game begins + +void G_ReloadDefaults(void) +{ + // killough 3/1/98: Initialize options based on config file + // (allows functions above to load different values for demos + // and savegames without messing up defaults). + + weapon_recoil = default_weapon_recoil; // weapon recoil + + player_bobbing = default_player_bobbing; // whether player bobs or not + + /* cph 2007/06/31 - for some reason, the default_* of the next 2 vars was never implemented */ + variable_friction = default_variable_friction; + allow_pushers = default_allow_pushers; + + + monsters_remember = default_monsters_remember; // remember former enemies + + monster_infighting = default_monster_infighting; // killough 7/19/98 + + + distfriend = default_distfriend; // killough 8/8/98 + + monster_backing = default_monster_backing; // killough 9/8/98 + + monster_avoid_hazards = default_monster_avoid_hazards; // killough 9/9/98 + + monster_friction = default_monster_friction; // killough 10/98 + + help_friends = default_help_friends; // killough 9/9/98 + + monkeys = default_monkeys; + + // jff 1/24/98 reset play mode to command line spec'd version + // killough 3/1/98: moved to here + respawnparm = clrespawnparm; + fastparm = clfastparm; + nomonsters = clnomonsters; + + //jff 3/24/98 set startskill from defaultskill in config file, unless + // it has already been set by a -skill parameter + if (startskill==sk_none) + startskill = (skill_t)(defaultskill-1); + + demoplayback = false; + netdemo = false; + + // killough 2/21/98: + memset(playeringame+1, 0, sizeof(*playeringame)*(MAXPLAYERS-1)); + + consoleplayer = 0; + + compatibility_level = default_compatibility_level; + { + int i = M_CheckParm("-complevel"); + if (i && (1+i) < myargc) { + int l = atoi(myargv[i+1]);; + if (l >= -1) compatibility_level = l; + } + } + if (compatibility_level == -1) + compatibility_level = best_compatibility; + + if (mbf_features) + memcpy(comp, default_comp, sizeof comp); + G_Compatibility(); + + // killough 3/31/98, 4/5/98: demo sync insurance + demo_insurance = default_demo_insurance == 1; + + rngseed += I_GetRandomTimeSeed() + gametic; // CPhipps +} + +void G_DoNewGame (void) +{ + G_ReloadDefaults(); // killough 3/1/98 + netgame = false; // killough 3/29/98 + deathmatch = false; + G_InitNew (d_skill, d_episode, d_map); + gameaction = ga_nothing; + + //jff 4/26/98 wake up the status bar in case were coming out of a DM demo + ST_Start(); +} + +// killough 4/10/98: New function to fix bug which caused Doom +// lockups when idclev was used in conjunction with -fast. + +void G_SetFastParms(int fast_pending) +{ + static int fast = 0; // remembers fast state + int i; + if (fast != fast_pending) { /* only change if necessary */ + if ((fast = fast_pending)) + { + for (i=S_SARG_RUN1; i<=S_SARG_PAIN2; i++) + if (states[i].tics != 1 || demo_compatibility) // killough 4/10/98 + states[i].tics >>= 1; // don't change 1->0 since it causes cycles + mobjinfo[MT_BRUISERSHOT].speed = 20*FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 20*FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 20*FRACUNIT; + } + else + { + for (i=S_SARG_RUN1; i<=S_SARG_PAIN2; i++) + states[i].tics <<= 1; + mobjinfo[MT_BRUISERSHOT].speed = 15*FRACUNIT; + mobjinfo[MT_HEADSHOT].speed = 10*FRACUNIT; + mobjinfo[MT_TROOPSHOT].speed = 10*FRACUNIT; + } + } +} + +// +// G_InitNew +// Can be called by the startup code or the menu task, +// consoleplayer, displayplayer, playeringame[] should be set. +// + +void G_InitNew(skill_t skill, int episode, int map) +{ + int i; + + if (paused) + { + paused = false; + S_ResumeSound(); + } + + if (skill > sk_nightmare) + skill = sk_nightmare; + + if (episode < 1) + episode = 1; + + if (gamemode == retail) + { + if (episode > 4) + episode = 4; + } + else + if (gamemode == shareware) + { + if (episode > 1) + episode = 1; // only start episode 1 on shareware + } + else + if (episode > 3) + episode = 3; + + if (map < 1) + map = 1; + if (map > 9 && gamemode != commercial) + map = 9; + + G_SetFastParms(fastparm || skill == sk_nightmare); // killough 4/10/98 + + M_ClearRandom(); + + respawnmonsters = skill == sk_nightmare || respawnparm; + + // force players to be initialized upon first level load + for (i=0 ; i demobuffer + demolength) + { + lprintf(LO_WARN, "G_ReadDemoTiccmd: missing DEMOMARKER\n"); + G_CheckDemoStatus(); + } + else + { + cmd->forwardmove = ((signed char)*demo_p++); + cmd->sidemove = ((signed char)*demo_p++); + if (!longtics) { + cmd->angleturn = ((unsigned char)(at = *demo_p++))<<8; + } else { + unsigned int lowbyte = (unsigned char)*demo_p++; + cmd->angleturn = (((signed int)(*demo_p++))<<8) + lowbyte; + } + cmd->buttons = (unsigned char)*demo_p++; + // e6y: ability to play tasdoom demos directly + if (compatibility_level == tasdoom_compatibility) + { + signed char k = cmd->forwardmove; + cmd->forwardmove = cmd->sidemove; + cmd->sidemove = (signed char)at; + cmd->angleturn = ((unsigned char)cmd->buttons)<<8; + cmd->buttons = (byte)k; + } + } +} + +// These functions are used to read and write game-specific options in demos +// and savegames so that demo sync is preserved and savegame restoration is +// complete. Not all options (for example "compatibility"), however, should +// be loaded and saved here. It is extremely important to use the same +// positions as before for the variables, so if one becomes obsolete, the +// byte(s) should still be skipped over or padded with 0's. +// Lee Killough 3/1/98 + +extern int forceOldBsp; + +byte *G_WriteOptions(byte *demo_p) +{ + byte *target = demo_p + GAME_OPTION_SIZE; + + *demo_p++ = monsters_remember; // part of monster AI + + *demo_p++ = variable_friction; // ice & mud + + *demo_p++ = weapon_recoil; // weapon recoil + + *demo_p++ = allow_pushers; // MT_PUSH Things + + *demo_p++ = 0; + + *demo_p++ = player_bobbing; // whether player bobs or not + + // killough 3/6/98: add parameters to savegame, move around some in demos + *demo_p++ = respawnparm; + *demo_p++ = fastparm; + *demo_p++ = nomonsters; + + *demo_p++ = demo_insurance; // killough 3/31/98 + + // killough 3/26/98: Added rngseed. 3/31/98: moved here + *demo_p++ = (byte)((rngseed >> 24) & 0xff); + *demo_p++ = (byte)((rngseed >> 16) & 0xff); + *demo_p++ = (byte)((rngseed >> 8) & 0xff); + *demo_p++ = (byte)( rngseed & 0xff); + + // Options new to v2.03 begin here + + *demo_p++ = monster_infighting; // killough 7/19/98 + + *demo_p++ = 0; + + *demo_p++ = 0; + *demo_p++ = 0; + + *demo_p++ = (distfriend >> 8) & 0xff; // killough 8/8/98 + *demo_p++ = distfriend & 0xff; // killough 8/8/98 + + *demo_p++ = monster_backing; // killough 9/8/98 + + *demo_p++ = monster_avoid_hazards; // killough 9/9/98 + + *demo_p++ = monster_friction; // killough 10/98 + + *demo_p++ = help_friends; // killough 9/9/98 + + *demo_p++ = 0; + + *demo_p++ = monkeys; + + { // killough 10/98: a compatibility vector now + int i; + for (i=0; i < COMP_TOTAL; i++) + *demo_p++ = comp[i] != 0; + } + + *demo_p++ = (compatibility_level >= prboom_2_compatibility) && forceOldBsp; // cph 2002/07/20 + + //---------------- + // Padding at end + //---------------- + while (demo_p < target) + *demo_p++ = 0; + + if (demo_p != target) + I_Error("G_WriteOptions: GAME_OPTION_SIZE is too small"); + + return target; +} + +/* Same, but read instead of write + * cph - const byte*'s + */ + +const byte *G_ReadOptions(const byte *demo_p) +{ + const byte *target = demo_p + GAME_OPTION_SIZE; + + monsters_remember = *demo_p++; + + variable_friction = *demo_p; // ice & mud + demo_p++; + + weapon_recoil = *demo_p; // weapon recoil + demo_p++; + + allow_pushers = *demo_p; // MT_PUSH Things + demo_p++; + + demo_p++; + + player_bobbing = *demo_p; // whether player bobs or not + demo_p++; + + // killough 3/6/98: add parameters to savegame, move from demo + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + + demo_insurance = *demo_p++; // killough 3/31/98 + + // killough 3/26/98: Added rngseed to demos; 3/31/98: moved here + + rngseed = *demo_p++ & 0xff; + rngseed <<= 8; + rngseed += *demo_p++ & 0xff; + rngseed <<= 8; + rngseed += *demo_p++ & 0xff; + rngseed <<= 8; + rngseed += *demo_p++ & 0xff; + + // Options new to v2.03 + if (mbf_features) + { + monster_infighting = *demo_p++; // killough 7/19/98 + + demo_p++; + + demo_p += 2; + + distfriend = *demo_p++ << 8; // killough 8/8/98 + distfriend+= *demo_p++; + + monster_backing = *demo_p++; // killough 9/8/98 + + monster_avoid_hazards = *demo_p++; // killough 9/9/98 + + monster_friction = *demo_p++; // killough 10/98 + + help_friends = *demo_p++; // killough 9/9/98 + + demo_p++; + + monkeys = *demo_p++; + + { // killough 10/98: a compatibility vector now + int i; + for (i=0; i < COMP_TOTAL; i++) + comp[i] = *demo_p++; + } + + forceOldBsp = *demo_p++; // cph 2002/07/20 + } + else /* defaults for versions <= 2.02 */ + { + /* G_Compatibility will set these */ + } + + G_Compatibility(); + return target; +} + +// +// G_PlayDemo +// + +static const char *defdemoname; + +void G_DeferedPlayDemo (const char* name) +{ + defdemoname = name; + gameaction = ga_playdemo; +} + +static int demolumpnum = -1; + +static int G_GetOriginalDoomCompatLevel(int ver) +{ + { + int lev; + int i = M_CheckParm("-complevel"); + if (i && (i+1 < myargc)) + { + lev = atoi(myargv[i+1]); + if (lev>=0) + return lev; + } + } + if (ver < 107) return doom_1666_compatibility; + if (gamemode == retail) return ultdoom_compatibility; + if (gamemission >= pack_tnt) return finaldoom_compatibility; + return doom2_19_compatibility; +} + +//e6y: Check for overrun +static boolean CheckForOverrun(const byte *start_p, const byte *current_p, size_t maxsize, size_t size, boolean failonerror) +{ + size_t pos = current_p - start_p; + if (pos + size > maxsize) + { + if (failonerror) + I_Error("G_ReadDemoHeader: wrong demo header\n"); + else + return true; + } + return false; +} + +static const byte* G_ReadDemoHeader(const byte *demo_p, size_t size, boolean failonerror) +{ + skill_t skill; + int i, episode, map; + + // e6y + // The local variable should be used instead of demobuffer, + // because demobuffer can be uninitialized + const byte *header_p = demo_p; + + const byte *option_p = NULL; /* killough 11/98 */ + + basetic = gametic; // killough 9/29/98 + + // killough 2/22/98, 2/28/98: autodetect old demos and act accordingly. + // Old demos turn on demo_compatibility => compatibility; new demos load + // compatibility flag, and other flags as well, as a part of the demo. + + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 1, failonerror)) + return NULL; + + demover = *demo_p++; + longtics = 0; + + // e6y + // Handling of unrecognized demo formats + // Versions up to 1.2 use a 7-byte header - first byte is a skill level. + // Versions after 1.2 use a 13-byte header - first byte is a demoversion. + // BOOM's demoversion starts from 200 + if (!((demover >= 0 && demover <= 4) || + (demover >= 104 && demover <= 111) || + (demover >= 200 && demover <= 214))) + { + I_Error("G_ReadDemoHeader: Unknown demo format %d.", demover); + } + + if (demover < 200) // Autodetect old demos + { + if (demover >= 111) longtics = 1; + + // killough 3/2/98: force these variables to be 0 in demo_compatibility + + variable_friction = 0; + + weapon_recoil = 0; + + allow_pushers = 0; + + monster_infighting = 1; // killough 7/19/98 + + monster_backing = 0; // killough 9/8/98 + + monster_avoid_hazards = 0; // killough 9/9/98 + + monster_friction = 0; // killough 10/98 + help_friends = 0; // killough 9/9/98 + monkeys = 0; + + // killough 3/6/98: rearrange to fix savegame bugs (moved fastparm, + // respawnparm, nomonsters flags to G_LoadOptions()/G_SaveOptions()) + + if ((skill=demover) >= 100) // For demos from versions >= 1.4 + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 8, failonerror)) + return NULL; + + compatibility_level = G_GetOriginalDoomCompatLevel(demover); + skill = *demo_p++; + episode = *demo_p++; + map = *demo_p++; + deathmatch = *demo_p++; + respawnparm = *demo_p++; + fastparm = *demo_p++; + nomonsters = *demo_p++; + consoleplayer = *demo_p++; + } + else + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 2, failonerror)) + return NULL; + + compatibility_level = doom_12_compatibility; + episode = *demo_p++; + map = *demo_p++; + deathmatch = respawnparm = fastparm = + nomonsters = consoleplayer = 0; + } + G_Compatibility(); + } + else // new versions of demos + { + demo_p += 6; // skip signature; + switch (demover) { + case 200: /* BOOM */ + case 201: + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 1, failonerror)) + return NULL; + + if (!*demo_p++) + compatibility_level = boom_201_compatibility; + else + compatibility_level = boom_compatibility_compatibility; + break; + case 202: + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 1, failonerror)) + return NULL; + + if (!*demo_p++) + compatibility_level = boom_202_compatibility; + else + compatibility_level = boom_compatibility_compatibility; + break; + case 203: + /* LxDoom or MBF - determine from signature + * cph - load compatibility level */ + switch (*(header_p + 2)) { + case 'B': /* LxDoom */ + /* cph - DEMOSYNC - LxDoom demos recorded in compatibility modes support dropped */ + compatibility_level = lxdoom_1_compatibility; + break; + case 'M': + compatibility_level = mbf_compatibility; + demo_p++; + break; + } + break; + case 210: + compatibility_level = prboom_2_compatibility; + demo_p++; + break; + case 211: + compatibility_level = prboom_3_compatibility; + demo_p++; + break; + case 212: + compatibility_level = prboom_4_compatibility; + demo_p++; + break; + case 213: + compatibility_level = prboom_5_compatibility; + demo_p++; + break; + case 214: + compatibility_level = prboom_6_compatibility; + longtics = 1; + demo_p++; + break; + } + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 5, failonerror)) + return NULL; + + skill = *demo_p++; + episode = *demo_p++; + map = *demo_p++; + deathmatch = *demo_p++; + consoleplayer = *demo_p++; + + /* killough 11/98: save option pointer for below */ + if (mbf_features) + option_p = demo_p; + + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, GAME_OPTION_SIZE, failonerror)) + return NULL; + + demo_p = G_ReadOptions(demo_p); // killough 3/1/98: Read game options + + if (demover == 200) // killough 6/3/98: partially fix v2.00 demos + demo_p += 256-GAME_OPTION_SIZE; + } + + if (sizeof(comp_lev_str)/sizeof(comp_lev_str[0]) != MAX_COMPATIBILITY_LEVEL) + I_Error("G_ReadDemoHeader: compatibility level strings incomplete"); + lprintf(LO_INFO, "G_DoPlayDemo: playing demo with %s compatibility\n", + comp_lev_str[compatibility_level]); + + if (demo_compatibility) // only 4 players can exist in old demos + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, 4, failonerror)) + return NULL; + + for (i=0; i<4; i++) // intentionally hard-coded 4 -- killough + playeringame[i] = *demo_p++; + for (;i < MAXPLAYERS; i++) + playeringame[i] = 0; + } + else + { + //e6y: check for overrun + if (CheckForOverrun(header_p, demo_p, size, MAXPLAYERS, failonerror)) + return NULL; + + for (i=0 ; i < MAXPLAYERS; i++) + playeringame[i] = *demo_p++; + demo_p += MIN_MAXPLAYERS - MAXPLAYERS; + } + + if (playeringame[1]) + { + netgame = true; + netdemo = true; + } + + if (gameaction != ga_loadgame) { /* killough 12/98: support -loadgame */ + G_InitNew(skill, episode, map); + } + + for (i=0; imessage=... and so I've added this dprintf. +// +// killough 3/6/98: Made limit static to allow z_zone functions to call +// this function, without calling realloc(), which seems to cause problems. + +#define MAX_MESSAGE_SIZE 1024 + +// CPhipps - renamed to doom_printf to avoid name collision with glibc +void doom_printf(const char *s, ...) +{ + static char msg[MAX_MESSAGE_SIZE]; + va_list v; + va_start(v,s); +#ifdef HAVE_VSNPRINTF + vsnprintf(msg,sizeof(msg),s,v); /* print message in buffer */ +#else + vsprintf(msg,s,v); +#endif + va_end(v); + players[consoleplayer].message = msg; // set new message +} diff --git a/src/g_game.h b/src/g_game.h new file mode 100644 index 00000000..e192546e --- /dev/null +++ b/src/g_game.h @@ -0,0 +1,181 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Main game control interface. + *-----------------------------------------------------------------------------*/ + +#ifndef __G_GAME__ +#define __G_GAME__ + +#include "doomdef.h" +#include "d_event.h" +#include "d_ticcmd.h" + +// +// GAME +// + +// killough 5/2/98: number of bytes reserved for saving options +#define GAME_OPTION_SIZE 64 + +boolean G_Responder(event_t *ev); +boolean G_CheckDemoStatus(void); +void G_DeathMatchSpawnPlayer(int playernum); +void G_InitNew(skill_t skill, int episode, int map); +void G_DeferedInitNew(skill_t skill, int episode, int map); +void G_DeferedPlayDemo(const char *demo); // CPhipps - const +void G_LoadGame(int slot, boolean is_command); // killough 5/15/98 +void G_ForcedLoadGame(void); // killough 5/15/98: forced loadgames +void G_DoLoadGame(void); +void G_SaveGame(int slot, char *description); // Called by M_Responder. +void G_ExitLevel(void); +void G_SecretExitLevel(void); +void G_WorldDone(void); +void G_EndGame(void); /* cph - make m_menu.c call a G_* function for this */ +void G_Ticker(void); +void G_ReloadDefaults(void); // killough 3/1/98: loads game defaults +void G_SaveGameName(char *, size_t, int, boolean); /* killough 3/22/98: sets savegame filename */ +void G_SetFastParms(int); // killough 4/10/98: sets -fast parameters +void G_DoNewGame(void); +void G_DoReborn(int playernum); +void G_DoPlayDemo(void); +void G_DoCompleted(void); +void G_ReadDemoTiccmd(ticcmd_t *cmd); +void G_DoWorldDone(void); +void G_Compatibility(void); +const byte *G_ReadOptions(const byte *demo_p); /* killough 3/1/98 - cph: const byte* */ +byte *G_WriteOptions(byte *demo_p); // killough 3/1/98 +void G_PlayerReborn(int player); +void G_RestartLevel(void); // CPhipps - menu involked level restart +void G_DoVictory(void); +void G_BuildTiccmd (ticcmd_t* cmd); // CPhipps - move decl to header +void G_ChangedPlayerColour(int pn, int cl); // CPhipps - On-the-fly player colour changing +void G_MakeSpecialEvent(buttoncode_t bc, ...); /* cph - new event stuff */ + +// killough 1/18/98: Doom-style printf; killough 4/25/98: add gcc attributes +// CPhipps - renames to doom_printf to avoid name collision with glibc +void doom_printf(const char *, ...) __attribute__((format(printf,1,2))); + +// killough 5/2/98: moved from m_misc.c: + +extern int key_right; +extern int key_left; +extern int key_up; +extern int key_down; +extern int key_menu_right; // phares 3/7/98 +extern int key_menu_left; // | +extern int key_menu_up; // V +extern int key_menu_down; +extern int key_menu_backspace; // ^ +extern int key_menu_escape; // | +extern int key_menu_enter; // phares 3/7/98 +extern int key_strafeleft; +extern int key_straferight; + +extern int key_fire; +extern int key_use; +extern int key_strafe; +extern int key_speed; +extern int key_escape; // phares +extern int key_savegame; // | +extern int key_loadgame; // V +extern int key_autorun; +extern int key_reverse; +extern int key_zoomin; +extern int key_zoomout; +extern int key_chat; +extern int key_backspace; +extern int key_enter; +extern int key_help; +extern int key_soundvolume; +extern int key_hud; +extern int key_quicksave; +extern int key_endgame; +extern int key_messages; +extern int key_quickload; +extern int key_quit; +extern int key_gamma; +extern int key_spy; +extern int key_pause; +extern int key_setup; +extern int key_forward; +extern int key_leftturn; +extern int key_rightturn; +extern int key_backward; +extern int key_weapontoggle; +extern int key_weaponcycleup; +extern int key_weaponcycledown; +extern int key_weapon1; +extern int key_weapon2; +extern int key_weapon3; +extern int key_weapon4; +extern int key_weapon5; +extern int key_weapon6; +extern int key_weapon7; +extern int key_weapon8; +extern int key_weapon9; +extern int destination_keys[MAXPLAYERS]; +extern int key_map_right; +extern int key_map_left; +extern int key_map_up; +extern int key_map_down; +extern int key_map_zoomin; +extern int key_map_zoomout; +extern int key_map; +extern int key_map_gobig; +extern int key_map_follow; +extern int key_map_mark; // ^ +extern int key_map_clear; // | +extern int key_map_grid; // phares +extern int key_map_rotate; // cph - map rotation +extern int key_map_overlay;// cph - map overlay +extern int key_screenshot; // killough 2/22/98 -- add key for screenshot +extern int autorun; // always running? // phares +extern int mousebfire; +extern int mousebstrafe; +extern int mousebforward; +extern int mousebbackward; +extern int mouse_double_click_use; + +extern int defaultskill; //jff 3/24/98 default skill +extern boolean haswolflevels; //jff 4/18/98 wolf levels present + +extern int bodyquesize; // killough 2/8/98: adustable corpse limit + +// killough 5/2/98: moved from d_deh.c: +// Par times (new item with BOOM) - from g_game.c +extern int pars[4][10]; // hardcoded array size +extern int cpars[32]; // hardcoded array size +// CPhipps - Make savedesciption visible in wider scope +#define SAVEDESCLEN 32 +extern char savedescription[SAVEDESCLEN]; // Description to save in savegame + +/* cph - compatibility level strings */ +extern const char * comp_lev_str[]; + +#endif diff --git a/src/hu_lib.c b/src/hu_lib.c new file mode 100644 index 00000000..07dfe1b5 --- /dev/null +++ b/src/hu_lib.c @@ -0,0 +1,701 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: heads-up text and input code + * + *----------------------------------------------------------------------------- + */ + +#include "doomdef.h" +#include "doomstat.h" +#include "v_video.h" +#include "m_swap.h" +#include "hu_lib.h" +#include "hu_stuff.h" +#include "r_main.h" +#include "r_draw.h" + +extern int key_backspace; // phares +extern int key_enter; // phares + +// +// not used currently +// code to initialize HUlib would go here if needed +// +static void HUlib_init(void) +{ +} + +//////////////////////////////////////////////////////// +// +// Basic text line widget +// +//////////////////////////////////////////////////////// + +// +// HUlib_clearTextLine() +// +// Blank the internal text line in a hu_textline_t widget +// +// Passed a hu_textline_t, returns nothing +// +void HUlib_clearTextLine(hu_textline_t* t) +{ + t->linelen = // killough 1/23 98: support multiple lines + t->len = 0; + t->l[0] = 0; + t->needsupdate = true; +} + +// +// HUlib_initTextLine() +// +// Initialize a hu_textline_t widget. Set the position, font, start char +// of the font, and color range to be used. +// +// Passed a hu_textline_t, and the values used to initialize +// Returns nothing +// +void HUlib_initTextLine(hu_textline_t* t, int x, int y, + const patchnum_t* f, int sc, int cm ) + //jff 2/16/98 add color range parameter +{ + t->x = x; + t->y = y; + t->f = f; + t->sc = sc; + t->cm = cm; + HUlib_clearTextLine(t); +} + +// +// HUlib_addCharToTextLine() +// +// Adds a character at the end of the text line in a hu_textline_t widget +// +// Passed the hu_textline_t and the char to add +// Returns false if already at length limit, true if the character added +// +boolean HUlib_addCharToTextLine +( hu_textline_t* t, + char ch ) +{ + // killough 1/23/98 -- support multiple lines + if (t->linelen == HU_MAXLINELENGTH) + return false; + else + { + t->linelen++; + if (ch == '\n') + t->linelen=0; + + t->l[t->len++] = ch; + t->l[t->len] = 0; + t->needsupdate = 4; + return true; + } + +} + +// +// HUlib_delCharFromTextLine() +// +// Deletes a character at the end of the text line in a hu_textline_t widget +// +// Passed the hu_textline_t +// Returns false if already empty, true if the character deleted +// +static boolean HUlib_delCharFromTextLine(hu_textline_t* t) +{ + if (!t->len) return false; + else + { + t->l[--t->len] = 0; + t->needsupdate = 4; + return true; + } +} + +// +// HUlib_drawTextLine() +// +// Draws a hu_textline_t widget +// +// Passed the hu_textline_t and flag whether to draw a cursor +// Returns nothing +// +void HUlib_drawTextLine +( hu_textline_t* l, + boolean drawcursor ) +{ + + int i; + int w; + int x; + unsigned char c; + int oc = l->cm; //jff 2/17/98 remember default color + int y = l->y; // killough 1/18/98 -- support multiple lines + + // draw the new stuff + x = l->x; + for (i=0;ilen;i++) + { + c = toupper(l->l[i]); //jff insure were not getting a cheap toupper conv. + + if (c=='\n') // killough 1/18/98 -- support multiple lines + x=0,y+=8; + else if (c=='\t') // killough 1/23/98 -- support tab stops + x=x-x%80+80; + else if (c=='\x1b') //jff 2/17/98 escape code for color change + { //jff 3/26/98 changed to actual escape char + if (++ilen) + if (l->l[i]>='0' && l->l[i]<='9') + l->cm = l->l[i]-'0'; + } + else if (c != ' ' && c >= l->sc && c <= 127) + { + w = l->f[c - l->sc].width; + if (x+w > BASE_WIDTH) + break; + // killough 1/18/98 -- support multiple lines: + // CPhipps - patch drawing updated + V_DrawNumPatch(x, y, FG, l->f[c - l->sc].lumpnum, l->cm, VPT_TRANS); + x += w; + } + else + { + x += 4; + if (x >= BASE_WIDTH) + break; + } + } + l->cm = oc; //jff 2/17/98 restore original color + + // draw the cursor if requested + if (drawcursor && x + l->f['_' - l->sc].width <= BASE_WIDTH) + { + // killough 1/18/98 -- support multiple lines + // CPhipps - patch drawing updated + V_DrawNumPatch(x, y, FG, l->f['_' - l->sc].lumpnum, CR_DEFAULT, VPT_NONE); + } +} + +// +// HUlib_eraseTextLine() +// +// Erases a hu_textline_t widget when screen border is behind text +// Sorta called by HU_Erase and just better darn get things straight +// +// Passed the hu_textline_t +// Returns nothing +// +void HUlib_eraseTextLine(hu_textline_t* l) +{ + if (l->needsupdate) l->needsupdate--; +} + +//////////////////////////////////////////////////////// +// +// Player message widget (up to 4 lines of text) +// +//////////////////////////////////////////////////////// + +// +// HUlib_initSText() +// +// Initialize a hu_stext_t widget. Set the position, number of lines, font, +// start char of the font, and color range to be used, and whether enabled. +// +// Passed a hu_stext_t, and the values used to initialize +// Returns nothing +// +void HUlib_initSText +( hu_stext_t* s, + int x, + int y, + int h, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + boolean* on ) +{ + + int i; + + s->h = h; + s->on = on; + s->laston = true; + s->cl = 0; + for (i=0;il[i], + x, + y - i*(font[0].height+1), + font, + startchar, + cm + ); +} + +// +// HUlib_addLineToSText() +// +// Adds a blank line to a hu_stext_t widget +// +// Passed a hu_stext_t +// Returns nothing +// +static void HUlib_addLineToSText(hu_stext_t* s) +{ + + int i; + + // add a clear line + if (++s->cl == s->h) + s->cl = 0; + HUlib_clearTextLine(&s->l[s->cl]); + + // everything needs updating + for (i=0 ; ih ; i++) + s->l[i].needsupdate = 4; + +} + +// +// HUlib_addMessageToSText() +// +// Adds a message line with prefix to a hu_stext_t widget +// +// Passed a hu_stext_t, the prefix string, and a message string +// Returns nothing +// +void HUlib_addMessageToSText(hu_stext_t* s, const char* prefix, const char* msg) +{ + HUlib_addLineToSText(s); + if (prefix) + while (*prefix) + HUlib_addCharToTextLine(&s->l[s->cl], *(prefix++)); + + while (*msg) + HUlib_addCharToTextLine(&s->l[s->cl], *(msg++)); +} + +// +// HUlib_drawSText() +// +// Displays a hu_stext_t widget +// +// Passed a hu_stext_t +// Returns nothing +// +void HUlib_drawSText(hu_stext_t* s) +{ + int i, idx; + hu_textline_t *l; + + if (!*s->on) + return; // if not on, don't draw + + // draw everything + for (i=0 ; ih ; i++) + { + idx = s->cl - i; + if (idx < 0) + idx += s->h; // handle queue of lines + + l = &s->l[idx]; + + // need a decision made here on whether to skip the draw + HUlib_drawTextLine(l, false); // no cursor, please + } +} + +// +// HUlib_eraseSText() +// +// Erases a hu_stext_t widget, when the screen is not fullsize +// +// Passed a hu_stext_t +// Returns nothing +// +void HUlib_eraseSText(hu_stext_t* s) +{ + int i; + + for (i=0 ; ih ; i++) + { + if (s->laston && !*s->on) + s->l[i].needsupdate = 4; + HUlib_eraseTextLine(&s->l[i]); + } + s->laston = *s->on; +} + +//////////////////////////////////////////////////////// +// +// Scrolling message review widget +// +// jff added 2/26/98 +// +//////////////////////////////////////////////////////// + +// +// HUlib_initMText() +// +// Initialize a hu_mtext_t widget. Set the position, width, number of lines, +// font, start char of the font, color range, background font, and whether +// enabled. +// +// Passed a hu_mtext_t, and the values used to initialize +// Returns nothing +// +void HUlib_initMText(hu_mtext_t *m, int x, int y, int w, int h, + const patchnum_t* font, int startchar, int cm, + const patchnum_t* bgfont, boolean *on) +{ + int i; + + m->nl = 0; + m->nr = 0; + m->cl = -1; //jff 4/28/98 prepare for pre-increment + m->x = x; + m->y = y; + m->w = w; + m->h = h; + m->bg = bgfont; + m->on = on; + for (i=0;il[i], + x, + y + (hud_list_bgon? i+1 : i)*HU_REFRESHSPACING, + font, + startchar, + cm + ); + } +} + +// +// HUlib_addLineToMText() +// +// Adds a blank line to a hu_mtext_t widget +// +// Passed a hu_mtext_t +// Returns nothing +// +static void HUlib_addLineToMText(hu_mtext_t* m) +{ + // add a clear line + if (++m->cl == hud_msg_lines) + m->cl = 0; + HUlib_clearTextLine(&m->l[m->cl]); + + if (m->nlnl++; + + // needs updating + m->l[m->cl].needsupdate = 4; +} + +// +// HUlib_addMessageToMText() +// +// Adds a message line with prefix to a hu_mtext_t widget +// +// Passed a hu_mtext_t, the prefix string, and a message string +// Returns nothing +// +void HUlib_addMessageToMText(hu_mtext_t* m, const char* prefix, const char* msg) +{ + HUlib_addLineToMText(m); + if (prefix) + while (*prefix) + HUlib_addCharToTextLine(&m->l[m->cl], *(prefix++)); + + while (*msg) + HUlib_addCharToTextLine(&m->l[m->cl], *(msg++)); +} + +// +// HUlib_drawMBg() +// +// Draws a background box which the message display review widget can +// display over +// +// Passed position, width, height, and the background patches +// Returns nothing +// +void HUlib_drawMBg +( int x, + int y, + int w, + int h, + const patchnum_t* bgp +) +{ + int xs = bgp[0].width; + int ys = bgp[0].height; + int i,j; + + // CPhipps - patch drawing updated + // top rows + V_DrawNumPatch(x, y, FG, bgp[0].lumpnum, CR_DEFAULT, VPT_NONE); // ul + for (j=x+xs;jon) + return; // if not on, don't draw + + // draw everything + if (hud_list_bgon) + HUlib_drawMBg(m->x,m->y,m->w,m->h,m->bg); + y = m->y + HU_REFRESHSPACING; + for (i=0 ; inl ; i++) + { + idx = m->cl - i; + if (idx < 0) + idx += m->nl; // handle queue of lines + + l = &m->l[idx]; + if (hud_list_bgon) + { + l->x = m->x + 4; + l->y = m->y + (i+1)*HU_REFRESHSPACING; + } + else + { + l->x = m->x; + l->y = m->y + i*HU_REFRESHSPACING; + } + + // need a decision made here on whether to skip the draw + HUlib_drawTextLine(l, false); // no cursor, please + } +} + +// +// HUlib_eraseMText() +// +// Erases a hu_mtext_t widget, when the screen is not fullsize +// +// Passed a hu_mtext_t +// Returns nothing +// +void HUlib_eraseMText(hu_mtext_t* m) +{ + int i; + + for (i=0 ; i< m->nl ; i++) + { + m->l[i].needsupdate = 4; + HUlib_eraseTextLine(&m->l[i]); + } +} + +//////////////////////////////////////////////////////// +// +// Interactive text entry widget +// +//////////////////////////////////////////////////////// + +// +// HUlib_initIText() +// +// Initialize a hu_itext_t widget. Set the position, font, +// start char of the font, color range, and whether enabled. +// +// Passed a hu_itext_t, and the values used to initialize +// Returns nothing +// +void HUlib_initIText +( hu_itext_t* it, + int x, + int y, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + boolean* on ) +{ + it->lm = 0; // default left margin is start of text + it->on = on; + it->laston = true; + HUlib_initTextLine(&it->l, x, y, font, startchar, cm); +} + +// The following deletion routines adhere to the left margin restriction + +// +// HUlib_delCharFromIText() +// +// Deletes a character at the end of the text line in a hu_itext_t widget +// +// Passed the hu_itext_t +// Returns nothing +// +static void HUlib_delCharFromIText(hu_itext_t* it) +{ + if (it->l.len != it->lm) + HUlib_delCharFromTextLine(&it->l); +} + +// +// HUlib_eraseLineFromIText() +// +// Deletes all characters from a hu_itext_t widget +// +// Passed the hu_itext_t +// Returns nothing +// +static void HUlib_eraseLineFromIText(hu_itext_t* it) +{ + while (it->lm != it->l.len) + HUlib_delCharFromTextLine(&it->l); +} + +// +// HUlib_resetIText() +// +// Deletes all characters from a hu_itext_t widget +// Resets left margin as well +// +// Passed the hu_itext_t +// Returns nothing +// +void HUlib_resetIText(hu_itext_t* it) +{ + it->lm = 0; + HUlib_clearTextLine(&it->l); +} + +// +// HUlib_addPrefixToIText() +// +// Adds a prefix string passed to a hu_itext_t widget +// Sets left margin to length of string added +// +// Passed the hu_itext_t and the prefix string +// Returns nothing +// +void HUlib_addPrefixToIText +( hu_itext_t* it, + char* str ) +{ + while (*str) + HUlib_addCharToTextLine(&it->l, *(str++)); + it->lm = it->l.len; +} + +// +// HUlib_keyInIText() +// +// Wrapper function for handling general keyed input. +// +// Passed the hu_itext_t and the char input +// Returns true if it ate the key +// +boolean HUlib_keyInIText +( hu_itext_t* it, + unsigned char ch ) +{ + + if (ch >= ' ' && ch <= '_') + HUlib_addCharToTextLine(&it->l, (char) ch); + else if (ch == key_backspace) // phares + HUlib_delCharFromIText(it); + else if (ch != key_enter) // phares + return false; // did not eat key + + return true; // ate the key +} + +// +// HUlib_drawIText() +// +// Displays a hu_itext_t widget +// +// Passed the hu_itext_t +// Returns nothing +// +void HUlib_drawIText(hu_itext_t* it) +{ + hu_textline_t *l = &it->l; + + if (!*it->on) + return; + HUlib_drawTextLine(l, true); // draw the line w/ cursor +} + +// +// HUlib_eraseIText() +// +// Erases a hu_itext_t widget when the screen is not fullsize +// +// Passed the hu_itext_t +// Returns nothing +// +void HUlib_eraseIText(hu_itext_t* it) +{ + if (it->laston && !*it->on) + it->l.needsupdate = 4; + HUlib_eraseTextLine(&it->l); + it->laston = *it->on; +} diff --git a/src/hu_lib.h b/src/hu_lib.h new file mode 100644 index 00000000..db175721 --- /dev/null +++ b/src/hu_lib.h @@ -0,0 +1,247 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: none + * + *-----------------------------------------------------------------------------*/ + +#ifndef __HULIB__ +#define __HULIB__ + +// We are referring to patches. +#include "r_defs.h" +#include "v_video.h" //jff 2/16/52 include color range defs + + +/* background and foreground screen numbers + * different from other modules. */ +#define BG 1 +#define FG 0 + +/* font stuff + * #define HU_CHARERASE KEYD_BACKSPACE / not used / phares + */ + +#define HU_MAXLINES 4 +#define HU_MAXLINELENGTH 80 +#define HU_REFRESHSPACING 8 /*jff 2/26/98 space lines in text refresh widget*/ +/*jff 2/26/98 maximum number of messages allowed in refresh list */ +#define HU_MAXMESSAGES 16 + +/* + * Typedefs of widgets + */ + +/* Text Line widget + * (parent of Scrolling Text and Input Text widgets) */ +typedef struct +{ + // left-justified position of scrolling text window + int x; + int y; + + const patchnum_t* f; // font + int sc; // start character + //const char *cr; //jff 2/16/52 output color range + // Proff - Made this an int again. Needed for OpenGL + int cm; //jff 2/16/52 output color range + + // killough 1/23/98: Support multiple lines: + #define MAXLINES 25 + + int linelen; + char l[HU_MAXLINELENGTH*MAXLINES+1]; // line of text + int len; // current line length + + // whether this line needs to be udpated + int needsupdate; + +} hu_textline_t; + + + +// Scrolling Text window widget +// (child of Text Line widget) +typedef struct +{ + hu_textline_t l[HU_MAXLINES]; // text lines to draw + int h; // height in lines + int cl; // current line number + + // pointer to boolean stating whether to update window + boolean* on; + boolean laston; // last value of *->on. + +} hu_stext_t; + +//jff 2/26/98 new widget to display last hud_msg_lines of messages +// Message refresh window widget +typedef struct +{ + hu_textline_t l[HU_MAXMESSAGES]; // text lines to draw + int nl; // height in lines + int nr; // total height in rows + int cl; // current line number + + int x,y,w,h; // window position and size + const patchnum_t *bg; // patches for background + + // pointer to boolean stating whether to update window + boolean* on; + boolean laston; // last value of *->on. + +} hu_mtext_t; + + + +// Input Text Line widget +// (child of Text Line widget) +typedef struct +{ + hu_textline_t l; // text line to input on + + // left margin past which I am not to delete characters + int lm; + + // pointer to boolean stating whether to update window + boolean* on; + boolean laston; // last value of *->on; + +} hu_itext_t; + + +// +// Widget creation, access, and update routines +// + +// +// textline code +// + +// clear a line of text +void HUlib_clearTextLine(hu_textline_t *t); + +void HUlib_initTextLine +( + hu_textline_t *t, + int x, + int y, + const patchnum_t *f, + int sc, + int cm //jff 2/16/98 add color range parameter +); + +// returns success +boolean HUlib_addCharToTextLine(hu_textline_t *t, char ch); + +// draws tline +void HUlib_drawTextLine(hu_textline_t *l, boolean drawcursor); + +// erases text line +void HUlib_eraseTextLine(hu_textline_t *l); + + +// +// Scrolling Text window widget routines +// + +// initialize an stext widget +void HUlib_initSText +( hu_stext_t* s, + int x, + int y, + int h, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + boolean* on ); + +// add a text message to an stext widget +void HUlib_addMessageToSText(hu_stext_t* s, const char* prefix, const char* msg); + +// draws stext +void HUlib_drawSText(hu_stext_t* s); + +// erases all stext lines +void HUlib_eraseSText(hu_stext_t* s); + +//jff 2/26/98 message refresh widget +// initialize refresh text widget +void HUlib_initMText(hu_mtext_t *m, int x, int y, int w, int h, const patchnum_t* font, + int startchar, int cm, const patchnum_t* bgfont, boolean *on); + +//jff 2/26/98 message refresh widget +// add a text message to refresh text widget +void HUlib_addMessageToMText(hu_mtext_t* m, const char* prefix, const char* msg); + +//jff 2/26/98 new routine to display a background on which +// the list of last hud_msg_lines are displayed +void HUlib_drawMBg +( int x, + int y, + int w, + int h, + const patchnum_t* bgp +); + +//jff 2/26/98 message refresh widget +// draws mtext +void HUlib_drawMText(hu_mtext_t* m); + +//jff 4/28/98 erases behind message list +void HUlib_eraseMText(hu_mtext_t* m); + +// Input Text Line widget routines +void HUlib_initIText +( hu_itext_t* it, + int x, + int y, + const patchnum_t* font, + int startchar, + int cm, //jff 2/16/98 add color range parameter + boolean* on ); + +// resets line and left margin +void HUlib_resetIText(hu_itext_t* it); + +// left of left-margin +void HUlib_addPrefixToIText +( hu_itext_t* it, + char* str ); + +// whether eaten +boolean HUlib_keyInIText +( hu_itext_t* it, + unsigned char ch ); + +void HUlib_drawIText(hu_itext_t* it); + +// erases all itext lines +void HUlib_eraseIText(hu_itext_t* it); + +#endif diff --git a/src/hu_stuff.c b/src/hu_stuff.c new file mode 100644 index 00000000..b75b9823 --- /dev/null +++ b/src/hu_stuff.c @@ -0,0 +1,1598 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Heads-up displays + * + *----------------------------------------------------------------------------- + */ + +// killough 5/3/98: remove unnecessary headers + +#include "doomstat.h" +#include "hu_stuff.h" +#include "hu_lib.h" +#include "st_stuff.h" /* jff 2/16/98 need loc of status bar */ +#include "w_wad.h" +#include "s_sound.h" +#include "dstrings.h" +#include "sounds.h" +#include "d_deh.h" /* Ty 03/27/98 - externalization of mapnamesx arrays */ +#include "g_game.h" +#include "r_main.h" + +// global heads up display controls + +int hud_active; //jff 2/17/98 controls heads-up display mode +int hud_displayed; //jff 2/23/98 turns heads-up display on/off +int hud_nosecrets; //jff 2/18/98 allows secrets line to be disabled in HUD +int hud_distributed; //jff 3/4/98 display HUD in different places on screen +int hud_graph_keys=1; //jff 3/7/98 display HUD keys as graphics + +// +// Locally used constants, shortcuts. +// +// Ty 03/28/98 - +// These four shortcuts modifed to reflect char ** of mapnamesx[] +#define HU_TITLE (*mapnames[(gameepisode-1)*9+gamemap-1]) +#define HU_TITLE2 (*mapnames2[gamemap-1]) +#define HU_TITLEP (*mapnamesp[gamemap-1]) +#define HU_TITLET (*mapnamest[gamemap-1]) +#define HU_TITLEHEIGHT 1 +#define HU_TITLEX 0 +//jff 2/16/98 change 167 to ST_Y-1 +// CPhipps - changed to ST_TY +// proff - changed to 200-ST_HEIGHT for stretching +#define HU_TITLEY ((200-ST_HEIGHT) - 1 - hu_font[0].height) + +//jff 2/16/98 add coord text widget coordinates +// proff - changed to SCREENWIDTH to 320 for stretching +#define HU_COORDX (320 - 13*hu_font2['A'-HU_FONTSTART].width) +//jff 3/3/98 split coord widget into three lines in upper right of screen +#define HU_COORDX_Y (1 + 0*hu_font['A'-HU_FONTSTART].height) +#define HU_COORDY_Y (2 + 1*hu_font['A'-HU_FONTSTART].height) +#define HU_COORDZ_Y (3 + 2*hu_font['A'-HU_FONTSTART].height) + +//jff 2/16/98 add ammo, health, armor widgets, 2/22/98 less gap +#define HU_GAPY 8 +#define HU_HUDHEIGHT (6*HU_GAPY) +#define HU_HUDX 2 +#define HU_HUDY (200-HU_HUDHEIGHT-1) +#define HU_MONSECX (HU_HUDX) +#define HU_MONSECY (HU_HUDY+0*HU_GAPY) +#define HU_KEYSX (HU_HUDX) +//jff 3/7/98 add offset for graphic key widget +#define HU_KEYSGX (HU_HUDX+4*hu_font2['A'-HU_FONTSTART].width) +#define HU_KEYSY (HU_HUDY+1*HU_GAPY) +#define HU_WEAPX (HU_HUDX) +#define HU_WEAPY (HU_HUDY+2*HU_GAPY) +#define HU_AMMOX (HU_HUDX) +#define HU_AMMOY (HU_HUDY+3*HU_GAPY) +#define HU_HEALTHX (HU_HUDX) +#define HU_HEALTHY (HU_HUDY+4*HU_GAPY) +#define HU_ARMORX (HU_HUDX) +#define HU_ARMORY (HU_HUDY+5*HU_GAPY) + +//jff 3/4/98 distributed HUD positions +#define HU_HUDX_LL 2 +#define HU_HUDY_LL (200-2*HU_GAPY-1) +// proff/nicolas 09/20/98: Changed for high-res +#define HU_HUDX_LR (320-120) +#define HU_HUDY_LR (200-2*HU_GAPY-1) +// proff/nicolas 09/20/98: Changed for high-res +#define HU_HUDX_UR (320-96) +#define HU_HUDY_UR 2 +#define HU_MONSECX_D (HU_HUDX_LL) +#define HU_MONSECY_D (HU_HUDY_LL+0*HU_GAPY) +#define HU_KEYSX_D (HU_HUDX_LL) +#define HU_KEYSGX_D (HU_HUDX_LL+4*hu_font2['A'-HU_FONTSTART].width) +#define HU_KEYSY_D (HU_HUDY_LL+1*HU_GAPY) +#define HU_WEAPX_D (HU_HUDX_LR) +#define HU_WEAPY_D (HU_HUDY_LR+0*HU_GAPY) +#define HU_AMMOX_D (HU_HUDX_LR) +#define HU_AMMOY_D (HU_HUDY_LR+1*HU_GAPY) +#define HU_HEALTHX_D (HU_HUDX_UR) +#define HU_HEALTHY_D (HU_HUDY_UR+0*HU_GAPY) +#define HU_ARMORX_D (HU_HUDX_UR) +#define HU_ARMORY_D (HU_HUDY_UR+1*HU_GAPY) + +//#define HU_INPUTTOGGLE 't' // not used // phares +#define HU_INPUTX HU_MSGX +#define HU_INPUTY (HU_MSGY + HU_MSGHEIGHT*(hu_font[0].height) +1) +#define HU_INPUTWIDTH 64 +#define HU_INPUTHEIGHT 1 + +#define key_alt KEYD_RALT +#define key_shift KEYD_RSHIFT + +const char* chat_macros[] = +// Ty 03/27/98 - *not* externalized +// CPhipps - const char* +{ + HUSTR_CHATMACRO0, + HUSTR_CHATMACRO1, + HUSTR_CHATMACRO2, + HUSTR_CHATMACRO3, + HUSTR_CHATMACRO4, + HUSTR_CHATMACRO5, + HUSTR_CHATMACRO6, + HUSTR_CHATMACRO7, + HUSTR_CHATMACRO8, + HUSTR_CHATMACRO9 +}; + +const char* player_names[] = +// Ty 03/27/98 - *not* externalized +// CPhipps - const char* +{ + HUSTR_PLRGREEN, + HUSTR_PLRINDIGO, + HUSTR_PLRBROWN, + HUSTR_PLRRED +}; + +//jff 3/17/98 translate player colmap to text color ranges +int plyrcoltran[MAXPLAYERS]={CR_GREEN,CR_GRAY,CR_BROWN,CR_RED}; + +char chat_char; // remove later. +static player_t* plr; + +// font sets +patchnum_t hu_font[HU_FONTSIZE]; +patchnum_t hu_font2[HU_FONTSIZE]; +patchnum_t hu_fontk[HU_FONTSIZE];//jff 3/7/98 added for graphic key indicators +patchnum_t hu_msgbg[9]; //jff 2/26/98 add patches for message background + +// widgets +static hu_textline_t w_title; +static hu_stext_t w_message; +static hu_itext_t w_chat; +static hu_itext_t w_inputbuffer[MAXPLAYERS]; +static hu_textline_t w_coordx; //jff 2/16/98 new coord widget for automap +static hu_textline_t w_coordy; //jff 3/3/98 split coord widgets automap +static hu_textline_t w_coordz; //jff 3/3/98 split coord widgets automap +static hu_textline_t w_ammo; //jff 2/16/98 new ammo widget for hud +static hu_textline_t w_health; //jff 2/16/98 new health widget for hud +static hu_textline_t w_armor; //jff 2/16/98 new armor widget for hud +static hu_textline_t w_weapon; //jff 2/16/98 new weapon widget for hud +static hu_textline_t w_keys; //jff 2/16/98 new keys widget for hud +static hu_textline_t w_gkeys; //jff 3/7/98 graphic keys widget for hud +static hu_textline_t w_monsec; //jff 2/16/98 new kill/secret widget for hud +static hu_mtext_t w_rtext; //jff 2/26/98 text message refresh widget + +static boolean always_off = false; +static char chat_dest[MAXPLAYERS]; +boolean chat_on; +static boolean message_on; +static boolean message_list; //2/26/98 enable showing list of messages +boolean message_dontfuckwithme; +static boolean message_nottobefuckedwith; +static int message_counter; +extern int showMessages; +extern boolean automapactive; +static boolean headsupactive = false; + +//jff 2/16/98 hud supported automap colors added +int hudcolor_titl; // color range of automap level title +int hudcolor_xyco; // color range of new coords on automap +//jff 2/16/98 hud text colors, controls added +int hudcolor_mesg; // color range of scrolling messages +int hudcolor_chat; // color range of chat lines +int hud_msg_lines; // number of message lines in window +//jff 2/26/98 hud text colors, controls added +int hudcolor_list; // list of messages color +int hud_list_bgon; // enable for solid window background for message list + +//jff 2/16/98 initialization strings for ammo, health, armor widgets +static char hud_coordstrx[32]; +static char hud_coordstry[32]; +static char hud_coordstrz[32]; +static char hud_ammostr[80]; +static char hud_healthstr[80]; +static char hud_armorstr[80]; +static char hud_weapstr[80]; +static char hud_keysstr[80]; +static char hud_gkeysstr[80]; //jff 3/7/98 add support for graphic key display +static char hud_monsecstr[80]; + +// +// Builtin map names. +// The actual names can be found in DStrings.h. +// +// Ty 03/27/98 - externalized map name arrays - now in d_deh.c +// and converted to arrays of pointers to char * +// See modified HUTITLEx macros +extern char **mapnames[]; +extern char **mapnames2[]; +extern char **mapnamesp[]; +extern char **mapnamest[]; + +extern int map_point_coordinates; + +// key tables +// jff 5/10/98 french support removed, +// as it was not being used and couldn't be easily tested +// +const char* shiftxform; + +const char english_shiftxform[] = +{ + 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, + ' ', '!', '"', '#', '$', '%', '&', + '"', // shift-' + '(', ')', '*', '+', + '<', // shift-, + '_', // shift-- + '>', // shift-. + '?', // shift-/ + ')', // shift-0 + '!', // shift-1 + '@', // shift-2 + '#', // shift-3 + '$', // shift-4 + '%', // shift-5 + '^', // shift-6 + '&', // shift-7 + '*', // shift-8 + '(', // shift-9 + ':', + ':', // shift-; + '<', + '+', // shift-= + '>', '?', '@', + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '[', // shift-[ + '!', // shift-backslash - OH MY GOD DOES WATCOM SUCK + ']', // shift-] + '"', '_', + '\'', // shift-` + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + '{', '|', '}', '~', 127 +}; + +// +// HU_Init() +// +// Initialize the heads-up display, text that overwrites the primary display +// +// Passed nothing, returns nothing +// +void HU_Init(void) +{ + + int i; + int j; + char buffer[9]; + + shiftxform = english_shiftxform; + + // load the heads-up font + j = HU_FONTSTART; + for (i=0;i122) + { + sprintf(buffer, "STBR%.3d",j); + R_SetPatchNum(&hu_font2[i], buffer); + R_SetPatchNum(&hu_font[i], buffer); + } + else + hu_font[i] = hu_font[0]; //jff 2/16/98 account for gap + } + + // CPhipps - load patches for message background + for (i=0; i<9; i++) { + sprintf(buffer, "BOX%c%c", "UCL"[i/3], "LCR"[i%3]); + R_SetPatchNum(&hu_msgbg[i], buffer); + } + + // CPhipps - load patches for keys and double keys + for (i=0; i<6; i++) { + sprintf(buffer, "STKEYS%d", i); + R_SetPatchNum(&hu_fontk[i], buffer); + } +} + +// +// HU_Stop() +// +// Make the heads-up displays inactive +// +// Passed nothing, returns nothing +// +static void HU_Stop(void) +{ + headsupactive = false; +} + +// +// HU_Start(void) +// +// Create and initialize the heads-up widgets, software machines to +// maintain, update, and display information over the primary display +// +// This routine must be called after any change to the heads up configuration +// in order for the changes to take effect in the actual displays +// +// Passed nothing, returns nothing +// +void HU_Start(void) +{ + + int i; + const char* s; /* cph - const */ + + if (headsupactive) // stop before starting + HU_Stop(); + + plr = &players[displayplayer]; // killough 3/7/98 + message_on = false; + message_dontfuckwithme = false; + message_nottobefuckedwith = false; + chat_on = false; + + // create the message widget + // messages to player in upper-left of screen + HUlib_initSText + ( + &w_message, + HU_MSGX, + HU_MSGY, + HU_MSGHEIGHT, + hu_font, + HU_FONTSTART, + hudcolor_mesg, + &message_on + ); + + //jff 2/16/98 added some HUD widgets + // create the map title widget - map title display in lower left of automap + HUlib_initTextLine + ( + &w_title, + HU_TITLEX, + HU_TITLEY, + hu_font, + HU_FONTSTART, + hudcolor_titl + ); + + // create the hud health widget + // bargraph and number for amount of health, + // lower left or upper right of screen + HUlib_initTextLine + ( + &w_health, + hud_distributed? HU_HEALTHX_D : HU_HEALTHX, //3/4/98 distribute + hud_distributed? HU_HEALTHY_D : HU_HEALTHY, + hu_font2, + HU_FONTSTART, + CR_GREEN + ); + + // create the hud armor widget + // bargraph and number for amount of armor, + // lower left or upper right of screen + HUlib_initTextLine + ( + &w_armor, + hud_distributed? HU_ARMORX_D : HU_ARMORX, //3/4/98 distribute + hud_distributed? HU_ARMORY_D : HU_ARMORY, + hu_font2, + HU_FONTSTART, + CR_GREEN + ); + + // create the hud ammo widget + // bargraph and number for amount of ammo for current weapon, + // lower left or lower right of screen + HUlib_initTextLine + ( + &w_ammo, + hud_distributed? HU_AMMOX_D : HU_AMMOX, //3/4/98 distribute + hud_distributed? HU_AMMOY_D : HU_AMMOY, + hu_font2, + HU_FONTSTART, + CR_GOLD + ); + + // create the hud weapons widget + // list of numbers of weapons possessed + // lower left or lower right of screen + HUlib_initTextLine + ( + &w_weapon, + hud_distributed? HU_WEAPX_D : HU_WEAPX, //3/4/98 distribute + hud_distributed? HU_WEAPY_D : HU_WEAPY, + hu_font2, + HU_FONTSTART, + CR_GRAY + ); + + // create the hud keys widget + // display of key letters possessed + // lower left of screen + HUlib_initTextLine + ( + &w_keys, + hud_distributed? HU_KEYSX_D : HU_KEYSX, //3/4/98 distribute + hud_distributed? HU_KEYSY_D : HU_KEYSY, + hu_font2, + HU_FONTSTART, + CR_GRAY + ); + + // create the hud graphic keys widget + // display of key graphics possessed + // lower left of screen + HUlib_initTextLine + ( + &w_gkeys, + hud_distributed? HU_KEYSGX_D : HU_KEYSGX, //3/4/98 distribute + hud_distributed? HU_KEYSY_D : HU_KEYSY, + hu_fontk, + HU_FONTSTART, + CR_RED + ); + + // create the hud monster/secret widget + // totals and current values for kills, items, secrets + // lower left of screen + HUlib_initTextLine + ( + &w_monsec, + hud_distributed? HU_MONSECX_D : HU_MONSECX, //3/4/98 distribute + hud_distributed? HU_MONSECY_D : HU_MONSECY, + hu_font2, + HU_FONTSTART, + CR_GRAY + ); + + // create the hud text refresh widget + // scrolling display of last hud_msg_lines messages received + if (hud_msg_lines>HU_MAXMESSAGES) + hud_msg_lines=HU_MAXMESSAGES; + //jff 4/21/98 if setup has disabled message list while active, turn it off + message_list = hud_msg_lines > 1; //jff 8/8/98 initialize both ways + //jff 2/26/98 add the text refresh widget initialization + HUlib_initMText + ( + &w_rtext, + 0, + 0, + 320, +// SCREENWIDTH, + (hud_msg_lines+2)*HU_REFRESHSPACING, + hu_font, + HU_FONTSTART, + hudcolor_list, + hu_msgbg, + &message_list + ); + + // initialize the automap's level title widget + if (gamestate == GS_LEVEL) /* cph - stop SEGV here when not in level */ + switch (gamemode) + { + case shareware: + case registered: + case retail: + s = HU_TITLE; + break; + + case commercial: + default: // Ty 08/27/98 - modified to check mission for TNT/Plutonia + s = (gamemission==pack_tnt) ? HU_TITLET : + (gamemission==pack_plut) ? HU_TITLEP : HU_TITLE2; + break; + } else s = ""; + while (*s) + HUlib_addCharToTextLine(&w_title, *(s++)); + + // create the automaps coordinate widget + // jff 3/3/98 split coord widget into three lines: x,y,z + // jff 2/16/98 added + HUlib_initTextLine + ( + &w_coordx, + HU_COORDX, + HU_COORDX_Y, + hu_font, + HU_FONTSTART, + hudcolor_xyco + ); + HUlib_initTextLine + ( + &w_coordy, + HU_COORDX, + HU_COORDY_Y, + hu_font, + HU_FONTSTART, + hudcolor_xyco + ); + HUlib_initTextLine + ( + &w_coordz, + HU_COORDX, + HU_COORDZ_Y, + hu_font, + HU_FONTSTART, + hudcolor_xyco + ); + + // initialize the automaps coordinate widget + //jff 3/3/98 split coordstr widget into 3 parts + if (map_point_coordinates) + { + sprintf(hud_coordstrx,"X: %-5d",0); //jff 2/22/98 added z + s = hud_coordstrx; + while (*s) + HUlib_addCharToTextLine(&w_coordx, *(s++)); + sprintf(hud_coordstry,"Y: %-5d",0); //jff 3/3/98 split x,y,z + s = hud_coordstry; + while (*s) + HUlib_addCharToTextLine(&w_coordy, *(s++)); + sprintf(hud_coordstrz,"Z: %-5d",0); //jff 3/3/98 split x,y,z + s = hud_coordstrz; + while (*s) + HUlib_addCharToTextLine(&w_coordz, *(s++)); + } + + //jff 2/16/98 initialize ammo widget + strcpy(hud_ammostr,"AMM "); + s = hud_ammostr; + while (*s) + HUlib_addCharToTextLine(&w_ammo, *(s++)); + + //jff 2/16/98 initialize health widget + strcpy(hud_healthstr,"HEL "); + s = hud_healthstr; + while (*s) + HUlib_addCharToTextLine(&w_health, *(s++)); + + //jff 2/16/98 initialize armor widget + strcpy(hud_armorstr,"ARM "); + s = hud_armorstr; + while (*s) + HUlib_addCharToTextLine(&w_armor, *(s++)); + + //jff 2/17/98 initialize weapons widget + strcpy(hud_weapstr,"WEA "); + s = hud_weapstr; + while (*s) + HUlib_addCharToTextLine(&w_weapon, *(s++)); + + //jff 2/17/98 initialize keys widget + if (!deathmatch) //jff 3/17/98 show frags in deathmatch mode + strcpy(hud_keysstr,"KEY "); + else + strcpy(hud_keysstr,"FRG "); + s = hud_keysstr; + while (*s) + HUlib_addCharToTextLine(&w_keys, *(s++)); + + //jff 2/17/98 initialize graphic keys widget + strcpy(hud_gkeysstr," "); + s = hud_gkeysstr; + while (*s) + HUlib_addCharToTextLine(&w_gkeys, *(s++)); + + //jff 2/17/98 initialize kills/items/secret widget + strcpy(hud_monsecstr,"STS "); + s = hud_monsecstr; + while (*s) + HUlib_addCharToTextLine(&w_monsec, *(s++)); + + // create the chat widget + HUlib_initIText + ( + &w_chat, + HU_INPUTX, + HU_INPUTY, + hu_font, + HU_FONTSTART, + hudcolor_chat, + &chat_on + ); + + // create the inputbuffer widgets, one per player + for (i=0 ; imo->x)>>FRACBITS); + HUlib_clearTextLine(&w_coordx); + s = hud_coordstrx; + while (*s) + HUlib_addCharToTextLine(&w_coordx, *(s++)); + HUlib_drawTextLine(&w_coordx, false); + + //jff 3/3/98 split coord display into x,y,z lines + // y-coord + sprintf(hud_coordstry,"Y: %-5d", (plr->mo->y)>>FRACBITS); + HUlib_clearTextLine(&w_coordy); + s = hud_coordstry; + while (*s) + HUlib_addCharToTextLine(&w_coordy, *(s++)); + HUlib_drawTextLine(&w_coordy, false); + + //jff 3/3/98 split coord display into x,y,z lines + //jff 2/22/98 added z + // z-coord + sprintf(hud_coordstrz,"Z: %-5d", (plr->mo->z)>>FRACBITS); + HUlib_clearTextLine(&w_coordz); + s = hud_coordstrz; + while (*s) + HUlib_addCharToTextLine(&w_coordz, *(s++)); + HUlib_drawTextLine(&w_coordz, false); + } + } + + // draw the weapon/health/ammo/armor/kills/keys displays if optioned + //jff 2/17/98 allow new hud stuff to be turned off + // killough 2/21/98: really allow new hud stuff to be turned off COMPLETELY + if + ( + hud_active>0 && // hud optioned on + hud_displayed && // hud on from fullscreen key + viewheight==SCREENHEIGHT && // fullscreen mode is active + !(automapmode & am_active) // automap is not active + ) + { + doit = !(gametic&1); //jff 3/4/98 speed update up for slow systems + if (doit) //jff 8/7/98 update every time, avoid lag in update + { + HU_MoveHud(); // insure HUD display coords are correct + + // do the hud ammo display + // clear the widgets internal line + HUlib_clearTextLine(&w_ammo); + strcpy(hud_ammostr,"AMM "); + if (weaponinfo[plr->readyweapon].ammo == am_noammo) + { // special case for weapon with no ammo selected - blank bargraph + N/A + strcat(hud_ammostr,"\x7f\x7f\x7f\x7f\x7f\x7f\x7f N/A"); + w_ammo.cm = CR_GRAY; + } + else + { + int ammo = plr->ammo[weaponinfo[plr->readyweapon].ammo]; + int fullammo = plr->maxammo[weaponinfo[plr->readyweapon].ammo]; + int ammolevel = P_GetAmmoLevel(plr, plr->readyweapon); + int ammobars = ammolevel/4; + + // build the numeric amount init string + sprintf(ammostr,"%d/%d",ammo,fullammo); + // build the bargraph string + // full bargraph chars + for (i=4;i<4+ammobars/4;) + hud_ammostr[i++] = 123; + // plus one last character with 0,1,2,3 bars + switch(ammobars%4) + { + case 0: + break; + case 1: + hud_ammostr[i++] = 126; + break; + case 2: + hud_ammostr[i++] = 125; + break; + case 3: + hud_ammostr[i++] = 124; + break; + } + // pad string with blank bar characters + while(i<4+7) + hud_ammostr[i++] = 127; + hud_ammostr[i] = '\0'; + strcat(hud_ammostr,ammostr); + + // set the display color from the percentage of total ammo held + if (ammolevel == 0) + w_ammo.cm = CR_BROWN; + else if (ammolevel >= 100) + w_ammo.cm = CR_BLUE; + else if (ammolevel < ammo_red) + w_ammo.cm = CR_RED; + else if (ammolevel < ammo_yellow) + w_ammo.cm = CR_GOLD; + else + w_ammo.cm = CR_GREEN; + } + // transfer the init string to the widget + s = hud_ammostr; + while (*s) + HUlib_addCharToTextLine(&w_ammo, *(s++)); + } + // display the ammo widget every frame + HUlib_drawTextLine(&w_ammo, false); + + // do the hud health display + if (doit) + { + int health = plr->health; + int healthbars = health>100? 25 : health/4; + + // clear the widgets internal line + HUlib_clearTextLine(&w_health); + + // build the numeric amount init string + sprintf(healthstr,"%3d",health); + // build the bargraph string + // full bargraph chars + for (i=4;i<4+healthbars/4;) + hud_healthstr[i++] = 123; + // plus one last character with 0,1,2,3 bars + switch(healthbars%4) + { + case 0: + break; + case 1: + hud_healthstr[i++] = 126; + break; + case 2: + hud_healthstr[i++] = 125; + break; + case 3: + hud_healthstr[i++] = 124; + break; + } + // pad string with blank bar characters + while(i<4+7) + hud_healthstr[i++] = 127; + hud_healthstr[i] = '\0'; + strcat(hud_healthstr,healthstr); + + // set the display color from the amount of health posessed + if (healtharmorpoints; + int armorbars = armor>100? 25 : armor/4; + + // clear the widgets internal line + HUlib_clearTextLine(&w_armor); + // build the numeric amount init string + sprintf(armorstr,"%3d",armor); + // build the bargraph string + // full bargraph chars + for (i=4;i<4+armorbars/4;) + hud_armorstr[i++] = 123; + // plus one last character with 0,1,2,3 bars + switch(armorbars%4) + { + case 0: + break; + case 1: + hud_armorstr[i++] = 126; + break; + case 2: + hud_armorstr[i++] = 125; + break; + case 3: + hud_armorstr[i++] = 124; + break; + } + // pad string with blank bar characters + while(i<4+7) + hud_armorstr[i++] = 127; + hud_armorstr[i] = '\0'; + strcat(hud_armorstr,armorstr); + + // set the display color from the amount of armor posessed + if (armor=wp_plasma && w!=wp_chainsaw) + ok=0; + break; + case retail: + case registered: + if (w>=wp_supershotgun) + ok=0; + break; + default: + case commercial: + break; + } + if (!ok) continue; + + // skip weapons not currently posessed + if (!plr->weaponowned[w]) + continue; + + ammolevel = P_GetAmmoLevel(plr, w); + + // display each weapon number in a color related to the ammo for it + hud_weapstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + if (weaponinfo[w].ammo==am_noammo) //jff 3/14/98 show berserk on HUD + hud_weapstr[i++] = plr->powers[pw_strength]? '0'+CR_GREEN : '0'+CR_GRAY; + else if (ammolevel == 0) + hud_weapstr[i++] = '0'+CR_BROWN; + else if (ammolevel >= 100) + hud_weapstr[i++] = '0'+CR_BLUE; + else if (ammolevel < ammo_red) + hud_weapstr[i++] = '0'+CR_RED; + else if (ammolevel < ammo_yellow) + hud_weapstr[i++] = '0'+CR_GOLD; + else + hud_weapstr[i++] = '0'+CR_GREEN; + hud_weapstr[i++] = '0'+w+1; + hud_weapstr[i++] = ' '; + hud_weapstr[i] = '\0'; + } + + // transfer the init string to the widget + s = hud_weapstr; + while (*s) + HUlib_addCharToTextLine(&w_weapon, *(s++)); + } + // display the weapon widget every frame + HUlib_drawTextLine(&w_weapon, false); + + if (doit && hud_active>1) + { + int k; + + hud_keysstr[4] = '\0'; //jff 3/7/98 make sure deleted keys go away + //jff add case for graphic key display + if (!deathmatch && hud_graph_keys) + { + i=0; + hud_gkeysstr[i] = '\0'; //jff 3/7/98 init graphic keys widget string + // build text string whose characters call out graphic keys from fontk + for (k=0;k<6;k++) + { + // skip keys not possessed + if (!plr->cards[k]) + continue; + + hud_gkeysstr[i++] = '!'+k; // key number plus '!' is char for key + hud_gkeysstr[i++] = ' '; // spacing + hud_gkeysstr[i++] = ' '; + } + hud_gkeysstr[i]='\0'; + } + else // not possible in current code, unless deathmatching, + { + i=4; + hud_keysstr[i] = '\0'; //jff 3/7/98 make sure deleted keys go away + + // if deathmatch, build string showing top four frag counts + if (deathmatch) //jff 3/17/98 show frags, not keys, in deathmatch + { + int top1=-999,top2=-999,top3=-999,top4=-999; + int idx1=-1,idx2=-1,idx3=-1,idx4=-1; + int fragcount,m; + char numbuf[32]; + + // scan thru players + for (k=0;ktop1) + { + top4=top3; top3=top2; top2 = top1; top1=fragcount; + idx4=idx3; idx3=idx2; idx2 = idx1; idx1=k; + } + else if (fragcount>top2) + { + top4=top3; top3=top2; top2=fragcount; + idx4=idx3; idx3=idx2; idx2=k; + } + else if (fragcount>top3) + { + top4=top3; top3=fragcount; + idx4=idx3; idx3=k; + } + else if (fragcount>top4) + { + top4=fragcount; + idx4=k; + } + } + // if the biggest number exists, put it in the init string + if (idx1>-1) + { + sprintf(numbuf,"%5d",top1); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx1&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + // if the second biggest number exists, put it in the init string + if (idx2>-1) + { + sprintf(numbuf,"%5d",top2); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx2&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + // if the third biggest number exists, put it in the init string + if (idx3>-1) + { + sprintf(numbuf,"%5d",top3); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx3&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + // if the fourth biggest number exists, put it in the init string + if (idx4>-1) + { + sprintf(numbuf,"%5d",top4); + // make frag count in player's color via escape code + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + hud_keysstr[i++] = '0'+plyrcoltran[idx4&3]; + s = numbuf; + while (*s) + hud_keysstr[i++] = *(s++); + } + hud_keysstr[i] = '\0'; + } //jff 3/17/98 end of deathmatch clause + else // build alphabetical key display (not used currently) + { + // scan the keys + for (k=0;k<6;k++) + { + // skip any not possessed by the displayed player's stats + if (!plr->cards[k]) + continue; + + // use color escapes to make text in key's color + hud_keysstr[i++] = '\x1b'; //jff 3/26/98 use ESC not '\' for paths + switch(k) + { + case 0: + hud_keysstr[i++] = '0'+CR_BLUE; + hud_keysstr[i++] = 'B'; + hud_keysstr[i++] = 'C'; + hud_keysstr[i++] = ' '; + break; + case 1: + hud_keysstr[i++] = '0'+CR_GOLD; + hud_keysstr[i++] = 'Y'; + hud_keysstr[i++] = 'C'; + hud_keysstr[i++] = ' '; + break; + case 2: + hud_keysstr[i++] = '0'+CR_RED; + hud_keysstr[i++] = 'R'; + hud_keysstr[i++] = 'C'; + hud_keysstr[i++] = ' '; + break; + case 3: + hud_keysstr[i++] = '0'+CR_BLUE; + hud_keysstr[i++] = 'B'; + hud_keysstr[i++] = 'S'; + hud_keysstr[i++] = ' '; + break; + case 4: + hud_keysstr[i++] = '0'+CR_GOLD; + hud_keysstr[i++] = 'Y'; + hud_keysstr[i++] = 'S'; + hud_keysstr[i++] = ' '; + break; + case 5: + hud_keysstr[i++] = '0'+CR_RED; + hud_keysstr[i++] = 'R'; + hud_keysstr[i++] = 'S'; + hud_keysstr[i++] = ' '; + break; + } + hud_keysstr[i]='\0'; + } + } + } + } + // display the keys/frags line each frame + if (hud_active>1) + { + HUlib_clearTextLine(&w_keys); // clear the widget strings + HUlib_clearTextLine(&w_gkeys); + + // transfer the built string (frags or key title) to the widget + s = hud_keysstr; //jff 3/7/98 display key titles/key text or frags + while (*s) + HUlib_addCharToTextLine(&w_keys, *(s++)); + HUlib_drawTextLine(&w_keys, false); + + //jff 3/17/98 show graphic keys in non-DM only + if (!deathmatch) //jff 3/7/98 display graphic keys + { + // transfer the graphic key text to the widget + s = hud_gkeysstr; + while (*s) + HUlib_addCharToTextLine(&w_gkeys, *(s++)); + // display the widget + HUlib_drawTextLine(&w_gkeys, false); + } + } + + // display the hud kills/items/secret display if optioned + if (!hud_nosecrets) + { + if (hud_active>1 && doit) + { + // clear the internal widget text buffer + HUlib_clearTextLine(&w_monsec); + //jff 3/26/98 use ESC not '\' for paths + // build the init string with fixed colors + sprintf + ( + hud_monsecstr, + "STS \x1b\x36K \x1b\x33%d \x1b\x36M \x1b\x33%d \x1b\x37I \x1b\x33%d/%d \x1b\x35S \x1b\x33%d/%d", + plr->killcount,totallive, + plr->itemcount,totalitems, + plr->secretcount,totalsecret + ); + // transfer the init string to the widget + s = hud_monsecstr; + while (*s) + HUlib_addCharToTextLine(&w_monsec, *(s++)); + } + // display the kills/items/secrets each frame, if optioned + if (hud_active>1) + HUlib_drawTextLine(&w_monsec, false); + } + } + + //jff 3/4/98 display last to give priority + HU_Erase(); // jff 4/24/98 Erase current lines before drawing current + // needed when screen not fullsize + + //jff 4/21/98 if setup has disabled message list while active, turn it off + if (hud_msg_lines<=1) + message_list = false; + + // if the message review not enabled, show the standard message widget + if (!message_list) + HUlib_drawSText(&w_message); + + // if the message review is enabled show the scrolling message review + if (hud_msg_lines>1 && message_list) + HUlib_drawMText(&w_rtext); + + // display the interactive buffer for chat entry + HUlib_drawIText(&w_chat); +} + +// +// HU_Erase() +// +// Erase hud display lines that can be trashed by small screen display +// +// Passed nothing, returns nothing +// +void HU_Erase(void) +{ + // erase the message display or the message review display + if (!message_list) + HUlib_eraseSText(&w_message); + else + HUlib_eraseMText(&w_rtext); + + // erase the interactive text buffer for chat entry + HUlib_eraseIText(&w_chat); + + // erase the automap title + HUlib_eraseTextLine(&w_title); +} + +// +// HU_Ticker() +// +// Update the hud displays once per frame +// +// Passed nothing, returns nothing +// +static boolean bsdown; // Is backspace down? +static int bscounter; + +void HU_Ticker(void) +{ + int i, rc; + char c; + + // tick down message counter if message is up + if (message_counter && !--message_counter) + { + message_on = false; + message_nottobefuckedwith = false; + } + if (bsdown && bscounter++ > 9) { + HUlib_keyInIText(&w_chat, (unsigned char)key_backspace); + bscounter = 8; + } + + // if messages on, or "Messages Off" is being displayed + // this allows the notification of turning messages off to be seen + if (showMessages || message_dontfuckwithme) + { + // display message if necessary + if ((plr->message && !message_nottobefuckedwith) + || (plr->message && message_dontfuckwithme)) + { + //post the message to the message widget + HUlib_addMessageToSText(&w_message, 0, plr->message); + //jff 2/26/98 add message to refresh text widget too + HUlib_addMessageToMText(&w_rtext, 0, plr->message); + + // clear the message to avoid posting multiple times + plr->message = 0; + // note a message is displayed + message_on = true; + // start the message persistence counter + message_counter = HU_MSGTIMEOUT; + // transfer "Messages Off" exception to the "being displayed" variable + message_nottobefuckedwith = message_dontfuckwithme; + // clear the flag that "Messages Off" is being posted + message_dontfuckwithme = 0; + } + } + + // check for incoming chat characters + if (netgame) + { + for (i=0; i= 'a' && c <= 'z') + c = (char) shiftxform[(unsigned char) c]; + rc = HUlib_keyInIText(&w_inputbuffer[i], c); + if (rc && c == KEYD_ENTER) + { + if (w_inputbuffer[i].l.len + && (chat_dest[i] == consoleplayer+1 + || chat_dest[i] == HU_BROADCAST)) + { + HUlib_addMessageToSText(&w_message, + player_names[i], + w_inputbuffer[i].l.l); + + message_nottobefuckedwith = true; + message_on = true; + message_counter = HU_MSGTIMEOUT; + if ( gamemode == commercial ) + S_StartSound(0, sfx_radio); + else + S_StartSound(0, sfx_tink); + } + HUlib_resetIText(&w_inputbuffer[i]); + } + } + players[i].cmd.chatchar = 0; + } + } + } +} + +#define QUEUESIZE 128 + +static char chatchars[QUEUESIZE]; +static int head = 0; +static int tail = 0; + +// +// HU_queueChatChar() +// +// Add an incoming character to the circular chat queue +// +// Passed the character to queue, returns nothing +// +static void HU_queueChatChar(char c) +{ + if (((head + 1) & (QUEUESIZE-1)) == tail) + { + plr->message = HUSTR_MSGU; + } + else + { + chatchars[head] = c; + head = (head + 1) & (QUEUESIZE-1); + } +} + +// +// HU_dequeueChatChar() +// +// Remove the earliest added character from the circular chat queue +// +// Passed nothing, returns the character dequeued +// +char HU_dequeueChatChar(void) +{ + char c; + + if (head != tail) + { + c = chatchars[tail]; + tail = (tail + 1) & (QUEUESIZE-1); + } + else + { + c = 0; + } + return c; +} + +// +// HU_Responder() +// +// Responds to input events that affect the heads up displays +// +// Passed the event to respond to, returns true if the event was handled +// +boolean HU_Responder(event_t *ev) +{ + + static char lastmessage[HU_MAXLINELENGTH+1]; + const char* macromessage; // CPhipps - const char* + boolean eatkey = false; + static boolean shiftdown = false; + static boolean altdown = false; + unsigned char c; + int i; + int numplayers; + + static int num_nobrainers = 0; + + numplayers = 0; + for (i=0 ; idata1 == key_shift) + { + shiftdown = ev->type == ev_keydown; + return false; + } + else if (ev->data1 == key_alt) + { + altdown = ev->type == ev_keydown; + return false; + } + else if (ev->data1 == key_backspace) + { + bsdown = ev->type == ev_keydown; + bscounter = 0; + } + + if (ev->type != ev_keydown) + return false; + + if (!chat_on) + { + if (ev->data1 == key_enter) // phares + { + if (hud_msg_lines>1) // it posts multi-line messages that will trash + { + if (message_list) HU_Erase(); //jff 4/28/98 erase behind messages + message_list = !message_list; //jff 2/26/98 toggle list of messages + } + if (!message_list) // if not message list, refresh message + { + message_on = true; + message_counter = HU_MSGTIMEOUT; + } + eatkey = true; + }//jff 2/26/98 no chat if message review is displayed + // killough 10/02/98: no chat if demo playback + // no chat in -solo-net mode + else if (!demoplayback && !message_list && netgame && numplayers > 1) + { + if (ev->data1 == key_chat) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar(HU_BROADCAST); + } + else if (numplayers > 2) + { + for (i=0; idata1 == destination_keys[i]) + { + if (playeringame[i] && i!=consoleplayer) + { + eatkey = chat_on = true; + HUlib_resetIText(&w_chat); + HU_queueChatChar((char)(i+1)); + break; + } + else if (i == consoleplayer) + { + num_nobrainers++; + if (num_nobrainers < 3) + plr->message = HUSTR_TALKTOSELF1; + else if (num_nobrainers < 6) + plr->message = HUSTR_TALKTOSELF2; + else if (num_nobrainers < 9) + plr->message = HUSTR_TALKTOSELF3; + else if (num_nobrainers < 32) + plr->message = HUSTR_TALKTOSELF4; + else + plr->message = HUSTR_TALKTOSELF5; + } + } + } + } + } + }//jff 2/26/98 no chat functions if message review is displayed + else if (!message_list) + { + c = ev->data1; + // send a macro + if (altdown) + { + c = c - '0'; + if (c > 9) + return false; + macromessage = chat_macros[c]; + + // kill last message with a '\n' + HU_queueChatChar((char)key_enter); // DEBUG!!! // phares + + // send the macro message + while (*macromessage) + HU_queueChatChar(*macromessage++); + HU_queueChatChar((char)key_enter); // phares + + // leave chat mode and notify that it was sent + chat_on = false; + strcpy(lastmessage, chat_macros[c]); + plr->message = lastmessage; + eatkey = true; + } + else + { + if (shiftdown || (c >= 'a' && c <= 'z')) + c = shiftxform[c]; + eatkey = HUlib_keyInIText(&w_chat, c); + if (eatkey) + HU_queueChatChar(c); + + if (c == key_enter) // phares + { + chat_on = false; + if (w_chat.l.len) + { + strcpy(lastmessage, w_chat.l.l); + plr->message = lastmessage; + } + } + else if (c == key_escape) // phares + chat_on = false; + } + } + return eatkey; +} diff --git a/src/hu_stuff.h b/src/hu_stuff.h new file mode 100644 index 00000000..e4cbf24b --- /dev/null +++ b/src/hu_stuff.h @@ -0,0 +1,90 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Head up display + * + *-----------------------------------------------------------------------------*/ + +#ifndef __HU_STUFF_H__ +#define __HU_STUFF_H__ + +#include "d_event.h" + +/* + * Globally visible constants. + */ +#define HU_FONTSTART '!' /* the first font characters */ +#define HU_FONTEND (0x7f) /*jff 2/16/98 '_' the last font characters */ + +/* Calculate # of glyphs in font. */ +#define HU_FONTSIZE (HU_FONTEND - HU_FONTSTART + 1) + +#define HU_BROADCAST 5 + +/*#define HU_MSGREFRESH KEYD_ENTER phares */ +#define HU_MSGX 0 +#define HU_MSGY 0 +#define HU_MSGWIDTH 64 /* in characters */ +#define HU_MSGHEIGHT 1 /* in lines */ + +#define HU_MSGTIMEOUT (4*TICRATE) + +/* + * Heads up text + */ +void HU_Init(void); +void HU_Start(void); + +boolean HU_Responder(event_t* ev); + +void HU_Ticker(void); +void HU_Drawer(void); +char HU_dequeueChatChar(void); +void HU_Erase(void); +void HU_MoveHud(void); // jff 3/9/98 avoid glitch in HUD display + +/* killough 5/2/98: moved from m_misc.c: */ + +/* jff 2/16/98 hud supported automap colors added */ +extern int hudcolor_titl; /* color range of automap level title */ +extern int hudcolor_xyco; /* color range of new coords on automap */ +/* jff 2/16/98 hud text colors, controls added */ +extern int hudcolor_mesg; /* color range of scrolling messages */ +extern int hudcolor_chat; /* color range of chat lines */ +/* jff 2/26/98 hud message list color and background enable */ +extern int hudcolor_list; /* color of list of past messages */ +extern int hud_list_bgon; /* solid window background for list of messages */ +extern int hud_msg_lines; /* number of message lines in window up to 16 */ +extern int hud_distributed; /* whether hud is all in lower left or distributed */ +/* jff 2/23/98 hud is currently displayed */ +extern int hud_displayed; /* hud is displayed */ +/* jff 2/18/98 hud/status control */ +extern int hud_active; /* hud mode 0=off, 1=small, 2=full */ +extern int hud_nosecrets; /* status does not list secrets/items/kills */ + +#endif diff --git a/src/i_main.h b/src/i_main.h new file mode 100644 index 00000000..60b46621 --- /dev/null +++ b/src/i_main.h @@ -0,0 +1,44 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * General system functions. Signal related stuff, exit function + * prototypes, and programmable Doom clock. + * + *----------------------------------------------------------------------------- + */ + +#ifndef __I_MAIN__ +#define __I_MAIN__ + +void I_Init(void); +void I_SafeExit(int rc); + +extern int (*I_GetTime)(void); + +#endif diff --git a/src/i_network.h b/src/i_network.h new file mode 100644 index 00000000..532941f2 --- /dev/null +++ b/src/i_network.h @@ -0,0 +1,74 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Low level network interface. + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef USE_SDL_NET + #include "SDL_net.h" + #define UDP_SOCKET UDPsocket + #define UDP_PACKET UDPpacket + #define AF_INET + #define UDP_CHANNEL int + extern UDP_SOCKET udp_socket; +#else + #define UDP_CHANNEL struct sockaddr +#endif + +#ifndef IPPORT_RESERVED + #define IPPORT_RESERVED 1024 +#endif + +void I_InitNetwork(void); +size_t I_GetPacket(packet_header_t* buffer, size_t buflen); +void I_SendPacket(packet_header_t* packet, size_t len); +void I_WaitForPacket(int ms); + +#ifdef USE_SDL_NET +UDP_SOCKET I_Socket(Uint16 port); +int I_ConnectToServer(const char *serv); +UDP_CHANNEL I_RegisterPlayer(IPaddress *ipaddr); +void I_UnRegisterPlayer(UDP_CHANNEL channel); +extern IPaddress sentfrom_addr; +#endif + +#ifdef AF_INET +void I_SendPacketTo(packet_header_t* packet, size_t len, UDP_CHANNEL *to); +void I_SetupSocket(int sock, int port, int family); +void I_PrintAddress(FILE* fp, UDP_CHANNEL *addr); + +extern UDP_CHANNEL sentfrom; +extern int v4socket, v6socket; +#endif + +extern size_t sentbytes, recvdbytes; diff --git a/src/i_sound.h b/src/i_sound.h new file mode 100644 index 00000000..e03ccbf2 --- /dev/null +++ b/src/i_sound.h @@ -0,0 +1,120 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System interface, sound. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __I_SOUND__ +#define __I_SOUND__ + +#include "sounds.h" +#include "doomtype.h" + +#define SNDSERV +#undef SNDINTR + +#ifndef SNDSERV +#include "l_soundgen.h" +#endif + +// Init at program start... +void I_InitSound(void); + +// ... shut down and relase at program termination. +void I_ShutdownSound(void); + +// +// SFX I/O +// + +// Initialize channels? +void I_SetChannels(void); + +// Get raw data lump index for sound descriptor. +int I_GetSfxLumpNum (sfxinfo_t *sfxinfo); + +// Starts a sound in a particular sound channel. +int I_StartSound(int id, int channel, int vol, int sep, int pitch, int priority); + +// Stops a sound channel. +void I_StopSound(int handle); + +// Called by S_*() functions +// to see if a channel is still playing. +// Returns 0 if no longer playing, 1 if playing. +boolean I_SoundIsPlaying(int handle); + +// Called by m_menu.c to let the quit sound play and quit right after it stops +boolean I_AnySoundStillPlaying(void); + +// Updates the volume, separation, +// and pitch of a sound channel. +void I_UpdateSoundParams(int handle, int vol, int sep, int pitch); + +// +// MUSIC I/O +// +void I_InitMusic(void); +void I_ShutdownMusic(void); + +void I_UpdateMusic(void); + +// Volume. +void I_SetMusicVolume(int volume); + +// PAUSE game handling. +void I_PauseSong(int handle); +void I_ResumeSong(int handle); + +// Registers a song handle to song data. +int I_RegisterSong(const void *data, size_t len); + +// cournia - tries to load a music file +int I_RegisterMusic( const char* filename, musicinfo_t *music ); + +// Called by anything that wishes to start music. +// plays a song, and when the song is done, +// starts playing it again in an endless loop. +// Horrible thing to do, considering. +void I_PlaySong(int handle, int looping); + +// Stops a song over 3 seconds. +void I_StopSong(int handle); + +// See above (register), then think backwards +void I_UnRegisterSong(int handle); + +// Allegro card support jff 1/18/98 +extern int snd_card; +extern int mus_card; +// CPhipps - put these in config file +extern int snd_samplerate; + +#endif diff --git a/src/i_system.h b/src/i_system.h new file mode 100644 index 00000000..11548c9d --- /dev/null +++ b/src/i_system.h @@ -0,0 +1,74 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System specific interface stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __I_SYSTEM__ +#define __I_SYSTEM__ + +#ifdef __GNUG__ +#pragma interface +#endif + +extern int ms_to_next_tick; +boolean I_StartDisplay(void); +void I_EndDisplay(void); +int I_GetTime_RealTime(void); /* killough */ +#ifndef PRBOOM_SERVER +fixed_t I_GetTimeFrac (void); +#endif +void I_GetTime_SaveMS(void); + +unsigned long I_GetRandomTimeSeed(void); /* cphipps */ + +void I_uSleep(unsigned long usecs); + +/* cphipps - I_GetVersionString + * Returns a version string in the given buffer + */ +const char* I_GetVersionString(char* buf, size_t sz); + +/* cphipps - I_SigString + * Returns a string describing a signal number + */ +#ifdef PRBOOM_SERVER +const char* I_SigString(char* buf, size_t sz, int signum); +#endif + +const char *I_DoomExeDir(void); // killough 2/16/98: path to executable's dir + +boolean HasTrailingSlash(const char* dn); +char* I_FindFile(const char* wfname, const char* ext); + +/* cph 2001/11/18 - Move W_Filelength to i_system.c */ +int I_Filelength(int handle); + +#endif diff --git a/src/i_video.h b/src/i_video.h new file mode 100644 index 00000000..7a712ce7 --- /dev/null +++ b/src/i_video.h @@ -0,0 +1,64 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * System specific interface stuff. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __I_VIDEO__ +#define __I_VIDEO__ + +#include "doomtype.h" +#include "v_video.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +void I_PreInitGraphics(void); /* CPhipps - do stuff immediately on start */ +void I_SetRes(void); /* set resolution */ +void I_InitGraphics (void); +void I_ShutdownGraphics(void); + +/* Takes full 8 bit values. */ +void I_SetPalette(int pal); /* CPhipps - pass down palette number */ + +void I_FinishUpdate (void); + +/* I_StartTic + * Called by D_DoomLoop, + * called before processing each tic in a frame. + * Quick syncronous operations are performed here. + * Can call D_PostEvent. + */ +void I_StartTic (void); + +extern int use_doublebuffer; /* proff 2001-7-4 - controls wether to use doublebuffering*/ + +#endif diff --git a/src/info.c b/src/info.c new file mode 100644 index 00000000..bb59896d --- /dev/null +++ b/src/info.c @@ -0,0 +1,4837 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thing frame/state LUT, + * generated by multigen utilitiy. + * This one is the original DOOM version, preserved. + * BOOM changes include commenting and addition of predefined lumps + * for providing things that aren't in the IWAD without sending a + * separate must-use wad file around with the EXE. + * + *----------------------------------------------------------------------------- + */ + +#include "doomdef.h" +#include "sounds.h" +#include "m_fixed.h" +#include "p_mobj.h" +#include "p_enemy.h" +#include "p_pspr.h" +#include "w_wad.h" + +#ifdef __GNUG__ +#pragma implementation "info.h" +#endif +#include "info.h" + + +// ******************************************************************** +// Sprite names +// ******************************************************************** +// This is the list of sprite 4-character prefixes. They are searched +// through, with a NULL entry terminating the list. In DOOM originally +// this NULL entry was missing, and coincidentally the next thing in +// memory was the dummy state_t[] entry that started with zero bytes. +// killough 1/17/98: add an explicit NULL entry. +// NUMSPRITES is an enum from info.h where all these are listed +// as SPR_xxxx + +const char *sprnames[NUMSPRITES+1] = { + "TROO","SHTG","PUNG","PISG","PISF","SHTF","SHT2","CHGG","CHGF","MISG", + "MISF","SAWG","PLSG","PLSF","BFGG","BFGF","BLUD","PUFF","BAL1","BAL2", + "PLSS","PLSE","MISL","BFS1","BFE1","BFE2","TFOG","IFOG","PLAY","POSS", + "SPOS","VILE","FIRE","FATB","FBXP","SKEL","MANF","FATT","CPOS","SARG", + "HEAD","BAL7","BOSS","BOS2","SKUL","SPID","BSPI","APLS","APBX","CYBR", + "PAIN","SSWV","KEEN","BBRN","BOSF","ARM1","ARM2","BAR1","BEXP","FCAN", + "BON1","BON2","BKEY","RKEY","YKEY","BSKU","RSKU","YSKU","STIM","MEDI", + "SOUL","PINV","PSTR","PINS","MEGA","SUIT","PMAP","PVIS","CLIP","AMMO", + "ROCK","BROK","CELL","CELP","SHEL","SBOX","BPAK","BFUG","MGUN","CSAW", + "LAUN","PLAS","SHOT","SGN2","COLU","SMT2","GOR1","POL2","POL5","POL4", + "POL3","POL1","POL6","GOR2","GOR3","GOR4","GOR5","SMIT","COL1","COL2", + "COL3","COL4","CAND","CBRA","COL6","TRE1","TRE2","ELEC","CEYE","FSKU", + "COL5","TBLU","TGRN","TRED","SMBT","SMGT","SMRT","HDB1","HDB2","HDB3", + "HDB4","HDB5","HDB6","POB1","POB2","BRS1","TLMP","TLP2", + "TNT1", // invisible sprite phares 3/9/98 + NULL +}; + +// ******************************************************************** +// State or "frame" information +// ******************************************************************** +// Each of the states, otherwise known as "frames", is outlined +// here. The data in each element of the array is the way it is +// initialized, with sprite names identified by their enumerator +// value such as SPR_SHTG. These correlate to the above sprite +// array so don't change them around unless you understand what +// you're doing. +// +// The commented name beginning with S_ at the end of each line +// is there to help figure out where the next-frame pointer is +// pointing. These are also additionally identified in info.h +// as enumerated values. From a change-and-recompile point of +// view this is fairly workable, but it adds a lot to the effort +// when trying to change things externally. See also the d_deh.c +// parts where frame rewiring is done for more details and the +// extended way a BEX file can handle this. + +state_t states[NUMSTATES] = { + {SPR_TROO,0,-1,NULL,S_NULL,0,0}, // S_NULL + {SPR_SHTG,4,0,A_Light0,S_NULL,0,0}, // S_LIGHTDONE + {SPR_PUNG,0,1,A_WeaponReady,S_PUNCH,0,0}, // S_PUNCH + {SPR_PUNG,0,1,A_Lower,S_PUNCHDOWN,0,0}, // S_PUNCHDOWN + {SPR_PUNG,0,1,A_Raise,S_PUNCHUP,0,0}, // S_PUNCHUP + {SPR_PUNG,1,4,NULL,S_PUNCH2,0,0}, // S_PUNCH1 + {SPR_PUNG,2,4,A_Punch,S_PUNCH3,0,0}, // S_PUNCH2 + {SPR_PUNG,3,5,NULL,S_PUNCH4,0,0}, // S_PUNCH3 + {SPR_PUNG,2,4,NULL,S_PUNCH5,0,0}, // S_PUNCH4 + {SPR_PUNG,1,5,A_ReFire,S_PUNCH,0,0}, // S_PUNCH5 + {SPR_PISG,0,1,A_WeaponReady,S_PISTOL,0,0},// S_PISTOL + {SPR_PISG,0,1,A_Lower,S_PISTOLDOWN,0,0}, // S_PISTOLDOWN + {SPR_PISG,0,1,A_Raise,S_PISTOLUP,0,0}, // S_PISTOLUP + {SPR_PISG,0,4,NULL,S_PISTOL2,0,0}, // S_PISTOL1 + {SPR_PISG,1,6,A_FirePistol,S_PISTOL3,0,0},// S_PISTOL2 + {SPR_PISG,2,4,NULL,S_PISTOL4,0,0}, // S_PISTOL3 + {SPR_PISG,1,5,A_ReFire,S_PISTOL,0,0}, // S_PISTOL4 + {SPR_PISF,32768,7,A_Light1,S_LIGHTDONE,0,0}, // S_PISTOLFLASH + {SPR_SHTG,0,1,A_WeaponReady,S_SGUN,0,0}, // S_SGUN + {SPR_SHTG,0,1,A_Lower,S_SGUNDOWN,0,0}, // S_SGUNDOWN + {SPR_SHTG,0,1,A_Raise,S_SGUNUP,0,0}, // S_SGUNUP + {SPR_SHTG,0,3,NULL,S_SGUN2,0,0}, // S_SGUN1 + {SPR_SHTG,0,7,A_FireShotgun,S_SGUN3,0,0}, // S_SGUN2 + {SPR_SHTG,1,5,NULL,S_SGUN4,0,0}, // S_SGUN3 + {SPR_SHTG,2,5,NULL,S_SGUN5,0,0}, // S_SGUN4 + {SPR_SHTG,3,4,NULL,S_SGUN6,0,0}, // S_SGUN5 + {SPR_SHTG,2,5,NULL,S_SGUN7,0,0}, // S_SGUN6 + {SPR_SHTG,1,5,NULL,S_SGUN8,0,0}, // S_SGUN7 + {SPR_SHTG,0,3,NULL,S_SGUN9,0,0}, // S_SGUN8 + {SPR_SHTG,0,7,A_ReFire,S_SGUN,0,0}, // S_SGUN9 + {SPR_SHTF,32768,4,A_Light1,S_SGUNFLASH2,0,0}, // S_SGUNFLASH1 + {SPR_SHTF,32769,3,A_Light2,S_LIGHTDONE,0,0}, // S_SGUNFLASH2 + {SPR_SHT2,0,1,A_WeaponReady,S_DSGUN,0,0}, // S_DSGUN + {SPR_SHT2,0,1,A_Lower,S_DSGUNDOWN,0,0}, // S_DSGUNDOWN + {SPR_SHT2,0,1,A_Raise,S_DSGUNUP,0,0}, // S_DSGUNUP + {SPR_SHT2,0,3,NULL,S_DSGUN2,0,0}, // S_DSGUN1 + {SPR_SHT2,0,7,A_FireShotgun2,S_DSGUN3,0,0}, // S_DSGUN2 + {SPR_SHT2,1,7,NULL,S_DSGUN4,0,0}, // S_DSGUN3 + {SPR_SHT2,2,7,A_CheckReload,S_DSGUN5,0,0}, // S_DSGUN4 + {SPR_SHT2,3,7,A_OpenShotgun2,S_DSGUN6,0,0}, // S_DSGUN5 + {SPR_SHT2,4,7,NULL,S_DSGUN7,0,0}, // S_DSGUN6 + {SPR_SHT2,5,7,A_LoadShotgun2,S_DSGUN8,0,0}, // S_DSGUN7 + {SPR_SHT2,6,6,NULL,S_DSGUN9,0,0}, // S_DSGUN8 + {SPR_SHT2,7,6,A_CloseShotgun2,S_DSGUN10,0,0}, // S_DSGUN9 + {SPR_SHT2,0,5,A_ReFire,S_DSGUN,0,0}, // S_DSGUN10 + {SPR_SHT2,1,7,NULL,S_DSNR2,0,0}, // S_DSNR1 + {SPR_SHT2,0,3,NULL,S_DSGUNDOWN,0,0}, // S_DSNR2 + {SPR_SHT2,32776,5,A_Light1,S_DSGUNFLASH2,0,0}, // S_DSGUNFLASH1 + {SPR_SHT2,32777,4,A_Light2,S_LIGHTDONE,0,0}, // S_DSGUNFLASH2 + {SPR_CHGG,0,1,A_WeaponReady,S_CHAIN,0,0}, // S_CHAIN + {SPR_CHGG,0,1,A_Lower,S_CHAINDOWN,0,0}, // S_CHAINDOWN + {SPR_CHGG,0,1,A_Raise,S_CHAINUP,0,0}, // S_CHAINUP + {SPR_CHGG,0,4,A_FireCGun,S_CHAIN2,0,0}, // S_CHAIN1 + {SPR_CHGG,1,4,A_FireCGun,S_CHAIN3,0,0}, // S_CHAIN2 + {SPR_CHGG,1,0,A_ReFire,S_CHAIN,0,0}, // S_CHAIN3 + {SPR_CHGF,32768,5,A_Light1,S_LIGHTDONE,0,0}, // S_CHAINFLASH1 + {SPR_CHGF,32769,5,A_Light2,S_LIGHTDONE,0,0}, // S_CHAINFLASH2 + {SPR_MISG,0,1,A_WeaponReady,S_MISSILE,0,0}, // S_MISSILE + {SPR_MISG,0,1,A_Lower,S_MISSILEDOWN,0,0}, // S_MISSILEDOWN + {SPR_MISG,0,1,A_Raise,S_MISSILEUP,0,0}, // S_MISSILEUP + {SPR_MISG,1,8,A_GunFlash,S_MISSILE2,0,0}, // S_MISSILE1 + {SPR_MISG,1,12,A_FireMissile,S_MISSILE3,0,0}, // S_MISSILE2 + {SPR_MISG,1,0,A_ReFire,S_MISSILE,0,0}, // S_MISSILE3 + {SPR_MISF,32768,3,A_Light1,S_MISSILEFLASH2,0,0}, // S_MISSILEFLASH1 + {SPR_MISF,32769,4,NULL,S_MISSILEFLASH3,0,0}, // S_MISSILEFLASH2 + {SPR_MISF,32770,4,A_Light2,S_MISSILEFLASH4,0,0}, // S_MISSILEFLASH3 + {SPR_MISF,32771,4,A_Light2,S_LIGHTDONE,0,0}, // S_MISSILEFLASH4 + {SPR_SAWG,2,4,A_WeaponReady,S_SAWB,0,0}, // S_SAW + {SPR_SAWG,3,4,A_WeaponReady,S_SAW,0,0}, // S_SAWB + {SPR_SAWG,2,1,A_Lower,S_SAWDOWN,0,0}, // S_SAWDOWN + {SPR_SAWG,2,1,A_Raise,S_SAWUP,0,0}, // S_SAWUP + {SPR_SAWG,0,4,A_Saw,S_SAW2,0,0}, // S_SAW1 + {SPR_SAWG,1,4,A_Saw,S_SAW3,0,0}, // S_SAW2 + {SPR_SAWG,1,0,A_ReFire,S_SAW,0,0}, // S_SAW3 + {SPR_PLSG,0,1,A_WeaponReady,S_PLASMA,0,0}, // S_PLASMA + {SPR_PLSG,0,1,A_Lower,S_PLASMADOWN,0,0}, // S_PLASMADOWN + {SPR_PLSG,0,1,A_Raise,S_PLASMAUP,0,0}, // S_PLASMAUP + {SPR_PLSG,0,3,A_FirePlasma,S_PLASMA2,0,0}, // S_PLASMA1 + {SPR_PLSG,1,20,A_ReFire,S_PLASMA,0,0}, // S_PLASMA2 + {SPR_PLSF,32768,4,A_Light1,S_LIGHTDONE,0,0}, // S_PLASMAFLASH1 + {SPR_PLSF,32769,4,A_Light1,S_LIGHTDONE,0,0}, // S_PLASMAFLASH2 + {SPR_BFGG,0,1,A_WeaponReady,S_BFG,0,0}, // S_BFG + {SPR_BFGG,0,1,A_Lower,S_BFGDOWN,0,0}, // S_BFGDOWN + {SPR_BFGG,0,1,A_Raise,S_BFGUP,0,0}, // S_BFGUP + {SPR_BFGG,0,20,A_BFGsound,S_BFG2,0,0}, // S_BFG1 + {SPR_BFGG,1,10,A_GunFlash,S_BFG3,0,0}, // S_BFG2 + {SPR_BFGG,1,10,A_FireBFG,S_BFG4,0,0}, // S_BFG3 + {SPR_BFGG,1,20,A_ReFire,S_BFG,0,0}, // S_BFG4 + {SPR_BFGF,32768,11,A_Light1,S_BFGFLASH2,0,0}, // S_BFGFLASH1 + {SPR_BFGF,32769,6,A_Light2,S_LIGHTDONE,0,0}, // S_BFGFLASH2 + {SPR_BLUD,2,8,NULL,S_BLOOD2,0,0}, // S_BLOOD1 + {SPR_BLUD,1,8,NULL,S_BLOOD3,0,0}, // S_BLOOD2 + {SPR_BLUD,0,8,NULL,S_NULL,0,0}, // S_BLOOD3 + {SPR_PUFF,32768,4,NULL,S_PUFF2,0,0}, // S_PUFF1 + {SPR_PUFF,1,4,NULL,S_PUFF3,0,0}, // S_PUFF2 + {SPR_PUFF,2,4,NULL,S_PUFF4,0,0}, // S_PUFF3 + {SPR_PUFF,3,4,NULL,S_NULL,0,0}, // S_PUFF4 + {SPR_BAL1,32768,4,NULL,S_TBALL2,0,0}, // S_TBALL1 + {SPR_BAL1,32769,4,NULL,S_TBALL1,0,0}, // S_TBALL2 + {SPR_BAL1,32770,6,NULL,S_TBALLX2,0,0}, // S_TBALLX1 + {SPR_BAL1,32771,6,NULL,S_TBALLX3,0,0}, // S_TBALLX2 + {SPR_BAL1,32772,6,NULL,S_NULL,0,0}, // S_TBALLX3 + {SPR_BAL2,32768,4,NULL,S_RBALL2,0,0}, // S_RBALL1 + {SPR_BAL2,32769,4,NULL,S_RBALL1,0,0}, // S_RBALL2 + {SPR_BAL2,32770,6,NULL,S_RBALLX2,0,0}, // S_RBALLX1 + {SPR_BAL2,32771,6,NULL,S_RBALLX3,0,0}, // S_RBALLX2 + {SPR_BAL2,32772,6,NULL,S_NULL,0,0}, // S_RBALLX3 + {SPR_PLSS,32768,6,NULL,S_PLASBALL2,0,0}, // S_PLASBALL + {SPR_PLSS,32769,6,NULL,S_PLASBALL,0,0}, // S_PLASBALL2 + {SPR_PLSE,32768,4,NULL,S_PLASEXP2,0,0}, // S_PLASEXP + {SPR_PLSE,32769,4,NULL,S_PLASEXP3,0,0}, // S_PLASEXP2 + {SPR_PLSE,32770,4,NULL,S_PLASEXP4,0,0}, // S_PLASEXP3 + {SPR_PLSE,32771,4,NULL,S_PLASEXP5,0,0}, // S_PLASEXP4 + {SPR_PLSE,32772,4,NULL,S_NULL,0,0}, // S_PLASEXP5 + {SPR_MISL,32768,1,NULL,S_ROCKET,0,0}, // S_ROCKET + {SPR_BFS1,32768,4,NULL,S_BFGSHOT2,0,0}, // S_BFGSHOT + {SPR_BFS1,32769,4,NULL,S_BFGSHOT,0,0}, // S_BFGSHOT2 + {SPR_BFE1,32768,8,NULL,S_BFGLAND2,0,0}, // S_BFGLAND + {SPR_BFE1,32769,8,NULL,S_BFGLAND3,0,0}, // S_BFGLAND2 + {SPR_BFE1,32770,8,A_BFGSpray,S_BFGLAND4,0,0}, // S_BFGLAND3 + {SPR_BFE1,32771,8,NULL,S_BFGLAND5,0,0}, // S_BFGLAND4 + {SPR_BFE1,32772,8,NULL,S_BFGLAND6,0,0}, // S_BFGLAND5 + {SPR_BFE1,32773,8,NULL,S_NULL,0,0}, // S_BFGLAND6 + {SPR_BFE2,32768,8,NULL,S_BFGEXP2,0,0}, // S_BFGEXP + {SPR_BFE2,32769,8,NULL,S_BFGEXP3,0,0}, // S_BFGEXP2 + {SPR_BFE2,32770,8,NULL,S_BFGEXP4,0,0}, // S_BFGEXP3 + {SPR_BFE2,32771,8,NULL,S_NULL,0,0}, // S_BFGEXP4 + {SPR_MISL,32769,8,A_Explode,S_EXPLODE2,0,0}, // S_EXPLODE1 + {SPR_MISL,32770,6,NULL,S_EXPLODE3,0,0}, // S_EXPLODE2 + {SPR_MISL,32771,4,NULL,S_NULL,0,0}, // S_EXPLODE3 + {SPR_TFOG,32768,6,NULL,S_TFOG01,0,0}, // S_TFOG + {SPR_TFOG,32769,6,NULL,S_TFOG02,0,0}, // S_TFOG01 + {SPR_TFOG,32768,6,NULL,S_TFOG2,0,0}, // S_TFOG02 + {SPR_TFOG,32769,6,NULL,S_TFOG3,0,0}, // S_TFOG2 + {SPR_TFOG,32770,6,NULL,S_TFOG4,0,0}, // S_TFOG3 + {SPR_TFOG,32771,6,NULL,S_TFOG5,0,0}, // S_TFOG4 + {SPR_TFOG,32772,6,NULL,S_TFOG6,0,0}, // S_TFOG5 + {SPR_TFOG,32773,6,NULL,S_TFOG7,0,0}, // S_TFOG6 + {SPR_TFOG,32774,6,NULL,S_TFOG8,0,0}, // S_TFOG7 + {SPR_TFOG,32775,6,NULL,S_TFOG9,0,0}, // S_TFOG8 + {SPR_TFOG,32776,6,NULL,S_TFOG10,0,0}, // S_TFOG9 + {SPR_TFOG,32777,6,NULL,S_NULL,0,0}, // S_TFOG10 + {SPR_IFOG,32768,6,NULL,S_IFOG01,0,0}, // S_IFOG + {SPR_IFOG,32769,6,NULL,S_IFOG02,0,0}, // S_IFOG01 + {SPR_IFOG,32768,6,NULL,S_IFOG2,0,0}, // S_IFOG02 + {SPR_IFOG,32769,6,NULL,S_IFOG3,0,0}, // S_IFOG2 + {SPR_IFOG,32770,6,NULL,S_IFOG4,0,0}, // S_IFOG3 + {SPR_IFOG,32771,6,NULL,S_IFOG5,0,0}, // S_IFOG4 + {SPR_IFOG,32772,6,NULL,S_NULL,0,0}, // S_IFOG5 + {SPR_PLAY,0,-1,NULL,S_NULL,0,0}, // S_PLAY + {SPR_PLAY,0,4,NULL,S_PLAY_RUN2,0,0}, // S_PLAY_RUN1 + {SPR_PLAY,1,4,NULL,S_PLAY_RUN3,0,0}, // S_PLAY_RUN2 + {SPR_PLAY,2,4,NULL,S_PLAY_RUN4,0,0}, // S_PLAY_RUN3 + {SPR_PLAY,3,4,NULL,S_PLAY_RUN1,0,0}, // S_PLAY_RUN4 + {SPR_PLAY,4,12,NULL,S_PLAY,0,0}, // S_PLAY_ATK1 + {SPR_PLAY,32773,6,NULL,S_PLAY_ATK1,0,0}, // S_PLAY_ATK2 + {SPR_PLAY,6,4,NULL,S_PLAY_PAIN2,0,0}, // S_PLAY_PAIN + {SPR_PLAY,6,4,A_Pain,S_PLAY,0,0}, // S_PLAY_PAIN2 + {SPR_PLAY,7,10,NULL,S_PLAY_DIE2,0,0}, // S_PLAY_DIE1 + {SPR_PLAY,8,10,A_PlayerScream,S_PLAY_DIE3,0,0}, // S_PLAY_DIE2 + {SPR_PLAY,9,10,A_Fall,S_PLAY_DIE4,0,0}, // S_PLAY_DIE3 + {SPR_PLAY,10,10,NULL,S_PLAY_DIE5,0,0}, // S_PLAY_DIE4 + {SPR_PLAY,11,10,NULL,S_PLAY_DIE6,0,0}, // S_PLAY_DIE5 + {SPR_PLAY,12,10,NULL,S_PLAY_DIE7,0,0}, // S_PLAY_DIE6 + {SPR_PLAY,13,-1,NULL,S_NULL,0,0}, // S_PLAY_DIE7 + {SPR_PLAY,14,5,NULL,S_PLAY_XDIE2,0,0}, // S_PLAY_XDIE1 + {SPR_PLAY,15,5,A_XScream,S_PLAY_XDIE3,0,0}, // S_PLAY_XDIE2 + {SPR_PLAY,16,5,A_Fall,S_PLAY_XDIE4,0,0}, // S_PLAY_XDIE3 + {SPR_PLAY,17,5,NULL,S_PLAY_XDIE5,0,0}, // S_PLAY_XDIE4 + {SPR_PLAY,18,5,NULL,S_PLAY_XDIE6,0,0}, // S_PLAY_XDIE5 + {SPR_PLAY,19,5,NULL,S_PLAY_XDIE7,0,0}, // S_PLAY_XDIE6 + {SPR_PLAY,20,5,NULL,S_PLAY_XDIE8,0,0}, // S_PLAY_XDIE7 + {SPR_PLAY,21,5,NULL,S_PLAY_XDIE9,0,0}, // S_PLAY_XDIE8 + {SPR_PLAY,22,-1,NULL,S_NULL,0,0}, // S_PLAY_XDIE9 + {SPR_POSS,0,10,A_Look,S_POSS_STND2,0,0}, // S_POSS_STND + {SPR_POSS,1,10,A_Look,S_POSS_STND,0,0}, // S_POSS_STND2 + {SPR_POSS,0,4,A_Chase,S_POSS_RUN2,0,0}, // S_POSS_RUN1 + {SPR_POSS,0,4,A_Chase,S_POSS_RUN3,0,0}, // S_POSS_RUN2 + {SPR_POSS,1,4,A_Chase,S_POSS_RUN4,0,0}, // S_POSS_RUN3 + {SPR_POSS,1,4,A_Chase,S_POSS_RUN5,0,0}, // S_POSS_RUN4 + {SPR_POSS,2,4,A_Chase,S_POSS_RUN6,0,0}, // S_POSS_RUN5 + {SPR_POSS,2,4,A_Chase,S_POSS_RUN7,0,0}, // S_POSS_RUN6 + {SPR_POSS,3,4,A_Chase,S_POSS_RUN8,0,0}, // S_POSS_RUN7 + {SPR_POSS,3,4,A_Chase,S_POSS_RUN1,0,0}, // S_POSS_RUN8 + {SPR_POSS,4,10,A_FaceTarget,S_POSS_ATK2,0,0}, // S_POSS_ATK1 + {SPR_POSS,5,8,A_PosAttack,S_POSS_ATK3,0,0}, // S_POSS_ATK2 + {SPR_POSS,4,8,NULL,S_POSS_RUN1,0,0}, // S_POSS_ATK3 + {SPR_POSS,6,3,NULL,S_POSS_PAIN2,0,0}, // S_POSS_PAIN + {SPR_POSS,6,3,A_Pain,S_POSS_RUN1,0,0}, // S_POSS_PAIN2 + {SPR_POSS,7,5,NULL,S_POSS_DIE2,0,0}, // S_POSS_DIE1 + {SPR_POSS,8,5,A_Scream,S_POSS_DIE3,0,0}, // S_POSS_DIE2 + {SPR_POSS,9,5,A_Fall,S_POSS_DIE4,0,0}, // S_POSS_DIE3 + {SPR_POSS,10,5,NULL,S_POSS_DIE5,0,0}, // S_POSS_DIE4 + {SPR_POSS,11,-1,NULL,S_NULL,0,0}, // S_POSS_DIE5 + {SPR_POSS,12,5,NULL,S_POSS_XDIE2,0,0}, // S_POSS_XDIE1 + {SPR_POSS,13,5,A_XScream,S_POSS_XDIE3,0,0}, // S_POSS_XDIE2 + {SPR_POSS,14,5,A_Fall,S_POSS_XDIE4,0,0}, // S_POSS_XDIE3 + {SPR_POSS,15,5,NULL,S_POSS_XDIE5,0,0}, // S_POSS_XDIE4 + {SPR_POSS,16,5,NULL,S_POSS_XDIE6,0,0}, // S_POSS_XDIE5 + {SPR_POSS,17,5,NULL,S_POSS_XDIE7,0,0}, // S_POSS_XDIE6 + {SPR_POSS,18,5,NULL,S_POSS_XDIE8,0,0}, // S_POSS_XDIE7 + {SPR_POSS,19,5,NULL,S_POSS_XDIE9,0,0}, // S_POSS_XDIE8 + {SPR_POSS,20,-1,NULL,S_NULL,0,0}, // S_POSS_XDIE9 + {SPR_POSS,10,5,NULL,S_POSS_RAISE2,0,0}, // S_POSS_RAISE1 + {SPR_POSS,9,5,NULL,S_POSS_RAISE3,0,0}, // S_POSS_RAISE2 + {SPR_POSS,8,5,NULL,S_POSS_RAISE4,0,0}, // S_POSS_RAISE3 + {SPR_POSS,7,5,NULL,S_POSS_RUN1,0,0}, // S_POSS_RAISE4 + {SPR_SPOS,0,10,A_Look,S_SPOS_STND2,0,0}, // S_SPOS_STND + {SPR_SPOS,1,10,A_Look,S_SPOS_STND,0,0}, // S_SPOS_STND2 + {SPR_SPOS,0,3,A_Chase,S_SPOS_RUN2,0,0}, // S_SPOS_RUN1 + {SPR_SPOS,0,3,A_Chase,S_SPOS_RUN3,0,0}, // S_SPOS_RUN2 + {SPR_SPOS,1,3,A_Chase,S_SPOS_RUN4,0,0}, // S_SPOS_RUN3 + {SPR_SPOS,1,3,A_Chase,S_SPOS_RUN5,0,0}, // S_SPOS_RUN4 + {SPR_SPOS,2,3,A_Chase,S_SPOS_RUN6,0,0}, // S_SPOS_RUN5 + {SPR_SPOS,2,3,A_Chase,S_SPOS_RUN7,0,0}, // S_SPOS_RUN6 + {SPR_SPOS,3,3,A_Chase,S_SPOS_RUN8,0,0}, // S_SPOS_RUN7 + {SPR_SPOS,3,3,A_Chase,S_SPOS_RUN1,0,0}, // S_SPOS_RUN8 + {SPR_SPOS,4,10,A_FaceTarget,S_SPOS_ATK2,0,0}, // S_SPOS_ATK1 + {SPR_SPOS,32773,10,A_SPosAttack,S_SPOS_ATK3,0,0}, // S_SPOS_ATK2 + {SPR_SPOS,4,10,NULL,S_SPOS_RUN1,0,0}, // S_SPOS_ATK3 + {SPR_SPOS,6,3,NULL,S_SPOS_PAIN2,0,0}, // S_SPOS_PAIN + {SPR_SPOS,6,3,A_Pain,S_SPOS_RUN1,0,0}, // S_SPOS_PAIN2 + {SPR_SPOS,7,5,NULL,S_SPOS_DIE2,0,0}, // S_SPOS_DIE1 + {SPR_SPOS,8,5,A_Scream,S_SPOS_DIE3,0,0}, // S_SPOS_DIE2 + {SPR_SPOS,9,5,A_Fall,S_SPOS_DIE4,0,0}, // S_SPOS_DIE3 + {SPR_SPOS,10,5,NULL,S_SPOS_DIE5,0,0}, // S_SPOS_DIE4 + {SPR_SPOS,11,-1,NULL,S_NULL,0,0}, // S_SPOS_DIE5 + {SPR_SPOS,12,5,NULL,S_SPOS_XDIE2,0,0}, // S_SPOS_XDIE1 + {SPR_SPOS,13,5,A_XScream,S_SPOS_XDIE3,0,0}, // S_SPOS_XDIE2 + {SPR_SPOS,14,5,A_Fall,S_SPOS_XDIE4,0,0}, // S_SPOS_XDIE3 + {SPR_SPOS,15,5,NULL,S_SPOS_XDIE5,0,0}, // S_SPOS_XDIE4 + {SPR_SPOS,16,5,NULL,S_SPOS_XDIE6,0,0}, // S_SPOS_XDIE5 + {SPR_SPOS,17,5,NULL,S_SPOS_XDIE7,0,0}, // S_SPOS_XDIE6 + {SPR_SPOS,18,5,NULL,S_SPOS_XDIE8,0,0}, // S_SPOS_XDIE7 + {SPR_SPOS,19,5,NULL,S_SPOS_XDIE9,0,0}, // S_SPOS_XDIE8 + {SPR_SPOS,20,-1,NULL,S_NULL,0,0}, // S_SPOS_XDIE9 + {SPR_SPOS,11,5,NULL,S_SPOS_RAISE2,0,0}, // S_SPOS_RAISE1 + {SPR_SPOS,10,5,NULL,S_SPOS_RAISE3,0,0}, // S_SPOS_RAISE2 + {SPR_SPOS,9,5,NULL,S_SPOS_RAISE4,0,0}, // S_SPOS_RAISE3 + {SPR_SPOS,8,5,NULL,S_SPOS_RAISE5,0,0}, // S_SPOS_RAISE4 + {SPR_SPOS,7,5,NULL,S_SPOS_RUN1,0,0}, // S_SPOS_RAISE5 + {SPR_VILE,0,10,A_Look,S_VILE_STND2,0,0}, // S_VILE_STND + {SPR_VILE,1,10,A_Look,S_VILE_STND,0,0}, // S_VILE_STND2 + {SPR_VILE,0,2,A_VileChase,S_VILE_RUN2,0,0}, // S_VILE_RUN1 + {SPR_VILE,0,2,A_VileChase,S_VILE_RUN3,0,0}, // S_VILE_RUN2 + {SPR_VILE,1,2,A_VileChase,S_VILE_RUN4,0,0}, // S_VILE_RUN3 + {SPR_VILE,1,2,A_VileChase,S_VILE_RUN5,0,0}, // S_VILE_RUN4 + {SPR_VILE,2,2,A_VileChase,S_VILE_RUN6,0,0}, // S_VILE_RUN5 + {SPR_VILE,2,2,A_VileChase,S_VILE_RUN7,0,0}, // S_VILE_RUN6 + {SPR_VILE,3,2,A_VileChase,S_VILE_RUN8,0,0}, // S_VILE_RUN7 + {SPR_VILE,3,2,A_VileChase,S_VILE_RUN9,0,0}, // S_VILE_RUN8 + {SPR_VILE,4,2,A_VileChase,S_VILE_RUN10,0,0}, // S_VILE_RUN9 + {SPR_VILE,4,2,A_VileChase,S_VILE_RUN11,0,0}, // S_VILE_RUN10 + {SPR_VILE,5,2,A_VileChase,S_VILE_RUN12,0,0}, // S_VILE_RUN11 + {SPR_VILE,5,2,A_VileChase,S_VILE_RUN1,0,0}, // S_VILE_RUN12 + {SPR_VILE,32774,0,A_VileStart,S_VILE_ATK2,0,0}, // S_VILE_ATK1 + {SPR_VILE,32774,10,A_FaceTarget,S_VILE_ATK3,0,0}, // S_VILE_ATK2 + {SPR_VILE,32775,8,A_VileTarget,S_VILE_ATK4,0,0}, // S_VILE_ATK3 + {SPR_VILE,32776,8,A_FaceTarget,S_VILE_ATK5,0,0}, // S_VILE_ATK4 + {SPR_VILE,32777,8,A_FaceTarget,S_VILE_ATK6,0,0}, // S_VILE_ATK5 + {SPR_VILE,32778,8,A_FaceTarget,S_VILE_ATK7,0,0}, // S_VILE_ATK6 + {SPR_VILE,32779,8,A_FaceTarget,S_VILE_ATK8,0,0}, // S_VILE_ATK7 + {SPR_VILE,32780,8,A_FaceTarget,S_VILE_ATK9,0,0}, // S_VILE_ATK8 + {SPR_VILE,32781,8,A_FaceTarget,S_VILE_ATK10,0,0}, // S_VILE_ATK9 + {SPR_VILE,32782,8,A_VileAttack,S_VILE_ATK11,0,0}, // S_VILE_ATK10 + {SPR_VILE,32783,20,NULL,S_VILE_RUN1,0,0}, // S_VILE_ATK11 + {SPR_VILE,32794,10,NULL,S_VILE_HEAL2,0,0}, // S_VILE_HEAL1 + {SPR_VILE,32795,10,NULL,S_VILE_HEAL3,0,0}, // S_VILE_HEAL2 + {SPR_VILE,32796,10,NULL,S_VILE_RUN1,0,0}, // S_VILE_HEAL3 + {SPR_VILE,16,5,NULL,S_VILE_PAIN2,0,0}, // S_VILE_PAIN + {SPR_VILE,16,5,A_Pain,S_VILE_RUN1,0,0}, // S_VILE_PAIN2 + {SPR_VILE,16,7,NULL,S_VILE_DIE2,0,0}, // S_VILE_DIE1 + {SPR_VILE,17,7,A_Scream,S_VILE_DIE3,0,0}, // S_VILE_DIE2 + {SPR_VILE,18,7,A_Fall,S_VILE_DIE4,0,0}, // S_VILE_DIE3 + {SPR_VILE,19,7,NULL,S_VILE_DIE5,0,0}, // S_VILE_DIE4 + {SPR_VILE,20,7,NULL,S_VILE_DIE6,0,0}, // S_VILE_DIE5 + {SPR_VILE,21,7,NULL,S_VILE_DIE7,0,0}, // S_VILE_DIE6 + {SPR_VILE,22,7,NULL,S_VILE_DIE8,0,0}, // S_VILE_DIE7 + {SPR_VILE,23,5,NULL,S_VILE_DIE9,0,0}, // S_VILE_DIE8 + {SPR_VILE,24,5,NULL,S_VILE_DIE10,0,0}, // S_VILE_DIE9 + {SPR_VILE,25,-1,NULL,S_NULL,0,0}, // S_VILE_DIE10 + {SPR_FIRE,32768,2,A_StartFire,S_FIRE2,0,0}, // S_FIRE1 + {SPR_FIRE,32769,2,A_Fire,S_FIRE3,0,0}, // S_FIRE2 + {SPR_FIRE,32768,2,A_Fire,S_FIRE4,0,0}, // S_FIRE3 + {SPR_FIRE,32769,2,A_Fire,S_FIRE5,0,0}, // S_FIRE4 + {SPR_FIRE,32770,2,A_FireCrackle,S_FIRE6,0,0}, // S_FIRE5 + {SPR_FIRE,32769,2,A_Fire,S_FIRE7,0,0}, // S_FIRE6 + {SPR_FIRE,32770,2,A_Fire,S_FIRE8,0,0}, // S_FIRE7 + {SPR_FIRE,32769,2,A_Fire,S_FIRE9,0,0}, // S_FIRE8 + {SPR_FIRE,32770,2,A_Fire,S_FIRE10,0,0}, // S_FIRE9 + {SPR_FIRE,32771,2,A_Fire,S_FIRE11,0,0}, // S_FIRE10 + {SPR_FIRE,32770,2,A_Fire,S_FIRE12,0,0}, // S_FIRE11 + {SPR_FIRE,32771,2,A_Fire,S_FIRE13,0,0}, // S_FIRE12 + {SPR_FIRE,32770,2,A_Fire,S_FIRE14,0,0}, // S_FIRE13 + {SPR_FIRE,32771,2,A_Fire,S_FIRE15,0,0}, // S_FIRE14 + {SPR_FIRE,32772,2,A_Fire,S_FIRE16,0,0}, // S_FIRE15 + {SPR_FIRE,32771,2,A_Fire,S_FIRE17,0,0}, // S_FIRE16 + {SPR_FIRE,32772,2,A_Fire,S_FIRE18,0,0}, // S_FIRE17 + {SPR_FIRE,32771,2,A_Fire,S_FIRE19,0,0}, // S_FIRE18 + {SPR_FIRE,32772,2,A_FireCrackle,S_FIRE20,0,0}, // S_FIRE19 + {SPR_FIRE,32773,2,A_Fire,S_FIRE21,0,0}, // S_FIRE20 + {SPR_FIRE,32772,2,A_Fire,S_FIRE22,0,0}, // S_FIRE21 + {SPR_FIRE,32773,2,A_Fire,S_FIRE23,0,0}, // S_FIRE22 + {SPR_FIRE,32772,2,A_Fire,S_FIRE24,0,0}, // S_FIRE23 + {SPR_FIRE,32773,2,A_Fire,S_FIRE25,0,0}, // S_FIRE24 + {SPR_FIRE,32774,2,A_Fire,S_FIRE26,0,0}, // S_FIRE25 + {SPR_FIRE,32775,2,A_Fire,S_FIRE27,0,0}, // S_FIRE26 + {SPR_FIRE,32774,2,A_Fire,S_FIRE28,0,0}, // S_FIRE27 + {SPR_FIRE,32775,2,A_Fire,S_FIRE29,0,0}, // S_FIRE28 + {SPR_FIRE,32774,2,A_Fire,S_FIRE30,0,0}, // S_FIRE29 + {SPR_FIRE,32775,2,A_Fire,S_NULL,0,0}, // S_FIRE30 + {SPR_PUFF,1,4,NULL,S_SMOKE2,0,0}, // S_SMOKE1 + {SPR_PUFF,2,4,NULL,S_SMOKE3,0,0}, // S_SMOKE2 + {SPR_PUFF,1,4,NULL,S_SMOKE4,0,0}, // S_SMOKE3 + {SPR_PUFF,2,4,NULL,S_SMOKE5,0,0}, // S_SMOKE4 + {SPR_PUFF,3,4,NULL,S_NULL,0,0}, // S_SMOKE5 + {SPR_FATB,32768,2,A_Tracer,S_TRACER2,0,0}, // S_TRACER + {SPR_FATB,32769,2,A_Tracer,S_TRACER,0,0}, // S_TRACER2 + {SPR_FBXP,32768,8,NULL,S_TRACEEXP2,0,0}, // S_TRACEEXP1 + {SPR_FBXP,32769,6,NULL,S_TRACEEXP3,0,0}, // S_TRACEEXP2 + {SPR_FBXP,32770,4,NULL,S_NULL,0,0}, // S_TRACEEXP3 + {SPR_SKEL,0,10,A_Look,S_SKEL_STND2,0,0}, // S_SKEL_STND + {SPR_SKEL,1,10,A_Look,S_SKEL_STND,0,0}, // S_SKEL_STND2 + {SPR_SKEL,0,2,A_Chase,S_SKEL_RUN2,0,0}, // S_SKEL_RUN1 + {SPR_SKEL,0,2,A_Chase,S_SKEL_RUN3,0,0}, // S_SKEL_RUN2 + {SPR_SKEL,1,2,A_Chase,S_SKEL_RUN4,0,0}, // S_SKEL_RUN3 + {SPR_SKEL,1,2,A_Chase,S_SKEL_RUN5,0,0}, // S_SKEL_RUN4 + {SPR_SKEL,2,2,A_Chase,S_SKEL_RUN6,0,0}, // S_SKEL_RUN5 + {SPR_SKEL,2,2,A_Chase,S_SKEL_RUN7,0,0}, // S_SKEL_RUN6 + {SPR_SKEL,3,2,A_Chase,S_SKEL_RUN8,0,0}, // S_SKEL_RUN7 + {SPR_SKEL,3,2,A_Chase,S_SKEL_RUN9,0,0}, // S_SKEL_RUN8 + {SPR_SKEL,4,2,A_Chase,S_SKEL_RUN10,0,0}, // S_SKEL_RUN9 + {SPR_SKEL,4,2,A_Chase,S_SKEL_RUN11,0,0}, // S_SKEL_RUN10 + {SPR_SKEL,5,2,A_Chase,S_SKEL_RUN12,0,0}, // S_SKEL_RUN11 + {SPR_SKEL,5,2,A_Chase,S_SKEL_RUN1,0,0}, // S_SKEL_RUN12 + {SPR_SKEL,6,0,A_FaceTarget,S_SKEL_FIST2,0,0}, // S_SKEL_FIST1 + {SPR_SKEL,6,6,A_SkelWhoosh,S_SKEL_FIST3,0,0}, // S_SKEL_FIST2 + {SPR_SKEL,7,6,A_FaceTarget,S_SKEL_FIST4,0,0}, // S_SKEL_FIST3 + {SPR_SKEL,8,6,A_SkelFist,S_SKEL_RUN1,0,0}, // S_SKEL_FIST4 + {SPR_SKEL,32777,0,A_FaceTarget,S_SKEL_MISS2,0,0}, // S_SKEL_MISS1 + {SPR_SKEL,32777,10,A_FaceTarget,S_SKEL_MISS3,0,0}, // S_SKEL_MISS2 + {SPR_SKEL,10,10,A_SkelMissile,S_SKEL_MISS4,0,0}, // S_SKEL_MISS3 + {SPR_SKEL,10,10,A_FaceTarget,S_SKEL_RUN1,0,0}, // S_SKEL_MISS4 + {SPR_SKEL,11,5,NULL,S_SKEL_PAIN2,0,0}, // S_SKEL_PAIN + {SPR_SKEL,11,5,A_Pain,S_SKEL_RUN1,0,0}, // S_SKEL_PAIN2 + {SPR_SKEL,11,7,NULL,S_SKEL_DIE2,0,0}, // S_SKEL_DIE1 + {SPR_SKEL,12,7,NULL,S_SKEL_DIE3,0,0}, // S_SKEL_DIE2 + {SPR_SKEL,13,7,A_Scream,S_SKEL_DIE4,0,0}, // S_SKEL_DIE3 + {SPR_SKEL,14,7,A_Fall,S_SKEL_DIE5,0,0}, // S_SKEL_DIE4 + {SPR_SKEL,15,7,NULL,S_SKEL_DIE6,0,0}, // S_SKEL_DIE5 + {SPR_SKEL,16,-1,NULL,S_NULL,0,0}, // S_SKEL_DIE6 + {SPR_SKEL,16,5,NULL,S_SKEL_RAISE2,0,0}, // S_SKEL_RAISE1 + {SPR_SKEL,15,5,NULL,S_SKEL_RAISE3,0,0}, // S_SKEL_RAISE2 + {SPR_SKEL,14,5,NULL,S_SKEL_RAISE4,0,0}, // S_SKEL_RAISE3 + {SPR_SKEL,13,5,NULL,S_SKEL_RAISE5,0,0}, // S_SKEL_RAISE4 + {SPR_SKEL,12,5,NULL,S_SKEL_RAISE6,0,0}, // S_SKEL_RAISE5 + {SPR_SKEL,11,5,NULL,S_SKEL_RUN1,0,0}, // S_SKEL_RAISE6 + {SPR_MANF,32768,4,NULL,S_FATSHOT2,0,0}, // S_FATSHOT1 + {SPR_MANF,32769,4,NULL,S_FATSHOT1,0,0}, // S_FATSHOT2 + {SPR_MISL,32769,8,NULL,S_FATSHOTX2,0,0}, // S_FATSHOTX1 + {SPR_MISL,32770,6,NULL,S_FATSHOTX3,0,0}, // S_FATSHOTX2 + {SPR_MISL,32771,4,NULL,S_NULL,0,0}, // S_FATSHOTX3 + {SPR_FATT,0,15,A_Look,S_FATT_STND2,0,0}, // S_FATT_STND + {SPR_FATT,1,15,A_Look,S_FATT_STND,0,0}, // S_FATT_STND2 + {SPR_FATT,0,4,A_Chase,S_FATT_RUN2,0,0}, // S_FATT_RUN1 + {SPR_FATT,0,4,A_Chase,S_FATT_RUN3,0,0}, // S_FATT_RUN2 + {SPR_FATT,1,4,A_Chase,S_FATT_RUN4,0,0}, // S_FATT_RUN3 + {SPR_FATT,1,4,A_Chase,S_FATT_RUN5,0,0}, // S_FATT_RUN4 + {SPR_FATT,2,4,A_Chase,S_FATT_RUN6,0,0}, // S_FATT_RUN5 + {SPR_FATT,2,4,A_Chase,S_FATT_RUN7,0,0}, // S_FATT_RUN6 + {SPR_FATT,3,4,A_Chase,S_FATT_RUN8,0,0}, // S_FATT_RUN7 + {SPR_FATT,3,4,A_Chase,S_FATT_RUN9,0,0}, // S_FATT_RUN8 + {SPR_FATT,4,4,A_Chase,S_FATT_RUN10,0,0}, // S_FATT_RUN9 + {SPR_FATT,4,4,A_Chase,S_FATT_RUN11,0,0}, // S_FATT_RUN10 + {SPR_FATT,5,4,A_Chase,S_FATT_RUN12,0,0}, // S_FATT_RUN11 + {SPR_FATT,5,4,A_Chase,S_FATT_RUN1,0,0}, // S_FATT_RUN12 + {SPR_FATT,6,20,A_FatRaise,S_FATT_ATK2,0,0}, // S_FATT_ATK1 + {SPR_FATT,32775,10,A_FatAttack1,S_FATT_ATK3,0,0}, // S_FATT_ATK2 + {SPR_FATT,8,5,A_FaceTarget,S_FATT_ATK4,0,0}, // S_FATT_ATK3 + {SPR_FATT,6,5,A_FaceTarget,S_FATT_ATK5,0,0}, // S_FATT_ATK4 + {SPR_FATT,32775,10,A_FatAttack2,S_FATT_ATK6,0,0}, // S_FATT_ATK5 + {SPR_FATT,8,5,A_FaceTarget,S_FATT_ATK7,0,0}, // S_FATT_ATK6 + {SPR_FATT,6,5,A_FaceTarget,S_FATT_ATK8,0,0}, // S_FATT_ATK7 + {SPR_FATT,32775,10,A_FatAttack3,S_FATT_ATK9,0,0}, // S_FATT_ATK8 + {SPR_FATT,8,5,A_FaceTarget,S_FATT_ATK10,0,0}, // S_FATT_ATK9 + {SPR_FATT,6,5,A_FaceTarget,S_FATT_RUN1,0,0}, // S_FATT_ATK10 + {SPR_FATT,9,3,NULL,S_FATT_PAIN2,0,0}, // S_FATT_PAIN + {SPR_FATT,9,3,A_Pain,S_FATT_RUN1,0,0}, // S_FATT_PAIN2 + {SPR_FATT,10,6,NULL,S_FATT_DIE2,0,0}, // S_FATT_DIE1 + {SPR_FATT,11,6,A_Scream,S_FATT_DIE3,0,0}, // S_FATT_DIE2 + {SPR_FATT,12,6,A_Fall,S_FATT_DIE4,0,0}, // S_FATT_DIE3 + {SPR_FATT,13,6,NULL,S_FATT_DIE5,0,0}, // S_FATT_DIE4 + {SPR_FATT,14,6,NULL,S_FATT_DIE6,0,0}, // S_FATT_DIE5 + {SPR_FATT,15,6,NULL,S_FATT_DIE7,0,0}, // S_FATT_DIE6 + {SPR_FATT,16,6,NULL,S_FATT_DIE8,0,0}, // S_FATT_DIE7 + {SPR_FATT,17,6,NULL,S_FATT_DIE9,0,0}, // S_FATT_DIE8 + {SPR_FATT,18,6,NULL,S_FATT_DIE10,0,0}, // S_FATT_DIE9 + {SPR_FATT,19,-1,A_BossDeath,S_NULL,0,0}, // S_FATT_DIE10 + {SPR_FATT,17,5,NULL,S_FATT_RAISE2,0,0}, // S_FATT_RAISE1 + {SPR_FATT,16,5,NULL,S_FATT_RAISE3,0,0}, // S_FATT_RAISE2 + {SPR_FATT,15,5,NULL,S_FATT_RAISE4,0,0}, // S_FATT_RAISE3 + {SPR_FATT,14,5,NULL,S_FATT_RAISE5,0,0}, // S_FATT_RAISE4 + {SPR_FATT,13,5,NULL,S_FATT_RAISE6,0,0}, // S_FATT_RAISE5 + {SPR_FATT,12,5,NULL,S_FATT_RAISE7,0,0}, // S_FATT_RAISE6 + {SPR_FATT,11,5,NULL,S_FATT_RAISE8,0,0}, // S_FATT_RAISE7 + {SPR_FATT,10,5,NULL,S_FATT_RUN1,0,0}, // S_FATT_RAISE8 + {SPR_CPOS,0,10,A_Look,S_CPOS_STND2,0,0}, // S_CPOS_STND + {SPR_CPOS,1,10,A_Look,S_CPOS_STND,0,0}, // S_CPOS_STND2 + {SPR_CPOS,0,3,A_Chase,S_CPOS_RUN2,0,0}, // S_CPOS_RUN1 + {SPR_CPOS,0,3,A_Chase,S_CPOS_RUN3,0,0}, // S_CPOS_RUN2 + {SPR_CPOS,1,3,A_Chase,S_CPOS_RUN4,0,0}, // S_CPOS_RUN3 + {SPR_CPOS,1,3,A_Chase,S_CPOS_RUN5,0,0}, // S_CPOS_RUN4 + {SPR_CPOS,2,3,A_Chase,S_CPOS_RUN6,0,0}, // S_CPOS_RUN5 + {SPR_CPOS,2,3,A_Chase,S_CPOS_RUN7,0,0}, // S_CPOS_RUN6 + {SPR_CPOS,3,3,A_Chase,S_CPOS_RUN8,0,0}, // S_CPOS_RUN7 + {SPR_CPOS,3,3,A_Chase,S_CPOS_RUN1,0,0}, // S_CPOS_RUN8 + {SPR_CPOS,4,10,A_FaceTarget,S_CPOS_ATK2,0,0}, // S_CPOS_ATK1 + {SPR_CPOS,32773,4,A_CPosAttack,S_CPOS_ATK3,0,0}, // S_CPOS_ATK2 + {SPR_CPOS,32772,4,A_CPosAttack,S_CPOS_ATK4,0,0}, // S_CPOS_ATK3 + {SPR_CPOS,5,1,A_CPosRefire,S_CPOS_ATK2,0,0}, // S_CPOS_ATK4 + {SPR_CPOS,6,3,NULL,S_CPOS_PAIN2,0,0}, // S_CPOS_PAIN + {SPR_CPOS,6,3,A_Pain,S_CPOS_RUN1,0,0}, // S_CPOS_PAIN2 + {SPR_CPOS,7,5,NULL,S_CPOS_DIE2,0,0}, // S_CPOS_DIE1 + {SPR_CPOS,8,5,A_Scream,S_CPOS_DIE3,0,0}, // S_CPOS_DIE2 + {SPR_CPOS,9,5,A_Fall,S_CPOS_DIE4,0,0}, // S_CPOS_DIE3 + {SPR_CPOS,10,5,NULL,S_CPOS_DIE5,0,0}, // S_CPOS_DIE4 + {SPR_CPOS,11,5,NULL,S_CPOS_DIE6,0,0}, // S_CPOS_DIE5 + {SPR_CPOS,12,5,NULL,S_CPOS_DIE7,0,0}, // S_CPOS_DIE6 + {SPR_CPOS,13,-1,NULL,S_NULL,0,0}, // S_CPOS_DIE7 + {SPR_CPOS,14,5,NULL,S_CPOS_XDIE2,0,0}, // S_CPOS_XDIE1 + {SPR_CPOS,15,5,A_XScream,S_CPOS_XDIE3,0,0}, // S_CPOS_XDIE2 + {SPR_CPOS,16,5,A_Fall,S_CPOS_XDIE4,0,0}, // S_CPOS_XDIE3 + {SPR_CPOS,17,5,NULL,S_CPOS_XDIE5,0,0}, // S_CPOS_XDIE4 + {SPR_CPOS,18,5,NULL,S_CPOS_XDIE6,0,0}, // S_CPOS_XDIE5 + {SPR_CPOS,19,-1,NULL,S_NULL,0,0}, // S_CPOS_XDIE6 + {SPR_CPOS,13,5,NULL,S_CPOS_RAISE2,0,0}, // S_CPOS_RAISE1 + {SPR_CPOS,12,5,NULL,S_CPOS_RAISE3,0,0}, // S_CPOS_RAISE2 + {SPR_CPOS,11,5,NULL,S_CPOS_RAISE4,0,0}, // S_CPOS_RAISE3 + {SPR_CPOS,10,5,NULL,S_CPOS_RAISE5,0,0}, // S_CPOS_RAISE4 + {SPR_CPOS,9,5,NULL,S_CPOS_RAISE6,0,0}, // S_CPOS_RAISE5 + {SPR_CPOS,8,5,NULL,S_CPOS_RAISE7,0,0}, // S_CPOS_RAISE6 + {SPR_CPOS,7,5,NULL,S_CPOS_RUN1,0,0}, // S_CPOS_RAISE7 + {SPR_TROO,0,10,A_Look,S_TROO_STND2,0,0}, // S_TROO_STND + {SPR_TROO,1,10,A_Look,S_TROO_STND,0,0}, // S_TROO_STND2 + {SPR_TROO,0,3,A_Chase,S_TROO_RUN2,0,0}, // S_TROO_RUN1 + {SPR_TROO,0,3,A_Chase,S_TROO_RUN3,0,0}, // S_TROO_RUN2 + {SPR_TROO,1,3,A_Chase,S_TROO_RUN4,0,0}, // S_TROO_RUN3 + {SPR_TROO,1,3,A_Chase,S_TROO_RUN5,0,0}, // S_TROO_RUN4 + {SPR_TROO,2,3,A_Chase,S_TROO_RUN6,0,0}, // S_TROO_RUN5 + {SPR_TROO,2,3,A_Chase,S_TROO_RUN7,0,0}, // S_TROO_RUN6 + {SPR_TROO,3,3,A_Chase,S_TROO_RUN8,0,0}, // S_TROO_RUN7 + {SPR_TROO,3,3,A_Chase,S_TROO_RUN1,0,0}, // S_TROO_RUN8 + {SPR_TROO,4,8,A_FaceTarget,S_TROO_ATK2,0,0}, // S_TROO_ATK1 + {SPR_TROO,5,8,A_FaceTarget,S_TROO_ATK3,0,0}, // S_TROO_ATK2 + {SPR_TROO,6,6,A_TroopAttack,S_TROO_RUN1,0,0}, // S_TROO_ATK3 + {SPR_TROO,7,2,NULL,S_TROO_PAIN2,0,0}, // S_TROO_PAIN + {SPR_TROO,7,2,A_Pain,S_TROO_RUN1,0,0}, // S_TROO_PAIN2 + {SPR_TROO,8,8,NULL,S_TROO_DIE2,0,0}, // S_TROO_DIE1 + {SPR_TROO,9,8,A_Scream,S_TROO_DIE3,0,0}, // S_TROO_DIE2 + {SPR_TROO,10,6,NULL,S_TROO_DIE4,0,0}, // S_TROO_DIE3 + {SPR_TROO,11,6,A_Fall,S_TROO_DIE5,0,0}, // S_TROO_DIE4 + {SPR_TROO,12,-1,NULL,S_NULL,0,0}, // S_TROO_DIE5 + {SPR_TROO,13,5,NULL,S_TROO_XDIE2,0,0}, // S_TROO_XDIE1 + {SPR_TROO,14,5,A_XScream,S_TROO_XDIE3,0,0}, // S_TROO_XDIE2 + {SPR_TROO,15,5,NULL,S_TROO_XDIE4,0,0}, // S_TROO_XDIE3 + {SPR_TROO,16,5,A_Fall,S_TROO_XDIE5,0,0}, // S_TROO_XDIE4 + {SPR_TROO,17,5,NULL,S_TROO_XDIE6,0,0}, // S_TROO_XDIE5 + {SPR_TROO,18,5,NULL,S_TROO_XDIE7,0,0}, // S_TROO_XDIE6 + {SPR_TROO,19,5,NULL,S_TROO_XDIE8,0,0}, // S_TROO_XDIE7 + {SPR_TROO,20,-1,NULL,S_NULL,0,0}, // S_TROO_XDIE8 + {SPR_TROO,12,8,NULL,S_TROO_RAISE2,0,0}, // S_TROO_RAISE1 + {SPR_TROO,11,8,NULL,S_TROO_RAISE3,0,0}, // S_TROO_RAISE2 + {SPR_TROO,10,6,NULL,S_TROO_RAISE4,0,0}, // S_TROO_RAISE3 + {SPR_TROO,9,6,NULL,S_TROO_RAISE5,0,0}, // S_TROO_RAISE4 + {SPR_TROO,8,6,NULL,S_TROO_RUN1,0,0}, // S_TROO_RAISE5 + {SPR_SARG,0,10,A_Look,S_SARG_STND2,0,0}, // S_SARG_STND + {SPR_SARG,1,10,A_Look,S_SARG_STND,0,0}, // S_SARG_STND2 + {SPR_SARG,0,2,A_Chase,S_SARG_RUN2,0,0}, // S_SARG_RUN1 + {SPR_SARG,0,2,A_Chase,S_SARG_RUN3,0,0}, // S_SARG_RUN2 + {SPR_SARG,1,2,A_Chase,S_SARG_RUN4,0,0}, // S_SARG_RUN3 + {SPR_SARG,1,2,A_Chase,S_SARG_RUN5,0,0}, // S_SARG_RUN4 + {SPR_SARG,2,2,A_Chase,S_SARG_RUN6,0,0}, // S_SARG_RUN5 + {SPR_SARG,2,2,A_Chase,S_SARG_RUN7,0,0}, // S_SARG_RUN6 + {SPR_SARG,3,2,A_Chase,S_SARG_RUN8,0,0}, // S_SARG_RUN7 + {SPR_SARG,3,2,A_Chase,S_SARG_RUN1,0,0}, // S_SARG_RUN8 + {SPR_SARG,4,8,A_FaceTarget,S_SARG_ATK2,0,0}, // S_SARG_ATK1 + {SPR_SARG,5,8,A_FaceTarget,S_SARG_ATK3,0,0}, // S_SARG_ATK2 + {SPR_SARG,6,8,A_SargAttack,S_SARG_RUN1,0,0}, // S_SARG_ATK3 + {SPR_SARG,7,2,NULL,S_SARG_PAIN2,0,0}, // S_SARG_PAIN + {SPR_SARG,7,2,A_Pain,S_SARG_RUN1,0,0}, // S_SARG_PAIN2 + {SPR_SARG,8,8,NULL,S_SARG_DIE2,0,0}, // S_SARG_DIE1 + {SPR_SARG,9,8,A_Scream,S_SARG_DIE3,0,0}, // S_SARG_DIE2 + {SPR_SARG,10,4,NULL,S_SARG_DIE4,0,0}, // S_SARG_DIE3 + {SPR_SARG,11,4,A_Fall,S_SARG_DIE5,0,0}, // S_SARG_DIE4 + {SPR_SARG,12,4,NULL,S_SARG_DIE6,0,0}, // S_SARG_DIE5 + {SPR_SARG,13,-1,NULL,S_NULL,0,0}, // S_SARG_DIE6 + {SPR_SARG,13,5,NULL,S_SARG_RAISE2,0,0}, // S_SARG_RAISE1 + {SPR_SARG,12,5,NULL,S_SARG_RAISE3,0,0}, // S_SARG_RAISE2 + {SPR_SARG,11,5,NULL,S_SARG_RAISE4,0,0}, // S_SARG_RAISE3 + {SPR_SARG,10,5,NULL,S_SARG_RAISE5,0,0}, // S_SARG_RAISE4 + {SPR_SARG,9,5,NULL,S_SARG_RAISE6,0,0}, // S_SARG_RAISE5 + {SPR_SARG,8,5,NULL,S_SARG_RUN1,0,0}, // S_SARG_RAISE6 + {SPR_HEAD,0,10,A_Look,S_HEAD_STND,0,0}, // S_HEAD_STND + {SPR_HEAD,0,3,A_Chase,S_HEAD_RUN1,0,0}, // S_HEAD_RUN1 + {SPR_HEAD,1,5,A_FaceTarget,S_HEAD_ATK2,0,0}, // S_HEAD_ATK1 + {SPR_HEAD,2,5,A_FaceTarget,S_HEAD_ATK3,0,0}, // S_HEAD_ATK2 + {SPR_HEAD,32771,5,A_HeadAttack,S_HEAD_RUN1,0,0}, // S_HEAD_ATK3 + {SPR_HEAD,4,3,NULL,S_HEAD_PAIN2,0,0}, // S_HEAD_PAIN + {SPR_HEAD,4,3,A_Pain,S_HEAD_PAIN3,0,0}, // S_HEAD_PAIN2 + {SPR_HEAD,5,6,NULL,S_HEAD_RUN1,0,0}, // S_HEAD_PAIN3 + {SPR_HEAD,6,8,NULL,S_HEAD_DIE2,0,0}, // S_HEAD_DIE1 + {SPR_HEAD,7,8,A_Scream,S_HEAD_DIE3,0,0}, // S_HEAD_DIE2 + {SPR_HEAD,8,8,NULL,S_HEAD_DIE4,0,0}, // S_HEAD_DIE3 + {SPR_HEAD,9,8,NULL,S_HEAD_DIE5,0,0}, // S_HEAD_DIE4 + {SPR_HEAD,10,8,A_Fall,S_HEAD_DIE6,0,0}, // S_HEAD_DIE5 + {SPR_HEAD,11,-1,NULL,S_NULL,0,0}, // S_HEAD_DIE6 + {SPR_HEAD,11,8,NULL,S_HEAD_RAISE2,0,0}, // S_HEAD_RAISE1 + {SPR_HEAD,10,8,NULL,S_HEAD_RAISE3,0,0}, // S_HEAD_RAISE2 + {SPR_HEAD,9,8,NULL,S_HEAD_RAISE4,0,0}, // S_HEAD_RAISE3 + {SPR_HEAD,8,8,NULL,S_HEAD_RAISE5,0,0}, // S_HEAD_RAISE4 + {SPR_HEAD,7,8,NULL,S_HEAD_RAISE6,0,0}, // S_HEAD_RAISE5 + {SPR_HEAD,6,8,NULL,S_HEAD_RUN1,0,0}, // S_HEAD_RAISE6 + {SPR_BAL7,32768,4,NULL,S_BRBALL2,0,0}, // S_BRBALL1 + {SPR_BAL7,32769,4,NULL,S_BRBALL1,0,0}, // S_BRBALL2 + {SPR_BAL7,32770,6,NULL,S_BRBALLX2,0,0}, // S_BRBALLX1 + {SPR_BAL7,32771,6,NULL,S_BRBALLX3,0,0}, // S_BRBALLX2 + {SPR_BAL7,32772,6,NULL,S_NULL,0,0}, // S_BRBALLX3 + {SPR_BOSS,0,10,A_Look,S_BOSS_STND2,0,0}, // S_BOSS_STND + {SPR_BOSS,1,10,A_Look,S_BOSS_STND,0,0}, // S_BOSS_STND2 + {SPR_BOSS,0,3,A_Chase,S_BOSS_RUN2,0,0}, // S_BOSS_RUN1 + {SPR_BOSS,0,3,A_Chase,S_BOSS_RUN3,0,0}, // S_BOSS_RUN2 + {SPR_BOSS,1,3,A_Chase,S_BOSS_RUN4,0,0}, // S_BOSS_RUN3 + {SPR_BOSS,1,3,A_Chase,S_BOSS_RUN5,0,0}, // S_BOSS_RUN4 + {SPR_BOSS,2,3,A_Chase,S_BOSS_RUN6,0,0}, // S_BOSS_RUN5 + {SPR_BOSS,2,3,A_Chase,S_BOSS_RUN7,0,0}, // S_BOSS_RUN6 + {SPR_BOSS,3,3,A_Chase,S_BOSS_RUN8,0,0}, // S_BOSS_RUN7 + {SPR_BOSS,3,3,A_Chase,S_BOSS_RUN1,0,0}, // S_BOSS_RUN8 + {SPR_BOSS,4,8,A_FaceTarget,S_BOSS_ATK2,0,0}, // S_BOSS_ATK1 + {SPR_BOSS,5,8,A_FaceTarget,S_BOSS_ATK3,0,0}, // S_BOSS_ATK2 + {SPR_BOSS,6,8,A_BruisAttack,S_BOSS_RUN1,0,0}, // S_BOSS_ATK3 + {SPR_BOSS,7,2,NULL,S_BOSS_PAIN2,0,0}, // S_BOSS_PAIN + {SPR_BOSS,7,2,A_Pain,S_BOSS_RUN1,0,0}, // S_BOSS_PAIN2 + {SPR_BOSS,8,8,NULL,S_BOSS_DIE2,0,0}, // S_BOSS_DIE1 + {SPR_BOSS,9,8,A_Scream,S_BOSS_DIE3,0,0}, // S_BOSS_DIE2 + {SPR_BOSS,10,8,NULL,S_BOSS_DIE4,0,0}, // S_BOSS_DIE3 + {SPR_BOSS,11,8,A_Fall,S_BOSS_DIE5,0,0}, // S_BOSS_DIE4 + {SPR_BOSS,12,8,NULL,S_BOSS_DIE6,0,0}, // S_BOSS_DIE5 + {SPR_BOSS,13,8,NULL,S_BOSS_DIE7,0,0}, // S_BOSS_DIE6 + {SPR_BOSS,14,-1,A_BossDeath,S_NULL,0,0}, // S_BOSS_DIE7 + {SPR_BOSS,14,8,NULL,S_BOSS_RAISE2,0,0}, // S_BOSS_RAISE1 + {SPR_BOSS,13,8,NULL,S_BOSS_RAISE3,0,0}, // S_BOSS_RAISE2 + {SPR_BOSS,12,8,NULL,S_BOSS_RAISE4,0,0}, // S_BOSS_RAISE3 + {SPR_BOSS,11,8,NULL,S_BOSS_RAISE5,0,0}, // S_BOSS_RAISE4 + {SPR_BOSS,10,8,NULL,S_BOSS_RAISE6,0,0}, // S_BOSS_RAISE5 + {SPR_BOSS,9,8,NULL,S_BOSS_RAISE7,0,0}, // S_BOSS_RAISE6 + {SPR_BOSS,8,8,NULL,S_BOSS_RUN1,0,0}, // S_BOSS_RAISE7 + {SPR_BOS2,0,10,A_Look,S_BOS2_STND2,0,0}, // S_BOS2_STND + {SPR_BOS2,1,10,A_Look,S_BOS2_STND,0,0}, // S_BOS2_STND2 + {SPR_BOS2,0,3,A_Chase,S_BOS2_RUN2,0,0}, // S_BOS2_RUN1 + {SPR_BOS2,0,3,A_Chase,S_BOS2_RUN3,0,0}, // S_BOS2_RUN2 + {SPR_BOS2,1,3,A_Chase,S_BOS2_RUN4,0,0}, // S_BOS2_RUN3 + {SPR_BOS2,1,3,A_Chase,S_BOS2_RUN5,0,0}, // S_BOS2_RUN4 + {SPR_BOS2,2,3,A_Chase,S_BOS2_RUN6,0,0}, // S_BOS2_RUN5 + {SPR_BOS2,2,3,A_Chase,S_BOS2_RUN7,0,0}, // S_BOS2_RUN6 + {SPR_BOS2,3,3,A_Chase,S_BOS2_RUN8,0,0}, // S_BOS2_RUN7 + {SPR_BOS2,3,3,A_Chase,S_BOS2_RUN1,0,0}, // S_BOS2_RUN8 + {SPR_BOS2,4,8,A_FaceTarget,S_BOS2_ATK2,0,0}, // S_BOS2_ATK1 + {SPR_BOS2,5,8,A_FaceTarget,S_BOS2_ATK3,0,0}, // S_BOS2_ATK2 + {SPR_BOS2,6,8,A_BruisAttack,S_BOS2_RUN1,0,0}, // S_BOS2_ATK3 + {SPR_BOS2,7,2,NULL,S_BOS2_PAIN2,0,0}, // S_BOS2_PAIN + {SPR_BOS2,7,2,A_Pain,S_BOS2_RUN1,0,0}, // S_BOS2_PAIN2 + {SPR_BOS2,8,8,NULL,S_BOS2_DIE2,0,0}, // S_BOS2_DIE1 + {SPR_BOS2,9,8,A_Scream,S_BOS2_DIE3,0,0}, // S_BOS2_DIE2 + {SPR_BOS2,10,8,NULL,S_BOS2_DIE4,0,0}, // S_BOS2_DIE3 + {SPR_BOS2,11,8,A_Fall,S_BOS2_DIE5,0,0}, // S_BOS2_DIE4 + {SPR_BOS2,12,8,NULL,S_BOS2_DIE6,0,0}, // S_BOS2_DIE5 + {SPR_BOS2,13,8,NULL,S_BOS2_DIE7,0,0}, // S_BOS2_DIE6 + {SPR_BOS2,14,-1,NULL,S_NULL,0,0}, // S_BOS2_DIE7 + {SPR_BOS2,14,8,NULL,S_BOS2_RAISE2,0,0}, // S_BOS2_RAISE1 + {SPR_BOS2,13,8,NULL,S_BOS2_RAISE3,0,0}, // S_BOS2_RAISE2 + {SPR_BOS2,12,8,NULL,S_BOS2_RAISE4,0,0}, // S_BOS2_RAISE3 + {SPR_BOS2,11,8,NULL,S_BOS2_RAISE5,0,0}, // S_BOS2_RAISE4 + {SPR_BOS2,10,8,NULL,S_BOS2_RAISE6,0,0}, // S_BOS2_RAISE5 + {SPR_BOS2,9,8,NULL,S_BOS2_RAISE7,0,0}, // S_BOS2_RAISE6 + {SPR_BOS2,8,8,NULL,S_BOS2_RUN1,0,0}, // S_BOS2_RAISE7 + {SPR_SKUL,32768,10,A_Look,S_SKULL_STND2,0,0}, // S_SKULL_STND + {SPR_SKUL,32769,10,A_Look,S_SKULL_STND,0,0}, // S_SKULL_STND2 + {SPR_SKUL,32768,6,A_Chase,S_SKULL_RUN2,0,0}, // S_SKULL_RUN1 + {SPR_SKUL,32769,6,A_Chase,S_SKULL_RUN1,0,0}, // S_SKULL_RUN2 + {SPR_SKUL,32770,10,A_FaceTarget,S_SKULL_ATK2,0,0}, // S_SKULL_ATK1 + {SPR_SKUL,32771,4,A_SkullAttack,S_SKULL_ATK3,0,0}, // S_SKULL_ATK2 + {SPR_SKUL,32770,4,NULL,S_SKULL_ATK4,0,0}, // S_SKULL_ATK3 + {SPR_SKUL,32771,4,NULL,S_SKULL_ATK3,0,0}, // S_SKULL_ATK4 + {SPR_SKUL,32772,3,NULL,S_SKULL_PAIN2,0,0}, // S_SKULL_PAIN + {SPR_SKUL,32772,3,A_Pain,S_SKULL_RUN1,0,0}, // S_SKULL_PAIN2 + {SPR_SKUL,32773,6,NULL,S_SKULL_DIE2,0,0}, // S_SKULL_DIE1 + {SPR_SKUL,32774,6,A_Scream,S_SKULL_DIE3,0,0}, // S_SKULL_DIE2 + {SPR_SKUL,32775,6,NULL,S_SKULL_DIE4,0,0}, // S_SKULL_DIE3 + {SPR_SKUL,32776,6,A_Fall,S_SKULL_DIE5,0,0}, // S_SKULL_DIE4 + {SPR_SKUL,9,6,NULL,S_SKULL_DIE6,0,0}, // S_SKULL_DIE5 + {SPR_SKUL,10,6,NULL,S_NULL,0,0}, // S_SKULL_DIE6 + {SPR_SPID,0,10,A_Look,S_SPID_STND2,0,0}, // S_SPID_STND + {SPR_SPID,1,10,A_Look,S_SPID_STND,0,0}, // S_SPID_STND2 + {SPR_SPID,0,3,A_Metal,S_SPID_RUN2,0,0}, // S_SPID_RUN1 + {SPR_SPID,0,3,A_Chase,S_SPID_RUN3,0,0}, // S_SPID_RUN2 + {SPR_SPID,1,3,A_Chase,S_SPID_RUN4,0,0}, // S_SPID_RUN3 + {SPR_SPID,1,3,A_Chase,S_SPID_RUN5,0,0}, // S_SPID_RUN4 + {SPR_SPID,2,3,A_Metal,S_SPID_RUN6,0,0}, // S_SPID_RUN5 + {SPR_SPID,2,3,A_Chase,S_SPID_RUN7,0,0}, // S_SPID_RUN6 + {SPR_SPID,3,3,A_Chase,S_SPID_RUN8,0,0}, // S_SPID_RUN7 + {SPR_SPID,3,3,A_Chase,S_SPID_RUN9,0,0}, // S_SPID_RUN8 + {SPR_SPID,4,3,A_Metal,S_SPID_RUN10,0,0}, // S_SPID_RUN9 + {SPR_SPID,4,3,A_Chase,S_SPID_RUN11,0,0}, // S_SPID_RUN10 + {SPR_SPID,5,3,A_Chase,S_SPID_RUN12,0,0}, // S_SPID_RUN11 + {SPR_SPID,5,3,A_Chase,S_SPID_RUN1,0,0}, // S_SPID_RUN12 + {SPR_SPID,32768,20,A_FaceTarget,S_SPID_ATK2,0,0}, // S_SPID_ATK1 + {SPR_SPID,32774,4,A_SPosAttack,S_SPID_ATK3,0,0}, // S_SPID_ATK2 + {SPR_SPID,32775,4,A_SPosAttack,S_SPID_ATK4,0,0}, // S_SPID_ATK3 + {SPR_SPID,32775,1,A_SpidRefire,S_SPID_ATK2,0,0}, // S_SPID_ATK4 + {SPR_SPID,8,3,NULL,S_SPID_PAIN2,0,0}, // S_SPID_PAIN + {SPR_SPID,8,3,A_Pain,S_SPID_RUN1,0,0}, // S_SPID_PAIN2 + {SPR_SPID,9,20,A_Scream,S_SPID_DIE2,0,0}, // S_SPID_DIE1 + {SPR_SPID,10,10,A_Fall,S_SPID_DIE3,0,0}, // S_SPID_DIE2 + {SPR_SPID,11,10,NULL,S_SPID_DIE4,0,0}, // S_SPID_DIE3 + {SPR_SPID,12,10,NULL,S_SPID_DIE5,0,0}, // S_SPID_DIE4 + {SPR_SPID,13,10,NULL,S_SPID_DIE6,0,0}, // S_SPID_DIE5 + {SPR_SPID,14,10,NULL,S_SPID_DIE7,0,0}, // S_SPID_DIE6 + {SPR_SPID,15,10,NULL,S_SPID_DIE8,0,0}, // S_SPID_DIE7 + {SPR_SPID,16,10,NULL,S_SPID_DIE9,0,0}, // S_SPID_DIE8 + {SPR_SPID,17,10,NULL,S_SPID_DIE10,0,0}, // S_SPID_DIE9 + {SPR_SPID,18,30,NULL,S_SPID_DIE11,0,0}, // S_SPID_DIE10 + {SPR_SPID,18,-1,A_BossDeath,S_NULL,0,0}, // S_SPID_DIE11 + {SPR_BSPI,0,10,A_Look,S_BSPI_STND2,0,0}, // S_BSPI_STND + {SPR_BSPI,1,10,A_Look,S_BSPI_STND,0,0}, // S_BSPI_STND2 + {SPR_BSPI,0,20,NULL,S_BSPI_RUN1,0,0}, // S_BSPI_SIGHT + {SPR_BSPI,0,3,A_BabyMetal,S_BSPI_RUN2,0,0}, // S_BSPI_RUN1 + {SPR_BSPI,0,3,A_Chase,S_BSPI_RUN3,0,0}, // S_BSPI_RUN2 + {SPR_BSPI,1,3,A_Chase,S_BSPI_RUN4,0,0}, // S_BSPI_RUN3 + {SPR_BSPI,1,3,A_Chase,S_BSPI_RUN5,0,0}, // S_BSPI_RUN4 + {SPR_BSPI,2,3,A_Chase,S_BSPI_RUN6,0,0}, // S_BSPI_RUN5 + {SPR_BSPI,2,3,A_Chase,S_BSPI_RUN7,0,0}, // S_BSPI_RUN6 + {SPR_BSPI,3,3,A_BabyMetal,S_BSPI_RUN8,0,0}, // S_BSPI_RUN7 + {SPR_BSPI,3,3,A_Chase,S_BSPI_RUN9,0,0}, // S_BSPI_RUN8 + {SPR_BSPI,4,3,A_Chase,S_BSPI_RUN10,0,0}, // S_BSPI_RUN9 + {SPR_BSPI,4,3,A_Chase,S_BSPI_RUN11,0,0}, // S_BSPI_RUN10 + {SPR_BSPI,5,3,A_Chase,S_BSPI_RUN12,0,0}, // S_BSPI_RUN11 + {SPR_BSPI,5,3,A_Chase,S_BSPI_RUN1,0,0}, // S_BSPI_RUN12 + {SPR_BSPI,32768,20,A_FaceTarget,S_BSPI_ATK2,0,0}, // S_BSPI_ATK1 + {SPR_BSPI,32774,4,A_BspiAttack,S_BSPI_ATK3,0,0}, // S_BSPI_ATK2 + {SPR_BSPI,32775,4,NULL,S_BSPI_ATK4,0,0}, // S_BSPI_ATK3 + {SPR_BSPI,32775,1,A_SpidRefire,S_BSPI_ATK2,0,0}, // S_BSPI_ATK4 + {SPR_BSPI,8,3,NULL,S_BSPI_PAIN2,0,0}, // S_BSPI_PAIN + {SPR_BSPI,8,3,A_Pain,S_BSPI_RUN1,0,0}, // S_BSPI_PAIN2 + {SPR_BSPI,9,20,A_Scream,S_BSPI_DIE2,0,0}, // S_BSPI_DIE1 + {SPR_BSPI,10,7,A_Fall,S_BSPI_DIE3,0,0}, // S_BSPI_DIE2 + {SPR_BSPI,11,7,NULL,S_BSPI_DIE4,0,0}, // S_BSPI_DIE3 + {SPR_BSPI,12,7,NULL,S_BSPI_DIE5,0,0}, // S_BSPI_DIE4 + {SPR_BSPI,13,7,NULL,S_BSPI_DIE6,0,0}, // S_BSPI_DIE5 + {SPR_BSPI,14,7,NULL,S_BSPI_DIE7,0,0}, // S_BSPI_DIE6 + {SPR_BSPI,15,-1,A_BossDeath,S_NULL,0,0}, // S_BSPI_DIE7 + {SPR_BSPI,15,5,NULL,S_BSPI_RAISE2,0,0}, // S_BSPI_RAISE1 + {SPR_BSPI,14,5,NULL,S_BSPI_RAISE3,0,0}, // S_BSPI_RAISE2 + {SPR_BSPI,13,5,NULL,S_BSPI_RAISE4,0,0}, // S_BSPI_RAISE3 + {SPR_BSPI,12,5,NULL,S_BSPI_RAISE5,0,0}, // S_BSPI_RAISE4 + {SPR_BSPI,11,5,NULL,S_BSPI_RAISE6,0,0}, // S_BSPI_RAISE5 + {SPR_BSPI,10,5,NULL,S_BSPI_RAISE7,0,0}, // S_BSPI_RAISE6 + {SPR_BSPI,9,5,NULL,S_BSPI_RUN1,0,0}, // S_BSPI_RAISE7 + {SPR_APLS,32768,5,NULL,S_ARACH_PLAZ2,0,0}, // S_ARACH_PLAZ + {SPR_APLS,32769,5,NULL,S_ARACH_PLAZ,0,0}, // S_ARACH_PLAZ2 + {SPR_APBX,32768,5,NULL,S_ARACH_PLEX2,0,0}, // S_ARACH_PLEX + {SPR_APBX,32769,5,NULL,S_ARACH_PLEX3,0,0}, // S_ARACH_PLEX2 + {SPR_APBX,32770,5,NULL,S_ARACH_PLEX4,0,0}, // S_ARACH_PLEX3 + {SPR_APBX,32771,5,NULL,S_ARACH_PLEX5,0,0}, // S_ARACH_PLEX4 + {SPR_APBX,32772,5,NULL,S_NULL,0,0}, // S_ARACH_PLEX5 + {SPR_CYBR,0,10,A_Look,S_CYBER_STND2,0,0}, // S_CYBER_STND + {SPR_CYBR,1,10,A_Look,S_CYBER_STND,0,0}, // S_CYBER_STND2 + {SPR_CYBR,0,3,A_Hoof,S_CYBER_RUN2,0,0}, // S_CYBER_RUN1 + {SPR_CYBR,0,3,A_Chase,S_CYBER_RUN3,0,0}, // S_CYBER_RUN2 + {SPR_CYBR,1,3,A_Chase,S_CYBER_RUN4,0,0}, // S_CYBER_RUN3 + {SPR_CYBR,1,3,A_Chase,S_CYBER_RUN5,0,0}, // S_CYBER_RUN4 + {SPR_CYBR,2,3,A_Chase,S_CYBER_RUN6,0,0}, // S_CYBER_RUN5 + {SPR_CYBR,2,3,A_Chase,S_CYBER_RUN7,0,0}, // S_CYBER_RUN6 + {SPR_CYBR,3,3,A_Metal,S_CYBER_RUN8,0,0}, // S_CYBER_RUN7 + {SPR_CYBR,3,3,A_Chase,S_CYBER_RUN1,0,0}, // S_CYBER_RUN8 + {SPR_CYBR,4,6,A_FaceTarget,S_CYBER_ATK2,0,0}, // S_CYBER_ATK1 + {SPR_CYBR,5,12,A_CyberAttack,S_CYBER_ATK3,0,0}, // S_CYBER_ATK2 + {SPR_CYBR,4,12,A_FaceTarget,S_CYBER_ATK4,0,0}, // S_CYBER_ATK3 + {SPR_CYBR,5,12,A_CyberAttack,S_CYBER_ATK5,0,0}, // S_CYBER_ATK4 + {SPR_CYBR,4,12,A_FaceTarget,S_CYBER_ATK6,0,0}, // S_CYBER_ATK5 + {SPR_CYBR,5,12,A_CyberAttack,S_CYBER_RUN1,0,0}, // S_CYBER_ATK6 + {SPR_CYBR,6,10,A_Pain,S_CYBER_RUN1,0,0}, // S_CYBER_PAIN + {SPR_CYBR,7,10,NULL,S_CYBER_DIE2,0,0}, // S_CYBER_DIE1 + {SPR_CYBR,8,10,A_Scream,S_CYBER_DIE3,0,0}, // S_CYBER_DIE2 + {SPR_CYBR,9,10,NULL,S_CYBER_DIE4,0,0}, // S_CYBER_DIE3 + {SPR_CYBR,10,10,NULL,S_CYBER_DIE5,0,0}, // S_CYBER_DIE4 + {SPR_CYBR,11,10,NULL,S_CYBER_DIE6,0,0}, // S_CYBER_DIE5 + {SPR_CYBR,12,10,A_Fall,S_CYBER_DIE7,0,0}, // S_CYBER_DIE6 + {SPR_CYBR,13,10,NULL,S_CYBER_DIE8,0,0}, // S_CYBER_DIE7 + {SPR_CYBR,14,10,NULL,S_CYBER_DIE9,0,0}, // S_CYBER_DIE8 + {SPR_CYBR,15,30,NULL,S_CYBER_DIE10,0,0}, // S_CYBER_DIE9 + {SPR_CYBR,15,-1,A_BossDeath,S_NULL,0,0}, // S_CYBER_DIE10 + {SPR_PAIN,0,10,A_Look,S_PAIN_STND,0,0}, // S_PAIN_STND + {SPR_PAIN,0,3,A_Chase,S_PAIN_RUN2,0,0}, // S_PAIN_RUN1 + {SPR_PAIN,0,3,A_Chase,S_PAIN_RUN3,0,0}, // S_PAIN_RUN2 + {SPR_PAIN,1,3,A_Chase,S_PAIN_RUN4,0,0}, // S_PAIN_RUN3 + {SPR_PAIN,1,3,A_Chase,S_PAIN_RUN5,0,0}, // S_PAIN_RUN4 + {SPR_PAIN,2,3,A_Chase,S_PAIN_RUN6,0,0}, // S_PAIN_RUN5 + {SPR_PAIN,2,3,A_Chase,S_PAIN_RUN1,0,0}, // S_PAIN_RUN6 + {SPR_PAIN,3,5,A_FaceTarget,S_PAIN_ATK2,0,0}, // S_PAIN_ATK1 + {SPR_PAIN,4,5,A_FaceTarget,S_PAIN_ATK3,0,0}, // S_PAIN_ATK2 + {SPR_PAIN,32773,5,A_FaceTarget,S_PAIN_ATK4,0,0}, // S_PAIN_ATK3 + {SPR_PAIN,32773,0,A_PainAttack,S_PAIN_RUN1,0,0}, // S_PAIN_ATK4 + {SPR_PAIN,6,6,NULL,S_PAIN_PAIN2,0,0}, // S_PAIN_PAIN + {SPR_PAIN,6,6,A_Pain,S_PAIN_RUN1,0,0}, // S_PAIN_PAIN2 + {SPR_PAIN,32775,8,NULL,S_PAIN_DIE2,0,0}, // S_PAIN_DIE1 + {SPR_PAIN,32776,8,A_Scream,S_PAIN_DIE3,0,0}, // S_PAIN_DIE2 + {SPR_PAIN,32777,8,NULL,S_PAIN_DIE4,0,0}, // S_PAIN_DIE3 + {SPR_PAIN,32778,8,NULL,S_PAIN_DIE5,0,0}, // S_PAIN_DIE4 + {SPR_PAIN,32779,8,A_PainDie,S_PAIN_DIE6,0,0}, // S_PAIN_DIE5 + {SPR_PAIN,32780,8,NULL,S_NULL,0,0}, // S_PAIN_DIE6 + {SPR_PAIN,12,8,NULL,S_PAIN_RAISE2,0,0}, // S_PAIN_RAISE1 + {SPR_PAIN,11,8,NULL,S_PAIN_RAISE3,0,0}, // S_PAIN_RAISE2 + {SPR_PAIN,10,8,NULL,S_PAIN_RAISE4,0,0}, // S_PAIN_RAISE3 + {SPR_PAIN,9,8,NULL,S_PAIN_RAISE5,0,0}, // S_PAIN_RAISE4 + {SPR_PAIN,8,8,NULL,S_PAIN_RAISE6,0,0}, // S_PAIN_RAISE5 + {SPR_PAIN,7,8,NULL,S_PAIN_RUN1,0,0}, // S_PAIN_RAISE6 + {SPR_SSWV,0,10,A_Look,S_SSWV_STND2,0,0}, // S_SSWV_STND + {SPR_SSWV,1,10,A_Look,S_SSWV_STND,0,0}, // S_SSWV_STND2 + {SPR_SSWV,0,3,A_Chase,S_SSWV_RUN2,0,0}, // S_SSWV_RUN1 + {SPR_SSWV,0,3,A_Chase,S_SSWV_RUN3,0,0}, // S_SSWV_RUN2 + {SPR_SSWV,1,3,A_Chase,S_SSWV_RUN4,0,0}, // S_SSWV_RUN3 + {SPR_SSWV,1,3,A_Chase,S_SSWV_RUN5,0,0}, // S_SSWV_RUN4 + {SPR_SSWV,2,3,A_Chase,S_SSWV_RUN6,0,0}, // S_SSWV_RUN5 + {SPR_SSWV,2,3,A_Chase,S_SSWV_RUN7,0,0}, // S_SSWV_RUN6 + {SPR_SSWV,3,3,A_Chase,S_SSWV_RUN8,0,0}, // S_SSWV_RUN7 + {SPR_SSWV,3,3,A_Chase,S_SSWV_RUN1,0,0}, // S_SSWV_RUN8 + {SPR_SSWV,4,10,A_FaceTarget,S_SSWV_ATK2,0,0}, // S_SSWV_ATK1 + {SPR_SSWV,5,10,A_FaceTarget,S_SSWV_ATK3,0,0}, // S_SSWV_ATK2 + {SPR_SSWV,32774,4,A_CPosAttack,S_SSWV_ATK4,0,0}, // S_SSWV_ATK3 + {SPR_SSWV,5,6,A_FaceTarget,S_SSWV_ATK5,0,0}, // S_SSWV_ATK4 + {SPR_SSWV,32774,4,A_CPosAttack,S_SSWV_ATK6,0,0}, // S_SSWV_ATK5 + {SPR_SSWV,5,1,A_CPosRefire,S_SSWV_ATK2,0,0}, // S_SSWV_ATK6 + {SPR_SSWV,7,3,NULL,S_SSWV_PAIN2,0,0}, // S_SSWV_PAIN + {SPR_SSWV,7,3,A_Pain,S_SSWV_RUN1,0,0}, // S_SSWV_PAIN2 + {SPR_SSWV,8,5,NULL,S_SSWV_DIE2,0,0}, // S_SSWV_DIE1 + {SPR_SSWV,9,5,A_Scream,S_SSWV_DIE3,0,0}, // S_SSWV_DIE2 + {SPR_SSWV,10,5,A_Fall,S_SSWV_DIE4,0,0}, // S_SSWV_DIE3 + {SPR_SSWV,11,5,NULL,S_SSWV_DIE5,0,0}, // S_SSWV_DIE4 + {SPR_SSWV,12,-1,NULL,S_NULL,0,0}, // S_SSWV_DIE5 + {SPR_SSWV,13,5,NULL,S_SSWV_XDIE2,0,0}, // S_SSWV_XDIE1 + {SPR_SSWV,14,5,A_XScream,S_SSWV_XDIE3,0,0}, // S_SSWV_XDIE2 + {SPR_SSWV,15,5,A_Fall,S_SSWV_XDIE4,0,0}, // S_SSWV_XDIE3 + {SPR_SSWV,16,5,NULL,S_SSWV_XDIE5,0,0}, // S_SSWV_XDIE4 + {SPR_SSWV,17,5,NULL,S_SSWV_XDIE6,0,0}, // S_SSWV_XDIE5 + {SPR_SSWV,18,5,NULL,S_SSWV_XDIE7,0,0}, // S_SSWV_XDIE6 + {SPR_SSWV,19,5,NULL,S_SSWV_XDIE8,0,0}, // S_SSWV_XDIE7 + {SPR_SSWV,20,5,NULL,S_SSWV_XDIE9,0,0}, // S_SSWV_XDIE8 + {SPR_SSWV,21,-1,NULL,S_NULL,0,0}, // S_SSWV_XDIE9 + {SPR_SSWV,12,5,NULL,S_SSWV_RAISE2,0,0}, // S_SSWV_RAISE1 + {SPR_SSWV,11,5,NULL,S_SSWV_RAISE3,0,0}, // S_SSWV_RAISE2 + {SPR_SSWV,10,5,NULL,S_SSWV_RAISE4,0,0}, // S_SSWV_RAISE3 + {SPR_SSWV,9,5,NULL,S_SSWV_RAISE5,0,0}, // S_SSWV_RAISE4 + {SPR_SSWV,8,5,NULL,S_SSWV_RUN1,0,0}, // S_SSWV_RAISE5 + {SPR_KEEN,0,-1,NULL,S_KEENSTND,0,0}, // S_KEENSTND + {SPR_KEEN,0,6,NULL,S_COMMKEEN2,0,0}, // S_COMMKEEN + {SPR_KEEN,1,6,NULL,S_COMMKEEN3,0,0}, // S_COMMKEEN2 + {SPR_KEEN,2,6,A_Scream,S_COMMKEEN4,0,0}, // S_COMMKEEN3 + {SPR_KEEN,3,6,NULL,S_COMMKEEN5,0,0}, // S_COMMKEEN4 + {SPR_KEEN,4,6,NULL,S_COMMKEEN6,0,0}, // S_COMMKEEN5 + {SPR_KEEN,5,6,NULL,S_COMMKEEN7,0,0}, // S_COMMKEEN6 + {SPR_KEEN,6,6,NULL,S_COMMKEEN8,0,0}, // S_COMMKEEN7 + {SPR_KEEN,7,6,NULL,S_COMMKEEN9,0,0}, // S_COMMKEEN8 + {SPR_KEEN,8,6,NULL,S_COMMKEEN10,0,0}, // S_COMMKEEN9 + {SPR_KEEN,9,6,NULL,S_COMMKEEN11,0,0}, // S_COMMKEEN10 + {SPR_KEEN,10,6,A_KeenDie,S_COMMKEEN12,0,0},// S_COMMKEEN11 + {SPR_KEEN,11,-1,NULL,S_NULL,0,0}, // S_COMMKEEN12 + {SPR_KEEN,12,4,NULL,S_KEENPAIN2,0,0}, // S_KEENPAIN + {SPR_KEEN,12,8,A_Pain,S_KEENSTND,0,0}, // S_KEENPAIN2 + {SPR_BBRN,0,-1,NULL,S_NULL,0,0}, // S_BRAIN + {SPR_BBRN,1,36,A_BrainPain,S_BRAIN,0,0}, // S_BRAIN_PAIN + {SPR_BBRN,0,100,A_BrainScream,S_BRAIN_DIE2,0,0}, // S_BRAIN_DIE1 + {SPR_BBRN,0,10,NULL,S_BRAIN_DIE3,0,0}, // S_BRAIN_DIE2 + {SPR_BBRN,0,10,NULL,S_BRAIN_DIE4,0,0}, // S_BRAIN_DIE3 + {SPR_BBRN,0,-1,A_BrainDie,S_NULL,0,0}, // S_BRAIN_DIE4 + {SPR_SSWV,0,10,A_Look,S_BRAINEYE,0,0}, // S_BRAINEYE + {SPR_SSWV,0,181,A_BrainAwake,S_BRAINEYE1,0,0}, // S_BRAINEYESEE + {SPR_SSWV,0,150,A_BrainSpit,S_BRAINEYE1,0,0}, // S_BRAINEYE1 + {SPR_BOSF,32768,3,A_SpawnSound,S_SPAWN2,0,0}, // S_SPAWN1 + {SPR_BOSF,32769,3,A_SpawnFly,S_SPAWN3,0,0}, // S_SPAWN2 + {SPR_BOSF,32770,3,A_SpawnFly,S_SPAWN4,0,0}, // S_SPAWN3 + {SPR_BOSF,32771,3,A_SpawnFly,S_SPAWN1,0,0}, // S_SPAWN4 + {SPR_FIRE,32768,4,A_Fire,S_SPAWNFIRE2,0,0}, // S_SPAWNFIRE1 + {SPR_FIRE,32769,4,A_Fire,S_SPAWNFIRE3,0,0}, // S_SPAWNFIRE2 + {SPR_FIRE,32770,4,A_Fire,S_SPAWNFIRE4,0,0}, // S_SPAWNFIRE3 + {SPR_FIRE,32771,4,A_Fire,S_SPAWNFIRE5,0,0}, // S_SPAWNFIRE4 + {SPR_FIRE,32772,4,A_Fire,S_SPAWNFIRE6,0,0}, // S_SPAWNFIRE5 + {SPR_FIRE,32773,4,A_Fire,S_SPAWNFIRE7,0,0}, // S_SPAWNFIRE6 + {SPR_FIRE,32774,4,A_Fire,S_SPAWNFIRE8,0,0}, // S_SPAWNFIRE7 + {SPR_FIRE,32775,4,A_Fire,S_NULL,0,0}, // S_SPAWNFIRE8 + {SPR_MISL,32769,10,NULL,S_BRAINEXPLODE2,0,0}, // S_BRAINEXPLODE1 + {SPR_MISL,32770,10,NULL,S_BRAINEXPLODE3,0,0}, // S_BRAINEXPLODE2 + {SPR_MISL,32771,10,A_BrainExplode,S_NULL,0,0}, // S_BRAINEXPLODE3 + {SPR_ARM1,0,6,NULL,S_ARM1A,0,0}, // S_ARM1 + {SPR_ARM1,32769,7,NULL,S_ARM1,0,0}, // S_ARM1A + {SPR_ARM2,0,6,NULL,S_ARM2A,0,0}, // S_ARM2 + {SPR_ARM2,32769,6,NULL,S_ARM2,0,0}, // S_ARM2A + {SPR_BAR1,0,6,NULL,S_BAR2,0,0}, // S_BAR1 + {SPR_BAR1,1,6,NULL,S_BAR1,0,0}, // S_BAR2 + {SPR_BEXP,32768,5,NULL,S_BEXP2,0,0}, // S_BEXP + {SPR_BEXP,32769,5,A_Scream,S_BEXP3,0,0}, // S_BEXP2 + {SPR_BEXP,32770,5,NULL,S_BEXP4,0,0}, // S_BEXP3 + {SPR_BEXP,32771,10,A_Explode,S_BEXP5,0,0}, // S_BEXP4 + {SPR_BEXP,32772,10,NULL,S_NULL,0,0}, // S_BEXP5 + {SPR_FCAN,32768,4,NULL,S_BBAR2,0,0}, // S_BBAR1 + {SPR_FCAN,32769,4,NULL,S_BBAR3,0,0}, // S_BBAR2 + {SPR_FCAN,32770,4,NULL,S_BBAR1,0,0}, // S_BBAR3 + {SPR_BON1,0,6,NULL,S_BON1A,0,0}, // S_BON1 + {SPR_BON1,1,6,NULL,S_BON1B,0,0}, // S_BON1A + {SPR_BON1,2,6,NULL,S_BON1C,0,0}, // S_BON1B + {SPR_BON1,3,6,NULL,S_BON1D,0,0}, // S_BON1C + {SPR_BON1,2,6,NULL,S_BON1E,0,0}, // S_BON1D + {SPR_BON1,1,6,NULL,S_BON1,0,0}, // S_BON1E + {SPR_BON2,0,6,NULL,S_BON2A,0,0}, // S_BON2 + {SPR_BON2,1,6,NULL,S_BON2B,0,0}, // S_BON2A + {SPR_BON2,2,6,NULL,S_BON2C,0,0}, // S_BON2B + {SPR_BON2,3,6,NULL,S_BON2D,0,0}, // S_BON2C + {SPR_BON2,2,6,NULL,S_BON2E,0,0}, // S_BON2D + {SPR_BON2,1,6,NULL,S_BON2,0,0}, // S_BON2E + {SPR_BKEY,0,10,NULL,S_BKEY2,0,0}, // S_BKEY + {SPR_BKEY,32769,10,NULL,S_BKEY,0,0}, // S_BKEY2 + {SPR_RKEY,0,10,NULL,S_RKEY2,0,0}, // S_RKEY + {SPR_RKEY,32769,10,NULL,S_RKEY,0,0}, // S_RKEY2 + {SPR_YKEY,0,10,NULL,S_YKEY2,0,0}, // S_YKEY + {SPR_YKEY,32769,10,NULL,S_YKEY,0,0}, // S_YKEY2 + {SPR_BSKU,0,10,NULL,S_BSKULL2,0,0}, // S_BSKULL + {SPR_BSKU,32769,10,NULL,S_BSKULL,0,0}, // S_BSKULL2 + {SPR_RSKU,0,10,NULL,S_RSKULL2,0,0}, // S_RSKULL + {SPR_RSKU,32769,10,NULL,S_RSKULL,0,0}, // S_RSKULL2 + {SPR_YSKU,0,10,NULL,S_YSKULL2,0,0}, // S_YSKULL + {SPR_YSKU,32769,10,NULL,S_YSKULL,0,0}, // S_YSKULL2 + {SPR_STIM,0,-1,NULL,S_NULL,0,0}, // S_STIM + {SPR_MEDI,0,-1,NULL,S_NULL,0,0}, // S_MEDI + {SPR_SOUL,32768,6,NULL,S_SOUL2,0,0}, // S_SOUL + {SPR_SOUL,32769,6,NULL,S_SOUL3,0,0}, // S_SOUL2 + {SPR_SOUL,32770,6,NULL,S_SOUL4,0,0}, // S_SOUL3 + {SPR_SOUL,32771,6,NULL,S_SOUL5,0,0}, // S_SOUL4 + {SPR_SOUL,32770,6,NULL,S_SOUL6,0,0}, // S_SOUL5 + {SPR_SOUL,32769,6,NULL,S_SOUL,0,0}, // S_SOUL6 + {SPR_PINV,32768,6,NULL,S_PINV2,0,0}, // S_PINV + {SPR_PINV,32769,6,NULL,S_PINV3,0,0}, // S_PINV2 + {SPR_PINV,32770,6,NULL,S_PINV4,0,0}, // S_PINV3 + {SPR_PINV,32771,6,NULL,S_PINV,0,0}, // S_PINV4 + {SPR_PSTR,32768,-1,NULL,S_NULL,0,0}, // S_PSTR + {SPR_PINS,32768,6,NULL,S_PINS2,0,0}, // S_PINS + {SPR_PINS,32769,6,NULL,S_PINS3,0,0}, // S_PINS2 + {SPR_PINS,32770,6,NULL,S_PINS4,0,0}, // S_PINS3 + {SPR_PINS,32771,6,NULL,S_PINS,0,0}, // S_PINS4 + {SPR_MEGA,32768,6,NULL,S_MEGA2,0,0}, // S_MEGA + {SPR_MEGA,32769,6,NULL,S_MEGA3,0,0}, // S_MEGA2 + {SPR_MEGA,32770,6,NULL,S_MEGA4,0,0}, // S_MEGA3 + {SPR_MEGA,32771,6,NULL,S_MEGA,0,0}, // S_MEGA4 + {SPR_SUIT,32768,-1,NULL,S_NULL,0,0}, // S_SUIT + {SPR_PMAP,32768,6,NULL,S_PMAP2,0,0}, // S_PMAP + {SPR_PMAP,32769,6,NULL,S_PMAP3,0,0}, // S_PMAP2 + {SPR_PMAP,32770,6,NULL,S_PMAP4,0,0}, // S_PMAP3 + {SPR_PMAP,32771,6,NULL,S_PMAP5,0,0}, // S_PMAP4 + {SPR_PMAP,32770,6,NULL,S_PMAP6,0,0}, // S_PMAP5 + {SPR_PMAP,32769,6,NULL,S_PMAP,0,0}, // S_PMAP6 + {SPR_PVIS,32768,6,NULL,S_PVIS2,0,0}, // S_PVIS + {SPR_PVIS,1,6,NULL,S_PVIS,0,0}, // S_PVIS2 + {SPR_CLIP,0,-1,NULL,S_NULL,0,0}, // S_CLIP + {SPR_AMMO,0,-1,NULL,S_NULL,0,0}, // S_AMMO + {SPR_ROCK,0,-1,NULL,S_NULL,0,0}, // S_ROCK + {SPR_BROK,0,-1,NULL,S_NULL,0,0}, // S_BROK + {SPR_CELL,0,-1,NULL,S_NULL,0,0}, // S_CELL + {SPR_CELP,0,-1,NULL,S_NULL,0,0}, // S_CELP + {SPR_SHEL,0,-1,NULL,S_NULL,0,0}, // S_SHEL + {SPR_SBOX,0,-1,NULL,S_NULL,0,0}, // S_SBOX + {SPR_BPAK,0,-1,NULL,S_NULL,0,0}, // S_BPAK + {SPR_BFUG,0,-1,NULL,S_NULL,0,0}, // S_BFUG + {SPR_MGUN,0,-1,NULL,S_NULL,0,0}, // S_MGUN + {SPR_CSAW,0,-1,NULL,S_NULL,0,0}, // S_CSAW + {SPR_LAUN,0,-1,NULL,S_NULL,0,0}, // S_LAUN + {SPR_PLAS,0,-1,NULL,S_NULL,0,0}, // S_PLAS + {SPR_SHOT,0,-1,NULL,S_NULL,0,0}, // S_SHOT + {SPR_SGN2,0,-1,NULL,S_NULL,0,0}, // S_SHOT2 + {SPR_COLU,32768,-1,NULL,S_NULL,0,0}, // S_COLU + {SPR_SMT2,0,-1,NULL,S_NULL,0,0}, // S_STALAG + {SPR_GOR1,0,10,NULL,S_BLOODYTWITCH2,0,0}, // S_BLOODYTWITCH + {SPR_GOR1,1,15,NULL,S_BLOODYTWITCH3,0,0}, // S_BLOODYTWITCH2 + {SPR_GOR1,2,8,NULL,S_BLOODYTWITCH4,0,0}, // S_BLOODYTWITCH3 + {SPR_GOR1,1,6,NULL,S_BLOODYTWITCH,0,0}, // S_BLOODYTWITCH4 + {SPR_PLAY,13,-1,NULL,S_NULL,0,0}, // S_DEADTORSO + {SPR_PLAY,18,-1,NULL,S_NULL,0,0}, // S_DEADBOTTOM + {SPR_POL2,0,-1,NULL,S_NULL,0,0}, // S_HEADSONSTICK + {SPR_POL5,0,-1,NULL,S_NULL,0,0}, // S_GIBS + {SPR_POL4,0,-1,NULL,S_NULL,0,0}, // S_HEADONASTICK + {SPR_POL3,32768,6,NULL,S_HEADCANDLES2,0,0}, // S_HEADCANDLES + {SPR_POL3,32769,6,NULL,S_HEADCANDLES,0,0}, // S_HEADCANDLES2 + {SPR_POL1,0,-1,NULL,S_NULL,0,0}, // S_DEADSTICK + {SPR_POL6,0,6,NULL,S_LIVESTICK2,0,0}, // S_LIVESTICK + {SPR_POL6,1,8,NULL,S_LIVESTICK,0,0}, // S_LIVESTICK2 + {SPR_GOR2,0,-1,NULL,S_NULL,0,0}, // S_MEAT2 + {SPR_GOR3,0,-1,NULL,S_NULL,0,0}, // S_MEAT3 + {SPR_GOR4,0,-1,NULL,S_NULL,0,0}, // S_MEAT4 + {SPR_GOR5,0,-1,NULL,S_NULL,0,0}, // S_MEAT5 + {SPR_SMIT,0,-1,NULL,S_NULL,0,0}, // S_STALAGTITE + {SPR_COL1,0,-1,NULL,S_NULL,0,0}, // S_TALLGRNCOL + {SPR_COL2,0,-1,NULL,S_NULL,0,0}, // S_SHRTGRNCOL + {SPR_COL3,0,-1,NULL,S_NULL,0,0}, // S_TALLREDCOL + {SPR_COL4,0,-1,NULL,S_NULL,0,0}, // S_SHRTREDCOL + {SPR_CAND,32768,-1,NULL,S_NULL,0,0}, // S_CANDLESTIK + {SPR_CBRA,32768,-1,NULL,S_NULL,0,0}, // S_CANDELABRA + {SPR_COL6,0,-1,NULL,S_NULL,0,0}, // S_SKULLCOL + {SPR_TRE1,0,-1,NULL,S_NULL,0,0}, // S_TORCHTREE + {SPR_TRE2,0,-1,NULL,S_NULL,0,0}, // S_BIGTREE + {SPR_ELEC,0,-1,NULL,S_NULL,0,0}, // S_TECHPILLAR + {SPR_CEYE,32768,6,NULL,S_EVILEYE2,0,0}, // S_EVILEYE + {SPR_CEYE,32769,6,NULL,S_EVILEYE3,0,0}, // S_EVILEYE2 + {SPR_CEYE,32770,6,NULL,S_EVILEYE4,0,0}, // S_EVILEYE3 + {SPR_CEYE,32769,6,NULL,S_EVILEYE,0,0}, // S_EVILEYE4 + {SPR_FSKU,32768,6,NULL,S_FLOATSKULL2,0,0}, // S_FLOATSKULL + {SPR_FSKU,32769,6,NULL,S_FLOATSKULL3,0,0}, // S_FLOATSKULL2 + {SPR_FSKU,32770,6,NULL,S_FLOATSKULL,0,0}, // S_FLOATSKULL3 + {SPR_COL5,0,14,NULL,S_HEARTCOL2,0,0}, // S_HEARTCOL + {SPR_COL5,1,14,NULL,S_HEARTCOL,0,0}, // S_HEARTCOL2 + {SPR_TBLU,32768,4,NULL,S_BLUETORCH2,0,0}, // S_BLUETORCH + {SPR_TBLU,32769,4,NULL,S_BLUETORCH3,0,0}, // S_BLUETORCH2 + {SPR_TBLU,32770,4,NULL,S_BLUETORCH4,0,0}, // S_BLUETORCH3 + {SPR_TBLU,32771,4,NULL,S_BLUETORCH,0,0}, // S_BLUETORCH4 + {SPR_TGRN,32768,4,NULL,S_GREENTORCH2,0,0}, // S_GREENTORCH + {SPR_TGRN,32769,4,NULL,S_GREENTORCH3,0,0}, // S_GREENTORCH2 + {SPR_TGRN,32770,4,NULL,S_GREENTORCH4,0,0}, // S_GREENTORCH3 + {SPR_TGRN,32771,4,NULL,S_GREENTORCH,0,0}, // S_GREENTORCH4 + {SPR_TRED,32768,4,NULL,S_REDTORCH2,0,0}, // S_REDTORCH + {SPR_TRED,32769,4,NULL,S_REDTORCH3,0,0}, // S_REDTORCH2 + {SPR_TRED,32770,4,NULL,S_REDTORCH4,0,0}, // S_REDTORCH3 + {SPR_TRED,32771,4,NULL,S_REDTORCH,0,0}, // S_REDTORCH4 + {SPR_SMBT,32768,4,NULL,S_BTORCHSHRT2,0,0}, // S_BTORCHSHRT + {SPR_SMBT,32769,4,NULL,S_BTORCHSHRT3,0,0}, // S_BTORCHSHRT2 + {SPR_SMBT,32770,4,NULL,S_BTORCHSHRT4,0,0}, // S_BTORCHSHRT3 + {SPR_SMBT,32771,4,NULL,S_BTORCHSHRT,0,0}, // S_BTORCHSHRT4 + {SPR_SMGT,32768,4,NULL,S_GTORCHSHRT2,0,0}, // S_GTORCHSHRT + {SPR_SMGT,32769,4,NULL,S_GTORCHSHRT3,0,0}, // S_GTORCHSHRT2 + {SPR_SMGT,32770,4,NULL,S_GTORCHSHRT4,0,0}, // S_GTORCHSHRT3 + {SPR_SMGT,32771,4,NULL,S_GTORCHSHRT,0,0}, // S_GTORCHSHRT4 + {SPR_SMRT,32768,4,NULL,S_RTORCHSHRT2,0,0}, // S_RTORCHSHRT + {SPR_SMRT,32769,4,NULL,S_RTORCHSHRT3,0,0}, // S_RTORCHSHRT2 + {SPR_SMRT,32770,4,NULL,S_RTORCHSHRT4,0,0}, // S_RTORCHSHRT3 + {SPR_SMRT,32771,4,NULL,S_RTORCHSHRT,0,0}, // S_RTORCHSHRT4 + {SPR_HDB1,0,-1,NULL,S_NULL,0,0}, // S_HANGNOGUTS + {SPR_HDB2,0,-1,NULL,S_NULL,0,0}, // S_HANGBNOBRAIN + {SPR_HDB3,0,-1,NULL,S_NULL,0,0}, // S_HANGTLOOKDN + {SPR_HDB4,0,-1,NULL,S_NULL,0,0}, // S_HANGTSKULL + {SPR_HDB5,0,-1,NULL,S_NULL,0,0}, // S_HANGTLOOKUP + {SPR_HDB6,0,-1,NULL,S_NULL,0,0}, // S_HANGTNOBRAIN + {SPR_POB1,0,-1,NULL,S_NULL,0,0}, // S_COLONGIBS + {SPR_POB2,0,-1,NULL,S_NULL,0,0}, // S_SMALLPOOL + {SPR_BRS1,0,-1,NULL,S_NULL,0,0}, // S_BRAINSTEM + {SPR_TLMP,32768,4,NULL,S_TECHLAMP2,0,0}, // S_TECHLAMP + {SPR_TLMP,32769,4,NULL,S_TECHLAMP3,0,0}, // S_TECHLAMP2 + {SPR_TLMP,32770,4,NULL,S_TECHLAMP4,0,0}, // S_TECHLAMP3 + {SPR_TLMP,32771,4,NULL,S_TECHLAMP,0,0}, // S_TECHLAMP4 + {SPR_TLP2,32768,4,NULL,S_TECH2LAMP2,0,0}, // S_TECH2LAMP + {SPR_TLP2,32769,4,NULL,S_TECH2LAMP3,0,0}, // S_TECH2LAMP2 + {SPR_TLP2,32770,4,NULL,S_TECH2LAMP4,0,0}, // S_TECH2LAMP3 + {SPR_TLP2,32771,4,NULL,S_TECH2LAMP,0,0}, // S_TECH2LAMP4 + {SPR_TNT1,0,-1,NULL,S_TNT1,0,0}, // S_TNT1 // phares 3/8/98 + + // killough 8/9/98: grenade + {SPR_MISL,32768,1000,A_Die,S_GRENADE}, // S_GRENADE + + // killough 8/10/98: variable damage explosion + {SPR_MISL,32769,4,A_Scream,S_DETONATE2}, // S_DETONATE + {SPR_MISL,32770,6,A_Detonate,S_DETONATE3}, // S_DETONATE2 + {SPR_MISL,32771,10,NULL,S_NULL}, // S_DETONATE3 + + // if dogs are disabled, dummy states are required for dehacked compatibility + {0,0,-1,NULL,S_NULL}, // S_DOGS_STND + {0,0,-1,NULL,S_NULL}, // S_DOGS_STND2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN1 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN3 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN4 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN5 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN6 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN7 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RUN8 + {0,0,-1,NULL,S_NULL}, // S_DOGS_ATK1 + {0,0,-1,NULL,S_NULL}, // S_DOGS_ATK2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_ATK3 + {0,0,-1,NULL,S_NULL}, // S_DOGS_PAIN + {0,0,-1,NULL,S_NULL}, // S_DOGS_PAIN2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE1 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE3 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE4 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE5 + {0,0,-1,NULL,S_NULL}, // S_DOGS_DIE6 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE1 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE2 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE3 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE4 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE5 + {0,0,-1,NULL,S_NULL}, // S_DOGS_RAISE6 + + // add dummy beta bfg / lost soul frames for dehacked compatibility + // fixes bug #1576151 (part 2) + {0,0,-1,NULL,S_NULL}, // S_OLDBFG1 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG2 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG3 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG4 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG5 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG6 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG7 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG8 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG9 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG10 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG11 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG12 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG13 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG14 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG15 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG16 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG17 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG18 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG19 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG20 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG21 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG22 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG23 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG24 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG25 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG26 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG27 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG28 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG29 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG30 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG31 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG32 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG33 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG34 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG35 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG36 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG37 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG38 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG39 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG40 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG41 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG42 + {0,0,-1,NULL,S_NULL}, // S_OLDBFG43 + + {0,0,-1,NULL,S_NULL}, // S_PLS1BALL + {0,0,-1,NULL,S_NULL}, // S_PLS1BALL2 + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP2 + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP3 + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP4 + {0,0,-1,NULL,S_NULL}, // S_PLS1EXP5 + + {0,0,-1,NULL,S_NULL}, // S_PLS2BALL + {0,0,-1,NULL,S_NULL}, // S_PLS2BALL2 + {0,0,-1,NULL,S_NULL}, // S_PLS2BALLX1 + {0,0,-1,NULL,S_NULL}, // S_PLS2BALLX2 + {0,0,-1,NULL,S_NULL}, // S_PLS2BALLX3 + + {0,0,-1,NULL,S_NULL}, // S_BON3 + {0,0,-1,NULL,S_NULL}, // S_BON4 + + {0,0,-1,NULL,S_NULL}, // S_BSKUL_STND + {0,0,-1,NULL,S_NULL}, // S_BSKUL_RUN1 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_RUN2 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_RUN3 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_RUN4 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_ATK1 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_ATK2 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_ATK3 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_PAIN1 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_PAIN2 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_PAIN3 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE1 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE2 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE3 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE4 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE5 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE6 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE7 + {0,0,-1,NULL,S_NULL}, // S_BSKUL_DIE8 + + // killough 10/98: mushroom effect + {SPR_MISL,32769,8,A_Mushroom,S_EXPLODE2}, // S_MUSHROOM +}; + +// ******************************************************************** +// Object "Thing" definitions +// ******************************************************************** +// Now we get to the actual objects and their characteristics. If +// you've seen Dehacked, much of this is where the Bits are set, +// commented below as "flags", as well as where you wire in which +// frames are the beginning frames for near and far attack, death, +// and such. Sounds are hooked in here too, as well as how much +// mass, speed and so forth a Thing has. Everything you ever wanted +// to know... +// +// Like all this other stuff, the MT_* entries are enumerated in info.h +// +// Note that these are all just indices of the elements involved, and +// not real pointers to them. For example, the player's death sequence +// is S_PLAY_DIE1, which just evaluates to the index in the states[] +// array above, which actually knows what happens then and what the +// sprite looks like, if it makes noise or not, etc. +// +// Additional comments about each of the entries are located in info.h +// next to the mobjinfo_t structure definition. +// +// This goes on for the next 3000+ lines... + +mobjinfo_t mobjinfo[NUMMOBJTYPES] = { + { // MT_PLAYER + -1, // doomednum + S_PLAY, // spawnstate + 100, // spawnhealth + S_PLAY_RUN1, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_PLAY_PAIN, // painstate + 255, // painchance + sfx_plpain, // painsound + S_NULL, // meleestate + S_PLAY_ATK1, // missilestate + S_PLAY_DIE1, // deathstate + S_PLAY_XDIE1, // xdeathstate + sfx_pldeth, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_POSSESSED + 3004, // doomednum + S_POSS_STND, // spawnstate + 20, // spawnhealth + S_POSS_RUN1, // seestate + sfx_posit1, // seesound + 8, // reactiontime + sfx_pistol, // attacksound + S_POSS_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + 0, // meleestate + S_POSS_ATK1, // missilestate + S_POSS_DIE1, // deathstate + S_POSS_XDIE1, // xdeathstate + sfx_podth1, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_POSS_RAISE1 // raisestate + }, + + { // MT_SHOTGUY + 9, // doomednum + S_SPOS_STND, // spawnstate + 30, // spawnhealth + S_SPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_SPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SPOS_ATK1, // missilestate + S_SPOS_DIE1, // deathstate + S_SPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SPOS_RAISE1 // raisestate + }, + + { // MT_VILE + 64, // doomednum + S_VILE_STND, // spawnstate + 700, // spawnhealth + S_VILE_RUN1, // seestate + sfx_vilsit, // seesound + 8, // reactiontime + 0, // attacksound + S_VILE_PAIN, // painstate + 10, // painchance + sfx_vipain, // painsound + 0, // meleestate + S_VILE_ATK1, // missilestate + S_VILE_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_vildth, // deathsound + 15, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 500, // mass + 0, // damage + sfx_vilact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_FIRE + -1, // doomednum + S_FIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_UNDEAD + 66, // doomednum + S_SKEL_STND, // spawnstate + 300, // spawnhealth + S_SKEL_RUN1, // seestate + sfx_skesit, // seesound + 8, // reactiontime + 0, // attacksound + S_SKEL_PAIN, // painstate + 100, // painchance + sfx_popain, // painsound + S_SKEL_FIST1, // meleestate + S_SKEL_MISS1, // missilestate + S_SKEL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_skedth, // deathsound + 10, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 500, // mass + 0, // damage + sfx_skeact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SKEL_RAISE1 // raisestate + }, + + { // MT_TRACER + -1, // doomednum + S_TRACER, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_skeatk, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TRACEEXP1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 10*FRACUNIT, // speed + 11*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 10, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SMOKE + -1, // doomednum + S_SMOKE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_FATSO + 67, // doomednum + S_FATT_STND, // spawnstate + 600, // spawnhealth + S_FATT_RUN1, // seestate + sfx_mansit, // seesound + 8, // reactiontime + 0, // attacksound + S_FATT_PAIN, // painstate + 80, // painchance + sfx_mnpain, // painsound + 0, // meleestate + S_FATT_ATK1, // missilestate + S_FATT_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_mandth, // deathsound + 8, // speed + 48*FRACUNIT, // radius + 64*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_FATT_RAISE1 // raisestate + }, + + { // MT_FATSHOT + -1, // doomednum + S_FATSHOT1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_FATSHOTX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 20*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags \\ killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_CHAINGUY + 65, // doomednum + S_CPOS_STND, // spawnstate + 70, // spawnhealth + S_CPOS_RUN1, // seestate + sfx_posit2, // seesound + 8, // reactiontime + 0, // attacksound + S_CPOS_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_CPOS_ATK1, // missilestate + S_CPOS_DIE1, // deathstate + S_CPOS_XDIE1, // xdeathstate + sfx_podth2, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_CPOS_RAISE1 // raisestate + }, + + { // MT_TROOP + 3001, // doomednum + S_TROO_STND, // spawnstate + 60, // spawnhealth + S_TROO_RUN1, // seestate + sfx_bgsit1, // seesound + 8, // reactiontime + 0, // attacksound + S_TROO_PAIN, // painstate + 200, // painchance + sfx_popain, // painsound + S_TROO_ATK1, // meleestate + S_TROO_ATK1, // missilestate + S_TROO_DIE1, // deathstate + S_TROO_XDIE1, // xdeathstate + sfx_bgdth1, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_bgact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // killough |MF_TRANSLUCENT, // flags // phares + S_TROO_RAISE1 // raisestate + }, + + { // MT_SERGEANT + 3002, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_SHADOWS + 58, // doomednum + S_SARG_STND, // spawnstate + 150, // spawnhealth + S_SARG_RUN1, // seestate + sfx_sgtsit, // seesound + 8, // reactiontime + sfx_sgtatk, // attacksound + S_SARG_PAIN, // painstate + 180, // painchance + sfx_dmpain, // painsound + S_SARG_ATK1, // meleestate + 0, // missilestate + S_SARG_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_sgtdth, // deathsound + 10, // speed + 30*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_SHADOW|MF_COUNTKILL, // flags + S_SARG_RAISE1 // raisestate + }, + + { // MT_HEAD + 3005, // doomednum + S_HEAD_STND, // spawnstate + 400, // spawnhealth + S_HEAD_RUN1, // seestate + sfx_cacsit, // seesound + 8, // reactiontime + 0, // attacksound + S_HEAD_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_HEAD_ATK1, // missilestate + S_HEAD_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cacdth, // deathsound + 8, // speed + 31*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL, // flags + S_HEAD_RAISE1 // raisestate + }, + + { // MT_BRUISER + 3003, // doomednum + S_BOSS_STND, // spawnstate + 1000, // spawnhealth + S_BOSS_RUN1, // seestate + sfx_brssit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOSS_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOSS_ATK1, // meleestate + S_BOSS_ATK1, // missilestate + S_BOSS_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_brsdth, // deathsound + 8, // speed + 24*FRACUNIT, // radius + 64*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_BOSS_RAISE1 // raisestate + }, + + { // MT_BRUISERSHOT + -1, // doomednum + S_BRBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 15*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 8, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_KNIGHT + 69, // doomednum + S_BOS2_STND, // spawnstate + 500, // spawnhealth + S_BOS2_RUN1, // seestate + sfx_kntsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BOS2_PAIN, // painstate + 50, // painchance + sfx_dmpain, // painsound + S_BOS2_ATK1, // meleestate + S_BOS2_ATK1, // missilestate + S_BOS2_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_kntdth, // deathsound + 8, // speed + 24*FRACUNIT, // radius + 64*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_BOS2_RAISE1 // raisestate + }, + + { // MT_SKULL + 3006, // doomednum + S_SKULL_STND, // spawnstate + 100, // spawnhealth + S_SKULL_RUN1, // seestate + 0, // seesound + 8, // reactiontime + sfx_sklatk, // attacksound + S_SKULL_PAIN, // painstate + 256, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SKULL_ATK1, // missilestate + S_SKULL_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 8, // speed + 16*FRACUNIT, // radius + 56*FRACUNIT, // height + 50, // mass + 3, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_SPIDER + 7, // doomednum + S_SPID_STND, // spawnstate + 3000, // spawnhealth + S_SPID_RUN1, // seestate + sfx_spisit, // seesound + 8, // reactiontime + sfx_shotgn, // attacksound + S_SPID_PAIN, // painstate + 40, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_SPID_ATK1, // missilestate + S_SPID_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_spidth, // deathsound + 12, // speed + 128*FRACUNIT, // radius + 100*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BABY + 68, // doomednum + S_BSPI_STND, // spawnstate + 500, // spawnhealth + S_BSPI_SIGHT, // seestate + sfx_bspsit, // seesound + 8, // reactiontime + 0, // attacksound + S_BSPI_PAIN, // painstate + 128, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_BSPI_ATK1, // missilestate + S_BSPI_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bspdth, // deathsound + 12, // speed + 64*FRACUNIT, // radius + 64*FRACUNIT, // height + 600, // mass + 0, // damage + sfx_bspact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_BSPI_RAISE1 // raisestate + }, + + { // MT_CYBORG + 16, // doomednum + S_CYBER_STND, // spawnstate + 4000, // spawnhealth + S_CYBER_RUN1, // seestate + sfx_cybsit, // seesound + 8, // reactiontime + 0, // attacksound + S_CYBER_PAIN, // painstate + 20, // painchance + sfx_dmpain, // painsound + 0, // meleestate + S_CYBER_ATK1, // missilestate + S_CYBER_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_cybdth, // deathsound + 16, // speed + 40*FRACUNIT, // radius + 110*FRACUNIT, // height + 1000, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_PAIN + 71, // doomednum + S_PAIN_STND, // spawnstate + 400, // spawnhealth + S_PAIN_RUN1, // seestate + sfx_pesit, // seesound + 8, // reactiontime + 0, // attacksound + S_PAIN_PAIN, // painstate + 128, // painchance + sfx_pepain, // painsound + 0, // meleestate + S_PAIN_ATK1, // missilestate + S_PAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_pedth, // deathsound + 8, // speed + 31*FRACUNIT, // radius + 56*FRACUNIT, // height + 400, // mass + 0, // damage + sfx_dmact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_FLOAT|MF_NOGRAVITY|MF_COUNTKILL, // flags + S_PAIN_RAISE1 // raisestate + }, + + { // MT_WOLFSS + 84, // doomednum + S_SSWV_STND, // spawnstate + 50, // spawnhealth + S_SSWV_RUN1, // seestate + sfx_sssit, // seesound + 8, // reactiontime + 0, // attacksound + S_SSWV_PAIN, // painstate + 170, // painchance + sfx_popain, // painsound + 0, // meleestate + S_SSWV_ATK1, // missilestate + S_SSWV_DIE1, // deathstate + S_SSWV_XDIE1, // xdeathstate + sfx_ssdth, // deathsound + 8, // speed + 20*FRACUNIT, // radius + 56*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_posact, // activesound + MF_SOLID|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_SSWV_RAISE1 // raisestate + }, + + { // MT_KEEN + 72, // doomednum + S_KEENSTND, // spawnstate + 100, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_KEENPAIN, // painstate + 256, // painchance + sfx_keenpn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_COMMKEEN, // deathstate + S_NULL, // xdeathstate + sfx_keendt, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 72*FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY|MF_SHOOTABLE|MF_COUNTKILL, // flags + S_NULL // raisestate + }, + + { // MT_BOSSBRAIN + 88, // doomednum + S_BRAIN, // spawnstate + 250, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_BRAIN_PAIN, // painstate + 255, // painchance + sfx_bospn, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BRAIN_DIE1, // deathstate + S_NULL, // xdeathstate + sfx_bosdth, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 10000000, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SHOOTABLE, // flags + S_NULL // raisestate + }, + + { // MT_BOSSSPIT + 89, // doomednum + S_BRAINEYE, // spawnstate + 1000, // spawnhealth + S_BRAINEYESEE, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 32*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_BOSSTARGET + 87, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 32*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNSHOT + -1, // doomednum + S_SPAWN1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_bospit, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10*FRACUNIT, // speed + 6*FRACUNIT, // radius + 32*FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_NOCLIP, // flags + S_NULL // raisestate + }, + + { // MT_SPAWNFIRE + -1, // doomednum + S_SPAWNFIRE1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_BARREL + 2035, // doomednum + S_BAR1, // spawnstate + 20, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BEXP, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 0, // speed + 10*FRACUNIT, // radius + 42*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SHOOTABLE|MF_NOBLOOD, // flags + S_NULL // raisestate + }, + + { // MT_TROOPSHOT + -1, // doomednum + S_TBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_TBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 3, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_HEADSHOT + -1, // doomednum + S_RBALL1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_firsht, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_RBALLX1, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 10*FRACUNIT, // speed + 6*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares, // flags + S_NULL // raisestate + }, + + { // MT_ROCKET + -1, // doomednum + S_ROCKET, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_rlaunc, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_EXPLODE1, // deathstate + S_NULL, // xdeathstate + sfx_barexp, // deathsound + 20*FRACUNIT, // speed + 11*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 20, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_PLASMA + -1, // doomednum + S_PLASBALL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_PLASEXP, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_BFG + -1, // doomednum + S_BFGSHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + 0, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_BFGLAND, // deathstate + S_NULL, // xdeathstate + sfx_rxplod, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 100, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_ARACHPLAZ + -1, // doomednum + S_ARACH_PLAZ, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_plasma, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_ARACH_PLEX, // deathstate + S_NULL, // xdeathstate + sfx_firxpl, // deathsound + 25*FRACUNIT, // speed + 13*FRACUNIT, // radius + 8*FRACUNIT, // height + 100, // mass + 5, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_MISSILE|MF_DROPOFF|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_PUFF + -1, // doomednum + S_PUFF1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_BLOOD + -1, // doomednum + S_BLOOD1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_TFOG + -1, // doomednum + S_TFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_IFOG + -1, // doomednum + S_IFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_TRANSLUCENT, // flags // phares + S_NULL // raisestate + }, + + { // MT_TELEPORTMAN + 14, // doomednum + S_NULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOSECTOR, // flags + S_NULL // raisestate + }, + + { // MT_EXTRABFG + -1, // doomednum + S_BFGEXP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC0 + 2018, // doomednum + S_ARM1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC1 + 2019, // doomednum + S_ARM2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC2 + 2014, // doomednum + S_BON1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC3 + 2015, // doomednum + S_BON2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC4 + 5, // doomednum + S_BKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC5 + 13, // doomednum + S_RKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC6 + 6, // doomednum + S_YKEY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC7 + 39, // doomednum + S_YSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC8 + 38, // doomednum + S_RSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC9 + 40, // doomednum + S_BSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOTDMATCH, // flags + S_NULL // raisestate + }, + + { // MT_MISC10 + 2011, // doomednum + S_STIM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC11 + 2012, // doomednum + S_MEDI, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC12 + 2013, // doomednum + S_SOUL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_INV + 2022, // doomednum + S_PINV, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_MISC13 + 2023, // doomednum + S_PSTR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_INS + 2024, // doomednum + S_PINS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_MISC14 + 2025, // doomednum + S_SUIT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC15 + 2026, // doomednum + S_PMAP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MISC16 + 2045, // doomednum + S_PVIS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM, // flags + S_NULL // raisestate + }, + + { // MT_MEGA + 83, // doomednum + S_MEGA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_COUNTITEM|MF_TRANSLUCENT, // flags // killough 2/21/98 + S_NULL // raisestate + }, + + { // MT_CLIP + 2007, // doomednum + S_CLIP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC17 + 2048, // doomednum + S_AMMO, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC18 + 2010, // doomednum + S_ROCK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC19 + 2046, // doomednum + S_BROK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC20 + 2047, // doomednum + S_CELL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC21 + 17, // doomednum + S_CELP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC22 + 2008, // doomednum + S_SHEL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC23 + 2049, // doomednum + S_SBOX, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC24 + 8, // doomednum + S_BPAK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC25 + 2006, // doomednum + S_BFUG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_CHAINGUN + 2002, // doomednum + S_MGUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC26 + 2005, // doomednum + S_CSAW, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC27 + 2003, // doomednum + S_LAUN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC28 + 2004, // doomednum + S_PLAS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SHOTGUN + 2001, // doomednum + S_SHOT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_SUPERSHOTGUN + 82, // doomednum + S_SHOT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL, // flags + S_NULL // raisestate + }, + + { // MT_MISC29 + 85, // doomednum + S_TECHLAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC30 + 86, // doomednum + S_TECH2LAMP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC31 + 2028, // doomednum + S_COLU, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC32 + 30, // doomednum + S_TALLGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC33 + 31, // doomednum + S_SHRTGRNCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC34 + 32, // doomednum + S_TALLREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC35 + 33, // doomednum + S_SHRTREDCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC36 + 37, // doomednum + S_SKULLCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC37 + 36, // doomednum + S_HEARTCOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC38 + 41, // doomednum + S_EVILEYE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC39 + 42, // doomednum + S_FLOATSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC40 + 43, // doomednum + S_TORCHTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC41 + 44, // doomednum + S_BLUETORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC42 + 45, // doomednum + S_GREENTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC43 + 46, // doomednum + S_REDTORCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC44 + 55, // doomednum + S_BTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC45 + 56, // doomednum + S_GTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC46 + 57, // doomednum + S_RTORCHSHRT, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC47 + 47, // doomednum + S_STALAGTITE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC48 + 48, // doomednum + S_TECHPILLAR, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC49 + 34, // doomednum + S_CANDLESTIK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC50 + 35, // doomednum + S_CANDELABRA, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC51 + 49, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC52 + 50, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 84*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC53 + 51, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 84*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC54 + 52, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC55 + 53, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 52*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC56 + 59, // doomednum + S_MEAT2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 84*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC57 + 60, // doomednum + S_MEAT4, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC58 + 61, // doomednum + S_MEAT3, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 52*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC59 + 62, // doomednum + S_MEAT5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 52*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC60 + 63, // doomednum + S_BLOODYTWITCH, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 68*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC61 + 22, // doomednum + S_HEAD_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC62 + 15, // doomednum + S_PLAY_DIE7, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC63 + 18, // doomednum + S_POSS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC64 + 21, // doomednum + S_SARG_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC65 + 23, // doomednum + S_SKULL_DIE6, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC66 + 20, // doomednum + S_TROO_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC67 + 19, // doomednum + S_SPOS_DIE5, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC68 + 10, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC69 + 12, // doomednum + S_PLAY_XDIE9, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC70 + 28, // doomednum + S_HEADSONSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC71 + 24, // doomednum + S_GIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + 0, // flags + S_NULL // raisestate + }, + + { // MT_MISC72 + 27, // doomednum + S_HEADONASTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC73 + 29, // doomednum + S_HEADCANDLES, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC74 + 25, // doomednum + S_DEADSTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC75 + 26, // doomednum + S_LIVESTICK, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC76 + 54, // doomednum + S_BIGTREE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 32*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC77 + 70, // doomednum + S_BBAR1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_MISC78 + 73, // doomednum + S_HANGNOGUTS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 88*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC79 + 74, // doomednum + S_HANGBNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 88*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC80 + 75, // doomednum + S_HANGTLOOKDN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC81 + 76, // doomednum + S_HANGTSKULL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC82 + 77, // doomednum + S_HANGTLOOKUP, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC83 + 78, // doomednum + S_HANGTNOBRAIN, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 16*FRACUNIT, // radius + 64*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID|MF_SPAWNCEILING|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + + { // MT_MISC84 + 79, // doomednum + S_COLONGIBS, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC85 + 80, // doomednum + S_SMALLPOOL, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + { // MT_MISC86 + 81, // doomednum + S_BRAINSTEM, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 20*FRACUNIT, // radius + 16*FRACUNIT, // height + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + // For use with wind and current effects + { // MT_PUSH // phares + 5001, // doomednum // | //jff 5/11/98 deconflict + S_TNT1, // spawnstate // V // with DOSDoom + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8, // radius + 8, // height + 10, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, + + // For use with wind and current effects + { // MT_PULL + 5002, // doomednum //jff 5/11/98 deconflict + S_TNT1, // spawnstate // with DOSDoom + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8, // radius + 8, // height + 10, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP, // flags + S_NULL // raisestate + }, +}; diff --git a/src/info.h b/src/info.h new file mode 100644 index 00000000..eb05d434 --- /dev/null +++ b/src/info.h @@ -0,0 +1,1490 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thing frame/state LUT, + * generated by multigen utilitiy. + * This one is the original DOOM version, preserved. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __INFO__ +#define __INFO__ + +/* Needed for action function pointer handling. */ +#include "d_think.h" + +/******************************************************************** + * Sprite name enumeration - must match info.c * + ********************************************************************/ +typedef enum +{ + SPR_TROO, + SPR_SHTG, + SPR_PUNG, + SPR_PISG, + SPR_PISF, + SPR_SHTF, + SPR_SHT2, + SPR_CHGG, + SPR_CHGF, + SPR_MISG, + SPR_MISF, + SPR_SAWG, + SPR_PLSG, + SPR_PLSF, + SPR_BFGG, + SPR_BFGF, + SPR_BLUD, + SPR_PUFF, + SPR_BAL1, + SPR_BAL2, + SPR_PLSS, + SPR_PLSE, + SPR_MISL, + SPR_BFS1, + SPR_BFE1, + SPR_BFE2, + SPR_TFOG, + SPR_IFOG, + SPR_PLAY, + SPR_POSS, + SPR_SPOS, + SPR_VILE, + SPR_FIRE, + SPR_FATB, + SPR_FBXP, + SPR_SKEL, + SPR_MANF, + SPR_FATT, + SPR_CPOS, + SPR_SARG, + SPR_HEAD, + SPR_BAL7, + SPR_BOSS, + SPR_BOS2, + SPR_SKUL, + SPR_SPID, + SPR_BSPI, + SPR_APLS, + SPR_APBX, + SPR_CYBR, + SPR_PAIN, + SPR_SSWV, + SPR_KEEN, + SPR_BBRN, + SPR_BOSF, + SPR_ARM1, + SPR_ARM2, + SPR_BAR1, + SPR_BEXP, + SPR_FCAN, + SPR_BON1, + SPR_BON2, + SPR_BKEY, + SPR_RKEY, + SPR_YKEY, + SPR_BSKU, + SPR_RSKU, + SPR_YSKU, + SPR_STIM, + SPR_MEDI, + SPR_SOUL, + SPR_PINV, + SPR_PSTR, + SPR_PINS, + SPR_MEGA, + SPR_SUIT, + SPR_PMAP, + SPR_PVIS, + SPR_CLIP, + SPR_AMMO, + SPR_ROCK, + SPR_BROK, + SPR_CELL, + SPR_CELP, + SPR_SHEL, + SPR_SBOX, + SPR_BPAK, + SPR_BFUG, + SPR_MGUN, + SPR_CSAW, + SPR_LAUN, + SPR_PLAS, + SPR_SHOT, + SPR_SGN2, + SPR_COLU, + SPR_SMT2, + SPR_GOR1, + SPR_POL2, + SPR_POL5, + SPR_POL4, + SPR_POL3, + SPR_POL1, + SPR_POL6, + SPR_GOR2, + SPR_GOR3, + SPR_GOR4, + SPR_GOR5, + SPR_SMIT, + SPR_COL1, + SPR_COL2, + SPR_COL3, + SPR_COL4, + SPR_CAND, + SPR_CBRA, + SPR_COL6, + SPR_TRE1, + SPR_TRE2, + SPR_ELEC, + SPR_CEYE, + SPR_FSKU, + SPR_COL5, + SPR_TBLU, + SPR_TGRN, + SPR_TRED, + SPR_SMBT, + SPR_SMGT, + SPR_SMRT, + SPR_HDB1, + SPR_HDB2, + SPR_HDB3, + SPR_HDB4, + SPR_HDB5, + SPR_HDB6, + SPR_POB1, + SPR_POB2, + SPR_BRS1, + SPR_TLMP, + SPR_TLP2, + SPR_TNT1, /* add invisible sprite phares 3/8/98 */ + + NUMSPRITES /* counter of how many there are */ + +} spritenum_t; + +/******************************************************************** + * States (frames) enumeration -- must match info.c * + ********************************************************************/ + +typedef enum +{ + S_NULL, + S_LIGHTDONE, + S_PUNCH, + S_PUNCHDOWN, + S_PUNCHUP, + S_PUNCH1, + S_PUNCH2, + S_PUNCH3, + S_PUNCH4, + S_PUNCH5, + S_PISTOL, + S_PISTOLDOWN, + S_PISTOLUP, + S_PISTOL1, + S_PISTOL2, + S_PISTOL3, + S_PISTOL4, + S_PISTOLFLASH, + S_SGUN, + S_SGUNDOWN, + S_SGUNUP, + S_SGUN1, + S_SGUN2, + S_SGUN3, + S_SGUN4, + S_SGUN5, + S_SGUN6, + S_SGUN7, + S_SGUN8, + S_SGUN9, + S_SGUNFLASH1, + S_SGUNFLASH2, + S_DSGUN, + S_DSGUNDOWN, + S_DSGUNUP, + S_DSGUN1, + S_DSGUN2, + S_DSGUN3, + S_DSGUN4, + S_DSGUN5, + S_DSGUN6, + S_DSGUN7, + S_DSGUN8, + S_DSGUN9, + S_DSGUN10, + S_DSNR1, + S_DSNR2, + S_DSGUNFLASH1, + S_DSGUNFLASH2, + S_CHAIN, + S_CHAINDOWN, + S_CHAINUP, + S_CHAIN1, + S_CHAIN2, + S_CHAIN3, + S_CHAINFLASH1, + S_CHAINFLASH2, + S_MISSILE, + S_MISSILEDOWN, + S_MISSILEUP, + S_MISSILE1, + S_MISSILE2, + S_MISSILE3, + S_MISSILEFLASH1, + S_MISSILEFLASH2, + S_MISSILEFLASH3, + S_MISSILEFLASH4, + S_SAW, + S_SAWB, + S_SAWDOWN, + S_SAWUP, + S_SAW1, + S_SAW2, + S_SAW3, + S_PLASMA, + S_PLASMADOWN, + S_PLASMAUP, + S_PLASMA1, + S_PLASMA2, + S_PLASMAFLASH1, + S_PLASMAFLASH2, + S_BFG, + S_BFGDOWN, + S_BFGUP, + S_BFG1, + S_BFG2, + S_BFG3, + S_BFG4, + S_BFGFLASH1, + S_BFGFLASH2, + S_BLOOD1, + S_BLOOD2, + S_BLOOD3, + S_PUFF1, + S_PUFF2, + S_PUFF3, + S_PUFF4, + S_TBALL1, + S_TBALL2, + S_TBALLX1, + S_TBALLX2, + S_TBALLX3, + S_RBALL1, + S_RBALL2, + S_RBALLX1, + S_RBALLX2, + S_RBALLX3, + S_PLASBALL, + S_PLASBALL2, + S_PLASEXP, + S_PLASEXP2, + S_PLASEXP3, + S_PLASEXP4, + S_PLASEXP5, + S_ROCKET, + S_BFGSHOT, + S_BFGSHOT2, + S_BFGLAND, + S_BFGLAND2, + S_BFGLAND3, + S_BFGLAND4, + S_BFGLAND5, + S_BFGLAND6, + S_BFGEXP, + S_BFGEXP2, + S_BFGEXP3, + S_BFGEXP4, + S_EXPLODE1, + S_EXPLODE2, + S_EXPLODE3, + S_TFOG, + S_TFOG01, + S_TFOG02, + S_TFOG2, + S_TFOG3, + S_TFOG4, + S_TFOG5, + S_TFOG6, + S_TFOG7, + S_TFOG8, + S_TFOG9, + S_TFOG10, + S_IFOG, + S_IFOG01, + S_IFOG02, + S_IFOG2, + S_IFOG3, + S_IFOG4, + S_IFOG5, + S_PLAY, + S_PLAY_RUN1, + S_PLAY_RUN2, + S_PLAY_RUN3, + S_PLAY_RUN4, + S_PLAY_ATK1, + S_PLAY_ATK2, + S_PLAY_PAIN, + S_PLAY_PAIN2, + S_PLAY_DIE1, + S_PLAY_DIE2, + S_PLAY_DIE3, + S_PLAY_DIE4, + S_PLAY_DIE5, + S_PLAY_DIE6, + S_PLAY_DIE7, + S_PLAY_XDIE1, + S_PLAY_XDIE2, + S_PLAY_XDIE3, + S_PLAY_XDIE4, + S_PLAY_XDIE5, + S_PLAY_XDIE6, + S_PLAY_XDIE7, + S_PLAY_XDIE8, + S_PLAY_XDIE9, + S_POSS_STND, + S_POSS_STND2, + S_POSS_RUN1, + S_POSS_RUN2, + S_POSS_RUN3, + S_POSS_RUN4, + S_POSS_RUN5, + S_POSS_RUN6, + S_POSS_RUN7, + S_POSS_RUN8, + S_POSS_ATK1, + S_POSS_ATK2, + S_POSS_ATK3, + S_POSS_PAIN, + S_POSS_PAIN2, + S_POSS_DIE1, + S_POSS_DIE2, + S_POSS_DIE3, + S_POSS_DIE4, + S_POSS_DIE5, + S_POSS_XDIE1, + S_POSS_XDIE2, + S_POSS_XDIE3, + S_POSS_XDIE4, + S_POSS_XDIE5, + S_POSS_XDIE6, + S_POSS_XDIE7, + S_POSS_XDIE8, + S_POSS_XDIE9, + S_POSS_RAISE1, + S_POSS_RAISE2, + S_POSS_RAISE3, + S_POSS_RAISE4, + S_SPOS_STND, + S_SPOS_STND2, + S_SPOS_RUN1, + S_SPOS_RUN2, + S_SPOS_RUN3, + S_SPOS_RUN4, + S_SPOS_RUN5, + S_SPOS_RUN6, + S_SPOS_RUN7, + S_SPOS_RUN8, + S_SPOS_ATK1, + S_SPOS_ATK2, + S_SPOS_ATK3, + S_SPOS_PAIN, + S_SPOS_PAIN2, + S_SPOS_DIE1, + S_SPOS_DIE2, + S_SPOS_DIE3, + S_SPOS_DIE4, + S_SPOS_DIE5, + S_SPOS_XDIE1, + S_SPOS_XDIE2, + S_SPOS_XDIE3, + S_SPOS_XDIE4, + S_SPOS_XDIE5, + S_SPOS_XDIE6, + S_SPOS_XDIE7, + S_SPOS_XDIE8, + S_SPOS_XDIE9, + S_SPOS_RAISE1, + S_SPOS_RAISE2, + S_SPOS_RAISE3, + S_SPOS_RAISE4, + S_SPOS_RAISE5, + S_VILE_STND, + S_VILE_STND2, + S_VILE_RUN1, + S_VILE_RUN2, + S_VILE_RUN3, + S_VILE_RUN4, + S_VILE_RUN5, + S_VILE_RUN6, + S_VILE_RUN7, + S_VILE_RUN8, + S_VILE_RUN9, + S_VILE_RUN10, + S_VILE_RUN11, + S_VILE_RUN12, + S_VILE_ATK1, + S_VILE_ATK2, + S_VILE_ATK3, + S_VILE_ATK4, + S_VILE_ATK5, + S_VILE_ATK6, + S_VILE_ATK7, + S_VILE_ATK8, + S_VILE_ATK9, + S_VILE_ATK10, + S_VILE_ATK11, + S_VILE_HEAL1, + S_VILE_HEAL2, + S_VILE_HEAL3, + S_VILE_PAIN, + S_VILE_PAIN2, + S_VILE_DIE1, + S_VILE_DIE2, + S_VILE_DIE3, + S_VILE_DIE4, + S_VILE_DIE5, + S_VILE_DIE6, + S_VILE_DIE7, + S_VILE_DIE8, + S_VILE_DIE9, + S_VILE_DIE10, + S_FIRE1, + S_FIRE2, + S_FIRE3, + S_FIRE4, + S_FIRE5, + S_FIRE6, + S_FIRE7, + S_FIRE8, + S_FIRE9, + S_FIRE10, + S_FIRE11, + S_FIRE12, + S_FIRE13, + S_FIRE14, + S_FIRE15, + S_FIRE16, + S_FIRE17, + S_FIRE18, + S_FIRE19, + S_FIRE20, + S_FIRE21, + S_FIRE22, + S_FIRE23, + S_FIRE24, + S_FIRE25, + S_FIRE26, + S_FIRE27, + S_FIRE28, + S_FIRE29, + S_FIRE30, + S_SMOKE1, + S_SMOKE2, + S_SMOKE3, + S_SMOKE4, + S_SMOKE5, + S_TRACER, + S_TRACER2, + S_TRACEEXP1, + S_TRACEEXP2, + S_TRACEEXP3, + S_SKEL_STND, + S_SKEL_STND2, + S_SKEL_RUN1, + S_SKEL_RUN2, + S_SKEL_RUN3, + S_SKEL_RUN4, + S_SKEL_RUN5, + S_SKEL_RUN6, + S_SKEL_RUN7, + S_SKEL_RUN8, + S_SKEL_RUN9, + S_SKEL_RUN10, + S_SKEL_RUN11, + S_SKEL_RUN12, + S_SKEL_FIST1, + S_SKEL_FIST2, + S_SKEL_FIST3, + S_SKEL_FIST4, + S_SKEL_MISS1, + S_SKEL_MISS2, + S_SKEL_MISS3, + S_SKEL_MISS4, + S_SKEL_PAIN, + S_SKEL_PAIN2, + S_SKEL_DIE1, + S_SKEL_DIE2, + S_SKEL_DIE3, + S_SKEL_DIE4, + S_SKEL_DIE5, + S_SKEL_DIE6, + S_SKEL_RAISE1, + S_SKEL_RAISE2, + S_SKEL_RAISE3, + S_SKEL_RAISE4, + S_SKEL_RAISE5, + S_SKEL_RAISE6, + S_FATSHOT1, + S_FATSHOT2, + S_FATSHOTX1, + S_FATSHOTX2, + S_FATSHOTX3, + S_FATT_STND, + S_FATT_STND2, + S_FATT_RUN1, + S_FATT_RUN2, + S_FATT_RUN3, + S_FATT_RUN4, + S_FATT_RUN5, + S_FATT_RUN6, + S_FATT_RUN7, + S_FATT_RUN8, + S_FATT_RUN9, + S_FATT_RUN10, + S_FATT_RUN11, + S_FATT_RUN12, + S_FATT_ATK1, + S_FATT_ATK2, + S_FATT_ATK3, + S_FATT_ATK4, + S_FATT_ATK5, + S_FATT_ATK6, + S_FATT_ATK7, + S_FATT_ATK8, + S_FATT_ATK9, + S_FATT_ATK10, + S_FATT_PAIN, + S_FATT_PAIN2, + S_FATT_DIE1, + S_FATT_DIE2, + S_FATT_DIE3, + S_FATT_DIE4, + S_FATT_DIE5, + S_FATT_DIE6, + S_FATT_DIE7, + S_FATT_DIE8, + S_FATT_DIE9, + S_FATT_DIE10, + S_FATT_RAISE1, + S_FATT_RAISE2, + S_FATT_RAISE3, + S_FATT_RAISE4, + S_FATT_RAISE5, + S_FATT_RAISE6, + S_FATT_RAISE7, + S_FATT_RAISE8, + S_CPOS_STND, + S_CPOS_STND2, + S_CPOS_RUN1, + S_CPOS_RUN2, + S_CPOS_RUN3, + S_CPOS_RUN4, + S_CPOS_RUN5, + S_CPOS_RUN6, + S_CPOS_RUN7, + S_CPOS_RUN8, + S_CPOS_ATK1, + S_CPOS_ATK2, + S_CPOS_ATK3, + S_CPOS_ATK4, + S_CPOS_PAIN, + S_CPOS_PAIN2, + S_CPOS_DIE1, + S_CPOS_DIE2, + S_CPOS_DIE3, + S_CPOS_DIE4, + S_CPOS_DIE5, + S_CPOS_DIE6, + S_CPOS_DIE7, + S_CPOS_XDIE1, + S_CPOS_XDIE2, + S_CPOS_XDIE3, + S_CPOS_XDIE4, + S_CPOS_XDIE5, + S_CPOS_XDIE6, + S_CPOS_RAISE1, + S_CPOS_RAISE2, + S_CPOS_RAISE3, + S_CPOS_RAISE4, + S_CPOS_RAISE5, + S_CPOS_RAISE6, + S_CPOS_RAISE7, + S_TROO_STND, + S_TROO_STND2, + S_TROO_RUN1, + S_TROO_RUN2, + S_TROO_RUN3, + S_TROO_RUN4, + S_TROO_RUN5, + S_TROO_RUN6, + S_TROO_RUN7, + S_TROO_RUN8, + S_TROO_ATK1, + S_TROO_ATK2, + S_TROO_ATK3, + S_TROO_PAIN, + S_TROO_PAIN2, + S_TROO_DIE1, + S_TROO_DIE2, + S_TROO_DIE3, + S_TROO_DIE4, + S_TROO_DIE5, + S_TROO_XDIE1, + S_TROO_XDIE2, + S_TROO_XDIE3, + S_TROO_XDIE4, + S_TROO_XDIE5, + S_TROO_XDIE6, + S_TROO_XDIE7, + S_TROO_XDIE8, + S_TROO_RAISE1, + S_TROO_RAISE2, + S_TROO_RAISE3, + S_TROO_RAISE4, + S_TROO_RAISE5, + S_SARG_STND, + S_SARG_STND2, + S_SARG_RUN1, + S_SARG_RUN2, + S_SARG_RUN3, + S_SARG_RUN4, + S_SARG_RUN5, + S_SARG_RUN6, + S_SARG_RUN7, + S_SARG_RUN8, + S_SARG_ATK1, + S_SARG_ATK2, + S_SARG_ATK3, + S_SARG_PAIN, + S_SARG_PAIN2, + S_SARG_DIE1, + S_SARG_DIE2, + S_SARG_DIE3, + S_SARG_DIE4, + S_SARG_DIE5, + S_SARG_DIE6, + S_SARG_RAISE1, + S_SARG_RAISE2, + S_SARG_RAISE3, + S_SARG_RAISE4, + S_SARG_RAISE5, + S_SARG_RAISE6, + S_HEAD_STND, + S_HEAD_RUN1, + S_HEAD_ATK1, + S_HEAD_ATK2, + S_HEAD_ATK3, + S_HEAD_PAIN, + S_HEAD_PAIN2, + S_HEAD_PAIN3, + S_HEAD_DIE1, + S_HEAD_DIE2, + S_HEAD_DIE3, + S_HEAD_DIE4, + S_HEAD_DIE5, + S_HEAD_DIE6, + S_HEAD_RAISE1, + S_HEAD_RAISE2, + S_HEAD_RAISE3, + S_HEAD_RAISE4, + S_HEAD_RAISE5, + S_HEAD_RAISE6, + S_BRBALL1, + S_BRBALL2, + S_BRBALLX1, + S_BRBALLX2, + S_BRBALLX3, + S_BOSS_STND, + S_BOSS_STND2, + S_BOSS_RUN1, + S_BOSS_RUN2, + S_BOSS_RUN3, + S_BOSS_RUN4, + S_BOSS_RUN5, + S_BOSS_RUN6, + S_BOSS_RUN7, + S_BOSS_RUN8, + S_BOSS_ATK1, + S_BOSS_ATK2, + S_BOSS_ATK3, + S_BOSS_PAIN, + S_BOSS_PAIN2, + S_BOSS_DIE1, + S_BOSS_DIE2, + S_BOSS_DIE3, + S_BOSS_DIE4, + S_BOSS_DIE5, + S_BOSS_DIE6, + S_BOSS_DIE7, + S_BOSS_RAISE1, + S_BOSS_RAISE2, + S_BOSS_RAISE3, + S_BOSS_RAISE4, + S_BOSS_RAISE5, + S_BOSS_RAISE6, + S_BOSS_RAISE7, + S_BOS2_STND, + S_BOS2_STND2, + S_BOS2_RUN1, + S_BOS2_RUN2, + S_BOS2_RUN3, + S_BOS2_RUN4, + S_BOS2_RUN5, + S_BOS2_RUN6, + S_BOS2_RUN7, + S_BOS2_RUN8, + S_BOS2_ATK1, + S_BOS2_ATK2, + S_BOS2_ATK3, + S_BOS2_PAIN, + S_BOS2_PAIN2, + S_BOS2_DIE1, + S_BOS2_DIE2, + S_BOS2_DIE3, + S_BOS2_DIE4, + S_BOS2_DIE5, + S_BOS2_DIE6, + S_BOS2_DIE7, + S_BOS2_RAISE1, + S_BOS2_RAISE2, + S_BOS2_RAISE3, + S_BOS2_RAISE4, + S_BOS2_RAISE5, + S_BOS2_RAISE6, + S_BOS2_RAISE7, + S_SKULL_STND, + S_SKULL_STND2, + S_SKULL_RUN1, + S_SKULL_RUN2, + S_SKULL_ATK1, + S_SKULL_ATK2, + S_SKULL_ATK3, + S_SKULL_ATK4, + S_SKULL_PAIN, + S_SKULL_PAIN2, + S_SKULL_DIE1, + S_SKULL_DIE2, + S_SKULL_DIE3, + S_SKULL_DIE4, + S_SKULL_DIE5, + S_SKULL_DIE6, + S_SPID_STND, + S_SPID_STND2, + S_SPID_RUN1, + S_SPID_RUN2, + S_SPID_RUN3, + S_SPID_RUN4, + S_SPID_RUN5, + S_SPID_RUN6, + S_SPID_RUN7, + S_SPID_RUN8, + S_SPID_RUN9, + S_SPID_RUN10, + S_SPID_RUN11, + S_SPID_RUN12, + S_SPID_ATK1, + S_SPID_ATK2, + S_SPID_ATK3, + S_SPID_ATK4, + S_SPID_PAIN, + S_SPID_PAIN2, + S_SPID_DIE1, + S_SPID_DIE2, + S_SPID_DIE3, + S_SPID_DIE4, + S_SPID_DIE5, + S_SPID_DIE6, + S_SPID_DIE7, + S_SPID_DIE8, + S_SPID_DIE9, + S_SPID_DIE10, + S_SPID_DIE11, + S_BSPI_STND, + S_BSPI_STND2, + S_BSPI_SIGHT, + S_BSPI_RUN1, + S_BSPI_RUN2, + S_BSPI_RUN3, + S_BSPI_RUN4, + S_BSPI_RUN5, + S_BSPI_RUN6, + S_BSPI_RUN7, + S_BSPI_RUN8, + S_BSPI_RUN9, + S_BSPI_RUN10, + S_BSPI_RUN11, + S_BSPI_RUN12, + S_BSPI_ATK1, + S_BSPI_ATK2, + S_BSPI_ATK3, + S_BSPI_ATK4, + S_BSPI_PAIN, + S_BSPI_PAIN2, + S_BSPI_DIE1, + S_BSPI_DIE2, + S_BSPI_DIE3, + S_BSPI_DIE4, + S_BSPI_DIE5, + S_BSPI_DIE6, + S_BSPI_DIE7, + S_BSPI_RAISE1, + S_BSPI_RAISE2, + S_BSPI_RAISE3, + S_BSPI_RAISE4, + S_BSPI_RAISE5, + S_BSPI_RAISE6, + S_BSPI_RAISE7, + S_ARACH_PLAZ, + S_ARACH_PLAZ2, + S_ARACH_PLEX, + S_ARACH_PLEX2, + S_ARACH_PLEX3, + S_ARACH_PLEX4, + S_ARACH_PLEX5, + S_CYBER_STND, + S_CYBER_STND2, + S_CYBER_RUN1, + S_CYBER_RUN2, + S_CYBER_RUN3, + S_CYBER_RUN4, + S_CYBER_RUN5, + S_CYBER_RUN6, + S_CYBER_RUN7, + S_CYBER_RUN8, + S_CYBER_ATK1, + S_CYBER_ATK2, + S_CYBER_ATK3, + S_CYBER_ATK4, + S_CYBER_ATK5, + S_CYBER_ATK6, + S_CYBER_PAIN, + S_CYBER_DIE1, + S_CYBER_DIE2, + S_CYBER_DIE3, + S_CYBER_DIE4, + S_CYBER_DIE5, + S_CYBER_DIE6, + S_CYBER_DIE7, + S_CYBER_DIE8, + S_CYBER_DIE9, + S_CYBER_DIE10, + S_PAIN_STND, + S_PAIN_RUN1, + S_PAIN_RUN2, + S_PAIN_RUN3, + S_PAIN_RUN4, + S_PAIN_RUN5, + S_PAIN_RUN6, + S_PAIN_ATK1, + S_PAIN_ATK2, + S_PAIN_ATK3, + S_PAIN_ATK4, + S_PAIN_PAIN, + S_PAIN_PAIN2, + S_PAIN_DIE1, + S_PAIN_DIE2, + S_PAIN_DIE3, + S_PAIN_DIE4, + S_PAIN_DIE5, + S_PAIN_DIE6, + S_PAIN_RAISE1, + S_PAIN_RAISE2, + S_PAIN_RAISE3, + S_PAIN_RAISE4, + S_PAIN_RAISE5, + S_PAIN_RAISE6, + S_SSWV_STND, + S_SSWV_STND2, + S_SSWV_RUN1, + S_SSWV_RUN2, + S_SSWV_RUN3, + S_SSWV_RUN4, + S_SSWV_RUN5, + S_SSWV_RUN6, + S_SSWV_RUN7, + S_SSWV_RUN8, + S_SSWV_ATK1, + S_SSWV_ATK2, + S_SSWV_ATK3, + S_SSWV_ATK4, + S_SSWV_ATK5, + S_SSWV_ATK6, + S_SSWV_PAIN, + S_SSWV_PAIN2, + S_SSWV_DIE1, + S_SSWV_DIE2, + S_SSWV_DIE3, + S_SSWV_DIE4, + S_SSWV_DIE5, + S_SSWV_XDIE1, + S_SSWV_XDIE2, + S_SSWV_XDIE3, + S_SSWV_XDIE4, + S_SSWV_XDIE5, + S_SSWV_XDIE6, + S_SSWV_XDIE7, + S_SSWV_XDIE8, + S_SSWV_XDIE9, + S_SSWV_RAISE1, + S_SSWV_RAISE2, + S_SSWV_RAISE3, + S_SSWV_RAISE4, + S_SSWV_RAISE5, + S_KEENSTND, + S_COMMKEEN, + S_COMMKEEN2, + S_COMMKEEN3, + S_COMMKEEN4, + S_COMMKEEN5, + S_COMMKEEN6, + S_COMMKEEN7, + S_COMMKEEN8, + S_COMMKEEN9, + S_COMMKEEN10, + S_COMMKEEN11, + S_COMMKEEN12, + S_KEENPAIN, + S_KEENPAIN2, + S_BRAIN, + S_BRAIN_PAIN, + S_BRAIN_DIE1, + S_BRAIN_DIE2, + S_BRAIN_DIE3, + S_BRAIN_DIE4, + S_BRAINEYE, + S_BRAINEYESEE, + S_BRAINEYE1, + S_SPAWN1, + S_SPAWN2, + S_SPAWN3, + S_SPAWN4, + S_SPAWNFIRE1, + S_SPAWNFIRE2, + S_SPAWNFIRE3, + S_SPAWNFIRE4, + S_SPAWNFIRE5, + S_SPAWNFIRE6, + S_SPAWNFIRE7, + S_SPAWNFIRE8, + S_BRAINEXPLODE1, + S_BRAINEXPLODE2, + S_BRAINEXPLODE3, + S_ARM1, + S_ARM1A, + S_ARM2, + S_ARM2A, + S_BAR1, + S_BAR2, + S_BEXP, + S_BEXP2, + S_BEXP3, + S_BEXP4, + S_BEXP5, + S_BBAR1, + S_BBAR2, + S_BBAR3, + S_BON1, + S_BON1A, + S_BON1B, + S_BON1C, + S_BON1D, + S_BON1E, + S_BON2, + S_BON2A, + S_BON2B, + S_BON2C, + S_BON2D, + S_BON2E, + S_BKEY, + S_BKEY2, + S_RKEY, + S_RKEY2, + S_YKEY, + S_YKEY2, + S_BSKULL, + S_BSKULL2, + S_RSKULL, + S_RSKULL2, + S_YSKULL, + S_YSKULL2, + S_STIM, + S_MEDI, + S_SOUL, + S_SOUL2, + S_SOUL3, + S_SOUL4, + S_SOUL5, + S_SOUL6, + S_PINV, + S_PINV2, + S_PINV3, + S_PINV4, + S_PSTR, + S_PINS, + S_PINS2, + S_PINS3, + S_PINS4, + S_MEGA, + S_MEGA2, + S_MEGA3, + S_MEGA4, + S_SUIT, + S_PMAP, + S_PMAP2, + S_PMAP3, + S_PMAP4, + S_PMAP5, + S_PMAP6, + S_PVIS, + S_PVIS2, + S_CLIP, + S_AMMO, + S_ROCK, + S_BROK, + S_CELL, + S_CELP, + S_SHEL, + S_SBOX, + S_BPAK, + S_BFUG, + S_MGUN, + S_CSAW, + S_LAUN, + S_PLAS, + S_SHOT, + S_SHOT2, + S_COLU, + S_STALAG, + S_BLOODYTWITCH, + S_BLOODYTWITCH2, + S_BLOODYTWITCH3, + S_BLOODYTWITCH4, + S_DEADTORSO, + S_DEADBOTTOM, + S_HEADSONSTICK, + S_GIBS, + S_HEADONASTICK, + S_HEADCANDLES, + S_HEADCANDLES2, + S_DEADSTICK, + S_LIVESTICK, + S_LIVESTICK2, + S_MEAT2, + S_MEAT3, + S_MEAT4, + S_MEAT5, + S_STALAGTITE, + S_TALLGRNCOL, + S_SHRTGRNCOL, + S_TALLREDCOL, + S_SHRTREDCOL, + S_CANDLESTIK, + S_CANDELABRA, + S_SKULLCOL, + S_TORCHTREE, + S_BIGTREE, + S_TECHPILLAR, + S_EVILEYE, + S_EVILEYE2, + S_EVILEYE3, + S_EVILEYE4, + S_FLOATSKULL, + S_FLOATSKULL2, + S_FLOATSKULL3, + S_HEARTCOL, + S_HEARTCOL2, + S_BLUETORCH, + S_BLUETORCH2, + S_BLUETORCH3, + S_BLUETORCH4, + S_GREENTORCH, + S_GREENTORCH2, + S_GREENTORCH3, + S_GREENTORCH4, + S_REDTORCH, + S_REDTORCH2, + S_REDTORCH3, + S_REDTORCH4, + S_BTORCHSHRT, + S_BTORCHSHRT2, + S_BTORCHSHRT3, + S_BTORCHSHRT4, + S_GTORCHSHRT, + S_GTORCHSHRT2, + S_GTORCHSHRT3, + S_GTORCHSHRT4, + S_RTORCHSHRT, + S_RTORCHSHRT2, + S_RTORCHSHRT3, + S_RTORCHSHRT4, + S_HANGNOGUTS, + S_HANGBNOBRAIN, + S_HANGTLOOKDN, + S_HANGTSKULL, + S_HANGTLOOKUP, + S_HANGTNOBRAIN, + S_COLONGIBS, + S_SMALLPOOL, + S_BRAINSTEM, + S_TECHLAMP, + S_TECHLAMP2, + S_TECHLAMP3, + S_TECHLAMP4, + S_TECH2LAMP, + S_TECH2LAMP2, + S_TECH2LAMP3, + S_TECH2LAMP4, + S_TNT1, /* add state for invisible sprite phares 3/8/98 */ + + S_GRENADE, /* killough 8/9/98: grenade launcher */ + S_DETONATE, /* killough 8/9/98: detonation of objects */ + S_DETONATE2, + S_DETONATE3, + + // always count dog states, even if dogs are disabled + S_DOGS_STND, /* killough 7/19/98: Marine's best friend :) */ + S_DOGS_STND2, + S_DOGS_RUN1, + S_DOGS_RUN2, + S_DOGS_RUN3, + S_DOGS_RUN4, + S_DOGS_RUN5, + S_DOGS_RUN6, + S_DOGS_RUN7, + S_DOGS_RUN8, + S_DOGS_ATK1, + S_DOGS_ATK2, + S_DOGS_ATK3, + S_DOGS_PAIN, + S_DOGS_PAIN2, + S_DOGS_DIE1, + S_DOGS_DIE2, + S_DOGS_DIE3, + S_DOGS_DIE4, + S_DOGS_DIE5, + S_DOGS_DIE6, + S_DOGS_RAISE1, + S_DOGS_RAISE2, + S_DOGS_RAISE3, + S_DOGS_RAISE4, + S_DOGS_RAISE5, + S_DOGS_RAISE6, + + // add dummy beta bfg / lost soul frames for dehacked compatibility + // fixes bug #1576151 (part 2) + S_OLDBFG1, // killough 7/11/98: the old BFG's 43 firing frames + S_OLDBFG42 = S_OLDBFG1+41, + S_OLDBFG43, + + S_PLS1BALL, // killough 7/19/98: first plasma fireball in the beta + S_PLS1BALL2, + S_PLS1EXP, + S_PLS1EXP2, + S_PLS1EXP3, + S_PLS1EXP4, + S_PLS1EXP5, + + S_PLS2BALL, // killough 7/19/98: second plasma fireball in the beta + S_PLS2BALL2, + S_PLS2BALLX1, + S_PLS2BALLX2, + S_PLS2BALLX3, + S_BON3, // killough 7/11/98: evil sceptre in beta version + S_BON4, // killough 7/11/98: unholy bible in beta version + + // killough 10/98: beta lost souls were different from their modern cousins + S_BSKUL_STND, + S_BSKUL_RUN1, + S_BSKUL_RUN2, + S_BSKUL_RUN3, + S_BSKUL_RUN4, + S_BSKUL_ATK1, + S_BSKUL_ATK2, + S_BSKUL_ATK3, + S_BSKUL_PAIN1, + S_BSKUL_PAIN2, + S_BSKUL_PAIN3, + S_BSKUL_DIE1, + S_BSKUL_DIE2, + S_BSKUL_DIE3, + S_BSKUL_DIE4, + S_BSKUL_DIE5, + S_BSKUL_DIE6, + S_BSKUL_DIE7, + S_BSKUL_DIE8, + + S_MUSHROOM, /* killough 10/98: mushroom explosion effect */ + + NUMSTATES /* Counter of how many there are */ + +} statenum_t; + +/******************************************************************** + * Definition of the state (frames) structure * + ********************************************************************/ + +typedef struct +{ + spritenum_t sprite; /* sprite number to show */ + long frame; /* which frame/subframe of the sprite is shown */ + long tics; /* number of gametics this frame should last */ + actionf_t action; /* code pointer to function for action if any */ + statenum_t nextstate; /* linked list pointer to next state or zero */ + long misc1, misc2; /* apparently never used in DOOM */ +} state_t; + +/* these are in info.c */ +extern state_t states[NUMSTATES]; +extern const char *sprnames[]; /* 1/17/98 killough - CPhipps - const */ + +/******************************************************************** + * Thing enumeration -- must match info.c * + ******************************************************************** + * Note that many of these are generically named for the ornamentals + */ + +typedef enum { + MT_PLAYER, + MT_POSSESSED, + MT_SHOTGUY, + MT_VILE, + MT_FIRE, + MT_UNDEAD, + MT_TRACER, + MT_SMOKE, + MT_FATSO, + MT_FATSHOT, + MT_CHAINGUY, + MT_TROOP, + MT_SERGEANT, + MT_SHADOWS, + MT_HEAD, + MT_BRUISER, + MT_BRUISERSHOT, + MT_KNIGHT, + MT_SKULL, + MT_SPIDER, + MT_BABY, + MT_CYBORG, + MT_PAIN, + MT_WOLFSS, + MT_KEEN, + MT_BOSSBRAIN, + MT_BOSSSPIT, + MT_BOSSTARGET, + MT_SPAWNSHOT, + MT_SPAWNFIRE, + MT_BARREL, + MT_TROOPSHOT, + MT_HEADSHOT, + MT_ROCKET, + MT_PLASMA, + MT_BFG, + MT_ARACHPLAZ, + MT_PUFF, + MT_BLOOD, + MT_TFOG, + MT_IFOG, + MT_TELEPORTMAN, + MT_EXTRABFG, + MT_MISC0, + MT_MISC1, + MT_MISC2, + MT_MISC3, + MT_MISC4, + MT_MISC5, + MT_MISC6, + MT_MISC7, + MT_MISC8, + MT_MISC9, + MT_MISC10, + MT_MISC11, + MT_MISC12, + MT_INV, + MT_MISC13, + MT_INS, + MT_MISC14, + MT_MISC15, + MT_MISC16, + MT_MEGA, + MT_CLIP, + MT_MISC17, + MT_MISC18, + MT_MISC19, + MT_MISC20, + MT_MISC21, + MT_MISC22, + MT_MISC23, + MT_MISC24, + MT_MISC25, + MT_CHAINGUN, + MT_MISC26, + MT_MISC27, + MT_MISC28, + MT_SHOTGUN, + MT_SUPERSHOTGUN, + MT_MISC29, + MT_MISC30, + MT_MISC31, + MT_MISC32, + MT_MISC33, + MT_MISC34, + MT_MISC35, + MT_MISC36, + MT_MISC37, + MT_MISC38, + MT_MISC39, + MT_MISC40, + MT_MISC41, + MT_MISC42, + MT_MISC43, + MT_MISC44, + MT_MISC45, + MT_MISC46, + MT_MISC47, + MT_MISC48, + MT_MISC49, + MT_MISC50, + MT_MISC51, + MT_MISC52, + MT_MISC53, + MT_MISC54, + MT_MISC55, + MT_MISC56, + MT_MISC57, + MT_MISC58, + MT_MISC59, + MT_MISC60, + MT_MISC61, + MT_MISC62, + MT_MISC63, + MT_MISC64, + MT_MISC65, + MT_MISC66, + MT_MISC67, + MT_MISC68, + MT_MISC69, + MT_MISC70, + MT_MISC71, + MT_MISC72, + MT_MISC73, + MT_MISC74, + MT_MISC75, + MT_MISC76, + MT_MISC77, + MT_MISC78, + MT_MISC79, + MT_MISC80, + MT_MISC81, + MT_MISC82, + MT_MISC83, + MT_MISC84, + MT_MISC85, + MT_MISC86, + MT_PUSH, /* controls push source - phares */ + MT_PULL, /* controls pull source - phares 3/20/98 */ + + /* proff 11/22/98: Andy Baker's stealth monsters (next 12) + * cph - moved below the MBF stuff, no need to displace them */ + MT_STEALTHBABY, + MT_STEALTHVILE, + MT_STEALTHBRUISER, + MT_STEALTHHEAD, + MT_STEALTHCHAINGUY, + MT_STEALTHSERGEANT, + MT_STEALTHKNIGHT, + MT_STEALTHIMP, + MT_STEALTHFATSO, + MT_STEALTHUNDEAD, + MT_STEALTHSHOTGUY, + MT_STEALTHZOMBIE, + + NUMMOBJTYPES // Counter of how many there are +} mobjtype_t; + +/******************************************************************** + * Definition of the Thing structure + ********************************************************************/ +/* Note that these are only indices to the state, sound, etc. arrays + * and not actual pointers. Most can be set to zero if the action or + * sound doesn't apply (like lamps generally don't attack or whistle). + */ + +typedef struct +{ + int doomednum; /* Thing number used in id's editor, and now + probably by every other editor too */ + int spawnstate; /* The state (frame) index when this Thing is + first created */ + int spawnhealth; /* The initial hit points for this Thing */ + int seestate; /* The state when it sees you or wakes up */ + int seesound; /* The sound it makes when waking */ + int reactiontime; /* How many tics it waits after it wakes up + before it will start to attack, in normal + skills (halved for nightmare) */ + int attacksound; /* The sound it makes when it attacks */ + int painstate; /* The state to indicate pain */ + int painchance; /* A number that is checked against a random + number 0-255 to see if the Thing is supposed + to go to its painstate or not. Note this + has absolutely nothing to do with the chance + it will get hurt, just the chance of it + reacting visibly. */ + int painsound; /* The sound it emits when it feels pain */ + int meleestate; /* Melee==close attack */ + int missilestate; /* What states to use when it's in the air, if + in fact it is ever used as a missile */ + int deathstate; /* What state begins the death sequence */ + int xdeathstate; /* What state begins the horrible death sequence + like when a rocket takes out a trooper */ + int deathsound; /* The death sound. See also A_Scream() in + p_enemy.c for some tweaking that goes on + for certain monsters */ + int speed; /* How fast it moves. Too fast and it can miss + collision logic. */ + int radius; /* An often incorrect radius */ + int height; /* An often incorrect height, used only to see + if a monster can enter a sector */ + int mass; /* How much an impact will move it. Cacodemons + seem to retreat when shot because they have + very little mass and are moved by impact */ + int damage; /* If this is a missile, how much does it hurt? */ + int activesound; /* What sound it makes wandering around, once + in a while. Chance is 3/256 it will. */ + uint_64_t flags; /* Bit masks for lots of things. See p_mobj.h */ + int raisestate; /* The first state for an Archvile or respawn + resurrection. Zero means it won't come + back to life. */ +} mobjinfo_t; + +/* See p_mobj_h for addition more technical info */ +extern mobjinfo_t mobjinfo[NUMMOBJTYPES]; + +#endif diff --git a/src/libretro/libretro.c b/src/libretro/libretro.c new file mode 100644 index 00000000..55d31ecd --- /dev/null +++ b/src/libretro/libretro.c @@ -0,0 +1,1223 @@ +#define LIBRETRO_CORE 1 +#include "config.h" +#include "libretro.h" + +/* prboom includes */ + +#include "d_main.h" +#include "lprintf.h" +#include "doomtype.h" +#include "doomdef.h" +#include "lprintf.h" +#include "m_fixed.h" +#include "r_fps.h" +#include "m_argv.h" +#include "m_misc.h" +#include "i_system.h" +#include "i_sound.h" +#include "v_video.h" +#include "st_stuff.h" +#include "z_zone.h" +#include "m_swap.h" +#include "w_wad.h" +#include "r_draw.h" + +#include +#include +#include +#include + +/* PS3 values for i_sound.h - check if correct for libretro */ +#define SAMPLECOUNT 256 +#define NUM_CHANNELS 32 +#define BUFMUL 4 +#define MIXBUFFERSIZE (SAMPLECOUNT*BUFMUL) +#define MAX_CHANNELS 32 + +//i_system +int ms_to_next_tick; + +//i_video +static unsigned char screen_buf[2 * 320 * 200]; + +//i_sound +int lengths[NUMSFX]; +int snd_card = 1; +int mus_card = 0; +int snd_samplerate= 32000; +int use_doublebuffer = 1; + +typedef struct { + // SFX id of the playing sound effect. + // Used to catch duplicates (like chainsaw). + int id; +// The channel step amount... + unsigned int step; +// ... and a 0.16 bit remainder of last step. + unsigned int stepremainder; + unsigned int samplerate; +// The channel data pointers, start and end. + const unsigned char* data; + const unsigned char* enddata; + // Hardware left and right channel volume lookup. + int *leftvol_lookup; + int *rightvol_lookup; +} channel_info_t; + +channel_info_t channelinfo[MAX_CHANNELS]; + +// The global mixing buffer. +// Basically, samples from all active internal channels +// are modifed and added, and stored in the buffer +// that is submitted to the audio device. +int16_t mixbuffer[MIXBUFFERSIZE]; + +typedef struct +{ + byte *snd_start_ptr, *snd_end_ptr; + unsigned int starttic; + int sfxid; + int *leftvol, *rightvol; + int handle; +} channel_t; + +static channel_t channels[NUM_CHANNELS]; + +int vol_lookup[128*256]; + + +/* libretro */ +static char g_wad_dir[1024]; + +//forward decls +extern void D_DoomMainSetup(void); +extern void D_DoomLoop(void); +extern void M_QuitDOOM(int choice); +void I_PreInitGraphics(void); +void D_DoomDeinit(void); +void I_SetRes(void); +extern int gametic; +extern int snd_SfxVolume; +extern int snd_MusicVolume; + +void retro_init(void) +{ +} + +void retro_deinit(void) +{ +} + +unsigned retro_api_version(void) +{ + return RETRO_API_VERSION; +} + +void retro_set_controller_port_device(unsigned port, unsigned device) +{ + (void)port; + (void)device; +} + +void retro_get_system_info(struct retro_system_info *info) +{ + memset(info, 0, sizeof(*info)); + info->library_name = "prboom"; + info->library_version = "v2.5.0"; + info->need_fullpath = false; + info->valid_extensions = ".WAD|.wad|.IWAD|.iwad"; // Anything is fine, we don't care. +} + +void retro_get_system_av_info(struct retro_system_av_info *info) +{ + info->timing = (struct retro_system_timing) { + .fps = 35.0, + .sample_rate = 30000.0, + }; + + info->geometry = (struct retro_game_geometry) { + .base_width = 320, + .base_height = 200, + .max_width = 320, + .max_height = 200, + .aspect_ratio = 4.0 / 3.0, + }; +} + +static retro_video_refresh_t video_cb; +static retro_audio_sample_t audio_cb; +static retro_audio_sample_batch_t audio_batch_cb; +static retro_environment_t environ_cb; +static retro_input_poll_t input_poll_cb; +static retro_input_state_t input_state_cb; + +void retro_set_environment(retro_environment_t cb) +{ + environ_cb = cb; +} + +void retro_set_audio_sample(retro_audio_sample_t cb) +{ + audio_cb = cb; +} + +void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) +{ + audio_batch_cb = cb; +} + +void retro_set_input_poll(retro_input_poll_t cb) +{ + input_poll_cb = cb; +} + +void retro_set_input_state(retro_input_state_t cb) +{ + input_state_cb = cb; +} + +void retro_set_video_refresh(retro_video_refresh_t cb) +{ + video_cb = cb; +} + +void retro_reset(void) +{ +} + +void retro_run(void) +{ + D_DoomLoop(); +} + +static void extract_directory(char *buf, const char *path, size_t size) +{ + strncpy(buf, path, size - 1); + buf[size - 1] = '\0'; + + char *base = strrchr(buf, '/'); + if (!base) + base = strrchr(buf, '\\'); + + if (base) + *base = '\0'; + else + buf[0] = '\0'; +} + +bool retro_load_game(const struct retro_game_info *info) +{ + int argc = 0; + char vbuf[200]; + char *argv[32] = {NULL}; + + extract_directory(g_wad_dir, info->path, sizeof(g_wad_dir)); + + argv[argc++] = strdup("prboom"); + if(info->path) + { + argv[argc++] = strdup("-iwad"); + argv[argc++] = strdup(info->path); + } + + myargc = argc; + myargv = argv; + + /* Version info */ + lprintf(LO_INFO,"\n"); + lprintf(LO_INFO,"%s\n",I_GetVersionString(vbuf,200)); + + Z_Init(); /* 1/18/98 killough: start up memory stuff first */ + + /* cphipps - call to video specific startup code */ + I_PreInitGraphics(); + + D_DoomMainSetup(); + return true; +} + + +void retro_unload_game(void) +{ + D_DoomDeinit(); +} + +unsigned retro_get_region(void) +{ + return RETRO_REGION_NTSC; +} + +bool retro_load_game_special(unsigned type, const struct retro_game_info *info, size_t num) +{ + (void)type; + (void)info; + (void)num; + return false; +} + +size_t retro_serialize_size(void) +{ + return 2; +} + +bool retro_serialize(void *data_, size_t size) +{ + return false; +} + +bool retro_unserialize(const void *data_, size_t size) +{ + return false; +} + +void *retro_get_memory_data(unsigned id) +{ + (void)id; + return NULL; +} + +size_t retro_get_memory_size(unsigned id) +{ + (void)id; + return 0; +} + +void retro_cheat_reset(void) +{} + +void retro_cheat_set(unsigned index, bool enabled, const char *code) +{ + (void)index; + (void)enabled; + (void)code; +} + +/* i_video */ + +static int action_lut[] = { + KEYD_RALT, /* RETRO_DEVICE_ID_JOYPAD_B */ + KEYD_RSHIFT, /* RETRO DEVICE_ID_JOYPAD_Y */ + KEYD_TAB, /* RETRO_DEVICE_ID_JOYPAD_SELECT */ + KEYD_ESCAPE, /* RETRO_DEVICE_ID_JOYPAD_START */ + KEYD_UPARROW, /* RETRO_DEVICE_ID_JOYPAD_UP */ + KEYD_DOWNARROW, /* RETRO_DEVICE_ID_JOYPAD_DOWN */ + KEYD_LEFTARROW, /* RETRO_DEVICE_ID_JOYPAD_LEFT */ + KEYD_RIGHTARROW, /* RETRO_DEVICE_ID_JOYPAD_RIGHT */ + KEYD_SPACEBAR, /* RETRO_DEVICE_ID_JOYPAD_A */ + KEYD_RCTRL, /* RETRO_DEVICE_ID_JOYPAD_X */ + ',', /* RETRO_DEVICE_ID_JOYPAD_L1 */ + '.', /* RETRO_DEVICE_ID_JOYPAD_R1 */ + 'n', /* RETRO_DEVICE_ID_JOYPAD_L2 */ + 'm', /* RETRO_DEVICE_ID_JOYPAD_R2 */ +}; + +void I_StartTic (void) +{ + static bool old_input[12]; + bool new_input[12]; + + input_poll_cb(); + + for(unsigned i = 0; i < 14; i++) + { + event_t event = {0}; + new_input[i] = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, i); + + if(new_input[i] && !old_input[i]) + { + event.type = ev_keydown; + event.data1 = action_lut[i]; + } + + if(!new_input[i] && old_input[i]) + { + event.type = ev_keyup; + event.data1 = action_lut[i]; + } + + if(event.type == ev_keydown || event.type == ev_keyup) + D_PostEvent(&event); + + old_input[i] = new_input[i]; + } +} + +static void I_UpdateVideoMode(void) +{ + lprintf(LO_INFO, "I_UpdateVideoMode: %dx%d\n", SCREENWIDTH, SCREENHEIGHT); + + V_InitMode(); + V_DestroyUnusedTrueColorPalettes(); + V_FreeScreens(); + + I_SetRes(); + + screens[0].not_on_heap = true; + screens[0].data = (unsigned char *)screen_buf; + + V_AllocScreens(); + + R_InitBuffer(SCREENWIDTH, SCREENHEIGHT); +} + +void I_FinishUpdate (void) +{ + video_cb(screen_buf, SCREENWIDTH, SCREENHEIGHT, SCREENPITCH); +} + +void I_SetPalette (int pal) +{ +} + +void I_InitGraphics(void) +{ + static int firsttime=1; + + if (firsttime) + { + firsttime = 0; + + lprintf(LO_INFO, "I_InitGraphics: %dx%d\n", SCREENWIDTH, SCREENHEIGHT); + + /* Set the video mode */ + I_UpdateVideoMode(); + + } +} + +void I_PreInitGraphics(void) +{ +} + +void I_SetRes(void) +{ + int i; + + for (i=0; i<3; i++) + screens[i].height = SCREENHEIGHT; + + screens[4].height = (ST_SCALED_HEIGHT+1); + + lprintf(LO_INFO,"I_SetRes: Using resolution %dx%d\n", SCREENWIDTH, SCREENHEIGHT); +} + +/* i_system - i_main */ + +static struct timeval start; +static boolean InDisplay = false; + +boolean I_StartDisplay(void) +{ + if (InDisplay) + return false; + + InDisplay = true; + return true; +} + +void I_EndDisplay(void) +{ + InDisplay = false; +} + +static uint32_t GetTicks(void) +{ + uint32_t ticks; + struct timeval now; + gettimeofday(&now, NULL); + ticks = (now.tv_sec - start.tv_sec) * 1000 + (now.tv_usec - start.tv_usec) / 1000; + return (ticks); +} + +static void Delay(uint32_t ms) +{ + int was_error; + + struct timeval tv; + uint32_t then, now, elapsed; + + /* Set the timeout interval */ + then = GetTicks(); + do { + errno = 0; + + /* Calculate the time interval left (in case of interrupt) */ + now = GetTicks(); + elapsed = (now - then); + then = now; + if (elapsed >= ms) { + break; + } + ms -= elapsed; + tv.tv_sec = ms / 1000; + tv.tv_usec = (ms % 1000) * 1000; + + was_error = select(0, NULL, NULL, NULL, &tv); + } + while (was_error && (errno == EINTR)); +} + +void I_uSleep(unsigned long usecs) +{ + Delay(usecs/1000); +} + + +static uint32_t StartTicks(void) +{ + gettimeofday(&start, NULL); +} + + + +int I_GetTime_RealTime (void) +{ + struct timeval tp; + struct timezone tzp; + int newtics; + static int basetime=0; + + gettimeofday(&tp, &tzp); + if (!basetime) + basetime = tp.tv_sec; + newtics = (tp.tv_sec-basetime)*TICRATE + tp.tv_usec*TICRATE/1000000; + return newtics; +} + +/* + * I_GetRandomTimeSeed + * + * CPhipps - extracted from G_ReloadDefaults because it is O/S based + */ +unsigned long I_GetRandomTimeSeed(void) +{ + /* killough 3/26/98: shuffle random seed, use the clock */ + struct timeval tv; + struct timezone tz; + gettimeofday(&tv,&tz); + return (tv.tv_sec*1000ul + tv.tv_usec/1000ul); +} + +/* cphipps - I_GetVersionString + * Returns a version string in the given buffer + */ +const char* I_GetVersionString(char* buf, size_t sz) +{ +#ifdef HAVE_SNPRINTF + snprintf(buf,sz,"%s v%s (http://prboom.sourceforge.net/)",PACKAGE,VERSION); +#else + sprintf(buf,"%s v%s (http://prboom.sourceforge.net/)",PACKAGE,VERSION); +#endif + return buf; +} + +#ifdef PRBOOM_SERVER + +/* cphipps - I_SigString + * Returns a string describing a signal number + */ +const char* I_SigString(char* buf, size_t sz, int signum) +{ +#ifdef HAVE_SNPRINTF + snprintf(buf,sz,"signal %d",signum); +#else + sprintf(buf,"signal %d",signum); +#endif + return buf; +} + +#else + +fixed_t I_GetTimeFrac (void) +{ + unsigned long now; + fixed_t frac; + + now = GetTicks(); + + if (tic_vars.step == 0) + return FRACUNIT; + else + { + frac = (fixed_t)((now - tic_vars.start) * FRACUNIT / tic_vars.step); + if (frac < 0) + frac = 0; + if (frac > FRACUNIT) + frac = FRACUNIT; + return frac; + } +} + +void I_GetTime_SaveMS(void) +{ + if (!movement_smooth) + return; + + tic_vars.start = GetTicks(); + tic_vars.next = (unsigned int) ((tic_vars.start * tic_vars.msec + 1.0f) / tic_vars.msec); + tic_vars.step = tic_vars.next - tic_vars.start; +} + +const char *I_DoomExeDir(void) +{ + return g_wad_dir; +} + +/* + * HasTrailingSlash + * + * cphipps - simple test for trailing slash on dir names + */ + +boolean HasTrailingSlash(const char* dn) +{ + return ( (dn[strlen(dn)-1] == '/')); +} + +/* + * I_FindFile + * + * proff_fs 2002-07-04 - moved to i_system + * + * cphipps 19/1999 - writen to unify the logic in FindIWADFile and the WAD + * autoloading code. + * Searches the standard dirs for a named WAD file + * The dirs are listed at the start of the function + */ + +char* I_FindFile(const char* wfname, const char* ext) +{ +lprintf(LO_ALWAYS, "wfname: %s\n", wfname); + // lookup table of directories to search + static const struct { + const char *dir; // directory + const char *sub; // subdirectory + const char *env; // environment variable + const char *(*func)(void); // for I_DoomExeDir + } search[] = { + {g_wad_dir}, + }; + + int i; + /* Precalculate a length we will need in the loop */ + size_t pl = strlen(wfname) + strlen(ext) + 4; + + for (i = 0; i < sizeof(search)/sizeof(*search); i++) { + char * p; + const char * d = NULL; + const char * s = NULL; + /* Each entry in the switch sets d to the directory to look in, + * and optionally s to a subdirectory of d */ + // switch replaced with lookup table + d = g_wad_dir; + lprintf(LO_ALWAYS, "d: %s\n", d); + s = search[i].sub; + + p = malloc((d ? strlen(d) : 0) + (s ? strlen(s) : 0) + pl); + sprintf(p, wfname); + lprintf(LO_ALWAYS, "p: %s\n", p); + + if (access(p,F_OK)) + strcat(p, ext); + if (!access(p,F_OK)) { + lprintf(LO_INFO, " found %s\n", p); + return p; + } + free(p); + } + return NULL; +} + +#endif + + +/* + * I_Filelength + * + * Return length of an open file. + */ + +int I_Filelength(int handle) +{ + struct stat fileinfo; + if (fstat(handle,&fileinfo) == -1) + I_Error("I_Filelength: %s",strerror(errno)); + return fileinfo.st_size; +} + +static int I_GetTime_Error(void) +{ + I_Error("I_GetTime_Error: GetTime() used before initialization"); + return 0; +} + +int (*I_GetTime)(void) = I_GetTime_Error; + +void I_Init(void) +{ + I_GetTime = I_GetTime_RealTime; + + { + /* killough 2/21/98: avoid sound initialization if no sound & no music */ + if (!(nomusicparm && nosfxparm)) + I_InitSound(); + } + + R_InitInterpolation(); +} + +// killough 2/22/98: Add support for ENDBOOM, which is PC-specific + +static void PrintVer(void) +{ + char vbuf[200]; + lprintf(LO_INFO,"%s\n",I_GetVersionString(vbuf,200)); +} + +extern void D_Doom_Deinit(void); + +/* i_sound */ +static void I_SndMixResetChannel (int channum) +{ + memset (&channels[channum], 0, sizeof(channel_t)); + + return; +} + + +// +// This function loads the sound data from the WAD lump +// for a single sound effect. +// +static void* I_SndLoadSample (const char* sfxname, int* len) +{ + int i; + int sfxlump_num, sfxlump_len; + char sfxlump_name[20]; + byte *sfxlump_data, *sfxlump_sound; + byte *padded_sfx_data; + uint16_t orig_rate; + int padded_sfx_len; + float times; + int x; + + sprintf (sfxlump_name, "DS%s", sfxname); + + // check if the sound lump exists + if (W_CheckNumForName(sfxlump_name) == -1) + return 0; + + sfxlump_num = W_GetNumForName (sfxlump_name); + sfxlump_len = W_LumpLength (sfxlump_num); + + // if it's not at least 9 bytes (8 byte header + at least 1 sample), it's + // not in the correct format + if (sfxlump_len < 9) + return 0; + + // load it + sfxlump_data = W_CacheLumpNum (sfxlump_num); + sfxlump_sound = sfxlump_data + 8; + sfxlump_len -= 8; + + // get original sample rate from DMX header + memcpy (&orig_rate, sfxlump_data+2, 2); + orig_rate = SHORT (orig_rate); + + times = 48000.0f / (float)orig_rate; + + padded_sfx_len = ((sfxlump_len*ceil(times) + (SAMPLECOUNT-1)) / SAMPLECOUNT) * SAMPLECOUNT; + padded_sfx_data = (byte*)malloc(padded_sfx_len); + + for (i=0; i < padded_sfx_len; i++) + { + x = floor ((float)i/times); + + if (x < sfxlump_len) // 8 was already subtracted + padded_sfx_data[i] = sfxlump_sound[x]; + else + padded_sfx_data[i] = 128; // fill the rest with silence + } + + Z_Free (sfxlump_data); // free original lump + + *len = padded_sfx_len; + return (void *)(padded_sfx_data); +} + + +// +// SFX API +// Note: this was called by S_Init. +// However, whatever they did in the +// old DPMS based DOS version, this +// were simply dummies in the Linux +// version. +// See soundserver initdata(). +// + +void I_SetChannels() +{ + // Init internal lookups (raw data, mixing buffer, channels). + // This function sets up internal lookups used during + // the mixing process. + + int i, j; + + // Okay, reset internal mixing channels to zero. + for (i=0; iname); + return W_GetNumForName(namebuf); +} + +void I_StopSound (int handle) +{ + int i; + + //sys_lwmutex_lock (&chanmutex, 0); + + for (i=0; i> 16); ///(256*256); + sep -= 257; + rightvol = vol - ((vol*sep*sep) >> 16); + + // Sanity check, clamp volume. + if (rightvol < 0 || rightvol > 127) + I_Error("addsfx: rightvol out of bounds"); + + if (leftvol < 0 || leftvol > 127) + I_Error("addsfx: leftvol out of bounds"); + + // Get the proper lookup table piece + // for this volume level??? + channels[slot].leftvol = &vol_lookup[leftvol*256]; + channels[slot].rightvol = &vol_lookup[rightvol*256]; + + // Preserve sound SFX id, + // e.g. for avoiding duplicates of chainsaw. + channels[slot].sfxid = id; + + //sys_lwmutex_unlock (&chanmutex); + + return currenthandle; +} + + +boolean I_SoundIsPlaying (int handle) +{ + int i; + + //sys_lwmutex_lock (&chanmutex, 0); + + for (i=0; i 127) *leftout = 127; + // else if (dl < -128) *leftout = -128; + // else *leftout = dl; + + if (dl > 0x7fff) + *leftout = 0x7fff; + else if (dl < -0x8000) + *leftout = -0x8000; + else + *leftout = dl; + + // Same for right hardware channel. + if (dr > 0x7fff) + *rightout = 0x7fff; + else if (dr < -0x8000) + *rightout = -0x8000; + else + *rightout = dr; + + // Increment current pointers in mixbuffer. + leftout += step; + rightout += step; + } + + return; +} + +#if 0 +static uint32_t playOneBlock(u64 *readIndex, float *audioDataStart) +{ + static uint64_t audio_block_index=1; + uint64_t current_block = *readIndex; + float *buf; + + if (audio_block_index == current_block) + return 0; + + buf = audioDataStart + 2 /*channelcount*/ * AUDIO_BLOCK_SAMPLES * audio_block_index; + + I_UpdateSound(); + + for (int i = 0; i < SAMPLECOUNT*2; i++) + buf[i] = (float)mixbuffer[i]/32767.0f; + + audio_block_index = (audio_block_index + 1) % AUDIO_BLOCK_8; + + return 1; +} + +static void mix_thread_func (uint64_t arg) +{ + for (;;) + { + usleep (20); + + sys_lwmutex_lock (&chanmutex, 0); + + playOneBlock((u64*)(u64)ps3_audio_port_cfg.readIndex, + (float*)(u64)ps3_audio_port_cfg.audioDataStart); + + sys_lwmutex_unlock (&chanmutex); + } + + return; +} +#endif + +void I_UpdateSoundParams (int handle, int vol, int sep, int pitch) +{ + int rightvol; + int leftvol; + int i; + + // sys_lwmutex_lock (&chanmutex, 0); + + for (i=0; i> 16); ///(256*256); + sep -= 257; + rightvol = vol - ((vol*sep*sep) >> 16); + + if (rightvol < 0 || rightvol > 127) + I_Error("I_UpdateSoundParams: rightvol out of bounds."); + + if (leftvol < 0 || leftvol > 127) + I_Error("I_UpdateSoundParams: leftvol out of bounds."); + + channels[i].leftvol = &vol_lookup[leftvol*256]; + channels[i].rightvol = &vol_lookup[rightvol*256]; + + //sys_lwmutex_unlock (&chanmutex); + return; + } + } + + //sys_lwmutex_unlock (&chanmutex); + return; +} + + +void I_ShutdownSound(void) +{ + return; +} + + +void I_InitSound(void) +{ + int i; + + memset (&lengths, 0, sizeof(int)*NUMSFX); + for (i=1 ; idata; + lengths[i] = lengths[(S_sfx[i].link - S_sfx)/sizeof(sfxinfo_t)]; + } + } + + I_SetChannels(); + + printf ("I_InitSound: \n"); + + return; +} + +boolean I_AnySoundStillPlaying(void) +{ + boolean result = false; + int i; + + for (i=0; i gametic; +} + +int I_RegisterMusic( const char* filename, musicinfo_t *song ) +{ + return 1; +} diff --git a/src/libretro/libretro.h b/src/libretro/libretro.h new file mode 100755 index 00000000..5818df81 --- /dev/null +++ b/src/libretro/libretro.h @@ -0,0 +1,283 @@ +#ifndef LIBRETRO_H__ +#define LIBRETRO_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#else +#if defined(_MSC_VER) && !defined(__cplusplus) +#define bool unsigned char +#define true 1 +#define false 0 +#else +#include +#undef true +#undef false +#endif +#endif + +#define RETRO_API_VERSION 1 + +#define RETRO_DEVICE_MASK 0xff +#define RETRO_DEVICE_NONE 0 +#define RETRO_DEVICE_JOYPAD 1 +#define RETRO_DEVICE_MOUSE 2 +#define RETRO_DEVICE_KEYBOARD 3 +#define RETRO_DEVICE_LIGHTGUN 4 + +#define RETRO_DEVICE_JOYPAD_MULTITAP ((1 << 8) | RETRO_DEVICE_JOYPAD) +#define RETRO_DEVICE_LIGHTGUN_SUPER_SCOPE ((1 << 8) | RETRO_DEVICE_LIGHTGUN) +#define RETRO_DEVICE_LIGHTGUN_JUSTIFIER ((2 << 8) | RETRO_DEVICE_LIGHTGUN) +#define RETRO_DEVICE_LIGHTGUN_JUSTIFIERS ((3 << 8) | RETRO_DEVICE_LIGHTGUN) + +#define RETRO_DEVICE_ID_JOYPAD_B 0 +#define RETRO_DEVICE_ID_JOYPAD_Y 1 +#define RETRO_DEVICE_ID_JOYPAD_SELECT 2 +#define RETRO_DEVICE_ID_JOYPAD_START 3 +#define RETRO_DEVICE_ID_JOYPAD_UP 4 +#define RETRO_DEVICE_ID_JOYPAD_DOWN 5 +#define RETRO_DEVICE_ID_JOYPAD_LEFT 6 +#define RETRO_DEVICE_ID_JOYPAD_RIGHT 7 +#define RETRO_DEVICE_ID_JOYPAD_A 8 +#define RETRO_DEVICE_ID_JOYPAD_X 9 +#define RETRO_DEVICE_ID_JOYPAD_L 10 +#define RETRO_DEVICE_ID_JOYPAD_R 11 +#define RETRO_DEVICE_ID_JOYPAD_L2 12 +#define RETRO_DEVICE_ID_JOYPAD_R2 13 +#define RETRO_DEVICE_ID_JOYPAD_L3 14 +#define RETRO_DEVICE_ID_JOYPAD_R3 15 + +#define RETRO_DEVICE_ID_MOUSE_X 0 +#define RETRO_DEVICE_ID_MOUSE_Y 1 +#define RETRO_DEVICE_ID_MOUSE_LEFT 2 +#define RETRO_DEVICE_ID_MOUSE_RIGHT 3 + +#define RETRO_DEVICE_ID_LIGHTGUN_X 0 +#define RETRO_DEVICE_ID_LIGHTGUN_Y 1 +#define RETRO_DEVICE_ID_LIGHTGUN_TRIGGER 2 +#define RETRO_DEVICE_ID_LIGHTGUN_CURSOR 3 +#define RETRO_DEVICE_ID_LIGHTGUN_TURBO 4 +#define RETRO_DEVICE_ID_LIGHTGUN_PAUSE 5 +#define RETRO_DEVICE_ID_LIGHTGUN_START 6 + +#define RETRO_REGION_NTSC 0 +#define RETRO_REGION_PAL 1 + +#define RETRO_MEMORY_MASK 0xff +#define RETRO_MEMORY_SAVE_RAM 0 +#define RETRO_MEMORY_RTC 1 +#define RETRO_MEMORY_SYSTEM_RAM 2 +#define RETRO_MEMORY_VIDEO_RAM 3 + +#define RETRO_MEMORY_SNES_BSX_RAM ((1 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_BSX_PRAM ((2 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_SUFAMI_TURBO_A_RAM ((3 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_SUFAMI_TURBO_B_RAM ((4 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_GAME_BOY_RAM ((5 << 8) | RETRO_MEMORY_SAVE_RAM) +#define RETRO_MEMORY_SNES_GAME_BOY_RTC ((6 << 8) | RETRO_MEMORY_RTC) + +#define RETRO_GAME_TYPE_BSX 0x101 +#define RETRO_GAME_TYPE_BSX_SLOTTED 0x102 +#define RETRO_GAME_TYPE_SUFAMI_TURBO 0x103 +#define RETRO_GAME_TYPE_SUPER_GAME_BOY 0x104 + + +// Environment commands. +#define RETRO_ENVIRONMENT_SET_ROTATION 1 // const unsigned * -- + // Sets screen rotation of graphics. + // Is only implemented if rotation can be accelerated by hardware. + // Valid values are 0, 1, 2, 3, which rotates screen by 0, 90, 180, 270 degrees + // counter-clockwise respectively. + // +#define RETRO_ENVIRONMENT_GET_OVERSCAN 2 // bool * -- + // Boolean value whether or not the implementation should use overscan, or crop away overscan. + // +#define RETRO_ENVIRONMENT_GET_CAN_DUPE 3 // bool * -- + // Boolean value whether or not RetroArch supports frame duping, + // passing NULL to video frame callback. + // +#define RETRO_ENVIRONMENT_GET_VARIABLE 4 // struct retro_variable * -- + // Interface to aquire user-defined information from environment + // that cannot feasibly be supported in a multi-system way. + // Mostly used for obscure, + // specific features that the user can tap into when neseccary. + // +#define RETRO_ENVIRONMENT_SET_VARIABLES 5 // const struct retro_variable * -- + // Allows an implementation to signal the environment + // which variables it might want to check for later using GET_VARIABLE. + // 'data' points to an array of retro_variable structs terminated by a { NULL, NULL } element. + // retro_variable::value should contain a human readable description of the key. + // +#define RETRO_ENVIRONMENT_SET_MESSAGE 6 // const struct retro_message * -- + // Sets a message to be displayed in implementation-specific manner for a certain amount of 'frames'. + // Should not be used for trivial messages, which should simply be logged to stderr. + +struct retro_message +{ + const char *msg; // Message to be displayed. + unsigned frames; // Duration in frames of message. +}; + +struct retro_system_info +{ + const char *library_name; // Descriptive name of library. Should not contain any version numbers, etc. + const char *library_version; // Descriptive version of core. + + const char *valid_extensions; // A string listing probably rom extensions the core will be able to load, separated with pipe. + // I.e. "bin|rom|iso". + // Typically used for a GUI to filter out extensions. + + bool need_fullpath; // If true, retro_load_game() is guaranteed to provide a valid pathname in retro_game_info::path. + // ::data and ::size are both invalid. + // If false, ::data and ::size are guaranteed to be valid, but ::path might not be valid. + // This is typically set to true for libretro implementations that must load from file. + // Implementations should strive for setting this to false, as it allows the frontend to perform patching, etc. + + bool block_extract; // If true, the frontend is not allowed to extract any archives before loading the real ROM. + // Necessary for certain libretro implementations that load games from zipped archives. +}; + +struct retro_game_geometry +{ + unsigned base_width; // Nominal video width of game. + unsigned base_height; // Nominal video height of game. + unsigned max_width; // Maximum possible width of game. + unsigned max_height; // Maximum possible height of game. + + float aspect_ratio; // Nominal aspect ratio of game. If aspect_ratio is <= 0.0, + // an aspect ratio of base_width / base_height is assumed. + // A frontend could override this setting if desired. +}; + +struct retro_system_timing +{ + double fps; // FPS of video content. + double sample_rate; // Sampling rate of audio. +}; + +struct retro_system_av_info +{ + struct retro_game_geometry geometry; + struct retro_system_timing timing; +}; + +struct retro_variable +{ + const char *key; // Variable to query in RETRO_ENVIRONMENT_GET_VARIABLE. + // If NULL, obtains the complete environment string if more complex parsing is necessary. + // The environment string is formatted as key-value pairs delimited by semicolons as so: + // "key1=value1;key2=value2;..." + const char *value; // Value to be obtained. If key does not exist, it is set to NULL. +}; + +struct retro_game_info +{ + const char *path; // Path to game, UTF-8 encoded. Usually used as a reference. + // May be NULL if rom was loaded from stdin or similar. + // retro_system_info::need_fullpath guaranteed that this path is valid. + const void *data; // Memory buffer of loaded game. Will be NULL if need_fullpath was set. + size_t size; // Size of memory buffer. + const char *meta; // String of implementation specific meta-data. +}; + +// Callbacks +// +// Environment callback. Gives implementations a way of performing uncommon tasks. Extensible. +typedef bool (*retro_environment_t)(unsigned cmd, void *data); + +// Render a frame. Pixel format is 15-bit XRGB1555 native endian. +// Width and height specify dimensions of buffer. +// Pitch specifices length in bytes between two lines in buffer. +typedef void (*retro_video_refresh_t)(const void *data, unsigned width, unsigned height, size_t pitch); + +// Renders a single audio frame. Should only be used if implementation generates a single sample at a time. +// Format is signed 16-bit native endian. +typedef void (*retro_audio_sample_t)(int16_t left, int16_t right); +// Renders multiple audio frames in one go. One frame is defined as a sample of left and right channels, interleaved. +// I.e. int16_t buf[4] = { l, r, l, r }; would be 2 frames. +// Only one of the audio callbacks must ever be used. +typedef size_t (*retro_audio_sample_batch_t)(const int16_t *data, size_t frames); + +// Polls input. +typedef void (*retro_input_poll_t)(void); +// Queries for input for player 'port'. device will be masked with RETRO_DEVICE_MASK. +// Specialization of devices such as RETRO_DEVICE_JOYPAD_MULTITAP that have been set with retro_set_controller_port_device() +// will still use the higher level RETRO_DEVICE_JOYPAD to request input. +typedef int16_t (*retro_input_state_t)(unsigned port, unsigned device, unsigned index, unsigned id); + +// Sets callbacks. retro_set_environment() is guaranteed to be called before retro_init(). +// The rest of the set_* functions are guaranteed to have been called before the first call to retro_run() is made. +void retro_set_environment(retro_environment_t); +void retro_set_video_refresh(retro_video_refresh_t); +void retro_set_audio_sample(retro_audio_sample_t); +void retro_set_audio_sample_batch(retro_audio_sample_batch_t); +void retro_set_input_poll(retro_input_poll_t); +void retro_set_input_state(retro_input_state_t); + +// Library global initialization/deinitialization. +void retro_init(void); +void retro_deinit(void); + +// Must return RETRO_API_VERSION. Used to validate ABI compatibility when the API is revised. +unsigned retro_api_version(void); + +// Gets statically known system info. Pointers provided in *info must be statically allocated. +// Can be called at any time, even before retro_init(). +void retro_get_system_info(struct retro_system_info *info); + +// Gets information about system audio/video timings and geometry. +// Can be called only after retro_load_game() has successfully completed. +void retro_get_system_av_info(struct retro_system_av_info *info); + +// Sets device to be used for player 'port'. +void retro_set_controller_port_device(unsigned port, unsigned device); + +// Resets the current game. +void retro_reset(void); + +// Runs the game for one video frame. +// During retro_run(), input_poll callback must be called at least once. +// +// If a frame is not rendered for reasons where a game "dropped" a frame, +// this still counts as a frame, and retro_run() should explicitly dupe a frame if GET_CAN_DUPE returns true. +// In this case, the video callback can take a NULL argument for data. +void retro_run(void); + +// Returns the amount of data the implementation requires to serialize internal state (save states). +// Beetween calls to retro_load_game() and retro_unload_game(), the returned size is never allowed to be larger than a previous returned value, to +// ensure that the frontend can allocate a save state buffer once. +size_t retro_serialize_size(void); + +// Serializes internal state. If failed, or size is lower than retro_serialize_size(), it should return false, true otherwise. +bool retro_serialize(void *data, size_t size); +bool retro_unserialize(const void *data, size_t size); + +void retro_cheat_reset(void); +void retro_cheat_set(unsigned index, bool enabled, const char *code); + +// Loads a game. +bool retro_load_game(const struct retro_game_info *game); + +// Loads a "special" kind of game. Should not be used except in extreme cases. +bool retro_load_game_special( + unsigned game_type, + const struct retro_game_info *info, size_t num_info +); + +// Unloads a currently loaded game. +void retro_unload_game(void); + +// Gets region of game. +unsigned retro_get_region(void); + +// Gets region of memory. +void *retro_get_memory_data(unsigned id); +size_t retro_get_memory_size(unsigned id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libretro/link.T b/src/libretro/link.T new file mode 100644 index 00000000..b0c262db --- /dev/null +++ b/src/libretro/link.T @@ -0,0 +1,5 @@ +{ + global: retro_*; + local: *; +}; + diff --git a/src/lprintf.c b/src/lprintf.c new file mode 100644 index 00000000..e2481629 --- /dev/null +++ b/src/lprintf.c @@ -0,0 +1,112 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Provides a logical console output routine that allows what is + * output to console normally and when output is redirected to + * be controlled.. + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#endif +#ifdef _MSC_VER +#include +#endif +#include +#include +#include +#include "doomtype.h" +#include "lprintf.h" +#include "i_main.h" +#include "m_argv.h" + +int cons_error_mask = -1-LO_INFO; /* all but LO_INFO when redir'd */ +int cons_output_mask = -1; /* all output enabled */ + +/* cphipps - enlarged message buffer and made non-static + * We still have to be careful here, this function can be called after exit + */ +#define MAX_MESSAGE_SIZE 2048 + +int lprintf(OutputLevels pri, const char *s, ...) +{ + int r=0; + char msg[MAX_MESSAGE_SIZE]; + int lvl=pri; + + va_list v; + va_start(v,s); +#ifdef HAVE_VSNPRINTF + vsnprintf(msg,sizeof(msg),s,v); /* print message in buffer */ +#else + vsprintf(msg,s,v); +#endif + va_end(v); + + if (lvl&cons_output_mask) /* mask output as specified */ + { + r=fprintf(stdout,"%s",msg); +#ifdef _WIN32 + I_ConPrintString(msg); +#endif + } + if (!isatty(1) && lvl&cons_error_mask) /* if stdout redirected */ + r=fprintf(stderr,"%s",msg); /* select output at console */ + + return r; +} + +/* + * I_Error + * + * cphipps - moved out of i_* headers, to minimise source files that depend on + * the low-level headers. All this does is print the error, then call the + * low-level safe exit function. + * killough 3/20/98: add const + */ + +void I_Error(const char *error, ...) +{ + char errmsg[MAX_MESSAGE_SIZE]; + va_list argptr; + va_start(argptr,error); +#ifdef HAVE_VSNPRINTF + vsnprintf(errmsg,sizeof(errmsg),error,argptr); +#else + vsprintf(errmsg,error,argptr); +#endif + va_end(argptr); + lprintf(LO_ERROR, "%s\n", errmsg); + I_SafeExit(-1); +} diff --git a/src/lprintf.h b/src/lprintf.h new file mode 100644 index 00000000..ecf165ae --- /dev/null +++ b/src/lprintf.h @@ -0,0 +1,61 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Declarations etc. for logical console output + * + *-----------------------------------------------------------------------------*/ + +#ifndef __LPRINTF__ +#define __LPRINTF__ + +typedef enum /* Logical output levels */ +{ + LO_INFO=1, /* One of these is used in each physical output */ + LO_CONFIRM=2, /* call. Which are output, or echoed to console */ + LO_WARN=4, /* if output redirected is determined by the */ + LO_ERROR=8, /* global masks: cons_output_mask,cons_error_mask. */ + LO_FATAL=16, + LO_DEBUG=32, + LO_ALWAYS=64, +} OutputLevels; + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +extern int lprintf(OutputLevels pri, const char *fmt, ...) __attribute__((format(printf,2,3))); +extern int cons_output_mask; +extern int cons_error_mask; + +/* killough 3/20/98: add const + * killough 4/25/98: add gcc attributes + * cphipps 01/11- moved from i_system.h */ +void I_Error(const char *error, ...) __attribute__((format(printf,1,2))); + +#endif diff --git a/src/m_argv.c b/src/m_argv.c new file mode 100644 index 00000000..8f3aff32 --- /dev/null +++ b/src/m_argv.c @@ -0,0 +1,63 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Some argument handling. + * + *-----------------------------------------------------------------------------*/ + +#include +// CPhipps - include the correct header +#include "doomtype.h" +#include "m_argv.h" +#include "lprintf.h" + +int myargc; +const char * const * myargv; // CPhipps - not sure if ANSI C allows you to +// modify contents of argv, but I can't imagine it does. + +// +// M_CheckParm +// Checks for the given parameter +// in the program's command line arguments. +// Returns the argument number (1 to argc-1) +// or 0 if not present +// + +int M_CheckParm(const char *check) +{ +#if 0 + lprintf(LO_ALWAYS, "M_CheckParm: Checking %s...\n", check); + lprintf(LO_ALWAYS, "M_CheckParm: myargc: %d, myargc - 1: %d\n", myargc, myargc-1); +#endif + signed int i = myargc; + while (--i>0) + if (!strcasecmp(check, myargv[i])) + return i; + return 0; +} diff --git a/src/m_argv.h b/src/m_argv.h new file mode 100644 index 00000000..5340c156 --- /dev/null +++ b/src/m_argv.h @@ -0,0 +1,47 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Argument handling. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __M_ARGV__ +#define __M_ARGV__ + +/* + * MISC + */ +extern int myargc; +extern const char * const * myargv; /* CPhipps - const * const * */ + +/* Returns the position of the given parameter in the arg list (0 if not found). */ +int M_CheckParm(const char *check); + +#endif diff --git a/src/m_bbox.c b/src/m_bbox.c new file mode 100644 index 00000000..b0a2daad --- /dev/null +++ b/src/m_bbox.c @@ -0,0 +1,58 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Main loop menu stuff. + * Random number LUT. + * Default Config File. + * PCX Screenshots. + * + *-----------------------------------------------------------------------------*/ + +#ifdef __GNUG__ +#pragma implementation "m_bbox.h" +#endif +#include "m_bbox.h" + +void M_ClearBox (fixed_t *box) +{ + box[BOXTOP] = box[BOXRIGHT] = INT_MIN; + box[BOXBOTTOM] = box[BOXLEFT] = INT_MAX; +} + +void M_AddToBox(fixed_t* box,fixed_t x,fixed_t y) +{ + if (xbox[BOXRIGHT]) + box[BOXRIGHT] = x; + if (ybox[BOXTOP]) + box[BOXTOP] = y; +} diff --git a/src/m_bbox.h b/src/m_bbox.h new file mode 100644 index 00000000..5c2f2df8 --- /dev/null +++ b/src/m_bbox.h @@ -0,0 +1,56 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Simple bounding box datatype and functions. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __M_BBOX__ +#define __M_BBOX__ + +#include +#include "m_fixed.h" + +/* Bounding box coordinate storage. */ +enum +{ + BOXTOP, + BOXBOTTOM, + BOXLEFT, + BOXRIGHT +}; /* bbox coordinates */ + +/* Bounding box functions. */ + +void M_ClearBox(fixed_t* box); + +void M_AddToBox(fixed_t* box,fixed_t x,fixed_t y); + +#endif diff --git a/src/m_cheat.c b/src/m_cheat.c new file mode 100644 index 00000000..a4e9f6df --- /dev/null +++ b/src/m_cheat.c @@ -0,0 +1,724 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Cheat sequence checking. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "g_game.h" +#include "r_data.h" +#include "p_inter.h" +#include "p_tick.h" +#include "m_cheat.h" +#include "m_argv.h" +#include "s_sound.h" +#include "sounds.h" +#include "dstrings.h" +#include "r_main.h" +#include "p_map.h" +#include "d_deh.h" // Ty 03/27/98 - externalized strings +/* cph 2006/07/23 - needs direct access to thinkercap */ +#include "p_tick.h" + +#define plyr (players+consoleplayer) /* the console player */ + +//----------------------------------------------------------------------------- +// +// CHEAT SEQUENCE PACKAGE +// +//----------------------------------------------------------------------------- + +static void cheat_mus(); +static void cheat_choppers(); +static void cheat_god(); +static void cheat_fa(); +static void cheat_k(); +static void cheat_kfa(); +static void cheat_noclip(); +static void cheat_pw(); +static void cheat_behold(); +static void cheat_clev(); +static void cheat_mypos(); +static void cheat_rate(); +static void cheat_comp(); +static void cheat_friction(); +static void cheat_pushers(); +static void cheat_massacre(); +static void cheat_ddt(); +static void cheat_hom(); +static void cheat_fast(); +static void cheat_tntkey(); +static void cheat_tntkeyx(); +static void cheat_tntkeyxx(); +static void cheat_tntweap(); +static void cheat_tntweapx(); +static void cheat_tntammo(); +static void cheat_tntammox(); +static void cheat_smart(); +static void cheat_pitch(); +static void cheat_megaarmour(); +static void cheat_health(); + +//----------------------------------------------------------------------------- +// +// List of cheat codes, functions, and special argument indicators. +// +// The first argument is the cheat code. +// +// The second argument is its DEH name, or NULL if it's not supported by -deh. +// +// The third argument is a combination of the bitmasks: +// {always, not_dm, not_coop, not_net, not_menu, not_demo, not_deh}, +// which excludes the cheat during certain modes of play. +// +// The fourth argument is the handler function. +// +// The fifth argument is passed to the handler function if it's non-negative; +// if negative, then its negative indicates the number of extra characters +// expected after the cheat code, which are passed to the handler function +// via a pointer to a buffer (after folding any letters to lowercase). +// +//----------------------------------------------------------------------------- + +struct cheat_s cheat[] = { + {"idmus", "Change music", always, + cheat_mus, -2}, + + {"idchoppers", "Chainsaw", not_net | not_demo, + cheat_choppers }, + + {"iddqd", "God mode", not_net | not_demo, + cheat_god }, + + {"idk", NULL, not_net | not_demo | not_deh, + cheat_k }, // The most controversial cheat code in Doom history!!! + + {"idkfa", "Ammo & Keys", not_net | not_demo, + cheat_kfa }, + + {"idfa", "Ammo", not_net | not_demo, + cheat_fa }, + + {"idspispopd", "No Clipping 1", not_net | not_demo, + cheat_noclip }, + + {"idclip", "No Clipping 2", not_net | not_demo, + cheat_noclip }, + + {"idbeholdh", "Invincibility", not_net | not_demo, + cheat_health }, + + {"idbeholdm", "Invincibility", not_net | not_demo, + cheat_megaarmour }, + + {"idbeholdv", "Invincibility", not_net | not_demo, + cheat_pw, pw_invulnerability }, + + {"idbeholds", "Berserk", not_net | not_demo, + cheat_pw, pw_strength }, + + {"idbeholdi", "Invisibility", not_net | not_demo, + cheat_pw, pw_invisibility }, + + {"idbeholdr", "Radiation Suit", not_net | not_demo, + cheat_pw, pw_ironfeet }, + + {"idbeholda", "Auto-map", not_dm, + cheat_pw, pw_allmap }, + + {"idbeholdl", "Lite-Amp Goggles", not_dm, + cheat_pw, pw_infrared }, + + {"idbehold", "BEHOLD menu", not_dm, + cheat_behold }, + + {"idclev", "Level Warp", not_net | not_demo | not_menu, + cheat_clev, -2}, + + {"idmypos", "Player Position", not_dm, + cheat_mypos }, + + {"idrate", "Frame rate", 0, + cheat_rate }, + + {"tntcomp", NULL, not_net | not_demo, + cheat_comp }, // phares + + {"tntem", NULL, not_net | not_demo, + cheat_massacre }, // jff 2/01/98 kill all monsters + + {"iddt", "Map cheat", not_dm, + cheat_ddt }, // killough 2/07/98: moved from am_map.c + + {"tnthom", NULL, always, + cheat_hom }, // killough 2/07/98: HOM autodetector + + {"tntkey", NULL, not_net | not_demo, + cheat_tntkey }, // killough 2/16/98: generalized key cheats + + {"tntkeyr", NULL, not_net | not_demo, + cheat_tntkeyx }, + + {"tntkeyy", NULL, not_net | not_demo, + cheat_tntkeyx }, + + {"tntkeyb", NULL, not_net | not_demo, + cheat_tntkeyx }, + + {"tntkeyrc", NULL, not_net | not_demo, + cheat_tntkeyxx, it_redcard }, + + {"tntkeyyc", NULL, not_net | not_demo, + cheat_tntkeyxx, it_yellowcard }, + + {"tntkeybc", NULL, not_net | not_demo, + cheat_tntkeyxx, it_bluecard }, + + {"tntkeyrs", NULL, not_net | not_demo, + cheat_tntkeyxx, it_redskull }, + + {"tntkeyys", NULL, not_net | not_demo, + cheat_tntkeyxx, it_yellowskull}, + + {"tntkeybs", NULL, not_net | not_demo, + cheat_tntkeyxx, it_blueskull }, // killough 2/16/98: end generalized keys + + {"tntka", NULL, not_net | not_demo, + cheat_k }, // Ty 04/11/98 - Added TNTKA + + {"tntweap", NULL, not_net | not_demo, + cheat_tntweap }, // killough 2/16/98: generalized weapon cheats + + {"tntweap", NULL, not_net | not_demo, + cheat_tntweapx, -1}, + + {"tntammo", NULL, not_net | not_demo, + cheat_tntammo }, + + {"tntammo", NULL, not_net | not_demo, + cheat_tntammox, -1}, // killough 2/16/98: end generalized weapons + + {"tntsmart", NULL, not_net | not_demo, + cheat_smart}, // killough 2/21/98: smart monster toggle + + {"tntpitch", NULL, always, + cheat_pitch}, // killough 2/21/98: pitched sound toggle + + // killough 2/21/98: reduce RSI injury by adding simpler alias sequences: + + {"tntamo", NULL, not_net | not_demo, + cheat_tntammo }, // killough 2/21/98: same as tntammo + + {"tntamo", NULL, not_net | not_demo, + cheat_tntammox, -1}, // killough 2/21/98: same as tntammo + + {"tntfast", NULL, not_net | not_demo, + cheat_fast }, // killough 3/6/98: -fast toggle + + {"tntice", NULL, not_net | not_demo, + cheat_friction }, // phares 3/10/98: toggle variable friction effects + + {"tntpush", NULL, not_net | not_demo, + cheat_pushers }, // phares 3/10/98: toggle pushers + + {NULL} // end-of-list marker +}; + +//----------------------------------------------------------------------------- + +static void cheat_mus(buf) +char buf[3]; +{ + int musnum; + + //jff 3/20/98 note: this cheat allowed in netgame/demorecord + + //jff 3/17/98 avoid musnum being negative and crashing + if (!isdigit(buf[0]) || !isdigit(buf[1])) + return; + + plyr->message = s_STSTR_MUS; // Ty 03/27/98 - externalized + + if (gamemode == commercial) + { + musnum = mus_runnin + (buf[0]-'0')*10 + buf[1]-'0' - 1; + + //jff 4/11/98 prevent IDMUS00 in DOOMII and IDMUS36 or greater + if (musnum < mus_runnin || ((buf[0]-'0')*10 + buf[1]-'0') > 35) + plyr->message = s_STSTR_NOMUS; // Ty 03/27/98 - externalized + else + { + S_ChangeMusic(musnum, 1); + idmusnum = musnum; //jff 3/17/98 remember idmus number for restore + } + } + else + { + musnum = mus_e1m1 + (buf[0]-'1')*9 + (buf[1]-'1'); + + //jff 4/11/98 prevent IDMUS0x IDMUSx0 in DOOMI and greater than introa + if (buf[0] < '1' || buf[1] < '1' || ((buf[0]-'1')*9 + buf[1]-'1') > 31) + plyr->message = s_STSTR_NOMUS; // Ty 03/27/98 - externalized + else + { + S_ChangeMusic(musnum, 1); + idmusnum = musnum; //jff 3/17/98 remember idmus number for restore + } + } +} + +// 'choppers' invulnerability & chainsaw +static void cheat_choppers() +{ + plyr->weaponowned[wp_chainsaw] = true; + plyr->powers[pw_invulnerability] = true; + plyr->message = s_STSTR_CHOPPERS; // Ty 03/27/98 - externalized +} + +static void cheat_god() +{ // 'dqd' cheat for toggleable god mode + plyr->cheats ^= CF_GODMODE; + if (plyr->cheats & CF_GODMODE) + { + if (plyr->mo) + plyr->mo->health = god_health; // Ty 03/09/98 - deh + + plyr->health = god_health; + plyr->message = s_STSTR_DQDON; // Ty 03/27/98 - externalized + } + else + plyr->message = s_STSTR_DQDOFF; // Ty 03/27/98 - externalized +} + +// CPhipps - new health and armour cheat codes +static void cheat_health() +{ + if (!(plyr->cheats & CF_GODMODE)) { + if (plyr->mo) + plyr->mo->health = mega_health; + plyr->health = mega_health; + plyr->message = s_STSTR_BEHOLDX; // Ty 03/27/98 - externalized + } +} + +static void cheat_megaarmour() +{ + plyr->armorpoints = idfa_armor; // Ty 03/09/98 - deh + plyr->armortype = idfa_armor_class; // Ty 03/09/98 - deh + plyr->message = s_STSTR_BEHOLDX; // Ty 03/27/98 - externalized +} + +static void cheat_fa() +{ + int i; + + if (!plyr->backpack) + { + for (i=0 ; imaxammo[i] *= 2; + plyr->backpack = true; + } + + plyr->armorpoints = idfa_armor; // Ty 03/09/98 - deh + plyr->armortype = idfa_armor_class; // Ty 03/09/98 - deh + + // You can't own weapons that aren't in the game // phares 02/27/98 + for (i=0;iweaponowned[i] = true; + + for (i=0;iammo[i] = plyr->maxammo[i]; + + plyr->message = s_STSTR_FAADDED; +} + +static void cheat_k() +{ + int i; + for (i=0;icards[i]) // only print message if at least one key added + { // however, caller may overwrite message anyway + plyr->cards[i] = true; + plyr->message = "Keys Added"; + } +} + +static void cheat_kfa() +{ + cheat_k(); + cheat_fa(); + plyr->message = STSTR_KFAADDED; +} + +static void cheat_noclip() +{ + // Simplified, accepting both "noclip" and "idspispopd". + // no clipping mode cheat + + plyr->message = (plyr->cheats ^= CF_NOCLIP) & CF_NOCLIP ? + s_STSTR_NCON : s_STSTR_NCOFF; // Ty 03/27/98 - externalized +} + +// 'behold?' power-up cheats (modified for infinite duration -- killough) +static void cheat_pw(int pw) +{ + if (plyr->powers[pw]) + plyr->powers[pw] = pw!=pw_strength && pw!=pw_allmap; // killough + else + { + P_GivePower(plyr, pw); + if (pw != pw_strength) + plyr->powers[pw] = -1; // infinite duration -- killough + } + plyr->message = s_STSTR_BEHOLDX; // Ty 03/27/98 - externalized +} + +// 'behold' power-up menu +static void cheat_behold() +{ + plyr->message = s_STSTR_BEHOLD; // Ty 03/27/98 - externalized +} + +// 'clev' change-level cheat +static void cheat_clev(char buf[3]) +{ + int epsd, map; + + if (gamemode == commercial) + { + epsd = 1; //jff was 0, but espd is 1-based + map = (buf[0] - '0')*10 + buf[1] - '0'; + } + else + { + epsd = buf[0] - '0'; + map = buf[1] - '0'; + } + + // Catch invalid maps. + if (epsd < 1 || map < 1 || // Ohmygod - this is not going to work. + (gamemode == retail && (epsd > 4 || map > 9 )) || + (gamemode == registered && (epsd > 3 || map > 9 )) || + (gamemode == shareware && (epsd > 1 || map > 9 )) || + (gamemode == commercial && (epsd > 1 || map > 32 )) ) //jff no 33 and 34 + return; //8/14/98 allowed + + // So be it. + + idmusnum = -1; //jff 3/17/98 revert to normal level music on IDCLEV + + plyr->message = s_STSTR_CLEV; // Ty 03/27/98 - externalized + + G_DeferedInitNew(gameskill, epsd, map); +} + +// 'mypos' for player position +// killough 2/7/98: simplified using dprintf and made output more user-friendly +static void cheat_mypos() +{ + doom_printf("Position (%d,%d,%d)\tAngle %-.0f", + players[consoleplayer].mo->x >> FRACBITS, + players[consoleplayer].mo->y >> FRACBITS, + players[consoleplayer].mo->z >> FRACBITS, + players[consoleplayer].mo->angle * (90.0/ANG90)); +} + +// cph - cheat to toggle frame rate/rendering stats display +static void cheat_rate() +{ + rendering_stats ^= 1; +} + +// compatibility cheat + +static void cheat_comp() +{ + // CPhipps - modified for new compatibility system + compatibility_level++; compatibility_level %= MAX_COMPATIBILITY_LEVEL; + // must call G_Compatibility after changing compatibility_level + // (fixes sf bug number 1558738) + G_Compatibility(); + doom_printf("New compatibility level:\n%s", + comp_lev_str[compatibility_level]); +} + +// variable friction cheat +static void cheat_friction() +{ + plyr->message = // Ty 03/27/98 - *not* externalized + (variable_friction = !variable_friction) ? "Variable Friction enabled" : + "Variable Friction disabled"; +} + + +// Pusher cheat +// phares 3/10/98 +static void cheat_pushers() +{ + plyr->message = // Ty 03/27/98 - *not* externalized + (allow_pushers = !allow_pushers) ? "Pushers enabled" : "Pushers disabled"; +} + +static void cheat_massacre() // jff 2/01/98 kill all monsters +{ + // jff 02/01/98 'em' cheat - kill all monsters + // partially taken from Chi's .46 port + // + // killough 2/7/98: cleaned up code and changed to use dprintf; + // fixed lost soul bug (LSs left behind when PEs are killed) + + int killcount=0; + thinker_t *currentthinker = NULL; + extern void A_PainDie(mobj_t *); + + // killough 7/20/98: kill friendly monsters only if no others to kill + uint_64_t mask = MF_FRIEND; + P_MapStart(); + do + while ((currentthinker = P_NextThinker(currentthinker,th_all)) != NULL) + if (currentthinker->function == P_MobjThinker && + !(((mobj_t *) currentthinker)->flags & mask) && // killough 7/20/98 + (((mobj_t *) currentthinker)->flags & MF_COUNTKILL || + ((mobj_t *) currentthinker)->type == MT_SKULL)) + { // killough 3/6/98: kill even if PE is dead + if (((mobj_t *) currentthinker)->health > 0) + { + killcount++; + P_DamageMobj((mobj_t *)currentthinker, NULL, NULL, 10000); + } + if (((mobj_t *) currentthinker)->type == MT_PAIN) + { + A_PainDie((mobj_t *) currentthinker); // killough 2/8/98 + P_SetMobjState ((mobj_t *) currentthinker, S_PAIN_DIE6); + } + } + while (!killcount && mask ? mask=0, 1 : 0); // killough 7/20/98 + P_MapEnd(); + // killough 3/22/98: make more intelligent about plural + // Ty 03/27/98 - string(s) *not* externalized + doom_printf("%d Monster%s Killed", killcount, killcount==1 ? "" : "s"); +} + +// killough 2/7/98: move iddt cheat from am_map.c to here +// killough 3/26/98: emulate Doom better +static void cheat_ddt() +{ + extern int ddt_cheating; + if (automapmode & am_active) + ddt_cheating = (ddt_cheating+1) % 3; +} + +// killough 2/7/98: HOM autodetection +static void cheat_hom() +{ + extern int autodetect_hom; // Ty 03/27/98 - *not* externalized + plyr->message = (autodetect_hom = !autodetect_hom) ? "HOM Detection On" : + "HOM Detection Off"; +} + +// killough 3/6/98: -fast parameter toggle +static void cheat_fast() +{ + plyr->message = (fastparm = !fastparm) ? "Fast Monsters On" : + "Fast Monsters Off"; // Ty 03/27/98 - *not* externalized + G_SetFastParms(fastparm); // killough 4/10/98: set -fast parameter correctly +} + +// killough 2/16/98: keycard/skullkey cheat functions +static void cheat_tntkey() +{ + plyr->message = "Red, Yellow, Blue"; // Ty 03/27/98 - *not* externalized +} + +static void cheat_tntkeyx() +{ + plyr->message = "Card, Skull"; // Ty 03/27/98 - *not* externalized +} + +static void cheat_tntkeyxx(int key) +{ + plyr->message = (plyr->cards[key] = !plyr->cards[key]) ? + "Key Added" : "Key Removed"; // Ty 03/27/98 - *not* externalized +} + +// killough 2/16/98: generalized weapon cheats + +static void cheat_tntweap() +{ // Ty 03/27/98 - *not* externalized + plyr->message = gamemode==commercial ? // killough 2/28/98 + "Weapon number 1-9" : "Weapon number 1-8"; +} + +static void cheat_tntweapx(buf) +char buf[3]; +{ + int w = *buf - '1'; + + if ((w==wp_supershotgun && gamemode!=commercial) || // killough 2/28/98 + ((w==wp_bfg || w==wp_plasma) && gamemode==shareware)) + return; + + if (w==wp_fist) // make '1' apply beserker strength toggle + cheat_pw(pw_strength); + else + if (w >= 0 && w < NUMWEAPONS) { + if ((plyr->weaponowned[w] = !plyr->weaponowned[w])) + plyr->message = "Weapon Added"; // Ty 03/27/98 - *not* externalized + else + { + plyr->message = "Weapon Removed"; // Ty 03/27/98 - *not* externalized + if (w==plyr->readyweapon) // maybe switch if weapon removed + plyr->pendingweapon = P_SwitchWeapon(plyr); + } + } +} + +// killough 2/16/98: generalized ammo cheats +static void cheat_tntammo() +{ + plyr->message = "Ammo 1-4, Backpack"; // Ty 03/27/98 - *not* externalized +} + +static void cheat_tntammox(buf) +char buf[1]; +{ + int a = *buf - '1'; + if (*buf == 'b') // Ty 03/27/98 - strings *not* externalized + if ((plyr->backpack = !plyr->backpack)) + for (plyr->message = "Backpack Added", a=0 ; amaxammo[a] <<= 1; + else + for (plyr->message = "Backpack Removed", a=0 ; aammo[a] > (plyr->maxammo[a] >>= 1)) + plyr->ammo[a] = plyr->maxammo[a]; + } + else + if (a>=0 && amessage = (plyr->ammo[a] = !plyr->ammo[a]) ? + plyr->ammo[a] = plyr->maxammo[a], "Ammo Added" : "Ammo Removed"; + } +} + +static void cheat_smart() +{ + plyr->message = (monsters_remember = !monsters_remember) ? + "Smart Monsters Enabled" : "Smart Monsters Disabled"; +} + +static void cheat_pitch() +{ + plyr->message=(pitched_sounds = !pitched_sounds) ? "Pitch Effects Enabled" : + "Pitch Effects Disabled"; +} + +//----------------------------------------------------------------------------- +// 2/7/98: Cheat detection rewritten by Lee Killough, to avoid +// scrambling and to use a more general table-driven approach. +//----------------------------------------------------------------------------- + +#define CHEAT_ARGS_MAX 8 /* Maximum number of args at end of cheats */ + +boolean M_FindCheats(int key) +{ + static uint_64_t sr; + static char argbuf[CHEAT_ARGS_MAX+1], *arg; + static int init, argsleft, cht; + int i, ret, matchedbefore; + + // If we are expecting arguments to a cheat + // (e.g. idclev), put them in the arg buffer + + if (argsleft) + { + *arg++ = tolower(key); // store key in arg buffer + if (!--argsleft) // if last key in arg list, + cheat[cht].func(argbuf); // process the arg buffer + return 1; // affirmative response + } + + key = tolower(key) - 'a'; + if (key < 0 || key >= 32) // ignore most non-alpha cheat letters + { + sr = 0; // clear shift register + return 0; + } + + if (!init) // initialize aux entries of table + { + init = 1; + for (i=0;cheat[i].cheat;i++) + { + uint_64_t c=0, m=0; + const char *p; + + for (p=cheat[i].cheat; *p; p++) + { + unsigned key = tolower(*p)-'a'; // convert to 0-31 + if (key >= 32) // ignore most non-alpha cheat letters + continue; + c = (c<<5) + key; // shift key into code + m = (m<<5) + 31; // shift 1's into mask + } + cheat[i].code = c; // code for this cheat key + cheat[i].mask = m; // mask for this cheat key + } + } + + sr = (sr<<5) + key; // shift this key into shift register + + for (matchedbefore = ret = i = 0; cheat[i].cheat; i++) + if ((sr & cheat[i].mask) == cheat[i].code && // if match found + !(cheat[i].when & not_dm && deathmatch) && // and if cheat allowed + !(cheat[i].when & not_coop && netgame && !deathmatch) && + !(cheat[i].when & not_demo && demoplayback) && + !(cheat[i].when & not_menu && menuactive) && + !(cheat[i].when & not_deh && M_CheckParm("-deh"))) { + if (cheat[i].arg < 0) // if additional args are required + { + cht = i; // remember this cheat code + arg = argbuf; // point to start of arg buffer + argsleft = -cheat[i].arg; // number of args expected + ret = 1; // responder has eaten key + } + else + if (!matchedbefore) // allow only one cheat at a time + { + matchedbefore = ret = 1; // responder has eaten key + cheat[i].func(cheat[i].arg); // call cheat handler + } + } + return ret; +} diff --git a/src/m_cheat.h b/src/m_cheat.h new file mode 100644 index 00000000..f7bafcea --- /dev/null +++ b/src/m_cheat.h @@ -0,0 +1,58 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Cheat code checking. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __M_CHEAT__ +#define __M_CHEAT__ + +/* killough 4/16/98: Cheat table structure */ + +extern struct cheat_s { + const char * cheat; + const char *const deh_cheat; + enum { + always = 0, + not_dm = 1, + not_coop = 2, + not_demo = 4, + not_menu = 8, + not_deh = 16, + not_net = not_dm | not_coop + } const when; + void (*const func)(); + const int arg; + uint_64_t code, mask; +} cheat[]; + +boolean M_FindCheats(int key); + +#endif diff --git a/src/m_fixed.h b/src/m_fixed.h new file mode 100644 index 00000000..15e24d5c --- /dev/null +++ b/src/m_fixed.h @@ -0,0 +1,102 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Fixed point arithemtics, implementation. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __M_FIXED__ +#define __M_FIXED__ + +#include "config.h" +#include "doomtype.h" + +/* + * Fixed point, 32bit as 16.16. + */ + +#define FRACBITS 16 +#define FRACUNIT (1<> (8*sizeof _t-1); + return (_t^_s)-_s; +} + +/* + * Fixed Point Multiplication + */ + +/* CPhipps - made __inline__ to inline, as specified in the gcc docs + * Also made const */ + +inline static CONSTFUNC fixed_t FixedMul(fixed_t a, fixed_t b) +{ + return (fixed_t)((int_64_t) a*b >> FRACBITS); +} + +/* + * Fixed Point Division + */ + +/* CPhipps - made __inline__ to inline, as specified in the gcc docs + * Also made const */ + +inline static CONSTFUNC fixed_t FixedDiv(fixed_t a, fixed_t b) +{ + return (D_abs(a)>>14) >= D_abs(b) ? ((a^b)>>31) ^ INT_MAX : + (fixed_t)(((int_64_t) a << FRACBITS) / b); +} + +/* CPhipps - + * FixedMod - returns a % b, guaranteeing 0<=a +#include + +#include "doomdef.h" +#include "doomstat.h" +#include "dstrings.h" +#include "d_main.h" +#include "v_video.h" +#include "w_wad.h" +#include "r_main.h" +#include "hu_stuff.h" +#include "g_game.h" +#include "s_sound.h" +#include "sounds.h" +#include "m_menu.h" +#include "d_deh.h" +#include "m_misc.h" +#include "lprintf.h" +#include "am_map.h" +#include "i_main.h" +#include "i_system.h" +#include "i_video.h" +#include "i_sound.h" +#include "r_demo.h" +#include "r_fps.h" + +extern patchnum_t hu_font[HU_FONTSIZE]; +extern boolean message_dontfuckwithme; + +extern boolean chat_on; // in heads-up code +extern int has_exited; + +// +// defaulted values +// + +int mouseSensitivity_horiz; // has default // killough +int mouseSensitivity_vert; // has default + +int showMessages; // Show messages has default, 0 = off, 1 = on + +int hide_setup=1; // killough 5/15/98 + +// Blocky mode, has default, 0 = high, 1 = normal +//int detailLevel; obsolete -- killough +int screenblocks; // has default + +int quickSaveSlot; // -1 = no quicksave slot picked! + +int messageToPrint; // 1 = message to be printed + +// CPhipps - static const +static const char* messageString; // ...and here is the message string! + +// message x & y +int messx; +int messy; +int messageLastMenuActive; + +boolean messageNeedsInput; // timed message = no input from user + +void (*messageRoutine)(int response); + +#define SAVESTRINGSIZE 24 + +/* killough 8/15/98: when changes are allowed to sync-critical variables */ +static int allow_changes(void) +{ + return !(demoplayback || netgame); +} + +static void M_UpdateCurrent(default_t* def) +{ + /* cph - requires rewrite of m_misc.c */ + if (def->current) { + if (allow_changes()) /* killough 8/15/98 */ + *def->current = *def->location.pi; + else if (*def->current != *def->location.pi) + warn_about_changes(S_LEVWARN); /* killough 8/15/98 */ + } +} + +int warning_about_changes, print_warning_about_changes; + +/* cphipps - M_DrawBackground renamed and moved to v_video.c */ + +boolean menu_background = 1; // do Boom fullscreen menus have backgrounds? + +static void M_DrawBackground(const char *flat, int scrn) +{ + if (menu_background) + V_DrawBackground(flat, scrn); +} + +// we are going to be entering a savegame string + +int saveStringEnter; +int saveSlot; // which slot to save in +int saveCharIndex; // which char we're editing +// old save description before edit +char saveOldString[SAVESTRINGSIZE]; + +boolean inhelpscreens; // indicates we are in or just left a help screen + +enum menuactive_e menuactive; // The menus are up + +#define SKULLXOFF -32 +#define LINEHEIGHT 16 + +char savegamestrings[10][SAVESTRINGSIZE]; + +// +// MENU TYPEDEFS +// + +typedef struct +{ + short status; // 0 = no cursor here, 1 = ok, 2 = arrows ok + char name[10]; + + // choice = menu item #. + // if status = 2, + // choice=0:leftarrow,1:rightarrow + void (*routine)(int choice); + char alphaKey; // hotkey in menu + const char *alttext; +} menuitem_t; + +typedef struct menu_s +{ + short numitems; // # of menu items + struct menu_s* prevMenu; // previous menu + menuitem_t* menuitems; // menu items + void (*routine)(); // draw routine + short x; + short y; // x,y of menu + short lastOn; // last item user was on in menu +} menu_t; + +short itemOn; // menu item skull is on (for Big Font menus) +short skullAnimCounter; // skull animation counter +short whichSkull; // which skull to draw (he blinks) + +// graphic name of skulls + +const char skullName[2][/*8*/9] = {"M_SKULL1","M_SKULL2"}; + +menu_t* currentMenu; // current menudef + +// phares 3/30/98 +// externs added for setup menus + +int mapcolor_me; // cph + +extern int map_point_coordinates; // killough 10/98 + +extern char* chat_macros[]; // chat macros +extern const char* shiftxform; +extern default_t defaults[]; +extern int numdefaults; + +// end of externs added for setup menus + +// +// PROTOTYPES +// +void M_NewGame(int choice); +void M_Episode(int choice); +void M_ChooseSkill(int choice); +void M_LoadGame(int choice); +void M_SaveGame(int choice); +void M_Options(int choice); +void M_EndGame(int choice); +void M_ReadThis(int choice); +void M_ReadThis2(int choice); +void M_QuitDOOM(int choice); + +void M_ChangeMessages(int choice); +void M_ChangeSensitivity(int choice); +void M_SfxVol(int choice); +void M_MusicVol(int choice); +/* void M_ChangeDetail(int choice); unused -- killough */ +void M_SizeDisplay(int choice); +void M_StartGame(int choice); +void M_Sound(int choice); + +void M_Mouse(int choice, int *sens); /* killough */ +void M_MouseVert(int choice); +void M_MouseHoriz(int choice); +void M_DrawMouse(void); + +void M_FinishReadThis(int choice); +void M_FinishHelp(int choice); // killough 10/98 +void M_LoadSelect(int choice); +void M_SaveSelect(int choice); +void M_ReadSaveStrings(void); +void M_QuickSave(void); +void M_QuickLoad(void); + +void M_DrawMainMenu(void); +void M_DrawReadThis1(void); +void M_DrawReadThis2(void); +void M_DrawNewGame(void); +void M_DrawEpisode(void); +void M_DrawOptions(void); +void M_DrawSound(void); +void M_DrawLoad(void); +void M_DrawSave(void); +void M_DrawSetup(void); // phares 3/21/98 +void M_DrawHelp (void); // phares 5/04/98 + +void M_DrawSaveLoadBorder(int x,int y); +void M_SetupNextMenu(menu_t *menudef); +void M_DrawThermo(int x,int y,int thermWidth,int thermDot); +void M_DrawEmptyCell(menu_t *menu,int item); +void M_DrawSelCell(menu_t *menu,int item); +void M_WriteText(int x, int y, const char *string, int cm); +int M_StringWidth(const char *string); +int M_StringHeight(const char *string); +void M_DrawTitle(int x, int y, const char *patch, int cm, + const char *alttext, int altcm); +void M_StartMessage(const char *string,void *routine,boolean input); +void M_StopMessage(void); +void M_ClearMenus (void); + +// phares 3/30/98 +// prototypes added to support Setup Menus and Extended HELP screens + +int M_GetKeyString(int,int); +void M_Setup(int choice); +void M_KeyBindings(int choice); +void M_Weapons(int); +void M_StatusBar(int); +void M_Automap(int); +void M_Enemy(int); +void M_Messages(int); +void M_ChatStrings(int); +void M_InitExtendedHelp(void); +void M_ExtHelpNextScreen(int); +void M_ExtHelp(int); +static int M_GetPixelWidth(const char*); +void M_DrawKeybnd(void); +void M_DrawWeapons(void); +static void M_DrawMenuString(int,int,int); +static void M_DrawStringCentered(int,int,int,const char*); +void M_DrawStatusHUD(void); +void M_DrawExtHelp(void); +void M_DrawAutoMap(void); +void M_DrawEnemy(void); +void M_DrawMessages(void); +void M_DrawChatStrings(void); +void M_Compat(int); // killough 10/98 +void M_ChangeDemoSmoothTurns(void); +void M_General(int); // killough 10/98 +void M_DrawCompat(void); // killough 10/98 +void M_DrawGeneral(void); // killough 10/98 + +menu_t NewDef; // phares 5/04/98 + +// end of prototypes added to support Setup Menus and Extended HELP screens + +///////////////////////////////////////////////////////////////////////////// +// +// DOOM MENUS +// + +///////////////////////////// +// +// MAIN MENU +// + +// main_e provides numerical values for which Big Font screen you're on + +enum +{ + newgame = 0, + loadgame, + savegame, + options, + readthis, + quitdoom, + main_end +} main_e; + +// +// MainMenu is the definition of what the main menu Screen should look +// like. Each entry shows that the cursor can land on each item (1), the +// built-in graphic lump (i.e. "M_NGAME") that should be displayed, +// the program which takes over when an item is selected, and the hotkey +// associated with the item. +// + +menuitem_t MainMenu[]= +{ + {1,"M_NGAME", M_NewGame, 'n'}, + {1,"M_OPTION",M_Options, 'o'}, + {1,"M_LOADG", M_LoadGame,'l'}, + {1,"M_SAVEG", M_SaveGame,'s'}, + // Another hickup with Special edition. + {1,"M_RDTHIS",M_ReadThis,'r'}, + {1,"M_QUITG", M_QuitDOOM,'q'} +}; + +menu_t MainDef = +{ + main_end, // number of menu items + NULL, // previous menu screen + MainMenu, // table that defines menu items + M_DrawMainMenu, // drawing routine + 97,64, // initial cursor position + 0 // last menu item the user was on +}; + +// +// M_DrawMainMenu +// + +void M_DrawMainMenu(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(94, 2, 0, "M_DOOM", CR_DEFAULT, VPT_NONE); +} + +///////////////////////////// +// +// Read This! MENU 1 & 2 +// + +// There are no menu items on the Read This! screens, so read_e just +// provides a placeholder to maintain structure. + +enum +{ + rdthsempty1, + read1_end +} read_e; + +enum +{ + rdthsempty2, + read2_end +} read_e2; + +enum // killough 10/98 +{ + helpempty, + help_end +} help_e; + + +// The definitions of the Read This! screens + +menuitem_t ReadMenu1[] = +{ + {1,"",M_ReadThis2,0} +}; + +menuitem_t ReadMenu2[]= +{ + {1,"",M_FinishReadThis,0} +}; + +menuitem_t HelpMenu[]= // killough 10/98 +{ + {1,"",M_FinishHelp,0} +}; + +menu_t ReadDef1 = +{ + read1_end, + &MainDef, + ReadMenu1, + M_DrawReadThis1, + 330,175, + //280,185, // killough 2/21/98: fix help screens + 0 +}; + +menu_t ReadDef2 = +{ + read2_end, + &ReadDef1, + ReadMenu2, + M_DrawReadThis2, + 330,175, + 0 +}; + +menu_t HelpDef = // killough 10/98 +{ + help_end, + &HelpDef, + HelpMenu, + M_DrawHelp, + 330,175, + 0 +}; + +// +// M_ReadThis +// + +void M_ReadThis(int choice) +{ + M_SetupNextMenu(&ReadDef1); +} + +void M_ReadThis2(int choice) +{ + M_SetupNextMenu(&ReadDef2); +} + +void M_FinishReadThis(int choice) +{ + M_SetupNextMenu(&MainDef); +} + +void M_FinishHelp(int choice) // killough 10/98 +{ + M_SetupNextMenu(&MainDef); +} + +// +// Read This Menus +// Had a "quick hack to fix romero bug" +// +// killough 10/98: updated with new screens + +void M_DrawReadThis1(void) +{ + inhelpscreens = true; + if (gamemode == shareware) + V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_NONE); + else + M_DrawCredits(); +} + +// +// Read This Menus - optional second page. +// +// killough 10/98: updated with new screens + +void M_DrawReadThis2(void) +{ + inhelpscreens = true; + if (gamemode == shareware) + M_DrawCredits(); + else + V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_NONE); +} + +///////////////////////////// +// +// EPISODE SELECT +// + +// +// episodes_e provides numbers for the episode menu items. The default is +// 4, to accomodate Ultimate Doom. If the user is running anything else, +// this is accounted for in the code. +// + +enum +{ + ep1, + ep2, + ep3, + ep4, + ep_end +} episodes_e; + +// The definitions of the Episodes menu + +menuitem_t EpisodeMenu[]= +{ + {1,"M_EPI1", M_Episode,'k'}, + {1,"M_EPI2", M_Episode,'t'}, + {1,"M_EPI3", M_Episode,'i'}, + {1,"M_EPI4", M_Episode,'t'} +}; + +menu_t EpiDef = +{ + ep_end, // # of menu items + &MainDef, // previous menu + EpisodeMenu, // menuitem_t -> + M_DrawEpisode, // drawing routine -> + 48,63, // x,y + ep1 // lastOn +}; + +// +// M_Episode +// +int epi; + +void M_DrawEpisode(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(54, 38, 0, "M_EPISOD", CR_DEFAULT, VPT_NONE); +} + +void M_Episode(int choice) +{ + if ( (gamemode == shareware) && choice) { + M_StartMessage(s_SWSTRING,NULL,false); // Ty 03/27/98 - externalized + M_SetupNextMenu(&ReadDef1); + return; + } + + // Yet another hack... + if ( (gamemode == registered) && (choice > 2)) + { + lprintf( LO_WARN, + "M_Episode: 4th episode requires UltimateDOOM\n"); + choice = 0; + } + + epi = choice; + M_SetupNextMenu(&NewDef); +} + +///////////////////////////// +// +// NEW GAME +// + +// numerical values for the New Game menu items + +enum +{ + killthings, + toorough, + hurtme, + violence, + nightmare, + newg_end +} newgame_e; + +// The definitions of the New Game menu + +menuitem_t NewGameMenu[]= +{ + {1,"M_JKILL", M_ChooseSkill, 'i'}, + {1,"M_ROUGH", M_ChooseSkill, 'h'}, + {1,"M_HURT", M_ChooseSkill, 'h'}, + {1,"M_ULTRA", M_ChooseSkill, 'u'}, + {1,"M_NMARE", M_ChooseSkill, 'n'} +}; + +menu_t NewDef = +{ + newg_end, // # of menu items + &EpiDef, // previous menu + NewGameMenu, // menuitem_t -> + M_DrawNewGame, // drawing routine -> + 48,63, // x,y + hurtme // lastOn +}; + +// +// M_NewGame +// + +void M_DrawNewGame(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(96, 14, 0, "M_NEWG", CR_DEFAULT, VPT_NONE); + V_DrawNamePatch(54, 38, 0, "M_SKILL",CR_DEFAULT, VPT_NONE); +} + +/* cph - make `New Game' restart the level in a netgame */ +static void M_RestartLevelResponse(int ch) +{ + if (ch != 'y') + return; + + currentMenu->lastOn = itemOn; + M_ClearMenus (); + G_RestartLevel (); +} + +void M_NewGame(int choice) +{ + if (netgame && !demoplayback) { + if (compatibility_level < lxdoom_1_compatibility) + M_StartMessage(s_NEWGAME,NULL,false); // Ty 03/27/98 - externalized + else // CPhipps - query restarting the level + M_RestartLevelResponse('y'); + return; + } + + if ( gamemode == commercial ) + M_SetupNextMenu(&NewDef); + else + M_SetupNextMenu(&EpiDef); +} + +// CPhipps - static +static void M_VerifyNightmare(int ch) +{ + if (ch != 'y') + return; + + G_DeferedInitNew(nightmare,epi+1,1); + M_ClearMenus (); +} + +void M_ChooseSkill(int choice) +{ + if (choice == nightmare) + { // Ty 03/27/98 - externalized + M_VerifyNightmare('y'); + return; + } + + G_DeferedInitNew(choice,epi+1,1); + M_ClearMenus (); +} + +///////////////////////////// +// +// LOAD GAME MENU +// + +// numerical values for the Load Game slots + +enum +{ + load1, + load2, + load3, + load4, + load5, + load6, + load7, //jff 3/15/98 extend number of slots + load8, + load_end +} load_e; + +// The definitions of the Load Game screen + +menuitem_t LoadMenue[]= +{ + {1,"", M_LoadSelect,'1'}, + {1,"", M_LoadSelect,'2'}, + {1,"", M_LoadSelect,'3'}, + {1,"", M_LoadSelect,'4'}, + {1,"", M_LoadSelect,'5'}, + {1,"", M_LoadSelect,'6'}, + {1,"", M_LoadSelect,'7'}, //jff 3/15/98 extend number of slots + {1,"", M_LoadSelect,'8'}, +}; + +menu_t LoadDef = +{ + load_end, + &MainDef, + LoadMenue, + M_DrawLoad, + 80,34, //jff 3/15/98 move menu up + 0 +}; + +#define LOADGRAPHIC_Y 8 + +// +// M_LoadGame & Cie. +// + +void M_DrawLoad(void) +{ + int i; + + //jff 3/15/98 use symbolic load position + // CPhipps - patch drawing updated + V_DrawNamePatch(72 ,LOADGRAPHIC_Y, 0, "M_LOADG", CR_DEFAULT, VPT_NONE); + for (i = 0 ; i < load_end ; i++) { + M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); + M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i], CR_DEFAULT); + } +} + +// +// Draw border for the savegame description +// + +void M_DrawSaveLoadBorder(int x,int y) +{ + int i; + + V_DrawNamePatch(x-8, y+7, 0, "M_LSLEFT", CR_DEFAULT, VPT_NONE); + + for (i = 0 ; i < 24 ; i++) + { + V_DrawNamePatch(x, y+7, 0, "M_LSCNTR", CR_DEFAULT, VPT_NONE); + x += 8; + } + + V_DrawNamePatch(x, y+7, 0, "M_LSRGHT", CR_DEFAULT, VPT_NONE); +} + +// +// User wants to load this game +// + +void M_LoadSelect(int choice) +{ + // CPhipps - Modified so savegame filename is worked out only internal + // to g_game.c, this only passes the slot. + + G_LoadGame(choice, false); // killough 3/16/98, 5/15/98: add slot, cmd + + M_ClearMenus (); +} + +// +// killough 5/15/98: add forced loadgames +// + +static char *forced_loadgame_message; + +static void M_VerifyForcedLoadGame(int ch) +{ + if (ch=='y') + G_ForcedLoadGame(); + free(forced_loadgame_message); // free the message strdup()'ed below + M_ClearMenus(); +} + +void M_ForcedLoadGame(const char *msg) +{ + forced_loadgame_message = strdup(msg); // free()'d above + M_StartMessage(forced_loadgame_message, M_VerifyForcedLoadGame, true); +} + +// +// Selected from DOOM menu +// + +void M_LoadGame (int choice) +{ + M_SetupNextMenu(&LoadDef); + M_ReadSaveStrings(); +} + +///////////////////////////// +// +// SAVE GAME MENU +// + +// The definitions of the Save Game screen + +menuitem_t SaveMenu[]= +{ + {1,"", M_SaveSelect,'1'}, + {1,"", M_SaveSelect,'2'}, + {1,"", M_SaveSelect,'3'}, + {1,"", M_SaveSelect,'4'}, + {1,"", M_SaveSelect,'5'}, + {1,"", M_SaveSelect,'6'}, + {1,"", M_SaveSelect,'7'}, //jff 3/15/98 extend number of slots + {1,"", M_SaveSelect,'8'}, +}; + +menu_t SaveDef = +{ + load_end, // same number of slots as the Load Game screen + &MainDef, + SaveMenu, + M_DrawSave, + 80,34, //jff 3/15/98 move menu up + 0 +}; + +// +// M_ReadSaveStrings +// read the strings from the savegame files +// +void M_ReadSaveStrings(void) +{ + int i; + + for (i = 0 ; i < load_end ; i++) { + char name[PATH_MAX+1]; // killough 3/22/98 + FILE *fp; // killough 11/98: change to use stdio + + /* killough 3/22/98 + * cph - add not-demoplayback parameter */ + G_SaveGameName(name,sizeof(name),i,false); + fp = fopen(name,"rb"); + if (!fp) { // Ty 03/27/98 - externalized: + strcpy(&savegamestrings[i][0],s_EMPTYSTRING); + LoadMenue[i].status = 0; + continue; + } + fread(&savegamestrings[i], SAVESTRINGSIZE, 1, fp); + fclose(fp); + LoadMenue[i].status = 1; + } +} + +// +// M_SaveGame & Cie. +// +void M_DrawSave(void) +{ + int i; + + //jff 3/15/98 use symbolic load position + // CPhipps - patch drawing updated + V_DrawNamePatch(72, LOADGRAPHIC_Y, 0, "M_SAVEG", CR_DEFAULT, VPT_NONE); + for (i = 0 ; i < load_end ; i++) + { + M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i); + M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i], CR_DEFAULT); + } + + if (saveStringEnter) + { + i = M_StringWidth(savegamestrings[saveSlot]); + M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_", CR_DEFAULT); + } +} + +// +// M_Responder calls this when user is finished +// +static void M_DoSave(int slot) +{ + G_SaveGame (slot,savegamestrings[slot]); + M_ClearMenus (); + + // PICK QUICKSAVE SLOT YET? + if (quickSaveSlot == -2) + quickSaveSlot = slot; +} + +// +// User wants to save. Start string input for M_Responder +// +void M_SaveSelect(int choice) +{ + // we are going to be intercepting all chars + saveStringEnter = 1; + + saveSlot = choice; + strcpy(saveOldString,savegamestrings[choice]); + if (!strcmp(savegamestrings[choice],s_EMPTYSTRING)) // Ty 03/27/98 - externalized + savegamestrings[choice][0] = 0; + saveCharIndex = strlen(savegamestrings[choice]); +} + +// +// Selected from DOOM menu +// +void M_SaveGame (int choice) +{ + // killough 10/6/98: allow savegames during single-player demo playback + if (!usergame && (!demoplayback || netgame)) + { + M_StartMessage(s_SAVEDEAD,NULL,false); // Ty 03/27/98 - externalized + return; + } + + if (gamestate != GS_LEVEL) + return; + + M_SetupNextMenu(&SaveDef); + M_ReadSaveStrings(); +} + +///////////////////////////// +// +// OPTIONS MENU +// + +// numerical values for the Options menu items + +enum +{ + general, // killough 10/98 + // killough 4/6/98: move setup to be a sub-menu of OPTIONs + setup, // phares 3/21/98 + endgame, + messages, + /* detail, obsolete -- killough */ + scrnsize, + option_empty1, + mousesens, + /* option_empty2, submenu now -- killough */ + soundvol, + opt_end +} options_e; + +// The definitions of the Options menu + +menuitem_t OptionsMenu[]= +{ + // killough 4/6/98: move setup to be a sub-menu of OPTIONs + {1,"M_GENERL", M_General, 'g', "GENERAL"}, // killough 10/98 + {1,"M_SETUP", M_Setup, 's', "SETUP"}, // phares 3/21/98 + {1,"M_ENDGAM", M_EndGame,'e', "END GAME"}, + {1,"M_MESSG", M_ChangeMessages,'m', "MESSAGES"}, + {2,"M_SCRNSZ", M_SizeDisplay,'s', "SCREEN SIZE"}, + {1,"M_MSENS", M_ChangeSensitivity,'m', "MOUSE SENSITIVITY"}, + {1,"M_SVOL", M_Sound,'s', "SOUND VOLUME"}, +}; + +menu_t OptionsDef = +{ + opt_end, + &MainDef, + OptionsMenu, + M_DrawOptions, + 60,37, + 0 +}; + +// +// M_Options +// +char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"}; +char msgNames[2][9] = {"M_MSGOFF","M_MSGON"}; + + +void M_DrawOptions(void) +{ + // CPhipps - patch drawing updated + // proff/nicolas 09/20/98 -- changed for hi-res + V_DrawNamePatch(108, 15, 0, "M_OPTTTL", CR_DEFAULT, VPT_NONE); + + V_DrawNamePatch(OptionsDef.x + 120, OptionsDef.y+LINEHEIGHT*messages, 0, + msgNames[showMessages], CR_DEFAULT, VPT_NONE); + + V_DrawNamePatch(OptionsDef.x + 150, OptionsDef.y + (LINEHEIGHT*4), 0, + detailNames[!screenblocks], CR_DEFAULT, VPT_NONE); +} + +void M_Options(int choice) +{ + M_SetupNextMenu(&OptionsDef); +} + +///////////////////////////// +// +// M_QuitDOOM +// +int quitsounds[8] = +{ + sfx_pldeth, + sfx_dmpain, + sfx_popain, + sfx_slop, + sfx_telept, + sfx_posit1, + sfx_posit3, + sfx_sgtatk +}; + +int quitsounds2[8] = +{ + sfx_vilact, + sfx_getpow, + sfx_boscub, + sfx_slop, + sfx_skeswg, + sfx_kntdth, + sfx_bspact, + sfx_sgtatk +}; + +static void M_QuitResponse(int ch) +{ + if (ch != 'y') + return; + if ((!netgame || demoplayback) // killough 12/98 + && !nosfxparm && snd_card) // avoid delay if no sound card + { + int i; + + if (gamemode == commercial) + S_StartSound(NULL,quitsounds2[(gametic>>2)&7]); + else + S_StartSound(NULL,quitsounds[(gametic>>2)&7]); + + // wait till all sounds stopped or 3 seconds are over + i = 30; + while (i>0) { + I_uSleep(100000); // CPhipps - don't thrash cpu in this loop + if (!I_AnySoundStillPlaying()) + break; + i--; + } + } + I_SafeExit(1); +} + +void M_QuitDOOM(int choice) +{ + static char endstring[160]; + + // We pick index 0 which is language sensitive, + // or one at random, between 1 and maximum number. + // Ty 03/27/98 - externalized DOSY as a string s_DOSY that's in the sprintf + if(!has_exited) + M_QuitResponse('y'); +} + +///////////////////////////// +// +// SOUND VOLUME MENU +// + +// numerical values for the Sound Volume menu items +// The 'empty' slots are where the sliding scales appear. + +enum +{ + sfx_vol, + sfx_empty1, + music_vol, + sfx_empty2, + sound_end +} sound_e; + +// The definitions of the Sound Volume menu + +menuitem_t SoundMenu[]= +{ + {2,"M_SFXVOL",M_SfxVol,'s'}, + {-1,"",0}, + {2,"M_MUSVOL",M_MusicVol,'m'}, + {-1,"",0} +}; + +menu_t SoundDef = +{ + sound_end, + &OptionsDef, + SoundMenu, + M_DrawSound, + 80,64, + 0 +}; + +// +// Change Sfx & Music volumes +// + +void M_DrawSound(void) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(60, 38, 0, "M_SVOL", CR_DEFAULT, VPT_NONE); + + M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),16,snd_SfxVolume); + + M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),16,snd_MusicVolume); +} + +void M_Sound(int choice) +{ + M_SetupNextMenu(&SoundDef); +} + +void M_SfxVol(int choice) +{ + switch(choice) + { + case 0: + if (snd_SfxVolume) + snd_SfxVolume--; + break; + case 1: + if (snd_SfxVolume < 15) + snd_SfxVolume++; + break; + } + + S_SetSfxVolume(snd_SfxVolume /* *8 */); +} + +void M_MusicVol(int choice) +{ + switch(choice) + { + case 0: + if (snd_MusicVolume) + snd_MusicVolume--; + break; + case 1: + if (snd_MusicVolume < 15) + snd_MusicVolume++; + break; + } + + S_SetMusicVolume(snd_MusicVolume /* *8 */); +} + +///////////////////////////// +// +// MOUSE SENSITIVITY MENU -- killough +// + +// numerical values for the Mouse Sensitivity menu items +// The 'empty' slots are where the sliding scales appear. + +enum +{ + mouse_horiz, + mouse_empty1, + mouse_vert, + mouse_empty2, + mouse_end +} mouse_e; + +// The definitions of the Mouse Sensitivity menu + +menuitem_t MouseMenu[]= +{ + {2,"M_HORSEN",M_MouseHoriz,'h', "HORIZONTAL"}, + {-1,"",0}, + {2,"M_VERSEN",M_MouseVert,'v', "VERTICAL"}, + {-1,"",0} +}; + +menu_t MouseDef = +{ + mouse_end, + &OptionsDef, + MouseMenu, + M_DrawMouse, + 60,64, + 0 +}; + + +// I'm using a scale of 100 since I don't know what's normal -- killough. + +#define MOUSE_SENS_MAX 100 + +// +// Change Mouse Sensitivities -- killough +// + +void M_DrawMouse(void) +{ + int mhmx,mvmx; /* jff 4/3/98 clamp drawn position 99max mead */ + + // CPhipps - patch drawing updated + V_DrawNamePatch(60, 38, 0, "M_MSENS", CR_DEFAULT, VPT_NONE); + + //jff 4/3/98 clamp horizontal sensitivity display + mhmx = mouseSensitivity_horiz>99? 99 : mouseSensitivity_horiz; /*mead*/ + M_DrawThermo(MouseDef.x,MouseDef.y+LINEHEIGHT*(mouse_horiz+1),100,mhmx); + //jff 4/3/98 clamp vertical sensitivity display + mvmx = mouseSensitivity_vert>99? 99 : mouseSensitivity_vert; /*mead*/ + M_DrawThermo(MouseDef.x,MouseDef.y+LINEHEIGHT*(mouse_vert+1),100,mvmx); +} + +void M_ChangeSensitivity(int choice) +{ + M_SetupNextMenu(&MouseDef); // killough + + // switch(choice) + // { + // case 0: + // if (mouseSensitivity) + // mouseSensitivity--; + // break; + // case 1: + // if (mouseSensitivity < 9) + // mouseSensitivity++; + // break; + // } +} + +void M_MouseHoriz(int choice) +{ + M_Mouse(choice, &mouseSensitivity_horiz); +} + +void M_MouseVert(int choice) +{ + M_Mouse(choice, &mouseSensitivity_vert); +} + +void M_Mouse(int choice, int *sens) +{ + switch(choice) + { + case 0: + if (*sens) + --*sens; + break; + case 1: + if (*sens < 99) + ++*sens; /*mead*/ + break; + } +} + +///////////////////////////// +// +// M_QuickSave +// + +char tempstring[80]; + +void M_QuickSave(void) +{ + if (!usergame && (!demoplayback || netgame)) { /* killough 10/98 */ + S_StartSound(NULL,sfx_oof); + return; + } + + if (gamestate != GS_LEVEL) + return; + + if (quickSaveSlot < 0) { + M_StartControlPanel(); + M_ReadSaveStrings(); + M_SetupNextMenu(&SaveDef); + quickSaveSlot = -2; // means to pick a slot now + return; + } + sprintf(tempstring,s_QSPROMPT,savegamestrings[quickSaveSlot]); // Ty 03/27/98 - externalized + M_DoSave(quickSaveSlot); + S_StartSound(NULL,sfx_swtchx); +} + +///////////////////////////// +// +// M_QuickLoad +// + +static void M_QuickLoadResponse(int ch) +{ + if (ch == 'y') { + M_LoadSelect(quickSaveSlot); + S_StartSound(NULL,sfx_swtchx); + } +} + +void M_QuickLoad(void) +{ + // cph - removed restriction against quickload in a netgame + + if (quickSaveSlot < 0) { + M_StartMessage(s_QSAVESPOT,NULL,false); // Ty 03/27/98 - externalized + return; + } + sprintf(tempstring,s_QLPROMPT,savegamestrings[quickSaveSlot]); // Ty 03/27/98 - externalized + M_QuickLoadResponse('y'); +} + +///////////////////////////// +// +// M_EndGame +// + +static void M_EndGameResponse(int ch) +{ + if (ch != 'y') + return; + + currentMenu->lastOn = itemOn; + M_ClearMenus (); + D_StartTitle (); +} + +void M_EndGame(int choice) +{ + if (netgame) + { + M_StartMessage(s_NETEND,NULL,false); // Ty 03/27/98 - externalized + return; + } + M_EndGameResponse('y'); +} + +///////////////////////////// +// +// Toggle messages on/off +// + +void M_ChangeMessages(int choice) +{ + // warning: unused parameter `int choice' + choice = 0; + showMessages = 1 - showMessages; + + if (!showMessages) + players[consoleplayer].message = s_MSGOFF; // Ty 03/27/98 - externalized + else + players[consoleplayer].message = s_MSGON ; // Ty 03/27/98 - externalized + + message_dontfuckwithme = true; +} + +///////////////////////////// +// +// CHANGE DISPLAY SIZE +// +// jff 2/23/98 restored to pre-HUD state +// hud_active controlled soley by F5=key_detail (key_hud) +// hud_displayed is toggled by + or = in fullscreen +// hud_displayed is cleared by - + +void M_SizeDisplay(int choice) +{ + screenblocks = choice; + R_SetViewSize (screenblocks); +} + +// +// End of Original Menus +// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// SETUP MENU (phares) +// +// We've added a set of Setup Screens from which you can configure a number +// of variables w/o having to restart the game. There are 7 screens: +// +// Key Bindings +// Weapons +// Status Bar / HUD +// Automap +// Enemies +// Messages +// Chat Strings +// +// killough 10/98: added Compatibility and General menus +// + +///////////////////////////// +// +// booleans for setup screens +// these tell you what state the setup screens are in, and whether any of +// the overlay screens (automap colors, reset button message) should be +// displayed + +boolean setup_active = false; // in one of the setup screens +boolean set_keybnd_active = false; // in key binding setup screens +boolean set_weapon_active = false; // in weapons setup screen +boolean set_status_active = false; // in status bar/hud setup screen +boolean set_auto_active = false; // in automap setup screen +boolean set_enemy_active = false; // in enemies setup screen +boolean set_mess_active = false; // in messages setup screen +boolean set_chat_active = false; // in chat string setup screen +boolean setup_select = false; // changing an item +boolean setup_gather = false; // gathering keys for value +boolean colorbox_active = false; // color palette being shown +boolean default_verify = false; // verify reset defaults decision +boolean set_general_active = false; +boolean set_compat_active = false; + +///////////////////////////// +// +// set_menu_itemon is an index that starts at zero, and tells you which +// item on the current screen the cursor is sitting on. +// +// current_setup_menu is a pointer to the current setup menu table. + +static int set_menu_itemon; // which setup item is selected? // phares 3/98 +setup_menu_t* current_setup_menu; // points to current setup menu table + +///////////////////////////// +// +// The menu_buffer is used to construct strings for display on the screen. + +static char menu_buffer[64]; + +///////////////////////////// +// +// The setup_e enum is used to provide a unique number for each group of Setup +// Screens. + +enum +{ + set_compat, + set_key_bindings, + set_weapons, + set_statbar, + set_automap, + set_enemy, + set_messages, + set_chatstrings, + set_setup_end +} setup_e; + +int setup_screen; // the current setup screen. takes values from setup_e + +///////////////////////////// +// +// SetupMenu is the definition of what the main Setup Screen should look +// like. Each entry shows that the cursor can land on each item (1), the +// built-in graphic lump (i.e. "M_KEYBND") that should be displayed, +// the program which takes over when an item is selected, and the hotkey +// associated with the item. + +menuitem_t SetupMenu[]= +{ + {1,"M_COMPAT",M_Compat, 'p', "DOOM COMPATIBILITY"}, + {1,"M_KEYBND",M_KeyBindings,'k', "KEY BINDINGS"}, + {1,"M_WEAP" ,M_Weapons, 'w', "WEAPONS"}, + {1,"M_STAT" ,M_StatusBar, 's', "STATUS BAR / HUD"}, + {1,"M_AUTO" ,M_Automap, 'a', "AUTOMAP"}, + {1,"M_ENEM" ,M_Enemy, 'e', "ENEMIES"}, + {1,"M_MESS" ,M_Messages, 'm', "MESSAGES"}, + {1,"M_CHAT" ,M_ChatStrings,'c', "CHAT STRINGS"}, +}; + +///////////////////////////// +// +// M_DoNothing does just that: nothing. Just a placeholder. + +static void M_DoNothing(int choice) +{ +} + +///////////////////////////// +// +// Items needed to satisfy the 'Big Font' menu structures: +// +// the generic_setup_e enum mimics the 'Big Font' menu structures, but +// means nothing to the Setup Menus. + +enum +{ + generic_setupempty1, + generic_setup_end +} generic_setup_e; + +// Generic_Setup is a do-nothing definition that the mainstream Menu code +// can understand, while the Setup Menu code is working. Another placeholder. + +menuitem_t Generic_Setup[] = +{ + {1,"",M_DoNothing,0} +}; + +///////////////////////////// +// +// SetupDef is the menu definition that the mainstream Menu code understands. +// This is used by M_Setup (below) to define what is drawn and what is done +// with the main Setup screen. + +menu_t SetupDef = +{ + set_setup_end, // number of Setup Menu items (Key Bindings, etc.) + &OptionsDef, // menu to return to when BACKSPACE is hit on this menu + SetupMenu, // definition of items to show on the Setup Screen + M_DrawSetup, // program that draws the Setup Screen + 59,37, // x,y position of the skull (modified when the skull is + // drawn). The skull is parked on the upper-left corner + // of the Setup screens, since it isn't needed as a cursor + 0 // last item the user was on for this menu +}; + +///////////////////////////// +// +// Here are the definitions of the individual Setup Menu screens. They +// follow the format of the 'Big Font' menu structures. See the comments +// for SetupDef (above) to help understand what each of these says. + +menu_t KeybndDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawKeybnd, + 34,5, // skull drawn here + 0 +}; + +menu_t WeaponDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawWeapons, + 34,5, // skull drawn here + 0 +}; + +menu_t StatusHUDDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawStatusHUD, + 34,5, // skull drawn here + 0 +}; + +menu_t AutoMapDef = +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawAutoMap, + 34,5, // skull drawn here + 0 +}; + +menu_t EnemyDef = // phares 4/08/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawEnemy, + 34,5, // skull drawn here + 0 +}; + +menu_t MessageDef = // phares 4/08/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawMessages, + 34,5, // skull drawn here + 0 +}; + +menu_t ChatStrDef = // phares 4/10/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawChatStrings, + 34,5, // skull drawn here + 0 +}; + +menu_t GeneralDef = // killough 10/98 +{ + generic_setup_end, + &OptionsDef, + Generic_Setup, + M_DrawGeneral, + 34,5, // skull drawn here + 0 +}; + +menu_t CompatDef = // killough 10/98 +{ + generic_setup_end, + &SetupDef, + Generic_Setup, + M_DrawCompat, + 34,5, // skull drawn here + 0 +}; + +///////////////////////////// +// +// Draws the Title for the main Setup screen + +void M_DrawSetup(void) +{ + // CPhipps - patch drawing updated + M_DrawTitle(124, 15, "M_SETUP", CR_DEFAULT, "SETUP", CR_GOLD); +} + +///////////////////////////// +// +// Uses the SetupDef structure to draw the menu items for the main +// Setup screen + +void M_Setup(int choice) +{ + M_SetupNextMenu(&SetupDef); +} + +///////////////////////////// +// +// Data that's used by the Setup screen code +// +// Establish the message colors to be used + +#define CR_TITLE CR_GOLD +#define CR_SET CR_GREEN +#define CR_ITEM CR_RED +#define CR_HILITE CR_ORANGE +#define CR_SELECT CR_GRAY + +// Data used by the Automap color selection code + +#define CHIP_SIZE 7 // size of color block for colored items + +#define COLORPALXORIG ((320 - 16*(CHIP_SIZE+1))/2) +#define COLORPALYORIG ((200 - 16*(CHIP_SIZE+1))/2) + +#define PAL_BLACK 0 +#define PAL_WHITE 4 + +// Data used by the Chat String editing code + +#define CHAT_STRING_BFR_SIZE 128 + +// chat strings must fit in this screen space +// killough 10/98: reduced, for more general uses +#define MAXCHATWIDTH 272 + +int chat_index; +char* chat_string_buffer; // points to new chat strings while editing + +///////////////////////////// +// +// phares 4/17/98: +// Added 'Reset to Defaults' Button to Setup Menu screens +// This is a small button that sits in the upper-right-hand corner of +// the first screen for each group. It blinks when selected, thus the +// two patches, which it toggles back and forth. + +char ResetButtonName[2][8] = {"M_BUTT1","M_BUTT2"}; + +///////////////////////////// +// +// phares 4/18/98: +// Consolidate Item drawing code +// +// M_DrawItem draws the description of the provided item (the left-hand +// part). A different color is used for the text depending on whether the +// item is selected or not, or whether it's about to change. + +// CPhipps - static, hanging else removed, const parameter +static void M_DrawItem(const setup_menu_t* s) +{ + int x = s->m_x; + int y = s->m_y; + int flags = s->m_flags; + if (flags & S_RESET) + + // This item is the reset button + // Draw the 'off' version if this isn't the current menu item + // Draw the blinking version in tune with the blinking skull otherwise + + // proff/nicolas 09/20/98 -- changed for hi-res + // CPhipps - Patch drawing updated, reformatted + + V_DrawNamePatch(x, y, 0, ResetButtonName[(flags & (S_HILITE|S_SELECT)) ? whichSkull : 0], + CR_DEFAULT, VPT_NONE); + + else { // Draw the item string + char *p, *t; + int w = 0; + int color = + flags & S_SELECT ? CR_SELECT : + flags & S_HILITE ? CR_HILITE : + flags & (S_TITLE|S_NEXT|S_PREV) ? CR_TITLE : CR_ITEM; // killough 10/98 + + /* killough 10/98: + * Enhance to support multiline text separated by newlines. + * This supports multiline items on horizontally-crowded menus. + */ + + for (p = t = strdup(s->m_text); (p = strtok(p,"\n")); y += 8, p = NULL) + { /* killough 10/98: support left-justification: */ + strcpy(menu_buffer,p); + if (!(flags & S_LEFTJUST)) + w = M_GetPixelWidth(menu_buffer) + 4; + M_DrawMenuString(x - w, y ,color); + } + free(t); + } +} + +// If a number item is being changed, allow up to N keystrokes to 'gather' +// the value. Gather_count tells you how many you have so far. The legality +// of what is gathered is determined by the low/high settings for the item. + +#define MAXGATHER 5 +int gather_count; +char gather_buffer[MAXGATHER+1]; // killough 10/98: make input character-based + +///////////////////////////// +// +// phares 4/18/98: +// Consolidate Item Setting drawing code +// +// M_DrawSetting draws the setting of the provided item (the right-hand +// part. It determines the text color based on whether the item is +// selected or being changed. Then, depending on the type of item, it +// displays the appropriate setting value: yes/no, a key binding, a number, +// a paint chip, etc. + +static void M_DrawSetting(const setup_menu_t* s) +{ + int x = s->m_x, y = s->m_y, flags = s->m_flags, color; + + // Determine color of the text. This may or may not be used later, + // depending on whether the item is a text string or not. + + color = flags & S_SELECT ? CR_SELECT : flags & S_HILITE ? CR_HILITE : CR_SET; + + // Is the item a YES/NO item? + + if (flags & S_YESNO) { + strcpy(menu_buffer,*s->var.def->location.pi ? "YES" : "NO"); + M_DrawMenuString(x,y,color); + return; + } + + // Is the item a simple number? + + if (flags & S_NUM) { + // killough 10/98: We must draw differently for items being gathered. + if (flags & (S_HILITE|S_SELECT) && setup_gather) { + gather_buffer[gather_count] = 0; + strcpy(menu_buffer, gather_buffer); + } + else + sprintf(menu_buffer,"%d",*s->var.def->location.pi); + M_DrawMenuString(x,y,color); + return; + } + + // Is the item a key binding? + + if (flags & S_KEY) { // Key Binding + int *key = s->var.m_key; + + // Draw the key bound to the action + + if (key) { + M_GetKeyString(*key,0); // string to display + if (key == &key_up || key == &key_down || key == &key_speed || + key == &key_fire || key == &key_strafe || key == &key_use) + { + if (s->m_mouse && *s->m_mouse != -1) + sprintf(menu_buffer+strlen(menu_buffer), "/MB%d", + *s->m_mouse+1); + } + M_DrawMenuString(x,y,color); + } + return; + } + + // Is the item a weapon number? + // OR, Is the item a colored text string from the Automap? + // + // killough 10/98: removed special code, since the rest of the engine + // already takes care of it, and this code prevented the user from setting + // their overall weapons preferences while playing Doom 1. + // + // killough 11/98: consolidated weapons code with color range code + + if (flags & (S_WEAP|S_CRITEM)) // weapon number or color range + { + sprintf(menu_buffer,"%d", *s->var.def->location.pi); + M_DrawMenuString(x,y, flags & S_CRITEM ? *s->var.def->location.pi : color); + return; + } + + // Is the item a paint chip? + + if (flags & S_COLOR) // Automap paint chip + { + int ch; + + ch = *s->var.def->location.pi; + // proff 12/6/98: Drawing of colorchips completly changed for hi-res, it now uses a patch + // draw the paint chip + V_FillRect(0, x*SCREENWIDTH/320, (y-1)*SCREENHEIGHT/200, + 8*SCREENWIDTH/320, 8*SCREENHEIGHT/200, + PAL_BLACK); + V_FillRect(0, (x+1)*SCREENWIDTH/320, y*SCREENHEIGHT/200, + 6*SCREENWIDTH/320, 6*SCREENHEIGHT/200, + (byte)ch); + + if (!ch) // don't show this item in automap mode + V_DrawNamePatch(x+1,y,0,"M_PALNO", CR_DEFAULT, VPT_NONE); + return; + } + + // Is the item a chat string? + // killough 10/98: or a filename? + + if (flags & S_STRING) { + /* cph - cast to char* as it's really a Z_Strdup'd string (see m_misc.h) */ + union { const char **c; char **s; } u; // type punning via unions + char *text; + + u.c = s->var.def->location.ppsz; + text = *(u.s); + + // Are we editing this string? If so, display a cursor under + // the correct character. + + if (setup_select && (s->m_flags & (S_HILITE|S_SELECT))) { + int cursor_start, char_width; + char c[2]; + + // If the string is too wide for the screen, trim it back, + // one char at a time until it fits. This should only occur + // while you're editing the string. + + while (M_GetPixelWidth(text) >= MAXCHATWIDTH) { + int len = strlen(text); + text[--len] = 0; + if (chat_index > len) + chat_index--; + } + + // Find the distance from the beginning of the string to + // where the cursor should be drawn, plus the width of + // the char the cursor is under.. + + *c = text[chat_index]; // hold temporarily + c[1] = 0; + char_width = M_GetPixelWidth(c); + if (char_width == 1) + char_width = 7; // default for end of line + text[chat_index] = 0; // NULL to get cursor position + cursor_start = M_GetPixelWidth(text); + text[chat_index] = *c; // replace stored char + + // Now draw the cursor + // proff 12/6/98: Drawing of cursor changed for hi-res + V_FillRect(0, ((x+cursor_start-1)*SCREENWIDTH)/320, (y*SCREENHEIGHT)/200, + (char_width*SCREENWIDTH)/320, 9*SCREENHEIGHT/200, PAL_WHITE); + } + + // Draw the setting for the item + + strcpy(menu_buffer,text); + M_DrawMenuString(x,y,color); + return; + } + + // Is the item a selection of choices? + + if (flags & S_CHOICE) { + if (s->var.def->type == def_int) { + if (s->selectstrings == NULL) { + sprintf(menu_buffer,"%d",*s->var.def->location.pi); + } else { + strcpy(menu_buffer,s->selectstrings[*s->var.def->location.pi]); + } + } + + if (s->var.def->type == def_str) { + sprintf(menu_buffer,"%s", *s->var.def->location.ppsz); + } + + M_DrawMenuString(x,y,color); + return; + } +} + +///////////////////////////// +// +// M_DrawScreenItems takes the data for each menu item and gives it to +// the drawing routines above. + +// CPhipps - static, const parameter, formatting +static void M_DrawScreenItems(const setup_menu_t* src) +{ + if (print_warning_about_changes > 0) { /* killough 8/15/98: print warning */ + if (warning_about_changes & S_BADVAL) { + strcpy(menu_buffer, "Value out of Range"); + M_DrawMenuString(100,176,CR_RED); + } else if (warning_about_changes & S_PRGWARN) { + strcpy(menu_buffer, "Warning: Program must be restarted to see changes"); + M_DrawMenuString(3, 176, CR_RED); + } else if (warning_about_changes & S_BADVID) { + strcpy(menu_buffer, "Video mode not supported"); + M_DrawMenuString(80,176,CR_RED); + } else { + strcpy(menu_buffer, "Warning: Changes are pending until next game"); + M_DrawMenuString(18,184,CR_RED); + } + } + + while (!(src->m_flags & S_END)) { + + // See if we're to draw the item description (left-hand part) + + if (src->m_flags & S_SHOWDESC) + M_DrawItem(src); + + // See if we're to draw the setting (right-hand part) + + if (src->m_flags & S_SHOWSET) + M_DrawSetting(src); + src++; + } +} + +///////////////////////////// +// +// Data used to draw the "are you sure?" dialogue box when resetting +// to defaults. + +#define VERIFYBOXXORG 66 +#define VERIFYBOXYORG 88 +#define PAL_GRAY1 91 +#define PAL_GRAY2 98 +#define PAL_GRAY3 105 + +// And the routine to draw it. + +static void M_DrawDefVerify(void) +{ + // proff 12/6/98: Drawing of verify box changed for hi-res, it now uses a patch + V_DrawNamePatch(VERIFYBOXXORG,VERIFYBOXYORG,0,"M_VBOX",CR_DEFAULT,VPT_NONE); + // The blinking messages is keyed off of the blinking of the + // cursor skull. + + if (whichSkull) { // blink the text + strcpy(menu_buffer,"Reset to defaults? (Y or N)"); + M_DrawMenuString(VERIFYBOXXORG+8,VERIFYBOXYORG+8,CR_RED); + } +} + + +///////////////////////////// +// +// phares 4/18/98: +// M_DrawInstructions writes the instruction text just below the screen title +// +// cph 2006/08/06 - go back to the Boom version, and then clean up by using +// M_DrawStringCentered (much better than all those magic 'x' valies!) + +static void M_DrawInstructions(void) +{ + int flags = current_setup_menu[set_menu_itemon].m_flags; + + // There are different instruction messages depending on whether you + // are changing an item or just sitting on it. + + if (setup_select) { + switch (flags & (S_KEY | S_YESNO | S_WEAP | S_NUM | S_COLOR | S_CRITEM | S_CHAT | S_RESET | S_FILE | S_CHOICE)) { + case S_KEY: + // See if a joystick or mouse button setting is allowed for + // this item. + if (current_setup_menu[set_menu_itemon].m_mouse) + M_DrawStringCentered(160, 20, CR_SELECT, "Press key or button for this action"); + else + M_DrawStringCentered(160, 20, CR_SELECT, "Press key for this action"); + break; + + case S_YESNO: + M_DrawStringCentered(160, 20, CR_SELECT, "Press ENTER key to toggle"); + break; + case S_WEAP: + M_DrawStringCentered(160, 20, CR_SELECT, "Enter weapon number"); + break; + case S_NUM: + M_DrawStringCentered(160, 20, CR_SELECT, "Enter value. Press ENTER when finished."); + break; + case S_COLOR: + M_DrawStringCentered(160, 20, CR_SELECT, "Select color and press enter"); + break; + case S_CRITEM: + M_DrawStringCentered(160, 20, CR_SELECT, "Enter value"); + break; + case S_CHAT: + M_DrawStringCentered(160, 20, CR_SELECT, "Type/edit chat string and Press ENTER"); + break; + case S_FILE: + M_DrawStringCentered(160, 20, CR_SELECT, "Type/edit filename and Press ENTER"); + break; + case S_CHOICE: + M_DrawStringCentered(160, 20, CR_SELECT, "Press left or right to choose"); + break; + case S_RESET: + break; + } + } else { + if (flags & S_RESET) + M_DrawStringCentered(160, 20, CR_HILITE, "Press ENTER key to reset to defaults"); + else + M_DrawStringCentered(160, 20, CR_HILITE, "Press Enter to Change"); + } +} + + +///////////////////////////// +// +// The Key Binding Screen tables. + +#define KB_X 160 +#define KB_PREV 57 +#define KB_NEXT 310 +#define KB_Y 31 + +// phares 4/16/98: +// X,Y position of reset button. This is the same for every screen, and is +// only defined once here. + +#define X_BUTTON 301 +#define Y_BUTTON 3 + +// Definitions of the (in this case) four key binding screens. + +setup_menu_t keys_settings1[]; +setup_menu_t keys_settings2[]; +setup_menu_t keys_settings3[]; +setup_menu_t keys_settings4[]; + +// The table which gets you from one screen table to the next. + +setup_menu_t* keys_settings[] = +{ + keys_settings1, + keys_settings2, + keys_settings3, + keys_settings4, + NULL +}; + +int mult_screens_index; // the index of the current screen in a set + +// Here's an example from this first screen, with explanations. +// +// { +// "STRAFE", // The description of the item ('strafe' key) +// S_KEY, // This is a key binding item +// m_scrn, // It belongs to the m_scrn group. Its key cannot be +// // bound to two items in this group. +// KB_X, // The X offset of the start of the right-hand side +// KB_Y+ 8*8, // The Y offset of the start of the right-hand side. +// // Always given in multiples off a baseline. +// &key_strafe, // The variable that holds the key value bound to this +// OR a string that holds the config variable name. +// OR a pointer to another setup_menu +// &mousebstrafe, // The variable that holds the mouse button bound to + // this. If zero, no mouse button can be bound here. +// } + +// The first Key Binding screen table. +// Note that the Y values are ascending. If you need to add something to +// this table, (well, this one's not a good example, because it's full) +// you need to make sure the Y values still make sense so everything gets +// displayed. +// +// Note also that the first screen of each set has a line for the reset +// button. If there is more than one screen in a set, the others don't get +// the reset button. +// +// Note also that this screen has a "NEXT ->" line. This acts like an +// item, in that 'activating' it moves you along to the next screen. If +// there's a "<- PREV" item on a screen, it behaves similarly, moving you +// to the previous screen. If you leave these off, you can't move from +// screen to screen. + +setup_menu_t keys_settings1[] = // Key Binding screen strings +{ + {"MOVEMENT" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"FORWARD" ,S_KEY ,m_scrn,KB_X,KB_Y+1*8,{&key_up},&mousebforward}, + {"BACKWARD" ,S_KEY ,m_scrn,KB_X,KB_Y+2*8,{&key_down},&mousebbackward}, + {"TURN LEFT" ,S_KEY ,m_scrn,KB_X,KB_Y+3*8,{&key_left}}, + {"TURN RIGHT" ,S_KEY ,m_scrn,KB_X,KB_Y+4*8,{&key_right}}, + {"RUN" ,S_KEY ,m_scrn,KB_X,KB_Y+5*8,{&key_speed},0}, + {"STRAFE LEFT" ,S_KEY ,m_scrn,KB_X,KB_Y+6*8,{&key_strafeleft}}, + {"STRAFE RIGHT",S_KEY ,m_scrn,KB_X,KB_Y+7*8,{&key_straferight}}, + {"STRAFE" ,S_KEY ,m_scrn,KB_X,KB_Y+8*8,{&key_strafe},&mousebstrafe}, + {"AUTORUN" ,S_KEY ,m_scrn,KB_X,KB_Y+9*8,{&key_autorun}}, + {"180 TURN" ,S_KEY ,m_scrn,KB_X,KB_Y+10*8,{&key_reverse}}, + {"USE" ,S_KEY ,m_scrn,KB_X,KB_Y+11*8,{&key_use},&mousebforward}, + + {"MENUS" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+12*8}, + {"NEXT ITEM" ,S_KEY ,m_menu,KB_X,KB_Y+13*8,{&key_menu_down}}, + {"PREV ITEM" ,S_KEY ,m_menu,KB_X,KB_Y+14*8,{&key_menu_up}}, + {"LEFT" ,S_KEY ,m_menu,KB_X,KB_Y+15*8,{&key_menu_left}}, + {"RIGHT" ,S_KEY ,m_menu,KB_X,KB_Y+16*8,{&key_menu_right}}, + {"BACKSPACE" ,S_KEY ,m_menu,KB_X,KB_Y+17*8,{&key_menu_backspace}}, + {"SELECT ITEM" ,S_KEY ,m_menu,KB_X,KB_Y+18*8,{&key_menu_enter}}, + {"EXIT" ,S_KEY ,m_menu,KB_X,KB_Y+19*8,{&key_menu_escape}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +setup_menu_t keys_settings2[] = // Key Binding screen strings +{ + {"SCREEN" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + + // phares 4/13/98: + // key_help and key_escape can no longer be rebound. This keeps the + // player from getting themselves in a bind where they can't remember how + // to get to the menus, and can't remember how to get to the help screen + // to give them a clue as to how to get to the menus. :) + + // Also, the keys assigned to these functions cannot be bound to other + // functions. Introduce an S_KEEP flag to show that you cannot swap this + // key with other keys in the same 'group'. (m_scrn, etc.) + + {"HELP" ,S_SKIP|S_KEEP ,m_scrn,0 ,0 ,{&key_help}}, + {"MENU" ,S_SKIP|S_KEEP ,m_scrn,0 ,0 ,{&key_escape}}, + // killough 10/98: hotkey for entering setup menu: + {"SETUP" ,S_KEY ,m_scrn,KB_X,KB_Y+ 1*8,{&key_setup}}, + {"PAUSE" ,S_KEY ,m_scrn,KB_X,KB_Y+ 2*8,{&key_pause}}, + {"AUTOMAP" ,S_KEY ,m_scrn,KB_X,KB_Y+ 3*8,{&key_map}}, + {"VOLUME" ,S_KEY ,m_scrn,KB_X,KB_Y+ 4*8,{&key_soundvolume}}, + {"HUD" ,S_KEY ,m_scrn,KB_X,KB_Y+ 5*8,{&key_hud}}, + {"MESSAGES" ,S_KEY ,m_scrn,KB_X,KB_Y+ 6*8,{&key_messages}}, + {"GAMMA FIX" ,S_KEY ,m_scrn,KB_X,KB_Y+ 7*8,{&key_gamma}}, + {"SPY" ,S_KEY ,m_scrn,KB_X,KB_Y+ 8*8,{&key_spy}}, + {"LARGER VIEW" ,S_KEY ,m_scrn,KB_X,KB_Y+ 9*8,{&key_zoomin}}, + {"SMALLER VIEW",S_KEY ,m_scrn,KB_X,KB_Y+10*8,{&key_zoomout}}, + {"SCREENSHOT" ,S_KEY ,m_scrn,KB_X,KB_Y+11*8,{&key_screenshot}}, + {"GAME" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+12*8}, + {"SAVE" ,S_KEY ,m_scrn,KB_X,KB_Y+13*8,{&key_savegame}}, + {"LOAD" ,S_KEY ,m_scrn,KB_X,KB_Y+14*8,{&key_loadgame}}, + {"QUICKSAVE" ,S_KEY ,m_scrn,KB_X,KB_Y+15*8,{&key_quicksave}}, + {"QUICKLOAD" ,S_KEY ,m_scrn,KB_X,KB_Y+16*8,{&key_quickload}}, + {"END GAME" ,S_KEY ,m_scrn,KB_X,KB_Y+17*8,{&key_endgame}}, + {"QUIT" ,S_KEY ,m_scrn,KB_X,KB_Y+18*8,{&key_quit}}, + {"<- PREV", S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings1}}, + {"NEXT ->", S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t keys_settings3[] = // Key Binding screen strings +{ + {"WEAPONS" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"FIST" ,S_KEY ,m_scrn,KB_X,KB_Y+ 1*8,{&key_weapon1}}, + {"PISTOL" ,S_KEY ,m_scrn,KB_X,KB_Y+ 2*8,{&key_weapon2}}, + {"SHOTGUN" ,S_KEY ,m_scrn,KB_X,KB_Y+ 3*8,{&key_weapon3}}, + {"CHAINGUN",S_KEY ,m_scrn,KB_X,KB_Y+ 4*8,{&key_weapon4}}, + {"ROCKET" ,S_KEY ,m_scrn,KB_X,KB_Y+ 5*8,{&key_weapon5}}, + {"PLASMA" ,S_KEY ,m_scrn,KB_X,KB_Y+ 6*8,{&key_weapon6}}, + {"BFG", S_KEY ,m_scrn,KB_X,KB_Y+ 7*8,{&key_weapon7}}, + {"CHAINSAW",S_KEY ,m_scrn,KB_X,KB_Y+ 8*8,{&key_weapon8}}, + {"SSG" ,S_KEY ,m_scrn,KB_X,KB_Y+ 9*8,{&key_weapon9}}, + {"BEST" ,S_KEY ,m_scrn,KB_X,KB_Y+10*8,{&key_weapontoggle}}, + {"FIRE" ,S_KEY ,m_scrn,KB_X,KB_Y+11*8,{&key_fire},&mousebfire}, + {"NEXT" ,S_KEY ,m_scrn,KB_X,KB_Y+12*8,{&key_weaponcycleup}}, + {"PREV" ,S_KEY ,m_scrn,KB_X,KB_Y+13*8,{&key_weaponcycledown}}, + + {"<- PREV",S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings2}}, + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {keys_settings4}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} + +}; + +setup_menu_t keys_settings4[] = // Key Binding screen strings +{ + {"AUTOMAP" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y}, + {"FOLLOW" ,S_KEY ,m_map ,KB_X,KB_Y+ 1*8,{&key_map_follow}}, + {"ZOOM IN" ,S_KEY ,m_map ,KB_X,KB_Y+ 2*8,{&key_map_zoomin}}, + {"ZOOM OUT" ,S_KEY ,m_map ,KB_X,KB_Y+ 3*8,{&key_map_zoomout}}, + {"SHIFT UP" ,S_KEY ,m_map ,KB_X,KB_Y+ 4*8,{&key_map_up}}, + {"SHIFT DOWN" ,S_KEY ,m_map ,KB_X,KB_Y+ 5*8,{&key_map_down}}, + {"SHIFT LEFT" ,S_KEY ,m_map ,KB_X,KB_Y+ 6*8,{&key_map_left}}, + {"SHIFT RIGHT",S_KEY ,m_map ,KB_X,KB_Y+ 7*8,{&key_map_right}}, + {"MARK PLACE" ,S_KEY ,m_map ,KB_X,KB_Y+ 8*8,{&key_map_mark}}, + {"CLEAR MARKS",S_KEY ,m_map ,KB_X,KB_Y+ 9*8,{&key_map_clear}}, + {"FULL/ZOOM" ,S_KEY ,m_map ,KB_X,KB_Y+10*8,{&key_map_gobig}}, + {"GRID" ,S_KEY ,m_map ,KB_X,KB_Y+11*8,{&key_map_grid}}, + + {"CHATTING" ,S_SKIP|S_TITLE,m_null,KB_X,KB_Y+12*8}, + {"BEGIN CHAT" ,S_KEY ,m_scrn,KB_X,KB_Y+13*8,{&key_chat}}, + {"PLAYER 1" ,S_KEY ,m_scrn,KB_X,KB_Y+14*8,{&destination_keys[0]}}, + {"PLAYER 2" ,S_KEY ,m_scrn,KB_X,KB_Y+15*8,{&destination_keys[1]}}, + {"PLAYER 3" ,S_KEY ,m_scrn,KB_X,KB_Y+16*8,{&destination_keys[2]}}, + {"PLAYER 4" ,S_KEY ,m_scrn,KB_X,KB_Y+17*8,{&destination_keys[3]}}, + {"BACKSPACE" ,S_KEY ,m_scrn,KB_X,KB_Y+18*8,{&key_backspace}}, + {"ENTER" ,S_KEY ,m_scrn,KB_X,KB_Y+19*8,{&key_enter}}, + + {"<- PREV" ,S_SKIP|S_PREV,m_null,KB_PREV,KB_Y+20*8, {keys_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} + +}; + +// Setting up for the Key Binding screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_KeyBindings(int choice) +{ + M_SetupNextMenu(&KeybndDef); + + setup_active = true; + setup_screen = ss_keys; + set_keybnd_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = keys_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Key Bindings Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawKeybnd(void) +{ + menuactive = mnact_full; + + // Set up the Key Binding screen + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(84, 2, "M_KEYBND", CR_DEFAULT, "KEY BINDINGS", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Weapon Screen tables. + +#define WP_X 203 +#define WP_Y 33 + +// There's only one weapon settings screen (for now). But since we're +// trying to fit a common description for screens, it gets a setup_menu_t, +// which only has one screen definition in it. +// +// Note that this screen has no PREV or NEXT items, since there are no +// neighboring screens. + +enum { // killough 10/98: enum for y-offset info + weap_recoil, + weap_bobbing, + weap_bfg, + weap_stub1, + weap_pref1, + weap_pref2, + weap_pref3, + weap_pref4, + weap_pref5, + weap_pref6, + weap_pref7, + weap_pref8, + weap_pref9, + weap_stub2, + weap_toggle, + weap_toggle2, +}; + +setup_menu_t weap_settings1[]; + +setup_menu_t* weap_settings[] = +{ + weap_settings1, + NULL +}; + +setup_menu_t weap_settings1[] = // Weapons Settings screen +{ + {"ENABLE RECOIL", S_YESNO,m_null,WP_X, WP_Y+ weap_recoil*8, {"weapon_recoil"}}, + {"ENABLE BOBBING",S_YESNO,m_null,WP_X, WP_Y+weap_bobbing*8, {"player_bobbing"}}, + + {"1ST CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref1*8, {"weapon_choice_1"}}, + {"2nd CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref2*8, {"weapon_choice_2"}}, + {"3rd CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref3*8, {"weapon_choice_3"}}, + {"4th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref4*8, {"weapon_choice_4"}}, + {"5th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref5*8, {"weapon_choice_5"}}, + {"6th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref6*8, {"weapon_choice_6"}}, + {"7th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref7*8, {"weapon_choice_7"}}, + {"8th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref8*8, {"weapon_choice_8"}}, + {"9th CHOICE WEAPON",S_WEAP,m_null,WP_X,WP_Y+weap_pref9*8, {"weapon_choice_9"}}, + + {"Enable Fist/Chainsaw\n& SG/SSG toggle", S_YESNO, m_null, WP_X, + WP_Y+ weap_toggle*8, {"doom_weapon_toggles"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +// Setting up for the Weapons screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Weapons(int choice) +{ + M_SetupNextMenu(&WeaponDef); + + setup_active = true; + setup_screen = ss_weap; + set_weapon_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = weap_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + + +// The drawing part of the Weapons Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawWeapons(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(109, 2, "M_WEAP", CR_DEFAULT, "WEAPONS", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Status Bar / HUD tables. + +#define ST_X 203 +#define ST_Y 31 + +// Screen table definitions + +setup_menu_t stat_settings1[]; + +setup_menu_t* stat_settings[] = +{ + stat_settings1, + NULL +}; + +setup_menu_t stat_settings1[] = // Status Bar and HUD Settings screen +{ + {"STATUS BAR" ,S_SKIP|S_TITLE,m_null,ST_X,ST_Y+ 1*8 }, + + {"USE RED NUMBERS" ,S_YESNO, m_null,ST_X,ST_Y+ 2*8, {"sts_always_red"}}, + {"GRAY %" ,S_YESNO, m_null,ST_X,ST_Y+ 3*8, {"sts_pct_always_gray"}}, + {"SINGLE KEY DISPLAY",S_YESNO, m_null,ST_X,ST_Y+ 4*8, {"sts_traditional_keys"}}, + + {"HEADS-UP DISPLAY" ,S_SKIP|S_TITLE,m_null,ST_X,ST_Y+ 6*8}, + + {"HIDE SECRETS" ,S_YESNO ,m_null,ST_X,ST_Y+ 7*8, {"hud_nosecrets"}}, + {"HEALTH LOW/OK" ,S_NUM ,m_null,ST_X,ST_Y+ 8*8, {"health_red"}}, + {"HEALTH OK/GOOD" ,S_NUM ,m_null,ST_X,ST_Y+ 9*8, {"health_yellow"}}, + {"HEALTH GOOD/EXTRA" ,S_NUM ,m_null,ST_X,ST_Y+10*8, {"health_green"}}, + {"ARMOR LOW/OK" ,S_NUM ,m_null,ST_X,ST_Y+11*8, {"armor_red"}}, + {"ARMOR OK/GOOD" ,S_NUM ,m_null,ST_X,ST_Y+12*8, {"armor_yellow"}}, + {"ARMOR GOOD/EXTRA" ,S_NUM ,m_null,ST_X,ST_Y+13*8, {"armor_green"}}, + {"AMMO LOW/OK" ,S_NUM ,m_null,ST_X,ST_Y+14*8, {"ammo_red"}}, + {"AMMO OK/GOOD" ,S_NUM ,m_null,ST_X,ST_Y+15*8, {"ammo_yellow"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +// Setting up for the Status Bar / HUD screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_StatusBar(int choice) +{ + M_SetupNextMenu(&StatusHUDDef); + + setup_active = true; + setup_screen = ss_stat; + set_status_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = stat_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + + +// The drawing part of the Status Bar / HUD Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawStatusHUD(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(59, 2, "M_STAT", CR_DEFAULT, "STATUS BAR / HUD", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The Automap tables. + +#define AU_X 250 +#define AU_Y 31 +#define AU_PREV KB_PREV +#define AU_NEXT KB_NEXT + +setup_menu_t auto_settings1[]; +setup_menu_t auto_settings2[]; + +setup_menu_t* auto_settings[] = +{ + auto_settings1, + auto_settings2, + NULL +}; + +setup_menu_t auto_settings1[] = // 1st AutoMap Settings screen +{ + {"background", S_COLOR, m_null, AU_X, AU_Y, {"mapcolor_back"}}, + {"grid lines", S_COLOR, m_null, AU_X, AU_Y + 1*8, {"mapcolor_grid"}}, + {"normal 1s wall", S_COLOR, m_null,AU_X,AU_Y+ 2*8, {"mapcolor_wall"}}, + {"line at floor height change", S_COLOR, m_null, AU_X, AU_Y+ 3*8, {"mapcolor_fchg"}}, + {"line at ceiling height change" ,S_COLOR,m_null,AU_X,AU_Y+ 4*8, {"mapcolor_cchg"}}, + {"line at sector with floor = ceiling",S_COLOR,m_null,AU_X,AU_Y+ 5*8, {"mapcolor_clsd"}}, + {"red key" ,S_COLOR,m_null,AU_X,AU_Y+ 6*8, {"mapcolor_rkey"}}, + {"blue key" ,S_COLOR,m_null,AU_X,AU_Y+ 7*8, {"mapcolor_bkey"}}, + {"yellow key" ,S_COLOR,m_null,AU_X,AU_Y+ 8*8, {"mapcolor_ykey"}}, + {"red door" ,S_COLOR,m_null,AU_X,AU_Y+ 9*8, {"mapcolor_rdor"}}, + {"blue door" ,S_COLOR,m_null,AU_X,AU_Y+10*8, {"mapcolor_bdor"}}, + {"yellow door" ,S_COLOR,m_null,AU_X,AU_Y+11*8, {"mapcolor_ydor"}}, + + {"AUTOMAP LEVEL TITLE COLOR" ,S_CRITEM,m_null,AU_X,AU_Y+13*8, {"hudcolor_titl"}}, + {"AUTOMAP COORDINATES COLOR" ,S_CRITEM,m_null,AU_X,AU_Y+14*8, {"hudcolor_xyco"}}, + + {"Show Secrets only after entering",S_YESNO,m_null,AU_X,AU_Y+15*8, {"map_secret_after"}}, + + {"Show coordinates of automap pointer",S_YESNO,m_null,AU_X,AU_Y+16*8, {"map_point_coord"}}, // killough 10/98 + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,AU_NEXT,AU_Y+20*8, {auto_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +setup_menu_t auto_settings2[] = // 2nd AutoMap Settings screen +{ + {"teleporter line" ,S_COLOR ,m_null,AU_X,AU_Y, {"mapcolor_tele"}}, + {"secret sector boundary" ,S_COLOR ,m_null,AU_X,AU_Y+ 1*8, {"mapcolor_secr"}}, + //jff 4/23/98 add exit line to automap + {"exit line" ,S_COLOR ,m_null,AU_X,AU_Y+ 2*8, {"mapcolor_exit"}}, + {"computer map unseen line" ,S_COLOR ,m_null,AU_X,AU_Y+ 3*8, {"mapcolor_unsn"}}, + {"line w/no floor/ceiling changes",S_COLOR ,m_null,AU_X,AU_Y+ 4*8, {"mapcolor_flat"}}, + {"general sprite" ,S_COLOR ,m_null,AU_X,AU_Y+ 5*8, {"mapcolor_sprt"}}, + {"countable enemy sprite" ,S_COLOR ,m_null,AU_X,AU_Y+ 6*8, {"mapcolor_enemy"}}, // cph 2006/06/30 + {"countable item sprite" ,S_COLOR ,m_null,AU_X,AU_Y+ 7*8, {"mapcolor_item"}}, // mead 3/4/2003 + {"crosshair" ,S_COLOR ,m_null,AU_X,AU_Y+ 8*8, {"mapcolor_hair"}}, + {"single player arrow" ,S_COLOR ,m_null,AU_X,AU_Y+ 9*8, {"mapcolor_sngl"}}, + {"your colour in multiplayer" ,S_COLOR ,m_null,AU_X,AU_Y+10*8, {"mapcolor_me"}}, + + {"friends" ,S_COLOR ,m_null,AU_X,AU_Y+12*8, {"mapcolor_frnd"}}, // killough 8/8/98 + + {"<- PREV",S_SKIP|S_PREV,m_null,AU_PREV,AU_Y+20*8, {auto_settings1}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} + +}; + + +// Setting up for the Automap screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Automap(int choice) +{ + M_SetupNextMenu(&AutoMapDef); + + setup_active = true; + setup_screen = ss_auto; + set_auto_active = true; + setup_select = false; + colorbox_active = false; + default_verify = false; + setup_gather = false; + set_menu_itemon = 0; + mult_screens_index = 0; + current_setup_menu = auto_settings[0]; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// Data used by the color palette that is displayed for the player to +// select colors. + +int color_palette_x; // X position of the cursor on the color palette +int color_palette_y; // Y position of the cursor on the color palette +byte palette_background[16*(CHIP_SIZE+1)+8]; + +// M_DrawColPal() draws the color palette when the user needs to select a +// color. + +// phares 4/1/98: now uses a single lump for the palette instead of +// building the image out of individual paint chips. + +static void M_DrawColPal(void) +{ + int cpx, cpy; + + // Draw a background, border, and paint chips + + // proff/nicolas 09/20/98 -- changed for hi-res + // CPhipps - patch drawing updated + V_DrawNamePatch(COLORPALXORIG-5, COLORPALYORIG-5, 0, "M_COLORS", CR_DEFAULT, VPT_NONE); + + // Draw the cursor around the paint chip + // (cpx,cpy) is the upper left-hand corner of the paint chip + + cpx = COLORPALXORIG+color_palette_x*(CHIP_SIZE+1)-1; + cpy = COLORPALYORIG+color_palette_y*(CHIP_SIZE+1)-1; + // proff 12/6/98: Drawing of colorchips completly changed for hi-res, it now uses a patch + V_DrawNamePatch(cpx,cpy,0,"M_PALSEL",CR_DEFAULT,VPT_NONE); // PROFF_GL_FIX +} + +// The drawing part of the Automap Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawAutoMap(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // CPhipps - patch drawing updated + M_DrawTitle(109, 2, "M_AUTO", CR_DEFAULT, "AUTOMAP", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If a color is being selected, need to show color paint chips + + if (colorbox_active) + M_DrawColPal(); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + else if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The Enemies table. + +#define E_X 250 +#define E_Y 31 + +setup_menu_t enem_settings1[]; + +setup_menu_t* enem_settings[] = +{ + enem_settings1, + NULL +}; + +enum { + enem_infighting, + + enem_remember = 1, + + enem_backing, + enem_monkeys, + enem_avoid_hazards, + enem_friction, + enem_help_friends, + + enem_distfriend, + + enem_end +}; + +setup_menu_t enem_settings1[] = // Enemy Settings screen +{ + // killough 7/19/98 + {"Monster Infighting When Provoked",S_YESNO,m_null,E_X,E_Y+ enem_infighting*8, {"monster_infighting"}}, + + {"Remember Previous Enemy",S_YESNO,m_null,E_X,E_Y+ enem_remember*8, {"monsters_remember"}}, + + // killough 9/8/98 + {"Monster Backing Out",S_YESNO,m_null,E_X,E_Y+ enem_backing*8, {"monster_backing"}}, + + {"Climb Steep Stairs", S_YESNO,m_null,E_X,E_Y+enem_monkeys*8, {"monkeys"}}, + + // killough 9/9/98 + {"Intelligently Avoid Hazards",S_YESNO,m_null,E_X,E_Y+ enem_avoid_hazards*8, {"monster_avoid_hazards"}}, + + // killough 10/98 + {"Affected by Friction",S_YESNO,m_null,E_X,E_Y+ enem_friction*8, {"monster_friction"}}, + + {"Rescue Dying Friends",S_YESNO,m_null,E_X,E_Y+ enem_help_friends*8, {"help_friends"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +///////////////////////////// + +// Setting up for the Enemies screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Enemy(int choice) +{ + M_SetupNextMenu(&EnemyDef); + + setup_active = true; + setup_screen = ss_enem; + set_enemy_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = enem_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Enemies Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawEnemy(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(114, 2, "M_ENEM", CR_DEFAULT, "ENEMIES", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The General table. +// killough 10/10/98 + +extern int usemouse, default_mus_card, default_snd_card; +extern int detect_voices, tran_filter_pct; + +setup_menu_t gen_settings1[], gen_settings2[], gen_settings3[]; + +setup_menu_t* gen_settings[] = +{ + gen_settings1, + gen_settings2, + gen_settings3, + NULL +}; + +enum { + general_trans, + general_transpct, +// general_pcx, +// general_diskicon, + general_uncapped, +}; + +enum { +// general_sndcard, +// general_muscard, +// general_detvoices, + general_sndchan, + general_pitch +}; + +#define G_X 250 +#define G_YA 44 +#define G_YA2 (G_YA+9*8) +#define G_YA3 (G_YA2+5*8) +#define GF_X 76 + +static const char *framerates[] = {"35fps", "60fps"}; + +setup_menu_t gen_settings1[] = { // General Settings screen1 + + {"Video" ,S_SKIP|S_TITLE, m_null, G_X, G_YA - 12}, + + {"Uncapped Framerate", S_CHOICE, m_null, G_X, + G_YA + general_uncapped*8, {"uncapped_framerate"}, 0, 0, NULL, framerates}, + +#if 0 + {"PCX instead of BMP for screenshots", S_YESNO, m_null, G_X, + G_YA + general_pcx*8, {"screenshot_pcx"}}, +#endif + +#if 0 // MBF + {"Flash Icon During Disk IO", S_YESNO, m_null, G_X, + G_YA + general_diskicon*8, {"disk_icon"}}, +#endif + + {"Sound & Music", S_SKIP|S_TITLE, m_null, G_X, G_YA3 - 12}, +#if 0 // MBF + {"Sound Card", S_NUM|S_PRGWARN, m_null, G_X, + G_YA2 + general_sndcard*8, {"sound_card"}}, + + {"Music Card", S_NUM|S_PRGWARN, m_null, G_X, + G_YA2 + general_muscard*8, {"music_card"}}, + + {"Autodetect Number of Voices", S_YESNO|S_PRGWARN, m_null, G_X, + G_YA2 + general_detvoices*8, {"detect_voices"}}, +#endif + + {"Number of Sound Channels", S_NUM|S_PRGWARN, m_null, G_X, + G_YA3 + general_sndchan*8, {"snd_channels"}}, + + {"Enable v1.1 Pitch Effects", S_YESNO, m_null, G_X, + G_YA3 + general_pitch*8, {"pitched_sounds"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +enum { + general_leds +}; + +enum { + general_wad1, + general_wad2, + general_deh1, + general_deh2 +}; + +enum { + general_corpse, + general_realtic, + general_smooth, + general_smoothfactor, + general_defskill, + general_menubg, +}; + +#define G_YB 44 +#define G_YB1 (G_YB+44) +#define G_YB2 (G_YB1+52) + +static const char *gen_skillstrings[] = { + // Dummy first option because defaultskill is 1-based + "", "ITYTD", "HNTR", "HMP", "UV", "NM", NULL +}; + +setup_menu_t gen_settings2[] = { // General Settings screen2 + + {"Files Preloaded at Game Startup",S_SKIP|S_TITLE, m_null, G_X, + G_YB1 - 12}, + + {"WAD # 1", S_FILE, m_null, GF_X, G_YB1 + general_wad1*8, {"wadfile_1"}}, + + {"WAD #2", S_FILE, m_null, GF_X, G_YB1 + general_wad2*8, {"wadfile_2"}}, + + {"DEH/BEX # 1", S_FILE, m_null, GF_X, G_YB1 + general_deh1*8, {"dehfile_1"}}, + + {"DEH/BEX #2", S_FILE, m_null, GF_X, G_YB1 + general_deh2*8, {"dehfile_2"}}, + + {"Miscellaneous" ,S_SKIP|S_TITLE, m_null, G_X, G_YB2 - 12}, + + {"Maximum number of player corpses", S_NUM|S_PRGWARN, m_null, G_X, + G_YB2 + general_corpse*8, {"max_player_corpse"}}, + + {"Smooth Demo Playback", S_YESNO, m_null, G_X, + G_YB2 + general_smooth*8, {"demo_smoothturns"}, 0, 0, M_ChangeDemoSmoothTurns}, + + {"Smooth Demo Playback Factor", S_NUM, m_null, G_X, + G_YB2 + general_smoothfactor*8, {"demo_smoothturnsfactor"}, 0, 0, M_ChangeDemoSmoothTurns}, + + {"Default skill level", S_CHOICE, m_null, G_X, + G_YB2 + general_defskill*8, {"default_skill"}, 0, 0, NULL, gen_skillstrings}, + + {"Fullscreen menu background", S_YESNO, m_null, G_X, + G_YB2 + general_menubg*8, {"menu_background"}}, + + {"<- PREV",S_SKIP|S_PREV, m_null, KB_PREV, KB_Y+20*8, {gen_settings1}}, + + {"NEXT ->",S_SKIP|S_NEXT,m_null,KB_NEXT,KB_Y+20*8, {gen_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +enum { + general_filterwall, + general_filterfloor, + general_filtersprite, + general_filterpatch, + general_filterz, + general_filter_threshold, + general_spriteedges, + general_patchedges, + general_hom, +}; + +#define G_YC 44 + +static const char *renderfilters[] = {"none", "point", "linear", "rounded"}; +static const char *edgetypes[] = {"jagged", "sloped"}; + +setup_menu_t gen_settings3[] = { // General Settings screen2 + + {"Renderer settings" ,S_SKIP|S_TITLE, m_null, G_X, G_YB - 12}, + + {"Filter for walls", S_CHOICE, m_null, G_X, + G_YC + general_filterwall*8, {"filter_wall"}, 0, 0, NULL, renderfilters}, + + {"Filter for floors/ceilings", S_CHOICE, m_null, G_X, + G_YC + general_filterfloor*8, {"filter_floor"}, 0, 0, NULL, renderfilters}, + + {"Filter for sprites", S_CHOICE, m_null, G_X, + G_YC + general_filtersprite*8, {"filter_sprite"}, 0, 0, NULL, renderfilters}, + + {"Filter for patches", S_CHOICE, m_null, G_X, + G_YC + general_filterpatch*8, {"filter_patch"}, 0, 0, NULL, renderfilters}, + + {"Filter for lighting", S_CHOICE, m_null, G_X, + G_YC + general_filterz*8, {"filter_z"}, 0, 0, NULL, renderfilters}, + + {"Drawing of sprite edges", S_CHOICE, m_null, G_X, + G_YC + general_spriteedges*8, {"sprite_edges"}, 0, 0, NULL, edgetypes}, + + {"Drawing of patch edges", S_CHOICE, m_null, G_X, + G_YC + general_patchedges*8, {"patch_edges"}, 0, 0, NULL, edgetypes}, + + {"Flashing HOM indicator", S_YESNO, m_null, G_X, + G_YC + general_hom*8, {"flashing_hom"}}, + + {"<- PREV",S_SKIP|S_PREV, m_null, KB_PREV, KB_Y+20*8, {gen_settings2}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +void M_ChangeDemoSmoothTurns(void) +{ + if (demo_smoothturns) + gen_settings2[12].m_flags &= ~(S_SKIP|S_SELECT); + else + gen_settings2[12].m_flags |= (S_SKIP|S_SELECT); + + R_SmoothPlaying_Reset(NULL); +} + +// Setting up for the General screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_General(int choice) +{ + M_SetupNextMenu(&GeneralDef); + + setup_active = true; + setup_screen = ss_gen; + set_general_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = gen_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the General Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawGeneral(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // proff/nicolas 09/20/98 -- changed for hi-res + M_DrawTitle(114, 2, "M_GENERL", CR_DEFAULT, "GENERAL", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Compatibility table. +// killough 10/10/98 + +#define C_X 284 +#define C_Y 32 +#define COMP_SPC 12 +#define C_NEXTPREV 131 + +setup_menu_t comp_settings1[], comp_settings2[], comp_settings3[]; + +setup_menu_t* comp_settings[] = +{ + comp_settings1, + comp_settings2, + comp_settings3, + NULL +}; + +enum +{ + compat_telefrag, + compat_dropoff, + compat_falloff, + compat_staylift, + compat_doorstuck, + compat_pursuit, + compat_vile, + compat_pain, + compat_skull, + compat_blazing, + compat_doorlight = 0, + compat_god, + compat_infcheat, + compat_zombie, + compat_skymap, + compat_stairs, + compat_floors, + compat_moveblock, + compat_model, + compat_zerotags, + compat_666 = 0, + compat_soul, + compat_maskedanim, + compat_sound +}; + +setup_menu_t comp_settings1[] = // Compatibility Settings screen #1 +{ + {"Any monster can telefrag on MAP30", S_YESNO, m_null, C_X, + C_Y + compat_telefrag * COMP_SPC, {"comp_telefrag"}}, + + {"Some objects never hang over tall ledges", S_YESNO, m_null, C_X, + C_Y + compat_dropoff * COMP_SPC, {"comp_dropoff"}}, + + {"Objects don't fall under their own weight", S_YESNO, m_null, C_X, + C_Y + compat_falloff * COMP_SPC, {"comp_falloff"}}, + + {"Monsters randomly walk off of moving lifts", S_YESNO, m_null, C_X, + C_Y + compat_staylift * COMP_SPC, {"comp_staylift"}}, + + {"Monsters get stuck on doortracks", S_YESNO, m_null, C_X, + C_Y + compat_doorstuck * COMP_SPC, {"comp_doorstuck"}}, + + {"Monsters don't give up pursuit of targets", S_YESNO, m_null, C_X, + C_Y + compat_pursuit * COMP_SPC, {"comp_pursuit"}}, + + {"Arch-Vile resurrects invincible ghosts", S_YESNO, m_null, C_X, + C_Y + compat_vile * COMP_SPC, {"comp_vile"}}, + + {"Pain Elementals limited to 21 lost souls", S_YESNO, m_null, C_X, + C_Y + compat_pain * COMP_SPC, {"comp_pain"}}, + + {"Lost souls get stuck behind walls", S_YESNO, m_null, C_X, + C_Y + compat_skull * COMP_SPC, {"comp_skull"}}, + + {"Blazing doors make double closing sounds", S_YESNO, m_null, C_X, + C_Y + compat_blazing * COMP_SPC, {"comp_blazing"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + {"NEXT ->",S_SKIP|S_NEXT, m_null, KB_NEXT, C_Y+C_NEXTPREV, {comp_settings2}}, + + // Final entry + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t comp_settings2[] = // Compatibility Settings screen #2 +{ + {"Tagged doors don't trigger special lighting", S_YESNO, m_null, C_X, + C_Y + compat_doorlight * COMP_SPC, {"comp_doorlight"}}, + + {"God mode isn't absolute", S_YESNO, m_null, C_X, + C_Y + compat_god * COMP_SPC, {"comp_god"}}, + + {"Powerup cheats are not infinite duration", S_YESNO, m_null, C_X, + C_Y + compat_infcheat * COMP_SPC, {"comp_infcheat"}}, + + {"Dead players can exit levels", S_YESNO, m_null, C_X, + C_Y + compat_zombie * COMP_SPC, {"comp_zombie"}}, + + {"Sky is unaffected by invulnerability", S_YESNO, m_null, C_X, + C_Y + compat_skymap * COMP_SPC, {"comp_skymap"}}, + + {"Use exactly Doom's stairbuilding method", S_YESNO, m_null, C_X, + C_Y + compat_stairs * COMP_SPC, {"comp_stairs"}}, + + {"Use exactly Doom's floor motion behavior", S_YESNO, m_null, C_X, + C_Y + compat_floors * COMP_SPC, {"comp_floors"}}, + + {"Use exactly Doom's movement clipping code", S_YESNO, m_null, C_X, + C_Y + compat_moveblock * COMP_SPC, {"comp_moveblock"}}, + + {"Use exactly Doom's linedef trigger model", S_YESNO, m_null, C_X, + C_Y + compat_model * COMP_SPC, {"comp_model"}}, + + {"Linedef effects work with sector tag = 0", S_YESNO, m_null, C_X, + C_Y + compat_zerotags * COMP_SPC, {"comp_zerotags"}}, + + {"<- PREV", S_SKIP|S_PREV, m_null, KB_PREV, C_Y+C_NEXTPREV,{comp_settings1}}, + + {"NEXT ->",S_SKIP|S_NEXT, m_null, KB_NEXT, C_Y+C_NEXTPREV, {comp_settings3}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +setup_menu_t comp_settings3[] = // Compatibility Settings screen #2 +{ + {"All boss types can trigger tag 666 at ExM8", S_YESNO, m_null, C_X, + C_Y + compat_666 * COMP_SPC, {"comp_666"}}, + + {"Lost souls don't bounce off flat surfaces", S_YESNO, m_null, C_X, + C_Y + compat_soul * COMP_SPC, {"comp_soul"}}, + + {"2S middle textures do not animate", S_YESNO, m_null, C_X, + C_Y + compat_maskedanim * COMP_SPC, {"comp_maskedanim"}}, + + {"Use exactly Doom's sound code behavior", S_YESNO, m_null, C_X, + C_Y + compat_sound * COMP_SPC, {"comp_sound"}}, + + {"<- PREV", S_SKIP|S_PREV, m_null, KB_PREV, C_Y+C_NEXTPREV,{comp_settings2}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +// Setting up for the Compatibility screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Compat(int choice) +{ + M_SetupNextMenu(&CompatDef); + + setup_active = true; + setup_screen = ss_comp; + set_general_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = comp_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Compatibility Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawCompat(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + M_DrawTitle(52, 2, "M_COMPAT", CR_DEFAULT, "DOOM COMPATIBILITY", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// The Messages table. + +#define M_X 230 +#define M_Y 39 + +// killough 11/98: enumerated + +enum { + mess_color_play, + mess_timer, + mess_color_chat, + mess_chat_timer, + mess_color_review, + mess_timed, + mess_hud_timer, + mess_lines, + mess_scrollup, + mess_background, +}; + +setup_menu_t mess_settings1[]; + +setup_menu_t* mess_settings[] = +{ + mess_settings1, + NULL +}; + +setup_menu_t mess_settings1[] = // Messages screen +{ + {"Message Color During Play", S_CRITEM, m_null, M_X, + M_Y + mess_color_play*8, {"hudcolor_mesg"}}, + +#if 0 + {"Message Duration During Play (ms)", S_NUM, m_null, M_X, + M_Y + mess_timer*8, {"message_timer"}}, +#endif + + {"Chat Message Color", S_CRITEM, m_null, M_X, + M_Y + mess_color_chat*8, {"hudcolor_chat"}}, + +#if 0 + {"Chat Message Duration (ms)", S_NUM, m_null, M_X, + M_Y + mess_chat_timer*8, {"chat_msg_timer"}}, +#endif + + {"Message Review Color", S_CRITEM, m_null, M_X, + M_Y + mess_color_review*8, {"hudcolor_list"}}, + +#if 0 + {"Message Listing Review is Temporary", S_YESNO, m_null, M_X, + M_Y + mess_timed*8, {"hud_msg_timed"}}, + + {"Message Review Duration (ms)", S_NUM, m_null, M_X, + M_Y + mess_hud_timer*8, {"hud_msg_timer"}}, +#endif + + {"Number of Review Message Lines", S_NUM, m_null, M_X, + M_Y + mess_lines*8, {"hud_msg_lines"}}, + +#if 0 + {"Message Listing Scrolls Upwards", S_YESNO, m_null, M_X, + M_Y + mess_scrollup*8, {"hud_msg_scrollup"}}, +#endif + + {"Message Background", S_YESNO, m_null, M_X, + M_Y + mess_background*8, {"hud_list_bgon"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + + +// Setting up for the Messages screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_Messages(int choice) +{ + M_SetupNextMenu(&MessageDef); + + setup_active = true; + setup_screen = ss_mess; + set_mess_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = mess_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + + +// The drawing part of the Messages Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawMessages(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // CPhipps - patch drawing updated + M_DrawTitle(103, 2, "M_MESS", CR_DEFAULT, "MESSAGES", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + if (default_verify) + M_DrawDefVerify(); +} + + +///////////////////////////// +// +// The Chat Strings table. + +#define CS_X 20 +#define CS_Y (31+8) + +setup_menu_t chat_settings1[]; + +setup_menu_t* chat_settings[] = +{ + chat_settings1, + NULL +}; + +setup_menu_t chat_settings1[] = // Chat Strings screen +{ + {"1",S_CHAT,m_null,CS_X,CS_Y+ 1*8, {"chatmacro1"}}, + {"2",S_CHAT,m_null,CS_X,CS_Y+ 2*8, {"chatmacro2"}}, + {"3",S_CHAT,m_null,CS_X,CS_Y+ 3*8, {"chatmacro3"}}, + {"4",S_CHAT,m_null,CS_X,CS_Y+ 4*8, {"chatmacro4"}}, + {"5",S_CHAT,m_null,CS_X,CS_Y+ 5*8, {"chatmacro5"}}, + {"6",S_CHAT,m_null,CS_X,CS_Y+ 6*8, {"chatmacro6"}}, + {"7",S_CHAT,m_null,CS_X,CS_Y+ 7*8, {"chatmacro7"}}, + {"8",S_CHAT,m_null,CS_X,CS_Y+ 8*8, {"chatmacro8"}}, + {"9",S_CHAT,m_null,CS_X,CS_Y+ 9*8, {"chatmacro9"}}, + {"0",S_CHAT,m_null,CS_X,CS_Y+10*8, {"chatmacro0"}}, + + // Button for resetting to defaults + {0,S_RESET,m_null,X_BUTTON,Y_BUTTON}, + + // Final entry + {0,S_SKIP|S_END,m_null} + +}; + +// Setting up for the Chat Strings screen. Turn on flags, set pointers, +// locate the first item on the screen where the cursor is allowed to +// land. + +void M_ChatStrings(int choice) +{ + M_SetupNextMenu(&ChatStrDef); + setup_active = true; + setup_screen = ss_chat; + set_chat_active = true; + setup_select = false; + default_verify = false; + setup_gather = false; + mult_screens_index = 0; + current_setup_menu = chat_settings[0]; + set_menu_itemon = 0; + while (current_setup_menu[set_menu_itemon++].m_flags & S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; +} + +// The drawing part of the Chat Strings Setup initialization. Draw the +// background, title, instruction line, and items. + +void M_DrawChatStrings(void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); // Draw background + + // CPhipps - patch drawing updated + M_DrawTitle(83, 2, "M_CHAT", CR_DEFAULT, "CHAT STRINGS", CR_GOLD); + M_DrawInstructions(); + M_DrawScreenItems(current_setup_menu); + + // If the Reset Button has been selected, an "Are you sure?" message + // is overlayed across everything else. + + if (default_verify) + M_DrawDefVerify(); +} + +///////////////////////////// +// +// General routines used by the Setup screens. +// + +static boolean shiftdown = false; // phares 4/10/98: SHIFT key down or not + +// phares 4/17/98: +// M_SelectDone() gets called when you have finished entering your +// Setup Menu item change. + +static void M_SelectDone(setup_menu_t* ptr) +{ + ptr->m_flags &= ~S_SELECT; + ptr->m_flags |= S_HILITE; + S_StartSound(NULL,sfx_itemup); + setup_select = false; + colorbox_active = false; + if (print_warning_about_changes) // killough 8/15/98 + print_warning_about_changes--; +} + +// phares 4/21/98: +// Array of setup screens used by M_ResetDefaults() + +static setup_menu_t **setup_screens[] = +{ + keys_settings, + weap_settings, + stat_settings, + auto_settings, + enem_settings, + mess_settings, + chat_settings, + gen_settings, // killough 10/98 + comp_settings, +}; + +// phares 4/19/98: +// M_ResetDefaults() resets all values for a setup screen to default values +// +// killough 10/98: rewritten to fix bugs and warn about pending changes + +static void M_ResetDefaults(void) +{ + int i; //e6y + + default_t *dp; + int warn = 0; + + // Look through the defaults table and reset every variable that + // belongs to the group we're interested in. + // + // killough: However, only reset variables whose field in the + // current setup screen is the same as in the defaults table. + // i.e. only reset variables really in the current setup screen. + + // e6y + // Fixed crash while trying to read data past array end + // All previous versions of prboom worked only by a lucky accident + // old code: for (dp = defaults; dp->name; dp++) + for (i = 0; i < numdefaults ; i++) + { + dp = &defaults[i]; + + if (dp->setupscreen == setup_screen) + { + setup_menu_t **l, *p; + for (l = setup_screens[setup_screen-1]; *l; l++) + for (p = *l; !(p->m_flags & S_END); p++) + if (p->m_flags & S_HASDEFPTR ? p->var.def == dp : + p->var.m_key == dp->location.pi || + p->m_mouse == dp->location.pi) + { + if (IS_STRING(*dp)) + { + union { const char **c; char **s; } u; // type punning via unions + + u.c = dp->location.ppsz; + free(*(u.s)); + *(u.c) = strdup(dp->defaultvalue.psz); + } + else + *dp->location.pi = dp->defaultvalue.i; + +#if 0 + if (p->m_flags & (S_LEVWARN | S_PRGWARN)) + warn |= p->m_flags & (S_LEVWARN | S_PRGWARN); + else + if (dp->current) + if (allow_changes()) + *dp->current = *dp->location.pi; + else + warn |= S_LEVWARN; +#endif + if (p->action) + p->action(); + + goto end; + } + end:; + } + } + + if (warn) + warn_about_changes(warn); +} + +// +// M_InitDefaults() +// +// killough 11/98: +// +// This function converts all setup menu entries consisting of cfg +// variable names, into pointers to the corresponding default[] +// array entry. var.name becomes converted to var.def. +// + +static void M_InitDefaults(void) +{ + setup_menu_t *const *p, *t; + default_t *dp; + int i; + for (i = 0; i < ss_max-1; i++) + for (p = setup_screens[i]; *p; p++) + for (t = *p; !(t->m_flags & S_END); t++) + if (t->m_flags & S_HASDEFPTR) { + if (!(dp = M_LookupDefault(t->var.name))) + I_Error("M_InitDefaults: Couldn't find config variable %s", t->var.name); + else + (t->var.def = dp)->setup_menu = t; + } +} + +// +// End of Setup Screens. +// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// Start of Extended HELP screens // phares 3/30/98 +// +// The wad designer can define a set of extended HELP screens for their own +// information display. These screens should be 320x200 graphic lumps +// defined in a separate wad. They should be named "HELP01" through "HELP99". +// "HELP01" is shown after the regular BOOM Dynamic HELP screen, and ENTER +// and BACKSPACE keys move the player through the HELP set. +// +// Rather than define a set of menu definitions for each of the possible +// HELP screens, one definition is used, and is altered on the fly +// depending on what HELPnn lumps the game finds. + +// phares 3/30/98: +// Extended Help Screen variables + +int extended_help_count; // number of user-defined help screens found +int extended_help_index; // index of current extended help screen + +menuitem_t ExtHelpMenu[] = +{ + {1,"",M_ExtHelpNextScreen,0} +}; + +menu_t ExtHelpDef = +{ + 1, // # of menu items + &ReadDef1, // previous menu + ExtHelpMenu, // menuitem_t -> + M_DrawExtHelp, // drawing routine -> + 330,181, // x,y + 0 // lastOn +}; + +// M_ExtHelpNextScreen establishes the number of the next HELP screen in +// the series. + +void M_ExtHelpNextScreen(int choice) +{ + choice = 0; + if (++extended_help_index > extended_help_count) + { + + // when finished with extended help screens, return to Main Menu + + extended_help_index = 1; + M_SetupNextMenu(&MainDef); + } +} + +// phares 3/30/98: +// Routine to look for HELPnn screens and create a menu +// definition structure that defines extended help screens. + +void M_InitExtendedHelp(void) + +{ + int index,i; + char namebfr[] = { "HELPnn"} ; + + extended_help_count = 0; + for (index = 1 ; index < 100 ; index++) { + namebfr[4] = index/10 + 0x30; + namebfr[5] = index%10 + 0x30; + i = W_CheckNumForName(namebfr); + if (i == -1) { + if (extended_help_count) { + if (gamemode == commercial) { + ExtHelpDef.prevMenu = &ReadDef1; /* previous menu */ + ReadMenu1[0].routine = M_ExtHelp; + } else { + ExtHelpDef.prevMenu = &ReadDef2; /* previous menu */ + ReadMenu2[0].routine = M_ExtHelp; + } + } + return; + } + extended_help_count++; + } + +} + +// Initialization for the extended HELP screens. + +void M_ExtHelp(int choice) +{ + choice = 0; + extended_help_index = 1; // Start with first extended help screen + M_SetupNextMenu(&ExtHelpDef); +} + +// Initialize the drawing part of the extended HELP screens. + +void M_DrawExtHelp(void) +{ + char namebfr[10] = { "HELPnn" }; // CPhipps - make it local & writable + + inhelpscreens = true; // killough 5/1/98 + namebfr[4] = extended_help_index/10 + 0x30; + namebfr[5] = extended_help_index%10 + 0x30; + // CPhipps - patch drawing updated + V_DrawNamePatch(0, 0, 0, namebfr, CR_DEFAULT, VPT_NONE); +} + +// +// End of Extended HELP screens // phares 3/30/98 +// +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// +// Dynamic HELP screen // phares 3/2/98 +// +// Rather than providing the static HELP screens from DOOM and its versions, +// BOOM provides the player with a dynamic HELP screen that displays the +// current settings of major key bindings. +// +// The Dynamic HELP screen is defined in a manner similar to that used for +// the Setup Screens above. +// +// M_GetKeyString finds the correct string to represent the key binding +// for the current item being drawn. + +int M_GetKeyString(int c,int offset) +{ + const char* s; + + if (c >= 33 && c <= 126) { + + // The '=', ',', and '.' keys originally meant the shifted + // versions of those keys, but w/o having to shift them in + // the game. Any actions that are mapped to these keys will + // still mean their shifted versions. Could be changed later + // if someone can come up with a better way to deal with them. + + if (c == '=') // probably means the '+' key? + c = '+'; + else if (c == ',') // probably means the '<' key? + c = '<'; + else if (c == '.') // probably means the '>' key? + c = '>'; + menu_buffer[offset++] = c; // Just insert the ascii key + menu_buffer[offset] = 0; + + } else { + + // Retrieve 4-letter (max) string representing the key + + // cph - Keypad keys, general code reorganisation to + // make this smaller and neater. + if ((0x100 <= c) && (c < 0x200)) { + if (c == KEYD_KEYPADENTER) + s = "PADE"; + else { + strcpy(&menu_buffer[offset], "PAD"); + offset+=4; + menu_buffer[offset-1] = c & 0xff; + menu_buffer[offset] = 0; + } + } else if ((KEYD_F1 <= c) && (c < KEYD_F10)) { + menu_buffer[offset++] = 'F'; + menu_buffer[offset++] = '1' + c - KEYD_F1; + menu_buffer[offset] = 0; + } else { + switch(c) { + case KEYD_TAB: s = "TAB"; break; + case KEYD_ENTER: s = "ENTR"; break; + case KEYD_ESCAPE: s = "ESC"; break; + case KEYD_SPACEBAR: s = "SPAC"; break; + case KEYD_BACKSPACE: s = "BACK"; break; + case KEYD_RCTRL: s = "CTRL"; break; + case KEYD_LEFTARROW: s = "LARR"; break; + case KEYD_UPARROW: s = "UARR"; break; + case KEYD_RIGHTARROW: s = "RARR"; break; + case KEYD_DOWNARROW: s = "DARR"; break; + case KEYD_RSHIFT: s = "SHFT"; break; + case KEYD_RALT: s = "ALT"; break; + case KEYD_CAPSLOCK: s = "CAPS"; break; + case KEYD_SCROLLLOCK: s = "SCRL"; break; + case KEYD_HOME: s = "HOME"; break; + case KEYD_PAGEUP: s = "PGUP"; break; + case KEYD_END: s = "END"; break; + case KEYD_PAGEDOWN: s = "PGDN"; break; + case KEYD_INSERT: s = "INST"; break; + case KEYD_DEL: s = "DEL"; break; + case KEYD_F10: s = "F10"; break; + case KEYD_F11: s = "F11"; break; + case KEYD_F12: s = "F12"; break; + case KEYD_PAUSE: s = "PAUS"; break; + default: s = "JUNK"; break; + } + + if (s) { // cph - Slight code change + strcpy(&menu_buffer[offset],s); // string to display + offset += strlen(s); + } + } + } + return offset; +} + +// +// The Dynamic HELP screen table. + +#define KT_X1 283 +#define KT_X2 172 +#define KT_X3 87 + +#define KT_Y1 2 +#define KT_Y2 118 +#define KT_Y3 102 + +setup_menu_t helpstrings[] = // HELP screen strings +{ + {"SCREEN" ,S_SKIP|S_TITLE,m_null,KT_X1,KT_Y1}, + {"HELP" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 1*8,{&key_help}}, + {"MENU" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 2*8,{&key_escape}}, + {"SETUP" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 3*8,{&key_setup}}, + {"PAUSE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 4*8,{&key_pause}}, + {"AUTOMAP" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 5*8,{&key_map}}, + {"SOUND VOLUME",S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 6*8,{&key_soundvolume}}, + {"HUD" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 7*8,{&key_hud}}, + {"MESSAGES" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 8*8,{&key_messages}}, + {"GAMMA FIX" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+ 9*8,{&key_gamma}}, + {"SPY" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+10*8,{&key_spy}}, + {"LARGER VIEW" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+11*8,{&key_zoomin}}, + {"SMALLER VIEW",S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+12*8,{&key_zoomout}}, + {"SCREENSHOT" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y1+13*8,{&key_screenshot}}, + + {"AUTOMAP" ,S_SKIP|S_TITLE,m_null,KT_X1,KT_Y2}, + {"FOLLOW MODE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 1*8,{&key_map_follow}}, + {"ZOOM IN" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 2*8,{&key_map_zoomin}}, + {"ZOOM OUT" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 3*8,{&key_map_zoomout}}, + {"MARK PLACE" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 4*8,{&key_map_mark}}, + {"CLEAR MARKS" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 5*8,{&key_map_clear}}, + {"FULL/ZOOM" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 6*8,{&key_map_gobig}}, + {"GRID" ,S_SKIP|S_KEY,m_null,KT_X1,KT_Y2+ 7*8,{&key_map_grid}}, + + {"WEAPONS" ,S_SKIP|S_TITLE,m_null,KT_X3,KT_Y1}, + {"FIST" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 1*8,{&key_weapon1}}, + {"PISTOL" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 2*8,{&key_weapon2}}, + {"SHOTGUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 3*8,{&key_weapon3}}, + {"CHAINGUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 4*8,{&key_weapon4}}, + {"ROCKET" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 5*8,{&key_weapon5}}, + {"PLASMA" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 6*8,{&key_weapon6}}, + {"BFG 9000" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 7*8,{&key_weapon7}}, + {"CHAINSAW" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 8*8,{&key_weapon8}}, + {"SSG" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+ 9*8,{&key_weapon9}}, + {"BEST" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+10*8,{&key_weapontoggle}}, + {"FIRE" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y1+11*8,{&key_fire},&mousebfire}, + + {"MOVEMENT" ,S_SKIP|S_TITLE,m_null,KT_X3,KT_Y3}, + {"FORWARD" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 1*8,{&key_up},&mousebforward}, + {"BACKWARD" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 2*8,{&key_down},&mousebbackward}, + {"TURN LEFT" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 3*8,{&key_left}}, + {"TURN RIGHT" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 4*8,{&key_right}}, + {"RUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 5*8,{&key_speed},0}, + {"STRAFE LEFT" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 6*8,{&key_strafeleft}}, + {"STRAFE RIGHT",S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 7*8,{&key_straferight}}, + {"STRAFE" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 8*8,{&key_strafe},&mousebstrafe}, + {"AUTORUN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+ 9*8,{&key_autorun}}, + {"180 TURN" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+10*8,{&key_reverse}}, + {"USE" ,S_SKIP|S_KEY,m_null,KT_X3,KT_Y3+11*8,{&key_use},&mousebforward}, + + {"GAME" ,S_SKIP|S_TITLE,m_null,KT_X2,KT_Y1}, + {"SAVE" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 1*8,{&key_savegame}}, + {"LOAD" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 2*8,{&key_loadgame}}, + {"QUICKSAVE" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 3*8,{&key_quicksave}}, + {"END GAME" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 4*8,{&key_endgame}}, + {"QUICKLOAD" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 5*8,{&key_quickload}}, + {"QUIT" ,S_SKIP|S_KEY,m_null,KT_X2,KT_Y1+ 6*8,{&key_quit}}, + + // Final entry + + {0,S_SKIP|S_END,m_null} +}; + +#define SPACEWIDTH 4 + +/* cph 2006/08/06 + * M_DrawString() is the old M_DrawMenuString, except that it is not tied to + * menu_buffer - no reason to force all the callers to write into one array! */ + +static void M_DrawString(int cx, int cy, int color, const char* ch) +{ + int w; + int c; + + while (*ch) { + c = *ch++; // get next char + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c> HU_FONTSIZE) + { + cx += SPACEWIDTH; // space + continue; + } + w = hu_font[c].width; + if (cx + w > 320) + break; + + // V_DrawpatchTranslated() will draw the string in the + // desired color, colrngs[color] + + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, color, VPT_NONE | VPT_TRANS); + // The screen is cramped, so trim one unit from each + // character so they butt up against each other. + cx += w - 1; + } +} + +// M_DrawMenuString() draws the string in menu_buffer[] + +static void M_DrawMenuString(int cx, int cy, int color) +{ + M_DrawString(cx, cy, color, menu_buffer); +} + +// M_GetPixelWidth() returns the number of pixels in the width of +// the string, NOT the number of chars in the string. + +static int M_GetPixelWidth(const char* ch) +{ + int len = 0; + int c; + + while (*ch) { + c = *ch++; // pick up next char + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c > HU_FONTSIZE) + { + len += SPACEWIDTH; // space + continue; + } + len += hu_font[c].width; + len--; // adjust so everything fits + } + len++; // replace what you took away on the last char only + return len; +} + +static void M_DrawStringCentered(int cx, int cy, int color, const char* ch) +{ + M_DrawString(cx - M_GetPixelWidth(ch)/2, cy, color, ch); +} + +// +// M_DrawHelp +// +// This displays the help screen + +void M_DrawHelp (void) +{ + menuactive = mnact_full; + + M_DrawBackground("FLOOR4_6", 0); + + M_DrawScreenItems(helpstrings); +} + +// +// End of Dynamic HELP screen // phares 3/2/98 +// +//////////////////////////////////////////////////////////////////////////// + +enum { + prog, + prog_stub, + prog_stub1, + prog_stub2, + adcr +}; + +enum { + cr_prog=0, + cr_adcr=2, +}; + +#define CR_S 9 +#define CR_X 20 +#define CR_X2 50 +#define CR_Y 32 +#define CR_SH 9 + +setup_menu_t cred_settings[]={ + + {"Programmers",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X, CR_Y + CR_S*prog + CR_SH*cr_prog}, + {"Florian 'Proff' Schulze",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+1) + CR_SH*cr_prog}, + {"Colin Phipps",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+2) + CR_SH*cr_prog}, + {"Neil Stevens",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+3) + CR_SH*cr_prog}, + {"Andrey Budko",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(prog+4) + CR_SH*cr_prog}, + + {"Additional Credit To",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X, CR_Y + CR_S*adcr + CR_SH*cr_adcr}, + {"id Software for DOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+1)+CR_SH*cr_adcr}, + {"TeamTNT for BOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+2)+CR_SH*cr_adcr}, + {"Lee Killough for MBF",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+3)+CR_SH*cr_adcr}, + {"The DOSDoom-Team for DOSDOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+4)+CR_SH*cr_adcr}, + {"Randy Heit for ZDOOM",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+5)+CR_SH*cr_adcr}, + {"Michael 'Kodak' Ryssen for DOOMGL",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+6)+CR_SH*cr_adcr}, + {"Jess Haas for lSDLDoom",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+7) + CR_SH*cr_adcr}, + {"all others who helped (see AUTHORS file)",S_SKIP|S_CREDIT|S_LEFTJUST,m_null, CR_X2, CR_Y + CR_S*(adcr+8)+CR_SH*cr_adcr}, + + {0,S_SKIP|S_END,m_null} +}; + +void M_DrawCredits(void) // killough 10/98: credit screen +{ + inhelpscreens = true; + // Use V_DrawBackground here deliberately to force drawing a background + V_DrawBackground(gamemode==shareware ? "CEIL5_1" : "MFLR8_4", 0); + M_DrawTitle(115, 9, "PRBOOM", CR_GOLD, + PACKAGE_NAME " v" PACKAGE_VERSION, CR_GOLD); + M_DrawScreenItems(cred_settings); +} + +static int M_IndexInChoices(const char *str, const char **choices) { + int i = 0; + + while (*choices != NULL) { + if (!strcmp(str, *choices)) + return i; + i++; + choices++; + } + return 0; +} + +///////////////////////////////////////////////////////////////////////////// +// +// M_Responder +// +// Examines incoming keystrokes and button pushes and determines some +// action based on the state of the system. +// + +boolean M_Responder (event_t* ev) { + int ch; + int i; + + ch = -1; // will be changed to a legit char if we're going to use it here + + + // Process keyboard input + + if (ev->type == ev_keydown) + { + ch = ev->data1; // phares 4/11/98: + if (ch == KEYD_RSHIFT) // For chat string processing, need + shiftdown = true; // to know when shift key is up or + } // down so you can get at the !,#, + else if (ev->type == ev_keyup) // etc. keys. Keydowns are allowed + if (ev->data1 == KEYD_RSHIFT) // past this point, but keyups aren't + shiftdown = false; // so we need to note the difference + + if (ch == -1) + return false; // we can't use the event here + + // Save Game string input + + if (saveStringEnter) { + if (ch == key_menu_backspace) // phares 3/7/98 + { + if (saveCharIndex > 0) + { + saveCharIndex--; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + } + + else if (ch == key_menu_escape) // phares 3/7/98 + { + saveStringEnter = 0; + strcpy(&savegamestrings[saveSlot][0],saveOldString); + } + + else if (ch == key_menu_enter) // phares 3/7/98 + { + saveStringEnter = 0; + if (savegamestrings[saveSlot][0]) + M_DoSave(saveSlot); + } + + else + { + ch = toupper(ch); + if (ch >= 32 && ch <= 127 && + saveCharIndex < SAVESTRINGSIZE-1 && + M_StringWidth(savegamestrings[saveSlot]) < (SAVESTRINGSIZE-2)*8) + { + savegamestrings[saveSlot][saveCharIndex++] = ch; + savegamestrings[saveSlot][saveCharIndex] = 0; + } + } + return true; + } + + // Take care of any messages that need input + + if (messageToPrint) { + if (messageNeedsInput == true && + !(ch == ' ' || ch == 'n' || ch == 'y' || ch == key_escape)) // phares + return false; + + menuactive = messageLastMenuActive; + messageToPrint = 0; + if (messageRoutine) + messageRoutine(ch); + + menuactive = mnact_inactive; + S_StartSound(NULL,sfx_swtchx); + return true; + } + + // If there is no active menu displayed... + + if (!menuactive) { // phares + if (ch == key_autorun) // Autorun // V + { + autorun = !autorun; + return true; + } + + if (ch == key_help) // Help key + { + M_StartControlPanel (); + + currentMenu = &HelpDef; // killough 10/98: new help screen + + itemOn = 0; + S_StartSound(NULL,sfx_swtchn); + return true; + } + + if (ch == key_savegame) // Save Game + { + M_StartControlPanel(); + S_StartSound(NULL,sfx_swtchn); + M_SaveGame(0); + return true; + } + + if (ch == key_loadgame) // Load Game + { + M_StartControlPanel(); + S_StartSound(NULL,sfx_swtchn); + M_LoadGame(0); + return true; + } + + if (ch == key_soundvolume) // Sound Volume + { + M_StartControlPanel (); + currentMenu = &SoundDef; + itemOn = sfx_vol; + S_StartSound(NULL,sfx_swtchn); + return true; + } + + if (ch == key_quicksave) // Quicksave + { + S_StartSound(NULL,sfx_swtchn); + M_QuickSave(); + return true; + } + + if (ch == key_endgame) // End game + { + S_StartSound(NULL,sfx_swtchn); + M_EndGame(0); + return true; + } + + if (ch == key_messages) // Toggle messages + { + M_ChangeMessages(0); + S_StartSound(NULL,sfx_swtchn); + return true; + } + + if (ch == key_quickload) // Quickload + { + S_StartSound(NULL,sfx_swtchn); + M_QuickLoad(); + return true; + } + + if (ch == key_quit) // Quit DOOM + { + S_StartSound(NULL,sfx_swtchn); + has_exited = true; + return true; + } + + if (ch == key_gamma) // gamma toggle + { + usegamma++; + if (usegamma > 4) + usegamma = 0; + players[consoleplayer].message = + usegamma == 0 ? s_GAMMALVL0 : + usegamma == 1 ? s_GAMMALVL1 : + usegamma == 2 ? s_GAMMALVL2 : + usegamma == 3 ? s_GAMMALVL3 : + s_GAMMALVL4; + V_SetPalette(0); + return true; + } + + + if (ch == key_zoomout) // zoom out + { + if ((automapmode & am_active) || chat_on) + return false; + M_SizeDisplay(0); + S_StartSound(NULL,sfx_stnmov); + return true; + } + + if (ch == key_zoomin) // zoom in + { // jff 2/23/98 + if ((automapmode & am_active) || chat_on) // allow + return false; // key_hud==key_zoomin + M_SizeDisplay(1); // ^ + S_StartSound(NULL,sfx_stnmov); // | + return true; // phares + } + + /* killough 10/98: allow key shortcut into Setup menu */ + if (ch == key_setup) { + M_StartControlPanel(); + S_StartSound(NULL,sfx_swtchn); + M_SetupNextMenu(&SetupDef); + return true; + } + } + // Pop-up Main menu? + + if (!menuactive) + { + if (ch == key_escape) // phares + { + M_StartControlPanel (); + S_StartSound(NULL,sfx_swtchn); + return true; + } + return false; + } + + // phares 3/26/98 - 4/11/98: + // Setup screen key processing + + if (setup_active) { + setup_menu_t* ptr1= current_setup_menu + set_menu_itemon; + setup_menu_t* ptr2 = NULL; + + // phares 4/19/98: + // Catch the response to the 'reset to default?' verification + // screen + + if (default_verify) + { + if (toupper(ch) == 'Y') { + M_ResetDefaults(); + default_verify = false; + M_SelectDone(ptr1); + } + else if (toupper(ch) == 'N') { + default_verify = false; + M_SelectDone(ptr1); + } + return true; + } + + // Common processing for some items + + if (setup_select) { // changing an entry + if (ch == key_menu_escape) // Exit key = no change + { + M_SelectDone(ptr1); // phares 4/17/98 + setup_gather = false; // finished gathering keys, if any + return true; + } + + if (ptr1->m_flags & S_YESNO) // yes or no setting? + { + if (ch == key_menu_enter) { + *ptr1->var.def->location.pi = !*ptr1->var.def->location.pi; // killough 8/15/98 + + // phares 4/14/98: + // If not in demoplayback, or netgame, + // and there's a second variable in var2, set that + // as well + + // killough 8/15/98: add warning messages + + if (ptr1->m_flags & (S_LEVWARN | S_PRGWARN)) + warn_about_changes(ptr1->m_flags & // killough 10/98 + (S_LEVWARN | S_PRGWARN)); + else + M_UpdateCurrent(ptr1->var.def); + + if (ptr1->action) // killough 10/98 + ptr1->action(); + } + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + if (ptr1->m_flags & S_CRITEM) + { + if (ch != key_menu_enter) + { + ch -= 0x30; // out of ascii + if (ch < 0 || ch > 9) + return true; // ignore + *ptr1->var.def->location.pi = ch; + } + if (ptr1->action) // killough 10/98 + ptr1->action(); + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + if (ptr1->m_flags & S_NUM) // number? + { + if (setup_gather) { // gathering keys for a value? + /* killough 10/98: Allow negatives, and use a more + * friendly input method (e.g. don't clear value early, + * allow backspace, and return to original value if bad + * value is entered). + */ + if (ch == key_menu_enter) { + if (gather_count) { // Any input? + int value; + + gather_buffer[gather_count] = 0; + value = atoi(gather_buffer); // Integer value + + if ((ptr1->var.def->minvalue != UL && + value < ptr1->var.def->minvalue) || + (ptr1->var.def->maxvalue != UL && + value > ptr1->var.def->maxvalue)) + warn_about_changes(S_BADVAL); + else { + *ptr1->var.def->location.pi = value; + + /* killough 8/9/98: fix numeric vars + * killough 8/15/98: add warning message + */ + if (ptr1->m_flags & (S_LEVWARN | S_PRGWARN)) + warn_about_changes(ptr1->m_flags & + (S_LEVWARN | S_PRGWARN)); + else + M_UpdateCurrent(ptr1->var.def); + + if (ptr1->action) // killough 10/98 + ptr1->action(); + } + } + M_SelectDone(ptr1); // phares 4/17/98 + setup_gather = false; // finished gathering keys + return true; + } + + if (ch == key_menu_backspace && gather_count) { + gather_count--; + return true; + } + + if (gather_count >= MAXGATHER) + return true; + + if (!isdigit(ch) && ch != '-') + return true; // ignore + + /* killough 10/98: character-based numerical input */ + gather_buffer[gather_count++] = ch; + } + return true; + } + + if (ptr1->m_flags & S_CHOICE) // selection of choices? + { + if (ch == key_menu_left) { + if (ptr1->var.def->type == def_int) { + int value = *ptr1->var.def->location.pi; + + value = value - 1; + if ((ptr1->var.def->minvalue != UL && + value < ptr1->var.def->minvalue)) + value = ptr1->var.def->minvalue; + if ((ptr1->var.def->maxvalue != UL && + value > ptr1->var.def->maxvalue)) + value = ptr1->var.def->maxvalue; + if (*ptr1->var.def->location.pi != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.pi = value; + } + if (ptr1->var.def->type == def_str) { + int old_value, value; + + old_value = M_IndexInChoices(*ptr1->var.def->location.ppsz, + ptr1->selectstrings); + value = old_value - 1; + if (value < 0) + value = 0; + if (old_value != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.ppsz = ptr1->selectstrings[value]; + } + } + if (ch == key_menu_right) { + if (ptr1->var.def->type == def_int) { + int value = *ptr1->var.def->location.pi; + + value = value + 1; + if ((ptr1->var.def->minvalue != UL && + value < ptr1->var.def->minvalue)) + value = ptr1->var.def->minvalue; + if ((ptr1->var.def->maxvalue != UL && + value > ptr1->var.def->maxvalue)) + value = ptr1->var.def->maxvalue; + if (*ptr1->var.def->location.pi != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.pi = value; + } + if (ptr1->var.def->type == def_str) { + int old_value, value; + + old_value = M_IndexInChoices(*ptr1->var.def->location.ppsz, + ptr1->selectstrings); + value = old_value + 1; + if (ptr1->selectstrings[value] == NULL) + value = old_value; + if (old_value != value) + S_StartSound(NULL,sfx_pstop); + *ptr1->var.def->location.ppsz = ptr1->selectstrings[value]; + } + } + if (ch == key_menu_enter) { + // phares 4/14/98: + // If not in demoplayback, or netgame, + // and there's a second variable in var2, set that + // as well + + // killough 8/15/98: add warning messages + + if (ptr1->m_flags & (S_LEVWARN | S_PRGWARN)) + warn_about_changes(ptr1->m_flags & // killough 10/98 + (S_LEVWARN | S_PRGWARN)); + else + M_UpdateCurrent(ptr1->var.def); + + if (ptr1->action) // killough 10/98 + ptr1->action(); + M_SelectDone(ptr1); // phares 4/17/98 + } + return true; + } + + } + + // Key Bindings + + if (set_keybnd_active) // on a key binding setup screen + if (setup_select) // incoming key or button gets bound + { + if (ev->type == ev_mouse) + { + int i,oldbutton,group; + boolean search = true; + + if (!ptr1->m_mouse) + return true; // not a legal action here (yet) + + // see if the button is already bound elsewhere. if so, you + // have to swap bindings so the action where it's currently + // bound doesn't go dead. Since there is more than one + // keybinding screen, you have to search all of them for + // any duplicates. You're only interested in the items + // that belong to the same group as the one you're changing. + + oldbutton = *ptr1->m_mouse; + group = ptr1->m_group; + if (ev->data1 & 1) + ch = 0; + else if (ev->data1 & 2) + ch = 1; + else if (ev->data1 & 4) + ch = 2; + else + return true; + for (i = 0 ; keys_settings[i] && search ; i++) + for (ptr2 = keys_settings[i] ; !(ptr2->m_flags & S_END) ; ptr2++) + if (ptr2->m_group == group && ptr1 != ptr2) + if (ptr2->m_flags & S_KEY && ptr2->m_mouse) + if (*ptr2->m_mouse == ch) + { + *ptr2->m_mouse = oldbutton; + search = false; + break; + } + *ptr1->m_mouse = ch; + } + else // keyboard key + { + int i,oldkey,group; + boolean search = true; + + // see if 'ch' is already bound elsewhere. if so, you have + // to swap bindings so the action where it's currently + // bound doesn't go dead. Since there is more than one + // keybinding screen, you have to search all of them for + // any duplicates. You're only interested in the items + // that belong to the same group as the one you're changing. + + // if you find that you're trying to swap with an action + // that has S_KEEP set, you can't bind ch; it's already + // bound to that S_KEEP action, and that action has to + // keep that key. + + oldkey = *ptr1->var.m_key; + group = ptr1->m_group; + for (i = 0 ; keys_settings[i] && search ; i++) + for (ptr2 = keys_settings[i] ; !(ptr2->m_flags & S_END) ; ptr2++) + if (ptr2->m_flags & (S_KEY|S_KEEP) && + ptr2->m_group == group && + ptr1 != ptr2) + if (*ptr2->var.m_key == ch) + { + if (ptr2->m_flags & S_KEEP) + return true; // can't have it! + *ptr2->var.m_key = oldkey; + search = false; + break; + } + *ptr1->var.m_key = ch; + } + + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + // Weapons + + if (set_weapon_active) // on the weapons setup screen + if (setup_select) // changing an entry + { + if (ch != key_menu_enter) + { + ch -= '0'; // out of ascii + if (ch < 1 || ch > 9) + return true; // ignore + + // Plasma and BFG don't exist in shareware + // killough 10/98: allow it anyway, since this + // isn't the game itself, just setting preferences + + // see if 'ch' is already assigned elsewhere. if so, + // you have to swap assignments. + + // killough 11/98: simplified + + for (i = 0; (ptr2 = weap_settings[i]); i++) + for (; !(ptr2->m_flags & S_END); ptr2++) + if (ptr2->m_flags & S_WEAP && + *ptr2->var.def->location.pi == ch && ptr1 != ptr2) + { + *ptr2->var.def->location.pi = *ptr1->var.def->location.pi; + goto end; + } + end: + *ptr1->var.def->location.pi = ch; + } + + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + // Automap + + if (set_auto_active) // on the automap setup screen + if (setup_select) // incoming key + { + if (ch == key_menu_down) + { + if (++color_palette_y == 16) + color_palette_y = 0; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_up) + { + if (--color_palette_y < 0) + color_palette_y = 15; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_left) + { + if (--color_palette_x < 0) + color_palette_x = 15; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_right) + { + if (++color_palette_x == 16) + color_palette_x = 0; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if (ch == key_menu_enter || ch == key_fire) + { + *ptr1->var.def->location.pi = color_palette_x + 16*color_palette_y; + M_SelectDone(ptr1); // phares 4/17/98 + colorbox_active = false; + return true; + } + } + + // killough 10/98: consolidate handling into one place: + if (setup_select && + set_enemy_active | set_general_active | set_chat_active | + set_mess_active | set_status_active | set_compat_active) + { + if (ptr1->m_flags & S_STRING) // creating/editing a string? + { + if (ch == key_menu_backspace) // backspace and DEL + { + if (chat_string_buffer[chat_index] == 0) + { + if (chat_index > 0) + chat_string_buffer[--chat_index] = 0; + } + // shift the remainder of the text one char left + else + strcpy(&chat_string_buffer[chat_index], + &chat_string_buffer[chat_index+1]); + } + else if (ch == key_menu_left) // move cursor left + { + if (chat_index > 0) + chat_index--; + } + else if (ch == key_menu_right) // move cursor right + { + if (chat_string_buffer[chat_index] != 0) + chat_index++; + } + else if ((ch == key_menu_enter) || + (ch == key_menu_escape) ||(ch == key_fire)) + { + *ptr1->var.def->location.ppsz = chat_string_buffer; + M_SelectDone(ptr1); // phares 4/17/98 + } + + // Adding a char to the text. Has to be a printable + // char, and you can't overrun the buffer. If the + // chat string gets larger than what the screen can hold, + // it is dealt with when the string is drawn (above). + + else if ((ch >= 32) && (ch <= 126)) + if ((chat_index+1) < CHAT_STRING_BFR_SIZE) + { + if (shiftdown) + ch = shiftxform[ch]; + if (chat_string_buffer[chat_index] == 0) + { + chat_string_buffer[chat_index++] = ch; + chat_string_buffer[chat_index] = 0; + } + else + chat_string_buffer[chat_index++] = ch; + } + return true; + } + + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + // Not changing any items on the Setup screens. See if we're + // navigating the Setup menus or selecting an item to change. + + if (ch == key_menu_down) + { + ptr1->m_flags &= ~S_HILITE; // phares 4/17/98 + do + if (ptr1->m_flags & S_END) + { + set_menu_itemon = 0; + ptr1 = current_setup_menu; + } + else + { + set_menu_itemon++; + ptr1++; + } + while (ptr1->m_flags & S_SKIP); + M_SelectDone(ptr1); // phares 4/17/98 + return true; + } + + if (ch == key_menu_up) + { + ptr1->m_flags &= ~S_HILITE; // phares 4/17/98 + do + { + if (set_menu_itemon == 0) + do + set_menu_itemon++; + while(!((current_setup_menu + set_menu_itemon)->m_flags & S_END)); + set_menu_itemon--; + } + while((current_setup_menu + set_menu_itemon)->m_flags & S_SKIP); + M_SelectDone(current_setup_menu + set_menu_itemon); // phares 4/17/98 + return true; + } + + if (ch == key_menu_enter || ch == key_fire) + { + int flags = ptr1->m_flags; + + // You've selected an item to change. Highlight it, post a new + // message about what to do, and get ready to process the + // change. + // + // killough 10/98: use friendlier char-based input buffer + + if (flags & S_NUM) + { + setup_gather = true; + print_warning_about_changes = false; + gather_count = 0; + } + else if (flags & S_COLOR) + { + int color = *ptr1->var.def->location.pi; + + if (color < 0 || color > 255) // range check the value + color = 0; // 'no show' if invalid + + color_palette_x = *ptr1->var.def->location.pi & 15; + color_palette_y = *ptr1->var.def->location.pi >> 4; + colorbox_active = true; + } + else if (flags & S_STRING) + { + // copy chat string into working buffer; trim if needed. + // free the old chat string memory and replace it with + // the (possibly larger) new memory for editing purposes + // + // killough 10/98: fix bugs, simplify + + chat_string_buffer = malloc(CHAT_STRING_BFR_SIZE); + strncpy(chat_string_buffer, + *ptr1->var.def->location.ppsz, CHAT_STRING_BFR_SIZE); + + // guarantee null delimiter + chat_string_buffer[CHAT_STRING_BFR_SIZE-1] = 0; + + // set chat table pointer to working buffer + // and free old string's memory. + { + union { const char **c; char **s; } u; // type punning via unions + + u.c = ptr1->var.def->location.ppsz; + free(*(u.s)); + *(u.c) = chat_string_buffer; + } + chat_index = 0; // current cursor position in chat_string_buffer + } + else if (flags & S_RESET) + default_verify = true; + + ptr1->m_flags |= S_SELECT; + setup_select = true; + S_StartSound(NULL,sfx_itemup); + return true; + } + + if ((ch == key_menu_escape) || (ch == key_menu_backspace)) + { + if (ch == key_menu_escape) // Clear all menus + M_ClearMenus(); + else // key_menu_backspace = return to Setup Menu + if (currentMenu->prevMenu) + { + currentMenu = currentMenu->prevMenu; + itemOn = currentMenu->lastOn; + S_StartSound(NULL,sfx_swtchn); + } + ptr1->m_flags &= ~(S_HILITE|S_SELECT);// phares 4/19/98 + setup_active = false; + set_keybnd_active = false; + set_weapon_active = false; + set_status_active = false; + set_auto_active = false; + set_enemy_active = false; + set_mess_active = false; + set_chat_active = false; + colorbox_active = false; + default_verify = false; // phares 4/19/98 + set_general_active = false; // killough 10/98 + set_compat_active = false; // killough 10/98 + HU_Start(); // catch any message changes // phares 4/19/98 + S_StartSound(NULL,sfx_swtchx); + return true; + } + + // Some setup screens may have multiple screens. + // When there are multiple screens, m_prev and m_next items need to + // be placed on the appropriate screen tables so the user can + // move among the screens using the left and right arrow keys. + // The m_var1 field contains a pointer to the appropriate screen + // to move to. + + if (ch == key_menu_left) + { + ptr2 = ptr1; + do + { + ptr2++; + if (ptr2->m_flags & S_PREV) + { + ptr1->m_flags &= ~S_HILITE; + mult_screens_index--; + current_setup_menu = ptr2->var.menu; + set_menu_itemon = 0; + print_warning_about_changes = false; // killough 10/98 + while (current_setup_menu[set_menu_itemon++].m_flags&S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; + S_StartSound(NULL,sfx_pstop); // killough 10/98 + return true; + } + } + while (!(ptr2->m_flags & S_END)); + } + + if (ch == key_menu_right) + { + ptr2 = ptr1; + do + { + ptr2++; + if (ptr2->m_flags & S_NEXT) + { + ptr1->m_flags &= ~S_HILITE; + mult_screens_index++; + current_setup_menu = ptr2->var.menu; + set_menu_itemon = 0; + print_warning_about_changes = false; // killough 10/98 + while (current_setup_menu[set_menu_itemon++].m_flags&S_SKIP); + current_setup_menu[--set_menu_itemon].m_flags |= S_HILITE; + S_StartSound(NULL,sfx_pstop); // killough 10/98 + return true; + } + } + while (!(ptr2->m_flags & S_END)); + } + + } // End of Setup Screen processing + + // From here on, these navigation keys are used on the BIG FONT menus + // like the Main Menu. + + if (ch == key_menu_down) // phares 3/7/98 + { + do + { + if (itemOn+1 > currentMenu->numitems-1) + itemOn = 0; + else + itemOn++; + S_StartSound(NULL,sfx_pstop); + } + while(currentMenu->menuitems[itemOn].status==-1); + return true; + } + + if (ch == key_menu_up) // phares 3/7/98 + { + do + { + if (!itemOn) + itemOn = currentMenu->numitems-1; + else + itemOn--; + S_StartSound(NULL,sfx_pstop); + } + while(currentMenu->menuitems[itemOn].status==-1); + return true; + } + + if (ch == key_menu_left) // phares 3/7/98 + { + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(NULL,sfx_stnmov); + currentMenu->menuitems[itemOn].routine(0); + } + return true; + } + + if (ch == key_menu_right) // phares 3/7/98 + { + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status == 2) + { + S_StartSound(NULL,sfx_stnmov); + currentMenu->menuitems[itemOn].routine(1); + } + return true; + } + + if (ch == key_menu_enter || ch == key_fire) // phares 3/7/98 + { + if (currentMenu->menuitems[itemOn].routine && + currentMenu->menuitems[itemOn].status) + { + currentMenu->lastOn = itemOn; + if (currentMenu->menuitems[itemOn].status == 2) + { + currentMenu->menuitems[itemOn].routine(1); // right arrow + S_StartSound(NULL,sfx_stnmov); + } + else + { + currentMenu->menuitems[itemOn].routine(itemOn); + S_StartSound(NULL,sfx_pistol); + } + } + //jff 3/24/98 remember last skill selected + // killough 10/98 moved to skill-specific functions + return true; + } + + if (ch == key_menu_escape) // phares 3/7/98 + { + currentMenu->lastOn = itemOn; + M_ClearMenus (); + S_StartSound(NULL,sfx_swtchx); + return true; + } + + if (ch == key_menu_backspace) // phares 3/7/98 + { + currentMenu->lastOn = itemOn; + + // phares 3/30/98: + // add checks to see if you're in the extended help screens + // if so, stay with the same menu definition, but bump the + // index back one. if the index bumps back far enough ( == 0) + // then you can return to the Read_Thisn menu definitions + + if (currentMenu->prevMenu) + { + if (currentMenu == &ExtHelpDef) + { + if (--extended_help_index == 0) + { + currentMenu = currentMenu->prevMenu; + extended_help_index = 1; // reset + } + } + else + currentMenu = currentMenu->prevMenu; + itemOn = currentMenu->lastOn; + S_StartSound(NULL,sfx_swtchn); + } + return true; + } + + else + { + for (i = itemOn+1;i < currentMenu->numitems;i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(NULL,sfx_pstop); + return true; + } + for (i = 0;i <= itemOn;i++) + if (currentMenu->menuitems[i].alphaKey == ch) + { + itemOn = i; + S_StartSound(NULL,sfx_pstop); + return true; + } + } + return false; +} + +// +// End of M_Responder +// +///////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////////// +// +// General Routines +// +// This displays the Main menu and gets the menu screens rolling. +// Plus a variety of routines that control the Big Font menu display. +// Plus some initialization for game-dependant situations. + +void M_StartControlPanel (void) +{ + // intro might call this repeatedly + + if (menuactive) + return; + + //jff 3/24/98 make default skill menu choice follow -skill or defaultskill + //from command line or config file + // + // killough 10/98: + // Fix to make "always floating" with menu selections, and to always follow + // defaultskill, instead of -skill. + + NewDef.lastOn = defaultskill - 1; + + default_verify = 0; // killough 10/98 + menuactive = mnact_float; + currentMenu = &MainDef; // JDC + itemOn = currentMenu->lastOn; // JDC + print_warning_about_changes = false; // killough 11/98 +} + +// +// M_Drawer +// Called after the view has been rendered, +// but before it has been blitted. +// +// killough 9/29/98: Significantly reformatted source +// + +void M_Drawer (void) +{ + inhelpscreens = false; + + // Horiz. & Vertically center string and print it. + // killough 9/29/98: simplified code, removed 40-character width limit + if (messageToPrint) + { + /* cph - strdup string to writable memory */ + char *ms = strdup(messageString); + char *p = ms; + + int y = 100 - M_StringHeight(messageString)/2; + while (*p) + { + char *string = p, c; + while ((c = *p) && *p != '\n') + p++; + *p = 0; + M_WriteText(160 - M_StringWidth(string)/2, y, string, CR_DEFAULT); + y += hu_font[0].height; + if ((*p = c)) + p++; + } + free(ms); + } + else + if (menuactive) + { + int x,y,max,i; + int lumps_missing = 0; + + menuactive = mnact_float; // Boom-style menu drawers will set mnact_full + + if (currentMenu->routine) + currentMenu->routine(); // call Draw routine + + // DRAW MENU + + x = currentMenu->x; + y = currentMenu->y; + max = currentMenu->numitems; + + for (i = 0; i < max; i++) + if (currentMenu->menuitems[i].name[0]) + if (W_CheckNumForName(currentMenu->menuitems[i].name) < 0) + lumps_missing++; + + if (lumps_missing == 0) + for (i=0;imenuitems[i].name[0]) + V_DrawNamePatch(x,y,0,currentMenu->menuitems[i].name, + CR_DEFAULT, VPT_NONE); + y += LINEHEIGHT; + } + else + for (i = 0; i < max; i++) + { + const char *alttext = currentMenu->menuitems[i].alttext; + if (alttext) + M_WriteText(x, y+8-(M_StringHeight(alttext)/2), alttext, CR_DEFAULT); + y += LINEHEIGHT; + } + + // DRAW SKULL + + // CPhipps - patch drawing updated + V_DrawNamePatch(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,0, + skullName[whichSkull], CR_DEFAULT, VPT_NONE); + } +} + +// +// M_ClearMenus +// +// Called when leaving the menu screens for the real world + +void M_ClearMenus (void) +{ + menuactive = mnact_inactive; + print_warning_about_changes = 0; // killough 8/15/98 + default_verify = 0; // killough 10/98 + + // if (!netgame && usergame && paused) + // sendpause = true; +} + +// +// M_SetupNextMenu +// +void M_SetupNextMenu(menu_t *menudef) +{ + currentMenu = menudef; + itemOn = currentMenu->lastOn; +} + +///////////////////////////// +// +// M_Ticker +// +void M_Ticker (void) +{ + if (--skullAnimCounter <= 0) + { + whichSkull ^= 1; + skullAnimCounter = 8; + } +} + +///////////////////////////// +// +// Message Routines +// + +void M_StartMessage (const char* string,void* routine,boolean input) +{ + messageLastMenuActive = menuactive; + messageToPrint = 1; + messageString = string; + messageRoutine = routine; + messageNeedsInput = input; + menuactive = mnact_float; + return; +} + +void M_StopMessage(void) +{ + menuactive = messageLastMenuActive; + messageToPrint = 0; +} + +///////////////////////////// +// +// Thermometer Routines +// + +// +// M_DrawThermo draws the thermometer graphic for Mouse Sensitivity, +// Sound Volume, etc. +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +// +void M_DrawThermo(int x,int y,int thermWidth,int thermDot ) +{ + int xx; + int i; + /* + * Modification By Barry Mead to allow the Thermometer to have vastly + * larger ranges. (the thermWidth parameter can now have a value as + * large as 200. Modified 1-9-2000 Originally I used it to make + * the sensitivity range for the mouse better. It could however also + * be used to improve the dynamic range of music and sound affect + * volume controls for example. + */ + int horizScaler; //Used to allow more thermo range for mouse sensitivity. + thermWidth = (thermWidth > 200) ? 200 : thermWidth; //Clamp to 200 max + horizScaler = (thermWidth > 23) ? (200 / thermWidth) : 8; //Dynamic range + xx = x; + V_DrawNamePatch(xx, y, 0, "M_THERML", CR_DEFAULT, VPT_NONE); + xx += 8; + for (i=0;ix - 10, menu->y+item*LINEHEIGHT - 1, 0, + "M_CELL1", CR_DEFAULT, VPT_NONE); +} + +// +// Draw a full cell in the thermometer +// + +void M_DrawSelCell (menu_t* menu,int item) +{ + // CPhipps - patch drawing updated + V_DrawNamePatch(menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0, + "M_CELL2", CR_DEFAULT, VPT_NONE); +} + +///////////////////////////// +// +// String-drawing Routines +// + +// +// Find string width from hu_font chars +// + +int M_StringWidth(const char* string) +{ + int i, c, w = 0; + for (i = 0;(size_t)i < strlen(string);i++) + w += (c = toupper(string[i]) - HU_FONTSTART) < 0 || c >= HU_FONTSIZE ? + 4 : hu_font[c].width; + return w; +} + +// +// Find string height from hu_font chars +// + +int M_StringHeight(const char* string) +{ + int i, h, height = h = hu_font[0].height; + for (i = 0;string[i];i++) // killough 1/31/98 + if (string[i] == '\n') + h += height; + return h; +} + +// +// Write a string using the hu_font +// +void M_WriteText (int x,int y, const char* string, int cm) +{ + int w; + const char* ch; + int c; + int cx; + int cy; + int flags; + + ch = string; + cx = x; + cy = y; + + flags = VPT_NONE; + if (cm != CR_DEFAULT) + flags |= VPT_TRANS; + + while(1) { + c = *ch++; + if (!c) + break; + if (c == '\n') { + cx = x; + cy += 12; + continue; + } + + c = toupper(c) - HU_FONTSTART; + if (c < 0 || c>= HU_FONTSIZE) { + cx += 4; + continue; + } + + w = hu_font[c].width; + if (cx+w > SCREENWIDTH) + break; + // proff/nicolas 09/20/98 -- changed for hi-res + // CPhipps - patch drawing updated + V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, cm, flags); + cx+=w; + } +} + +void M_DrawTitle(int x, int y, const char *patch, int cm, + const char *alttext, int altcm) +{ + int lumpnum = W_CheckNumForName(patch); + + if (lumpnum >= 0) + { + int flags = VPT_NONE; + if (cm != CR_DEFAULT) + flags |= VPT_TRANS; + V_DrawNumPatch(x, y, 0, lumpnum, cm, flags); + } + else + { + // patch doesn't exist, draw some text in place of it + M_WriteText(160-(M_StringWidth(alttext)/2), + y+8-(M_StringHeight(alttext)/2), // assumes patch height 16 + alttext, altcm); + } +} + +///////////////////////////// +// +// Initialization Routines to take care of one-time setup +// + +// phares 4/08/98: +// M_InitHelpScreen() clears the weapons from the HELP +// screen that don't exist in this version of the game. + +void M_InitHelpScreen(void) +{ + setup_menu_t* src; + + src = helpstrings; + while (!(src->m_flags & S_END)) { + + if ((strncmp(src->m_text,"PLASMA",6) == 0) && (gamemode == shareware)) + src->m_flags = S_SKIP; // Don't show setting or item + if ((strncmp(src->m_text,"BFG",3) == 0) && (gamemode == shareware)) + src->m_flags = S_SKIP; // Don't show setting or item + if ((strncmp(src->m_text,"SSG",3) == 0) && (gamemode != commercial)) + src->m_flags = S_SKIP; // Don't show setting or item + src++; + } +} + +// +// M_Init +// +void M_Init(void) +{ + M_InitDefaults(); // killough 11/98 + currentMenu = &MainDef; + menuactive = mnact_inactive; + itemOn = currentMenu->lastOn; + whichSkull = 0; + skullAnimCounter = 10; + messageToPrint = 0; + screenblocks = 0; + messageString = NULL; + messageLastMenuActive = menuactive; + quickSaveSlot = -1; + + // Here we could catch other version dependencies, + // like HELP1/2, and four episodes. + + switch(gamemode) + { + case commercial: + // This is used because DOOM 2 had only one HELP + // page. I use CREDIT as second page now, but + // kept this hack for educational purposes. + MainMenu[readthis] = MainMenu[quitdoom]; + MainDef.numitems--; + MainDef.y += 8; + NewDef.prevMenu = &MainDef; + ReadDef1.routine = M_DrawReadThis1; + ReadDef1.x = 330; + ReadDef1.y = 165; + ReadMenu1[0].routine = M_FinishReadThis; + break; + case registered: + // Episode 2 and 3 are handled, + // branching to an ad screen. + + // killough 2/21/98: Fix registered Doom help screen + // killough 10/98: moved to second screen, moved up to the top + ReadDef2.y = 15; + + case shareware: + // We need to remove the fourth episode. + EpiDef.numitems--; + break; + case retail: + // We are fine. + default: + break; + } + + M_InitHelpScreen(); // init the help screen // phares 4/08/98 + M_InitExtendedHelp(); // init extended help screens // phares 3/30/98 + + M_ChangeDemoSmoothTurns(); +} + +// +// End of General Routines +// +///////////////////////////////////////////////////////////////////////////// diff --git a/src/m_menu.h b/src/m_menu.h new file mode 100644 index 00000000..69953db1 --- /dev/null +++ b/src/m_menu.h @@ -0,0 +1,184 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Menu widget stuff, episode selection and such. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __M_MENU__ +#define __M_MENU__ + +#include "d_event.h" + +// +// MENUS +// +// Called by main loop, +// saves config file and calls I_Quit when user exits. +// Even when the menu is not displayed, +// this can resize the view and change game parameters. +// Does all the real work of the menu interaction. + +boolean M_Responder (event_t *ev); + +// Called by main loop, +// only used for menu (skull cursor) animation. + +void M_Ticker (void); + +// Called by main loop, +// draws the menus directly into the screen buffer. + +void M_Drawer (void); + +// Called by D_DoomMain, +// loads the config file. + +void M_Init (void); + +// Called by intro code to force menu up upon a keypress, +// does nothing if menu is already up. + +void M_StartControlPanel (void); + +void M_ForcedLoadGame(const char *msg); // killough 5/15/98: forced loadgames + +void M_Trans(void); // killough 11/98: reset translucency + +void M_ResetMenu(void); // killough 11/98: reset main menu ordering + +void M_DrawCredits(void); // killough 11/98 + +/* killough 8/15/98: warn about changes not being committed until next game */ +#define warn_about_changes(x) (warning_about_changes=(x), \ + print_warning_about_changes = 2) + +extern int warning_about_changes, print_warning_about_changes; + +extern boolean menu_background; + +/**************************** + * + * The following #defines are for the m_flags field of each item on every + * Setup Screen. They can be OR'ed together where appropriate + */ + +#define S_HILITE 0x1 // Cursor is sitting on this item +#define S_SELECT 0x2 // We're changing this item +#define S_TITLE 0x4 // Title item +#define S_YESNO 0x8 // Yes or No item +#define S_CRITEM 0x10 // Message color +#define S_COLOR 0x20 // Automap color +#define S_CHAT 0x40 // Chat String +#define S_RESET 0x80 // Reset to Defaults Button +#define S_PREV 0x100 // Previous menu exists +#define S_NEXT 0x200 // Next menu exists +#define S_KEY 0x400 // Key Binding +#define S_WEAP 0x800 // Weapon # +#define S_NUM 0x1000 // Numerical item +#define S_SKIP 0x2000 // Cursor can't land here +#define S_KEEP 0x4000 // Don't swap key out +#define S_END 0x8000 // Last item in list (dummy) +#define S_LEVWARN 0x10000// killough 8/30/98: Always warn about pending change +#define S_PRGWARN 0x20000// killough 10/98: Warn about change until next run +#define S_BADVAL 0x40000// killough 10/98: Warn about bad value +#define S_FILE 0x80000// killough 10/98: Filenames +#define S_LEFTJUST 0x100000 // killough 10/98: items which are left-justified +#define S_CREDIT 0x200000 // killough 10/98: credit +#define S_BADVID 0x400000 // killough 12/98: video mode change error +#define S_CHOICE 0x800000 // this item has several values + +/* S_SHOWDESC = the set of items whose description should be displayed + * S_SHOWSET = the set of items whose setting should be displayed + * S_STRING = the set of items whose settings are strings -- killough 10/98: + * S_HASDEFPTR = the set of items whose var field points to default array + */ + +#define S_SHOWDESC (S_TITLE|S_YESNO|S_CRITEM|S_COLOR|S_CHAT|S_RESET|S_PREV|S_NEXT|S_KEY|S_WEAP|S_NUM|S_FILE|S_CREDIT|S_CHOICE) + +#define S_SHOWSET (S_YESNO|S_CRITEM|S_COLOR|S_CHAT|S_KEY|S_WEAP|S_NUM|S_FILE|S_CHOICE) + +#define S_STRING (S_CHAT|S_FILE) + +#define S_HASDEFPTR (S_STRING|S_YESNO|S_NUM|S_WEAP|S_COLOR|S_CRITEM|S_CHOICE) + +/**************************** + * + * The setup_group enum is used to show which 'groups' keys fall into so + * that you can bind a key differently in each 'group'. + */ + +typedef enum { + m_null, // Has no meaning; not applicable + m_scrn, // A key can not be assigned to more than one action + m_map, // in the same group. A key can be assigned to one + m_menu, // action in one group, and another action in another. +} setup_group; + +/**************************** + * + * phares 4/17/98: + * State definition for each item. + * This is the definition of the structure for each setup item. Not all + * fields are used by all items. + * + * A setup screen is defined by an array of these items specific to + * that screen. + * + * killough 11/98: + * + * Restructured to allow simpler table entries, + * and to Xref with defaults[] array in m_misc.c. + * Moved from m_menu.c to m_menu.h so that m_misc.c can use it. + */ + +typedef struct setup_menu_s +{ + const char *m_text; /* text to display */ + int m_flags; /* phares 4/17/98: flag bits S_* (defined above) */ + setup_group m_group; /* Group */ + short m_x; /* screen x position (left is 0) */ + short m_y; /* screen y position (top is 0) */ + + union /* killough 11/98: The first field is a union of several types */ + { + const void *var; /* generic variable */ + int *m_key; /* key value, or 0 if not shown */ + const char *name; /* name */ + struct default_s *def; /* default[] table entry */ + struct setup_menu_s *menu; /* next or prev menu */ + } var; + + int *m_mouse; /* mouse button value, or 0 if not shown */ + int *m_joy; /* joystick button value, or 0 if not shown */ + void (*action)(void); /* killough 10/98: function to call after changing */ + const char **selectstrings; /* list of strings for choice value */ +} setup_menu_t; + +#endif diff --git a/src/m_misc.c b/src/m_misc.c new file mode 100644 index 00000000..fbb1e11d --- /dev/null +++ b/src/m_misc.c @@ -0,0 +1,916 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Main loop menu stuff. + * Default Config File. + * PCX Screenshots. + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#ifdef _MSC_VER +#include +#endif +#include +#include + +#include "doomstat.h" +#include "m_argv.h" +#include "g_game.h" +#include "m_menu.h" +#include "am_map.h" +#include "w_wad.h" +#include "i_system.h" +#include "i_sound.h" +#include "i_video.h" +#include "v_video.h" +#include "hu_stuff.h" +#include "st_stuff.h" +#include "dstrings.h" +#include "m_misc.h" +#include "s_sound.h" +#include "sounds.h" +#include "lprintf.h" +#include "d_main.h" +#include "r_draw.h" +#include "r_demo.h" +#include "r_fps.h" + +/* + * M_WriteFile + * + * killough 9/98: rewritten to use stdio and to flash disk icon + */ + +boolean M_WriteFile(char const *name, void *source, int length) +{ + FILE *fp; + + errno = 0; + + if (!(fp = fopen(name, "wb"))) // Try opening file + return 0; // Could not open file for writing + + length = fwrite(source, 1, length, fp) == (size_t)length; // Write data + fclose(fp); + + if (!length) // Remove partially written file + remove(name); + + return length; +} + +/* + * M_ReadFile + * + * killough 9/98: rewritten to use stdio and to flash disk icon + */ + +int M_ReadFile(char const *name, byte **buffer) +{ + FILE *fp; + + if ((fp = fopen(name, "rb"))) + { + size_t length; + + fseek(fp, 0, SEEK_END); + length = ftell(fp); + fseek(fp, 0, SEEK_SET); + *buffer = Z_Malloc(length, PU_STATIC, 0); + if (fread(*buffer, 1, length, fp) == length) + { + fclose(fp); + return length; + } + fclose(fp); + } + + /* cph 2002/08/10 - this used to return 0 on error, but that's ambiguous, + * because we could have a legit 0-length file. So make it -1. */ + return -1; +} + +// +// DEFAULTS +// + +int usemouse; +boolean precache = true; /* if true, load all graphics at start */ + +extern int viewwidth; +extern int viewheight; + +extern int tran_filter_pct; // killough 2/21/98 + +extern int screenblocks; +extern int showMessages; + +int mus_pause_opt; // 0 = kill music, 1 = pause, 2 = continue + +extern const char* chat_macros[]; + +extern const char* S_music_files[]; // cournia + +/* cph - Some MBF stuff parked here for now + * killough 10/98 + */ +int map_point_coordinates; + +default_t defaults[] = +{ + {"Misc settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"default_compatibility_level",{(int*)&default_compatibility_level}, + {-1},-1,MAX_COMPATIBILITY_LEVEL-1, + def_int,ss_none}, // compatibility level" - CPhipps + {"menu_background", {(int*)&menu_background}, {1}, 0, 1, + def_bool,ss_none}, // do Boom fullscreen menus have backgrounds? + {"max_player_corpse", {&bodyquesize}, {32},-1,UL, // killough 2/8/98 + def_int,ss_none}, // number of dead bodies in view supported (-1 = no limit) + {"flashing_hom",{&flashing_hom},{0},0,1, + def_bool,ss_none}, // killough 10/98 - enable flashing HOM indicator + {"demo_insurance",{&default_demo_insurance},{2},0,2, // killough 3/31/98 + def_int,ss_none}, // 1=take special steps ensuring demo sync, 2=only during recordings + {"level_precache",{(int*)&precache},{0},0,1, + def_bool,ss_none}, // precache level data? + {"demo_smoothturns", {&demo_smoothturns}, {0},0,1, + def_bool,ss_stat}, + {"demo_smoothturnsfactor", {&demo_smoothturnsfactor}, {6},1,SMOOTH_PLAYING_MAXFACTOR, + def_int,ss_stat}, + + {"Files",{NULL},{0},UL,UL,def_none,ss_none}, + /* cph - MBF-like wad/deh/bex autoload code */ + {"wadfile_1",{NULL,&wad_files[0]},{0,""},UL,UL,def_str,ss_none}, + {"wadfile_2",{NULL,&wad_files[1]},{0,""},UL,UL,def_str,ss_none}, + {"dehfile_1",{NULL,&deh_files[0]},{0,""},UL,UL,def_str,ss_none}, + {"dehfile_2",{NULL,&deh_files[1]},{0,""},UL,UL,def_str,ss_none}, + + {"Game settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"default_skill",{&defaultskill},{3},1,5, // jff 3/24/98 allow default skill setting + def_int,ss_none}, // selects default skill 1=TYTD 2=NTR 3=HMP 4=UV 5=NM + {"weapon_recoil",{&default_weapon_recoil},{0},0,1, + def_bool,ss_weap, &weapon_recoil}, + /* killough 10/98 - toggle between SG/SSG and Fist/Chainsaw */ + {"doom_weapon_toggles",{&doom_weapon_toggles}, {1}, 0, 1, + def_bool, ss_weap }, + {"player_bobbing",{&default_player_bobbing},{1},0,1, // phares 2/25/98 + def_bool,ss_weap, &player_bobbing}, + {"monsters_remember",{&default_monsters_remember},{1},0,1, // killough 3/1/98 + def_bool,ss_enem, &monsters_remember}, + /* MBF AI enhancement options */ + {"monster_infighting",{&default_monster_infighting}, {1}, 0, 1, + def_bool, ss_enem, &monster_infighting}, + {"monster_backing",{&default_monster_backing}, {0}, 0, 1, + def_bool, ss_enem, &monster_backing}, + {"monster_avoid_hazards",{&default_monster_avoid_hazards}, {1}, 0, 1, + def_bool, ss_enem, &monster_avoid_hazards}, + {"monkeys",{&default_monkeys}, {0}, 0, 1, + def_bool, ss_enem, &monkeys}, + {"monster_friction",{&default_monster_friction}, {1}, 0, 1, + def_bool, ss_enem, &monster_friction}, + {"help_friends",{&default_help_friends}, {1}, 0, 1, + def_bool, ss_enem, &help_friends}, + {"allow_pushers",{&default_allow_pushers},{1},0,1, + def_bool,ss_weap, &allow_pushers}, + {"variable_friction",{&default_variable_friction},{1},0,1, + def_bool,ss_weap, &variable_friction}, + /* End of MBF AI extras */ + + {"sts_always_red",{&sts_always_red},{1},0,1, // no color changes on status bar + def_bool,ss_stat}, + {"sts_pct_always_gray",{&sts_pct_always_gray},{0},0,1, // 2/23/98 chg default + def_bool,ss_stat}, // makes percent signs on status bar always gray + {"sts_traditional_keys",{&sts_traditional_keys},{0},0,1, // killough 2/28/98 + def_bool,ss_stat}, // disables doubled card and skull key display on status bar + {"show_messages",{&showMessages},{1},0,1, + def_bool,ss_none}, // enables message display + {"autorun",{&autorun},{0},0,1, // killough 3/6/98: preserve autorun across games + def_bool,ss_none}, + + {"Compatibility settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"comp_zombie",{&default_comp[comp_zombie]},{0},0,1,def_bool,ss_comp,&comp[comp_zombie]}, + {"comp_infcheat",{&default_comp[comp_infcheat]},{0},0,1,def_bool,ss_comp,&comp[comp_infcheat]}, + {"comp_stairs",{&default_comp[comp_stairs]},{0},0,1,def_bool,ss_comp,&comp[comp_stairs]}, + {"comp_telefrag",{&default_comp[comp_telefrag]},{0},0,1,def_bool,ss_comp,&comp[comp_telefrag]}, + {"comp_dropoff",{&default_comp[comp_dropoff]},{0},0,1,def_bool,ss_comp,&comp[comp_dropoff]}, + {"comp_falloff",{&default_comp[comp_falloff]},{0},0,1,def_bool,ss_comp,&comp[comp_falloff]}, + {"comp_staylift",{&default_comp[comp_staylift]},{0},0,1,def_bool,ss_comp,&comp[comp_staylift]}, + {"comp_doorstuck",{&default_comp[comp_doorstuck]},{0},0,1,def_bool,ss_comp,&comp[comp_doorstuck]}, + {"comp_pursuit",{&default_comp[comp_pursuit]},{0},0,1,def_bool,ss_comp,&comp[comp_pursuit]}, + {"comp_vile",{&default_comp[comp_vile]},{0},0,1,def_bool,ss_comp,&comp[comp_vile]}, + {"comp_pain",{&default_comp[comp_pain]},{0},0,1,def_bool,ss_comp,&comp[comp_pain]}, + {"comp_skull",{&default_comp[comp_skull]},{0},0,1,def_bool,ss_comp,&comp[comp_skull]}, + {"comp_blazing",{&default_comp[comp_blazing]},{0},0,1,def_bool,ss_comp,&comp[comp_blazing]}, + {"comp_doorlight",{&default_comp[comp_doorlight]},{0},0,1,def_bool,ss_comp,&comp[comp_doorlight]}, + {"comp_god",{&default_comp[comp_god]},{0},0,1,def_bool,ss_comp,&comp[comp_god]}, + {"comp_skymap",{&default_comp[comp_skymap]},{0},0,1,def_bool,ss_comp,&comp[comp_skymap]}, + {"comp_floors",{&default_comp[comp_floors]},{0},0,1,def_bool,ss_comp,&comp[comp_floors]}, + {"comp_model",{&default_comp[comp_model]},{0},0,1,def_bool,ss_comp,&comp[comp_model]}, + {"comp_zerotags",{&default_comp[comp_zerotags]},{0},0,1,def_bool,ss_comp,&comp[comp_zerotags]}, + {"comp_moveblock",{&default_comp[comp_moveblock]},{0},0,1,def_bool,ss_comp,&comp[comp_moveblock]}, + {"comp_sound",{&default_comp[comp_sound]},{0},0,1,def_bool,ss_comp,&comp[comp_sound]}, + {"comp_666",{&default_comp[comp_666]},{0},0,1,def_bool,ss_comp,&comp[comp_666]}, + {"comp_soul",{&default_comp[comp_soul]},{0},0,1,def_bool,ss_comp,&comp[comp_soul]}, + {"comp_maskedanim",{&default_comp[comp_maskedanim]},{0},0,1,def_bool,ss_comp,&comp[comp_maskedanim]}, + + {"Sound settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"sound_card",{&snd_card},{-1},-1,7, // jff 1/18/98 allow Allegro drivers + def_int,ss_none}, // select sounds driver (DOS), -1 is autodetect, 0 is none; in Linux, non-zero enables sound + {"music_card",{&mus_card},{-1},-1,9, // to be set, -1 = autodetect + def_int,ss_none}, // select music driver (DOS), -1 is autodetect, 0 is none"; in Linux, non-zero enables music + {"pitched_sounds",{&pitched_sounds},{0},0,1, // killough 2/21/98 + def_bool,ss_none}, // enables variable pitch in sound effects (from id's original code) + {"samplerate",{&snd_samplerate},{22050},11025,48000, def_int,ss_none}, + {"sfx_volume",{&snd_SfxVolume},{8},0,15, def_int,ss_none}, + {"music_volume",{&snd_MusicVolume},{8},0,15, def_int,ss_none}, + {"mus_pause_opt",{&mus_pause_opt},{2},0,2, // CPhipps - music pausing + def_int, ss_none}, // 0 = kill music when paused, 1 = pause music, 2 = let music continue + {"snd_channels",{&default_numChannels},{8},1,32, + def_int,ss_none}, // number of audio events simultaneously // killough + + {"Video settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"use_doublebuffer",{&use_doublebuffer},{1},0,1, // proff 2001-7-4 + def_bool,ss_none}, // enable doublebuffer to avoid display tearing (fullscreen) + {"screenblocks",{&screenblocks},{10},3,11, // killough 2/21/98: default to 10 + def_int,ss_none}, + {"usegamma",{&usegamma},{0},0,4, //jff 3/6/98 fix erroneous upper limit in range + def_int,ss_none}, // gamma correction level // killough 1/18/98 + {"uncapped_framerate", {&movement_smooth}, {0},0,1, + def_bool,ss_stat}, + {"filter_wall",{(int*)&drawvars.filterwall},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_floor",{(int*)&drawvars.filterfloor},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_sprite",{(int*)&drawvars.filtersprite},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_z",{(int*)&drawvars.filterz},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_LINEAR, def_int,ss_none}, + {"filter_patch",{(int*)&drawvars.filterpatch},{RDRAW_FILTER_POINT}, + RDRAW_FILTER_POINT, RDRAW_FILTER_ROUNDED, def_int,ss_none}, + {"filter_threshold",{(int*)&drawvars.mag_threshold},{49152}, + 0, UL, def_int,ss_none}, + {"sprite_edges",{(int*)&drawvars.sprite_edges},{RDRAW_MASKEDCOLUMNEDGE_SQUARE}, + RDRAW_MASKEDCOLUMNEDGE_SQUARE, RDRAW_MASKEDCOLUMNEDGE_SLOPED, def_int,ss_none}, + {"patch_edges",{(int*)&drawvars.patch_edges},{RDRAW_MASKEDCOLUMNEDGE_SQUARE}, + RDRAW_MASKEDCOLUMNEDGE_SQUARE, RDRAW_MASKEDCOLUMNEDGE_SLOPED, def_int,ss_none}, + + + {"Mouse settings",{NULL},{0},UL,UL,def_none,ss_none}, + {"use_mouse",{&usemouse},{1},0,1, + def_bool,ss_none}, // enables use of mouse with DOOM + //jff 4/3/98 allow unlimited sensitivity + {"mouse_sensitivity_horiz",{&mouseSensitivity_horiz},{10},0,UL, + def_int,ss_none}, /* adjust horizontal (x) mouse sensitivity killough/mead */ + //jff 4/3/98 allow unlimited sensitivity + {"mouse_sensitivity_vert",{&mouseSensitivity_vert},{10},0,UL, + def_int,ss_none}, /* adjust vertical (y) mouse sensitivity killough/mead */ + //jff 3/8/98 allow -1 in mouse bindings to disable mouse function + {"mouseb_fire",{&mousebfire},{0},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for fire + {"mouseb_strafe",{&mousebstrafe},{1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for strafing + {"mouseb_forward",{&mousebforward},{2},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for forward motion + {"mouseb_backward",{&mousebbackward},{-1},-1,MAX_MOUSEB, + def_int,ss_keys}, // mouse button number to use for backward motion + +// For key bindings, the values stored in the key_* variables // phares +// are the internal Doom Codes. The values stored in the default.cfg +// file are the keyboard codes. +// CPhipps - now they're the doom codes, so default.cfg can be portable + + {"Key bindings",{NULL},{0},UL,UL,def_none,ss_none}, + {"key_right", {&key_right}, {KEYD_RIGHTARROW}, + 0,MAX_KEY,def_key,ss_keys}, // key to turn right + {"key_left", {&key_left}, {KEYD_LEFTARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to turn left + {"key_up", {&key_up}, {KEYD_UPARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to move forward + {"key_down", {&key_down}, {KEYD_DOWNARROW}, + 0,MAX_KEY,def_key,ss_keys}, // key to move backward + {"key_menu_right", {&key_menu_right}, {KEYD_RIGHTARROW},// phares 3/7/98 + 0,MAX_KEY,def_key,ss_keys}, // key to move right in a menu // | + {"key_menu_left", {&key_menu_left}, {KEYD_LEFTARROW} ,// V + 0,MAX_KEY,def_key,ss_keys}, // key to move left in a menu + {"key_menu_up", {&key_menu_up}, {KEYD_UPARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to move up in a menu + {"key_menu_down", {&key_menu_down}, {KEYD_DOWNARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to move down in a menu + {"key_menu_backspace",{&key_menu_backspace},{KEYD_BACKSPACE} , + 0,MAX_KEY,def_key,ss_keys}, // delete key in a menu + {"key_menu_escape", {&key_menu_escape}, {KEYD_ESCAPE} , + 0,MAX_KEY,def_key,ss_keys}, // key to leave a menu , // phares 3/7/98 + {"key_menu_enter", {&key_menu_enter}, {KEYD_ENTER} , + 0,MAX_KEY,def_key,ss_keys}, // key to select from menu + {"key_setup", {&key_setup}, {KEYD_HOME}, + 0,MAX_KEY,def_key,ss_keys}, //e6y: key for entering setup menu + {"key_strafeleft", {&key_strafeleft}, {','} , + 0,MAX_KEY,def_key,ss_keys}, // key to strafe left + {"key_straferight", {&key_straferight}, {'.'} , + 0,MAX_KEY,def_key,ss_keys}, // key to strafe right + + {"key_fire", {&key_fire}, {KEYD_RCTRL} , + 0,MAX_KEY,def_key,ss_keys}, // duh + {"key_use", {&key_use}, {' '} , + 0,MAX_KEY,def_key,ss_keys}, // key to open a door, use a switch + {"key_strafe", {&key_strafe}, {KEYD_RALT} , + 0,MAX_KEY,def_key,ss_keys}, // key to use with arrows to strafe + {"key_speed", {&key_speed}, {KEYD_RSHIFT} , + 0,MAX_KEY,def_key,ss_keys}, // key to run + + {"key_savegame", {&key_savegame}, {KEYD_F2} , + 0,MAX_KEY,def_key,ss_keys}, // key to save current game + {"key_loadgame", {&key_loadgame}, {KEYD_F3} , + 0,MAX_KEY,def_key,ss_keys}, // key to restore from saved games + {"key_soundvolume", {&key_soundvolume}, {KEYD_F4} , + 0,MAX_KEY,def_key,ss_keys}, // key to bring up sound controls + {"key_hud", {&key_hud}, {KEYD_F5} , + 0,MAX_KEY,def_key,ss_keys}, // key to adjust HUD + {"key_quicksave", {&key_quicksave}, {KEYD_F6} , + 0,MAX_KEY,def_key,ss_keys}, // key to to quicksave + {"key_endgame", {&key_endgame}, {KEYD_F7} , + 0,MAX_KEY,def_key,ss_keys}, // key to end the game + {"key_messages", {&key_messages}, {KEYD_F8} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle message enable + {"key_quickload", {&key_quickload}, {KEYD_F9} , + 0,MAX_KEY,def_key,ss_keys}, // key to load from quicksave + {"key_quit", {&key_quit}, {KEYD_F10} , + 0,MAX_KEY,def_key,ss_keys}, // key to quit game + {"key_gamma", {&key_gamma}, {KEYD_F11} , + 0,MAX_KEY,def_key,ss_keys}, // key to adjust gamma correction + {"key_spy", {&key_spy}, {KEYD_F12} , + 0,MAX_KEY,def_key,ss_keys}, // key to view from another coop player's view + {"key_pause", {&key_pause}, {KEYD_PAUSE} , + 0,MAX_KEY,def_key,ss_keys}, // key to pause the game + {"key_autorun", {&key_autorun}, {KEYD_CAPSLOCK} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle always run mode + {"key_chat", {&key_chat}, {'t'} , + 0,MAX_KEY,def_key,ss_keys}, // key to enter a chat message + {"key_backspace", {&key_backspace}, {KEYD_BACKSPACE} , + 0,MAX_KEY,def_key,ss_keys}, // backspace key + {"key_enter", {&key_enter}, {KEYD_ENTER} , + 0,MAX_KEY,def_key,ss_keys}, // key to select from menu or see last message + {"key_map", {&key_map}, {KEYD_TAB} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle automap display + {"key_map_right", {&key_map_right}, {KEYD_RIGHTARROW},// phares 3/7/98 + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap right // | + {"key_map_left", {&key_map_left}, {KEYD_LEFTARROW} ,// V + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap left + {"key_map_up", {&key_map_up}, {KEYD_UPARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap up + {"key_map_down", {&key_map_down}, {KEYD_DOWNARROW} , + 0,MAX_KEY,def_key,ss_keys}, // key to shift automap down + {"key_map_zoomin", {&key_map_zoomin}, {'='} , + 0,MAX_KEY,def_key,ss_keys}, // key to enlarge automap + {"key_map_zoomout", {&key_map_zoomout}, {'-'} , + 0,MAX_KEY,def_key,ss_keys}, // key to reduce automap + {"key_map_gobig", {&key_map_gobig}, {'0'} , + 0,MAX_KEY,def_key,ss_keys}, // key to get max zoom for automap + {"key_map_follow", {&key_map_follow}, {'f'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle follow mode + {"key_map_mark", {&key_map_mark}, {'m'} , + 0,MAX_KEY,def_key,ss_keys}, // key to drop a marker on automap + {"key_map_clear", {&key_map_clear}, {'c'} , + 0,MAX_KEY,def_key,ss_keys}, // key to clear all markers on automap + {"key_map_grid", {&key_map_grid}, {'g'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle grid display over automap + {"key_map_rotate", {&key_map_rotate}, {'r'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle rotating the automap to match the player's orientation + {"key_map_overlay", {&key_map_overlay}, {'o'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle overlaying the automap on the rendered display + {"key_reverse", {&key_reverse}, {'/'} , + 0,MAX_KEY,def_key,ss_keys}, // key to spin 180 instantly + {"key_zoomin", {&key_zoomin}, {'='} , + 0,MAX_KEY,def_key,ss_keys}, // key to enlarge display + {"key_zoomout", {&key_zoomout}, {'-'} , + 0,MAX_KEY,def_key,ss_keys}, // key to reduce display + {"key_chatplayer1", {&destination_keys[0]}, {'g'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 1 + // killough 11/98: fix 'i'/'b' reversal + {"key_chatplayer2", {&destination_keys[1]}, {'i'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 2 + {"key_chatplayer3", {&destination_keys[2]}, {'b'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 3 + {"key_chatplayer4", {&destination_keys[3]}, {'r'} , + 0,MAX_KEY,def_key,ss_keys}, // key to chat with player 4 + {"key_weapontoggle",{&key_weapontoggle}, {'0'} , + 0,MAX_KEY,def_key,ss_keys}, // key to toggle between two most preferred weapons with ammo + {"key_weaponcycleup",{&key_weaponcycleup}, {'m'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to the next weapon + {"key_weaponcycledown",{&key_weaponcycledown}, {'n'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to the previous weapon + {"key_weapon1", {&key_weapon1}, {'1'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 1 (fist/chainsaw) + {"key_weapon2", {&key_weapon2}, {'2'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 2 (pistol) + {"key_weapon3", {&key_weapon3}, {'3'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 3 (supershotgun/shotgun) + {"key_weapon4", {&key_weapon4}, {'4'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 4 (chaingun) + {"key_weapon5", {&key_weapon5}, {'5'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 5 (rocket launcher) + {"key_weapon6", {&key_weapon6}, {'6'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 6 (plasma rifle) + {"key_weapon7", {&key_weapon7}, {'7'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 7 (bfg9000) // ^ + {"key_weapon8", {&key_weapon8}, {'8'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 8 (chainsaw) // | + {"key_weapon9", {&key_weapon9}, {'9'} , + 0,MAX_KEY,def_key,ss_keys}, // key to switch to weapon 9 (supershotgun) // phares + + // killough 2/22/98: screenshot key + {"key_screenshot", {&key_screenshot}, {'*'} , + 0,MAX_KEY,def_key,ss_keys}, // key to take a screenshot + {"Chat macros",{NULL},{0},UL,UL,def_none,ss_none}, + {"chatmacro0", {0,&chat_macros[0]}, {0,HUSTR_CHATMACRO0},UL,UL, + def_str,ss_chat}, // chat string associated with 0 key + {"chatmacro1", {0,&chat_macros[1]}, {0,HUSTR_CHATMACRO1},UL,UL, + def_str,ss_chat}, // chat string associated with 1 key + {"chatmacro2", {0,&chat_macros[2]}, {0,HUSTR_CHATMACRO2},UL,UL, + def_str,ss_chat}, // chat string associated with 2 key + {"chatmacro3", {0,&chat_macros[3]}, {0,HUSTR_CHATMACRO3},UL,UL, + def_str,ss_chat}, // chat string associated with 3 key + {"chatmacro4", {0,&chat_macros[4]}, {0,HUSTR_CHATMACRO4},UL,UL, + def_str,ss_chat}, // chat string associated with 4 key + {"chatmacro5", {0,&chat_macros[5]}, {0,HUSTR_CHATMACRO5},UL,UL, + def_str,ss_chat}, // chat string associated with 5 key + {"chatmacro6", {0,&chat_macros[6]}, {0,HUSTR_CHATMACRO6},UL,UL, + def_str,ss_chat}, // chat string associated with 6 key + {"chatmacro7", {0,&chat_macros[7]}, {0,HUSTR_CHATMACRO7},UL,UL, + def_str,ss_chat}, // chat string associated with 7 key + {"chatmacro8", {0,&chat_macros[8]}, {0,HUSTR_CHATMACRO8},UL,UL, + def_str,ss_chat}, // chat string associated with 8 key + {"chatmacro9", {0,&chat_macros[9]}, {0,HUSTR_CHATMACRO9},UL,UL, + def_str,ss_chat}, // chat string associated with 9 key + + {"Automap settings",{NULL},{0},UL,UL,def_none,ss_none}, + //jff 1/7/98 defaults for automap colors + //jff 4/3/98 remove -1 in lower range, 0 now disables new map features + {"mapcolor_back", {&mapcolor_back}, {247},0,255, // black //jff 4/6/98 new black + def_colour,ss_auto}, // color used as background for automap + {"mapcolor_grid", {&mapcolor_grid}, {104},0,255, // dk gray + def_colour,ss_auto}, // color used for automap grid lines + {"mapcolor_wall", {&mapcolor_wall}, {23},0,255, // red-brown + def_colour,ss_auto}, // color used for one side walls on automap + {"mapcolor_fchg", {&mapcolor_fchg}, {55},0,255, // lt brown + def_colour,ss_auto}, // color used for lines floor height changes across + {"mapcolor_cchg", {&mapcolor_cchg}, {215},0,255, // orange + def_colour,ss_auto}, // color used for lines ceiling height changes across + {"mapcolor_clsd", {&mapcolor_clsd}, {208},0,255, // white + def_colour,ss_auto}, // color used for lines denoting closed doors, objects + {"mapcolor_rkey", {&mapcolor_rkey}, {175},0,255, // red + def_colour,ss_auto}, // color used for red key sprites + {"mapcolor_bkey", {&mapcolor_bkey}, {204},0,255, // blue + def_colour,ss_auto}, // color used for blue key sprites + {"mapcolor_ykey", {&mapcolor_ykey}, {231},0,255, // yellow + def_colour,ss_auto}, // color used for yellow key sprites + {"mapcolor_rdor", {&mapcolor_rdor}, {175},0,255, // red + def_colour,ss_auto}, // color used for closed red doors + {"mapcolor_bdor", {&mapcolor_bdor}, {204},0,255, // blue + def_colour,ss_auto}, // color used for closed blue doors + {"mapcolor_ydor", {&mapcolor_ydor}, {231},0,255, // yellow + def_colour,ss_auto}, // color used for closed yellow doors + {"mapcolor_tele", {&mapcolor_tele}, {119},0,255, // dk green + def_colour,ss_auto}, // color used for teleporter lines + {"mapcolor_secr", {&mapcolor_secr}, {252},0,255, // purple + def_colour,ss_auto}, // color used for lines around secret sectors + {"mapcolor_exit", {&mapcolor_exit}, {0},0,255, // none + def_colour,ss_auto}, // color used for exit lines + {"mapcolor_unsn", {&mapcolor_unsn}, {104},0,255, // dk gray + def_colour,ss_auto}, // color used for lines not seen without computer map + {"mapcolor_flat", {&mapcolor_flat}, {88},0,255, // lt gray + def_colour,ss_auto}, // color used for lines with no height changes + {"mapcolor_sprt", {&mapcolor_sprt}, {112},0,255, // green + def_colour,ss_auto}, // color used as things + {"mapcolor_item", {&mapcolor_item}, {231},0,255, // yellow + def_colour,ss_auto}, // color used for counted items + {"mapcolor_hair", {&mapcolor_hair}, {208},0,255, // white + def_colour,ss_auto}, // color used for dot crosshair denoting center of map + {"mapcolor_sngl", {&mapcolor_sngl}, {208},0,255, // white + def_colour,ss_auto}, // color used for the single player arrow + {"mapcolor_me", {&mapcolor_me}, {112},0,255, // green + def_colour,ss_auto}, // your (player) colour + {"mapcolor_enemy", {&mapcolor_enemy}, {177},0,255, + def_colour,ss_auto}, + {"mapcolor_frnd", {&mapcolor_frnd}, {112},0,255, + def_colour,ss_auto}, + //jff 3/9/98 add option to not show secrets til after found + {"map_secret_after", {&map_secret_after}, {0},0,1, // show secret after gotten + def_bool,ss_auto}, // prevents showing secret sectors till after entered + {"map_point_coord", {&map_point_coordinates}, {0},0,1, + def_bool,ss_auto}, + //jff 1/7/98 end additions for automap + {"automapmode", {(int*)&automapmode}, {0}, 0, 31, // CPhipps - remember automap mode + def_hex,ss_none}, // automap mode + + {"Heads-up display settings",{NULL},{0},UL,UL,def_none,ss_none}, + //jff 2/16/98 defaults for color ranges in hud and status + {"hudcolor_titl", {&hudcolor_titl}, {5},0,9, // gold range + def_int,ss_auto}, // color range used for automap level title + {"hudcolor_xyco", {&hudcolor_xyco}, {3},0,9, // green range + def_int,ss_auto}, // color range used for automap coordinates + {"hudcolor_mesg", {&hudcolor_mesg}, {6},0,9, // red range + def_int,ss_mess}, // color range used for messages during play + {"hudcolor_chat", {&hudcolor_chat}, {5},0,9, // gold range + def_int,ss_mess}, // color range used for chat messages and entry + {"hudcolor_list", {&hudcolor_list}, {5},0,9, // gold range //jff 2/26/98 + def_int,ss_mess}, // color range used for message review + {"hud_msg_lines", {&hud_msg_lines}, {1},1,16, // 1 line scrolling window + def_int,ss_mess}, // number of messages in review display (1=disable) + {"hud_list_bgon", {&hud_list_bgon}, {0},0,1, // solid window bg ena //jff 2/26/98 + def_bool,ss_mess}, // enables background window behind message review + {"hud_distributed",{&hud_distributed},{0},0,1, // hud broken up into 3 displays //jff 3/4/98 + def_bool,ss_none}, // splits HUD into three 2 line displays + + {"health_red", {&health_red} , {25},0,200, // below is red + def_int,ss_stat}, // amount of health for red to yellow transition + {"health_yellow", {&health_yellow}, {50},0,200, // below is yellow + def_int,ss_stat}, // amount of health for yellow to green transition + {"health_green", {&health_green} , {100},0,200,// below is green, above blue + def_int,ss_stat}, // amount of health for green to blue transition + {"armor_red", {&armor_red} , {25},0,200, // below is red + def_int,ss_stat}, // amount of armor for red to yellow transition + {"armor_yellow", {&armor_yellow} , {50},0,200, // below is yellow + def_int,ss_stat}, // amount of armor for yellow to green transition + {"armor_green", {&armor_green} , {100},0,200,// below is green, above blue + def_int,ss_stat}, // amount of armor for green to blue transition + {"ammo_red", {&ammo_red} , {25},0,100, // below 25% is red + def_int,ss_stat}, // percent of ammo for red to yellow transition + {"ammo_yellow", {&ammo_yellow} , {50},0,100, // below 50% is yellow, above green + def_int,ss_stat}, // percent of ammo for yellow to green transition + + //jff 2/16/98 HUD and status feature controls + {"hud_active", {&hud_active}, {2},0,2, // 0=off, 1=small, 2=full + def_int,ss_none}, // 0 for HUD off, 1 for HUD small, 2 for full HUD + //jff 2/23/98 + {"hud_displayed", {&hud_displayed}, {0},0,1, // whether hud is displayed + def_bool,ss_none}, // enables display of HUD + {"hud_nosecrets", {&hud_nosecrets}, {0},0,1, // no secrets/items/kills HUD line + def_bool,ss_stat}, // disables display of kills/items/secrets on HUD + + {"Weapon preferences",{NULL},{0},UL,UL,def_none,ss_none}, + // killough 2/8/98: weapon preferences set by user: + {"weapon_choice_1", {&weapon_preferences[0][0]}, {6}, 0,9, + def_int,ss_weap}, // first choice for weapon (best) + {"weapon_choice_2", {&weapon_preferences[0][1]}, {9}, 0,9, + def_int,ss_weap}, // second choice for weapon + {"weapon_choice_3", {&weapon_preferences[0][2]}, {4}, 0,9, + def_int,ss_weap}, // third choice for weapon + {"weapon_choice_4", {&weapon_preferences[0][3]}, {3}, 0,9, + def_int,ss_weap}, // fourth choice for weapon + {"weapon_choice_5", {&weapon_preferences[0][4]}, {2}, 0,9, + def_int,ss_weap}, // fifth choice for weapon + {"weapon_choice_6", {&weapon_preferences[0][5]}, {8}, 0,9, + def_int,ss_weap}, // sixth choice for weapon + {"weapon_choice_7", {&weapon_preferences[0][6]}, {5}, 0,9, + def_int,ss_weap}, // seventh choice for weapon + {"weapon_choice_8", {&weapon_preferences[0][7]}, {7}, 0,9, + def_int,ss_weap}, // eighth choice for weapon + {"weapon_choice_9", {&weapon_preferences[0][8]}, {1}, 0,9, + def_int,ss_weap}, // ninth choice for weapon (worst) + + // cournia - support for arbitrary music file (defaults are mp3) + {"Music", {NULL},{0},UL,UL,def_none,ss_none}, + {"mus_e1m1", {0,&S_music_files[mus_e1m1]}, {0,"e1m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m2", {0,&S_music_files[mus_e1m2]}, {0,"e1m2.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m3", {0,&S_music_files[mus_e1m3]}, {0,"e1m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m4", {0,&S_music_files[mus_e1m4]}, {0,"e1m4.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m5", {0,&S_music_files[mus_e1m5]}, {0,"e1m5.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m6", {0,&S_music_files[mus_e1m6]}, {0,"e1m6.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m7", {0,&S_music_files[mus_e1m7]}, {0,"e1m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m8", {0,&S_music_files[mus_e1m8]}, {0,"e1m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e1m9", {0,&S_music_files[mus_e1m9]}, {0,"e1m9.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m1", {0,&S_music_files[mus_e2m1]}, {0,"e2m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m2", {0,&S_music_files[mus_e2m2]}, {0,"e2m2.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m3", {0,&S_music_files[mus_e2m3]}, {0,"e2m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m4", {0,&S_music_files[mus_e2m4]}, {0,"e2m4.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m5", {0,&S_music_files[mus_e2m5]}, {0,"e1m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m6", {0,&S_music_files[mus_e2m6]}, {0,"e2m6.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m7", {0,&S_music_files[mus_e2m7]}, {0,"e2m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m8", {0,&S_music_files[mus_e2m8]}, {0,"e2m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e2m9", {0,&S_music_files[mus_e2m9]}, {0,"e3m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m1", {0,&S_music_files[mus_e3m1]}, {0,"e3m1.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m2", {0,&S_music_files[mus_e3m2]}, {0,"e3m2.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m3", {0,&S_music_files[mus_e3m3]}, {0,"e3m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m4", {0,&S_music_files[mus_e3m4]}, {0,"e1m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m5", {0,&S_music_files[mus_e3m5]}, {0,"e1m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m6", {0,&S_music_files[mus_e3m6]}, {0,"e1m6.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m7", {0,&S_music_files[mus_e3m7]}, {0,"e2m7.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m8", {0,&S_music_files[mus_e3m8]}, {0,"e3m8.mp3"},UL,UL, + def_str,ss_none}, + {"mus_e3m9", {0,&S_music_files[mus_e3m9]}, {0,"e1m9.mp3"},UL,UL, + def_str,ss_none}, + {"mus_inter", {0,&S_music_files[mus_inter]}, {0,"e2m3.mp3"},UL,UL, + def_str,ss_none}, + {"mus_intro", {0,&S_music_files[mus_intro]}, {0,"intro.mp3"},UL,UL, + def_str,ss_none}, + {"mus_bunny", {0,&S_music_files[mus_bunny]}, {0,"bunny.mp3"},UL,UL, + def_str,ss_none}, + {"mus_victor", {0,&S_music_files[mus_victor]}, {0,"victor.mp3"},UL,UL, + def_str,ss_none}, + {"mus_introa", {0,&S_music_files[mus_introa]}, {0,"intro.mp3"},UL,UL, + def_str,ss_none}, + {"mus_runnin", {0,&S_music_files[mus_runnin]}, {0,"runnin.mp3"},UL,UL, + def_str,ss_none}, + {"mus_stalks", {0,&S_music_files[mus_stalks]}, {0,"stalks.mp3"},UL,UL, + def_str,ss_none}, + {"mus_countd", {0,&S_music_files[mus_countd]}, {0,"countd.mp3"},UL,UL, + def_str,ss_none}, + {"mus_betwee", {0,&S_music_files[mus_betwee]}, {0,"betwee.mp3"},UL,UL, + def_str,ss_none}, + {"mus_doom", {0,&S_music_files[mus_doom]}, {0,"doom.mp3"},UL,UL, + def_str,ss_none}, + {"mus_the_da", {0,&S_music_files[mus_the_da]}, {0,"the_da.mp3"},UL,UL, + def_str,ss_none}, + {"mus_shawn", {0,&S_music_files[mus_shawn]}, {0,"shawn.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ddtblu", {0,&S_music_files[mus_ddtblu]}, {0,"ddtblu.mp3"},UL,UL, + def_str,ss_none}, + {"mus_in_cit", {0,&S_music_files[mus_in_cit]}, {0,"in_cit.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dead", {0,&S_music_files[mus_dead]}, {0,"dead.mp3"},UL,UL, + def_str,ss_none}, + {"mus_stlks2", {0,&S_music_files[mus_stlks2]}, {0,"stalks.mp3"},UL,UL, + def_str,ss_none}, + {"mus_theda2", {0,&S_music_files[mus_theda2]}, {0,"the_da.mp3"},UL,UL, + def_str,ss_none}, + {"mus_doom2", {0,&S_music_files[mus_doom2]}, {0,"doom.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ddtbl2", {0,&S_music_files[mus_ddtbl2]}, {0,"ddtblu.mp3"},UL,UL, + def_str,ss_none}, + {"mus_runni2", {0,&S_music_files[mus_runni2]}, {0,"runnin.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dead2", {0,&S_music_files[mus_dead2]}, {0,"dead.mp3"},UL,UL, + def_str,ss_none}, + {"mus_stlks3", {0,&S_music_files[mus_stlks3]}, {0,"stalks.mp3"},UL,UL, + def_str,ss_none}, + {"mus_romero", {0,&S_music_files[mus_romero]}, {0,"romero.mp3"},UL,UL, + def_str,ss_none}, + {"mus_shawn2", {0,&S_music_files[mus_shawn2]}, {0,"shawn.mp3"},UL,UL, + def_str,ss_none}, + {"mus_messag", {0,&S_music_files[mus_messag]}, {0,"messag.mp3"},UL,UL, + def_str,ss_none}, + {"mus_count2", {0,&S_music_files[mus_count2]}, {0,"countd.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ddtbl3", {0,&S_music_files[mus_ddtbl3]}, {0,"ddtblu.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ampie", {0,&S_music_files[mus_ampie]}, {0,"ampie.mp3"},UL,UL, + def_str,ss_none}, + {"mus_theda3", {0,&S_music_files[mus_theda3]}, {0,"the_da.mp3"},UL,UL, + def_str,ss_none}, + {"mus_adrian", {0,&S_music_files[mus_adrian]}, {0,"adrian.mp3"},UL,UL, + def_str,ss_none}, + {"mus_messg2", {0,&S_music_files[mus_messg2]}, {0,"messag.mp3"},UL,UL, + def_str,ss_none}, + {"mus_romer2", {0,&S_music_files[mus_romer2]}, {0,"romero.mp3"},UL,UL, + def_str,ss_none}, + {"mus_tense", {0,&S_music_files[mus_tense]}, {0,"tense.mp3"},UL,UL, + def_str,ss_none}, + {"mus_shawn3", {0,&S_music_files[mus_shawn3]}, {0,"shawn.mp3"},UL,UL, + def_str,ss_none}, + {"mus_openin", {0,&S_music_files[mus_openin]}, {0,"openin.mp3"},UL,UL, + def_str,ss_none}, + {"mus_evil", {0,&S_music_files[mus_evil]}, {0,"evil.mp3"},UL,UL, + def_str,ss_none}, + {"mus_ultima", {0,&S_music_files[mus_ultima]}, {0,"ultima.mp3"},UL,UL, + def_str,ss_none}, + {"mus_read_m", {0,&S_music_files[mus_read_m]}, {0,"read_m.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dm2ttl", {0,&S_music_files[mus_dm2ttl]}, {0,"dm2ttl.mp3"},UL,UL, + def_str,ss_none}, + {"mus_dm2int", {0,&S_music_files[mus_dm2int]}, {0,"dm2int.mp3"},UL,UL, + def_str,ss_none}, +}; + +int numdefaults; +static char *defaultfile; + +// +// M_SaveDefaults +// + +void M_SaveDefaults (void) +{ + int i; + FILE* f; + + f = fopen (defaultfile, "w"); + if (!f) + return; // can't write the file, but don't complain + + // 3/3/98 explain format of file + + fprintf(f,"# Doom config file\n"); + fprintf(f,"# Format:\n"); + fprintf(f,"# variable value\n"); + + for (i = 0 ; i < numdefaults ; i++) { + if (defaults[i].type == def_none) { + // CPhipps - pure headers + fprintf(f, "\n# %s\n", defaults[i].name); + } else + // CPhipps - modified for new default_t form + if (!IS_STRING(defaults[i])) //jff 4/10/98 kill super-hack on pointer value + { + // CPhipps - remove keycode hack + // killough 3/6/98: use spaces instead of tabs for uniform justification + if (defaults[i].type == def_hex) + fprintf (f,"%-25s 0x%x\n",defaults[i].name,*(defaults[i].location.pi)); + else + fprintf (f,"%-25s %5i\n",defaults[i].name,*(defaults[i].location.pi)); + } + else + { + fprintf (f,"%-25s \"%s\"\n",defaults[i].name,*(defaults[i].location.ppsz)); + } + } + + fclose (f); +} + +/* + * M_LookupDefault + * + * cph - mimic MBF function for now. Yes it's crap. + */ + +struct default_s *M_LookupDefault(const char *name) +{ + int i; + for (i = 0 ; i < numdefaults ; i++) + if ((defaults[i].type != def_none) && !strcmp(name, defaults[i].name)) + return &defaults[i]; + I_Error("M_LookupDefault: %s not found",name); + return NULL; +} + +// +// M_LoadDefaults +// + +#define NUMCHATSTRINGS 10 // phares 4/13/98 + +void M_LoadDefaults (void) +{ + int i; + int len; + FILE* f; + char def[80]; + char strparm[100]; + char* newstring = NULL; // killough + int parm; + boolean isstring; + + // set everything to base values + + numdefaults = sizeof(defaults)/sizeof(defaults[0]); + for (i = 0 ; i < numdefaults ; i++) { + if (defaults[i].location.ppsz) + *defaults[i].location.ppsz = strdup(defaults[i].defaultvalue.psz); + if (defaults[i].location.pi) + *defaults[i].location.pi = defaults[i].defaultvalue.i; + } + + // check for a custom default file + + i = M_CheckParm ("-config"); + if (i && i < myargc-1) + defaultfile = strdup(myargv[i+1]); + else { + const char* exedir = I_DoomExeDir(); + defaultfile = malloc(PATH_MAX+1); + /* get config file from same directory as executable */ +#ifdef HAVE_SNPRINTF + snprintf(defaultfile, PATH_MAX, +#else + sprintf (defaultfile, +#endif + "%s%s%sboom.cfg", exedir, HasTrailingSlash(exedir) ? "" : "/", + "pr"); + } + + lprintf (LO_CONFIRM, " default file: %s\n",defaultfile); + + // read the file in, overriding any set defaults + + f = fopen (defaultfile, "r"); + if (f) + { + while (!feof(f)) + { + isstring = false; + if (fscanf (f, "%79s %[^\n]\n", def, strparm) == 2) + { + + //jff 3/3/98 skip lines not starting with an alphanum + + if (!isalnum(def[0])) + continue; + + if (strparm[0] == '"') { + // get a string default + + isstring = true; + len = strlen(strparm); + newstring = (char *) malloc(len); + strparm[len-1] = 0; // clears trailing double-quote mark + strcpy(newstring, strparm+1); // clears leading double-quote mark + } else if ((strparm[0] == '0') && (strparm[1] == 'x')) { + // CPhipps - allow ints to be specified in hex + sscanf(strparm+2, "%x", &parm); + } else { + sscanf(strparm, "%i", &parm); + // Keycode hack removed + } + + for (i = 0 ; i < numdefaults ; i++) + if ((defaults[i].type != def_none) && !strcmp(def, defaults[i].name)) + { + // CPhipps - safety check + if (isstring != IS_STRING(defaults[i])) { + lprintf(LO_WARN, "M_LoadDefaults: Type mismatch reading %s\n", defaults[i].name); + continue; + } + if (!isstring) + { + + //jff 3/4/98 range check numeric parameters + + if ((defaults[i].minvalue==UL || defaults[i].minvalue<=parm) && + (defaults[i].maxvalue==UL || defaults[i].maxvalue>=parm)) + *(defaults[i].location.pi) = parm; + } + else + { + union { const char **c; char **s; } u; // type punning via unions + + u.c = defaults[i].location.ppsz; + free(*(u.s)); + *(u.s) = newstring; + } + break; + } + } + } + + fclose (f); + } + //jff 3/4/98 redundant range checks for hud deleted here +} + diff --git a/src/m_misc.h b/src/m_misc.h new file mode 100644 index 00000000..4478212f --- /dev/null +++ b/src/m_misc.h @@ -0,0 +1,109 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * External non-system-specific stuff, like storing config settings, + * simple file handling, and saving screnshots. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __M_MISC__ +#define __M_MISC__ + + +#include "doomtype.h" +// +// MISC +// + +boolean M_WriteFile (char const* name,void* source,int length); + +int M_ReadFile (char const* name,byte** buffer); + +void M_LoadDefaults (void); + +void M_SaveDefaults (void); + +struct default_s *M_LookupDefault(const char *name); /* killough 11/98 */ + +// phares 4/21/98: +// Moved from m_misc.c so m_menu.c could see it. + +// CPhipps - struct to hold a value in a config file +// Cannot be a union, as it must be initialised +typedef struct default_s +{ + const char* name; + /* cph - + * The location struct holds the pointer to the variable holding the + * setting. For int's we do nothing special. + * For strings, the string is actually stored on our heap with Z_Strdup() + * BUT we don't want the rest of the program to be able to modify them, + * so we declare it const. It's not really const though, and m_misc.c and + * m_menu.c cast it back when they need to change it. Possibly this is + * more trouble than it's worth. + */ + // Note: casts are now made via unions to avoid discarding qualifier warnings + struct { + int* pi; + const char** ppsz; + } location; + struct { + int i; + const char* psz; + } defaultvalue; // CPhipps - default value + // Limits (for an int) + int minvalue; // jff 3/3/98 minimum allowed value + int maxvalue; // jff 3/3/98 maximum allowed value + enum { + def_none, // Dummy entry + def_str, // A string + def_int, // Integer + def_hex, // Integer (write in hex) + def_bool = def_int, // Boolean + def_key = def_hex, // Key code (byte) + def_mouseb = def_int,// Mouse button + def_colour = def_hex // Colour (256 colour palette entry) + } type; // CPhipps - type of entry + int setupscreen; // phares 4/19/98: setup screen where this appears + int *current; /* cph - MBF-like pointer to current value */ + // cph - removed the help strings from the config file + // const char* help; // jff 3/3/98 description of parameter + // CPhipps - remove unused "lousy hack" code + struct setup_menu_s *setup_menu; /* Xref to setup menu item, if any */ +} default_t; + +#define IS_STRING(dv) ((dv).type == def_str) +// CPhipps - What is the max. key code that X will send us? +#define MAX_KEY 65536 +#define MAX_MOUSEB 2 + +#define UL (-123456789) /* magic number for no min or max for parameter */ + +#endif diff --git a/src/m_random.c b/src/m_random.c new file mode 100644 index 00000000..a37dca7e --- /dev/null +++ b/src/m_random.c @@ -0,0 +1,140 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Random number LUT. + * + * 1/19/98 killough: Rewrote random number generator for better randomness, + * while at the same time maintaining demo sync and backward compatibility. + * + * 2/16/98 killough: Made each RNG local to each control-equivalent block, + * to reduce the chances of demo sync problems. + * + *-----------------------------------------------------------------------------*/ + + +#include "doomstat.h" +#include "m_random.h" +#include "lprintf.h" + +// +// M_Random +// Returns a 0-255 number +// +static const unsigned char rndtable[256] = { // 1/19/98 killough -- made const + 0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 , + 74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 , + 95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 , + 52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 , + 149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 , + 145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 , + 175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 , + 25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 , + 94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 , + 136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 , + 135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 , + 80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 , + 24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 , + 145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 , + 28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 , + 71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 , + 17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 , + 197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 , + 120, 163, 236, 249 +}; + +rng_t rng; // the random number state + +unsigned long rngseed = 1993; // killough 3/26/98: The seed + +int (P_Random)(pr_class_t pr_class +) +{ + // killough 2/16/98: We always update both sets of random number + // generators, to ensure repeatability if the demo_compatibility + // flag is changed while the program is running. Changing the + // demo_compatibility flag does not change the sequences generated, + // only which one is selected from. + // + // All of this RNG stuff is tricky as far as demo sync goes -- + // it's like playing with explosives :) Lee + + int compat = pr_class == pr_misc ? + (rng.prndindex = (rng.prndindex + 1) & 255) : + (rng. rndindex = (rng. rndindex + 1) & 255) ; + + unsigned long boom; + + // killough 3/31/98: + // If demo sync insurance is not requested, use + // much more unstable method by putting everything + // except pr_misc into pr_all_in_one + + if (pr_class != pr_misc && !demo_insurance) // killough 3/31/98 + pr_class = pr_all_in_one; + + boom = rng.seed[pr_class]; + + // killough 3/26/98: add pr_class*2 to addend + + rng.seed[pr_class] = boom * 1664525ul + 221297ul + pr_class*2; + + if (demo_compatibility) + return rndtable[compat]; + + boom >>= 20; + + /* killough 3/30/98: use gametic-levelstarttic to shuffle RNG + * killough 3/31/98: but only if demo insurance requested, + * since it's unnecessary for random shuffling otherwise + * killough 9/29/98: but use basetic now instead of levelstarttic + * cph - DEMOSYNC - this change makes MBF demos work, + * but does it break Boom ones? + */ + + if (demo_insurance) + boom += (gametic-basetic)*7; + + return boom & 255; +} + +// Initialize all the seeds +// +// This initialization method is critical to maintaining demo sync. +// Each seed is initialized according to its class, so if new classes +// are added they must be added to end of pr_class_t list. killough +// + +void M_ClearRandom (void) +{ + int i; + unsigned long seed = rngseed*2+1; // add 3/26/98: add rngseed + for (i=0; i +#ifdef __arch_swab16 +#define doom_swap_s (signed short)__arch_swab16 +#endif +#ifdef __arch_swab32 +#define doom_swap_l (signed long)__arch_swab32 +#endif +#endif /* HAVE_ASM_BYTEORDER_H */ + +#ifdef HAVE_LIBKERN_OSBYTEORDER_H +#include + +#define doom_swap_s (short)OSSwapInt16 +#define doom_swap_l (long)OSSwapInt32 +#endif + +#ifndef doom_swap_l +#define doom_swap_l(x) \ + ((long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \ + (((unsigned long int)(x) & 0x0000ff00U) << 8) | \ + (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \ + (((unsigned long int)(x) & 0xff000000U) >> 24))) +#endif + +#ifndef doom_swap_s +#define doom_swap_s(x) \ + ((short int)((((unsigned short int)(x) & 0x00ff) << 8) | \ + (((unsigned short int)(x) & 0xff00) >> 8))) +#endif + +/* Macros are named doom_XtoYT, where + * X is thing to convert from, Y is thing to convert to, chosen from + * n for network, h for host (i.e our machine's), w for WAD (Doom data files) + * and T is the type, l or s for long or short + * + * CPhipps - all WADs and network packets will be little endian for now + * Use separate macros so network could be converted to big-endian later. + */ + +#ifdef WORDS_BIGENDIAN + +#define doom_wtohl(x) doom_swap_l(x) +#define doom_htowl(x) doom_swap_l(x) +#define doom_wtohs(x) doom_swap_s(x) +#define doom_htows(x) doom_swap_s(x) + +#define doom_ntohl(x) doom_swap_l(x) +#define doom_htonl(x) doom_swap_l(x) +#define doom_ntohs(x) doom_swap_s(x) +#define doom_htons(x) doom_swap_s(x) + +#else + +#define doom_wtohl(x) (long int)(x) +#define doom_htowl(x) (long int)(x) +#define doom_wtohs(x) (short int)(x) +#define doom_htows(x) (short int)(x) + +#define doom_ntohl(x) (long int)(x) +#define doom_htonl(x) (long int)(x) +#define doom_ntohs(x) (short int)(x) +#define doom_htons(x) (short int)(x) + +#endif + +/* CPhipps - Boom's old LONG and SHORT endianness macros are for WAD stuff */ + +#define LONG(x) doom_wtohl(x) +#define SHORT(x) doom_htows(x) + +#endif diff --git a/src/md5.c b/src/md5.c new file mode 100644 index 00000000..d69ec0cc --- /dev/null +++ b/src/md5.c @@ -0,0 +1,240 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson . + * Still in the public domain. + */ +#include "config.h" + +#include /* for memcpy() */ +#include /* for stupid systems */ + +#include "md5.h" + +#ifdef WORDS_BIGENDIAN +void +byteSwap(UWORD32 *buf, unsigned words) +{ + md5byte *p = (md5byte *)buf; + + do { + *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} +#else +#define byteSwap(buf,words) +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) +{ + UWORD32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((md5byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((md5byte *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(md5byte digest[16], struct MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + md5byte *p = (md5byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (md5byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) +{ + register UWORD32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 00000000..3ebeb369 --- /dev/null +++ b/src/md5.h @@ -0,0 +1,47 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson . + * Still in the public domain. + */ + +#ifndef MD5_H +#define MD5_H + +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN +#include +#define UWORD32 DWORD +#else +#include +#define UWORD32 uint32_t +#endif +#define md5byte unsigned char + +struct MD5Context { + UWORD32 buf[4]; + UWORD32 bytes[2]; + UWORD32 in[16]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]); + +#endif /* !MD5_H */ diff --git a/src/mmus2mid.c b/src/mmus2mid.c new file mode 100644 index 00000000..2764fddd --- /dev/null +++ b/src/mmus2mid.c @@ -0,0 +1,735 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * This file supports conversion of MUS format music in memory + * to MIDI format 1 music in memory. + * + * The primary routine, mmus2mid, converts a block of memory in MUS format + * to an Allegro MIDI structure. This supports playing MUS lumps in a wad + * file with BOOM. + * + * Another routine, Midi2MIDI, converts a block of memory in MIDI format 1 to + * an Allegro MIDI structure. This supports playing MIDI lumps in a wad + * file with BOOM. + * + * + * Much of the code here is thanks to S. Bacquet's source for QMUS2MID.C + * + *----------------------------------------------------------------------------- + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include "mmus2mid.h" +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "m_swap.h" +#include "z_zone.h" + +// some macros to decode mus event bit fields + +#define last(e) ((UBYTE)((e) & 0x80)) +#define event_type(e) ((UBYTE)(((e) & 0x7F) >> 4)) +#define channel(e) ((UBYTE)((e) & 0x0F)) + +// event types + +typedef enum +{ + RELEASE_NOTE, + PLAY_NOTE, + BEND_NOTE, + SYS_EVENT, + CNTL_CHANGE, + UNKNOWN_EVENT1, + SCORE_END, + UNKNOWN_EVENT2, +} mus_event_t; + +// MUS format header structure + +typedef struct +{ + char ID[4]; // identifier "MUS"0x1A + UWORD ScoreLength; // length of music portion + UWORD ScoreStart; // offset of music portion + UWORD channels; // count of primary channels + UWORD SecChannels; // count of secondary channels + UWORD InstrCnt; // number of instruments +} PACKEDATTR MUSheader; + +// to keep track of information in a MIDI track + +typedef struct Track +{ + char velocity; + long deltaT; + UBYTE lastEvt; + long alloced; +} TrackInfo; + +// array of info about tracks + +static TrackInfo track[MIDI_TRACKS]; + +// initial track size allocation +#define TRACKBUFFERSIZE 1024 + +// lookup table MUS -> MID controls +static UBYTE MUS2MIDcontrol[15] = +{ + 0, // Program change - not a MIDI control change + 0x00, // Bank select + 0x01, // Modulation pot + 0x07, // Volume + 0x0A, // Pan pot + 0x0B, // Expression pot + 0x5B, // Reverb depth + 0x5D, // Chorus depth + 0x40, // Sustain pedal + 0x43, // Soft pedal + 0x78, // All sounds off + 0x7B, // All notes off + 0x7E, // Mono + 0x7F, // Poly + 0x79 // Reset all controllers +}; + +// some strings of bytes used in the midi format + +static UBYTE midikey[] = +{0x00,0xff,0x59,0x02,0x00,0x00}; // C major +static UBYTE miditempo[] = +{0x00,0xff,0x51,0x03,0x09,0xa3,0x1a}; // uS/qnote +static UBYTE midihdr[] = +{'M','T','h','d',0,0,0,6,0,1,0,0,0,0}; // header (length 6, format 1) +static UBYTE trackhdr[] = +{'M','T','r','k'}; // track header + +// static routine prototypes + +static int TWriteByte(MIDI *mididata, int MIDItrack, UBYTE byte); +static int TWriteVarLen(MIDI *mididata, int MIDItrack, register ULONG value); +static ULONG ReadTime(const UBYTE **musptrp); +static int FirstChannelAvailable(int MUS2MIDchannel[]); +static UBYTE MidiEvent(MIDI *mididata,UBYTE midicode,UBYTE MIDIchannel, + UBYTE MIDItrack,int nocomp); + +// +// TWriteByte() +// +// write one byte to the selected MIDItrack, update current position +// if track allocation exceeded, double it +// if track not allocated, initially allocate TRACKBUFFERSIZE bytes +// +// Passed pointer to Allegro MIDI structure, number of the MIDI track being +// written, and the byte to write. +// +// Returns 0 on success, MEMALLOC if a memory allocation error occurs +// +static int TWriteByte(MIDI *mididata, int MIDItrack, UBYTE byte) +{ + ULONG pos ; + + pos = mididata->track[MIDItrack].len; + if (pos >= (ULONG)track[MIDItrack].alloced) + { + track[MIDItrack].alloced = // double allocation + track[MIDItrack].alloced? // or set initial TRACKBUFFERSIZE + 2*track[MIDItrack].alloced : + TRACKBUFFERSIZE; + + if (!(mididata->track[MIDItrack].data = // attempt to reallocate + realloc(mididata->track[MIDItrack].data, + track[MIDItrack].alloced))) + return MEMALLOC; + } + mididata->track[MIDItrack].data[pos] = byte; + mididata->track[MIDItrack].len++; + return 0; +} + +// +// TWriteVarLen() +// +// write the ULONG value to tracknum-th track, in midi format, which is +// big endian, 7 bits per byte, with all bytes but the last flagged by +// bit 8 being set, allowing the length to vary. +// +// Passed the Allegro MIDI structure, the track number to write, +// and the ULONG value to encode in midi format there +// +// Returns 0 if sucessful, MEMALLOC if a memory allocation error occurs +// +static int TWriteVarLen(MIDI *mididata, int tracknum, register ULONG value) +{ + register ULONG buffer; + + buffer = value & 0x7f; + while ((value >>= 7)) // terminates because value unsigned + { + buffer <<= 8; // note first value shifted in has bit 8 clear + buffer |= 0x80; // all succeeding values do not + buffer += (value & 0x7f); + } + while (1) // write bytes out in opposite order + { + if (TWriteByte(mididata, tracknum, (UBYTE)(buffer&0xff))) // insure buffer masked + return MEMALLOC; + + if (buffer & 0x80) + buffer >>= 8; + else // terminate on the byte with bit 8 clear + break; + } + return 0; +} + +// +// ReadTime() +// +// Read a time value from the MUS buffer, advancing the position in it +// +// A time value is a variable length sequence of 8 bit bytes, with all +// but the last having bit 8 set. +// +// Passed a pointer to the pointer to the MUS buffer +// Returns the integer unsigned long time value there and advances the pointer +// +static ULONG ReadTime(const UBYTE **musptrp) +{ + register ULONG timeval = 0; + int byte; + + do // shift each byte read up in the result until a byte with bit 8 clear + { + byte = *(*musptrp)++; + timeval = (timeval << 7) + (byte & 0x7F); + } + while(byte & 0x80); + + return timeval; +} + +// +// FirstChannelAvailable() +// +// Return the next unassigned MIDI channel number +// +// The assignment for MUS channel 15 is not counted in the caculation, that +// being percussion and always assigned to MIDI channel 9 (base 0). +// +// Passed the array of MIDI channels assigned to MUS channels +// Returns the maximum channel number unassigned unless that is 9 in which +// case 10 is returned. +// +// killough 10/7/98: changed char parameter, return values to int + +static int FirstChannelAvailable(int MUS2MIDchannel[]) +{ + int i ; + int max = -1 ; + + // find the largest MIDI channel assigned so far + for (i = 0; i < 15; i++) + if (MUS2MIDchannel[i] > max) + max = MUS2MIDchannel[i]; + + return (max == 8 ? 10 : max+1); // skip MIDI channel 9 (percussion) +} + +// +// MidiEvent() +// +// Constructs a MIDI event code, and writes it to the current MIDI track +// unless its the same as the last event code and compressio is enabled +// in which case nothing is written. +// +// Passed the Allegro MIDI structure, the midi event code, the current +// MIDI channel number, the current MIDI track number, and whether compression +// (running status) is enabled. +// +// Returns the new event code if successful, 0 if a memory allocation error +// +static UBYTE MidiEvent(MIDI *mididata,UBYTE midicode,UBYTE MIDIchannel, + UBYTE MIDItrack,int nocomp) +{ + UBYTE newevent; + + newevent = midicode | MIDIchannel; + if ((newevent != track[MIDItrack].lastEvt) || nocomp) + { + if (TWriteByte(mididata,MIDItrack, newevent)) + return 0; // indicates MEMALLOC error + track[MIDItrack].lastEvt = newevent; + } + return newevent; +} + +// +// mmus2mid() +// +// Convert a memory buffer contain MUS data to an Allegro MIDI structure +// with specified time division and compression. +// +// Passed a pointer to the buffer containing MUS data, a pointer to the +// Allegro MIDI structure, the divisions, and a flag whether to compress. +// +// Returns 0 if successful, otherwise an error code (see mmus2mid.h). +// +int mmus2mid(const UBYTE *mus, MIDI *mididata, UWORD division, int nocomp) +{ + UWORD TrackCnt = 0; + UBYTE evt, MUSchannel, MIDIchannel, MIDItrack=0, NewEvent; + int i, event, data; + const UBYTE *musptr; + size_t muslen; + static MUSheader MUSh; + UBYTE MIDIchan2track[MIDI_TRACKS]; // killough 10/7/98: fix too small array + int MUS2MIDchannel[MIDI_TRACKS]; // killough 10/7/98: fix too small array + + // copy the MUS header from the MUS buffer to the MUSh header structure + + memcpy(&MUSh,mus,sizeof(MUSheader)); + MUSh.ScoreLength = doom_wtohs(MUSh.ScoreLength); + MUSh.ScoreStart = doom_wtohs(MUSh.ScoreStart); + MUSh.channels = doom_wtohs(MUSh.channels); + MUSh.SecChannels = doom_wtohs(MUSh.SecChannels); + MUSh.InstrCnt = doom_wtohs(MUSh.InstrCnt); + + // check some things and set length of MUS buffer from internal data + + if (!(muslen = MUSh.ScoreLength + MUSh.ScoreStart)) + return MUSDATAMT; // MUS file empty + + if (MUSh.channels > 15) // MUSchannels + drum channel > 16 + return TOOMCHAN ; + + musptr = mus+MUSh.ScoreStart; // init musptr to start of score + + for (i = 0; i < MIDI_TRACKS; i++) // init the track structure's tracks + { + MUS2MIDchannel[i] = -1; // flag for channel not used yet + track[i].velocity = 64; + track[i].deltaT = 0; + track[i].lastEvt = 0; + //free(mididata->track[i].data);//jff 3/5/98 remove old allocations + mididata->track[i].data=NULL; + track[i].alloced = 0; + mididata->track[i].len = 0; + } + + if (!division) + division = 70; + + // allocate the first track which is a special tempo/key track + // note multiple tracks means midi format 1 + + // set the divisions (ticks per quarter note) + mididata->divisions = division; + + // allocate for midi tempo/key track, allow for end of track + if (!(mididata->track[0].data = + realloc(mididata->track[0].data,sizeof(midikey)+sizeof(miditempo)+4))) + return MEMALLOC; + + // key C major + memcpy(mididata->track[0].data,midikey,sizeof(midikey)); + // tempo uS/qnote + memcpy(mididata->track[0].data+sizeof(midikey),miditempo,sizeof(miditempo)); + mididata->track[0].len = sizeof(midikey)+sizeof(miditempo); + + TrackCnt++; // music tracks start at 1 + + // process the MUS events in the MUS buffer + + do + { + // get a mus event, decode its type and channel fields + + event = *musptr++; + if ((evt = event_type(event)) == SCORE_END) //jff 1/23/98 use symbol + break; // if end of score event, leave + MUSchannel = channel(event); + + // if this channel not initialized, do so + + if (MUS2MIDchannel[MUSchannel] == -1) + { + // set MIDIchannel and MIDItrack + + MIDIchannel = MUS2MIDchannel[MUSchannel] = + (MUSchannel == 15 ? 9 : FirstChannelAvailable(MUS2MIDchannel)); + MIDItrack = MIDIchan2track[MIDIchannel] = (UBYTE)TrackCnt++; + } + else // channel already allocated as a track, use those values + { + MIDIchannel = MUS2MIDchannel[MUSchannel]; + MIDItrack = MIDIchan2track[MIDIchannel]; + } + + if (TWriteVarLen(mididata, MIDItrack, track[MIDItrack].deltaT)) + return MEMALLOC; + track[MIDItrack].deltaT = 0; + + switch(evt) + { + case RELEASE_NOTE: + // killough 10/7/98: Fix noise problems by not allowing compression + if (!(NewEvent=MidiEvent(mididata,0x90,MIDIchannel,MIDItrack,1))) + return MEMALLOC; + + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) + return MEMALLOC; + if (TWriteByte(mididata, MIDItrack, 0)) + return MEMALLOC; + break; + + case PLAY_NOTE: + if (!(NewEvent=MidiEvent(mididata,0x90,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) + return MEMALLOC; + if( data & 0x80 ) + track[MIDItrack].velocity = (*musptr++) & 0x7f; + if (TWriteByte(mididata, MIDItrack, track[MIDItrack].velocity)) + return MEMALLOC; + break; + + case BEND_NOTE: + if (!(NewEvent=MidiEvent(mididata,0xE0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)((data & 1) << 6))) + return MEMALLOC; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data >> 1))) + return MEMALLOC; + break; + + case SYS_EVENT: + if (!(NewEvent=MidiEvent(mididata,0xB0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + data = *musptr++; + if (data<10 || data>14) + return BADSYSEVT; + + if (TWriteByte(mididata, MIDItrack, MUS2MIDcontrol[data])) + return MEMALLOC; + if (data == 12) + { + if (TWriteByte(mididata, MIDItrack, (UBYTE)(MUSh.channels+1))) + return MEMALLOC; + } + else + if (TWriteByte(mididata, MIDItrack, 0)) + return MEMALLOC; + break; + + case CNTL_CHANGE: + data = *musptr++; + if (data>9) + return BADCTLCHG; + + if (data) + { + if (!(NewEvent=MidiEvent(mididata,0xB0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + + if (TWriteByte(mididata, MIDItrack, MUS2MIDcontrol[data])) + return MEMALLOC; + } + else + { + if (!(NewEvent=MidiEvent(mididata,0xC0,MIDIchannel,MIDItrack,nocomp))) + return MEMALLOC; + } + data = *musptr++; + if (TWriteByte(mididata, MIDItrack, (UBYTE)(data & 0x7F))) + return MEMALLOC; + break; + + case UNKNOWN_EVENT1: // mus events 5 and 7 + case UNKNOWN_EVENT2: // meaning not known + return BADMUSCTL; + + case SCORE_END: + break; + + default: + return BADMUSCTL; // exit with error + } + if (last(event)) + { + ULONG DeltaTime = ReadTime(&musptr); // killough 10/7/98: make local + for (i = 0;i < MIDI_TRACKS; i++) //jff 3/13/98 update all tracks + track[i].deltaT += DeltaTime; //whether allocated yet or not + } + + } + while ((evt != SCORE_END) && ((size_t)(musptr-mus) < muslen)); + + if (evt!=SCORE_END) + return MUSDATACOR; + + // Now add an end of track to each mididata track, correct allocation + + for (i = 0; i < MIDI_TRACKS; i++) + if (mididata->track[i].len) + { // killough 10/7/98: simplify code + if (TWriteByte(mididata, i, 0x00) || // midi end of track code + TWriteByte(mididata, i, 0xFF) || + TWriteByte(mididata, i, 0x2F) || + TWriteByte(mididata, i, 0x00)) + return MEMALLOC; + + // jff 1/23/98 fix failure to set data NULL, len 0 for unused tracks + // shorten allocation to proper length (important for Allegro) + if (!(mididata->track[i].data = + realloc(mididata->track[i].data,mididata->track[i].len))) + return MEMALLOC; + } + else + { + free(mididata->track[i].data); + mididata->track[i].data = NULL; + } + + return 0; +} + +void free_mididata(MIDI *mid) +{ + int i; + + for (i = 0; i < MIDI_TRACKS; i++) + if (mid->track[i].data) + free(mid->track[i].data); +} + +// +// ReadLength() +// +// Reads the length of a chunk in a midi buffer, advancing the pointer +// 4 bytes, bigendian +// +// Passed a pointer to the pointer to a MIDI buffer +// Returns the chunk length at the pointer position +// +static size_t ReadLength(UBYTE **mid) +{ + UBYTE *midptr = *mid; + + size_t length = (*midptr++)<<24; + length += (*midptr++)<<16; + length += (*midptr++)<<8; + length += *midptr++; + *mid = midptr; + return length; +} + +// +// MidiToMIDI() +// +// Convert an in-memory copy of a MIDI format 0 or 1 file to +// an Allegro MIDI structure, that is valid or has been zeroed +// +// Passed a pointer to a memory buffer with MIDI format music in it and a +// pointer to an Allegro MIDI structure. +// +// Returns 0 if successful, BADMIDHDR if the buffer is not MIDI format +// +int MidiToMIDI(UBYTE *mid,MIDI *mididata) +{ + int i; + int ntracks; + + // read the midi header + + if (memcmp(mid,midihdr,4)) + return BADMIDHDR; + + mididata->divisions = (mid[12]<<8)+mid[13]; + ntracks = (mid[10]<<8)+mid[11]; + + if (ntracks>=MIDI_TRACKS) + return BADMIDHDR; + + mid += 4; + { // killough 10/7/98: fix mid from being modified twice before sequence pt. + size_t t = ReadLength(&mid); // seek past header + mid += t; + } + + // now read each track + + for (i=0;itrack[i].len = ReadLength(&mid); // get length, move mid past it + + // read a track + mididata->track[i].data = realloc(mididata->track[i].data,mididata->track[i].len); + memcpy(mididata->track[i].data,mid,mididata->track[i].len); + mid += mididata->track[i].len; + } + for (;itrack[i].len) + { + free(mididata->track[i].data); + mididata->track[i].data = NULL; + mididata->track[i].len = 0; + } + return 0; +} + +// proff: I moved this down, because I need MIDItoMidi + +static void FreeTracks(MIDI *mididata); +static void TWriteLength(UBYTE **midiptr,ULONG length); + +// +// FreeTracks() +// +// Free all track allocations in the MIDI structure +// +// Passed a pointer to an Allegro MIDI structure +// Returns nothing +// +static void FreeTracks(MIDI *mididata) +{ + int i; + + for (i=0; itrack[i].data); + mididata->track[i].data = NULL; + mididata->track[i].len = 0; + } +} + +// +// TWriteLength() +// +// Write the length of a MIDI chunk to a midi buffer. The length is four +// bytes and is written byte-reversed for bigendian. The pointer to the +// midi buffer is advanced. +// +// Passed a pointer to the pointer to a midi buffer, and the length to write +// Returns nothing +// +static void TWriteLength(UBYTE **midiptr,ULONG length) +{ +// proff: Added typecast to avoid warning + *(*midiptr)++ = (unsigned char)((length>>24)&0xff); + *(*midiptr)++ = (unsigned char)((length>>16)&0xff); + *(*midiptr)++ = (unsigned char)((length>>8)&0xff); + *(*midiptr)++ = (unsigned char)((length)&0xff); +} + +// +// MIDIToMidi() +// +// This routine converts an Allegro MIDI structure to a midi 1 format file +// in memory. It is used to support memory MUS -> MIDI conversion +// +// Passed a pointer to an Allegro MIDI structure, a pointer to a pointer to +// a buffer containing midi data, and a pointer to a length return. +// Returns 0 if successful, MEMALLOC if a memory allocation error occurs +// +int MIDIToMidi(MIDI *mididata,UBYTE **mid,int *midlen) +{ + size_t total; + int i,ntrks; + UBYTE *midiptr; + + // calculate how long the mid buffer must be, and allocate + + total = sizeof(midihdr); + for (i=0,ntrks=0;itrack[i].len) + { + total += 8 + mididata->track[i].len; // Track hdr + track length + ntrks++; + } + if ((*mid = malloc(total))==NULL) + return MEMALLOC; + + + // fill in number of tracks and bigendian divisions (ticks/qnote) + + midihdr[10] = 0; + midihdr[11] = (UBYTE)ntrks; // set number of tracks in header + midihdr[12] = (mididata->divisions>>8) & 0x7f; + midihdr[13] = (mididata->divisions) & 0xff; + + // write the midi header + + midiptr = *mid; + memcpy(midiptr,midihdr,sizeof(midihdr)); + midiptr += sizeof(midihdr); + + // write the tracks + + for (i=0;itrack[i].len) + { + memcpy(midiptr,trackhdr,sizeof(trackhdr)); // header + midiptr += sizeof(trackhdr); + TWriteLength(&midiptr,mididata->track[i].len); // track length + // data + memcpy(midiptr,mididata->track[i].data,mididata->track[i].len); + midiptr += mididata->track[i].len; + } + } + + // return length information + + *midlen = midiptr - *mid; + + return 0; +} diff --git a/src/mmus2mid.h b/src/mmus2mid.h new file mode 100644 index 00000000..55d58036 --- /dev/null +++ b/src/mmus2mid.h @@ -0,0 +1,76 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * mmus2mid.c supports conversion of MUS format music in memory + * to MIDI format 1 music in memory. + */ + +#if !defined( MMUS2MID_H ) +#define MMUS2MID_H + +// error codes + +typedef enum +{ + MUSDATACOR, // MUS data corrupt + TOOMCHAN, // Too many channels + MEMALLOC, // Memory allocation error + MUSDATAMT, // MUS file empty + BADMUSCTL, // MUS event 5 or 7 found + BADSYSEVT, // MUS system event not in 10-14 range + BADCTLCHG, // MUS control change larger than 9 + TRACKOVF, // MIDI track exceeds allocation + BADMIDHDR, // bad midi header detected +} error_code_t; + +// some names for integers of various sizes, all unsigned +typedef unsigned char UBYTE; // a one-byte int +typedef unsigned short UWORD; // a two-byte int +// proff: changed from unsigned int to unsigned long to avoid warning +typedef unsigned long ULONG; // a four-byte int (assumes int 4 bytes) + +#ifndef MSDOS /* proff: This is from allegro.h */ +#define MIDI_TRACKS 32 + +typedef struct MIDI /* a midi file */ +{ + int divisions; /* number of ticks per quarter note */ + struct { + unsigned char *data; /* MIDI message stream */ + int len; /* length of the track data */ + } track[MIDI_TRACKS]; +} MIDI; +#endif /* !MSDOS */ + +extern int mmus2mid(const UBYTE *mus,MIDI *mid, UWORD division, int nocomp); +extern void free_mididata(MIDI *mid); +extern int MIDIToMidi(MIDI *mididata,UBYTE **mid,int *midlen); +extern int MidiToMIDI(UBYTE *mid,MIDI *mididata); + +#endif diff --git a/src/p_ceilng.c b/src/p_ceilng.c new file mode 100644 index 00000000..7b45ba58 --- /dev/null +++ b/src/p_ceilng.c @@ -0,0 +1,467 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Ceiling aninmation (lowering, crushing, raising) + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" + +// the list of ceilings moving currently, including crushers +ceilinglist_t *activeceilings; + +///////////////////////////////////////////////////////////////// +// +// Ceiling action routine and linedef type handler +// +///////////////////////////////////////////////////////////////// + +// +// T_MoveCeiling +// +// Action routine that moves ceilings. Called once per tick. +// +// Passed a ceiling_t structure that contains all the info about the move. +// see P_SPEC.H for fields. No return. +// +// jff 02/08/98 all cases with labels beginning with gen added to support +// generalized line type behaviors. +// +void T_MoveCeiling (ceiling_t* ceiling) +{ + result_e res; + + switch(ceiling->direction) + { + case 0: + // If ceiling in stasis, do nothing + break; + + case 1: + // Ceiling is moving up + res = T_MovePlane + ( + ceiling->sector, + ceiling->speed, + ceiling->topheight, + false, + 1, + ceiling->direction + ); + + // if not a silent crusher, make moving sound + if (!(leveltime&7)) + { + switch(ceiling->type) + { + case silentCrushAndRaise: + case genSilentCrusher: + break; + default: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_stnmov); + break; + } + } + + // handle reaching destination height + if (res == pastdest) + { + switch(ceiling->type) + { + // plain movers are just removed + case raiseToHighest: + case genCeiling: + P_RemoveActiveCeiling(ceiling); + break; + + // movers with texture change, change the texture then get removed + case genCeilingChgT: + case genCeilingChg0: + ceiling->sector->special = ceiling->newspecial; + //jff 3/14/98 transfer old special field as well + ceiling->sector->oldspecial = ceiling->oldspecial; + case genCeilingChg: + ceiling->sector->ceilingpic = ceiling->texture; + P_RemoveActiveCeiling(ceiling); + break; + + // crushers reverse direction at the top + case silentCrushAndRaise: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_pstop); + case genSilentCrusher: + case genCrusher: + case fastCrushAndRaise: + case crushAndRaise: + ceiling->direction = -1; + break; + + default: + break; + } + } + break; + + case -1: + // Ceiling moving down + res = T_MovePlane + ( + ceiling->sector, + ceiling->speed, + ceiling->bottomheight, + ceiling->crush, + 1, + ceiling->direction + ); + + // if not silent crusher type make moving sound + if (!(leveltime&7)) + { + switch(ceiling->type) + { + case silentCrushAndRaise: + case genSilentCrusher: + break; + default: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_stnmov); + } + } + + // handle reaching destination height + if (res == pastdest) + { + switch(ceiling->type) + { + // 02/09/98 jff change slow crushers' speed back to normal + // start back up + case genSilentCrusher: + case genCrusher: + if (ceiling->oldspeedspeed = ceiling->oldspeed; + ceiling->direction = 1; //jff 2/22/98 make it go back up! + break; + + // make platform stop at bottom of all crusher strokes + // except generalized ones, reset speed, start back up + case silentCrushAndRaise: + S_StartSound((mobj_t *)&ceiling->sector->soundorg,sfx_pstop); + case crushAndRaise: + ceiling->speed = CEILSPEED; + case fastCrushAndRaise: + ceiling->direction = 1; + break; + + // in the case of ceiling mover/changer, change the texture + // then remove the active ceiling + case genCeilingChgT: + case genCeilingChg0: + ceiling->sector->special = ceiling->newspecial; + //jff add to fix bug in special transfers from changes + ceiling->sector->oldspecial = ceiling->oldspecial; + case genCeilingChg: + ceiling->sector->ceilingpic = ceiling->texture; + P_RemoveActiveCeiling(ceiling); + break; + + // all other case, just remove the active ceiling + case lowerAndCrush: + case lowerToFloor: + case lowerToLowest: + case lowerToMaxFloor: + case genCeiling: + P_RemoveActiveCeiling(ceiling); + break; + + default: + break; + } + } + else // ( res != pastdest ) + { + // handle the crusher encountering an obstacle + if (res == crushed) + { + switch(ceiling->type) + { + //jff 02/08/98 slow down slow crushers on obstacle + case genCrusher: + case genSilentCrusher: + if (ceiling->oldspeed < CEILSPEED*3) + ceiling->speed = CEILSPEED / 8; + break; + case silentCrushAndRaise: + case crushAndRaise: + case lowerAndCrush: + ceiling->speed = CEILSPEED / 8; + break; + + default: + break; + } + } + } + break; + } +} + + +// +// EV_DoCeiling +// +// Move a ceiling up/down or start a crusher +// +// Passed the linedef activating the function and the type of function desired +// returns true if a thinker started +// +int EV_DoCeiling +( line_t* line, + ceiling_e type ) +{ + int secnum; + int rtn; + sector_t* sec; + ceiling_t* ceiling; + + secnum = -1; + rtn = 0; + + // Reactivate in-stasis ceilings...for certain types. + // This restarts a crusher after it has been stopped + switch(type) + { + case fastCrushAndRaise: + case silentCrushAndRaise: + case crushAndRaise: + //jff 4/5/98 return if activated + rtn = P_ActivateInStasisCeiling(line); + default: + break; + } + + // affects all sectors with the same tag as the linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + // if ceiling already moving, don't start a second function on it + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + continue; + + // create a new ceiling thinker + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + memset(ceiling, 0, sizeof(*ceiling)); + P_AddThinker (&ceiling->thinker); + sec->ceilingdata = ceiling; //jff 2/22/98 + ceiling->thinker.function = T_MoveCeiling; + ceiling->sector = sec; + ceiling->crush = false; + + // setup ceiling structure according to type of function + switch(type) + { + case fastCrushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8*FRACUNIT); + ceiling->direction = -1; + ceiling->speed = CEILSPEED * 2; + break; + + case silentCrushAndRaise: + case crushAndRaise: + ceiling->crush = true; + ceiling->topheight = sec->ceilingheight; + case lowerAndCrush: + case lowerToFloor: + ceiling->bottomheight = sec->floorheight; + if (type != lowerToFloor) + ceiling->bottomheight += 8*FRACUNIT; + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case raiseToHighest: + ceiling->topheight = P_FindHighestCeilingSurrounding(sec); + ceiling->direction = 1; + ceiling->speed = CEILSPEED; + break; + + case lowerToLowest: + ceiling->bottomheight = P_FindLowestCeilingSurrounding(sec); + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + case lowerToMaxFloor: + ceiling->bottomheight = P_FindHighestFloorSurrounding(sec); + ceiling->direction = -1; + ceiling->speed = CEILSPEED; + break; + + default: + break; + } + + // add the ceiling to the active list + ceiling->tag = sec->tag; + ceiling->type = type; + P_AddActiveCeiling(ceiling); + } + return rtn; +} + +////////////////////////////////////////////////////////////////////// +// +// Active ceiling list primitives +// +///////////////////////////////////////////////////////////////////// + +// jff 2/22/98 - modified Lee's plat code to work for ceilings +// +// The following were all rewritten by Lee Killough +// to use the new structure which places no limits +// on active ceilings. It also avoids spending as much +// time searching for active ceilings. Previously a +// fixed-size array was used, with NULL indicating +// empty entries, while now a doubly-linked list +// is used. + +// +// P_ActivateInStasisCeiling() +// +// Reactivates all stopped crushers with the right tag +// +// Passed the line reactivating the crusher +// Returns true if a ceiling reactivated +// +//jff 4/5/98 return if activated +int P_ActivateInStasisCeiling(line_t *line) +{ + ceilinglist_t *cl; + int rtn=0; + + for (cl=activeceilings; cl; cl=cl->next) + { + ceiling_t *ceiling = cl->ceiling; + if (ceiling->tag == line->tag && ceiling->direction == 0) + { + ceiling->direction = ceiling->olddirection; + ceiling->thinker.function = T_MoveCeiling; + //jff 4/5/98 return if activated + rtn=1; + } + } + return rtn; +} + +// +// EV_CeilingCrushStop() +// +// Stops all active ceilings with the right tag +// +// Passed the linedef stopping the ceilings +// Returns true if a ceiling put in stasis +// +int EV_CeilingCrushStop(line_t* line) +{ + int rtn=0; + + ceilinglist_t *cl; + for (cl=activeceilings; cl; cl=cl->next) + { + ceiling_t *ceiling = cl->ceiling; + if (ceiling->direction != 0 && ceiling->tag == line->tag) + { + ceiling->olddirection = ceiling->direction; + ceiling->direction = 0; + ceiling->thinker.function = NULL; + rtn=1; + } + } + return rtn; +} + +// +// P_AddActiveCeiling() +// +// Adds a ceiling to the head of the list of active ceilings +// +// Passed the ceiling motion structure +// Returns nothing +// +void P_AddActiveCeiling(ceiling_t* ceiling) +{ + ceilinglist_t *list = malloc(sizeof *list); + list->ceiling = ceiling; + ceiling->list = list; + if ((list->next = activeceilings)) + list->next->prev = &list->next; + list->prev = &activeceilings; + activeceilings = list; +} + +// +// P_RemoveActiveCeiling() +// +// Removes a ceiling from the list of active ceilings +// +// Passed the ceiling motion structure +// Returns nothing +// +void P_RemoveActiveCeiling(ceiling_t* ceiling) +{ + ceilinglist_t *list = ceiling->list; + ceiling->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker(&ceiling->thinker); + if ((*list->prev = list->next)) + list->next->prev = list->prev; + free(list); +} + +// +// P_RemoveAllActiveCeilings() +// +// Removes all ceilings from the active ceiling list +// +// Passed nothing, returns nothing +// +void P_RemoveAllActiveCeilings(void) +{ + while (activeceilings) + { + ceilinglist_t *next = activeceilings->next; + free(activeceilings); + activeceilings = next; + } +} diff --git a/src/p_checksum.c b/src/p_checksum.c new file mode 100644 index 00000000..6cd40a9f --- /dev/null +++ b/src/p_checksum.c @@ -0,0 +1,98 @@ +#include +#include +#include +#include /* */ + +#include "p_checksum.h" +#include "md5.h" +#include "doomstat.h" /* players{,ingame} */ +#include "lprintf.h" + +/* forward decls */ +void checksum_gamestate(int tic); + +/* vars */ +static void p_checksum_nop(int tic){} /* do nothing */ +void (*P_Checksum)(int) = p_checksum_nop; + +/* + * P_RecordChecksum + * sets up the file and function pointers to write out checksum data + */ +static FILE *outfile = NULL; +static struct MD5Context md5global; + +void P_RecordChecksum(const char *file) { + size_t fnsize; + + fnsize = strlen(file); + + /* special case: write to stdout */ + if(0 == strncmp("-",file,MIN(1,fnsize))) + outfile = stdout; + else { + outfile = fopen(file,"wb"); + if(NULL == outfile) { + I_Error("cannot open %s for writing checksum:\n%s\n", + file, strerror(errno)); + } + } + + MD5Init(&md5global); + + P_Checksum = checksum_gamestate; +} + +void P_ChecksumFinal(void) { + int i; + unsigned char digest[16]; + + if (!outfile) + return; + + MD5Final(digest, &md5global); + fprintf(outfile, "final: "); + for (i=0; i<16; i++) + fprintf(outfile,"%x", digest[i]); + fprintf(outfile, "\n"); + MD5Init(&md5global); +} + +void p_checksum_cleanup(void) { + if (outfile && (outfile != stdout)) + fclose(outfile); +} + +/* + * runs on each tic when recording checksums + */ +void checksum_gamestate(int tic) { + int i; + struct MD5Context md5ctx; + unsigned char digest[16]; + char buffer[2048]; + + fprintf(outfile,"%6d, ", tic); + + /* based on "ArchivePlayers" */ + MD5Init(&md5ctx); + for (i=0 ; idirection) + { + case 0: + // Door is waiting + if (!--door->topcountdown) // downcount and check + { + switch(door->type) + { + case blazeRaise: + case genBlazeRaise: + door->direction = -1; // time to go back down + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); + break; + + case normal: + case genRaise: + door->direction = -1; // time to go back down + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); + break; + + case close30ThenOpen: + case genCdO: + door->direction = 1; // time to go back up + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + + case genBlazeCdO: + door->direction = 1; // time to go back up + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); + break; + + default: + break; + } + } + break; + + case 2: + // Special case for sector type door that opens in 5 mins + if (!--door->topcountdown) // 5 minutes up? + { + switch(door->type) + { + case raiseIn5Mins: + door->direction = 1; // time to raise then + door->type = normal; // door acts just like normal 1 DR door now + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + + default: + break; + } + } + break; + + case -1: + // Door is moving down + res = T_MovePlane + ( + door->sector, + door->speed, + door->sector->floorheight, + false, + 1, + door->direction + ); + + /* killough 10/98: implement gradual lighting effects */ + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + // Old code: if (door->lighttag && door->topheight - door->sector->floorheight) + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level >= mbf_compatibility) + EV_LightTurnOnPartway(door->line, + FixedDiv(door->sector->ceilingheight - + door->sector->floorheight, + door->topheight - + door->sector->floorheight)); + + // handle door reaching bottom + if (res == pastdest) + { + switch(door->type) + { + // regular open and close doors are all done, remove them + case blazeRaise: + case blazeClose: + case genBlazeRaise: + case genBlazeClose: + door->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker (&door->thinker); // unlink and free + // killough 4/15/98: remove double-closing sound of blazing doors + if (comp[comp_blazing]) + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); + break; + + case normal: + case close: + case genRaise: + case genClose: + door->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker (&door->thinker); // unlink and free + break; + + // close then open doors start waiting + case close30ThenOpen: + door->direction = 0; + door->topcountdown = TICRATE*30; + break; + + case genCdO: + case genBlazeCdO: + door->direction = 0; + door->topcountdown = door->topwait; // jff 5/8/98 insert delay + break; + + default: + break; + } + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level < mbf_compatibility) + EV_LightTurnOnPartway(door->line,0); + } + /* jff 1/31/98 turn lighting off in tagged sectors of manual doors + * killough 10/98: replaced with gradual lighting code + */ + else if (res == crushed) // handle door meeting obstruction on way down + { + switch(door->type) + { + case genClose: + case genBlazeClose: + case blazeClose: + case close: // Close types do not bounce, merely wait + break; + + case blazeRaise: + case genBlazeRaise: + door->direction = 1; + if (!comp[comp_blazing]) { + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); + break; + } + + default: // other types bounce off the obstruction + door->direction = 1; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + } + } + break; + + case 1: + // Door is moving up + res = T_MovePlane + ( + door->sector, + door->speed, + door->topheight, + false, + 1, + door->direction + ); + + /* killough 10/98: implement gradual lighting effects */ + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + // Old code: if (door->lighttag && door->topheight - door->sector->floorheight) + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level >= mbf_compatibility) + EV_LightTurnOnPartway(door->line, + FixedDiv(door->sector->ceilingheight - + door->sector->floorheight, + door->topheight - + door->sector->floorheight)); + + // handle door reaching the top + if (res == pastdest) + { + switch(door->type) + { + case blazeRaise: // regular open/close doors start waiting + case normal: + case genRaise: + case genBlazeRaise: + door->direction = 0; // wait at top with delay + door->topcountdown = door->topwait; + break; + + case close30ThenOpen: // close and close/open doors are done + case blazeOpen: + case open: + case genBlazeOpen: + case genOpen: + case genCdO: + case genBlazeCdO: + door->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker (&door->thinker); // unlink and free + break; + + default: + break; + } + + /* jff 1/31/98 turn lighting on in tagged sectors of manual doors + * killough 10/98: replaced with gradual lighting code */ + // e6y: "Tagged doors don't trigger special lighting" handled wrong + // http://sourceforge.net/tracker/index.php?func=detail&aid=1411400&group_id=148658&atid=772943 + if (door->lighttag && door->topheight - door->sector->floorheight && compatibility_level < mbf_compatibility) + EV_LightTurnOnPartway(door->line,FRACUNIT); + } + break; + } +} + +/////////////////////////////////////////////////////////////// +// +// Door linedef handlers +// +/////////////////////////////////////////////////////////////// + +// +// EV_DoLockedDoor +// +// Handle opening a tagged locked door +// +// Passed the line activating the door, the type of door, +// and the thing that activated the line +// Returns true if a thinker created +// +int EV_DoLockedDoor +( line_t* line, + vldoor_e type, + mobj_t* thing ) +{ + player_t* p; + + // only players can open locked doors + p = thing->player; + if (!p) + return 0; + + // check type of linedef, and if key is possessed to open it + switch(line->special) + { + case 99: // Blue Lock + case 133: + if (!p->cards[it_bluecard] && !p->cards[it_blueskull]) + { + p->message = s_PD_BLUEO; // Ty 03/27/98 - externalized + S_StartSound(p->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 134: // Red Lock + case 135: + if (!p->cards[it_redcard] && !p->cards[it_redskull]) + { + p->message = s_PD_REDO; // Ty 03/27/98 - externalized + S_StartSound(p->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 136: // Yellow Lock + case 137: + if (!p->cards[it_yellowcard] && !p->cards[it_yellowskull]) + { + p->message = s_PD_YELLOWO; // Ty 03/27/98 - externalized + S_StartSound(p->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + } + + // got the key, so open the door + return EV_DoDoor(line,type); +} + + +// +// EV_DoDoor +// +// Handle opening a tagged door +// +// Passed the line activating the door and the type of door +// Returns true if a thinker created +// +int EV_DoDoor +( line_t* line, + vldoor_e type ) +{ + int secnum,rtn; + sector_t* sec; + vldoor_t* door; + + secnum = -1; + rtn = 0; + + // open all doors with the same tag as the activating line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + // if the ceiling already moving, don't start the door action + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + continue; + + // new door thinker + rtn = 1; + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->type = type; + door->topwait = VDOORWAIT; + door->speed = VDOORSPEED; + door->line = line; // jff 1/31/98 remember line that triggered us + door->lighttag = 0; /* killough 10/98: no light effects with tagged doors */ + + // setup door parameters according to type of door + switch(type) + { + case blazeClose: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + door->speed = VDOORSPEED * 4; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdcls); + break; + + case close: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); + break; + + case close30ThenOpen: + door->topheight = sec->ceilingheight; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_dorcls); + break; + + case blazeRaise: + case blazeOpen: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->speed = VDOORSPEED * 4; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_bdopn); + break; + + case normal: + case open: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,sfx_doropn); + break; + + default: + break; + } + } + return rtn; +} + + +// +// EV_VerticalDoor +// +// Handle opening a door manually, no tag value +// +// Passed the line activating the door and the thing activating it +// Returns true if a thinker created +// +// jff 2/12/98 added int return value, fixed all returns +// +int EV_VerticalDoor +( line_t* line, + mobj_t* thing ) +{ + player_t* player; + int secnum; + sector_t* sec; + vldoor_t* door; + + // Check for locks + player = thing->player; + + switch(line->special) + { + case 26: // Blue Lock + case 32: + if ( !player ) + return 0; + if (!player->cards[it_bluecard] && !player->cards[it_blueskull]) + { + player->message = s_PD_BLUEK; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 27: // Yellow Lock + case 34: + if ( !player ) + return 0; + if (!player->cards[it_yellowcard] && !player->cards[it_yellowskull]) + { + player->message = s_PD_YELLOWK; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + case 28: // Red Lock + case 33: + if ( !player ) + return 0; + if (!player->cards[it_redcard] && !player->cards[it_redskull]) + { + player->message = s_PD_REDK; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + break; + + default: + break; + } + + // if the wrong side of door is pushed, give oof sound + if (line->sidenum[1]==NO_INDEX) // killough + { + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return 0; + } + + // get the sector on the second side of activating linedef + sec = sides[line->sidenum[1]].sector; + secnum = sec-sectors; + + /* if door already has a thinker, use it + * cph 2001/04/05 - + * Ok, this is a disaster area. We're assuming that sec->ceilingdata + * is a vldoor_t! What if this door is controlled by both DR lines + * and by switches? I don't know how to fix that. + * Secondly, original Doom didn't distinguish floor/lighting/ceiling + * actions, so we need to do the same in demo compatibility mode. + */ + door = sec->ceilingdata; + if (demo_compatibility) { + if (!door) door = sec->floordata; + if (!door) door = sec->lightingdata; + } + /* If this is a repeatable line, and the door is already moving, then we can just reverse the current action. Note that in prboom 2.3.0 I erroneously removed the if-this-is-repeatable check, hence the prboom_4_compatibility clause below (foolishly assumed that already moving implies repeatable - but it could be moving due to another switch, e.g. lv19-509) */ + if (door && + ((compatibility_level == prboom_4_compatibility) || + (line->special == 1) || (line->special == 117) || (line->special == 26) || (line->special == 27) || (line->special == 28) + ) + ) { + /* For old demos we have to emulate the old buggy behavior and + * mess up non-T_VerticalDoor actions. + */ + if (compatibility_level < prboom_4_compatibility || + door->thinker.function == T_VerticalDoor) { + /* cph - we are writing outval to door->direction iff it is non-zero */ + signed int outval = 0; + + /* An already moving repeatable door which is being re-pressed, or a + * monster is trying to open a closing door - so change direction + * DEMOSYNC: we only read door->direction now if it really is a door. + */ + if (door->thinker.function == T_VerticalDoor && door->direction == -1) { + outval = 1; /* go back up */ + } else if (player) { + outval = -1; /* go back down */ + } + + /* Write this to the thinker. In demo compatibility mode, we might be + * overwriting a field of a non-vldoor_t thinker - we need to add any + * other thinker types here if any demos depend on specific fields + * being corrupted by this. + */ + if (outval) { + if (door->thinker.function == T_VerticalDoor) { + door->direction = outval; + } else if (door->thinker.function == T_PlatRaise) { + plat_t* p = (plat_t*)door; + p->wait = outval; + } else { + lprintf(LO_DEBUG, "EV_VerticalDoor: unknown thinker.function in thinker corruption emulation"); + } + + return 1; + } + } + /* Either we're in prboom >=v2.3 and it's not a door, or it's a door but + * we're a monster and don't want to shut it; exit with no action. + */ + return 0; + } + + // emit proper sound + switch(line->special) + { + case 117: // blazing door raise + case 118: // blazing door open + S_StartSound((mobj_t *)&sec->soundorg,sfx_bdopn); + break; + + default: // normal or locked door sound + S_StartSound((mobj_t *)&sec->soundorg,sfx_doropn); + break; + } + + // new door thinker + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 1; + door->speed = VDOORSPEED; + door->topwait = VDOORWAIT; + door->line = line; // jff 1/31/98 remember line that triggered us + + /* killough 10/98: use gradual lighting changes if nonzero tag given */ + door->lighttag = comp[comp_doorlight] ? 0 : line->tag; + + // set the type of door from the activating linedef type + switch(line->special) + { + case 1: + case 26: + case 27: + case 28: + door->type = normal; + break; + + case 31: + case 32: + case 33: + case 34: + door->type = open; + line->special = 0; + break; + + case 117: // blazing door raise + door->type = blazeRaise; + door->speed = VDOORSPEED*4; + break; + case 118: // blazing door open + door->type = blazeOpen; + line->special = 0; + door->speed = VDOORSPEED*4; + break; + + default: + door->lighttag = 0; // killough 10/98 + break; + } + + // find the top and bottom of the movement range + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + return 1; +} + + +/////////////////////////////////////////////////////////////// +// +// Sector type door spawners +// +/////////////////////////////////////////////////////////////// + +// +// P_SpawnDoorCloseIn30() +// +// Spawn a door that closes after 30 seconds (called at level init) +// +// Passed the sector of the door, whose type specified the door action +// Returns nothing +// +void P_SpawnDoorCloseIn30 (sector_t* sec) +{ + vldoor_t* door; + + door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); + + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + + sec->ceilingdata = door; //jff 2/22/98 + sec->special = 0; + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 0; + door->type = normal; + door->speed = VDOORSPEED; + door->topcountdown = 30 * 35; + door->line = NULL; // jff 1/31/98 remember line that triggered us + door->lighttag = 0; /* killough 10/98: no lighting changes */ +} + +// +// P_SpawnDoorRaiseIn5Mins() +// +// Spawn a door that opens after 5 minutes (called at level init) +// +// Passed the sector of the door, whose type specified the door action +// Returns nothing +// +void P_SpawnDoorRaiseIn5Mins +( sector_t* sec, + int secnum ) +{ + vldoor_t* door; + + door = Z_Malloc ( sizeof(*door), PU_LEVSPEC, 0); + + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + + sec->ceilingdata = door; //jff 2/22/98 + sec->special = 0; + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->direction = 2; + door->type = raiseIn5Mins; + door->speed = VDOORSPEED; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->topwait = VDOORWAIT; + door->topcountdown = 5 * 60 * 35; + door->line = NULL; // jff 1/31/98 remember line that triggered us + door->lighttag = 0; /* killough 10/98: no lighting changes */ +} diff --git a/src/p_enemy.c b/src/p_enemy.c new file mode 100644 index 00000000..9498cd00 --- /dev/null +++ b/src/p_enemy.c @@ -0,0 +1,2584 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000,2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Enemy thinking, AI. + * Action Pointer Functions + * that are associated with states/frames. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_random.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "p_spec.h" +#include "s_sound.h" +#include "sounds.h" +#include "p_inter.h" +#include "g_game.h" +#include "p_enemy.h" +#include "p_tick.h" +#include "m_bbox.h" +#include "lprintf.h" + +static mobj_t *current_actor; + +typedef enum { + DI_EAST, + DI_NORTHEAST, + DI_NORTH, + DI_NORTHWEST, + DI_WEST, + DI_SOUTHWEST, + DI_SOUTH, + DI_SOUTHEAST, + DI_NODIR, + NUMDIRS +} dirtype_t; + +static void P_NewChaseDir(mobj_t *actor); +void P_ZBumpCheck(mobj_t *); // phares + +// +// ENEMY THINKING +// Enemies are allways spawned +// with targetplayer = -1, threshold = 0 +// Most monsters are spawned unaware of all players, +// but some can be made preaware +// + +// +// Called by P_NoiseAlert. +// Recursively traverse adjacent sectors, +// sound blocking lines cut off traversal. +// +// killough 5/5/98: reformatted, cleaned up + +static void P_RecursiveSound(sector_t *sec, int soundblocks, + mobj_t *soundtarget) +{ + int i; + + // wake up all monsters in this sector + if (sec->validcount == validcount && sec->soundtraversed <= soundblocks+1) + return; // already flooded + + sec->validcount = validcount; + sec->soundtraversed = soundblocks+1; + P_SetTarget(&sec->soundtarget, soundtarget); + + for (i=0; ilinecount; i++) + { + sector_t *other; + line_t *check = sec->lines[i]; + + if (!(check->flags & ML_TWOSIDED)) + continue; + + P_LineOpening(check); + + if (openrange <= 0) + continue; // closed door + + other=sides[check->sidenum[sides[check->sidenum[0]].sector==sec]].sector; + + if (!(check->flags & ML_SOUNDBLOCK)) + P_RecursiveSound(other, soundblocks, soundtarget); + else + if (!soundblocks) + P_RecursiveSound(other, 1, soundtarget); + } +} + +// +// P_NoiseAlert +// If a monster yells at a player, +// it will alert other monsters to the player. +// +void P_NoiseAlert(mobj_t *target, mobj_t *emitter) +{ + validcount++; + P_RecursiveSound(emitter->subsector->sector, 0, target); +} + +// +// P_CheckMeleeRange +// + +static boolean P_CheckMeleeRange(mobj_t *actor) +{ + mobj_t *pl = actor->target; + + return // killough 7/18/98: friendly monsters don't attack other friends + pl && !(actor->flags & pl->flags & MF_FRIEND) && + (P_AproxDistance(pl->x-actor->x, pl->y-actor->y) < + MELEERANGE - 20*FRACUNIT + pl->info->radius) && + P_CheckSight(actor, actor->target); +} + +// +// P_HitFriend() +// +// killough 12/98 +// This function tries to prevent shooting at friends + +static boolean P_HitFriend(mobj_t *actor) +{ + return actor->flags & MF_FRIEND && actor->target && + (P_AimLineAttack(actor, + R_PointToAngle2(actor->x, actor->y, + actor->target->x, actor->target->y), + P_AproxDistance(actor->x-actor->target->x, + actor->y-actor->target->y), 0), + linetarget) && linetarget != actor->target && + !((linetarget->flags ^ actor->flags) & MF_FRIEND); +} + +// +// P_CheckMissileRange +// +static boolean P_CheckMissileRange(mobj_t *actor) +{ + fixed_t dist; + + if (!P_CheckSight(actor, actor->target)) + return false; + + if (actor->flags & MF_JUSTHIT) + { // the target just hit the enemy, so fight back! + actor->flags &= ~MF_JUSTHIT; + + /* killough 7/18/98: no friendly fire at corpses + * killough 11/98: prevent too much infighting among friends + * cph - yikes, talk about fitting everything on one line... */ + + return + !(actor->flags & MF_FRIEND) || + (actor->target->health > 0 && + (!(actor->target->flags & MF_FRIEND) || + (actor->target->player ? + monster_infighting || P_Random(pr_defect) >128 : + !(actor->target->flags & MF_JUSTHIT) && P_Random(pr_defect) >128))); + } + + /* killough 7/18/98: friendly monsters don't attack other friendly + * monsters or players (except when attacked, and then only once) + */ + if (actor->flags & actor->target->flags & MF_FRIEND) + return false; + + if (actor->reactiontime) + return false; // do not attack yet + + // OPTIMIZE: get this from a global checksight + dist = P_AproxDistance ( actor->x-actor->target->x, + actor->y-actor->target->y) - 64*FRACUNIT; + + if (!actor->info->meleestate) + dist -= 128*FRACUNIT; // no melee attack, so fire more + + dist >>= FRACBITS; + + if (actor->type == MT_VILE) + if (dist > 14*64) + return false; // too far away + + + if (actor->type == MT_UNDEAD) + { + if (dist < 196) + return false; // close for fist attack + dist >>= 1; + } + + if (actor->type == MT_CYBORG || + actor->type == MT_SPIDER || + actor->type == MT_SKULL) + dist >>= 1; + + if (dist > 200) + dist = 200; + + if (actor->type == MT_CYBORG && dist > 160) + dist = 160; + + if (P_Random(pr_missrange) < dist) + return false; + + if (P_HitFriend(actor)) + return false; + + return true; +} + +/* + * P_IsOnLift + * + * killough 9/9/98: + * + * Returns true if the object is on a lift. Used for AI, + * since it may indicate the need for crowded conditions, + * or that a monster should stay on the lift for a while + * while it goes up or down. + */ + +static boolean P_IsOnLift(const mobj_t *actor) +{ + const sector_t *sec = actor->subsector->sector; + line_t line; + int l; + + // Short-circuit: it's on a lift which is active. + if (sec->floordata && ((thinker_t *) sec->floordata)->function==T_PlatRaise) + return true; + + // Check to see if it's in a sector which can be activated as a lift. + if ((line.tag = sec->tag)) + for (l = -1; (l = P_FindLineFromLineTag(&line, l)) >= 0;) + switch (lines[l].special) + { + case 10: case 14: case 15: case 20: case 21: case 22: + case 47: case 53: case 62: case 66: case 67: case 68: + case 87: case 88: case 95: case 120: case 121: case 122: + case 123: case 143: case 162: case 163: case 181: case 182: + case 144: case 148: case 149: case 211: case 227: case 228: + case 231: case 232: case 235: case 236: + return true; + } + + return false; +} + +/* + * P_IsUnderDamage + * + * killough 9/9/98: + * + * Returns nonzero if the object is under damage based on + * their current position. Returns 1 if the damage is moderate, + * -1 if it is serious. Used for AI. + */ + +static int P_IsUnderDamage(mobj_t *actor) +{ + const struct msecnode_s *seclist; + const ceiling_t *cl; // Crushing ceiling + int dir = 0; + for (seclist=actor->touching_sectorlist; seclist; seclist=seclist->m_tnext) + if ((cl = seclist->m_sector->ceilingdata) && + cl->thinker.function == T_MoveCeiling) + dir |= cl->direction; + return dir; +} + +// +// P_Move +// Move in the current direction, +// returns false if the move is blocked. +// + +static fixed_t xspeed[8] = {FRACUNIT,47000,0,-47000,-FRACUNIT,-47000,0,47000}; +static fixed_t yspeed[8] = {0,47000,FRACUNIT,47000,0,-47000,-FRACUNIT,-47000}; + +// 1/11/98 killough: Limit removed on special lines crossed +extern line_t **spechit; // New code -- killough +extern int numspechit; + +static boolean P_Move(mobj_t *actor, boolean dropoff) /* killough 9/12/98 */ +{ + fixed_t tryx, tryy, deltax, deltay, origx, origy; + boolean try_ok; + int movefactor = ORIG_FRICTION_FACTOR; // killough 10/98 + int friction = ORIG_FRICTION; + int speed; + + if (actor->movedir == DI_NODIR) + return false; + + // killough 10/98: make monsters get affected by ice and sludge too: + + if (monster_friction) + movefactor = P_GetMoveFactor(actor, &friction); + + speed = actor->info->speed; + + if (friction < ORIG_FRICTION && // sludge + !(speed = ((ORIG_FRICTION_FACTOR - (ORIG_FRICTION_FACTOR-movefactor)/2) + * speed) / ORIG_FRICTION_FACTOR)) + speed = 1; // always give the monster a little bit of speed + + tryx = (origx = actor->x) + (deltax = speed * xspeed[actor->movedir]); + tryy = (origy = actor->y) + (deltay = speed * yspeed[actor->movedir]); + + try_ok = P_TryMove(actor, tryx, tryy, dropoff); + + // killough 10/98: + // Let normal momentum carry them, instead of steptoeing them across ice. + + if (try_ok && friction > ORIG_FRICTION) + { + actor->x = origx; + actor->y = origy; + movefactor *= FRACUNIT / ORIG_FRICTION_FACTOR / 4; + actor->momx += FixedMul(deltax, movefactor); + actor->momy += FixedMul(deltay, movefactor); + } + + if (!try_ok) + { // open any specials + int good; + + if (actor->flags & MF_FLOAT && floatok) + { + if (actor->z < tmfloorz) // must adjust height + actor->z += FLOATSPEED; + else + actor->z -= FLOATSPEED; + + actor->flags |= MF_INFLOAT; + + return true; + } + + if (!numspechit) + return false; + + actor->movedir = DI_NODIR; + + /* if the special is not a door that can be opened, return false + * + * killough 8/9/98: this is what caused monsters to get stuck in + * doortracks, because it thought that the monster freed itself + * by opening a door, even if it was moving towards the doortrack, + * and not the door itself. + * + * killough 9/9/98: If a line blocking the monster is activated, + * return true 90% of the time. If a line blocking the monster is + * not activated, but some other line is, return false 90% of the + * time. A bit of randomness is needed to ensure it's free from + * lockups, but for most cases, it returns the correct result. + * + * Do NOT simply return false 1/4th of the time (causes monsters to + * back out when they shouldn't, and creates secondary stickiness). + */ + + for (good = false; numspechit--; ) + if (P_UseSpecialLine(actor, spechit[numspechit], 0)) + good |= spechit[numspechit] == blockline ? 1 : 2; + + /* cph - compatibility maze here + * Boom v2.01 and orig. Doom return "good" + * Boom v2.02 and LxDoom return good && (P_Random(pr_trywalk)&3) + * MBF plays even more games + */ + if (!good || comp[comp_doorstuck]) return good; + if (!mbf_features) + return (P_Random(pr_trywalk)&3); /* jff 8/13/98 */ + else /* finally, MBF code */ + return ((P_Random(pr_opendoor) >= 230) ^ (good & 1)); + } + else + actor->flags &= ~MF_INFLOAT; + + /* killough 11/98: fall more slowly, under gravity, if felldown==true */ + if (!(actor->flags & MF_FLOAT) && + (!felldown || !mbf_features)) + actor->z = actor->floorz; + + return true; +} + +/* + * P_SmartMove + * + * killough 9/12/98: Same as P_Move, except smarter + */ + +static boolean P_SmartMove(mobj_t *actor) +{ + mobj_t *target = actor->target; + int on_lift, dropoff = false, under_damage; + + /* killough 9/12/98: Stay on a lift if target is on one */ + on_lift = !comp[comp_staylift] + && target && target->health > 0 + && target->subsector->sector->tag==actor->subsector->sector->tag && + P_IsOnLift(actor); + + under_damage = monster_avoid_hazards && P_IsUnderDamage(actor); + + // killough 10/98: allow dogs to drop off of taller ledges sometimes. + // dropoff==1 means always allow it, dropoff==2 means only up to 128 high, + // and only if the target is immediately on the other side of the line. + + if (!P_Move(actor, dropoff)) + return false; + + // killough 9/9/98: avoid crushing ceilings or other damaging areas + if ( + (on_lift && P_Random(pr_stayonlift) < 230 && // Stay on lift + !P_IsOnLift(actor)) + || + (monster_avoid_hazards && !under_damage && // Get away from damage + (under_damage = P_IsUnderDamage(actor)) && + (under_damage < 0 || P_Random(pr_avoidcrush) < 200)) + ) + actor->movedir = DI_NODIR; // avoid the area (most of the time anyway) + + return true; +} + +// +// TryWalk +// Attempts to move actor on +// in its current (ob->moveangle) direction. +// If blocked by either a wall or an actor +// returns FALSE +// If move is either clear or blocked only by a door, +// returns TRUE and sets... +// If a door is in the way, +// an OpenDoor call is made to start it opening. +// + +static boolean P_TryWalk(mobj_t *actor) +{ + if (!P_SmartMove(actor)) + return false; + actor->movecount = P_Random(pr_trywalk)&15; + return true; +} + +// +// P_DoNewChaseDir +// +// killough 9/8/98: +// +// Most of P_NewChaseDir(), except for what +// determines the new direction to take +// + +static void P_DoNewChaseDir(mobj_t *actor, fixed_t deltax, fixed_t deltay) +{ + dirtype_t xdir, ydir, tdir; + dirtype_t olddir = actor->movedir; + dirtype_t turnaround = olddir; + + if (turnaround != DI_NODIR) // find reverse direction + turnaround ^= 4; + + xdir = + deltax > 10*FRACUNIT ? DI_EAST : + deltax < -10*FRACUNIT ? DI_WEST : DI_NODIR; + + ydir = + deltay < -10*FRACUNIT ? DI_SOUTH : + deltay > 10*FRACUNIT ? DI_NORTH : DI_NODIR; + + // try direct route + if (xdir != DI_NODIR && ydir != DI_NODIR && turnaround != + (actor->movedir = deltay < 0 ? deltax > 0 ? DI_SOUTHEAST : DI_SOUTHWEST : + deltax > 0 ? DI_NORTHEAST : DI_NORTHWEST) && P_TryWalk(actor)) + return; + + // try other directions + if (P_Random(pr_newchase) > 200 || D_abs(deltay)>D_abs(deltax)) + tdir = xdir, xdir = ydir, ydir = tdir; + + if ((xdir == turnaround ? xdir = DI_NODIR : xdir) != DI_NODIR && + (actor->movedir = xdir, P_TryWalk(actor))) + return; // either moved forward or attacked + + if ((ydir == turnaround ? ydir = DI_NODIR : ydir) != DI_NODIR && + (actor->movedir = ydir, P_TryWalk(actor))) + return; + + // there is no direct path to the player, so pick another direction. + if (olddir != DI_NODIR && (actor->movedir = olddir, P_TryWalk(actor))) + return; + + // randomly determine direction of search + if (P_Random(pr_newchasedir) & 1) + { + for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++) + if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) + return; + } + else + for (tdir = DI_SOUTHEAST; tdir != DI_EAST-1; tdir--) + if (tdir != turnaround && (actor->movedir = tdir, P_TryWalk(actor))) + return; + + if ((actor->movedir = turnaround) != DI_NODIR && !P_TryWalk(actor)) + actor->movedir = DI_NODIR; +} + +// +// killough 11/98: +// +// Monsters try to move away from tall dropoffs. +// +// In Doom, they were never allowed to hang over dropoffs, +// and would remain stuck if involuntarily forced over one. +// This logic, combined with p_map.c (P_TryMove), allows +// monsters to free themselves without making them tend to +// hang over dropoffs. + +static fixed_t dropoff_deltax, dropoff_deltay, floorz; + +static boolean PIT_AvoidDropoff(line_t *line) +{ + if (line->backsector && // Ignore one-sided linedefs + tmbbox[BOXRIGHT] > line->bbox[BOXLEFT] && + tmbbox[BOXLEFT] < line->bbox[BOXRIGHT] && + tmbbox[BOXTOP] > line->bbox[BOXBOTTOM] && // Linedef must be contacted + tmbbox[BOXBOTTOM] < line->bbox[BOXTOP] && + P_BoxOnLineSide(tmbbox, line) == -1) + { + fixed_t front = line->frontsector->floorheight; + fixed_t back = line->backsector->floorheight; + angle_t angle; + + // The monster must contact one of the two floors, + // and the other must be a tall dropoff (more than 24). + + if (back == floorz && front < floorz - FRACUNIT*24) + angle = R_PointToAngle2(0,0,line->dx,line->dy); // front side dropoff + else + if (front == floorz && back < floorz - FRACUNIT*24) + angle = R_PointToAngle2(line->dx,line->dy,0,0); // back side dropoff + else + return true; + + // Move away from dropoff at a standard speed. + // Multiple contacted linedefs are cumulative (e.g. hanging over corner) + dropoff_deltax -= finesine[angle >> ANGLETOFINESHIFT]*32; + dropoff_deltay += finecosine[angle >> ANGLETOFINESHIFT]*32; + } + return true; +} + +// +// Driver for above +// + +static fixed_t P_AvoidDropoff(mobj_t *actor) +{ + int yh=((tmbbox[BOXTOP] = actor->y+actor->radius)-bmaporgy)>>MAPBLOCKSHIFT; + int yl=((tmbbox[BOXBOTTOM]= actor->y-actor->radius)-bmaporgy)>>MAPBLOCKSHIFT; + int xh=((tmbbox[BOXRIGHT] = actor->x+actor->radius)-bmaporgx)>>MAPBLOCKSHIFT; + int xl=((tmbbox[BOXLEFT] = actor->x-actor->radius)-bmaporgx)>>MAPBLOCKSHIFT; + int bx, by; + + floorz = actor->z; // remember floor height + + dropoff_deltax = dropoff_deltay = 0; + + // check lines + + validcount++; + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockLinesIterator(bx, by, PIT_AvoidDropoff); // all contacted lines + + return dropoff_deltax | dropoff_deltay; // Non-zero if movement prescribed +} + +// +// P_NewChaseDir +// +// killough 9/8/98: Split into two functions +// + +static void P_NewChaseDir(mobj_t *actor) +{ + mobj_t *target = actor->target; + fixed_t deltax = target->x - actor->x; + fixed_t deltay = target->y - actor->y; + + // killough 8/8/98: sometimes move away from target, keeping distance + // + // 1) Stay a certain distance away from a friend, to avoid being in their way + // 2) Take advantage over an enemy without missiles, by keeping distance + + actor->strafecount = 0; + + if (mbf_features) { + if (actor->floorz - actor->dropoffz > FRACUNIT*24 && + actor->z <= actor->floorz && + !(actor->flags & (MF_DROPOFF|MF_FLOAT)) && + !comp[comp_dropoff] && + P_AvoidDropoff(actor)) /* Move away from dropoff */ + { + P_DoNewChaseDir(actor, dropoff_deltax, dropoff_deltay); + + // If moving away from dropoff, set movecount to 1 so that + // small steps are taken to get monster away from dropoff. + + actor->movecount = 1; + return; + } + else + { + fixed_t dist = P_AproxDistance(deltax, deltay); + + // Move away from friends when too close, except + // in certain situations (e.g. a crowded lift) + + if (actor->flags & target->flags & MF_FRIEND && + distfriend << FRACBITS > dist && + !P_IsOnLift(target) && !P_IsUnderDamage(actor)) + { + deltax = -deltax, deltay = -deltay; + } else + if (target->health > 0 && (actor->flags ^ target->flags) & MF_FRIEND) + { // Live enemy target + if (monster_backing && + actor->info->missilestate && actor->type != MT_SKULL && + ((!target->info->missilestate && dist < MELEERANGE*2) || + (target->player && dist < MELEERANGE*3 && + (target->player->readyweapon == wp_fist || + target->player->readyweapon == wp_chainsaw)))) + { // Back away from melee attacker + actor->strafecount = P_Random(pr_enemystrafe) & 15; + deltax = -deltax, deltay = -deltay; + } + } + } + } + + P_DoNewChaseDir(actor, deltax, deltay); + + // If strafing, set movecount to strafecount so that old Doom + // logic still works the same, except in the strafing part + + if (actor->strafecount) + actor->movecount = actor->strafecount; +} + +// +// P_IsVisible +// +// killough 9/9/98: whether a target is visible to a monster +// + +static boolean P_IsVisible(mobj_t *actor, mobj_t *mo, boolean allaround) +{ + if (!allaround) + { + angle_t an = R_PointToAngle2(actor->x, actor->y, + mo->x, mo->y) - actor->angle; + if (an > ANG90 && an < ANG270 && + P_AproxDistance(mo->x-actor->x, mo->y-actor->y) > MELEERANGE) + return false; + } + return P_CheckSight(actor, mo); +} + +// +// PIT_FindTarget +// +// killough 9/5/98 +// +// Finds monster targets for other monsters +// + +static int current_allaround; + +static boolean PIT_FindTarget(mobj_t *mo) +{ + mobj_t *actor = current_actor; + + if (!((mo->flags ^ actor->flags) & MF_FRIEND && // Invalid target + mo->health > 0 && (mo->flags & MF_COUNTKILL || mo->type == MT_SKULL))) + return true; + + // If the monster is already engaged in a one-on-one attack + // with a healthy friend, don't attack around 60% the time + { + const mobj_t *targ = mo->target; + if (targ && targ->target == mo && + P_Random(pr_skiptarget) > 100 && + (targ->flags ^ mo->flags) & MF_FRIEND && + targ->health*2 >= targ->info->spawnhealth) + return true; + } + + if (!P_IsVisible(actor, mo, current_allaround)) + return true; + + P_SetTarget(&actor->lastenemy, actor->target); // Remember previous target + P_SetTarget(&actor->target, mo); // Found target + + // Move the selected monster to the end of its associated + // list, so that it gets searched last next time. + + { + thinker_t *cap = &thinkerclasscap[mo->flags & MF_FRIEND ? + th_friends : th_enemies]; + (mo->thinker.cprev->cnext = mo->thinker.cnext)->cprev = mo->thinker.cprev; + (mo->thinker.cprev = cap->cprev)->cnext = &mo->thinker; + (mo->thinker.cnext = cap)->cprev = &mo->thinker; + } + + return false; +} + +// +// P_LookForPlayers +// If allaround is false, only look 180 degrees in front. +// Returns true if a player is targeted. +// + +static boolean P_LookForPlayers(mobj_t *actor, boolean allaround) +{ + player_t *player; + int stop, stopc, c; + + if (actor->flags & MF_FRIEND) + { // killough 9/9/98: friendly monsters go about players differently + int anyone; + +#if 0 + if (!allaround) // If you want friendly monsters not to awaken unprovoked + return false; +#endif + + // Go back to a player, no matter whether it's visible or not + for (anyone=0; anyone<=1; anyone++) + for (c=0; ctarget, players[c].mo); + + // killough 12/98: + // get out of refiring loop, to avoid hitting player accidentally + + if (actor->info->missilestate) + { + P_SetMobjState(actor, actor->info->seestate); + actor->flags &= ~MF_JUSTHIT; + } + + return true; + } + + return false; + } + + // Change mask of 3 to (MAXPLAYERS-1) -- killough 2/15/98: + stop = (actor->lastlook-1)&(MAXPLAYERS-1); + + c = 0; + + stopc = !mbf_features && + !demo_compatibility && monsters_remember ? + MAXPLAYERS : 2; // killough 9/9/98 + + for (;; actor->lastlook = (actor->lastlook+1)&(MAXPLAYERS-1)) + { + if (!playeringame[actor->lastlook]) + continue; + + // killough 2/15/98, 9/9/98: + if (c++ == stopc || actor->lastlook == stop) // done looking + { + // e6y + // Fixed Boom incompatibilities. The following code was missed. + // There are no more desyncs on Donce's demos on horror.wad + + // Use last known enemy if no players sighted -- killough 2/15/98: + if (!mbf_features && !demo_compatibility && monsters_remember) + { + if (actor->lastenemy && actor->lastenemy->health > 0) + { + actor->target = actor->lastenemy; + actor->lastenemy = NULL; + return true; + } + } + + return false; + } + + player = &players[actor->lastlook]; + + if (player->health <= 0) + continue; // dead + + if (!P_IsVisible(actor, player->mo, allaround)) + continue; + + P_SetTarget(&actor->target, player->mo); + + /* killough 9/9/98: give monsters a threshold towards getting players + * (we don't want it to be too easy for a player with dogs :) + */ + if (!comp[comp_pursuit]) + actor->threshold = 60; + + return true; + } +} + +// +// Friendly monsters, by Lee Killough 7/18/98 +// +// Friendly monsters go after other monsters first, but +// also return to owner if they cannot find any targets. +// A marine's best friend :) killough 7/18/98, 9/98 +// + +static boolean P_LookForMonsters(mobj_t *actor, boolean allaround) +{ + thinker_t *cap, *th; + + if (demo_compatibility) + return false; + + if (actor->lastenemy && actor->lastenemy->health > 0 && monsters_remember && + !(actor->lastenemy->flags & actor->flags & MF_FRIEND)) // not friends + { + P_SetTarget(&actor->target, actor->lastenemy); + P_SetTarget(&actor->lastenemy, NULL); + return true; + } + + /* Old demos do not support monster-seeking bots */ + if (!mbf_features) + return false; + + // Search the threaded list corresponding to this object's potential targets + cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_enemies : th_friends]; + + // Search for new enemy + + if (cap->cnext != cap) // Empty list? bail out early + { + int x = (actor->x - bmaporgx)>>MAPBLOCKSHIFT; + int y = (actor->y - bmaporgy)>>MAPBLOCKSHIFT; + int d; + + current_actor = actor; + current_allaround = allaround; + + // Search first in the immediate vicinity. + + if (!P_BlockThingsIterator(x, y, PIT_FindTarget)) + return true; + + for (d=1; d<5; d++) + { + int i = 1 - d; + do + if (!P_BlockThingsIterator(x+i, y-d, PIT_FindTarget) || + !P_BlockThingsIterator(x+i, y+d, PIT_FindTarget)) + return true; + while (++i < d); + do + if (!P_BlockThingsIterator(x-d, y+i, PIT_FindTarget) || + !P_BlockThingsIterator(x+d, y+i, PIT_FindTarget)) + return true; + while (--i + d >= 0); + } + + { // Random number of monsters, to prevent patterns from forming + int n = (P_Random(pr_friends) & 31) + 15; + + for (th = cap->cnext; th != cap; th = th->cnext) + if (--n < 0) + { + // Only a subset of the monsters were searched. Move all of + // the ones which were searched so far, to the end of the list. + + (cap->cnext->cprev = cap->cprev)->cnext = cap->cnext; + (cap->cprev = th->cprev)->cnext = cap; + (th->cprev = cap)->cnext = th; + break; + } + else + if (!PIT_FindTarget((mobj_t *) th)) // If target sighted + return true; + } + } + + return false; // No monster found +} + +// +// P_LookForTargets +// +// killough 9/5/98: look for targets to go after, depending on kind of monster +// + +static boolean P_LookForTargets(mobj_t *actor, int allaround) +{ + return actor->flags & MF_FRIEND ? + P_LookForMonsters(actor, allaround) || P_LookForPlayers (actor, allaround): + P_LookForPlayers (actor, allaround) || P_LookForMonsters(actor, allaround); +} + +// +// P_HelpFriend +// +// killough 9/8/98: Help friends in danger of dying +// + +static boolean P_HelpFriend(mobj_t *actor) +{ + thinker_t *cap, *th; + + // If less than 33% health, self-preservation rules + if (actor->health*3 < actor->info->spawnhealth) + return false; + + current_actor = actor; + current_allaround = true; + + // Possibly help a friend under 50% health + cap = &thinkerclasscap[actor->flags & MF_FRIEND ? th_friends : th_enemies]; + + for (th = cap->cnext; th != cap; th = th->cnext) + if (((mobj_t *) th)->health*2 >= ((mobj_t *) th)->info->spawnhealth) + { + if (P_Random(pr_helpfriend) < 180) + break; + } + else + if (((mobj_t *) th)->flags & MF_JUSTHIT && + ((mobj_t *) th)->target && + ((mobj_t *) th)->target != actor->target && + !PIT_FindTarget(((mobj_t *) th)->target)) + { + // Ignore any attacking monsters, while searching for friend + actor->threshold = BASETHRESHOLD; + return true; + } + + return false; +} + +// +// A_KeenDie +// DOOM II special, map 32. +// Uses special tag 666. +// +void A_KeenDie(mobj_t* mo) +{ + thinker_t *th; + line_t junk; + + A_Fall(mo); + + // scan the remaining thinkers to see if all Keens are dead + + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + { + mobj_t *mo2 = (mobj_t *) th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + return; // other Keen not dead + } + + junk.tag = 666; + EV_DoDoor(&junk,open); +} + + +// +// ACTION ROUTINES +// + +// +// A_Look +// Stay in state until a player is sighted. +// + +void A_Look(mobj_t *actor) +{ + mobj_t *targ = actor->subsector->sector->soundtarget; + actor->threshold = 0; // any shot will wake up + + /* killough 7/18/98: + * Friendly monsters go after other monsters first, but + * also return to player, without attacking them, if they + * cannot find any targets. A marine's best friend :) + */ + actor->pursuecount = 0; + + if (!(actor->flags & MF_FRIEND && P_LookForTargets(actor, false)) && + !((targ = actor->subsector->sector->soundtarget) && + targ->flags & MF_SHOOTABLE && + (P_SetTarget(&actor->target, targ), + !(actor->flags & MF_AMBUSH) || P_CheckSight(actor, targ))) && + (actor->flags & MF_FRIEND || !P_LookForTargets(actor, false))) + return; + + // go into chase state + + if (actor->info->seesound) + { + int sound; + switch (actor->info->seesound) + { + case sfx_posit1: + case sfx_posit2: + case sfx_posit3: + sound = sfx_posit1+P_Random(pr_see)%3; + break; + + case sfx_bgsit1: + case sfx_bgsit2: + sound = sfx_bgsit1+P_Random(pr_see)%2; + break; + + default: + sound = actor->info->seesound; + break; + } + if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) + S_StartSound(NULL, sound); // full volume + else + S_StartSound(actor, sound); + } + P_SetMobjState(actor, actor->info->seestate); +} + +// +// A_KeepChasing +// +// killough 10/98: +// Allows monsters to continue movement while attacking +// + +static void A_KeepChasing(mobj_t *actor) +{ + if (actor->movecount) + { + actor->movecount--; + if (actor->strafecount) + actor->strafecount--; + P_SmartMove(actor); + } +} + +// +// A_Chase +// Actor has a melee attack, +// so it tries to close as fast as possible +// + +void A_Chase(mobj_t *actor) +{ + if (actor->reactiontime) + actor->reactiontime--; + + if (actor->threshold) { /* modify target threshold */ + if (!actor->target || actor->target->health <= 0) + actor->threshold = 0; + else + actor->threshold--; + } + + /* turn towards movement direction if not there yet + * killough 9/7/98: keep facing towards target if strafing or backing out + */ + + if (actor->strafecount) + A_FaceTarget(actor); + else if (actor->movedir < 8) + { + int delta = (actor->angle &= (7<<29)) - (actor->movedir << 29); + if (delta > 0) + actor->angle -= ANG90/2; + else + if (delta < 0) + actor->angle += ANG90/2; + } + + if (!actor->target || !(actor->target->flags&MF_SHOOTABLE)) + { + if (!P_LookForTargets(actor,true)) // look for a new target + P_SetMobjState(actor, actor->info->spawnstate); // no new target + return; + } + + // do not attack twice in a row + if (actor->flags & MF_JUSTATTACKED) + { + actor->flags &= ~MF_JUSTATTACKED; + if (gameskill != sk_nightmare && !fastparm) + P_NewChaseDir(actor); + return; + } + + // check for melee attack + if (actor->info->meleestate && P_CheckMeleeRange(actor)) + { + if (actor->info->attacksound) + S_StartSound(actor, actor->info->attacksound); + P_SetMobjState(actor, actor->info->meleestate); + /* killough 8/98: remember an attack + * cph - DEMOSYNC? */ + if (!actor->info->missilestate) + actor->flags |= MF_JUSTHIT; + return; + } + + // check for missile attack + if (actor->info->missilestate) + if (!(gameskill < sk_nightmare && !fastparm && actor->movecount)) + if (P_CheckMissileRange(actor)) + { + P_SetMobjState(actor, actor->info->missilestate); + actor->flags |= MF_JUSTATTACKED; + return; + } + + if (!actor->threshold) { + if (!mbf_features) + { /* killough 9/9/98: for backward demo compatibility */ + if (netgame && !P_CheckSight(actor, actor->target) && + P_LookForPlayers(actor, true)) + return; + } + /* killough 7/18/98, 9/9/98: new monster AI */ + else if (help_friends && P_HelpFriend(actor)) + return; /* killough 9/8/98: Help friends in need */ + /* Look for new targets if current one is bad or is out of view */ + else if (actor->pursuecount) + actor->pursuecount--; + else { + /* Our pursuit time has expired. We're going to think about + * changing targets */ + actor->pursuecount = BASETHRESHOLD; + + /* Unless (we have a live target + * and it's not friendly + * and we can see it) + * try to find a new one; return if sucessful */ + + if (!(actor->target && actor->target->health > 0 && + ((comp[comp_pursuit] && !netgame) || + (((actor->target->flags ^ actor->flags) & MF_FRIEND || + (!(actor->flags & MF_FRIEND) && monster_infighting)) && + P_CheckSight(actor, actor->target)))) + && P_LookForTargets(actor, true)) + return; + + /* (Current target was good, or no new target was found.) + * + * If monster is a missile-less friend, give up pursuit and + * return to player, if no attacks have occurred recently. + */ + + if (!actor->info->missilestate && actor->flags & MF_FRIEND) { + if (actor->flags & MF_JUSTHIT) /* if recent action, */ + actor->flags &= ~MF_JUSTHIT; /* keep fighting */ + else if (P_LookForPlayers(actor, true)) /* else return to player */ + return; + } + } + } + + if (actor->strafecount) + actor->strafecount--; + + // chase towards player + if (--actor->movecount<0 || !P_SmartMove(actor)) + P_NewChaseDir(actor); + + // make active sound + if (actor->info->activesound && P_Random(pr_see)<3) + S_StartSound(actor, actor->info->activesound); +} + +// +// A_FaceTarget +// +void A_FaceTarget(mobj_t *actor) +{ + if (!actor->target) + return; + actor->flags &= ~MF_AMBUSH; + actor->angle = R_PointToAngle2(actor->x, actor->y, + actor->target->x, actor->target->y); + if (actor->target->flags & MF_SHADOW) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_facetarget); + actor->angle += (t-P_Random(pr_facetarget))<<21; + } +} + +// +// A_PosAttack +// + +void A_PosAttack(mobj_t *actor) +{ + int angle, damage, slope, t; + + if (!actor->target) + return; + A_FaceTarget(actor); + angle = actor->angle; + slope = P_AimLineAttack(actor, angle, MISSILERANGE, 0); /* killough 8/2/98 */ + S_StartSound(actor, sfx_pistol); + + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_posattack); + angle += (t - P_Random(pr_posattack))<<20; + damage = (P_Random(pr_posattack)%5 + 1)*3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + +void A_SPosAttack(mobj_t* actor) +{ + int i, bangle, slope; + + if (!actor->target) + return; + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ + for (i=0; i<3; i++) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_sposattack); + int angle = bangle + ((t - P_Random(pr_sposattack))<<20); + int damage = ((P_Random(pr_sposattack)%5)+1)*3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); + } +} + +void A_CPosAttack(mobj_t *actor) +{ + int angle, bangle, damage, slope, t; + + if (!actor->target) + return; + S_StartSound(actor, sfx_shotgn); + A_FaceTarget(actor); + bangle = actor->angle; + slope = P_AimLineAttack(actor, bangle, MISSILERANGE, 0); /* killough 8/2/98 */ + + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_cposattack); + angle = bangle + ((t - P_Random(pr_cposattack))<<20); + damage = ((P_Random(pr_cposattack)%5)+1)*3; + P_LineAttack(actor, angle, MISSILERANGE, slope, damage); +} + +void A_CPosRefire(mobj_t *actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + /* killough 12/98: Stop firing if a friend has gotten in the way */ + if (P_HitFriend(actor)) + goto stop; + + /* killough 11/98: prevent refiring on friends continuously */ + if (P_Random(pr_cposrefire) < 40) { + if (actor->target && actor->flags & actor->target->flags & MF_FRIEND) + goto stop; + else + return; + } + + if (!actor->target || actor->target->health <= 0 + || !P_CheckSight(actor, actor->target)) +stop: P_SetMobjState(actor, actor->info->seestate); +} + +void A_SpidRefire(mobj_t* actor) +{ + // keep firing unless target got out of sight + A_FaceTarget(actor); + + /* killough 12/98: Stop firing if a friend has gotten in the way */ + if (P_HitFriend(actor)) + goto stop; + + if (P_Random(pr_spidrefire) < 10) + return; + + // killough 11/98: prevent refiring on friends continuously + if (!actor->target || actor->target->health <= 0 + || actor->flags & actor->target->flags & MF_FRIEND + || !P_CheckSight(actor, actor->target)) + stop: P_SetMobjState(actor, actor->info->seestate); +} + +void A_BspiAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, MT_ARACHPLAZ); // launch a missile +} + +// +// A_TroopAttack +// + +void A_TroopAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + int damage; + S_StartSound(actor, sfx_claw); + damage = (P_Random(pr_troopattack)%8+1)*3; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + P_SpawnMissile(actor, actor->target, MT_TROOPSHOT); // launch a missile +} + +void A_SargAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + int damage = ((P_Random(pr_sargattack)%10)+1)*4; + P_DamageMobj(actor->target, actor, actor, damage); + } +} + +void A_HeadAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget (actor); + if (P_CheckMeleeRange(actor)) + { + int damage = (P_Random(pr_headattack)%6+1)*10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + P_SpawnMissile(actor, actor->target, MT_HEADSHOT); // launch a missile +} + +void A_CyberAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + P_SpawnMissile(actor, actor->target, MT_ROCKET); +} + +void A_BruisAttack(mobj_t *actor) +{ + if (!actor->target) + return; + if (P_CheckMeleeRange(actor)) + { + int damage; + S_StartSound(actor, sfx_claw); + damage = (P_Random(pr_bruisattack)%8+1)*10; + P_DamageMobj(actor->target, actor, actor, damage); + return; + } + P_SpawnMissile(actor, actor->target, MT_BRUISERSHOT); // launch a missile +} + +// +// A_SkelMissile +// + +void A_SkelMissile(mobj_t *actor) +{ + mobj_t *mo; + + if (!actor->target) + return; + + A_FaceTarget (actor); + actor->z += 16*FRACUNIT; // so missile spawns higher + mo = P_SpawnMissile (actor, actor->target, MT_TRACER); + actor->z -= 16*FRACUNIT; // back to normal + + mo->x += mo->momx; + mo->y += mo->momy; + P_SetTarget(&mo->tracer, actor->target); +} + +int TRACEANGLE = 0xc000000; + +void A_Tracer(mobj_t *actor) +{ + angle_t exact; + fixed_t dist; + fixed_t slope; + mobj_t *dest; + mobj_t *th; + + /* killough 1/18/98: this is why some missiles do not have smoke + * and some do. Also, internal demos start at random gametics, thus + * the bug in which revenants cause internal demos to go out of sync. + * + * killough 3/6/98: fix revenant internal demo bug by subtracting + * levelstarttic from gametic. + * + * killough 9/29/98: use new "basetic" so that demos stay in sync + * during pauses and menu activations, while retaining old demo sync. + * + * leveltime would have been better to use to start with in Doom, but + * since old demos were recorded using gametic, we must stick with it, + * and improvise around it (using leveltime causes desync across levels). + */ + + if ((gametic-basetic) & 3) + return; + + // spawn a puff of smoke behind the rocket + P_SpawnPuff(actor->x, actor->y, actor->z); + + th = P_SpawnMobj (actor->x-actor->momx, + actor->y-actor->momy, + actor->z, MT_SMOKE); + + th->momz = FRACUNIT; + th->tics -= P_Random(pr_tracer) & 3; + if (th->tics < 1) + th->tics = 1; + + // adjust direction + dest = actor->tracer; + + if (!dest || dest->health <= 0) + return; + + // change angle + exact = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y); + + if (exact != actor->angle) { + if (exact - actor->angle > 0x80000000) + { + actor->angle -= TRACEANGLE; + if (exact - actor->angle < 0x80000000) + actor->angle = exact; + } + else + { + actor->angle += TRACEANGLE; + if (exact - actor->angle > 0x80000000) + actor->angle = exact; + } + } + + exact = actor->angle>>ANGLETOFINESHIFT; + actor->momx = FixedMul(actor->info->speed, finecosine[exact]); + actor->momy = FixedMul(actor->info->speed, finesine[exact]); + + // change slope + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + + dist = dist / actor->info->speed; + + if (dist < 1) + dist = 1; + + slope = (dest->z+40*FRACUNIT - actor->z) / dist; + + if (slope < actor->momz) + actor->momz -= FRACUNIT/8; + else + actor->momz += FRACUNIT/8; +} + +void A_SkelWhoosh(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + S_StartSound(actor,sfx_skeswg); +} + +void A_SkelFist(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + if (P_CheckMeleeRange(actor)) + { + int damage = ((P_Random(pr_skelfist)%10)+1)*6; + S_StartSound(actor, sfx_skepch); + P_DamageMobj(actor->target, actor, actor, damage); + } +} + +// +// PIT_VileCheck +// Detect a corpse that could be raised. +// + +mobj_t* corpsehit; +mobj_t* vileobj; +fixed_t viletryx; +fixed_t viletryy; + +static boolean PIT_VileCheck(mobj_t *thing) +{ + int maxdist; + boolean check; + + if (!(thing->flags & MF_CORPSE) ) + return true; // not a monster + + if (thing->tics != -1) + return true; // not lying still yet + + if (thing->info->raisestate == S_NULL) + return true; // monster doesn't have a raise state + + maxdist = thing->info->radius + mobjinfo[MT_VILE].radius; + + if (D_abs(thing->x-viletryx) > maxdist || D_abs(thing->y-viletryy) > maxdist) + return true; // not actually touching + +// Check to see if the radius and height are zero. If they are // phares +// then this is a crushed monster that has been turned into a // | +// gib. One of the options may be to ignore this guy. // V + +// Option 1: the original, buggy method, -> ghost (compatibility) +// Option 2: ressurect the monster, but not as a ghost +// Option 3: ignore the gib + +// if (Option3) // ^ +// if ((thing->height == 0) && (thing->radius == 0)) // | +// return true; // phares + + corpsehit = thing; + corpsehit->momx = corpsehit->momy = 0; + if (comp[comp_vile]) // phares + { // | + corpsehit->height <<= 2; // V + check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); + corpsehit->height >>= 2; + } + else + { + int height,radius; + + height = corpsehit->height; // save temporarily + radius = corpsehit->radius; // save temporarily + corpsehit->height = corpsehit->info->height; + corpsehit->radius = corpsehit->info->radius; + corpsehit->flags |= MF_SOLID; + check = P_CheckPosition(corpsehit,corpsehit->x,corpsehit->y); + corpsehit->height = height; // restore + corpsehit->radius = radius; // restore // ^ + corpsehit->flags &= ~MF_SOLID; + } // | + // phares + if (!check) + return true; // doesn't fit here + return false; // got one, so stop checking +} + +// +// A_VileChase +// Check for ressurecting a body +// + +void A_VileChase(mobj_t* actor) +{ + int xl, xh; + int yl, yh; + int bx, by; + + if (actor->movedir != DI_NODIR) + { + // check for corpses to raise + viletryx = + actor->x + actor->info->speed*xspeed[actor->movedir]; + viletryy = + actor->y + actor->info->speed*yspeed[actor->movedir]; + + xl = (viletryx - bmaporgx - MAXRADIUS*2)>>MAPBLOCKSHIFT; + xh = (viletryx - bmaporgx + MAXRADIUS*2)>>MAPBLOCKSHIFT; + yl = (viletryy - bmaporgy - MAXRADIUS*2)>>MAPBLOCKSHIFT; + yh = (viletryy - bmaporgy + MAXRADIUS*2)>>MAPBLOCKSHIFT; + + vileobj = actor; + for (bx=xl ; bx<=xh ; bx++) + { + for (by=yl ; by<=yh ; by++) + { + // Call PIT_VileCheck to check + // whether object is a corpse + // that canbe raised. + if (!P_BlockThingsIterator(bx,by,PIT_VileCheck)) + { + mobjinfo_t *info; + + // got one! + mobj_t* temp = actor->target; + actor->target = corpsehit; + A_FaceTarget(actor); + actor->target = temp; + + P_SetMobjState(actor, S_VILE_HEAL1); + S_StartSound(corpsehit, sfx_slop); + info = corpsehit->info; + + P_SetMobjState(corpsehit,info->raisestate); + + if (comp[comp_vile]) // phares + corpsehit->height <<= 2; // | + else // V + { + corpsehit->height = info->height; // fix Ghost bug + corpsehit->radius = info->radius; // fix Ghost bug + } // phares + + /* killough 7/18/98: + * friendliness is transferred from AV to raised corpse + */ + corpsehit->flags = + (info->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); + + if (!((corpsehit->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totallive++; + + corpsehit->health = info->spawnhealth; + P_SetTarget(&corpsehit->target, NULL); // killough 11/98 + + if (mbf_features) + { /* kilough 9/9/98 */ + P_SetTarget(&corpsehit->lastenemy, NULL); + corpsehit->flags &= ~MF_JUSTHIT; + } + + /* killough 8/29/98: add to appropriate thread */ + P_UpdateThinker(&corpsehit->thinker); + + return; + } + } + } + } + A_Chase(actor); // Return to normal attack. +} + +// +// A_VileStart +// + +void A_VileStart(mobj_t *actor) +{ + S_StartSound(actor, sfx_vilatk); +} + +// +// A_Fire +// Keep fire in front of player unless out of sight +// + +void A_StartFire(mobj_t *actor) +{ + S_StartSound(actor,sfx_flamst); + A_Fire(actor); +} + +void A_FireCrackle(mobj_t* actor) +{ + S_StartSound(actor,sfx_flame); + A_Fire(actor); +} + +void A_Fire(mobj_t *actor) +{ + unsigned an; + mobj_t *dest = actor->tracer; + + if (!dest) + return; + + // don't move it if the vile lost sight + if (!P_CheckSight(actor->target, dest) ) + return; + + an = dest->angle >> ANGLETOFINESHIFT; + + P_UnsetThingPosition(actor); + actor->x = dest->x + FixedMul(24*FRACUNIT, finecosine[an]); + actor->y = dest->y + FixedMul(24*FRACUNIT, finesine[an]); + actor->z = dest->z; + P_SetThingPosition(actor); +} + +// +// A_VileTarget +// Spawn the hellfire +// + +void A_VileTarget(mobj_t *actor) +{ + mobj_t *fog; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + // killough 12/98: fix Vile fog coordinates // CPhipps - compatibility optioned + fog = P_SpawnMobj(actor->target->x, + (compatibility_level < lxdoom_1_compatibility) ? actor->target->x : actor->target->y, + actor->target->z,MT_FIRE); + + P_SetTarget(&actor->tracer, fog); + P_SetTarget(&fog->target, actor); + P_SetTarget(&fog->tracer, actor->target); + A_Fire(fog); +} + +// +// A_VileAttack +// + +void A_VileAttack(mobj_t *actor) +{ + mobj_t *fire; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + if (!P_CheckSight(actor, actor->target)) + return; + + S_StartSound(actor, sfx_barexp); + P_DamageMobj(actor->target, actor, actor, 20); + actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; + + an = actor->angle >> ANGLETOFINESHIFT; + + fire = actor->tracer; + + if (!fire) + return; + + // move the fire between the vile and the player + fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]); + fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]); + P_RadiusAttack(fire, actor, 70); +} + +// +// Mancubus attack, +// firing three missiles (bruisers) +// in three different directions? +// Doesn't look like it. +// + +#define FATSPREAD (ANG90/8) + +void A_FatRaise(mobj_t *actor) +{ + A_FaceTarget(actor); + S_StartSound(actor, sfx_manatk); +} + +void A_FatAttack1(mobj_t *actor) +{ + mobj_t *mo; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + // Change direction to ... + actor->angle += FATSPREAD; + + P_SpawnMissile(actor, actor->target, MT_FATSHOT); + + mo = P_SpawnMissile (actor, actor->target, MT_FATSHOT); + mo->angle += FATSPREAD; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + +void A_FatAttack2(mobj_t *actor) +{ + mobj_t *mo; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + // Now here choose opposite deviation. + actor->angle -= FATSPREAD; + P_SpawnMissile(actor, actor->target, MT_FATSHOT); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle -= FATSPREAD*2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + +void A_FatAttack3(mobj_t *actor) +{ + mobj_t *mo; + int an; + + if (!actor->target) + return; + + A_FaceTarget(actor); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle -= FATSPREAD/2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); + + mo = P_SpawnMissile(actor, actor->target, MT_FATSHOT); + mo->angle += FATSPREAD/2; + an = mo->angle >> ANGLETOFINESHIFT; + mo->momx = FixedMul(mo->info->speed, finecosine[an]); + mo->momy = FixedMul(mo->info->speed, finesine[an]); +} + + +// +// SkullAttack +// Fly at the player like a missile. +// +#define SKULLSPEED (20*FRACUNIT) + +void A_SkullAttack(mobj_t *actor) +{ + mobj_t *dest; + angle_t an; + int dist; + + if (!actor->target) + return; + + dest = actor->target; + actor->flags |= MF_SKULLFLY; + + S_StartSound(actor, actor->info->attacksound); + A_FaceTarget(actor); + an = actor->angle >> ANGLETOFINESHIFT; + actor->momx = FixedMul(SKULLSPEED, finecosine[an]); + actor->momy = FixedMul(SKULLSPEED, finesine[an]); + dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y); + dist = dist / SKULLSPEED; + + if (dist < 1) + dist = 1; + actor->momz = (dest->z+(dest->height>>1) - actor->z) / dist; +} + +// +// A_PainShootSkull +// Spawn a lost soul and launch it at the target +// + +static void A_PainShootSkull(mobj_t *actor, angle_t angle) +{ + fixed_t x,y,z; + mobj_t *newmobj; + angle_t an; + int prestep; + +// The original code checked for 20 skulls on the level, // phares +// and wouldn't spit another one if there were. If not in // phares +// compatibility mode, we remove the limit. // phares + // phares + if (comp[comp_pain]) /* killough 10/98: compatibility-optioned */ + { + // count total number of skulls currently on the level + int count = 0; + thinker_t *currentthinker = NULL; + while ((currentthinker = P_NextThinker(currentthinker,th_all)) != NULL) + if ((currentthinker->function == P_MobjThinker) + && ((mobj_t *)currentthinker)->type == MT_SKULL) + count++; + if (count > 20) // phares + return; // phares + } + + // okay, there's room for another one + + an = angle >> ANGLETOFINESHIFT; + + prestep = 4*FRACUNIT + 3*(actor->info->radius + mobjinfo[MT_SKULL].radius)/2; + + x = actor->x + FixedMul(prestep, finecosine[an]); + y = actor->y + FixedMul(prestep, finesine[an]); + z = actor->z + 8*FRACUNIT; + + if (comp[comp_skull]) /* killough 10/98: compatibility-optioned */ + newmobj = P_SpawnMobj(x, y, z, MT_SKULL); // phares + else // V + { + // Check whether the Lost Soul is being fired through a 1-sided + // wall or an impassible line, or a "monsters can't cross" line. + // If it is, then we don't allow the spawn. This is a bug fix, but + // it should be considered an enhancement, since it may disturb + // existing demos, so don't do it in compatibility mode. + + if (Check_Sides(actor,x,y)) + return; + + newmobj = P_SpawnMobj(x, y, z, MT_SKULL); + + // Check to see if the new Lost Soul's z value is above the + // ceiling of its new sector, or below the floor. If so, kill it. + + if ((newmobj->z > + (newmobj->subsector->sector->ceilingheight - newmobj->height)) || + (newmobj->z < newmobj->subsector->sector->floorheight)) + { + // kill it immediately + P_DamageMobj(newmobj,actor,actor,10000); + return; // ^ + } // | + } // phares + + /* killough 7/20/98: PEs shoot lost souls with the same friendliness */ + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (actor->flags & MF_FRIEND); + + /* killough 8/29/98: add to appropriate thread */ + P_UpdateThinker(&newmobj->thinker); + + // Check for movements. + // killough 3/15/98: don't jump over dropoffs: + + if (!P_TryMove(newmobj, newmobj->x, newmobj->y, false)) + { + // kill it immediately + P_DamageMobj(newmobj, actor, actor, 10000); + return; + } + + P_SetTarget(&newmobj->target, actor->target); + A_SkullAttack(newmobj); +} + +// +// A_PainAttack +// Spawn a lost soul and launch it at the target +// + +void A_PainAttack(mobj_t *actor) +{ + if (!actor->target) + return; + A_FaceTarget(actor); + A_PainShootSkull(actor, actor->angle); +} + +void A_PainDie(mobj_t *actor) +{ + A_Fall(actor); + A_PainShootSkull(actor, actor->angle+ANG90); + A_PainShootSkull(actor, actor->angle+ANG180); + A_PainShootSkull(actor, actor->angle+ANG270); +} + +void A_Scream(mobj_t *actor) +{ + int sound; + + switch (actor->info->deathsound) + { + case 0: + return; + + case sfx_podth1: + case sfx_podth2: + case sfx_podth3: + sound = sfx_podth1 + P_Random(pr_scream)%3; + break; + + case sfx_bgdth1: + case sfx_bgdth2: + sound = sfx_bgdth1 + P_Random(pr_scream)%2; + break; + + default: + sound = actor->info->deathsound; + break; + } + + // Check for bosses. + if (actor->type==MT_SPIDER || actor->type == MT_CYBORG) + S_StartSound(NULL, sound); // full volume + else + S_StartSound(actor, sound); +} + +void A_XScream(mobj_t *actor) +{ + S_StartSound(actor, sfx_slop); +} + +void A_Pain(mobj_t *actor) +{ + if (actor->info->painsound) + S_StartSound(actor, actor->info->painsound); +} + +void A_Fall(mobj_t *actor) +{ + // actor is on ground, it can be walked over + actor->flags &= ~MF_SOLID; +} + +// +// A_Explode +// +void A_Explode(mobj_t *thingy) +{ + P_RadiusAttack( thingy, thingy->target, 128 ); +} + +// +// A_BossDeath +// Possibly trigger special effects +// if on first boss level +// + +void A_BossDeath(mobj_t *mo) +{ + thinker_t *th; + line_t junk; + int i; + + if (gamemode == commercial) + { + if (gamemap != 7) + return; + + if ((mo->type != MT_FATSO) + && (mo->type != MT_BABY)) + return; + } + else + { + // e6y + // Additional check of gameepisode is necessary, because + // there is no right or wrong solution for E4M6 in original EXEs, + // there's nothing to emulate. + if (comp[comp_666] && gameepisode < 4) + { + // e6y + // Only following checks are present in doom2.exe ver. 1.666 and 1.9 + // instead of separate checks for each episode in doomult.exe, plutonia.exe and tnt.exe + // There is no more desync on doom.wad\episode3.lmp + // http://www.doomworld.com/idgames/index.php?id=6909 + if (gamemap != 8) + return; + if (mo->type == MT_BRUISER && gameepisode != 1) + return; + } + else + { + switch(gameepisode) + { + case 1: + if (gamemap != 8) + return; + + if (mo->type != MT_BRUISER) + return; + break; + + case 2: + if (gamemap != 8) + return; + + if (mo->type != MT_CYBORG) + return; + break; + + case 3: + if (gamemap != 8) + return; + + if (mo->type != MT_SPIDER) + return; + + break; + + case 4: + switch(gamemap) + { + case 6: + if (mo->type != MT_CYBORG) + return; + break; + + case 8: + if (mo->type != MT_SPIDER) + return; + break; + + default: + return; + break; + } + break; + + default: + if (gamemap != 8) + return; + break; + } + } + + } + + // make sure there is a player alive for victory + for (i=0; i 0) + break; + + if (i==MAXPLAYERS) + return; // no one left alive, so do not end game + + // scan the remaining thinkers to see + // if all bosses are dead + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + { + mobj_t *mo2 = (mobj_t *) th; + if (mo2 != mo && mo2->type == mo->type && mo2->health > 0) + return; // other boss not dead + } + + // victory! + if ( gamemode == commercial) + { + if (gamemap == 7) + { + if (mo->type == MT_FATSO) + { + junk.tag = 666; + EV_DoFloor(&junk,lowerFloorToLowest); + return; + } + + if (mo->type == MT_BABY) + { + junk.tag = 667; + EV_DoFloor(&junk,raiseToTexture); + return; + } + } + } + else + { + switch(gameepisode) + { + case 1: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + + case 4: + switch(gamemap) + { + case 6: + junk.tag = 666; + EV_DoDoor(&junk, blazeOpen); + return; + break; + + case 8: + junk.tag = 666; + EV_DoFloor(&junk, lowerFloorToLowest); + return; + break; + } + } + } + G_ExitLevel(); +} + + +void A_Hoof (mobj_t* mo) +{ + S_StartSound(mo, sfx_hoof); + A_Chase(mo); +} + +void A_Metal(mobj_t *mo) +{ + S_StartSound(mo, sfx_metal); + A_Chase(mo); +} + +void A_BabyMetal(mobj_t *mo) +{ + S_StartSound(mo, sfx_bspwlk); + A_Chase(mo); +} + +void A_OpenShotgun2(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_dbopn); +} + +void A_LoadShotgun2(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_dbload); +} + +void A_CloseShotgun2(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_dbcls); + A_ReFire(player,psp); +} + +// killough 2/7/98: Remove limit on icon landings: +mobj_t **braintargets; +int numbraintargets_alloc; +int numbraintargets; + +struct brain_s brain; // killough 3/26/98: global state of boss brain + +// killough 3/26/98: initialize icon landings at level startup, +// rather than at boss wakeup, to prevent savegame-related crashes + +void P_SpawnBrainTargets(void) // killough 3/26/98: renamed old function +{ + thinker_t *thinker; + + // find all the target spots + numbraintargets = 0; + brain.targeton = 0; + brain.easy = 0; // killough 3/26/98: always init easy to 0 + + for (thinker = thinkercap.next ; + thinker != &thinkercap ; + thinker = thinker->next) + if (thinker->function == P_MobjThinker) + { + mobj_t *m = (mobj_t *) thinker; + + if (m->type == MT_BOSSTARGET ) + { // killough 2/7/98: remove limit on icon landings: + if (numbraintargets >= numbraintargets_alloc) + braintargets = realloc(braintargets, + (numbraintargets_alloc = numbraintargets_alloc ? + numbraintargets_alloc*2 : 32) *sizeof *braintargets); + braintargets[numbraintargets++] = m; + } + } +} + +void A_BrainAwake(mobj_t *mo) +{ + S_StartSound(NULL,sfx_bossit); // killough 3/26/98: only generates sound now +} + +void A_BrainPain(mobj_t *mo) +{ + S_StartSound(NULL,sfx_bospn); +} + +void A_BrainScream(mobj_t *mo) +{ + int x; + for (x=mo->x - 196*FRACUNIT ; x< mo->x + 320*FRACUNIT ; x+= FRACUNIT*8) + { + int y = mo->y - 320*FRACUNIT; + int z = 128 + P_Random(pr_brainscream)*2*FRACUNIT; + mobj_t *th = P_SpawnMobj (x,y,z, MT_ROCKET); + th->momz = P_Random(pr_brainscream)*512; + P_SetMobjState(th, S_BRAINEXPLODE1); + th->tics -= P_Random(pr_brainscream)&7; + if (th->tics < 1) + th->tics = 1; + } + S_StartSound(NULL,sfx_bosdth); +} + +void A_BrainExplode(mobj_t *mo) +{ // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_brainexp); + int x = mo->x + (t - P_Random(pr_brainexp))*2048; + int y = mo->y; + int z = 128 + P_Random(pr_brainexp)*2*FRACUNIT; + mobj_t *th = P_SpawnMobj(x,y,z, MT_ROCKET); + th->momz = P_Random(pr_brainexp)*512; + P_SetMobjState(th, S_BRAINEXPLODE1); + th->tics -= P_Random(pr_brainexp)&7; + if (th->tics < 1) + th->tics = 1; +} + +void A_BrainDie(mobj_t *mo) +{ + G_ExitLevel(); +} + +void A_BrainSpit(mobj_t *mo) +{ + mobj_t *targ, *newmobj; + + if (!numbraintargets) // killough 4/1/98: ignore if no targets + return; + + brain.easy ^= 1; // killough 3/26/98: use brain struct + if (gameskill <= sk_easy && !brain.easy) + return; + + // shoot a cube at current target + targ = braintargets[brain.targeton++]; // killough 3/26/98: + brain.targeton %= numbraintargets; // Use brain struct for targets + + // spawn brain missile + newmobj = P_SpawnMissile(mo, targ, MT_SPAWNSHOT); + P_SetTarget(&newmobj->target, targ); + newmobj->reactiontime = (short)(((targ->y-mo->y)/newmobj->momy)/newmobj->state->tics); + + // killough 7/18/98: brain friendliness is transferred + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); + + // killough 8/29/98: add to appropriate thread + P_UpdateThinker(&newmobj->thinker); + + S_StartSound(NULL, sfx_bospit); +} + +// travelling cube sound +void A_SpawnSound(mobj_t *mo) +{ + S_StartSound(mo,sfx_boscub); + A_SpawnFly(mo); +} + +void A_SpawnFly(mobj_t *mo) +{ + mobj_t *newmobj; + mobj_t *fog; + mobj_t *targ; + int r; + mobjtype_t type; + + if (--mo->reactiontime) + return; // still flying + + targ = mo->target; + + // First spawn teleport fog. + fog = P_SpawnMobj(targ->x, targ->y, targ->z, MT_SPAWNFIRE); + S_StartSound(fog, sfx_telept); + + // Randomly select monster to spawn. + r = P_Random(pr_spawnfly); + + // Probability distribution (kind of :), decreasing likelihood. + if ( r<50 ) + type = MT_TROOP; + else if (r<90) + type = MT_SERGEANT; + else if (r<120) + type = MT_SHADOWS; + else if (r<130) + type = MT_PAIN; + else if (r<160) + type = MT_HEAD; + else if (r<162) + type = MT_VILE; + else if (r<172) + type = MT_UNDEAD; + else if (r<192) + type = MT_BABY; + else if (r<222) + type = MT_FATSO; + else if (r<246) + type = MT_KNIGHT; + else + type = MT_BRUISER; + + newmobj = P_SpawnMobj(targ->x, targ->y, targ->z, type); + + /* killough 7/18/98: brain friendliness is transferred */ + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); + + /* killough 8/29/98: add to appropriate thread */ + P_UpdateThinker(&newmobj->thinker); + + if (P_LookForTargets(newmobj,true)) /* killough 9/4/98 */ + P_SetMobjState(newmobj, newmobj->info->seestate); + + // telefrag anything in this spot + P_TeleportMove(newmobj, newmobj->x, newmobj->y, true); /* killough 8/9/98 */ + + // remove self (i.e., cube). + P_RemoveMobj(mo); +} + +void A_PlayerScream(mobj_t *mo) +{ + int sound = sfx_pldeth; // Default death sound. + if (gamemode != shareware && mo->health < -50) + sound = sfx_pdiehi; // IF THE PLAYER DIES LESS THAN -50% WITHOUT GIBBING + S_StartSound(mo, sound); +} + +/* cph - MBF-added codepointer functions */ + +// killough 11/98: kill an object +void A_Die(mobj_t *actor) +{ + P_DamageMobj(actor, NULL, NULL, actor->health); +} + +// +// A_Detonate +// killough 8/9/98: same as A_Explode, except that the damage is variable +// + +void A_Detonate(mobj_t *mo) +{ + P_RadiusAttack(mo, mo->target, mo->info->damage); +} + +// +// killough 9/98: a mushroom explosion effect, sorta :) +// Original idea: Linguica +// + +void A_Mushroom(mobj_t *actor) +{ + int i, j, n = actor->info->damage; + + A_Explode(actor); // First make normal explosion + + // Now launch mushroom cloud + for (i = -n; i <= n; i += 8) + for (j = -n; j <= n; j += 8) + { + mobj_t target = *actor, *mo; + target.x += i << FRACBITS; // Aim in many directions from source + target.y += j << FRACBITS; + target.z += P_AproxDistance(i,j) << (FRACBITS+2); // Aim up fairly high + mo = P_SpawnMissile(actor, &target, MT_FATSHOT); // Launch fireball + mo->momx >>= 1; + mo->momy >>= 1; // Slow it down a bit + mo->momz >>= 1; + mo->flags &= ~MF_NOGRAVITY; // Make debris fall under gravity + } +} + +// +// killough 11/98 +// +// The following were inspired by Len Pitre +// +// A small set of highly-sought-after code pointers +// + +void A_Spawn(mobj_t *mo) +{ + if (mo->state->misc1) + { + /* mobj_t *newmobj = */ + P_SpawnMobj(mo->x, mo->y, (mo->state->misc2 << FRACBITS) + mo->z, + mo->state->misc1 - 1); + /* CPhipps - no friendlyness (yet) + newmobj->flags = (newmobj->flags & ~MF_FRIEND) | (mo->flags & MF_FRIEND); + */ + } +} + +void A_Turn(mobj_t *mo) +{ + mo->angle += (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); +} + +void A_Face(mobj_t *mo) +{ + mo->angle = (unsigned int)(((uint_64_t) mo->state->misc1 << 32) / 360); +} + +void A_Scratch(mobj_t *mo) +{ + mo->target && (A_FaceTarget(mo), P_CheckMeleeRange(mo)) ? + mo->state->misc2 ? S_StartSound(mo, mo->state->misc2) : (void) 0, + P_DamageMobj(mo->target, mo, mo, mo->state->misc1) : (void) 0; +} + +void A_PlaySound(mobj_t *mo) +{ + S_StartSound(mo->state->misc2 ? NULL : mo, mo->state->misc1); +} + +void A_RandomJump(mobj_t *mo) +{ + if (P_Random(pr_randomjump) < mo->state->misc2) + P_SetMobjState(mo, mo->state->misc1); +} + +// +// This allows linedef effects to be activated inside deh frames. +// + +void A_LineEffect(mobj_t *mo) +{ + static line_t junk; + player_t player; + player_t *oldplayer; + junk = *lines; + oldplayer = mo->player; + mo->player = &player; + player.health = 100; + junk.special = (short)mo->state->misc1; + if (!junk.special) + return; + junk.tag = (short)mo->state->misc2; + if (!P_UseSpecialLine(mo, &junk, 0)) + P_CrossSpecialLine(&junk, 0, mo); + mo->state->misc1 = junk.special; + mo->player = oldplayer; +} diff --git a/src/p_enemy.h b/src/p_enemy.h new file mode 100644 index 00000000..3c858616 --- /dev/null +++ b/src/p_enemy.h @@ -0,0 +1,118 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Enemy thinking, AI. + * Action Pointer Functions + * that are associated with states/frames. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_ENEMY__ +#define __P_ENEMY__ + +#include "p_mobj.h" + +void P_NoiseAlert (mobj_t *target, mobj_t *emmiter); +void P_SpawnBrainTargets(void); /* killough 3/26/98: spawn icon landings */ + +extern struct brain_s { /* killough 3/26/98: global state of boss brain */ + int easy, targeton; +} brain; + +// ******************************************************************** +// Function addresses or Code Pointers +// ******************************************************************** +// These function addresses are the Code Pointers that have been +// modified for years by Dehacked enthusiasts. The new BEX format +// allows more extensive changes (see d_deh.c) + +// Doesn't work with g++, needs actionf_p1 +void A_Explode(); +void A_Pain(); +void A_PlayerScream(); +void A_Fall(); +void A_XScream(); +void A_Look(); +void A_Chase(); +void A_FaceTarget(); +void A_PosAttack(); +void A_Scream(); +void A_SPosAttack(); +void A_VileChase(); +void A_VileStart(); +void A_VileTarget(); +void A_VileAttack(); +void A_StartFire(); +void A_Fire(); +void A_FireCrackle(); +void A_Tracer(); +void A_SkelWhoosh(); +void A_SkelFist(); +void A_SkelMissile(); +void A_FatRaise(); +void A_FatAttack1(); +void A_FatAttack2(); +void A_FatAttack3(); +void A_BossDeath(); +void A_CPosAttack(); +void A_CPosRefire(); +void A_TroopAttack(); +void A_SargAttack(); +void A_HeadAttack(); +void A_BruisAttack(); +void A_SkullAttack(); +void A_Metal(); +void A_SpidRefire(); +void A_BabyMetal(); +void A_BspiAttack(); +void A_Hoof(); +void A_CyberAttack(); +void A_PainAttack(); +void A_PainDie(); +void A_KeenDie(); +void A_BrainPain(); +void A_BrainScream(); +void A_BrainDie(); +void A_BrainAwake(); +void A_BrainSpit(); +void A_SpawnSound(); +void A_SpawnFly(); +void A_BrainExplode(); +void A_Die(); +void A_Detonate(); /* killough 8/9/98: detonate a bomb or other device */ +void A_Mushroom(); /* killough 10/98: mushroom effect */ +void A_Spawn(); // killough 11/98 +void A_Turn(); // killough 11/98 +void A_Face(); // killough 11/98 +void A_Scratch(); // killough 11/98 +void A_PlaySound(); // killough 11/98 +void A_RandomJump(); // killough 11/98 +void A_LineEffect(); // killough 11/98 + +#endif // __P_ENEMY__ diff --git a/src/p_floor.c b/src/p_floor.c new file mode 100644 index 00000000..9f756b17 --- /dev/null +++ b/src/p_floor.c @@ -0,0 +1,1045 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * General plane mover and floor mover action routines + * Floor motion, pure changer types, raising stairs. donuts, elevators + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_map.h" +#include "p_spec.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" + +/////////////////////////////////////////////////////////////////////// +// +// Plane (floor or ceiling), Floor motion and Elevator action routines +// +/////////////////////////////////////////////////////////////////////// + +// +// T_MovePlane() +// +// Move a plane (floor or ceiling) and check for crushing. Called +// every tick by all actions that move floors or ceilings. +// +// Passed the sector to move a plane in, the speed to move it at, +// the dest height it is to achieve, whether it crushes obstacles, +// whether it moves a floor or ceiling, and the direction up or down +// to move. +// +// Returns a result_e: +// ok - plane moved normally, has not achieved destination yet +// pastdest - plane moved normally and is now at destination height +// crushed - plane encountered an obstacle, is holding until removed +// +result_e T_MovePlane +( sector_t* sector, + fixed_t speed, + fixed_t dest, + boolean crush, + int floorOrCeiling, + int direction ) +{ + boolean flag; + fixed_t lastpos; + fixed_t destheight; //jff 02/04/98 used to keep floors/ceilings + // from moving thru each other + + switch(floorOrCeiling) + { + case 0: + // Moving a floor + switch(direction) + { + case -1: + // Moving a floor down + if (sector->floorheight - speed < dest) + { + lastpos = sector->floorheight; + sector->floorheight = dest; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + sector->floorheight =lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + lastpos = sector->floorheight; + sector->floorheight -= speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + /* cph - make more compatible with original Doom, by + * reintroducing this code. This means floors can't lower + * if objects are stuck in the ceiling */ + if ((flag == true) && comp[comp_floors]) { + sector->floorheight = lastpos; + P_ChangeSector(sector,crush); + return crushed; + } + } + break; + + case 1: + // Moving a floor up + // jff 02/04/98 keep floor from moving thru ceilings + // jff 2/22/98 weaken check to demo_compatibility + destheight = (comp[comp_floors] || destceilingheight)? + dest : sector->ceilingheight; + if (sector->floorheight + speed > destheight) + { + lastpos = sector->floorheight; + sector->floorheight = destheight; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + sector->floorheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + // crushing is possible + lastpos = sector->floorheight; + sector->floorheight += speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + /* jff 1/25/98 fix floor crusher */ + if (comp[comp_floors]) { + if (crush == true) + return crushed; + } + sector->floorheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + return crushed; + } + } + break; + } + break; + + case 1: + // moving a ceiling + switch(direction) + { + case -1: + // moving a ceiling down + // jff 02/04/98 keep ceiling from moving thru floors + // jff 2/22/98 weaken check to demo_compatibility + destheight = (comp[comp_floors] || dest>sector->floorheight)? + dest : sector->floorheight; + if (sector->ceilingheight - speed < destheight) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = destheight; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + + if (flag == true) + { + sector->ceilingheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + // crushing is possible + lastpos = sector->ceilingheight; + sector->ceilingheight -= speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + + if (flag == true) + { + if (crush == true) + return crushed; + sector->ceilingheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + return crushed; + } + } + break; + + case 1: + // moving a ceiling up + if (sector->ceilingheight + speed > dest) + { + lastpos = sector->ceilingheight; + sector->ceilingheight = dest; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + if (flag == true) + { + sector->ceilingheight = lastpos; + P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + return pastdest; + } + else + { + lastpos = sector->ceilingheight; + sector->ceilingheight += speed; + flag = P_CheckSector(sector,crush); //jff 3/19/98 use faster chk + } + break; + } + break; + } + return ok; +} + +// +// T_MoveFloor() +// +// Move a floor to it's destination (up or down). +// Called once per tick for each moving floor. +// +// Passed a floormove_t structure that contains all pertinent info about the +// move. See P_SPEC.H for fields. +// No return. +// +// jff 02/08/98 all cases with labels beginning with gen added to support +// generalized line type behaviors. + +void T_MoveFloor(floormove_t* floor) +{ + result_e res; + + res = T_MovePlane // move the floor + ( + floor->sector, + floor->speed, + floor->floordestheight, + floor->crush, + 0, + floor->direction + ); + + if (!(leveltime&7)) // make the floormove sound + S_StartSound((mobj_t *)&floor->sector->soundorg, sfx_stnmov); + + if (res == pastdest) // if destination height is reached + { + if (floor->direction == 1) // going up + { + switch(floor->type) // handle texture/type changes + { + case donutRaise: + floor->sector->special = floor->newspecial; + floor->sector->floorpic = floor->texture; + break; + case genFloorChgT: + case genFloorChg0: + floor->sector->special = floor->newspecial; + //jff add to fix bug in special transfers from changes + floor->sector->oldspecial = floor->oldspecial; + //fall thru + case genFloorChg: + floor->sector->floorpic = floor->texture; + break; + default: + break; + } + } + else if (floor->direction == -1) // going down + { + switch(floor->type) // handle texture/type changes + { + case lowerAndChange: + floor->sector->special = floor->newspecial; + //jff add to fix bug in special transfers from changes + floor->sector->oldspecial = floor->oldspecial; + floor->sector->floorpic = floor->texture; + break; + case genFloorChgT: + case genFloorChg0: + floor->sector->special = floor->newspecial; + //jff add to fix bug in special transfers from changes + floor->sector->oldspecial = floor->oldspecial; + //fall thru + case genFloorChg: + floor->sector->floorpic = floor->texture; + break; + default: + break; + } + } + + floor->sector->floordata = NULL; //jff 2/22/98 + P_RemoveThinker(&floor->thinker);//remove this floor from list of movers + + //jff 2/26/98 implement stair retrigger lockout while still building + // note this only applies to the retriggerable generalized stairs + + if (floor->sector->stairlock==-2) // if this sector is stairlocked + { + sector_t *sec = floor->sector; + sec->stairlock=-1; // thinker done, promote lock to -1 + + while (sec->prevsec!=-1 && sectors[sec->prevsec].stairlock!=-2) + sec = §ors[sec->prevsec]; // search for a non-done thinker + if (sec->prevsec==-1) // if all thinkers previous are done + { + sec = floor->sector; // search forward + while (sec->nextsec!=-1 && sectors[sec->nextsec].stairlock!=-2) + sec = §ors[sec->nextsec]; + if (sec->nextsec==-1) // if all thinkers ahead are done too + { + while (sec->prevsec!=-1) // clear all locks + { + sec->stairlock = 0; + sec = §ors[sec->prevsec]; + } + sec->stairlock = 0; + } + } + } + + // make floor stop sound + S_StartSound((mobj_t *)&floor->sector->soundorg, sfx_pstop); + } +} + +// +// T_MoveElevator() +// +// Move an elevator to it's destination (up or down) +// Called once per tick for each moving floor. +// +// Passed an elevator_t structure that contains all pertinent info about the +// move. See P_SPEC.H for fields. +// No return. +// +// jff 02/22/98 added to support parallel floor/ceiling motion +// +void T_MoveElevator(elevator_t* elevator) +{ + result_e res; + + if (elevator->direction<0) // moving down + { + res = T_MovePlane //jff 4/7/98 reverse order of ceiling/floor + ( + elevator->sector, + elevator->speed, + elevator->ceilingdestheight, + 0, + 1, // move floor + elevator->direction + ); + if (res==ok || res==pastdest) // jff 4/7/98 don't move ceil if blocked + T_MovePlane + ( + elevator->sector, + elevator->speed, + elevator->floordestheight, + 0, + 0, // move ceiling + elevator->direction + ); + } + else // up + { + res = T_MovePlane //jff 4/7/98 reverse order of ceiling/floor + ( + elevator->sector, + elevator->speed, + elevator->floordestheight, + 0, + 0, // move ceiling + elevator->direction + ); + if (res==ok || res==pastdest) // jff 4/7/98 don't move floor if blocked + T_MovePlane + ( + elevator->sector, + elevator->speed, + elevator->ceilingdestheight, + 0, + 1, // move floor + elevator->direction + ); + } + + // make floor move sound + if (!(leveltime&7)) + S_StartSound((mobj_t *)&elevator->sector->soundorg, sfx_stnmov); + + if (res == pastdest) // if destination height acheived + { + elevator->sector->floordata = NULL; //jff 2/22/98 + elevator->sector->ceilingdata = NULL; //jff 2/22/98 + P_RemoveThinker(&elevator->thinker); // remove elevator from actives + + // make floor stop sound + S_StartSound((mobj_t *)&elevator->sector->soundorg, sfx_pstop); + } +} + +/////////////////////////////////////////////////////////////////////// +// +// Floor motion linedef handlers +// +/////////////////////////////////////////////////////////////////////// + +// +// EV_DoFloor() +// +// Handle regular and extended floor types +// +// Passed the line that activated the floor and the type of floor motion +// Returns true if a thinker was created. +// +int EV_DoFloor +( line_t* line, + floor_e floortype ) +{ + int secnum; + int rtn; + int i; + sector_t* sec; + floormove_t* floor; + + secnum = -1; + rtn = 0; + // move all floors with the same tag as the linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + // Don't start a second thinker on the same floor + if (P_SectorActive(floor_special,sec)) //jff 2/23/98 + continue; + + // new floor thinker + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->type = floortype; + floor->crush = false; + + // setup the thinker according to the linedef type + switch(floortype) + { + case lowerFloor: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindHighestFloorSurrounding(sec); + break; + + //jff 02/03/30 support lowering floor by 24 absolute + case lowerFloor24: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; + break; + + //jff 02/03/30 support lowering floor by 32 absolute (fast) + case lowerFloor32Turbo: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED*4; + floor->floordestheight = floor->sector->floorheight + 32 * FRACUNIT; + break; + + case lowerFloorToLowest: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindLowestFloorSurrounding(sec); + break; + + //jff 02/03/30 support lowering floor to next lowest floor + case lowerFloorToNearest: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = + P_FindNextLowestFloor(sec,floor->sector->floorheight); + break; + + case turboLower: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED * 4; + floor->floordestheight = P_FindHighestFloorSurrounding(sec); + if (floor->floordestheight != sec->floorheight) + floor->floordestheight += 8*FRACUNIT; + break; + + case raiseFloorCrush: + floor->crush = true; + case raiseFloor: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindLowestCeilingSurrounding(sec); + if (floor->floordestheight > sec->ceilingheight) + floor->floordestheight = sec->ceilingheight; + floor->floordestheight -= (8*FRACUNIT)*(floortype == raiseFloorCrush); + break; + + case raiseFloorTurbo: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED*4; + floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); + break; + + case raiseFloorToNearest: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindNextHighestFloor(sec,sec->floorheight); + break; + + case raiseFloor24: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; + break; + + // jff 2/03/30 support straight raise by 32 (fast) + case raiseFloor32Turbo: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED*4; + floor->floordestheight = floor->sector->floorheight + 32 * FRACUNIT; + break; + + case raiseFloor512: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 512 * FRACUNIT; + break; + + case raiseFloor24AndChange: + floor->direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = floor->sector->floorheight + 24 * FRACUNIT; + sec->floorpic = line->frontsector->floorpic; + sec->special = line->frontsector->special; + //jff 3/14/98 transfer both old and new special + sec->oldspecial = line->frontsector->oldspecial; + break; + + case raiseToTexture: + { + int minsize = INT_MAX; + side_t* side; + + /* jff 3/13/98 no ovf */ + if (!comp[comp_model]) minsize = 32000<direction = 1; + floor->sector = sec; + floor->speed = FLOORSPEED; + for (i = 0; i < sec->linecount; i++) + { + if (twoSided (secnum, i) ) + { + side = getSide(secnum,i,0); + // jff 8/14/98 don't scan texture 0, its not real + if (side->bottomtexture > 0 || + (comp[comp_model] && !side->bottomtexture)) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + side = getSide(secnum,i,1); + // jff 8/14/98 don't scan texture 0, its not real + if (side->bottomtexture > 0 || + (comp[comp_model] && !side->bottomtexture)) + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + } + } + if (comp[comp_model]) + floor->floordestheight = floor->sector->floorheight + minsize; + else + { + floor->floordestheight = + (floor->sector->floorheight>>FRACBITS) + (minsize>>FRACBITS); + if (floor->floordestheight>32000) + floor->floordestheight = 32000; //jff 3/13/98 do not + floor->floordestheight<<=FRACBITS; // allow height overflow + } + } + break; + + case lowerAndChange: + floor->direction = -1; + floor->sector = sec; + floor->speed = FLOORSPEED; + floor->floordestheight = P_FindLowestFloorSurrounding(sec); + floor->texture = sec->floorpic; + + // jff 1/24/98 make sure floor->newspecial gets initialized + // in case no surrounding sector is at floordestheight + // --> should not affect compatibility <-- + floor->newspecial = sec->special; + //jff 3/14/98 transfer both old and new special + floor->oldspecial = sec->oldspecial; + + //jff 5/23/98 use model subroutine to unify fixes and handling + sec = P_FindModelFloorSector(floor->floordestheight,sec-sectors); + if (sec) + { + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + //jff 3/14/98 transfer both old and new special + floor->oldspecial = sec->oldspecial; + } + break; + default: + break; + } + } + return rtn; +} + +// +// EV_DoChange() +// +// Handle pure change types. These change floor texture and sector type +// by trigger or numeric model without moving the floor. +// +// The linedef causing the change and the type of change is passed +// Returns true if any sector changes +// +// jff 3/15/98 added to better support generalized sector types +// +int EV_DoChange +( line_t* line, + change_e changetype ) +{ + int secnum; + int rtn; + sector_t* sec; + sector_t* secm; + + secnum = -1; + rtn = 0; + // change all sectors with the same tag as the linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + rtn = 1; + + // handle trigger or numeric change type + switch(changetype) + { + case trigChangeOnly: + sec->floorpic = line->frontsector->floorpic; + sec->special = line->frontsector->special; + sec->oldspecial = line->frontsector->oldspecial; + break; + case numChangeOnly: + secm = P_FindModelFloorSector(sec->floorheight,secnum); + if (secm) // if no model, no change + { + sec->floorpic = secm->floorpic; + sec->special = secm->special; + sec->oldspecial = secm->oldspecial; + } + break; + default: + break; + } + } + return rtn; +} + +/* + * EV_BuildStairs() + * + * Handles staircase building. A sequence of sectors chosen by algorithm + * rise at a speed indicated to a height that increases by the stepsize + * each step. + * + * Passed the linedef triggering the stairs and the type of stair rise + * Returns true if any thinkers are created + * + * cph 2001/09/21 - compatibility nightmares again + * There are three different ways this function has, during its history, stepped + * through all the stairs to be triggered by the single switch + * - original Doom used a linear P_FindSectorFromLineTag, but failed to preserve + * the index of the previous sector found, so instead it would restart its + * linear search from the last sector of the previous staircase + * - MBF/PrBoom with comp_stairs fail to emulate this, because their + * P_FindSectorFromLineTag is a chained hash table implementation. Instead they + * start following the hash chain from the last sector of the previous + * staircase, which will (probably) have the wrong tag, so they miss any further + * stairs + * - Boom fixed the bug, and MBF/PrBoom without comp_stairs work right + */ +static inline int P_FindSectorFromLineTagWithLowerBound +(line_t* l, int start, int min) +{ + /* Emulate original Doom's linear lower-bounded P_FindSectorFromLineTag + * as needed */ + do { + start = P_FindSectorFromLineTag(l,start); + } while (start >= 0 && start <= min); + return start; +} + +int EV_BuildStairs +( line_t* line, + stair_e type ) +{ + /* cph 2001/09/22 - cleaned up this function to save my sanity. A separate + * outer loop index makes the logic much cleared, and local variables moved + * into the inner blocks helps too */ + int ssec = -1; + int minssec = -1; + int rtn = 0; + + // start a stair at each sector tagged the same as the linedef + while ((ssec = P_FindSectorFromLineTagWithLowerBound(line,ssec,minssec)) >= 0) + { + int secnum = ssec; + sector_t* sec = §ors[secnum]; + + // don't start a stair if the first step's floor is already moving + if (!P_SectorActive(floor_special,sec)) { //jff 2/22/98 + floormove_t* floor; + int texture, height; + fixed_t stairsize; + fixed_t speed; + int ok; + + // create new floor thinker for first step + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + floor->type = buildStair; //jff 3/31/98 do not leave uninited + + // set up the speed and stepsize according to the stairs type + switch(type) + { + default: // killough -- prevent compiler warning + case build8: + speed = FLOORSPEED/4; + stairsize = 8*FRACUNIT; + if (!demo_compatibility) + floor->crush = false; //jff 2/27/98 fix uninitialized crush field + break; + case turbo16: + speed = FLOORSPEED*4; + stairsize = 16*FRACUNIT; + if (!demo_compatibility) + floor->crush = true; //jff 2/27/98 fix uninitialized crush field + break; + } + floor->speed = speed; + height = sec->floorheight + stairsize; + floor->floordestheight = height; + + texture = sec->floorpic; + + // Find next sector to raise + // 1. Find 2-sided line with same sector side[0] (lowest numbered) + // 2. Other side is the next sector to raise + // 3. Unless already moving, or different texture, then stop building + do + { + int i; + ok = 0; + + for (i = 0;i < sec->linecount;i++) + { + sector_t* tsec = (sec->lines[i])->frontsector; + int newsecnum; + if ( !((sec->lines[i])->flags & ML_TWOSIDED) ) + continue; + + newsecnum = tsec-sectors; + + if (secnum != newsecnum) + continue; + + tsec = (sec->lines[i])->backsector; + if (!tsec) continue; //jff 5/7/98 if no backside, continue + newsecnum = tsec - sectors; + + // if sector's floor is different texture, look for another + if (tsec->floorpic != texture) + continue; + + /* jff 6/19/98 prevent double stepsize + * killough 10/98: intentionally left this way [MBF comment] + * cph 2001/02/06: stair bug fix should be controlled by comp_stairs, + * except if we're emulating MBF which perversly reverted the fix + */ + if (comp[comp_stairs] || (compatibility_level == mbf_compatibility)) + height += stairsize; // jff 6/28/98 change demo compatibility + + // if sector's floor already moving, look for another + if (P_SectorActive(floor_special,tsec)) //jff 2/22/98 + continue; + + /* cph - see comment above - do this iff we didn't do so above */ + if (!comp[comp_stairs] && (compatibility_level != mbf_compatibility)) + height += stairsize; + + sec = tsec; + secnum = newsecnum; + + // create and initialize a thinker for the next step + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + + sec->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->direction = 1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + floor->type = buildStair; //jff 3/31/98 do not leave uninited + //jff 2/27/98 fix uninitialized crush field + if (!demo_compatibility) + floor->crush = type==build8? false : true; + ok = 1; + break; + } + } while(ok); // continue until no next step is found + + } + /* killough 10/98: compatibility option */ + if (comp[comp_stairs]) { + /* cph 2001/09/22 - emulate buggy MBF comp_stairs for demos, with logic + * reversed since we now have a separate outer loop index. + * DEMOSYNC - what about boom_compatibility_compatibility? + */ + if ((compatibility_level >= mbf_compatibility) && (compatibility_level < + prboom_3_compatibility)) ssec = secnum; /* Trash outer loop index */ + else { + /* cph 2001/09/22 - now the correct comp_stairs - Doom used a linear + * search from the last secnum, so we set that as a minimum value and do + * a fresh tag search + */ + ssec = -1; minssec = secnum; + } + } + } + return rtn; +} + +// +// EV_DoDonut() +// +// Handle donut function: lower pillar, raise surrounding pool, both to height, +// texture and type of the sector surrounding the pool. +// +// Passed the linedef that triggered the donut +// Returns whether a thinker was created +// +int EV_DoDonut(line_t* line) +{ + sector_t* s1; + sector_t* s2; + sector_t* s3; + int secnum; + int rtn; + int i; + floormove_t* floor; + + secnum = -1; + rtn = 0; + // do function on all sectors with same tag as linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + s1 = §ors[secnum]; // s1 is pillar's sector + + // do not start the donut if the pillar is already moving + if (P_SectorActive(floor_special,s1)) //jff 2/22/98 + continue; + + s2 = getNextSector(s1->lines[0],s1); // s2 is pool's sector + if (!s2) continue; // note lowest numbered line around + // pillar must be two-sided + + /* do not start the donut if the pool is already moving + * cph - DEMOSYNC - was !compatibility */ + if (!comp[comp_floors] && P_SectorActive(floor_special,s2)) + continue; //jff 5/7/98 + + // find a two sided line around the pool whose other side isn't the pillar + for (i = 0;i < s2->linecount;i++) + { + //jff 3/29/98 use true two-sidedness, not the flag + // killough 4/5/98: changed demo_compatibility to compatibility + if (comp[comp_model]) + { + // original code: !s2->lines[i]->flags & ML_TWOSIDED + // equivalent to: (!s2->lines[i]->flags) & ML_TWOSIDED , i.e. 0 + // should be: !(s2->lines[i]->flags & ML_TWOSIDED) + if (((!s2->lines[i]->flags) & ML_TWOSIDED) || + (s2->lines[i]->backsector == s1)) + continue; + } + else if (!s2->lines[i]->backsector || s2->lines[i]->backsector == s1) + continue; + + rtn = 1; //jff 1/26/98 no donut action - no switch change on return + + s3 = s2->lines[i]->backsector; // s3 is model sector for changes + + // Spawn rising slime + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + s2->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->type = donutRaise; + floor->crush = false; + floor->direction = 1; + floor->sector = s2; + floor->speed = FLOORSPEED / 2; + floor->texture = s3->floorpic; + floor->newspecial = 0; + floor->floordestheight = s3->floorheight; + + // Spawn lowering donut-hole pillar + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + s1->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + floor->type = lowerFloor; + floor->crush = false; + floor->direction = -1; + floor->sector = s1; + floor->speed = FLOORSPEED / 2; + floor->floordestheight = s3->floorheight; + break; + } + } + return rtn; +} + +// +// EV_DoElevator +// +// Handle elevator linedef types +// +// Passed the linedef that triggered the elevator and the elevator action +// +// jff 2/22/98 new type to move floor and ceiling in parallel +// +int EV_DoElevator +( line_t* line, + elevator_e elevtype ) +{ + int secnum; + int rtn; + sector_t* sec; + elevator_t* elevator; + + secnum = -1; + rtn = 0; + // act on all sectors with the same tag as the triggering linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + // If either floor or ceiling is already activated, skip it + if (sec->floordata || sec->ceilingdata) //jff 2/22/98 + continue; + + // create and initialize new elevator thinker + rtn = 1; + elevator = Z_Malloc (sizeof(*elevator), PU_LEVSPEC, 0); + memset(elevator, 0, sizeof(*elevator)); + P_AddThinker (&elevator->thinker); + sec->floordata = elevator; //jff 2/22/98 + sec->ceilingdata = elevator; //jff 2/22/98 + elevator->thinker.function = T_MoveElevator; + elevator->type = elevtype; + + // set up the fields according to the type of elevator action + switch(elevtype) + { + // elevator down to next floor + case elevateDown: + elevator->direction = -1; + elevator->sector = sec; + elevator->speed = ELEVATORSPEED; + elevator->floordestheight = + P_FindNextLowestFloor(sec,sec->floorheight); + elevator->ceilingdestheight = + elevator->floordestheight + sec->ceilingheight - sec->floorheight; + break; + + // elevator up to next floor + case elevateUp: + elevator->direction = 1; + elevator->sector = sec; + elevator->speed = ELEVATORSPEED; + elevator->floordestheight = + P_FindNextHighestFloor(sec,sec->floorheight); + elevator->ceilingdestheight = + elevator->floordestheight + sec->ceilingheight - sec->floorheight; + break; + + // elevator to floor height of activating switch's front sector + case elevateCurrent: + elevator->sector = sec; + elevator->speed = ELEVATORSPEED; + elevator->floordestheight = line->frontsector->floorheight; + elevator->ceilingdestheight = + elevator->floordestheight + sec->ceilingheight - sec->floorheight; + elevator->direction = + elevator->floordestheight>sec->floorheight? 1 : -1; + break; + + default: + break; + } + } + return rtn; +} diff --git a/src/p_genlin.c b/src/p_genlin.c new file mode 100644 index 00000000..873b25bf --- /dev/null +++ b/src/p_genlin.c @@ -0,0 +1,1164 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Generalized linedef type handlers + * Floors, Ceilings, Doors, Locked Doors, Lifts, Stairs, Crushers + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" //jff 6/19/98 for demo_compatibility +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" +#include "m_random.h" +#include "s_sound.h" +#include "sounds.h" + +////////////////////////////////////////////////////////// +// +// Generalized Linedef Type handlers +// +////////////////////////////////////////////////////////// + +// +// EV_DoGenFloor() +// +// Handle generalized floor types +// +// Passed the line activating the generalized floor function +// Returns true if a thinker is created +// +// jff 02/04/98 Added this routine (and file) to handle generalized +// floor movers using bit fields in the line special type. +// +int EV_DoGenFloor +( line_t* line ) +{ + int secnum; + int rtn; + boolean manual; + sector_t* sec; + floormove_t* floor; + unsigned value = (unsigned)line->special - GenFloorBase; + + // parse the bit fields in the line's special type + + int Crsh = (value & FloorCrush) >> FloorCrushShift; + int ChgT = (value & FloorChange) >> FloorChangeShift; + int Targ = (value & FloorTarget) >> FloorTargetShift; + int Dirn = (value & FloorDirection) >> FloorDirectionShift; + int ChgM = (value & FloorModel) >> FloorModelShift; + int Sped = (value & FloorSpeed) >> FloorSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_floor; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_floor: + // Do not start another function if floor already moving + if (P_SectorActive(floor_special,sec)) + { + if (!manual) + continue; + else + return rtn; + } + + // new floor thinker + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->crush = Crsh; + floor->direction = Dirn? 1 : -1; + floor->sector = sec; + floor->texture = sec->floorpic; + floor->newspecial = sec->special; + //jff 3/14/98 transfer old special field too + floor->oldspecial = sec->oldspecial; + floor->type = genFloor; + + // set the speed of motion + switch (Sped) + { + case SpeedSlow: + floor->speed = FLOORSPEED; + break; + case SpeedNormal: + floor->speed = FLOORSPEED*2; + break; + case SpeedFast: + floor->speed = FLOORSPEED*4; + break; + case SpeedTurbo: + floor->speed = FLOORSPEED*8; + break; + default: + break; + } + + // set the destination height + switch(Targ) + { + case FtoHnF: + floor->floordestheight = P_FindHighestFloorSurrounding(sec); + break; + case FtoLnF: + floor->floordestheight = P_FindLowestFloorSurrounding(sec); + break; + case FtoNnF: + floor->floordestheight = Dirn? + P_FindNextHighestFloor(sec,sec->floorheight) : + P_FindNextLowestFloor(sec,sec->floorheight); + break; + case FtoLnC: + floor->floordestheight = P_FindLowestCeilingSurrounding(sec); + break; + case FtoC: + floor->floordestheight = sec->ceilingheight; + break; + case FbyST: + floor->floordestheight = (floor->sector->floorheight>>FRACBITS) + + floor->direction * (P_FindShortestTextureAround(secnum)>>FRACBITS); + if (floor->floordestheight>32000) //jff 3/13/98 prevent overflow + floor->floordestheight=32000; // wraparound in floor height + if (floor->floordestheight<-32000) + floor->floordestheight=-32000; + floor->floordestheight<<=FRACBITS; + break; + case Fby24: + floor->floordestheight = floor->sector->floorheight + + floor->direction * 24*FRACUNIT; + break; + case Fby32: + floor->floordestheight = floor->sector->floorheight + + floor->direction * 32*FRACUNIT; + break; + default: + break; + } + + // set texture/type change properties + if (ChgT) // if a texture change is indicated + { + if (ChgM) // if a numeric model change + { + sector_t *sec; + + //jff 5/23/98 find model with ceiling at target height if target + //is a ceiling type + sec = (Targ==FtoLnC || Targ==FtoC)? + P_FindModelCeilingSector(floor->floordestheight,secnum) : + P_FindModelFloorSector(floor->floordestheight,secnum); + if (sec) + { + floor->texture = sec->floorpic; + switch(ChgT) + { + case FChgZero: // zero type + floor->newspecial = 0; + //jff 3/14/98 change old field too + floor->oldspecial = 0; + floor->type = genFloorChg0; + break; + case FChgTyp: // copy type + floor->newspecial = sec->special; + //jff 3/14/98 change old field too + floor->oldspecial = sec->oldspecial; + floor->type = genFloorChgT; + break; + case FChgTxt: // leave type be + floor->type = genFloorChg; + break; + default: + break; + } + } + } + else // else if a trigger model change + { + floor->texture = line->frontsector->floorpic; + switch (ChgT) + { + case FChgZero: // zero type + floor->newspecial = 0; + //jff 3/14/98 change old field too + floor->oldspecial = 0; + floor->type = genFloorChg0; + break; + case FChgTyp: // copy type + floor->newspecial = line->frontsector->special; + //jff 3/14/98 change old field too + floor->oldspecial = line->frontsector->oldspecial; + floor->type = genFloorChgT; + break; + case FChgTxt: // leave type be + floor->type = genFloorChg; + default: + break; + } + } + } + if (manual) return rtn; + } + return rtn; +} + + +// +// EV_DoGenCeiling() +// +// Handle generalized ceiling types +// +// Passed the linedef activating the ceiling function +// Returns true if a thinker created +// +// jff 02/04/98 Added this routine (and file) to handle generalized +// floor movers using bit fields in the line special type. +// +int EV_DoGenCeiling +( line_t* line ) +{ + int secnum; + int rtn; + boolean manual; + fixed_t targheight; + sector_t* sec; + ceiling_t* ceiling; + unsigned value = (unsigned)line->special - GenCeilingBase; + + // parse the bit fields in the line's special type + + int Crsh = (value & CeilingCrush) >> CeilingCrushShift; + int ChgT = (value & CeilingChange) >> CeilingChangeShift; + int Targ = (value & CeilingTarget) >> CeilingTargetShift; + int Dirn = (value & CeilingDirection) >> CeilingDirectionShift; + int ChgM = (value & CeilingModel) >> CeilingModelShift; + int Sped = (value & CeilingSpeed) >> CeilingSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_ceiling; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_ceiling: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new ceiling thinker + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + memset(ceiling, 0, sizeof(*ceiling)); + P_AddThinker (&ceiling->thinker); + sec->ceilingdata = ceiling; //jff 2/22/98 + ceiling->thinker.function = T_MoveCeiling; + ceiling->crush = Crsh; + ceiling->direction = Dirn? 1 : -1; + ceiling->sector = sec; + ceiling->texture = sec->ceilingpic; + ceiling->newspecial = sec->special; + //jff 3/14/98 change old field too + ceiling->oldspecial = sec->oldspecial; + ceiling->tag = sec->tag; + ceiling->type = genCeiling; + + // set speed of motion + switch (Sped) + { + case SpeedSlow: + ceiling->speed = CEILSPEED; + break; + case SpeedNormal: + ceiling->speed = CEILSPEED*2; + break; + case SpeedFast: + ceiling->speed = CEILSPEED*4; + break; + case SpeedTurbo: + ceiling->speed = CEILSPEED*8; + break; + default: + break; + } + + // set destination target height + targheight = sec->ceilingheight; + switch(Targ) + { + case CtoHnC: + targheight = P_FindHighestCeilingSurrounding(sec); + break; + case CtoLnC: + targheight = P_FindLowestCeilingSurrounding(sec); + break; + case CtoNnC: + targheight = Dirn? + P_FindNextHighestCeiling(sec,sec->ceilingheight) : + P_FindNextLowestCeiling(sec,sec->ceilingheight); + break; + case CtoHnF: + targheight = P_FindHighestFloorSurrounding(sec); + break; + case CtoF: + targheight = sec->floorheight; + break; + case CbyST: + targheight = (ceiling->sector->ceilingheight>>FRACBITS) + + ceiling->direction * (P_FindShortestUpperAround(secnum)>>FRACBITS); + if (targheight>32000) //jff 3/13/98 prevent overflow + targheight=32000; // wraparound in ceiling height + if (targheight<-32000) + targheight=-32000; + targheight<<=FRACBITS; + break; + case Cby24: + targheight = ceiling->sector->ceilingheight + + ceiling->direction * 24*FRACUNIT; + break; + case Cby32: + targheight = ceiling->sector->ceilingheight + + ceiling->direction * 32*FRACUNIT; + break; + default: + break; + } + if (Dirn) ceiling->topheight = targheight; + else ceiling->bottomheight = targheight; + + // set texture/type change properties + if (ChgT) // if a texture change is indicated + { + if (ChgM) // if a numeric model change + { + sector_t *sec; + + //jff 5/23/98 find model with floor at target height if target + //is a floor type + sec = (Targ==CtoHnF || Targ==CtoF)? + P_FindModelFloorSector(targheight,secnum) : + P_FindModelCeilingSector(targheight,secnum); + if (sec) + { + ceiling->texture = sec->ceilingpic; + switch (ChgT) + { + case CChgZero: // type is zeroed + ceiling->newspecial = 0; + //jff 3/14/98 change old field too + ceiling->oldspecial = 0; + ceiling->type = genCeilingChg0; + break; + case CChgTyp: // type is copied + ceiling->newspecial = sec->special; + //jff 3/14/98 change old field too + ceiling->oldspecial = sec->oldspecial; + ceiling->type = genCeilingChgT; + break; + case CChgTxt: // type is left alone + ceiling->type = genCeilingChg; + break; + default: + break; + } + } + } + else // else if a trigger model change + { + ceiling->texture = line->frontsector->ceilingpic; + switch (ChgT) + { + case CChgZero: // type is zeroed + ceiling->newspecial = 0; + //jff 3/14/98 change old field too + ceiling->oldspecial = 0; + ceiling->type = genCeilingChg0; + break; + case CChgTyp: // type is copied + ceiling->newspecial = line->frontsector->special; + //jff 3/14/98 change old field too + ceiling->oldspecial = line->frontsector->oldspecial; + ceiling->type = genCeilingChgT; + break; + case CChgTxt: // type is left alone + ceiling->type = genCeilingChg; + break; + default: + break; + } + } + } + P_AddActiveCeiling(ceiling); // add this ceiling to the active list + if (manual) return rtn; + } + return rtn; +} + +// +// EV_DoGenLift() +// +// Handle generalized lift types +// +// Passed the linedef activating the lift +// Returns true if a thinker is created +// +int EV_DoGenLift +( line_t* line ) +{ + plat_t* plat; + int secnum; + int rtn; + boolean manual; + sector_t* sec; + unsigned value = (unsigned)line->special - GenLiftBase; + + // parse the bit fields in the line's special type + + int Targ = (value & LiftTarget) >> LiftTargetShift; + int Dely = (value & LiftDelay) >> LiftDelayShift; + int Sped = (value & LiftSpeed) >> LiftSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + secnum = -1; + rtn = 0; + + // Activate all plats that are in_stasis + + if (Targ==LnF2HnF) + P_ActivateInStasis(line->tag); + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_lift; + } + + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_lift: + // Do not start another function if floor already moving + if (P_SectorActive(floor_special,sec)) + { + if (!manual) + continue; + else + return rtn; + } + + // Setup the plat thinker + rtn = 1; + plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); + memset(plat, 0, sizeof(*plat)); + P_AddThinker(&plat->thinker); + + plat->sector = sec; + plat->sector->floordata = plat; + plat->thinker.function = T_PlatRaise; + plat->crush = false; + plat->tag = line->tag; + + plat->type = genLift; + plat->high = sec->floorheight; + plat->status = down; + + // setup the target destination height + switch(Targ) + { + case F2LnF: + plat->low = P_FindLowestFloorSurrounding(sec); + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + break; + case F2NnF: + plat->low = P_FindNextLowestFloor(sec,sec->floorheight); + break; + case F2LnC: + plat->low = P_FindLowestCeilingSurrounding(sec); + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + break; + case LnF2HnF: + plat->type = genPerpetual; + plat->low = P_FindLowestFloorSurrounding(sec); + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + plat->high = P_FindHighestFloorSurrounding(sec); + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + plat->status = P_Random(pr_genlift)&1; + break; + default: + break; + } + + // setup the speed of motion + switch(Sped) + { + case SpeedSlow: + plat->speed = PLATSPEED * 2; + break; + case SpeedNormal: + plat->speed = PLATSPEED * 4; + break; + case SpeedFast: + plat->speed = PLATSPEED * 8; + break; + case SpeedTurbo: + plat->speed = PLATSPEED * 16; + break; + default: + break; + } + + // setup the delay time before the floor returns + switch(Dely) + { + case 0: + plat->wait = 1*35; + break; + case 1: + plat->wait = PLATWAIT*35; + break; + case 2: + plat->wait = 5*35; + break; + case 3: + plat->wait = 10*35; + break; + } + + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + P_AddActivePlat(plat); // add this plat to the list of active plats + + if (manual) + return rtn; + } + return rtn; +} + +// +// EV_DoGenStairs() +// +// Handle generalized stair building +// +// Passed the linedef activating the stairs +// Returns true if a thinker is created +// +int EV_DoGenStairs +( line_t* line ) +{ + int secnum; + int osecnum; //jff 3/4/98 preserve loop index + int height; + int i; + int newsecnum; + int texture; + int ok; + int rtn; + boolean manual; + + sector_t* sec; + sector_t* tsec; + + floormove_t* floor; + + fixed_t stairsize; + fixed_t speed; + + unsigned value = (unsigned)line->special - GenStairsBase; + + // parse the bit fields in the line's special type + + int Igno = (value & StairIgnore) >> StairIgnoreShift; + int Dirn = (value & StairDirection) >> StairDirectionShift; + int Step = (value & StairStep) >> StairStepShift; + int Sped = (value & StairSpeed) >> StairSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_stair; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_stair: + //Do not start another function if floor already moving + //jff 2/26/98 add special lockout condition to wait for entire + //staircase to build before retriggering + if (P_SectorActive(floor_special,sec) || sec->stairlock) + { + if (!manual) + continue; + else + return rtn; + } + + // new floor thinker + rtn = 1; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->direction = Dirn? 1 : -1; + floor->sector = sec; + + // setup speed of stair building + switch(Sped) + { + default: + case SpeedSlow: + floor->speed = FLOORSPEED/4; + break; + case SpeedNormal: + floor->speed = FLOORSPEED/2; + break; + case SpeedFast: + floor->speed = FLOORSPEED*2; + break; + case SpeedTurbo: + floor->speed = FLOORSPEED*4; + break; + } + + // setup stepsize for stairs + switch(Step) + { + default: + case 0: + stairsize = 4*FRACUNIT; + break; + case 1: + stairsize = 8*FRACUNIT; + break; + case 2: + stairsize = 16*FRACUNIT; + break; + case 3: + stairsize = 24*FRACUNIT; + break; + } + + speed = floor->speed; + height = sec->floorheight + floor->direction * stairsize; + floor->floordestheight = height; + texture = sec->floorpic; + floor->crush = false; + floor->type = genBuildStair; // jff 3/31/98 do not leave uninited + + sec->stairlock = -2; // jff 2/26/98 set up lock on current sector + sec->nextsec = -1; + sec->prevsec = -1; + + osecnum = secnum; //jff 3/4/98 preserve loop index + // Find next sector to raise + // 1. Find 2-sided line with same sector side[0] + // 2. Other side is the next sector to raise + do + { + ok = 0; + for (i = 0;i < sec->linecount;i++) + { + if ( !((sec->lines[i])->backsector) ) + continue; + + tsec = (sec->lines[i])->frontsector; + newsecnum = tsec-sectors; + + if (secnum != newsecnum) + continue; + + tsec = (sec->lines[i])->backsector; + newsecnum = tsec - sectors; + + if (!Igno && tsec->floorpic != texture) + continue; + + /* jff 6/19/98 prevent double stepsize */ + if (compatibility_level < boom_202_compatibility) + height += floor->direction * stairsize; + + //jff 2/26/98 special lockout condition for retriggering + if (P_SectorActive(floor_special,tsec) || tsec->stairlock) + continue; + + /* jff 6/19/98 increase height AFTER continue */ + if (compatibility_level >= boom_202_compatibility) + height += floor->direction * stairsize; + + // jff 2/26/98 + // link the stair chain in both directions + // lock the stair sector until building complete + sec->nextsec = newsecnum; // link step to next + tsec->prevsec = secnum; // link next back + tsec->nextsec = -1; // set next forward link as end + tsec->stairlock = -2; // lock the step + + sec = tsec; + secnum = newsecnum; + floor = Z_Malloc (sizeof(*floor), PU_LEVSPEC, 0); + + memset(floor, 0, sizeof(*floor)); + P_AddThinker (&floor->thinker); + + sec->floordata = floor; + floor->thinker.function = T_MoveFloor; + floor->direction = Dirn? 1 : -1; + floor->sector = sec; + floor->speed = speed; + floor->floordestheight = height; + floor->crush = false; + floor->type = genBuildStair; // jff 3/31/98 do not leave uninited + + ok = 1; + break; + } + } while(ok); + if (manual) + return rtn; + secnum = osecnum; //jff 3/4/98 restore old loop index + } + // retriggerable generalized stairs build up or down alternately + if (rtn) + line->special ^= StairDirection; // alternate dir on succ activations + return rtn; +} + +// +// EV_DoGenCrusher() +// +// Handle generalized crusher types +// +// Passed the linedef activating the crusher +// Returns true if a thinker created +// +int EV_DoGenCrusher +( line_t* line ) +{ + int secnum; + int rtn; + boolean manual; + sector_t* sec; + ceiling_t* ceiling; + unsigned value = (unsigned)line->special - GenCrusherBase; + + // parse the bit fields in the line's special type + + int Slnt = (value & CrusherSilent) >> CrusherSilentShift; + int Sped = (value & CrusherSpeed) >> CrusherSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + //jff 2/22/98 Reactivate in-stasis ceilings...for certain types. + //jff 4/5/98 return if activated + rtn = P_ActivateInStasisCeiling(line); + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_crusher; + } + + secnum = -1; + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + +manual_crusher: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new ceiling thinker + rtn = 1; + ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVSPEC, 0); + memset(ceiling, 0, sizeof(*ceiling)); + P_AddThinker (&ceiling->thinker); + sec->ceilingdata = ceiling; //jff 2/22/98 + ceiling->thinker.function = T_MoveCeiling; + ceiling->crush = true; + ceiling->direction = -1; + ceiling->sector = sec; + ceiling->texture = sec->ceilingpic; + ceiling->newspecial = sec->special; + ceiling->tag = sec->tag; + ceiling->type = Slnt? genSilentCrusher : genCrusher; + ceiling->topheight = sec->ceilingheight; + ceiling->bottomheight = sec->floorheight + (8*FRACUNIT); + + // setup ceiling motion speed + switch (Sped) + { + case SpeedSlow: + ceiling->speed = CEILSPEED; + break; + case SpeedNormal: + ceiling->speed = CEILSPEED*2; + break; + case SpeedFast: + ceiling->speed = CEILSPEED*4; + break; + case SpeedTurbo: + ceiling->speed = CEILSPEED*8; + break; + default: + break; + } + ceiling->oldspeed=ceiling->speed; + + P_AddActiveCeiling(ceiling); // add to list of active ceilings + if (manual) return rtn; + } + return rtn; +} + +// +// EV_DoGenLockedDoor() +// +// Handle generalized locked door types +// +// Passed the linedef activating the generalized locked door +// Returns true if a thinker created +// +int EV_DoGenLockedDoor +( line_t* line ) +{ + int secnum,rtn; + sector_t* sec; + vldoor_t* door; + boolean manual; + unsigned value = (unsigned)line->special - GenLockedBase; + + // parse the bit fields in the line's special type + + int Kind = (value & LockedKind) >> LockedKindShift; + int Sped = (value & LockedSpeed) >> LockedSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_locked; + } + + secnum = -1; + rtn = 0; + + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; +manual_locked: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new door thinker + rtn = 1; + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + door->topwait = VDOORWAIT; + door->line = line; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = 1; + + /* killough 10/98: implement gradual lighting */ + door->lighttag = !comp[comp_doorlight] && + (line->special&6) == 6 && + line->special > GenLockedBase ? line->tag : 0; + + // setup speed of door motion + switch(Sped) + { + default: + case SpeedSlow: + door->type = Kind? genOpen : genRaise; + door->speed = VDOORSPEED; + break; + case SpeedNormal: + door->type = Kind? genOpen : genRaise; + door->speed = VDOORSPEED*2; + break; + case SpeedFast: + door->type = Kind? genBlazeOpen : genBlazeRaise; + door->speed = VDOORSPEED*4; + break; + case SpeedTurbo: + door->type = Kind? genBlazeOpen : genBlazeRaise; + door->speed = VDOORSPEED*8; + + break; + } + + // killough 4/15/98: fix generalized door opening sounds + // (previously they always had the blazing door close sound) + + S_StartSound((mobj_t *)&door->sector->soundorg, // killough 4/15/98 + door->speed >= VDOORSPEED*4 ? sfx_bdopn : sfx_doropn); + + if (manual) + return rtn; + } + return rtn; +} + +// +// EV_DoGenDoor() +// +// Handle generalized door types +// +// Passed the linedef activating the generalized door +// Returns true if a thinker created +// +int EV_DoGenDoor +( line_t* line ) +{ + int secnum,rtn; + sector_t* sec; + boolean manual; + vldoor_t* door; + unsigned value = (unsigned)line->special - GenDoorBase; + + // parse the bit fields in the line's special type + + int Dely = (value & DoorDelay) >> DoorDelayShift; + int Kind = (value & DoorKind) >> DoorKindShift; + int Sped = (value & DoorSpeed) >> DoorSpeedShift; + int Trig = (value & TriggerType) >> TriggerTypeShift; + + rtn = 0; + + // check if a manual trigger, if so do just the sector on the backside + manual = false; + if (Trig==PushOnce || Trig==PushMany) + { + if (!(sec = line->backsector)) + return rtn; + secnum = sec-sectors; + manual = true; + goto manual_door; + } + + + secnum = -1; + rtn = 0; + + // if not manual do all sectors tagged the same as the line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; +manual_door: + // Do not start another function if ceiling already moving + if (P_SectorActive(ceiling_special,sec)) //jff 2/22/98 + { + if (!manual) + continue; + else + return rtn; + } + + // new door thinker + rtn = 1; + door = Z_Malloc (sizeof(*door), PU_LEVSPEC, 0); + memset(door, 0, sizeof(*door)); + P_AddThinker (&door->thinker); + sec->ceilingdata = door; //jff 2/22/98 + + door->thinker.function = T_VerticalDoor; + door->sector = sec; + // setup delay for door remaining open/closed + switch(Dely) + { + default: + case 0: + door->topwait = 35; + break; + case 1: + door->topwait = VDOORWAIT; + break; + case 2: + door->topwait = 2*VDOORWAIT; + break; + case 3: + door->topwait = 7*VDOORWAIT; + break; + } + + // setup speed of door motion + switch(Sped) + { + default: + case SpeedSlow: + door->speed = VDOORSPEED; + break; + case SpeedNormal: + door->speed = VDOORSPEED*2; + break; + case SpeedFast: + door->speed = VDOORSPEED*4; + break; + case SpeedTurbo: + door->speed = VDOORSPEED*8; + break; + } + door->line = line; // jff 1/31/98 remember line that triggered us + + /* killough 10/98: implement gradual lighting */ + door->lighttag = !comp[comp_doorlight] && + (line->special&6) == 6 && + line->special > GenLockedBase ? line->tag : 0; + + // set kind of door, whether it opens then close, opens, closes etc. + // assign target heights accordingly + switch(Kind) + { + case OdCDoor: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast || comp[comp_sound] ? sfx_bdopn : sfx_doropn); + door->type = Sped>=SpeedFast? genBlazeRaise : genRaise; + break; + case ODoor: + door->direction = 1; + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + if (door->topheight != sec->ceilingheight) + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast || comp[comp_sound] ? sfx_bdopn : sfx_doropn); + door->type = Sped>=SpeedFast? genBlazeOpen : genOpen; + break; + case CdODoor: + door->topheight = sec->ceilingheight; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast && !comp[comp_sound] ? sfx_bdcls : sfx_dorcls); + door->type = Sped>=SpeedFast? genBlazeCdO : genCdO; + break; + case CDoor: + door->topheight = P_FindLowestCeilingSurrounding(sec); + door->topheight -= 4*FRACUNIT; + door->direction = -1; + S_StartSound((mobj_t *)&door->sector->soundorg,Sped>=SpeedFast && !comp[comp_sound] ? sfx_bdcls : sfx_dorcls); + door->type = Sped>=SpeedFast? genBlazeClose : genClose; + break; + default: + break; + } + if (manual) + return rtn; + } + return rtn; +} diff --git a/src/p_inter.c b/src/p_inter.c new file mode 100644 index 00000000..0636a05c --- /dev/null +++ b/src/p_inter.c @@ -0,0 +1,908 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Handling interactions (i.e., collisions). + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "dstrings.h" +#include "m_random.h" +#include "am_map.h" +#include "r_main.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_deh.h" // Ty 03/22/98 - externalized strings +#include "p_tick.h" +#include "lprintf.h" + +#include "p_inter.h" +#include "p_enemy.h" + +#ifdef __GNUG__ +#pragma implementation "p_inter.h" +#endif +#include "p_inter.h" + +#define BONUSADD 6 + +// Ty 03/07/98 - add deh externals +// Maximums and such were hardcoded values. Need to externalize those for +// dehacked support (and future flexibility). Most var names came from the key +// strings used in dehacked. + +int initial_health = 100; +int initial_bullets = 50; +int maxhealth = 100; // was MAXHEALTH as a #define, used only in this module +int max_armor = 200; +int green_armor_class = 1; // these are involved with armortype below +int blue_armor_class = 2; +int max_soul = 200; +int soul_health = 100; +int mega_health = 200; +int god_health = 100; // these are used in cheats (see st_stuff.c) +int idfa_armor = 200; +int idfa_armor_class = 2; +// not actually used due to pairing of cheat_k and cheat_fa +int idkfa_armor = 200; +int idkfa_armor_class = 2; + +int bfgcells = 40; // used in p_pspr.c +int monsters_infight = 0; // e6y: Dehacked support - monsters infight +// Ty 03/07/98 - end deh externals + +// a weapon is found with two clip loads, +// a big item has five clip loads +int maxammo[NUMAMMO] = {200, 50, 300, 50}; +int clipammo[NUMAMMO] = { 10, 4, 20, 1}; + +// +// GET STUFF +// + +// +// P_GiveAmmo +// Num is the number of clip loads, +// not the individual count (0= 1/2 clip). +// Returns false if the ammo can't be picked up at all +// + +static boolean P_GiveAmmo(player_t *player, ammotype_t ammo, int num) +{ + int oldammo; + + if (ammo == am_noammo) + return false; + + if ( player->ammo[ammo] == player->maxammo[ammo] ) + return false; + + if (num) + num *= clipammo[ammo]; + else + num = clipammo[ammo]/2; + + // give double ammo in trainer mode, you'll need in nightmare + if (gameskill == sk_baby || gameskill == sk_nightmare) + num <<= 1; + + oldammo = player->ammo[ammo]; + player->ammo[ammo] += num; + + if (player->ammo[ammo] > player->maxammo[ammo]) + player->ammo[ammo] = player->maxammo[ammo]; + + // If non zero ammo, don't change up weapons, player was lower on purpose. + if (oldammo) + return true; + + // We were down to zero, so select a new weapon. + // Preferences are not user selectable. + + switch (ammo) + { + case am_clip: + if (player->readyweapon == wp_fist) { + if (player->weaponowned[wp_chaingun]) + player->pendingweapon = wp_chaingun; + else + player->pendingweapon = wp_pistol; + } + break; + + case am_shell: + if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) + if (player->weaponowned[wp_shotgun]) + player->pendingweapon = wp_shotgun; + break; + + case am_cell: + if (player->readyweapon == wp_fist || player->readyweapon == wp_pistol) + if (player->weaponowned[wp_plasma]) + player->pendingweapon = wp_plasma; + break; + + case am_misl: + if (player->readyweapon == wp_fist) + if (player->weaponowned[wp_missile]) + player->pendingweapon = wp_missile; + default: + break; + } + return true; +} + +// +// P_GiveWeapon +// The weapon name may have a MF_DROPPED flag ored in. +// + +static boolean P_GiveWeapon(player_t *player, weapontype_t weapon, boolean dropped) +{ + boolean gaveammo; + boolean gaveweapon; + + if (netgame && deathmatch!=2 && !dropped) + { + // leave placed weapons forever on net games + if (player->weaponowned[weapon]) + return false; + + player->bonuscount += BONUSADD; + player->weaponowned[weapon] = true; + + P_GiveAmmo(player, weaponinfo[weapon].ammo, deathmatch ? 5 : 2); + + player->pendingweapon = weapon; + /* cph 20028/10 - for old-school DM addicts, allow old behavior + * where only consoleplayer's pickup sounds are heard */ + // displayplayer, not consoleplayer, for viewing multiplayer demos + if (!comp[comp_sound] || player == &players[displayplayer]) + S_StartSound (player->mo, sfx_wpnup|PICKUP_SOUND); // killough 4/25/98 + return false; + } + + if (weaponinfo[weapon].ammo != am_noammo) + { + // give one clip with a dropped weapon, + // two clips with a found weapon + gaveammo = P_GiveAmmo (player, weaponinfo[weapon].ammo, dropped ? 1 : 2); + } + else + gaveammo = false; + + if (player->weaponowned[weapon]) + gaveweapon = false; + else + { + gaveweapon = true; + player->weaponowned[weapon] = true; + player->pendingweapon = weapon; + } + return gaveweapon || gaveammo; +} + +// +// P_GiveBody +// Returns false if the body isn't needed at all +// + +static boolean P_GiveBody(player_t *player, int num) +{ + if (player->health >= maxhealth) + return false; // Ty 03/09/98 externalized MAXHEALTH to maxhealth + player->health += num; + if (player->health > maxhealth) + player->health = maxhealth; + player->mo->health = player->health; + return true; +} + +// +// P_GiveArmor +// Returns false if the armor is worse +// than the current armor. +// + +static boolean P_GiveArmor(player_t *player, int armortype) +{ + int hits = armortype*100; + if (player->armorpoints >= hits) + return false; // don't pick up + player->armortype = armortype; + player->armorpoints = hits; + return true; +} + +// +// P_GiveCard +// + +static void P_GiveCard(player_t *player, card_t card) +{ + if (player->cards[card]) + return; + player->bonuscount = BONUSADD; + player->cards[card] = 1; +} + +// +// P_GivePower +// +// Rewritten by Lee Killough +// + +boolean P_GivePower(player_t *player, int power) +{ + static const int tics[NUMPOWERS] = { + INVULNTICS, 1 /* strength */, INVISTICS, + IRONTICS, 1 /* allmap */, INFRATICS, + }; + + switch (power) + { + case pw_invisibility: + player->mo->flags |= MF_SHADOW; + break; + case pw_allmap: + if (player->powers[pw_allmap]) + return false; + break; + case pw_strength: + P_GiveBody(player,100); + break; + } + + // Unless player has infinite duration cheat, set duration (killough) + + if (player->powers[power] >= 0) + player->powers[power] = tics[power]; + return true; +} + +// +// P_TouchSpecialThing +// + +void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher) +{ + player_t *player; + int i; + int sound; + fixed_t delta = special->z - toucher->z; + + if (delta > toucher->height || delta < -8*FRACUNIT) + return; // out of reach + + sound = sfx_itemup; + player = toucher->player; + + // Dead thing touching. + // Can happen with a sliding player corpse. + if (toucher->health <= 0) + return; + + // Identify by sprite. + switch (special->sprite) + { + // armor + case SPR_ARM1: + if (!P_GiveArmor (player, green_armor_class)) + return; + player->message = s_GOTARMOR; // Ty 03/22/98 - externalized + break; + + case SPR_ARM2: + if (!P_GiveArmor (player, blue_armor_class)) + return; + player->message = s_GOTMEGA; // Ty 03/22/98 - externalized + break; + + // bonus items + case SPR_BON1: + player->health++; // can go over 100% + if (player->health > (maxhealth * 2)) + player->health = (maxhealth * 2); + player->mo->health = player->health; + player->message = s_GOTHTHBONUS; // Ty 03/22/98 - externalized + break; + + case SPR_BON2: + player->armorpoints++; // can go over 100% + if (player->armorpoints > max_armor) + player->armorpoints = max_armor; + if (!player->armortype) + player->armortype = green_armor_class; + player->message = s_GOTARMBONUS; // Ty 03/22/98 - externalized + break; + + case SPR_SOUL: + player->health += soul_health; + if (player->health > max_soul) + player->health = max_soul; + player->mo->health = player->health; + player->message = s_GOTSUPER; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_MEGA: + if (gamemode != commercial) + return; + player->health = mega_health; + player->mo->health = player->health; + P_GiveArmor (player,blue_armor_class); + player->message = s_GOTMSPHERE; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + // cards + // leave cards for everyone + case SPR_BKEY: + if (!player->cards[it_bluecard]) + player->message = s_GOTBLUECARD; // Ty 03/22/98 - externalized + P_GiveCard (player, it_bluecard); + if (!netgame) + break; + return; + + case SPR_YKEY: + if (!player->cards[it_yellowcard]) + player->message = s_GOTYELWCARD; // Ty 03/22/98 - externalized + P_GiveCard (player, it_yellowcard); + if (!netgame) + break; + return; + + case SPR_RKEY: + if (!player->cards[it_redcard]) + player->message = s_GOTREDCARD; // Ty 03/22/98 - externalized + P_GiveCard (player, it_redcard); + if (!netgame) + break; + return; + + case SPR_BSKU: + if (!player->cards[it_blueskull]) + player->message = s_GOTBLUESKUL; // Ty 03/22/98 - externalized + P_GiveCard (player, it_blueskull); + if (!netgame) + break; + return; + + case SPR_YSKU: + if (!player->cards[it_yellowskull]) + player->message = s_GOTYELWSKUL; // Ty 03/22/98 - externalized + P_GiveCard (player, it_yellowskull); + if (!netgame) + break; + return; + + case SPR_RSKU: + if (!player->cards[it_redskull]) + player->message = s_GOTREDSKULL; // Ty 03/22/98 - externalized + P_GiveCard (player, it_redskull); + if (!netgame) + break; + return; + + // medikits, heals + case SPR_STIM: + if (!P_GiveBody (player, 10)) + return; + player->message = s_GOTSTIM; // Ty 03/22/98 - externalized + break; + + case SPR_MEDI: + if (!P_GiveBody (player, 25)) + return; + + if (player->health < 50) // cph - 25 + the 25 just added, thanks to Quasar for reporting this bug + player->message = s_GOTMEDINEED; // Ty 03/22/98 - externalized + else + player->message = s_GOTMEDIKIT; // Ty 03/22/98 - externalized + break; + + + // power ups + case SPR_PINV: + if (!P_GivePower (player, pw_invulnerability)) + return; + player->message = s_GOTINVUL; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_PSTR: + if (!P_GivePower (player, pw_strength)) + return; + player->message = s_GOTBERSERK; // Ty 03/22/98 - externalized + if (player->readyweapon != wp_fist) + player->pendingweapon = wp_fist; + sound = sfx_getpow; + break; + + case SPR_PINS: + if (!P_GivePower (player, pw_invisibility)) + return; + player->message = s_GOTINVIS; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_SUIT: + if (!P_GivePower (player, pw_ironfeet)) + return; + player->message = s_GOTSUIT; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_PMAP: + if (!P_GivePower (player, pw_allmap)) + return; + player->message = s_GOTMAP; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + case SPR_PVIS: + if (!P_GivePower (player, pw_infrared)) + return; + player->message = s_GOTVISOR; // Ty 03/22/98 - externalized + sound = sfx_getpow; + break; + + // ammo + case SPR_CLIP: + if (special->flags & MF_DROPPED) + { + if (!P_GiveAmmo (player,am_clip,0)) + return; + } + else + { + if (!P_GiveAmmo (player,am_clip,1)) + return; + } + player->message = s_GOTCLIP; // Ty 03/22/98 - externalized + break; + + case SPR_AMMO: + if (!P_GiveAmmo (player, am_clip,5)) + return; + player->message = s_GOTCLIPBOX; // Ty 03/22/98 - externalized + break; + + case SPR_ROCK: + if (!P_GiveAmmo (player, am_misl,1)) + return; + player->message = s_GOTROCKET; // Ty 03/22/98 - externalized + break; + + case SPR_BROK: + if (!P_GiveAmmo (player, am_misl,5)) + return; + player->message = s_GOTROCKBOX; // Ty 03/22/98 - externalized + break; + + case SPR_CELL: + if (!P_GiveAmmo (player, am_cell,1)) + return; + player->message = s_GOTCELL; // Ty 03/22/98 - externalized + break; + + case SPR_CELP: + if (!P_GiveAmmo (player, am_cell,5)) + return; + player->message = s_GOTCELLBOX; // Ty 03/22/98 - externalized + break; + + case SPR_SHEL: + if (!P_GiveAmmo (player, am_shell,1)) + return; + player->message = s_GOTSHELLS; // Ty 03/22/98 - externalized + break; + + case SPR_SBOX: + if (!P_GiveAmmo (player, am_shell,5)) + return; + player->message = s_GOTSHELLBOX; // Ty 03/22/98 - externalized + break; + + case SPR_BPAK: + if (!player->backpack) + { + for (i=0 ; imaxammo[i] *= 2; + player->backpack = true; + } + for (i=0 ; imessage = s_GOTBACKPACK; // Ty 03/22/98 - externalized + break; + + // weapons + case SPR_BFUG: + if (!P_GiveWeapon (player, wp_bfg, false) ) + return; + player->message = s_GOTBFG9000; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_MGUN: + if (!P_GiveWeapon (player, wp_chaingun, (special->flags&MF_DROPPED)!=0) ) + return; + player->message = s_GOTCHAINGUN; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_CSAW: + if (!P_GiveWeapon (player, wp_chainsaw, false) ) + return; + player->message = s_GOTCHAINSAW; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_LAUN: + if (!P_GiveWeapon (player, wp_missile, false) ) + return; + player->message = s_GOTLAUNCHER; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_PLAS: + if (!P_GiveWeapon (player, wp_plasma, false) ) + return; + player->message = s_GOTPLASMA; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_SHOT: + if (!P_GiveWeapon (player, wp_shotgun, (special->flags&MF_DROPPED)!=0 ) ) + return; + player->message = s_GOTSHOTGUN; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + case SPR_SGN2: + if (!P_GiveWeapon(player, wp_supershotgun, (special->flags&MF_DROPPED)!=0)) + return; + player->message = s_GOTSHOTGUN2; // Ty 03/22/98 - externalized + sound = sfx_wpnup; + break; + + default: + I_Error ("P_SpecialThing: Unknown gettable thing"); + } + + if (special->flags & MF_COUNTITEM) + player->itemcount++; + P_RemoveMobj (special); + player->bonuscount += BONUSADD; + + /* cph 20028/10 - for old-school DM addicts, allow old behavior + * where only consoleplayer's pickup sounds are heard */ + // displayplayer, not consoleplayer, for viewing multiplayer demos + if (!comp[comp_sound] || player == &players[displayplayer]) + S_StartSound (player->mo, sound | PICKUP_SOUND); // killough 4/25/98 +} + +// +// KillMobj +// +// killough 11/98: make static +static void P_KillMobj(mobj_t *source, mobj_t *target) +{ + mobjtype_t item; + mobj_t *mo; + + target->flags &= ~(MF_SHOOTABLE|MF_FLOAT|MF_SKULLFLY); + + if (target->type != MT_SKULL) + target->flags &= ~MF_NOGRAVITY; + + target->flags |= MF_CORPSE|MF_DROPOFF; + target->height >>= 2; + + if (!((target->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totallive--; + + if (source && source->player) + { + // count for intermission + if (target->flags & MF_COUNTKILL) + source->player->killcount++; + if (target->player) + source->player->frags[target->player-players]++; + } + else + if (target->flags & MF_COUNTKILL) { /* Add to kills tally */ + if ((compatibility_level < lxdoom_1_compatibility) || !netgame) { + if (!netgame) + // count all monster deaths, + // even those caused by other monsters + players[0].killcount++; + } else + if (!deathmatch) { + // try and find a player to give the kill to, otherwise give the + // kill to a random player. this fixes the missing monsters bug + // in coop - rain + // CPhipps - not a bug as such, but certainly an inconsistency. + if (target->lastenemy && target->lastenemy->health > 0 + && target->lastenemy->player) // Fighting a player + target->lastenemy->player->killcount++; + else { + // cph - randomely choose a player in the game to be credited + // and do it uniformly between the active players + unsigned int activeplayers = 0, player, i; + + for (player = 0; playerplayer) + { + // count environment kills against you + if (!source) + target->player->frags[target->player-players]++; + + target->flags &= ~MF_SOLID; + target->player->playerstate = PST_DEAD; + P_DropWeapon (target->player); + + if (target->player == &players[consoleplayer] && (automapmode & am_active)) + AM_Stop(); // don't die in auto map; switch view prior to dying + } + + if (target->health < -target->info->spawnhealth && target->info->xdeathstate) + P_SetMobjState (target, target->info->xdeathstate); + else + P_SetMobjState (target, target->info->deathstate); + + target->tics -= P_Random(pr_killtics)&3; + + if (target->tics < 1) + target->tics = 1; + + // Drop stuff. + // This determines the kind of object spawned + // during the death frame of a thing. + + switch (target->type) + { + case MT_WOLFSS: + case MT_POSSESSED: + item = MT_CLIP; + break; + + case MT_SHOTGUY: + item = MT_SHOTGUN; + break; + + case MT_CHAINGUY: + item = MT_CHAINGUN; + break; + + default: + return; + } + + mo = P_SpawnMobj (target->x,target->y,ONFLOORZ, item); + mo->flags |= MF_DROPPED; // special versions of items +} + +// +// P_DamageMobj +// Damages both enemies and players +// "inflictor" is the thing that caused the damage +// creature or missile, can be NULL (slime, etc) +// "source" is the thing to target after taking damage +// creature or NULL +// Source and inflictor are the same for melee attacks. +// Source can be NULL for slime, barrel explosions +// and other environmental stuff. +// + +void P_DamageMobj(mobj_t *target,mobj_t *inflictor, mobj_t *source, int damage) +{ + player_t *player; + boolean justhit = false; /* killough 11/98 */ + + /* killough 8/31/98: allow bouncers to take damage */ + if (!(target->flags & (MF_SHOOTABLE | MF_BOUNCES))) + return; // shouldn't happen... + + if (target->health <= 0) + return; + + if (target->flags & MF_SKULLFLY) + target->momx = target->momy = target->momz = 0; + + player = target->player; + if (player && gameskill == sk_baby) + damage >>= 1; // take half damage in trainer mode + + // Some close combat weapons should not + // inflict thrust and push the victim out of reach, + // thus kick away unless using the chainsaw. + + if (inflictor && !(target->flags & MF_NOCLIP) && + (!source || !source->player || + source->player->readyweapon != wp_chainsaw)) + { + unsigned ang = R_PointToAngle2 (inflictor->x, inflictor->y, + target->x, target->y); + + fixed_t thrust = damage*(FRACUNIT>>3)*100/target->info->mass; + + // make fall forwards sometimes + if ( damage < 40 && damage > target->health + && target->z - inflictor->z > 64*FRACUNIT + && P_Random(pr_damagemobj) & 1) + { + ang += ANG180; + thrust *= 4; + } + + ang >>= ANGLETOFINESHIFT; + target->momx += FixedMul (thrust, finecosine[ang]); + target->momy += FixedMul (thrust, finesine[ang]); + + /* killough 11/98: thrust objects hanging off ledges */ + if (target->intflags & MIF_FALLING && target->gear >= MAXGEAR) + target->gear = 0; + } + + // player specific + if (player) + { + // end of game hell hack + if (target->subsector->sector->special == 11 && damage >= target->health) + damage = target->health - 1; + + // Below certain threshold, + // ignore damage in GOD mode, or with INVUL power. + // killough 3/26/98: make god mode 100% god mode in non-compat mode + + if ((damage < 1000 || (!comp[comp_god] && (player->cheats&CF_GODMODE))) && + (player->cheats&CF_GODMODE || player->powers[pw_invulnerability])) + return; + + if (player->armortype) + { + int saved = player->armortype == 1 ? damage/3 : damage/2; + if (player->armorpoints <= saved) + { + // armor is used up + saved = player->armorpoints; + player->armortype = 0; + } + player->armorpoints -= saved; + damage -= saved; + } + + player->health -= damage; // mirror mobj health here for Dave + if (player->health < 0) + player->health = 0; + + player->attacker = source; + player->damagecount += damage; // add damage after armor / invuln + + if (player->damagecount > 100) + player->damagecount = 100; // teleport stomp does 10k points... + } + + // do the damage + target->health -= damage; + if (target->health <= 0) + { + P_KillMobj (source, target); + return; + } + + // killough 9/7/98: keep track of targets so that friends can help friends + if (mbf_features) + { + /* If target is a player, set player's target to source, + * so that a friend can tell who's hurting a player + */ + if (player) + P_SetTarget(&target->target, source); + + /* killough 9/8/98: + * If target's health is less than 50%, move it to the front of its list. + * This will slightly increase the chances that enemies will choose to + * "finish it off", but its main purpose is to alert friends of danger. + */ + if (target->health*2 < target->info->spawnhealth) + { + thinker_t *cap = &thinkerclasscap[target->flags & MF_FRIEND ? + th_friends : th_enemies]; + (target->thinker.cprev->cnext = target->thinker.cnext)->cprev = + target->thinker.cprev; + (target->thinker.cnext = cap->cnext)->cprev = &target->thinker; + (target->thinker.cprev = cap)->cnext = &target->thinker; + } + } + + if (P_Random (pr_painchance) < target->info->painchance && + !(target->flags & MF_SKULLFLY)) { //killough 11/98: see below + if (mbf_features) + justhit = true; + else + target->flags |= MF_JUSTHIT; // fight back! + + P_SetMobjState(target, target->info->painstate); + } + + target->reactiontime = 0; // we're awake now... + + /* killough 9/9/98: cleaned up, made more consistent: */ + + if (source && source != target && source->type != MT_VILE && + (!target->threshold || target->type == MT_VILE) && + ((source->flags ^ target->flags) & MF_FRIEND || + monster_infighting || + !mbf_features)) + { + /* if not intent on another player, chase after this one + * + * killough 2/15/98: remember last enemy, to prevent + * sleeping early; 2/21/98: Place priority on players + * killough 9/9/98: cleaned up, made more consistent: + */ + + if (!target->lastenemy || target->lastenemy->health <= 0 || + (!mbf_features ? + !target->lastenemy->player : + !((target->flags ^ target->lastenemy->flags) & MF_FRIEND) && + target->target != source)) // remember last enemy - killough + P_SetTarget(&target->lastenemy, target->target); + + P_SetTarget(&target->target, source); // killough 11/98 + target->threshold = BASETHRESHOLD; + if (target->state == &states[target->info->spawnstate] + && target->info->seestate != S_NULL) + P_SetMobjState (target, target->info->seestate); + } + + /* killough 11/98: Don't attack a friend, unless hit by that friend. + * cph 2006/04/01 - implicitly this is only if mbf_features */ + if (justhit && (target->target == source || !target->target || + !(target->flags & target->target->flags & MF_FRIEND))) + target->flags |= MF_JUSTHIT; // fight back! +} diff --git a/src/p_inter.h b/src/p_inter.h new file mode 100644 index 00000000..53b64a77 --- /dev/null +++ b/src/p_inter.h @@ -0,0 +1,75 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thing events, and dehacked specified numbers controlling them. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_INTER__ +#define __P_INTER__ + +#include "d_player.h" +#include "p_mobj.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* Ty 03/09/98 Moved to an int in p_inter.c for deh and externalization */ +#define MAXHEALTH maxhealth + +/* follow a player exlusively for 3 seconds */ +#define BASETHRESHOLD (100) + +boolean P_GivePower(player_t *, int); +void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher); +void P_DamageMobj(mobj_t *target,mobj_t *inflictor,mobj_t *source,int damage); + +/* killough 5/2/98: moved from d_deh.c, g_game.c, m_misc.c, others: */ + +extern int god_health; /* Ty 03/09/98 - deh support, see also p_inter.c */ +extern int idfa_armor; +extern int idfa_armor_class; +extern int idkfa_armor; +extern int idkfa_armor_class; /* Ty - end */ +/* Ty 03/13/98 - externalized initial settings for respawned player */ +extern int initial_health; +extern int initial_bullets; +extern int maxhealth; +extern int max_armor; +extern int green_armor_class; +extern int blue_armor_class; +extern int max_soul; +extern int soul_health; +extern int mega_health; +extern int bfgcells; +extern int monsters_infight; // e6y: Dehacked support - monsters infight +extern int maxammo[], clipammo[]; + +#endif diff --git a/src/p_lights.c b/src/p_lights.c new file mode 100644 index 00000000..84936e08 --- /dev/null +++ b/src/p_lights.c @@ -0,0 +1,443 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Action routines for lighting thinkers + * Spawn sector based lighting effects. + * Handle lighting linedef types + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" //jff 5/18/98 +#include "doomdef.h" +#include "m_random.h" +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" + +////////////////////////////////////////////////////////// +// +// Lighting action routines, called once per tick +// +////////////////////////////////////////////////////////// + +// +// T_FireFlicker() +// +// Firelight flicker action routine, called once per tick +// +// Passed a fireflicker_t structure containing light levels and timing +// Returns nothing +// +void T_FireFlicker (fireflicker_t* flick) +{ + int amount; + + if (--flick->count) + return; + + amount = (P_Random(pr_lights)&3)*16; + + if (flick->sector->lightlevel - amount < flick->minlight) + flick->sector->lightlevel = flick->minlight; + else + flick->sector->lightlevel = flick->maxlight - amount; + + flick->count = 4; +} + +// +// T_LightFlash() +// +// Broken light flashing action routine, called once per tick +// +// Passed a lightflash_t structure containing light levels and timing +// Returns nothing +// +void T_LightFlash (lightflash_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->maxlight) + { + flash-> sector->lightlevel = flash->minlight; + flash->count = (P_Random(pr_lights)&flash->mintime)+1; + } + else + { + flash-> sector->lightlevel = flash->maxlight; + flash->count = (P_Random(pr_lights)&flash->maxtime)+1; + } + +} + +// +// T_StrobeFlash() +// +// Strobe light flashing action routine, called once per tick +// +// Passed a strobe_t structure containing light levels and timing +// Returns nothing +// +void T_StrobeFlash (strobe_t* flash) +{ + if (--flash->count) + return; + + if (flash->sector->lightlevel == flash->minlight) + { + flash-> sector->lightlevel = flash->maxlight; + flash->count = flash->brighttime; + } + else + { + flash-> sector->lightlevel = flash->minlight; + flash->count =flash->darktime; + } +} + +// +// T_Glow() +// +// Glowing light action routine, called once per tick +// +// Passed a glow_t structure containing light levels and timing +// Returns nothing +// + +void T_Glow(glow_t* g) +{ + switch(g->direction) + { + case -1: + // light dims + g->sector->lightlevel -= GLOWSPEED; + if (g->sector->lightlevel <= g->minlight) + { + g->sector->lightlevel += GLOWSPEED; + g->direction = 1; + } + break; + + case 1: + // light brightens + g->sector->lightlevel += GLOWSPEED; + if (g->sector->lightlevel >= g->maxlight) + { + g->sector->lightlevel -= GLOWSPEED; + g->direction = -1; + } + break; + } +} + +////////////////////////////////////////////////////////// +// +// Sector lighting type spawners +// +// After the map has been loaded, each sector is scanned +// for specials that spawn thinkers +// +////////////////////////////////////////////////////////// + +// +// P_SpawnFireFlicker() +// +// Spawns a fire flicker lighting thinker +// +// Passed the sector that spawned the thinker +// Returns nothing +// +void P_SpawnFireFlicker (sector_t* sector) +{ + fireflicker_t* flick; + + // Note that we are resetting sector attributes. + // Nothing special about it during gameplay. + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type + + flick = Z_Malloc ( sizeof(*flick), PU_LEVSPEC, 0); + + memset(flick, 0, sizeof(*flick)); + P_AddThinker (&flick->thinker); + + flick->thinker.function = T_FireFlicker; + flick->sector = sector; + flick->maxlight = sector->lightlevel; + flick->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel)+16; + flick->count = 4; +} + +// +// P_SpawnLightFlash() +// +// Spawns a broken light flash lighting thinker +// +// Passed the sector that spawned the thinker +// Returns nothing +// +void P_SpawnLightFlash (sector_t* sector) +{ + lightflash_t* flash; + + // nothing special about it during gameplay + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type + + flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); + + memset(flash, 0, sizeof(*flash)); + P_AddThinker (&flash->thinker); + + flash->thinker.function = T_LightFlash; + flash->sector = sector; + flash->maxlight = sector->lightlevel; + + flash->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); + flash->maxtime = 64; + flash->mintime = 7; + flash->count = (P_Random(pr_lights)&flash->maxtime)+1; +} + +// +// P_SpawnStrobeFlash +// +// Spawns a blinking light thinker +// +// Passed the sector that spawned the thinker, speed of blinking +// and whether blinking is to by syncrhonous with other sectors +// +// Returns nothing +// +void P_SpawnStrobeFlash +( sector_t* sector, + int fastOrSlow, + int inSync ) +{ + strobe_t* flash; + + flash = Z_Malloc ( sizeof(*flash), PU_LEVSPEC, 0); + + memset(flash, 0, sizeof(*flash)); + P_AddThinker (&flash->thinker); + + flash->sector = sector; + flash->darktime = fastOrSlow; + flash->brighttime = STROBEBRIGHT; + flash->thinker.function = T_StrobeFlash; + flash->maxlight = sector->lightlevel; + flash->minlight = P_FindMinSurroundingLight(sector, sector->lightlevel); + + if (flash->minlight == flash->maxlight) + flash->minlight = 0; + + // nothing special about it during gameplay + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type + + if (!inSync) + flash->count = (P_Random(pr_lights)&7)+1; + else + flash->count = 1; +} + +// +// P_SpawnGlowingLight() +// +// Spawns a glowing light (smooth oscillation from min to max) thinker +// +// Passed the sector that spawned the thinker +// Returns nothing +// +void P_SpawnGlowingLight(sector_t* sector) +{ + glow_t* g; + + g = Z_Malloc( sizeof(*g), PU_LEVSPEC, 0); + + memset(g, 0, sizeof(*g)); + P_AddThinker(&g->thinker); + + g->sector = sector; + g->minlight = P_FindMinSurroundingLight(sector,sector->lightlevel); + g->maxlight = sector->lightlevel; + g->thinker.function = T_Glow; + g->direction = -1; + + sector->special &= ~31; //jff 3/14/98 clear non-generalized sector type +} + +////////////////////////////////////////////////////////// +// +// Linedef lighting function handlers +// +////////////////////////////////////////////////////////// + +// +// EV_StartLightStrobing() +// +// Start strobing lights (usually from a trigger) +// +// Passed the line that activated the strobing +// Returns true +// +// jff 2/12/98 added int return value, fixed return +// +int EV_StartLightStrobing(line_t* line) +{ + int secnum; + sector_t* sec; + + secnum = -1; + // start lights strobing in all sectors tagged same as line + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + // if already doing a lighting function, don't start a second + if (P_SectorActive(lighting_special,sec)) //jff 2/22/98 + continue; + + P_SpawnStrobeFlash (sec,SLOWDARK, 0); + } + return 1; +} + +// +// EV_TurnTagLightsOff() +// +// Turn line's tagged sector's lights to min adjacent neighbor level +// +// Passed the line that activated the lights being turned off +// Returns true +// +// jff 2/12/98 added int return value, fixed return +// +int EV_TurnTagLightsOff(line_t* line) +{ + int j; + + // search sectors for those with same tag as activating line + + // killough 10/98: replaced inefficient search with fast search + for (j = -1; (j = P_FindSectorFromLineTag(line,j)) >= 0;) + { + sector_t *sector = sectors + j, *tsec; + int i, min = sector->lightlevel; + // find min neighbor light level + for (i = 0;i < sector->linecount; i++) + if ((tsec = getNextSector(sector->lines[i], sector)) && + tsec->lightlevel < min) + min = tsec->lightlevel; + sector->lightlevel = min; + } + return 1; +} + +// +// EV_LightTurnOn() +// +// Turn sectors tagged to line lights on to specified or max neighbor level +// +// Passed the activating line, and a level to set the light to +// If level passed is 0, the maximum neighbor lighting is used +// Returns true +// +// jff 2/12/98 added int return value, fixed return +// +int EV_LightTurnOn(line_t *line, int bright) +{ + int i; + + // search all sectors for ones with same tag as activating line + + // killough 10/98: replace inefficient search with fast search + for (i = -1; (i = P_FindSectorFromLineTag(line,i)) >= 0;) + { + sector_t *temp, *sector = sectors+i; + int j, tbright = bright; //jff 5/17/98 search for maximum PER sector + + // bright = 0 means to search for highest light level surrounding sector + + if (!bright) + for (j = 0;j < sector->linecount; j++) + if ((temp = getNextSector(sector->lines[j],sector)) && + temp->lightlevel > tbright) + tbright = temp->lightlevel; + + sector->lightlevel = tbright; + + //jff 5/17/98 unless compatibility optioned + //then maximum near ANY tagged sector + if (comp[comp_model]) + bright = tbright; + } + return 1; +} + +/* killough 10/98: + * + * EV_LightTurnOnPartway() + * + * Turn sectors tagged to line lights on to specified or max neighbor level + * + * Passed the activating line, and a light level fraction between 0 and 1. + * Sets the light to min on 0, max on 1, and interpolates in-between. + * Used for doors with gradual lighting effects. + * + * Returns true + */ + +int EV_LightTurnOnPartway(line_t *line, fixed_t level) +{ + int i; + + if (level < 0) // clip at extremes + level = 0; + if (level > FRACUNIT) + level = FRACUNIT; + + // search all sectors for ones with same tag as activating line + for (i = -1; (i = P_FindSectorFromLineTag(line,i)) >= 0;) + { + sector_t *temp, *sector = sectors+i; + int j, bright = 0, min = sector->lightlevel; + + for (j = 0; j < sector->linecount; j++) + if ((temp = getNextSector(sector->lines[j],sector))) + { + if (temp->lightlevel > bright) + bright = temp->lightlevel; + if (temp->lightlevel < min) + min = temp->lightlevel; + } + + sector->lightlevel = // Set level in-between extremes + (level * bright + (FRACUNIT-level) * min) >> FRACBITS; + } + return 1; +} + diff --git a/src/p_map.c b/src/p_map.c new file mode 100644 index 00000000..bc15b7c8 --- /dev/null +++ b/src/p_map.c @@ -0,0 +1,2335 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Movement, collision handling. + * Shooting and aiming. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_mobj.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "p_spec.h" +#include "s_sound.h" +#include "sounds.h" +#include "p_inter.h" +#include "m_random.h" +#include "m_bbox.h" +#include "lprintf.h" + +static mobj_t *tmthing; +static fixed_t tmx; +static fixed_t tmy; +static int pe_x; // Pain Elemental position for Lost Soul checks // phares +static int pe_y; // Pain Elemental position for Lost Soul checks // phares +static int ls_x; // Lost Soul position for Lost Soul checks // phares +static int ls_y; // Lost Soul position for Lost Soul checks // phares + +// If "floatok" true, move would be ok +// if within "tmfloorz - tmceilingz". + +boolean floatok; + +/* killough 11/98: if "felldown" true, object was pushed down ledge */ +boolean felldown; + +// The tm* items are used to hold information globally, usually for +// line or object intersection checking + +fixed_t tmbbox[4]; // bounding box for line intersection checks +fixed_t tmfloorz; // floor you'd hit if free to fall +fixed_t tmceilingz; // ceiling of sector you're in +fixed_t tmdropoffz; // dropoff on other side of line you're crossing + +// keep track of the line that lowers the ceiling, +// so missiles don't explode against sky hack walls + +line_t *ceilingline; +line_t *blockline; /* killough 8/11/98: blocking linedef */ +line_t *floorline; /* killough 8/1/98: Highest touched floor */ +static int tmunstuck; /* killough 8/1/98: whether to allow unsticking */ + +// keep track of special lines as they are hit, +// but don't process them until the move is proven valid + +// 1/11/98 killough: removed limit on special lines crossed +line_t **spechit; // new code -- killough +static int spechit_max; // killough + +int numspechit; + +// Temporary holder for thing_sectorlist threads +msecnode_t* sector_list = NULL; // phares 3/16/98 + +// +// TELEPORT MOVE +// + +// +// PIT_StompThing +// + +static boolean telefrag; /* killough 8/9/98: whether to telefrag at exit */ + +boolean PIT_StompThing (mobj_t* thing) +{ + fixed_t blockdist; + + // phares 9/10/98: moved this self-check to start of routine + + // don't clip against self + + if (thing == tmthing) + return true; + + if (!(thing->flags & MF_SHOOTABLE)) // Can't shoot it? Can't stomp it! + return true; + + blockdist = thing->radius + tmthing->radius; + + if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist) + return true; // didn't hit it + + // monsters don't stomp things except on boss level + if (!telefrag) // killough 8/9/98: make consistent across all levels + return false; + + P_DamageMobj (thing, tmthing, tmthing, 10000); // Stomp! + + return true; +} + + +/* + * killough 8/28/98: + * + * P_GetFriction() + * + * Returns the friction associated with a particular mobj. + */ + +int P_GetFriction(const mobj_t *mo, int *frictionfactor) +{ + int friction = ORIG_FRICTION; + int movefactor = ORIG_FRICTION_FACTOR; + const msecnode_t *m; + const sector_t *sec; + + /* Assign the friction value to objects on the floor, non-floating, + * and clipped. Normally the object's friction value is kept at + * ORIG_FRICTION and this thinker changes it for icy or muddy floors. + * + * When the object is straddling sectors with the same + * floorheight that have different frictions, use the lowest + * friction value (muddy has precedence over icy). + */ + + if (!(mo->flags & (MF_NOCLIP|MF_NOGRAVITY)) + && (mbf_features || (mo->player && !compatibility)) && + variable_friction) + for (m = mo->touching_sectorlist; m; m = m->m_tnext) + if ((sec = m->m_sector)->special & FRICTION_MASK && + (sec->friction < friction || friction == ORIG_FRICTION) && + (mo->z <= sec->floorheight || + (sec->heightsec != -1 && + mo->z <= sectors[sec->heightsec].floorheight && + mbf_features))) + friction = sec->friction, movefactor = sec->movefactor; + + if (frictionfactor) + *frictionfactor = movefactor; + + return friction; +} + +/* phares 3/19/98 + * P_GetMoveFactor() returns the value by which the x,y + * movements are multiplied to add to player movement. + * + * killough 8/28/98: rewritten + */ + +int P_GetMoveFactor(mobj_t *mo, int *frictionp) +{ + int movefactor, friction; + + //e6y + if (!mbf_features) + { + int momentum; + + movefactor = ORIG_FRICTION_FACTOR; + + if (!compatibility && variable_friction && + !(mo->flags & (MF_NOGRAVITY | MF_NOCLIP))) + { + friction = mo->friction; + if (friction == ORIG_FRICTION) // normal floor + ; + else if (friction > ORIG_FRICTION) // ice + { + movefactor = mo->movefactor; + mo->movefactor = ORIG_FRICTION_FACTOR; // reset + } + else // sludge + { + + // phares 3/11/98: you start off slowly, then increase as + // you get better footing + + momentum = (P_AproxDistance(mo->momx,mo->momy)); + movefactor = mo->movefactor; + if (momentum > MORE_FRICTION_MOMENTUM<<2) + movefactor <<= 3; + + else if (momentum > MORE_FRICTION_MOMENTUM<<1) + movefactor <<= 2; + + else if (momentum > MORE_FRICTION_MOMENTUM) + movefactor <<= 1; + + mo->movefactor = ORIG_FRICTION_FACTOR; // reset + } + } // ^ + + return(movefactor); // | + } + + // If the floor is icy or muddy, it's harder to get moving. This is where + // the different friction factors are applied to 'trying to move'. In + // p_mobj.c, the friction factors are applied as you coast and slow down. + + if ((friction = P_GetFriction(mo, &movefactor)) < ORIG_FRICTION) + { + // phares 3/11/98: you start off slowly, then increase as + // you get better footing + + int momentum = P_AproxDistance(mo->momx,mo->momy); + + if (momentum > MORE_FRICTION_MOMENTUM<<2) + movefactor <<= 3; + else if (momentum > MORE_FRICTION_MOMENTUM<<1) + movefactor <<= 2; + else if (momentum > MORE_FRICTION_MOMENTUM) + movefactor <<= 1; + } + + if (frictionp) + *frictionp = friction; + + return movefactor; +} + +// +// P_TeleportMove +// + +boolean P_TeleportMove (mobj_t* thing,fixed_t x,fixed_t y, boolean boss) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + + subsector_t* newsubsec; + + /* killough 8/9/98: make telefragging more consistent, preserve compatibility */ + telefrag = thing->player || + (!comp[comp_telefrag] ? boss : (gamemap==30)); + + // kill anything occupying the position + + tmthing = thing; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector (x,y); + ceilingline = NULL; + + // The base floor/ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + + validcount++; + numspechit = 0; + + // stomp on any things contacted + + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockThingsIterator(bx,by,PIT_StompThing)) + return false; + + // the move is ok, + // so unlink from the old position & link into the new position + + P_UnsetThingPosition (thing); + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->dropoffz = tmdropoffz; // killough 11/98 + + thing->x = x; + thing->y = y; + + P_SetThingPosition (thing); + + thing->PrevX = x; + thing->PrevY = y; + thing->PrevZ = thing->floorz; + + return true; +} + + +// +// MOVEMENT ITERATOR FUNCTIONS +// + +// e6y: Spechits overrun emulation code +static void SpechitOverrun(line_t *ld); + +// // phares +// PIT_CrossLine // | +// Checks to see if a PE->LS trajectory line crosses a blocking // V +// line. Returns false if it does. +// +// tmbbox holds the bounding box of the trajectory. If that box +// does not touch the bounding box of the line in question, +// then the trajectory is not blocked. If the PE is on one side +// of the line and the LS is on the other side, then the +// trajectory is blocked. +// +// Currently this assumes an infinite line, which is not quite +// correct. A more correct solution would be to check for an +// intersection of the trajectory and the line, but that takes +// longer and probably really isn't worth the effort. +// + +static // killough 3/26/98: make static +boolean PIT_CrossLine (line_t* ld) +{ + if (!(ld->flags & ML_TWOSIDED) || + (ld->flags & (ML_BLOCKING|ML_BLOCKMONSTERS))) + if (!(tmbbox[BOXLEFT] > ld->bbox[BOXRIGHT] || + tmbbox[BOXRIGHT] < ld->bbox[BOXLEFT] || + tmbbox[BOXTOP] < ld->bbox[BOXBOTTOM] || + tmbbox[BOXBOTTOM] > ld->bbox[BOXTOP])) + if (P_PointOnLineSide(pe_x,pe_y,ld) != P_PointOnLineSide(ls_x,ls_y,ld)) + return(false); // line blocks trajectory // ^ + return(true); // line doesn't block trajectory // | +} // phares + + +/* killough 8/1/98: used to test intersection between thing and line + * assuming NO movement occurs -- used to avoid sticky situations. + */ + +static int untouched(line_t *ld) +{ + fixed_t x, y, tmbbox[4]; + return + (tmbbox[BOXRIGHT] = (x=tmthing->x)+tmthing->radius) <= ld->bbox[BOXLEFT] || + (tmbbox[BOXLEFT] = x-tmthing->radius) >= ld->bbox[BOXRIGHT] || + (tmbbox[BOXTOP] = (y=tmthing->y)+tmthing->radius) <= ld->bbox[BOXBOTTOM] || + (tmbbox[BOXBOTTOM] = y-tmthing->radius) >= ld->bbox[BOXTOP] || + P_BoxOnLineSide(tmbbox, ld) != -1; +} + +// +// PIT_CheckLine +// Adjusts tmfloorz and tmceilingz as lines are contacted +// + +static // killough 3/26/98: make static +boolean PIT_CheckLine (line_t* ld) +{ + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] + || tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] + || tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] + || tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP] ) + return true; // didn't hit it + + if (P_BoxOnLineSide(tmbbox, ld) != -1) + return true; // didn't hit it + + // A line has been hit + + // The moving thing's destination position will cross the given line. + // If this should not be allowed, return false. + // If the line is special, keep track of it + // to process later if the move is proven ok. + // NOTE: specials are NOT sorted by order, + // so two special lines that are only 8 pixels apart + // could be crossed in either order. + + // killough 7/24/98: allow player to move out of 1s wall, to prevent sticking + if (!ld->backsector) // one sided line + { + blockline = ld; + return tmunstuck && !untouched(ld) && + FixedMul(tmx-tmthing->x,ld->dy) > FixedMul(tmy-tmthing->y,ld->dx); + } + + // killough 8/10/98: allow bouncing objects to pass through as missiles + if (!(tmthing->flags & (MF_MISSILE | MF_BOUNCES))) + { + if (ld->flags & ML_BLOCKING) // explicitly blocking everything + return tmunstuck && !untouched(ld); // killough 8/1/98: allow escape + + // killough 8/9/98: monster-blockers don't affect friends + if (!(tmthing->flags & MF_FRIEND || tmthing->player) + && ld->flags & ML_BLOCKMONSTERS) + return false; // block monsters only + } + + // set openrange, opentop, openbottom + // these define a 'window' from one sector to another across this line + + P_LineOpening (ld); + + // adjust floor & ceiling heights + + if (opentop < tmceilingz) + { + tmceilingz = opentop; + ceilingline = ld; + blockline = ld; + } + + if (openbottom > tmfloorz) + { + tmfloorz = openbottom; + floorline = ld; // killough 8/1/98: remember floor linedef + blockline = ld; + } + + if (lowfloor < tmdropoffz) + tmdropoffz = lowfloor; + + // if contacted a special line, add it to the list + + if (ld->special) + { + // 1/11/98 killough: remove limit on lines hit, by array doubling + if (numspechit >= spechit_max) { + spechit_max = spechit_max ? spechit_max*2 : 8; + spechit = realloc(spechit,sizeof *spechit*spechit_max); // killough + } + spechit[numspechit++] = ld; + // e6y: Spechits overrun emulation code + if (numspechit >= 8 && demo_compatibility) + SpechitOverrun(ld); + } + + return true; +} + +// +// PIT_CheckThing +// + +static boolean PIT_CheckThing(mobj_t *thing) // killough 3/26/98: make static +{ + fixed_t blockdist; + int damage; + + // killough 11/98: add touchy things + if (!(thing->flags & (MF_SOLID|MF_SPECIAL|MF_SHOOTABLE|MF_TOUCHY))) + return true; + + blockdist = thing->radius + tmthing->radius; + + if (D_abs(thing->x - tmx) >= blockdist || D_abs(thing->y - tmy) >= blockdist) + return true; // didn't hit it + + // killough 11/98: + // + // This test has less information content (it's almost always false), so it + // should not be moved up to first, as it adds more overhead than it removes. + + // don't clip against self + + if (thing == tmthing) + return true; + + /* killough 11/98: + * + * TOUCHY flag, for mines or other objects which die on contact with solids. + * If a solid object of a different type comes in contact with a touchy + * thing, and the touchy thing is not the sole one moving relative to fixed + * surroundings such as walls, then the touchy thing dies immediately. + */ + + if (thing->flags & MF_TOUCHY && // touchy object + tmthing->flags & MF_SOLID && // solid object touches it + thing->health > 0 && // touchy object is alive + (thing->intflags & MIF_ARMED || // Thing is an armed mine + sentient(thing)) && // ... or a sentient thing + (thing->type != tmthing->type || // only different species + thing->type == MT_PLAYER) && // ... or different players + thing->z + thing->height >= tmthing->z && // touches vertically + tmthing->z + tmthing->height >= thing->z && + (thing->type ^ MT_PAIN) | // PEs and lost souls + (tmthing->type ^ MT_SKULL) && // are considered same + (thing->type ^ MT_SKULL) | // (but Barons & Knights + (tmthing->type ^ MT_PAIN)) // are intentionally not) + { + P_DamageMobj(thing, NULL, NULL, thing->health); // kill object + return true; + } + + // check for skulls slamming into things + + if (tmthing->flags & MF_SKULLFLY) + { + // A flying skull is smacking something. + // Determine damage amount, and the skull comes to a dead stop. + + int damage = ((P_Random(pr_skullfly)%8)+1)*tmthing->info->damage; + + P_DamageMobj (thing, tmthing, tmthing, damage); + + tmthing->flags &= ~MF_SKULLFLY; + tmthing->momx = tmthing->momy = tmthing->momz = 0; + + P_SetMobjState (tmthing, tmthing->info->spawnstate); + + return false; // stop moving + } + + // missiles can hit other things + // killough 8/10/98: bouncing non-solid things can hit other things too + + if (tmthing->flags & MF_MISSILE || (tmthing->flags & MF_BOUNCES && + !(tmthing->flags & MF_SOLID))) + { + // see if it went over / under + + if (tmthing->z > thing->z + thing->height) + return true; // overhead + + if (tmthing->z+tmthing->height < thing->z) + return true; // underneath + + if (tmthing->target && (tmthing->target->type == thing->type || + (tmthing->target->type == MT_KNIGHT && thing->type == MT_BRUISER)|| + (tmthing->target->type == MT_BRUISER && thing->type == MT_KNIGHT))) + { + if (thing == tmthing->target) + return true; // Don't hit same species as originator. + else + // e6y: Dehacked support - monsters infight + if (thing->type != MT_PLAYER && !monsters_infight) // Explode, but do no damage. + return false; // Let players missile other players. + } + + // killough 8/10/98: if moving thing is not a missile, no damage + // is inflicted, and momentum is reduced if object hit is solid. + + if (!(tmthing->flags & MF_MISSILE)) { + if (!(thing->flags & MF_SOLID)) { + return true; + } else { + tmthing->momx = -tmthing->momx; + tmthing->momy = -tmthing->momy; + if (!(tmthing->flags & MF_NOGRAVITY)) + { + tmthing->momx >>= 2; + tmthing->momy >>= 2; + } + return false; + } + } + + if (!(thing->flags & MF_SHOOTABLE)) + return !(thing->flags & MF_SOLID); // didn't do any damage + + // damage / explode + + damage = ((P_Random(pr_damage)%8)+1)*tmthing->info->damage; + P_DamageMobj (thing, tmthing, tmthing->target, damage); + + // don't traverse any more + return false; + } + + // check for special pickup + + if (thing->flags & MF_SPECIAL) + { + uint_64_t solid = thing->flags & MF_SOLID; + if (tmthing->flags & MF_PICKUP) + P_TouchSpecialThing(thing, tmthing); // can remove thing + return !solid; + } + + // killough 3/16/98: Allow non-solid moving objects to move through solid + // ones, by allowing the moving thing (tmthing) to move if it's non-solid, + // despite another solid thing being in the way. + // killough 4/11/98: Treat no-clipping things as not blocking + // ...but not in demo_compatibility mode + + return !(thing->flags & MF_SOLID) + || (!demo_compatibility + && (thing->flags & MF_NOCLIP || !(tmthing->flags & MF_SOLID))); + + // return !(thing->flags & MF_SOLID); // old code -- killough +} + +// This routine checks for Lost Souls trying to be spawned // phares +// across 1-sided lines, impassible lines, or "monsters can't // | +// cross" lines. Draw an imaginary line between the PE // V +// and the new Lost Soul spawn spot. If that line crosses +// a 'blocking' line, then disallow the spawn. Only search +// lines in the blocks of the blockmap where the bounding box +// of the trajectory line resides. Then check bounding box +// of the trajectory vs. the bounding box of each blocking +// line to see if the trajectory and the blocking line cross. +// Then check the PE and LS to see if they're on different +// sides of the blocking line. If so, return true, otherwise +// false. + +boolean Check_Sides(mobj_t* actor, int x, int y) +{ + int bx,by,xl,xh,yl,yh; + + pe_x = actor->x; + pe_y = actor->y; + ls_x = x; + ls_y = y; + + // Here is the bounding box of the trajectory + + tmbbox[BOXLEFT] = pe_x < x ? pe_x : x; + tmbbox[BOXRIGHT] = pe_x > x ? pe_x : x; + tmbbox[BOXTOP] = pe_y > y ? pe_y : y; + tmbbox[BOXBOTTOM] = pe_y < y ? pe_y : y; + + // Determine which blocks to look in for blocking lines + + xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT; + + // xl->xh, yl->yh determine the mapblock set to search + + validcount++; // prevents checking same line twice + for (bx = xl ; bx <= xh ; bx++) + for (by = yl ; by <= yh ; by++) + if (!P_BlockLinesIterator(bx,by,PIT_CrossLine)) + return true; // ^ + return(false); // | +} // phares + +// +// MOVEMENT CLIPPING +// + +// +// P_CheckPosition +// This is purely informative, nothing is modified +// (except things picked up). +// +// in: +// a mobj_t (can be valid or invalid) +// a position to be checked +// (doesn't need to be related to the mobj_t->x,y) +// +// during: +// special things are touched if MF_PICKUP +// early out on solid lines? +// +// out: +// newsubsec +// floorz +// ceilingz +// tmdropoffz +// the lowest point contacted +// (monsters won't move to a dropoff) +// speciallines[] +// numspeciallines +// + +boolean P_CheckPosition (mobj_t* thing,fixed_t x,fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + subsector_t* newsubsec; + + tmthing = thing; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + newsubsec = R_PointInSubsector (x,y); + floorline = blockline = ceilingline = NULL; // killough 8/1/98 + + // Whether object can get out of a sticky situation: + tmunstuck = thing->player && /* only players */ + thing->player->mo == thing && /* not voodoo dolls */ + mbf_features; /* not under old demos */ + + // The base floor / ceiling is from the subsector + // that contains the point. + // Any contacted lines the step closer together + // will adjust them. + + tmfloorz = tmdropoffz = newsubsec->sector->floorheight; + tmceilingz = newsubsec->sector->ceilingheight; + validcount++; + numspechit = 0; + + if ( tmthing->flags & MF_NOCLIP ) + return true; + + // Check things first, possibly picking things up. + // The bounding box is extended by MAXRADIUS + // because mobj_ts are grouped into mapblocks + // based on their origin point, and can overlap + // into adjacent blocks by up to MAXRADIUS units. + + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockThingsIterator(bx,by,PIT_CheckThing)) + return false; + + // check lines + + xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT; + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + if (!P_BlockLinesIterator (bx,by,PIT_CheckLine)) + return false; // doesn't fit + + return true; +} + + +// +// P_TryMove +// Attempt to move to a new position, +// crossing special lines unless MF_TELEPORT is set. +// +boolean P_TryMove(mobj_t* thing,fixed_t x,fixed_t y, + boolean dropoff) // killough 3/15/98: allow dropoff as option + { + fixed_t oldx; + fixed_t oldy; + + felldown = floatok = false; // killough 11/98 + + if (!P_CheckPosition (thing, x, y)) + return false; // solid wall or thing + + if ( !(thing->flags & MF_NOCLIP) ) + { + // killough 7/26/98: reformatted slightly + // killough 8/1/98: Possibly allow escape if otherwise stuck + + if (tmceilingz - tmfloorz < thing->height || // doesn't fit + // mobj must lower to fit + (floatok = true, !(thing->flags & MF_TELEPORT) && + tmceilingz - thing->z < thing->height) || + // too big a step up + (!(thing->flags & MF_TELEPORT) && + tmfloorz - thing->z > 24*FRACUNIT)) + return tmunstuck + && !(ceilingline && untouched(ceilingline)) + && !( floorline && untouched( floorline)); + + /* killough 3/15/98: Allow certain objects to drop off + * killough 7/24/98, 8/1/98: + * Prevent monsters from getting stuck hanging off ledges + * killough 10/98: Allow dropoffs in controlled circumstances + * killough 11/98: Improve symmetry of clipping on stairs + */ + + if (!(thing->flags & (MF_DROPOFF|MF_FLOAT))) { + if (comp[comp_dropoff]) + { + if ((compatibility || !dropoff + // fix demosync bug in mbf compatibility mode + || (mbf_features && compatibility_level <= prboom_2_compatibility)) + && (tmfloorz - tmdropoffz > 24*FRACUNIT)) + return false; // don't stand over a dropoff + } + else + if (!dropoff || (dropoff==2 && // large jump down (e.g. dogs) + (tmfloorz-tmdropoffz > 128*FRACUNIT || + !thing->target || thing->target->z >tmdropoffz))) + { + if (!monkeys || !mbf_features ? + tmfloorz - tmdropoffz > 24*FRACUNIT : + thing->floorz - tmfloorz > 24*FRACUNIT || + thing->dropoffz - tmdropoffz > 24*FRACUNIT) + return false; + } + else { /* dropoff allowed -- check for whether it fell more than 24 */ + felldown = !(thing->flags & MF_NOGRAVITY) && + thing->z - tmfloorz > 24*FRACUNIT; + } + } + + if (thing->flags & MF_BOUNCES && // killough 8/13/98 + !(thing->flags & (MF_MISSILE|MF_NOGRAVITY)) && + !sentient(thing) && tmfloorz - thing->z > 16*FRACUNIT) + return false; // too big a step up for bouncers under gravity + + // killough 11/98: prevent falling objects from going up too many steps + if (thing->intflags & MIF_FALLING && tmfloorz - thing->z > + FixedMul(thing->momx,thing->momx)+FixedMul(thing->momy,thing->momy)) + return false; + } + + // the move is ok, + // so unlink from the old position and link into the new position + + P_UnsetThingPosition (thing); + + oldx = thing->x; + oldy = thing->y; + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->dropoffz = tmdropoffz; // killough 11/98: keep track of dropoffs + thing->x = x; + thing->y = y; + + P_SetThingPosition (thing); + + // if any special lines were hit, do the effect + + if (! (thing->flags&(MF_TELEPORT|MF_NOCLIP)) ) + while (numspechit--) + if (spechit[numspechit]->special) // see if the line was crossed + { + int oldside; + if ((oldside = P_PointOnLineSide(oldx, oldy, spechit[numspechit])) != + P_PointOnLineSide(thing->x, thing->y, spechit[numspechit])) + P_CrossSpecialLine(spechit[numspechit], oldside, thing); + } + + return true; + } + +/* + * killough 9/12/98: + * + * Apply "torque" to objects hanging off of ledges, so that they + * fall off. It's not really torque, since Doom has no concept of + * rotation, but it's a convincing effect which avoids anomalies + * such as lifeless objects hanging more than halfway off of ledges, + * and allows objects to roll off of the edges of moving lifts, or + * to slide up and then back down stairs, or to fall into a ditch. + * If more than one linedef is contacted, the effects are cumulative, + * so balancing is possible. + */ + +static boolean PIT_ApplyTorque(line_t *ld) +{ + if (ld->backsector && // If thing touches two-sided pivot linedef + tmbbox[BOXRIGHT] > ld->bbox[BOXLEFT] && + tmbbox[BOXLEFT] < ld->bbox[BOXRIGHT] && + tmbbox[BOXTOP] > ld->bbox[BOXBOTTOM] && + tmbbox[BOXBOTTOM] < ld->bbox[BOXTOP] && + P_BoxOnLineSide(tmbbox, ld) == -1) + { + mobj_t *mo = tmthing; + + fixed_t dist = // lever arm + + (ld->dx >> FRACBITS) * (mo->y >> FRACBITS) + - (ld->dy >> FRACBITS) * (mo->x >> FRACBITS) + - (ld->dx >> FRACBITS) * (ld->v1->y >> FRACBITS) + + (ld->dy >> FRACBITS) * (ld->v1->x >> FRACBITS); + + if (dist < 0 ? // dropoff direction + ld->frontsector->floorheight < mo->z && + ld->backsector->floorheight >= mo->z : + ld->backsector->floorheight < mo->z && + ld->frontsector->floorheight >= mo->z) + { + /* At this point, we know that the object straddles a two-sided + * linedef, and that the object's center of mass is above-ground. + */ + + fixed_t x = D_abs(ld->dx), y = D_abs(ld->dy); + + if (y > x) + { + fixed_t t = x; + x = y; + y = t; + } + + y = finesine[(tantoangle[FixedDiv(y,x)>>DBITS] + + ANG90) >> ANGLETOFINESHIFT]; + + /* Momentum is proportional to distance between the + * object's center of mass and the pivot linedef. + * + * It is scaled by 2^(OVERDRIVE - gear). When gear is + * increased, the momentum gradually decreases to 0 for + * the same amount of pseudotorque, so that oscillations + * are prevented, yet it has a chance to reach equilibrium. + */ + dist = FixedDiv(FixedMul(dist, (mo->gear < OVERDRIVE) ? + y << -(mo->gear - OVERDRIVE) : + y >> +(mo->gear - OVERDRIVE)), x); + + /* Apply momentum away from the pivot linedef. */ + + x = FixedMul(ld->dy, dist); + y = FixedMul(ld->dx, dist); + + /* Avoid moving too fast all of a sudden (step into "overdrive") */ + + dist = FixedMul(x,x) + FixedMul(y,y); + + while (dist > FRACUNIT*4 && mo->gear < MAXGEAR) + ++mo->gear, x >>= 1, y >>= 1, dist >>= 1; + + mo->momx -= x; + mo->momy += y; + } + } + return true; +} + +/* + * killough 9/12/98 + * + * Applies "torque" to objects, based on all contacted linedefs + */ + +void P_ApplyTorque(mobj_t *mo) +{ + int xl = ((tmbbox[BOXLEFT] = + mo->x - mo->radius) - bmaporgx) >> MAPBLOCKSHIFT; + int xh = ((tmbbox[BOXRIGHT] = + mo->x + mo->radius) - bmaporgx) >> MAPBLOCKSHIFT; + int yl = ((tmbbox[BOXBOTTOM] = + mo->y - mo->radius) - bmaporgy) >> MAPBLOCKSHIFT; + int yh = ((tmbbox[BOXTOP] = + mo->y + mo->radius) - bmaporgy) >> MAPBLOCKSHIFT; + int bx,by,flags = mo->intflags; //Remember the current state, for gear-change + + tmthing = mo; + validcount++; /* prevents checking same line twice */ + + for (bx = xl ; bx <= xh ; bx++) + for (by = yl ; by <= yh ; by++) + P_BlockLinesIterator(bx, by, PIT_ApplyTorque); + + /* If any momentum, mark object as 'falling' using engine-internal flags */ + if (mo->momx | mo->momy) + mo->intflags |= MIF_FALLING; + else // Clear the engine-internal flag indicating falling object. + mo->intflags &= ~MIF_FALLING; + + /* If the object has been moving, step up the gear. + * This helps reach equilibrium and avoid oscillations. + * + * Doom has no concept of potential energy, much less + * of rotation, so we have to creatively simulate these + * systems somehow :) + */ + + if (!((mo->intflags | flags) & MIF_FALLING)) // If not falling for a while, + mo->gear = 0; // Reset it to full strength + else + if (mo->gear < MAXGEAR) // Else if not at max gear, + mo->gear++; // move up a gear +} + +// +// P_ThingHeightClip +// Takes a valid thing and adjusts the thing->floorz, +// thing->ceilingz, and possibly thing->z. +// This is called for all nearby monsters +// whenever a sector changes height. +// If the thing doesn't fit, +// the z will be set to the lowest value +// and false will be returned. +// + +boolean P_ThingHeightClip (mobj_t* thing) +{ + boolean onfloor; + + onfloor = (thing->z == thing->floorz); + + P_CheckPosition (thing, thing->x, thing->y); + + /* what about stranding a monster partially off an edge? + * killough 11/98: Answer: see below (upset balance if hanging off ledge) + */ + + thing->floorz = tmfloorz; + thing->ceilingz = tmceilingz; + thing->dropoffz = tmdropoffz; /* killough 11/98: remember dropoffs */ + + if (onfloor) + { + + // walking monsters rise and fall with the floor + + thing->z = thing->floorz; + + /* killough 11/98: Possibly upset balance of objects hanging off ledges */ + if (thing->intflags & MIF_FALLING && thing->gear >= MAXGEAR) + thing->gear = 0; + } + else + { + + // don't adjust a floating monster unless forced to + + if (thing->z+thing->height > thing->ceilingz) + thing->z = thing->ceilingz - thing->height; + } + + return thing->ceilingz - thing->floorz >= thing->height; +} + + +// +// SLIDE MOVE +// Allows the player to slide along any angled walls. +// + +/* killough 8/2/98: make variables static */ +static fixed_t bestslidefrac; +static line_t* bestslideline; +static mobj_t* slidemo; +static fixed_t tmxmove; +static fixed_t tmymove; + + +// +// P_HitSlideLine +// Adjusts the xmove / ymove +// so that the next move will slide along the wall. +// If the floor is icy, then you can bounce off a wall. // phares +// + +void P_HitSlideLine (line_t* ld) +{ + int side; + angle_t lineangle; + angle_t moveangle; + angle_t deltaangle; + fixed_t movelen; + fixed_t newlen; + boolean icyfloor; // is floor icy? // phares + // | + // Under icy conditions, if the angle of approach to the wall // V + // is more than 45 degrees, then you'll bounce and lose half + // your momentum. If less than 45 degrees, you'll slide along + // the wall. 45 is arbitrary and is believable. + + // Check for the special cases of horz or vert walls. + + /* killough 10/98: only bounce if hit hard (prevents wobbling) + * cph - DEMOSYNC - should only affect players in Boom demos? */ + + //e6y + if (mbf_features) + { + icyfloor = + P_AproxDistance(tmxmove, tmymove) > 4*FRACUNIT && + variable_friction && // killough 8/28/98: calc friction on demand + slidemo->z <= slidemo->floorz && + P_GetFriction(slidemo, NULL) > ORIG_FRICTION; + } + else + { + extern boolean onground; + icyfloor = !compatibility && + variable_friction && + slidemo->player && + onground && + slidemo->friction > ORIG_FRICTION; + } + + if (ld->slopetype == ST_HORIZONTAL) + { + if (icyfloor && (D_abs(tmymove) > D_abs(tmxmove))) + { + tmxmove /= 2; // absorb half the momentum + tmymove = -tmymove/2; + S_StartSound(slidemo,sfx_oof); // oooff! + } + else + tmymove = 0; // no more movement in the Y direction + return; + } + + if (ld->slopetype == ST_VERTICAL) + { + if (icyfloor && (D_abs(tmxmove) > D_abs(tmymove))) + { + tmxmove = -tmxmove/2; // absorb half the momentum + tmymove /= 2; + S_StartSound(slidemo,sfx_oof); // oooff! // ^ + } // | + else // phares + tmxmove = 0; // no more movement in the X direction + return; + } + + // The wall is angled. Bounce if the angle of approach is // phares + // less than 45 degrees. // phares + + side = P_PointOnLineSide (slidemo->x, slidemo->y, ld); + + lineangle = R_PointToAngle2 (0,0, ld->dx, ld->dy); + if (side == 1) + lineangle += ANG180; + moveangle = R_PointToAngle2 (0,0, tmxmove, tmymove); + + // killough 3/2/98: + // The moveangle+=10 breaks v1.9 demo compatibility in + // some demos, so it needs demo_compatibility switch. + + if (!demo_compatibility) + moveangle += 10; // prevents sudden path reversal due to // phares + // rounding error // | + deltaangle = moveangle-lineangle; // V + movelen = P_AproxDistance (tmxmove, tmymove); + if (icyfloor && (deltaangle > ANG45) && (deltaangle < ANG90+ANG45)) + { + moveangle = lineangle - deltaangle; + movelen /= 2; // absorb + S_StartSound(slidemo,sfx_oof); // oooff! + moveangle >>= ANGLETOFINESHIFT; + tmxmove = FixedMul (movelen, finecosine[moveangle]); + tmymove = FixedMul (movelen, finesine[moveangle]); + } // ^ + else // | + { // phares + if (deltaangle > ANG180) + deltaangle += ANG180; + + // I_Error ("SlideLine: ang>ANG180"); + + lineangle >>= ANGLETOFINESHIFT; + deltaangle >>= ANGLETOFINESHIFT; + newlen = FixedMul (movelen, finecosine[deltaangle]); + tmxmove = FixedMul (newlen, finecosine[lineangle]); + tmymove = FixedMul (newlen, finesine[lineangle]); + } // phares +} + + +// +// PTR_SlideTraverse +// + +boolean PTR_SlideTraverse (intercept_t* in) +{ + line_t* li; + + if (!in->isaline) + I_Error ("PTR_SlideTraverse: not a line?"); + + li = in->d.line; + + if ( ! (li->flags & ML_TWOSIDED) ) + { + if (P_PointOnLineSide (slidemo->x, slidemo->y, li)) + return true; // don't hit the back side + goto isblocking; + } + + // set openrange, opentop, openbottom. + // These define a 'window' from one sector to another across a line + + P_LineOpening (li); + + if (openrange < slidemo->height) + goto isblocking; // doesn't fit + + if (opentop - slidemo->z < slidemo->height) + goto isblocking; // mobj is too high + + if (openbottom - slidemo->z > 24*FRACUNIT ) + goto isblocking; // too big a step up + + // this line doesn't block movement + + return true; + + // the line does block movement, + // see if it is closer than best so far + +isblocking: + + if (in->frac < bestslidefrac) + { + bestslidefrac = in->frac; + bestslideline = li; + } + + return false; // stop +} + + +// +// P_SlideMove +// The momx / momy move is bad, so try to slide +// along a wall. +// Find the first line hit, move flush to it, +// and slide along it +// +// This is a kludgy mess. +// +// killough 11/98: reformatted + +void P_SlideMove(mobj_t *mo) +{ + int hitcount = 3; + + slidemo = mo; // the object that's sliding + + do + { + fixed_t leadx, leady, trailx, traily; + + if (!--hitcount) + goto stairstep; // don't loop forever + + // trace along the three leading corners + + if (mo->momx > 0) + leadx = mo->x + mo->radius, trailx = mo->x - mo->radius; + else + leadx = mo->x - mo->radius, trailx = mo->x + mo->radius; + + if (mo->momy > 0) + leady = mo->y + mo->radius, traily = mo->y - mo->radius; + else + leady = mo->y - mo->radius, traily = mo->y + mo->radius; + + bestslidefrac = FRACUNIT+1; + + P_PathTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(trailx, leady, trailx+mo->momx, leady+mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + P_PathTraverse(leadx, traily, leadx+mo->momx, traily+mo->momy, + PT_ADDLINES, PTR_SlideTraverse); + + // move up to the wall + + if (bestslidefrac == FRACUNIT+1) + { + // the move must have hit the middle, so stairstep + + stairstep: + + /* killough 3/15/98: Allow objects to drop off ledges + * + * phares 5/4/98: kill momentum if you can't move at all + * This eliminates player bobbing if pressed against a wall + * while on ice. + * + * killough 10/98: keep buggy code around for old Boom demos + * + * cph 2000/09//23: buggy code was only in Boom v2.01 + */ + + if (!P_TryMove(mo, mo->x, mo->y + mo->momy, true)) + if (!P_TryMove(mo, mo->x + mo->momx, mo->y, true)) + if (compatibility_level == boom_201_compatibility) + mo->momx = mo->momy = 0; + + break; + } + + // fudge a bit to make sure it doesn't hit + + if ((bestslidefrac -= 0x800) > 0) + { + fixed_t newx = FixedMul(mo->momx, bestslidefrac); + fixed_t newy = FixedMul(mo->momy, bestslidefrac); + + // killough 3/15/98: Allow objects to drop off ledges + + if (!P_TryMove(mo, mo->x+newx, mo->y+newy, true)) + goto stairstep; + } + + // Now continue along the wall. + // First calculate remainder. + + bestslidefrac = FRACUNIT-(bestslidefrac+0x800); + + if (bestslidefrac > FRACUNIT) + bestslidefrac = FRACUNIT; + + if (bestslidefrac <= 0) + break; + + tmxmove = FixedMul(mo->momx, bestslidefrac); + tmymove = FixedMul(mo->momy, bestslidefrac); + + P_HitSlideLine(bestslideline); // clip the moves + + mo->momx = tmxmove; + mo->momy = tmymove; + + /* killough 10/98: affect the bobbing the same way (but not voodoo dolls) + * cph - DEMOSYNC? */ + if (mo->player && mo->player->mo == mo) + { + if (D_abs(mo->player->momx) > D_abs(tmxmove)) + mo->player->momx = tmxmove; + if (D_abs(mo->player->momy) > D_abs(tmymove)) + mo->player->momy = tmymove; + } + } // killough 3/15/98: Allow objects to drop off ledges: + while (!P_TryMove(mo, mo->x+tmxmove, mo->y+tmymove, true)); +} + +// +// P_LineAttack +// +mobj_t* linetarget; // who got hit (or NULL) +static mobj_t* shootthing; + +/* killough 8/2/98: for more intelligent autoaiming */ +static uint_64_t aim_flags_mask; + +// Height if not aiming up or down +fixed_t shootz; + +int la_damage; +fixed_t attackrange; + +static fixed_t aimslope; + +// slopes to top and bottom of target +// killough 4/20/98: make static instead of using ones in p_sight.c + +static fixed_t topslope; +static fixed_t bottomslope; + + +// +// PTR_AimTraverse +// Sets linetaget and aimslope when a target is aimed at. +// +boolean PTR_AimTraverse (intercept_t* in) +{ + line_t* li; + mobj_t* th; + fixed_t slope; + fixed_t thingtopslope; + fixed_t thingbottomslope; + fixed_t dist; + + if (in->isaline) + { + li = in->d.line; + + if ( !(li->flags & ML_TWOSIDED) ) + return false; // stop + + // Crosses a two sided line. + // A two sided line will restrict + // the possible target ranges. + + P_LineOpening (li); + + if (openbottom >= opentop) + return false; // stop + + dist = FixedMul (attackrange, in->frac); + + if (li->frontsector->floorheight != li->backsector->floorheight) + { + slope = FixedDiv (openbottom - shootz , dist); + if (slope > bottomslope) + bottomslope = slope; + } + + if (li->frontsector->ceilingheight != li->backsector->ceilingheight) + { + slope = FixedDiv (opentop - shootz , dist); + if (slope < topslope) + topslope = slope; + } + + if (topslope <= bottomslope) + return false; // stop + + return true; // shot continues + } + + // shoot a thing + + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags&MF_SHOOTABLE)) + return true; // corpse or something + + /* killough 7/19/98, 8/2/98: + * friends don't aim at friends (except players), at least not first + */ + if (th->flags & shootthing->flags & aim_flags_mask && !th->player) + return true; + + // check angles to see if the thing can be aimed at + + dist = FixedMul (attackrange, in->frac); + thingtopslope = FixedDiv (th->z+th->height - shootz , dist); + + if (thingtopslope < bottomslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv (th->z - shootz, dist); + + if (thingbottomslope > topslope) + return true; // shot under the thing + + // this thing can be hit! + + if (thingtopslope > topslope) + thingtopslope = topslope; + + if (thingbottomslope < bottomslope) + thingbottomslope = bottomslope; + + aimslope = (thingtopslope+thingbottomslope)/2; + linetarget = th; + + return false; // don't go any farther +} + + +// +// PTR_ShootTraverse +// +boolean PTR_ShootTraverse (intercept_t* in) +{ + fixed_t x; + fixed_t y; + fixed_t z; + fixed_t frac; + + mobj_t* th; + + fixed_t slope; + fixed_t dist; + fixed_t thingtopslope; + fixed_t thingbottomslope; + + if (in->isaline) + { + line_t *li = in->d.line; + + if (li->special) + P_ShootSpecialLine (shootthing, li); + + if (li->flags & ML_TWOSIDED) + { // crosses a two sided (really 2s) line + P_LineOpening (li); + dist = FixedMul(attackrange, in->frac); + + // killough 11/98: simplify + + if ((li->frontsector->floorheight==li->backsector->floorheight || + (slope = FixedDiv(openbottom - shootz , dist)) <= aimslope) && + (li->frontsector->ceilingheight==li->backsector->ceilingheight || + (slope = FixedDiv (opentop - shootz , dist)) >= aimslope)) + return true; // shot continues + } + + // hit line + // position a bit closer + + frac = in->frac - FixedDiv (4*FRACUNIT,attackrange); + x = trace.x + FixedMul (trace.dx, frac); + y = trace.y + FixedMul (trace.dy, frac); + z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); + + if (li->frontsector->ceilingpic == skyflatnum) + { + // don't shoot the sky! + + if (z > li->frontsector->ceilingheight) + return false; + + // it's a sky hack wall + + if (li->backsector && li->backsector->ceilingpic == skyflatnum) + + // fix bullet-eaters -- killough: + // WARNING: Almost all demos will lose sync without this + // demo_compatibility flag check!!! killough 1/18/98 + if (demo_compatibility || li->backsector->ceilingheight < z) + return false; + } + + // Spawn bullet puffs. + + P_SpawnPuff (x,y,z); + + // don't go any farther + + return false; + } + + // shoot a thing + + th = in->d.thing; + if (th == shootthing) + return true; // can't shoot self + + if (!(th->flags&MF_SHOOTABLE)) + return true; // corpse or something + + // check angles to see if the thing can be aimed at + + dist = FixedMul (attackrange, in->frac); + thingtopslope = FixedDiv (th->z+th->height - shootz , dist); + + if (thingtopslope < aimslope) + return true; // shot over the thing + + thingbottomslope = FixedDiv (th->z - shootz, dist); + + if (thingbottomslope > aimslope) + return true; // shot under the thing + + // hit thing + // position a bit closer + + frac = in->frac - FixedDiv (10*FRACUNIT,attackrange); + + x = trace.x + FixedMul (trace.dx, frac); + y = trace.y + FixedMul (trace.dy, frac); + z = shootz + FixedMul (aimslope, FixedMul(frac, attackrange)); + + // Spawn bullet puffs or blod spots, + // depending on target type. + if (in->d.thing->flags & MF_NOBLOOD) + P_SpawnPuff (x,y,z); + else + P_SpawnBlood (x,y,z, la_damage); + + if (la_damage) + P_DamageMobj (th, shootthing, shootthing, la_damage); + + // don't go any farther + return false; +} + + +// +// P_AimLineAttack +// +fixed_t P_AimLineAttack(mobj_t* t1,angle_t angle,fixed_t distance, uint_64_t mask) +{ + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + + x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; + y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; + shootz = t1->z + (t1->height>>1) + 8*FRACUNIT; + + // can't shoot outside view angles + + topslope = 100*FRACUNIT/160; + bottomslope = -100*FRACUNIT/160; + + attackrange = distance; + linetarget = NULL; + + /* killough 8/2/98: prevent friends from aiming at friends */ + aim_flags_mask = mask; + + P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_AimTraverse); + + if (linetarget) + return aimslope; + + return 0; +} + + +// +// P_LineAttack +// If damage == 0, it is just a test trace +// that will leave linetarget set. +// + +void P_LineAttack +(mobj_t* t1, + angle_t angle, + fixed_t distance, + fixed_t slope, + int damage) + { + fixed_t x2; + fixed_t y2; + + angle >>= ANGLETOFINESHIFT; + shootthing = t1; + la_damage = damage; + x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; + y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; + shootz = t1->z + (t1->height>>1) + 8*FRACUNIT; + attackrange = distance; + aimslope = slope; + + P_PathTraverse(t1->x,t1->y,x2,y2,PT_ADDLINES|PT_ADDTHINGS,PTR_ShootTraverse); + } + + +// +// USE LINES +// + +mobj_t* usething; + +boolean PTR_UseTraverse (intercept_t* in) +{ + int side; + + if (!in->d.line->special) + { + P_LineOpening (in->d.line); + if (openrange <= 0) + { + S_StartSound (usething, sfx_noway); + + // can't use through a wall + return false; + } + + // not a special line, but keep checking + + return true; + } + + side = 0; + if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1) + side = 1; + + // return false; // don't use back side + + P_UseSpecialLine (usething, in->d.line, side); + + //WAS can't use for than one special line in a row + //jff 3/21/98 NOW multiple use allowed with enabling line flag + + return (!demo_compatibility && (in->d.line->flags&ML_PASSUSE))? + true : false; +} + +// Returns false if a "oof" sound should be made because of a blocking +// linedef. Makes 2s middles which are impassable, as well as 2s uppers +// and lowers which block the player, cause the sound effect when the +// player tries to activate them. Specials are excluded, although it is +// assumed that all special linedefs within reach have been considered +// and rejected already (see P_UseLines). +// +// by Lee Killough +// + +boolean PTR_NoWayTraverse(intercept_t* in) +{ + line_t *ld = in->d.line; + // This linedef + return ld->special || !( // Ignore specials + ld->flags & ML_BLOCKING || ( // Always blocking + P_LineOpening(ld), // Find openings + openrange <= 0 || // No opening + openbottom > usething->z+24*FRACUNIT || // Too high it blocks + opentop < usething->z+usething->height // Too low it blocks + ) + ); +} + +// +// P_UseLines +// Looks for special lines in front of the player to activate. +// +void P_UseLines (player_t* player) +{ + int angle; + fixed_t x1; + fixed_t y1; + fixed_t x2; + fixed_t y2; + + usething = player->mo; + + angle = player->mo->angle >> ANGLETOFINESHIFT; + + x1 = player->mo->x; + y1 = player->mo->y; + x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle]; + y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle]; + + // old code: + // + // P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse ); + // + // This added test makes the "oof" sound work on 2s lines -- killough: + + if (P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse )) + if (!comp[comp_sound] && !P_PathTraverse ( x1, y1, x2, y2, PT_ADDLINES, PTR_NoWayTraverse )) + S_StartSound (usething, sfx_noway); +} + + +// +// RADIUS ATTACK +// + +static mobj_t *bombsource, *bombspot; +static int bombdamage; + + +// +// PIT_RadiusAttack +// "bombsource" is the creature +// that caused the explosion at "bombspot". +// + +boolean PIT_RadiusAttack (mobj_t* thing) +{ + fixed_t dx; + fixed_t dy; + fixed_t dist; + + /* killough 8/20/98: allow bouncers to take damage + * (missile bouncers are already excluded with MF_NOBLOCKMAP) + */ + + if (!(thing->flags & (MF_SHOOTABLE | MF_BOUNCES))) + return true; + + // Boss spider and cyborg + // take no damage from concussion. + + // killough 8/10/98: allow grenades to hurt anyone, unless + // fired by Cyberdemons, in which case it won't hurt Cybers. + + if (bombspot->flags & MF_BOUNCES ? + thing->type == MT_CYBORG && bombsource->type == MT_CYBORG : + thing->type == MT_CYBORG || thing->type == MT_SPIDER) + return true; + + dx = D_abs(thing->x - bombspot->x); + dy = D_abs(thing->y - bombspot->y); + + dist = dx>dy ? dx : dy; + dist = (dist - thing->radius) >> FRACBITS; + + if (dist < 0) + dist = 0; + + if (dist >= bombdamage) + return true; // out of range + + if ( P_CheckSight (thing, bombspot) ) + { + // must be in direct path + P_DamageMobj (thing, bombspot, bombsource, bombdamage - dist); + } + + return true; +} + + +// +// P_RadiusAttack +// Source is the creature that caused the explosion at spot. +// +void P_RadiusAttack(mobj_t* spot,mobj_t* source,int damage) +{ + int x; + int y; + + int xl; + int xh; + int yl; + int yh; + + fixed_t dist; + + dist = (damage+MAXRADIUS)<y + dist - bmaporgy)>>MAPBLOCKSHIFT; + yl = (spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT; + xh = (spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT; + xl = (spot->x - dist - bmaporgx)>>MAPBLOCKSHIFT; + bombspot = spot; + bombsource = source; + bombdamage = damage; + + for (y=yl ; y<=yh ; y++) + for (x=xl ; x<=xh ; x++) + P_BlockThingsIterator (x, y, PIT_RadiusAttack ); +} + + + +// +// SECTOR HEIGHT CHANGING +// After modifying a sectors floor or ceiling height, +// call this routine to adjust the positions +// of all things that touch the sector. +// +// If anything doesn't fit anymore, true will be returned. +// If crunch is true, they will take damage +// as they are being crushed. +// If Crunch is false, you should set the sector height back +// the way it was and call P_ChangeSector again +// to undo the changes. +// + +static boolean crushchange, nofit; + +// +// PIT_ChangeSector +// + +boolean PIT_ChangeSector (mobj_t* thing) +{ + mobj_t* mo; + + if (P_ThingHeightClip (thing)) + return true; // keep checking + + // crunch bodies to giblets + + if (thing->health <= 0) + { + P_SetMobjState (thing, S_GIBS); + + thing->flags &= ~MF_SOLID; + thing->height = 0; + thing->radius = 0; + return true; // keep checking + } + + // crunch dropped items + + if (thing->flags & MF_DROPPED) + { + P_RemoveMobj (thing); + + // keep checking + return true; + } + + /* killough 11/98: kill touchy things immediately */ + if (thing->flags & MF_TOUCHY && + (thing->intflags & MIF_ARMED || sentient(thing))) + { + P_DamageMobj(thing, NULL, NULL, thing->health); // kill object + return true; // keep checking + } + + if (! (thing->flags & MF_SHOOTABLE) ) + { + // assume it is bloody gibs or something + return true; + } + + nofit = true; + + if (crushchange && !(leveltime&3)) { + int t; + P_DamageMobj(thing,NULL,NULL,10); + + // spray blood in a random direction + mo = P_SpawnMobj (thing->x, + thing->y, + thing->z + thing->height/2, MT_BLOOD); + + /* killough 8/10/98: remove dependence on order of evaluation */ + t = P_Random(pr_crush); + mo->momx = (t - P_Random (pr_crush))<<12; + t = P_Random(pr_crush); + mo->momy = (t - P_Random (pr_crush))<<12; + } + + // keep checking (crush other things) + return true; +} + + +// +// P_ChangeSector +// +boolean P_ChangeSector(sector_t* sector,boolean crunch) +{ + int x; + int y; + + nofit = false; + crushchange = crunch; + + // ARRGGHHH!!!! + // This is horrendously slow!!! + // killough 3/14/98 + + // re-check heights for all things near the moving sector + + for (x=sector->blockbox[BOXLEFT] ; x<= sector->blockbox[BOXRIGHT] ; x++) + for (y=sector->blockbox[BOXBOTTOM];y<= sector->blockbox[BOXTOP] ; y++) + P_BlockThingsIterator (x, y, PIT_ChangeSector); + + return nofit; +} + +// +// P_CheckSector +// jff 3/19/98 added to just check monsters on the periphery +// of a moving sector instead of all in bounding box of the +// sector. Both more accurate and faster. +// + +boolean P_CheckSector(sector_t* sector,boolean crunch) +{ + msecnode_t *n; + + if (comp[comp_floors]) /* use the old routine for old demos though */ + return P_ChangeSector(sector,crunch); + + nofit = false; + crushchange = crunch; + + // killough 4/4/98: scan list front-to-back until empty or exhausted, + // restarting from beginning after each thing is processed. Avoids + // crashes, and is sure to examine all things in the sector, and only + // the things which are in the sector, until a steady-state is reached. + // Things can arbitrarily be inserted and removed and it won't mess up. + // + // killough 4/7/98: simplified to avoid using complicated counter + + // Mark all things invalid + + for (n=sector->touching_thinglist; n; n=n->m_snext) + n->visited = false; + + do + for (n=sector->touching_thinglist; n; n=n->m_snext) // go through list + if (!n->visited) // unprocessed thing found + { + n->visited = true; // mark thing as processed + if (!(n->m_thing->flags & MF_NOBLOCKMAP)) //jff 4/7/98 don't do these + PIT_ChangeSector(n->m_thing); // process it + break; // exit and start over + } + while (n); // repeat from scratch until all things left are marked valid + + return nofit; +} + + +// CPhipps - +// Use block memory allocator here + +#include "z_bmalloc.h" + +IMPLEMENT_BLOCK_MEMORY_ALLOC_ZONE(secnodezone, sizeof(msecnode_t), PU_LEVEL, 32, "SecNodes"); + +inline static msecnode_t* P_GetSecnode(void) +{ + return (msecnode_t*)Z_BMalloc(&secnodezone); +} + +// P_PutSecnode() returns a node to the freelist. + +inline static void P_PutSecnode(msecnode_t* node) +{ + Z_BFree(&secnodezone, node); +} + +// phares 3/16/98 +// +// P_AddSecnode() searches the current list to see if this sector is +// already there. If not, it adds a sector node at the head of the list of +// sectors this object appears in. This is called when creating a list of +// nodes that will get linked in later. Returns a pointer to the new node. + +msecnode_t* P_AddSecnode(sector_t* s, mobj_t* thing, msecnode_t* nextnode) +{ + msecnode_t* node; + + node = nextnode; + while (node) + { + if (node->m_sector == s) // Already have a node for this sector? + { + node->m_thing = thing; // Yes. Setting m_thing says 'keep it'. + return(nextnode); + } + node = node->m_tnext; + } + + // Couldn't find an existing node for this sector. Add one at the head + // of the list. + + node = P_GetSecnode(); + + // killough 4/4/98, 4/7/98: mark new nodes unvisited. + node->visited = 0; + + node->m_sector = s; // sector + node->m_thing = thing; // mobj + node->m_tprev = NULL; // prev node on Thing thread + node->m_tnext = nextnode; // next node on Thing thread + if (nextnode) + nextnode->m_tprev = node; // set back link on Thing + + // Add new node at head of sector thread starting at s->touching_thinglist + + node->m_sprev = NULL; // prev node on sector thread + node->m_snext = s->touching_thinglist; // next node on sector thread + if (s->touching_thinglist) + node->m_snext->m_sprev = node; + s->touching_thinglist = node; + return(node); +} + + +// P_DelSecnode() deletes a sector node from the list of +// sectors this object appears in. Returns a pointer to the next node +// on the linked list, or NULL. + +msecnode_t* P_DelSecnode(msecnode_t* node) +{ + msecnode_t* tp; // prev node on thing thread + msecnode_t* tn; // next node on thing thread + msecnode_t* sp; // prev node on sector thread + msecnode_t* sn; // next node on sector thread + + if (node) + { + + // Unlink from the Thing thread. The Thing thread begins at + // sector_list and not from mobj_t->touching_sectorlist. + + tp = node->m_tprev; + tn = node->m_tnext; + if (tp) + tp->m_tnext = tn; + if (tn) + tn->m_tprev = tp; + + // Unlink from the sector thread. This thread begins at + // sector_t->touching_thinglist. + + sp = node->m_sprev; + sn = node->m_snext; + if (sp) + sp->m_snext = sn; + else + node->m_sector->touching_thinglist = sn; + if (sn) + sn->m_sprev = sp; + + // Return this node to the freelist + + P_PutSecnode(node); + return(tn); + } + return(NULL); +} // phares 3/13/98 + +// Delete an entire sector list + +void P_DelSeclist(msecnode_t* node) + + { + while (node) + node = P_DelSecnode(node); + } + + +// phares 3/14/98 +// +// PIT_GetSectors +// Locates all the sectors the object is in by looking at the lines that +// cross through it. You have already decided that the object is allowed +// at this location, so don't bother with checking impassable or +// blocking lines. + +boolean PIT_GetSectors(line_t* ld) +{ + if (tmbbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || + tmbbox[BOXLEFT] >= ld->bbox[BOXRIGHT] || + tmbbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || + tmbbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) + return true; + + if (P_BoxOnLineSide(tmbbox, ld) != -1) + return true; + + // This line crosses through the object. + + // Collect the sector(s) from the line and add to the + // sector_list you're examining. If the Thing ends up being + // allowed to move to this position, then the sector_list + // will be attached to the Thing's mobj_t at touching_sectorlist. + + sector_list = P_AddSecnode(ld->frontsector,tmthing,sector_list); + + /* Don't assume all lines are 2-sided, since some Things + * like MT_TFOG are allowed regardless of whether their radius takes + * them beyond an impassable linedef. + * + * killough 3/27/98, 4/4/98: + * Use sidedefs instead of 2s flag to determine two-sidedness. + * killough 8/1/98: avoid duplicate if same sector on both sides + * cph - DEMOSYNC? */ + + if (ld->backsector && ld->backsector != ld->frontsector) + sector_list = P_AddSecnode(ld->backsector, tmthing, sector_list); + + return true; +} + + +// phares 3/14/98 +// +// P_CreateSecNodeList alters/creates the sector_list that shows what sectors +// the object resides in. + +void P_CreateSecNodeList(mobj_t* thing,fixed_t x,fixed_t y) +{ + int xl; + int xh; + int yl; + int yh; + int bx; + int by; + msecnode_t* node; + mobj_t* saved_tmthing = tmthing; /* cph - see comment at func end */ + fixed_t saved_tmx = tmx, saved_tmy = tmy; /* ditto */ + + // First, clear out the existing m_thing fields. As each node is + // added or verified as needed, m_thing will be set properly. When + // finished, delete all nodes where m_thing is still NULL. These + // represent the sectors the Thing has vacated. + + node = sector_list; + while (node) + { + node->m_thing = NULL; + node = node->m_tnext; + } + + tmthing = thing; + + tmx = x; + tmy = y; + + tmbbox[BOXTOP] = y + tmthing->radius; + tmbbox[BOXBOTTOM] = y - tmthing->radius; + tmbbox[BOXRIGHT] = x + tmthing->radius; + tmbbox[BOXLEFT] = x - tmthing->radius; + + validcount++; // used to make sure we only process a line once + + xl = (tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT; + + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockLinesIterator(bx,by,PIT_GetSectors); + + // Add the sector of the (x,y) point to sector_list. + + sector_list = P_AddSecnode(thing->subsector->sector,thing,sector_list); + + // Now delete any nodes that won't be used. These are the ones where + // m_thing is still NULL. + + node = sector_list; + while (node) + { + if (node->m_thing == NULL) + { + if (node == sector_list) + sector_list = node->m_tnext; + node = P_DelSecnode(node); + } + else + node = node->m_tnext; + } + + /* cph - + * This is the strife we get into for using global variables. tmthing + * is being used by several different functions calling + * P_BlockThingIterator, including functions that can be called *from* + * P_BlockThingIterator. Using a global tmthing is not reentrant. + * OTOH for Boom/MBF demos we have to preserve the buggy behavior. + * Fun. We restore its previous value unless we're in a Boom/MBF demo. + */ + if ((compatibility_level < boom_compatibility_compatibility) || + (compatibility_level >= prboom_3_compatibility)) + tmthing = saved_tmthing; + /* And, duh, the same for tmx/y - cph 2002/09/22 + * And for tmbbox - cph 2003/08/10 */ + if ((compatibility_level < boom_compatibility_compatibility) /* || + (compatibility_level >= prboom_4_compatibility) */) { + tmx = saved_tmx, tmy = saved_tmy; + if (tmthing) { + tmbbox[BOXTOP] = tmy + tmthing->radius; + tmbbox[BOXBOTTOM] = tmy - tmthing->radius; + tmbbox[BOXRIGHT] = tmx + tmthing->radius; + tmbbox[BOXLEFT] = tmx - tmthing->radius; + } + } +} + +/* cphipps 2004/08/30 - + * Must clear tmthing at tic end, as it might contain a pointer to a removed thinker, or the level might have ended/been ended and we clear the objects it was pointing too. Hopefully we don't need to carry this between tics for sync. */ +void P_MapStart(void) { + if (tmthing) I_Error("P_MapStart: tmthing set!"); +} +void P_MapEnd(void) { + tmthing = NULL; +} + +// e6y +// Code to emulate the behavior of Vanilla Doom when encountering an overrun +// of the spechit array. +// No more desyncs on compet-n\hr.wad\hr18*.lmp, all strain.wad\map07 demos etc. +// http://www.doomworld.com/vb/showthread.php?s=&threadid=35214 +static void SpechitOverrun(line_t *ld) +{ + //int addr = 0x01C09C98 + (ld - lines) * 0x3E; + int addr = 0x00C09C98 + (ld - lines) * 0x3E; + + if (compatibility_level == dosdoom_compatibility || compatibility_level == tasdoom_compatibility) + { + // e6y + // There are no more desyncs in the following dosdoom demos: + // flsofdth.wad\fod3uv.lmp - http://www.doomworld.com/sda/flsofdth.htm + // hr.wad\hf181430.lmp - http://www.doomworld.com/tas/hf181430.zip + // hr.wad\hr181329.lmp - http://www.doomworld.com/tas/hr181329.zip + // icarus.wad\ic09uv.lmp - http://competn.doom2.net/pub/sda/i-o/icuvlmps.zip + + switch(numspechit) + { + case 8: break; /* strange cph's code */ + case 9: + tmfloorz = addr; + break; + case 10: + tmceilingz = addr; + break; + + default: + lprintf(LO_ERROR, "SpechitOverrun: Warning: unable to emulate" + " an overrun where numspechit=%i\n", + numspechit); + break; + } + } + else + { + switch(numspechit) + { + case 8: break; /* numspechit, not significant it seems - cph */ + case 9: + case 10: + case 11: + case 12: + tmbbox[numspechit-9] = addr; + break; + case 13: + nofit = addr; + break; + case 14: + crushchange = addr; + break; + default: + lprintf(LO_ERROR, "SpechitOverrun: Warning: unable to emulate" + " an overrun where numspechit=%i\n", + numspechit); + break; + } + } +} diff --git a/src/p_map.h b/src/p_map.h new file mode 100644 index 00000000..eb7e77c1 --- /dev/null +++ b/src/p_map.h @@ -0,0 +1,92 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Map functions + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_MAP__ +#define __P_MAP__ + +#include "r_defs.h" +#include "d_player.h" + +#define USERANGE (64*FRACUNIT) +#define MELEERANGE (64*FRACUNIT) +#define MISSILERANGE (32*64*FRACUNIT) + +// MAXRADIUS is for precalculated sector block boxes the spider demon +// is larger, but we do not have any moving sectors nearby +#define MAXRADIUS (32*FRACUNIT) + +// killough 3/15/98: add fourth argument to P_TryMove +boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean dropoff); + +// killough 8/9/98: extra argument for telefragging +boolean P_TeleportMove(mobj_t *thing, fixed_t x, fixed_t y,boolean boss); +void P_SlideMove(mobj_t *mo); +boolean P_CheckSight(mobj_t *t1, mobj_t *t2); +void P_UseLines(player_t *player); + +// killough 8/2/98: add 'mask' argument to prevent friends autoaiming at others +fixed_t P_AimLineAttack(mobj_t *t1,angle_t angle,fixed_t distance, uint_64_t mask); + +void P_LineAttack(mobj_t *t1, angle_t angle, fixed_t distance, + fixed_t slope, int damage ); +void P_RadiusAttack(mobj_t *spot, mobj_t *source, int damage); +boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y); + +//jff 3/19/98 P_CheckSector(): new routine to replace P_ChangeSector() +boolean P_ChangeSector(sector_t* sector,boolean crunch); +boolean P_CheckSector(sector_t *sector, boolean crunch); +void P_DelSeclist(msecnode_t*); // phares 3/16/98 +void P_CreateSecNodeList(mobj_t*,fixed_t,fixed_t); // phares 3/14/98 +boolean Check_Sides(mobj_t *, int, int); // phares + +int P_GetMoveFactor(mobj_t *mo, int *friction); // killough 8/28/98 +int P_GetFriction(const mobj_t *mo, int *factor); // killough 8/28/98 +void P_ApplyTorque(mobj_t *mo); // killough 9/12/98 + +/* cphipps 2004/08/30 */ +void P_MapStart(void); +void P_MapEnd(void); + +// If "floatok" true, move would be ok if within "tmfloorz - tmceilingz". +extern boolean floatok; +extern boolean felldown; // killough 11/98: indicates object pushed off ledge +extern fixed_t tmfloorz; +extern fixed_t tmceilingz; +extern line_t *ceilingline; +extern line_t *floorline; // killough 8/23/98 +extern mobj_t *linetarget; // who got hit (or NULL) +extern msecnode_t *sector_list; // phares 3/16/98 +extern fixed_t tmbbox[4]; // phares 3/20/98 +extern line_t *blockline; // killough 8/11/98 + +#endif // __P_MAP__ diff --git a/src/p_maputl.c b/src/p_maputl.c new file mode 100644 index 00000000..62ce91c9 --- /dev/null +++ b/src/p_maputl.c @@ -0,0 +1,683 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Movement/collision utility functions, + * as used by function in p_map.c. + * BLOCKMAP Iterator functions, + * and some PIT_* functions to use for iteration. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_bbox.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" + +// +// P_AproxDistance +// Gives an estimation of distance (not exact) +// + +fixed_t CONSTFUNC P_AproxDistance(fixed_t dx, fixed_t dy) +{ + dx = D_abs(dx); + dy = D_abs(dy); + if (dx < dy) + return dx+dy-(dx>>1); + return dx+dy-(dy>>1); +} + +// +// P_PointOnLineSide +// Returns 0 or 1 +// +// killough 5/3/98: reformatted, cleaned up + +int PUREFUNC P_PointOnLineSide(fixed_t x, fixed_t y, const line_t *line) +{ + return + !line->dx ? x <= line->v1->x ? line->dy > 0 : line->dy < 0 : + !line->dy ? y <= line->v1->y ? line->dx < 0 : line->dx > 0 : + FixedMul(y-line->v1->y, line->dx>>FRACBITS) >= + FixedMul(line->dy>>FRACBITS, x-line->v1->x); +} + +// +// P_BoxOnLineSide +// Considers the line to be infinite +// Returns side 0 or 1, -1 if box crosses the line. +// +// killough 5/3/98: reformatted, cleaned up + +int PUREFUNC P_BoxOnLineSide(const fixed_t *tmbox, const line_t *ld) +{ + switch (ld->slopetype) + { + int p; + default: // shut up compiler warnings -- killough + case ST_HORIZONTAL: + return + (tmbox[BOXBOTTOM] > ld->v1->y) == (p = tmbox[BOXTOP] > ld->v1->y) ? + p ^ (ld->dx < 0) : -1; + case ST_VERTICAL: + return + (tmbox[BOXLEFT] < ld->v1->x) == (p = tmbox[BOXRIGHT] < ld->v1->x) ? + p ^ (ld->dy < 0) : -1; + case ST_POSITIVE: + return + P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld) == + (p = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld)) ? p : -1; + case ST_NEGATIVE: + return + (P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld)) == + (p = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld)) ? p : -1; + } +} + +// +// P_PointOnDivlineSide +// Returns 0 or 1. +// +// killough 5/3/98: reformatted, cleaned up + +static int PUREFUNC P_PointOnDivlineSide(fixed_t x, fixed_t y, const divline_t *line) +{ + return + !line->dx ? x <= line->x ? line->dy > 0 : line->dy < 0 : + !line->dy ? y <= line->y ? line->dx < 0 : line->dx > 0 : + (line->dy^line->dx^(x -= line->x)^(y -= line->y)) < 0 ? (line->dy^x) < 0 : + FixedMul(y>>8, line->dx>>8) >= FixedMul(line->dy>>8, x>>8); +} + +// +// P_MakeDivline +// + +static void P_MakeDivline(const line_t *li, divline_t *dl) +{ + dl->x = li->v1->x; + dl->y = li->v1->y; + dl->dx = li->dx; + dl->dy = li->dy; +} + +// +// P_InterceptVector +// Returns the fractional intercept point +// along the first divline. +// This is only called by the addthings +// and addlines traversers. +// + +/* cph - this is killough's 4/19/98 version of P_InterceptVector and + * P_InterceptVector2 (which were interchangeable). We still use this + * in compatibility mode. */ +fixed_t PUREFUNC P_InterceptVector2(const divline_t *v2, const divline_t *v1) +{ + fixed_t den; + return (den = FixedMul(v1->dy>>8, v2->dx) - FixedMul(v1->dx>>8, v2->dy)) ? + FixedDiv(FixedMul((v1->x - v2->x)>>8, v1->dy) + + FixedMul((v2->y - v1->y)>>8, v1->dx), den) : 0; +} + +fixed_t PUREFUNC P_InterceptVector(const divline_t *v2, const divline_t *v1) +{ + if (compatibility_level < prboom_4_compatibility) + return P_InterceptVector2(v2, v1); + else { + /* cph - This was introduced at prboom_4_compatibility - no precision/overflow problems */ + int_64_t den = (int_64_t)v1->dy * v2->dx - (int_64_t)v1->dx * v2->dy; + den >>= 16; + if (!den) + return 0; + return (fixed_t)(((int_64_t)(v1->x - v2->x) * v1->dy - (int_64_t)(v1->y - v2->y) * v1->dx) / den); + } +} + +// +// P_LineOpening +// Sets opentop and openbottom to the window +// through a two sided line. +// OPTIMIZE: keep this precalculated +// + +fixed_t opentop; +fixed_t openbottom; +fixed_t openrange; +fixed_t lowfloor; + +// moved front and back outside P-LineOpening and changed // phares 3/7/98 +// them to these so we can pick up the new friction value +// in PIT_CheckLine() +sector_t *openfrontsector; // made global // phares +sector_t *openbacksector; // made global + +void P_LineOpening(const line_t *linedef) +{ + if (linedef->sidenum[1] == NO_INDEX) // single sided line + { + openrange = 0; + return; + } + + openfrontsector = linedef->frontsector; + openbacksector = linedef->backsector; + + if (openfrontsector->ceilingheight < openbacksector->ceilingheight) + opentop = openfrontsector->ceilingheight; + else + opentop = openbacksector->ceilingheight; + + if (openfrontsector->floorheight > openbacksector->floorheight) + { + openbottom = openfrontsector->floorheight; + lowfloor = openbacksector->floorheight; + } + else + { + openbottom = openbacksector->floorheight; + lowfloor = openfrontsector->floorheight; + } + openrange = opentop - openbottom; +} + +// +// THING POSITION SETTING +// + +// +// P_UnsetThingPosition +// Unlinks a thing from block map and sectors. +// On each position change, BLOCKMAP and other +// lookups maintaining lists ot things inside +// these structures need to be updated. +// + +void P_UnsetThingPosition (mobj_t *thing) +{ + if (!(thing->flags & MF_NOSECTOR)) + { + /* invisible things don't need to be in sector list + * unlink from subsector + * + * killough 8/11/98: simpler scheme using pointers-to-pointers for prev + * pointers, allows head node pointers to be treated like everything else + */ + + mobj_t **sprev = thing->sprev; + mobj_t *snext = thing->snext; + if ((*sprev = snext)) // unlink from sector list + snext->sprev = sprev; + + // phares 3/14/98 + // + // Save the sector list pointed to by touching_sectorlist. + // In P_SetThingPosition, we'll keep any nodes that represent + // sectors the Thing still touches. We'll add new ones then, and + // delete any nodes for sectors the Thing has vacated. Then we'll + // put it back into touching_sectorlist. It's done this way to + // avoid a lot of deleting/creating for nodes, when most of the + // time you just get back what you deleted anyway. + // + // If this Thing is being removed entirely, then the calling + // routine will clear out the nodes in sector_list. + + sector_list = thing->touching_sectorlist; + thing->touching_sectorlist = NULL; //to be restored by P_SetThingPosition + } + + if (!(thing->flags & MF_NOBLOCKMAP)) + { + /* inert things don't need to be in blockmap + * + * killough 8/11/98: simpler scheme using pointers-to-pointers for prev + * pointers, allows head node pointers to be treated like everything else + * + * Also more robust, since it doesn't depend on current position for + * unlinking. Old method required computing head node based on position + * at time of unlinking, assuming it was the same position as during + * linking. + */ + + mobj_t *bnext, **bprev = thing->bprev; + if (bprev && (*bprev = bnext = thing->bnext)) // unlink from block map + bnext->bprev = bprev; + } +} + +// +// P_SetThingPosition +// Links a thing into both a block and a subsector +// based on it's x y. +// Sets thing->subsector properly +// +// killough 5/3/98: reformatted, cleaned up + +void P_SetThingPosition(mobj_t *thing) +{ // link into subsector + subsector_t *ss = thing->subsector = R_PointInSubsector(thing->x, thing->y); + if (!(thing->flags & MF_NOSECTOR)) + { + // invisible things don't go into the sector links + + // killough 8/11/98: simpler scheme using pointer-to-pointer prev + // pointers, allows head nodes to be treated like everything else + + mobj_t **link = &ss->sector->thinglist; + mobj_t *snext = *link; + if ((thing->snext = snext)) + snext->sprev = &thing->snext; + thing->sprev = link; + *link = thing; + + // phares 3/16/98 + // + // If sector_list isn't NULL, it has a collection of sector + // nodes that were just removed from this Thing. + + // Collect the sectors the object will live in by looking at + // the existing sector_list and adding new nodes and deleting + // obsolete ones. + + // When a node is deleted, its sector links (the links starting + // at sector_t->touching_thinglist) are broken. When a node is + // added, new sector links are created. + + P_CreateSecNodeList(thing,thing->x,thing->y); + thing->touching_sectorlist = sector_list; // Attach to Thing's mobj_t + sector_list = NULL; // clear for next time + } + + // link into blockmap + if (!(thing->flags & MF_NOBLOCKMAP)) + { + // inert things don't need to be in blockmap + int blockx = (thing->x - bmaporgx)>>MAPBLOCKSHIFT; + int blocky = (thing->y - bmaporgy)>>MAPBLOCKSHIFT; + if (blockx>=0 && blockx < bmapwidth && blocky>=0 && blocky < bmapheight) + { + // killough 8/11/98: simpler scheme using pointer-to-pointer prev + // pointers, allows head nodes to be treated like everything else + + mobj_t **link = &blocklinks[blocky*bmapwidth+blockx]; + mobj_t *bnext = *link; + if ((thing->bnext = bnext)) + bnext->bprev = &thing->bnext; + thing->bprev = link; + *link = thing; + } + else // thing is off the map + thing->bnext = NULL, thing->bprev = NULL; + } +} + +// +// BLOCK MAP ITERATORS +// For each line/thing in the given mapblock, +// call the passed PIT_* function. +// If the function returns false, +// exit with false without checking anything else. +// + +// +// P_BlockLinesIterator +// The validcount flags are used to avoid checking lines +// that are marked in multiple mapblocks, +// so increment validcount before the first call +// to P_BlockLinesIterator, then make one or more calls +// to it. +// +// killough 5/3/98: reformatted, cleaned up + +boolean P_BlockLinesIterator(int x, int y, boolean func(line_t*)) +{ + int offset; + const long *list; // killough 3/1/98: for removal of blockmap limit + + if (x<0 || y<0 || x>=bmapwidth || y>=bmapheight) + return true; + offset = y*bmapwidth+x; + offset = *(blockmap+offset); + list = blockmaplump+offset; // original was reading // phares + // delmiting 0 as linedef 0 // phares + + // killough 1/31/98: for compatibility we need to use the old method. + // Most demos go out of sync, and maybe other problems happen, if we + // don't consider linedef 0. For safety this should be qualified. + + if (!demo_compatibility) // killough 2/22/98: demo_compatibility check + list++; // skip 0 starting delimiter // phares + for ( ; *list != -1 ; list++) // phares + { + line_t *ld = &lines[*list]; + if (ld->validcount == validcount) + continue; // line has already been checked + ld->validcount = validcount; + if (!func(ld)) + return false; + } + return true; // everything was checked +} + +// +// P_BlockThingsIterator +// +// killough 5/3/98: reformatted, cleaned up + +boolean P_BlockThingsIterator(int x, int y, boolean func(mobj_t*)) +{ + mobj_t *mobj; + if (!(x<0 || y<0 || x>=bmapwidth || y>=bmapheight)) + for (mobj = blocklinks[y*bmapwidth+x]; mobj; mobj = mobj->bnext) + if (!func(mobj)) + return false; + return true; +} + +// +// INTERCEPT ROUTINES +// + +// 1/11/98 killough: Intercept limit removed +static intercept_t *intercepts, *intercept_p; + +// Check for limit and double size if necessary -- killough +static void check_intercept(void) +{ + static size_t num_intercepts; + size_t offset = intercept_p - intercepts; + if (offset >= num_intercepts) + { + num_intercepts = num_intercepts ? num_intercepts*2 : 128; + intercepts = realloc(intercepts, sizeof(*intercepts)*num_intercepts); + intercept_p = intercepts + offset; + } +} + +divline_t trace; + +// PIT_AddLineIntercepts. +// Looks for lines in the given block +// that intercept the given trace +// to add to the intercepts list. +// +// A line is crossed if its endpoints +// are on opposite sides of the trace. +// +// killough 5/3/98: reformatted, cleaned up + +boolean PIT_AddLineIntercepts(line_t *ld) +{ + int s1; + int s2; + fixed_t frac; + divline_t dl; + + // avoid precision problems with two routines + if (trace.dx > FRACUNIT*16 || trace.dy > FRACUNIT*16 || + trace.dx < -FRACUNIT*16 || trace.dy < -FRACUNIT*16) + { + s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &trace); + s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &trace); + } + else + { + s1 = P_PointOnLineSide (trace.x, trace.y, ld); + s2 = P_PointOnLineSide (trace.x+trace.dx, trace.y+trace.dy, ld); + } + + if (s1 == s2) + return true; // line isn't crossed + + // hit the line + P_MakeDivline(ld, &dl); + frac = P_InterceptVector(&trace, &dl); + + if (frac < 0) + return true; // behind source + + check_intercept(); // killough + + intercept_p->frac = frac; + intercept_p->isaline = true; + intercept_p->d.line = ld; + intercept_p++; + + return true; // continue +} + +// +// PIT_AddThingIntercepts +// +// killough 5/3/98: reformatted, cleaned up + +boolean PIT_AddThingIntercepts(mobj_t *thing) +{ + fixed_t x1, y1; + fixed_t x2, y2; + int s1, s2; + divline_t dl; + fixed_t frac; + + // check a corner to corner crossection for hit + if ((trace.dx ^ trace.dy) > 0) + { + x1 = thing->x - thing->radius; + y1 = thing->y + thing->radius; + x2 = thing->x + thing->radius; + y2 = thing->y - thing->radius; + } + else + { + x1 = thing->x - thing->radius; + y1 = thing->y - thing->radius; + x2 = thing->x + thing->radius; + y2 = thing->y + thing->radius; + } + + s1 = P_PointOnDivlineSide (x1, y1, &trace); + s2 = P_PointOnDivlineSide (x2, y2, &trace); + + if (s1 == s2) + return true; // line isn't crossed + + dl.x = x1; + dl.y = y1; + dl.dx = x2-x1; + dl.dy = y2-y1; + + frac = P_InterceptVector (&trace, &dl); + + if (frac < 0) + return true; // behind source + + check_intercept(); // killough + + intercept_p->frac = frac; + intercept_p->isaline = false; + intercept_p->d.thing = thing; + intercept_p++; + + return true; // keep going +} + +// +// P_TraverseIntercepts +// Returns true if the traverser function returns true +// for all lines. +// +// killough 5/3/98: reformatted, cleaned up + +boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac) +{ + intercept_t *in = NULL; + int count = intercept_p - intercepts; + while (count--) + { + fixed_t dist = INT_MAX; + intercept_t *scan; + for (scan = intercepts; scan < intercept_p; scan++) + if (scan->frac < dist) + dist = (in=scan)->frac; + if (dist > maxfrac) + return true; // checked everything in range + if (!func(in)) + return false; // don't bother going farther + in->frac = INT_MAX; + } + return true; // everything was traversed +} + +// +// P_PathTraverse +// Traces a line from x1,y1 to x2,y2, +// calling the traverser function for each. +// Returns true if the traverser function returns true +// for all lines. +// +// killough 5/3/98: reformatted, cleaned up + +boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, boolean trav(intercept_t *)) +{ + fixed_t xt1, yt1; + fixed_t xt2, yt2; + fixed_t xstep, ystep; + fixed_t partial; + fixed_t xintercept, yintercept; + int mapx, mapy; + int mapxstep, mapystep; + int count; + + validcount++; + intercept_p = intercepts; + + if (!((x1-bmaporgx)&(MAPBLOCKSIZE-1))) + x1 += FRACUNIT; // don't side exactly on a line + + if (!((y1-bmaporgy)&(MAPBLOCKSIZE-1))) + y1 += FRACUNIT; // don't side exactly on a line + + trace.x = x1; + trace.y = y1; + trace.dx = x2 - x1; + trace.dy = y2 - y1; + + x1 -= bmaporgx; + y1 -= bmaporgy; + xt1 = x1>>MAPBLOCKSHIFT; + yt1 = y1>>MAPBLOCKSHIFT; + + x2 -= bmaporgx; + y2 -= bmaporgy; + xt2 = x2>>MAPBLOCKSHIFT; + yt2 = y2>>MAPBLOCKSHIFT; + + if (xt2 > xt1) + { + mapxstep = 1; + partial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1)); + ystep = FixedDiv (y2-y1,D_abs(x2-x1)); + } + else + if (xt2 < xt1) + { + mapxstep = -1; + partial = (x1>>MAPBTOFRAC)&(FRACUNIT-1); + ystep = FixedDiv (y2-y1,D_abs(x2-x1)); + } + else + { + mapxstep = 0; + partial = FRACUNIT; + ystep = 256*FRACUNIT; + } + + yintercept = (y1>>MAPBTOFRAC) + FixedMul(partial, ystep); + + if (yt2 > yt1) + { + mapystep = 1; + partial = FRACUNIT - ((y1>>MAPBTOFRAC)&(FRACUNIT-1)); + xstep = FixedDiv (x2-x1,D_abs(y2-y1)); + } + else + if (yt2 < yt1) + { + mapystep = -1; + partial = (y1>>MAPBTOFRAC)&(FRACUNIT-1); + xstep = FixedDiv (x2-x1,D_abs(y2-y1)); + } + else + { + mapystep = 0; + partial = FRACUNIT; + xstep = 256*FRACUNIT; + } + + xintercept = (x1>>MAPBTOFRAC) + FixedMul (partial, xstep); + + // Step through map blocks. + // Count is present to prevent a round off error + // from skipping the break. + + mapx = xt1; + mapy = yt1; + + for (count = 0; count < 64; count++) + { + if (flags & PT_ADDLINES) + if (!P_BlockLinesIterator(mapx, mapy,PIT_AddLineIntercepts)) + return false; // early out + + if (flags & PT_ADDTHINGS) + if (!P_BlockThingsIterator(mapx, mapy,PIT_AddThingIntercepts)) + return false; // early out + + if (mapx == xt2 && mapy == yt2) + break; + + if ((yintercept >> FRACBITS) == mapy) + { + yintercept += ystep; + mapx += mapxstep; + } + else + if ((xintercept >> FRACBITS) == mapx) + { + xintercept += xstep; + mapy += mapystep; + } + } + + // go through the sorted list + return P_TraverseIntercepts(trav, FRACUNIT); +} diff --git a/src/p_maputl.h b/src/p_maputl.h new file mode 100644 index 00000000..8a70ba05 --- /dev/null +++ b/src/p_maputl.h @@ -0,0 +1,89 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Map utility functions + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_MAPUTL__ +#define __P_MAPUTL__ + +#include "r_defs.h" + +/* mapblocks are used to check movement against lines and things */ +#define MAPBLOCKUNITS 128 +#define MAPBLOCKSIZE (MAPBLOCKUNITS*FRACUNIT) +#define MAPBLOCKSHIFT (FRACBITS+7) +#define MAPBMASK (MAPBLOCKSIZE-1) +#define MAPBTOFRAC (MAPBLOCKSHIFT-FRACBITS) + +#define PT_ADDLINES 1 +#define PT_ADDTHINGS 2 +#define PT_EARLYOUT 4 + +typedef struct { + fixed_t x; + fixed_t y; + fixed_t dx; + fixed_t dy; +} divline_t; + +typedef struct { + fixed_t frac; /* along trace line */ + boolean isaline; + union { + mobj_t* thing; + line_t* line; + } d; +} intercept_t; + +typedef boolean (*traverser_t)(intercept_t *in); + +fixed_t CONSTFUNC P_AproxDistance (fixed_t dx, fixed_t dy); +int PUREFUNC P_PointOnLineSide (fixed_t x, fixed_t y, const line_t *line); +int PUREFUNC P_BoxOnLineSide (const fixed_t *tmbox, const line_t *ld); +fixed_t PUREFUNC P_InterceptVector (const divline_t *v2, const divline_t *v1); +/* cph - old compatibility version below */ +fixed_t PUREFUNC P_InterceptVector2(const divline_t *v2, const divline_t *v1); + +void P_LineOpening (const line_t *linedef); +void P_UnsetThingPosition(mobj_t *thing); +void P_SetThingPosition(mobj_t *thing); +boolean P_BlockLinesIterator (int x, int y, boolean func(line_t *)); +boolean P_BlockThingsIterator(int x, int y, boolean func(mobj_t *)); +boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, + int flags, boolean trav(intercept_t *)); + +extern fixed_t opentop; +extern fixed_t openbottom; +extern fixed_t openrange; +extern fixed_t lowfloor; +extern divline_t trace; + +#endif /* __P_MAPUTL__ */ diff --git a/src/p_mobj.c b/src/p_mobj.c new file mode 100644 index 00000000..ffb44cb1 --- /dev/null +++ b/src/p_mobj.c @@ -0,0 +1,1496 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Moving object handling. Spawn functions. + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "m_random.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_tick.h" +#include "sounds.h" +#include "st_stuff.h" +#include "hu_stuff.h" +#include "s_sound.h" +#include "info.h" +#include "g_game.h" +#include "p_inter.h" +#include "lprintf.h" +#include "r_demo.h" + +// +// P_SetMobjState +// Returns true if the mobj is still present. +// + +boolean P_SetMobjState(mobj_t* mobj,statenum_t state) +{ + state_t* st; + + // killough 4/9/98: remember states seen, to detect cycles: + + static statenum_t seenstate_tab[NUMSTATES]; // fast transition table + statenum_t *seenstate = seenstate_tab; // pointer to table + static int recursion; // detects recursion + statenum_t i = state; // initial state + boolean ret = true; // return value + statenum_t tempstate[NUMSTATES]; // for use with recursion + + if (recursion++) // if recursion detected, + memset(seenstate=tempstate,0,sizeof tempstate); // clear state table + + do + { + if (state == S_NULL) + { + mobj->state = (state_t *) S_NULL; + P_RemoveMobj (mobj); + ret = false; + break; // killough 4/9/98 + } + + st = &states[state]; + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + + // Modified handling. + // Call action functions when the state is set + + if (st->action) + st->action(mobj); + + seenstate[state] = 1 + st->nextstate; // killough 4/9/98 + + state = st->nextstate; + } while (!mobj->tics && !seenstate[state]); // killough 4/9/98 + + if (ret && !mobj->tics) // killough 4/9/98: detect state cycles + doom_printf("Warning: State Cycle Detected"); + + if (!--recursion) + for (;(state=seenstate[i]);i=state-1) + seenstate[i] = 0; // killough 4/9/98: erase memory of states + + return ret; +} + + +// +// P_ExplodeMissile +// + +void P_ExplodeMissile (mobj_t* mo) +{ + mo->momx = mo->momy = mo->momz = 0; + + P_SetMobjState (mo, mobjinfo[mo->type].deathstate); + + mo->tics -= P_Random(pr_explode)&3; + + if (mo->tics < 1) + mo->tics = 1; + + mo->flags &= ~MF_MISSILE; + + if (mo->info->deathsound) + S_StartSound (mo, mo->info->deathsound); +} + + +// +// P_XYMovement +// +// Attempts to move something if it has momentum. +// + +static void P_XYMovement (mobj_t* mo) +{ + player_t *player; + fixed_t xmove, ymove; + + //e6y + fixed_t oldx,oldy; // phares 9/10/98: reducing bobbing/momentum on ice + +#if 0 + fixed_t ptryx; + fixed_t ptryy; + fixed_t xmove; + fixed_t ymove; + fixed_t oldx,oldy; // phares 9/10/98: reducing bobbing/momentum on ice + // when up against walls +#endif + if (!(mo->momx | mo->momy)) // Any momentum? + { + if (mo->flags & MF_SKULLFLY) + { + + // the skull slammed into something + + mo->flags &= ~MF_SKULLFLY; + mo->momz = 0; + + P_SetMobjState (mo, mo->info->spawnstate); + } + return; + } + + player = mo->player; + + if (mo->momx > MAXMOVE) + mo->momx = MAXMOVE; + else if (mo->momx < -MAXMOVE) + mo->momx = -MAXMOVE; + + if (mo->momy > MAXMOVE) + mo->momy = MAXMOVE; + else if (mo->momy < -MAXMOVE) + mo->momy = -MAXMOVE; + + xmove = mo->momx; + ymove = mo->momy; + + oldx = mo->x; // phares 9/10/98: new code to reduce bobbing/momentum + oldy = mo->y; // when on ice & up against wall. These will be compared + // to your x,y values later to see if you were able to move + + do + { + fixed_t ptryx, ptryy; + // killough 8/9/98: fix bug in original Doom source: + // Large negative displacements were never considered. + // This explains the tendency for Mancubus fireballs + // to pass through walls. + // CPhipps - compatibility optioned + + if (xmove > MAXMOVE/2 || ymove > MAXMOVE/2 || + (!comp[comp_moveblock] + && (xmove < -MAXMOVE/2 || ymove < -MAXMOVE/2))) + { + ptryx = mo->x + xmove/2; + ptryy = mo->y + ymove/2; + xmove >>= 1; + ymove >>= 1; + } + else + { + ptryx = mo->x + xmove; + ptryy = mo->y + ymove; + xmove = ymove = 0; + } + + // killough 3/15/98: Allow objects to drop off + + if (!P_TryMove (mo, ptryx, ptryy, true)) + { + // blocked move + + // killough 8/11/98: bouncing off walls + // killough 10/98: + // Add ability for objects other than players to bounce on ice + + if (!(mo->flags & MF_MISSILE) && + mbf_features && + (mo->flags & MF_BOUNCES || + (!player && blockline && + variable_friction && mo->z <= mo->floorz && + P_GetFriction(mo, NULL) > ORIG_FRICTION))) + { + if (blockline) + { + fixed_t r = ((blockline->dx >> FRACBITS) * mo->momx + + (blockline->dy >> FRACBITS) * mo->momy) / + ((blockline->dx >> FRACBITS)*(blockline->dx >> FRACBITS)+ + (blockline->dy >> FRACBITS)*(blockline->dy >> FRACBITS)); + fixed_t x = FixedMul(r, blockline->dx); + fixed_t y = FixedMul(r, blockline->dy); + + // reflect momentum away from wall + + mo->momx = x*2 - mo->momx; + mo->momy = y*2 - mo->momy; + + // if under gravity, slow down in + // direction perpendicular to wall. + + if (!(mo->flags & MF_NOGRAVITY)) + { + mo->momx = (mo->momx + x)/2; + mo->momy = (mo->momy + y)/2; + } + } + else + mo->momx = mo->momy = 0; + } + else + if (player) // try to slide along it + P_SlideMove (mo); + else + if (mo->flags & MF_MISSILE) + { + // explode a missile + + if (ceilingline && + ceilingline->backsector && + ceilingline->backsector->ceilingpic == skyflatnum) + if (demo_compatibility || // killough + mo->z > ceilingline->backsector->ceilingheight) + { + // Hack to prevent missiles exploding + // against the sky. + // Does not handle sky floors. + + P_RemoveMobj (mo); + return; + } + P_ExplodeMissile (mo); + } + else // whatever else it is, it is now standing still in (x,y) + mo->momx = mo->momy = 0; + } + } while (xmove || ymove); + + // slow down + +#if 0 /* killough 10/98: this is unused code (except maybe in .deh files?) */ + if (player && player->cheats & CF_NOMOMENTUM) + { + // debug option for no sliding at all + mo->momx = mo->momy = 0; + player->momx = player->momy = 0; /* killough 10/98 */ + return; + } +#endif + + /* no friction for missiles or skulls ever, no friction when airborne */ + if (mo->flags & (MF_MISSILE | MF_SKULLFLY) || mo->z > mo->floorz) + return; + + /* killough 8/11/98: add bouncers + * killough 9/15/98: add objects falling off ledges + * killough 11/98: only include bouncers hanging off ledges + */ + if (((mo->flags & MF_BOUNCES && mo->z > mo->dropoffz) || + mo->flags & MF_CORPSE || mo->intflags & MIF_FALLING) && + (mo->momx > FRACUNIT/4 || mo->momx < -FRACUNIT/4 || + mo->momy > FRACUNIT/4 || mo->momy < -FRACUNIT/4) && + mo->floorz != mo->subsector->sector->floorheight) + return; // do not stop sliding if halfway off a step with some momentum + + // killough 11/98: + // Stop voodoo dolls that have come to rest, despite any + // moving corresponding player, except in old demos: + + if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED && + mo->momy > -STOPSPEED && mo->momy < STOPSPEED && + (!player || !(player->cmd.forwardmove | player->cmd.sidemove) || + (player->mo != mo && compatibility_level >= lxdoom_1_compatibility))) + { + // if in a walking frame, stop moving + + // killough 10/98: + // Don't affect main player when voodoo dolls stop, except in old demos: + +// if ( player&&(unsigned)((player->mo->state - states)- S_PLAY_RUN1) < 4) +// P_SetMobjState (player->mo, S_PLAY); + if (player && (unsigned)(player->mo->state - states - S_PLAY_RUN1) < 4 + && (player->mo == mo || compatibility_level >= lxdoom_1_compatibility)) + P_SetMobjState(player->mo, S_PLAY); + + mo->momx = mo->momy = 0; + + /* killough 10/98: kill any bobbing momentum too (except in voodoo dolls) + * cph - DEMOSYNC - needs compatibility check? + */ + if (player && player->mo == mo) + player->momx = player->momy = 0; + } + else + { + /* phares 3/17/98 + * + * Friction will have been adjusted by friction thinkers for + * icy or muddy floors. Otherwise it was never touched and + * remained set at ORIG_FRICTION + * + * killough 8/28/98: removed inefficient thinker algorithm, + * instead using touching_sectorlist in P_GetFriction() to + * determine friction (and thus only when it is needed). + * + * killough 10/98: changed to work with new bobbing method. + * Reducing player momentum is no longer needed to reduce + * bobbing, so ice works much better now. + * + * cph - DEMOSYNC - need old code for Boom demos? + */ + + //e6y + if (compatibility_level <= boom_201_compatibility) + { + // phares 3/17/98 + // Friction will have been adjusted by friction thinkers for icy + // or muddy floors. Otherwise it was never touched and + // remained set at ORIG_FRICTION + mo->momx = FixedMul(mo->momx,mo->friction); + mo->momy = FixedMul(mo->momy,mo->friction); + mo->friction = ORIG_FRICTION; // reset to normal for next tic + } + else if (compatibility_level <= lxdoom_1_compatibility) + { + // phares 9/10/98: reduce bobbing/momentum when on ice & up against wall + + if ((oldx == mo->x) && (oldy == mo->y)) // Did you go anywhere? + { // No. Use original friction. This allows you to not bob so much + // if you're on ice, but keeps enough momentum around to break free + // when you're mildly stuck in a wall. + mo->momx = FixedMul(mo->momx,ORIG_FRICTION); + mo->momy = FixedMul(mo->momy,ORIG_FRICTION); + } + else + { // Yes. Use stored friction. + mo->momx = FixedMul(mo->momx,mo->friction); + mo->momy = FixedMul(mo->momy,mo->friction); + } + mo->friction = ORIG_FRICTION; // reset to normal for next tic + } + else + { + + fixed_t friction = P_GetFriction(mo, NULL); + + mo->momx = FixedMul(mo->momx, friction); + mo->momy = FixedMul(mo->momy, friction); + + /* killough 10/98: Always decrease player bobbing by ORIG_FRICTION. + * This prevents problems with bobbing on ice, where it was not being + * reduced fast enough, leading to all sorts of kludges being developed. + */ + + if (player && player->mo == mo) /* Not voodoo dolls */ + { + player->momx = FixedMul(player->momx, ORIG_FRICTION); + player->momy = FixedMul(player->momy, ORIG_FRICTION); + } + + } + + } +} + + +// +// P_ZMovement +// +// Attempt vertical movement. + +static void P_ZMovement (mobj_t* mo) +{ + /* killough 7/11/98: + * BFG fireballs bounced on floors and ceilings in Pre-Beta Doom + * killough 8/9/98: added support for non-missile objects bouncing + * (e.g. grenade, mine, pipebomb) + */ + + if (mo->flags & MF_BOUNCES && mo->momz) { + mo->z += mo->momz; + if (mo->z <= mo->floorz) { /* bounce off floors */ + mo->z = mo->floorz; + if (mo->momz < 0) { + mo->momz = -mo->momz; + if (!(mo->flags & MF_NOGRAVITY)) { /* bounce back with decay */ + mo->momz = mo->flags & MF_FLOAT ? // floaters fall slowly + mo->flags & MF_DROPOFF ? // DROPOFF indicates rate + FixedMul(mo->momz, (fixed_t)(FRACUNIT*.85)) : + FixedMul(mo->momz, (fixed_t)(FRACUNIT*.70)) : + FixedMul(mo->momz, (fixed_t)(FRACUNIT*.45)) ; + + /* Bring it to rest below a certain speed */ + if (D_abs(mo->momz) <= mo->info->mass*(GRAVITY*4/256)) + mo->momz = 0; + } + + /* killough 11/98: touchy objects explode on impact */ + if (mo->flags & MF_TOUCHY && mo->intflags & MIF_ARMED + && mo->health > 0) + P_DamageMobj(mo, NULL, NULL, mo->health); + else if (mo->flags & MF_FLOAT && sentient(mo)) + goto floater; + return; + } + } else if (mo->z >= mo->ceilingz - mo->height) { + /* bounce off ceilings */ + mo->z = mo->ceilingz - mo->height; + if (mo->momz > 0) { + if (mo->subsector->sector->ceilingpic != skyflatnum) + mo->momz = -mo->momz; /* always bounce off non-sky ceiling */ + else if (mo->flags & MF_MISSILE) + P_RemoveMobj(mo); /* missiles don't bounce off skies */ + else if (mo->flags & MF_NOGRAVITY) + mo->momz = -mo->momz; // bounce unless under gravity + + if (mo->flags & MF_FLOAT && sentient(mo)) + goto floater; + + return; + } + } else { + if (!(mo->flags & MF_NOGRAVITY)) /* free-fall under gravity */ + mo->momz -= mo->info->mass*(GRAVITY/256); + + if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; + return; + } + + /* came to a stop */ + mo->momz = 0; + + if (mo->flags & MF_MISSILE) { + if (ceilingline && + ceilingline->backsector && + ceilingline->backsector->ceilingpic == skyflatnum && + mo->z > ceilingline->backsector->ceilingheight) + P_RemoveMobj(mo); /* don't explode on skies */ + else + P_ExplodeMissile(mo); + } + + if (mo->flags & MF_FLOAT && sentient(mo)) goto floater; + return; + } + + /* killough 8/9/98: end bouncing object code */ + + // check for smooth step up + + if (mo->player && + mo->player->mo == mo && // killough 5/12/98: exclude voodoo dolls + mo->z < mo->floorz) + { + mo->player->viewheight -= mo->floorz-mo->z; + mo->player->deltaviewheight = (VIEWHEIGHT - mo->player->viewheight)>>3; + } + + // adjust altitude + + mo->z += mo->momz; + +floater: + if ((mo->flags & MF_FLOAT) && mo->target) + + // float down towards target if too close + + if (!((mo->flags ^ MF_FLOAT) & (MF_FLOAT | MF_SKULLFLY | MF_INFLOAT)) && + mo->target) /* killough 11/98: simplify */ + { + fixed_t delta; + if (P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y) < + D_abs(delta = mo->target->z + (mo->height>>1) - mo->z)*3) + mo->z += delta < 0 ? -FLOATSPEED : FLOATSPEED; + } + + // clip movement + + if (mo->z <= mo->floorz) + { + // hit the floor + + /* Note (id): + * somebody left this after the setting momz to 0, + * kinda useless there. + * cph - This was the a bug in the linuxdoom-1.10 source which + * caused it not to sync Doom 2 v1.9 demos. Someone + * added the above comment and moved up the following code. So + * demos would desync in close lost soul fights. + * cph - revised 2001/04/15 - + * This was a bug in the Doom/Doom 2 source; the following code + * is meant to make charging lost souls bounce off of floors, but it + * was incorrectly placed after momz was set to 0. + * However, this bug was fixed in Doom95, Final/Ultimate Doom, and + * the v1.10 source release (which is one reason why it failed to sync + * some Doom2 v1.9 demos) + * I've added a comp_soul compatibility option to make this behavior + * selectable for PrBoom v2.3+. For older demos, we do this here only + * if we're in a compatibility level above Doom 2 v1.9 (in which case we + * mimic the bug and do it further down instead) + */ + + if (mo->flags & MF_SKULLFLY && + (!comp[comp_soul] || + (compatibility_level > doom2_19_compatibility && + compatibility_level < prboom_4_compatibility) + )) + mo->momz = -mo->momz; // the skull slammed into something + + if (mo->momz < 0) + { + /* killough 11/98: touchy objects explode on impact */ + if (mo->flags & MF_TOUCHY && mo->intflags & MIF_ARMED && mo->health > 0) + P_DamageMobj(mo, NULL, NULL, mo->health); + else + if (mo->player && /* killough 5/12/98: exclude voodoo dolls */ + mo->player->mo == mo && mo->momz < -GRAVITY*8) + { + // Squat down. + // Decrease viewheight for a moment + // after hitting the ground (hard), + // and utter appropriate sound. + + mo->player->deltaviewheight = mo->momz>>3; + /* cph - prevent "oof" when dead */ + if (comp[comp_sound] || mo->health > 0) + S_StartSound (mo, sfx_oof); + } + mo->momz = 0; + } + mo->z = mo->floorz; + + /* cph 2001/04/15 - + * This is the buggy lost-soul bouncing code referenced above. + * We've already set momz = 0 normally by this point, so it's useless. + * However we might still have upward momentum, in which case this will + * incorrectly reverse it, so we might still need this for demo sync + */ + if (mo->flags & MF_SKULLFLY && + compatibility_level <= doom2_19_compatibility) + mo->momz = -mo->momz; // the skull slammed into something + + if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) + { + P_ExplodeMissile (mo); + return; + } + } + else // still above the floor // phares + if (!(mo->flags & MF_NOGRAVITY)) + { + if (!mo->momz) + mo->momz = -GRAVITY; + mo->momz -= GRAVITY; + } + + if (mo->z + mo->height > mo->ceilingz) + { + /* cph 2001/04/15 - + * Lost souls were meant to bounce off of ceilings; + * new comp_soul compatibility option added + */ + if (!comp[comp_soul] && mo->flags & MF_SKULLFLY) + mo->momz = -mo->momz; // the skull slammed into something + + // hit the ceiling + + if (mo->momz > 0) + mo->momz = 0; + + mo->z = mo->ceilingz - mo->height; + + /* cph 2001/04/15 - + * We might have hit a ceiling but had downward momentum (e.g. ceiling is + * lowering on us), so for old demos we must still do the buggy + * momentum reversal here + */ + if (comp[comp_soul] && mo->flags & MF_SKULLFLY) + mo->momz = -mo->momz; // the skull slammed into something + + if ( (mo->flags & MF_MISSILE) && !(mo->flags & MF_NOCLIP) ) + { + P_ExplodeMissile (mo); + return; + } + } + } + +// +// P_NightmareRespawn +// + +static void P_NightmareRespawn(mobj_t* mobj) +{ + fixed_t x; + fixed_t y; + fixed_t z; + subsector_t* ss; + mobj_t* mo; + mapthing_t* mthing; + + x = mobj->spawnpoint.x << FRACBITS; + y = mobj->spawnpoint.y << FRACBITS; + + /* haleyjd: stupid nightmare respawning bug fix + * + * 08/09/00: compatibility added, time to ramble :) + * This fixes the notorious nightmare respawning bug that causes monsters + * that didn't spawn at level startup to respawn at the point (0,0) + * regardless of that point's nature. SMMU and Eternity need this for + * script-spawned things like Halif Swordsmythe, as well. + * + * cph - copied from eternity, except comp_respawnfix becomes comp_respawn + * and the logic is reversed (i.e. like the rest of comp_ it *disables* + * the fix) + */ + if(!comp[comp_respawn] && !x && !y) + { + // spawnpoint was zeroed out, so use point of death instead + x = mobj->x; + y = mobj->y; + } + + // something is occupying its position? + + if (!P_CheckPosition (mobj, x, y) ) + return; // no respwan + + // spawn a teleport fog at old spot + // because of removal of the body? + + mo = P_SpawnMobj (mobj->x, + mobj->y, + mobj->subsector->sector->floorheight, + MT_TFOG); + + // initiate teleport sound + + S_StartSound (mo, sfx_telept); + + // spawn a teleport fog at the new spot + + ss = R_PointInSubsector (x,y); + + mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_TFOG); + + S_StartSound (mo, sfx_telept); + + // spawn the new monster + + mthing = &mobj->spawnpoint; + if (mobj->info->flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + // inherit attributes from deceased one + + mo = P_SpawnMobj (x,y,z, mobj->type); + mo->spawnpoint = mobj->spawnpoint; + mo->angle = ANG45 * (mthing->angle/45); + + if (mthing->options & MTF_AMBUSH) + mo->flags |= MF_AMBUSH; + + /* killough 11/98: transfer friendliness from deceased */ + mo->flags = (mo->flags & ~MF_FRIEND) | (mobj->flags & MF_FRIEND); + + mo->reactiontime = 18; + + // remove the old monster, + + P_RemoveMobj (mobj); +} + + +// +// P_MobjThinker +// + +void P_MobjThinker (mobj_t* mobj) +{ + // killough 11/98: + // removed old code which looked at target references + // (we use pointer reference counting now) + + mobj->PrevX = mobj->x; + mobj->PrevY = mobj->y; + mobj->PrevZ = mobj->z; + + // momentum movement + if (mobj->momx | mobj->momy || mobj->flags & MF_SKULLFLY) + { + P_XYMovement(mobj); + if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed + return; // killough - mobj was removed + } + + if (mobj->z != mobj->floorz || mobj->momz) + { + P_ZMovement(mobj); + if (mobj->thinker.function != P_MobjThinker) // cph - Must've been removed + return; // killough - mobj was removed + } + else + if (!(mobj->momx | mobj->momy) && !sentient(mobj)) + { // non-sentient objects at rest + mobj->intflags |= MIF_ARMED; // arm a mine which has come to rest + + // killough 9/12/98: objects fall off ledges if they are hanging off + // slightly push off of ledge if hanging more than halfway off + + if (mobj->z > mobj->dropoffz && // Only objects contacting dropoff + !(mobj->flags & MF_NOGRAVITY) && // Only objects which fall + !comp[comp_falloff]) // Not in old demos + P_ApplyTorque(mobj); // Apply torque + else + mobj->intflags &= ~MIF_FALLING, mobj->gear = 0; // Reset torque + } + + // cycle through states, + // calling action functions at transitions + + if (mobj->tics != -1) + { + mobj->tics--; + + // you can cycle through multiple states in a tic + + if (!mobj->tics) + if (!P_SetMobjState (mobj, mobj->state->nextstate) ) + return; // freed itself + } + else + { + + // check for nightmare respawn + + if (! (mobj->flags & MF_COUNTKILL) ) + return; + + if (!respawnmonsters) + return; + + mobj->movecount++; + + if (mobj->movecount < 12*35) + return; + + if (leveltime & 31) + return; + + if (P_Random (pr_respawn) > 4) + return; + + P_NightmareRespawn (mobj); + } + +} + + +// +// P_SpawnMobj +// +mobj_t* P_SpawnMobj(fixed_t x,fixed_t y,fixed_t z,mobjtype_t type) +{ + mobj_t* mobj; + state_t* st; + mobjinfo_t* info; + + mobj = Z_Malloc (sizeof(*mobj), PU_LEVEL, NULL); + memset (mobj, 0, sizeof (*mobj)); + info = &mobjinfo[type]; + mobj->type = type; + mobj->info = info; + mobj->x = x; + mobj->y = y; + mobj->radius = info->radius; + mobj->height = info->height; // phares + mobj->flags = info->flags; + + /* killough 8/23/98: no friends, bouncers, or touchy things in old demos */ + if (!mbf_features) + mobj->flags &= ~(MF_BOUNCES | MF_FRIEND | MF_TOUCHY); + else + if (type == MT_PLAYER) // Except in old demos, players + mobj->flags |= MF_FRIEND; // are always friends. + + mobj->health = info->spawnhealth; + + if (gameskill != sk_nightmare) + mobj->reactiontime = info->reactiontime; + + mobj->lastlook = P_Random (pr_lastlook) % MAXPLAYERS; + + // do not set the state with P_SetMobjState, + // because action routines can not be called yet + + st = &states[info->spawnstate]; + + mobj->state = st; + mobj->tics = st->tics; + mobj->sprite = st->sprite; + mobj->frame = st->frame; + mobj->touching_sectorlist = NULL; // NULL head of sector list // phares 3/13/98 + + // set subsector and/or block links + + P_SetThingPosition (mobj); + + mobj->dropoffz = /* killough 11/98: for tracking dropoffs */ + mobj->floorz = mobj->subsector->sector->floorheight; + mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + mobj->z = z == ONFLOORZ ? mobj->floorz : z == ONCEILINGZ ? + mobj->ceilingz - mobj->height : z; + + mobj->PrevX = mobj->x; + mobj->PrevY = mobj->y; + mobj->PrevZ = mobj->z; + + mobj->thinker.function = P_MobjThinker; + + //e6y + mobj->friction = ORIG_FRICTION; // phares 3/17/98 + + mobj->target = mobj->tracer = mobj->lastenemy = NULL; + P_AddThinker (&mobj->thinker); + if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totallive++; + return mobj; +} + + +static mapthing_t itemrespawnque[ITEMQUESIZE]; +static int itemrespawntime[ITEMQUESIZE]; +int iquehead; +int iquetail; + + +// +// P_RemoveMobj +// + +void P_RemoveMobj (mobj_t* mobj) +{ + if ((mobj->flags & MF_SPECIAL) + && !(mobj->flags & MF_DROPPED) + && (mobj->type != MT_INV) + && (mobj->type != MT_INS)) + { + itemrespawnque[iquehead] = mobj->spawnpoint; + itemrespawntime[iquehead] = leveltime; + iquehead = (iquehead+1)&(ITEMQUESIZE-1); + + // lose one off the end? + + if (iquehead == iquetail) + iquetail = (iquetail+1)&(ITEMQUESIZE-1); + } + + // unlink from sector and block lists + + P_UnsetThingPosition (mobj); + + // Delete all nodes on the current sector_list phares 3/16/98 + + if (sector_list) + { + P_DelSeclist(sector_list); + sector_list = NULL; + } + + // stop any playing sound + + S_StopSound (mobj); + + // killough 11/98: + // + // Remove any references to other mobjs. + // + // Older demos might depend on the fields being left alone, however, + // if multiple thinkers reference each other indirectly before the + // end of the current tic. + // CPhipps - only leave dead references in old demos; I hope lxdoom_1 level + // demos are rare and don't rely on this. I hope. + + if ((compatibility_level >= lxdoom_1_compatibility) || + (!demoplayback)) { + P_SetTarget(&mobj->target, NULL); + P_SetTarget(&mobj->tracer, NULL); + P_SetTarget(&mobj->lastenemy, NULL); + } + // free block + + P_RemoveThinker (&mobj->thinker); +} + + +/* + * P_FindDoomedNum + * + * Finds a mobj type with a matching doomednum + * + * killough 8/24/98: rewrote to use hashing + */ + +static PUREFUNC int P_FindDoomedNum(unsigned type) +{ + static struct { int first, next; } *hash; + register int i; + + if (!hash) + { + hash = Z_Malloc(sizeof *hash * NUMMOBJTYPES, PU_CACHE, (void **) &hash); + for (i=0; ix << FRACBITS; + y = mthing->y << FRACBITS; + + // spawn a teleport fog at the new spot + + ss = R_PointInSubsector (x,y); + mo = P_SpawnMobj (x, y, ss->sector->floorheight , MT_IFOG); + S_StartSound (mo, sfx_itmbk); + + // find which type to spawn + + /* killough 8/23/98: use table for faster lookup */ + i = P_FindDoomedNum(mthing->type); + + // spawn it + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mo = P_SpawnMobj (x,y,z, i); + mo->spawnpoint = *mthing; + mo->angle = ANG45 * (mthing->angle/45); + + // pull it from the queue + + iquetail = (iquetail+1)&(ITEMQUESIZE-1); +} + +// +// P_SpawnPlayer +// Called when a player is spawned on the level. +// Most of the player structure stays unchanged +// between levels. +// + +extern byte playernumtotrans[MAXPLAYERS]; + +void P_SpawnPlayer (int n, const mapthing_t* mthing) +{ + player_t* p; + fixed_t x; + fixed_t y; + fixed_t z; + mobj_t* mobj; + int i; + + // not playing? + + if (!playeringame[n]) + return; + + p = &players[n]; + + if (p->playerstate == PST_REBORN) + G_PlayerReborn (mthing->type-1); + + /* cph 2001/08/14 - use the options field of memorised player starts to + * indicate whether the start really exists in the level. + */ + if (!mthing->options) + I_Error("P_SpawnPlayer: attempt to spawn player at unavailable start point"); + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + z = ONFLOORZ; + mobj = P_SpawnMobj (x,y,z, MT_PLAYER); + + // set color translations for player sprites + + mobj->flags |= playernumtotrans[n]<angle = ANG45 * (mthing->angle/45); + mobj->player = p; + mobj->health = p->health; + + p->mo = mobj; + p->playerstate = PST_LIVE; + p->refire = 0; + p->message = NULL; + p->damagecount = 0; + p->bonuscount = 0; + p->extralight = 0; + p->fixedcolormap = 0; + p->viewheight = VIEWHEIGHT; + + p->momx = p->momy = 0; // killough 10/98: initialize bobbing to 0. + + // setup gun psprite + + P_SetupPsprites (p); + + // give all cards in death match mode + + if (deathmatch) + for (i = 0 ; i < NUMCARDS ; i++) + p->cards[i] = true; + + if (mthing->type-1 == consoleplayer) + { + ST_Start(); // wake up the status bar + HU_Start(); // wake up the heads up text + } + R_SmoothPlaying_Reset(p); // e6y +} + +/* + * P_IsDoomnumAllowed() + * Based on code taken from P_LoadThings() in src/p_setup.c Return TRUE + * if the thing in question is expected to be available in the gamemode used. + */ + +boolean P_IsDoomnumAllowed(int doomnum) +{ + // Do not spawn cool, new monsters if !commercial + if (gamemode != commercial) + switch(doomnum) + { + case 64: // Archvile + case 65: // Former Human Commando + case 66: // Revenant + case 67: // Mancubus + case 68: // Arachnotron + case 69: // Hell Knight + case 71: // Pain Elemental + case 84: // Wolf SS + case 88: // Boss Brain + case 89: // Boss Shooter + return false; + } + + return true; +} + +// +// P_SpawnMapThing +// The fields of the mapthing should +// already be in host byte order. +// + +void P_SpawnMapThing (const mapthing_t* mthing) +{ + int i; + //int bit; + mobj_t* mobj; + fixed_t x; + fixed_t y; + fixed_t z; + int options = mthing->options; /* cph 2001/07/07 - make writable copy */ + + // killough 2/26/98: Ignore type-0 things as NOPs + // phares 5/14/98: Ignore Player 5-8 starts (for now) + + switch(mthing->type) + { + case 0: + case DEN_PLAYER5: + case DEN_PLAYER6: + case DEN_PLAYER7: + case DEN_PLAYER8: + return; + } + + // killough 11/98: clear flags unused by Doom + // + // We clear the flags unused in Doom if we see flag mask 256 set, since + // it is reserved to be 0 under the new scheme. A 1 in this reserved bit + // indicates it's a Doom wad made by a Doom editor which puts 1's in + // bits that weren't used in Doom (such as HellMaker wads). So we should + // then simply ignore all upper bits. + + if (demo_compatibility || + (compatibility_level >= lxdoom_1_compatibility && + options & MTF_RESERVED)) { + if (!demo_compatibility) // cph - Add warning about bad thing flags + lprintf(LO_WARN, "P_SpawnMapThing: correcting bad flags (%u) (thing type %d)\n", + options, mthing->type); + options &= MTF_EASY|MTF_NORMAL|MTF_HARD|MTF_AMBUSH|MTF_NOTSINGLE; + } + + // count deathmatch start positions + + // doom2.exe has at most 10 deathmatch starts + if (mthing->type == 11) + { + if (!(!compatibility || deathmatch_p-deathmatchstarts < 10)) { + return; + } else { + // 1/11/98 killough -- new code removes limit on deathmatch starts: + + size_t offset = deathmatch_p - deathmatchstarts; + + if (offset >= num_deathmatchstarts) + { + num_deathmatchstarts = num_deathmatchstarts ? + num_deathmatchstarts*2 : 16; + deathmatchstarts = realloc(deathmatchstarts, + num_deathmatchstarts * + sizeof(*deathmatchstarts)); + deathmatch_p = deathmatchstarts + offset; + } + memcpy(deathmatch_p++, mthing, sizeof(*mthing)); + (deathmatch_p-1)->options = 1; + return; + } + } + + // check for players specially + + if (mthing->type <= 4 && mthing->type > 0) // killough 2/26/98 -- fix crashes + { + + // save spots for respawning in coop games + playerstarts[mthing->type-1] = *mthing; + /* cph 2006/07/24 - use the otherwise-unused options field to flag that + * this start is present (so we know which elements of the array are filled + * in, in effect). Also note that the call below to P_SpawnPlayer must use + * the playerstarts version with this field set */ + playerstarts[mthing->type-1].options = 1; + + if (!deathmatch) + P_SpawnPlayer (mthing->type-1, &playerstarts[mthing->type-1]); + return; + } + + // check for apropriate skill level + + /* jff "not single" thing flag */ + if (!netgame && options & MTF_NOTSINGLE) + return; + + //jff 3/30/98 implement "not deathmatch" thing flag + + if (netgame && deathmatch && options & MTF_NOTDM) + return; + + //jff 3/30/98 implement "not cooperative" thing flag + + if (netgame && !deathmatch && options & MTF_NOTCOOP) + return; + + // killough 11/98: simplify + if (gameskill == sk_baby || gameskill == sk_easy ? + !(options & MTF_EASY) : + gameskill == sk_hard || gameskill == sk_nightmare ? + !(options & MTF_HARD) : !(options & MTF_NORMAL)) + return; + + // find which type to spawn + + // killough 8/23/98: use table for faster lookup + i = P_FindDoomedNum(mthing->type); + + // phares 5/16/98: + // Do not abort because of an unknown thing. Ignore it, but post a + // warning message for the player. + + if (i == NUMMOBJTYPES) + { + doom_printf("Unknown Thing type %i at (%i, %i)",mthing->type,mthing->x,mthing->y); + return; + } + + // don't spawn keycards and players in deathmatch + + if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH) + return; + + // don't spawn any monsters if -nomonsters + + if (nomonsters && (i == MT_SKULL || (mobjinfo[i].flags & MF_COUNTKILL))) + return; + + // spawn it + + x = mthing->x << FRACBITS; + y = mthing->y << FRACBITS; + + if (mobjinfo[i].flags & MF_SPAWNCEILING) + z = ONCEILINGZ; + else + z = ONFLOORZ; + + mobj = P_SpawnMobj (x,y,z, i); + mobj->spawnpoint = *mthing; + + if (mobj->tics > 0) + mobj->tics = 1 + (P_Random (pr_spawnthing) % mobj->tics); + + if (!(mobj->flags & MF_FRIEND) && + options & MTF_FRIEND && + mbf_features) + { + mobj->flags |= MF_FRIEND; // killough 10/98: + P_UpdateThinker(&mobj->thinker); // transfer friendliness flag + } + + /* killough 7/20/98: exclude friends */ + if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL))) + totalkills++; + + if (mobj->flags & MF_COUNTITEM) + totalitems++; + + mobj->angle = ANG45 * (mthing->angle/45); + if (options & MTF_AMBUSH) + mobj->flags |= MF_AMBUSH; +} + + +// +// GAME SPAWN FUNCTIONS +// + +// +// P_SpawnPuff +// + +extern fixed_t attackrange; + +void P_SpawnPuff(fixed_t x,fixed_t y,fixed_t z) +{ + mobj_t* th; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_spawnpuff); + z += (t - P_Random(pr_spawnpuff))<<10; + + th = P_SpawnMobj (x,y,z, MT_PUFF); + th->momz = FRACUNIT; + th->tics -= P_Random(pr_spawnpuff)&3; + + if (th->tics < 1) + th->tics = 1; + + // don't make punches spark on the wall + + if (attackrange == MELEERANGE) + P_SetMobjState (th, S_PUFF3); +} + + +// +// P_SpawnBlood +// +void P_SpawnBlood(fixed_t x,fixed_t y,fixed_t z,int damage) +{ + mobj_t* th; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_spawnblood); + z += (t - P_Random(pr_spawnblood))<<10; + th = P_SpawnMobj(x,y,z, MT_BLOOD); + th->momz = FRACUNIT*2; + th->tics -= P_Random(pr_spawnblood)&3; + + if (th->tics < 1) + th->tics = 1; + + if (damage <= 12 && damage >= 9) + P_SetMobjState (th,S_BLOOD2); + else if (damage < 9) + P_SetMobjState (th,S_BLOOD3); +} + + +// +// P_CheckMissileSpawn +// Moves the missile forward a bit +// and possibly explodes it right there. +// + +void P_CheckMissileSpawn (mobj_t* th) +{ + th->tics -= P_Random(pr_missile)&3; + if (th->tics < 1) + th->tics = 1; + + // move a little forward so an angle can + // be computed if it immediately explodes + + th->x += (th->momx>>1); + th->y += (th->momy>>1); + th->z += (th->momz>>1); + + // killough 8/12/98: for non-missile objects (e.g. grenades) + if (!(th->flags & MF_MISSILE) && mbf_features) + return; + + // killough 3/15/98: no dropoff (really = don't care for missiles) + + if (!P_TryMove (th, th->x, th->y, false)) + P_ExplodeMissile (th); +} + + +// +// P_SpawnMissile +// + +mobj_t* P_SpawnMissile(mobj_t* source,mobj_t* dest,mobjtype_t type) +{ + mobj_t* th; + angle_t an; + int dist; + + th = P_SpawnMobj (source->x,source->y,source->z + 4*8*FRACUNIT,type); + + if (th->info->seesound) + S_StartSound (th, th->info->seesound); + + P_SetTarget(&th->target, source); // where it came from + an = R_PointToAngle2 (source->x, source->y, dest->x, dest->y); + + // fuzzy player + + if (dest->flags & MF_SHADOW) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_shadow); + an += (t - P_Random(pr_shadow))<<20; + } + + th->angle = an; + an >>= ANGLETOFINESHIFT; + th->momx = FixedMul (th->info->speed, finecosine[an]); + th->momy = FixedMul (th->info->speed, finesine[an]); + + dist = P_AproxDistance (dest->x - source->x, dest->y - source->y); + dist = dist / th->info->speed; + + if (dist < 1) + dist = 1; + + th->momz = (dest->z - source->z) / dist; + P_CheckMissileSpawn (th); + + return th; +} + + +// +// P_SpawnPlayerMissile +// Tries to aim at a nearby monster +// + +void P_SpawnPlayerMissile(mobj_t* source,mobjtype_t type) +{ + mobj_t *th; + fixed_t x, y, z, slope = 0; + + // see which target is to be aimed at + + angle_t an = source->angle; + + // killough 7/19/98: autoaiming was not in original beta + { + // killough 8/2/98: prefer autoaiming at enemies + uint_64_t mask = mbf_features ? MF_FRIEND : 0; + + do + { + slope = P_AimLineAttack(source, an, 16*64*FRACUNIT, mask); + if (!linetarget) + slope = P_AimLineAttack(source, an += 1<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + slope = P_AimLineAttack(source, an -= 2<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + an = source->angle, slope = 0; + } + while (mask && (mask=0, !linetarget)); // killough 8/2/98 + } + + x = source->x; + y = source->y; + z = source->z + 4*8*FRACUNIT; + + th = P_SpawnMobj (x,y,z, type); + + if (th->info->seesound) + S_StartSound (th, th->info->seesound); + + P_SetTarget(&th->target, source); + th->angle = an; + th->momx = FixedMul(th->info->speed,finecosine[an>>ANGLETOFINESHIFT]); + th->momy = FixedMul(th->info->speed,finesine[an>>ANGLETOFINESHIFT]); + th->momz = FixedMul(th->info->speed,slope); + + P_CheckMissileSpawn(th); + } diff --git a/src/p_mobj.h b/src/p_mobj.h new file mode 100644 index 00000000..ccd66f3c --- /dev/null +++ b/src/p_mobj.h @@ -0,0 +1,403 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Map Objects, MObj, definition and handling. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_MOBJ__ +#define __P_MOBJ__ + +// Basics. +#include "tables.h" +#include "m_fixed.h" + +// We need the thinker_t stuff. +#include "d_think.h" + +// We need the WAD data structure for Map things, +// from the THINGS lump. +#include "doomdata.h" + +// States are tied to finite states are +// tied to animation frames. +// Needs precompiled tables/data structures. +#include "info.h" + +// +// NOTES: mobj_t +// +// mobj_ts are used to tell the refresh where to draw an image, +// tell the world simulation when objects are contacted, +// and tell the sound driver how to position a sound. +// +// The refresh uses the next and prev links to follow +// lists of things in sectors as they are being drawn. +// The sprite, frame, and angle elements determine which patch_t +// is used to draw the sprite if it is visible. +// The sprite and frame values are allmost allways set +// from state_t structures. +// The statescr.exe utility generates the states.h and states.c +// files that contain the sprite/frame numbers from the +// statescr.txt source file. +// The xyz origin point represents a point at the bottom middle +// of the sprite (between the feet of a biped). +// This is the default origin position for patch_ts grabbed +// with lumpy.exe. +// A walking creature will have its z equal to the floor +// it is standing on. +// +// The sound code uses the x,y, and subsector fields +// to do stereo positioning of any sound effited by the mobj_t. +// +// The play simulation uses the blocklinks, x,y,z, radius, height +// to determine when mobj_ts are touching each other, +// touching lines in the map, or hit by trace lines (gunshots, +// lines of sight, etc). +// The mobj_t->flags element has various bit flags +// used by the simulation. +// +// Every mobj_t is linked into a single sector +// based on its origin coordinates. +// The subsector_t is found with R_PointInSubsector(x,y), +// and the sector_t can be found with subsector->sector. +// The sector links are only used by the rendering code, +// the play simulation does not care about them at all. +// +// Any mobj_t that needs to be acted upon by something else +// in the play world (block movement, be shot, etc) will also +// need to be linked into the blockmap. +// If the thing has the MF_NOBLOCK flag set, it will not use +// the block links. It can still interact with other things, +// but only as the instigator (missiles will run into other +// things, but nothing can run into a missile). +// Each block in the grid is 128*128 units, and knows about +// every line_t that it contains a piece of, and every +// interactable mobj_t that has its origin contained. +// +// A valid mobj_t is a mobj_t that has the proper subsector_t +// filled in for its xy coordinates and is linked into the +// sector from which the subsector was made, or has the +// MF_NOSECTOR flag set (the subsector_t needs to be valid +// even if MF_NOSECTOR is set), and is linked into a blockmap +// block or has the MF_NOBLOCKMAP flag set. +// Links should only be modified by the P_[Un]SetThingPosition() +// functions. +// Do not change the MF_NO? flags while a thing is valid. +// +// Any questions? +// + +// +// Misc. mobj flags +// + +// Call P_SpecialThing when touched. +#define MF_SPECIAL (uint_64_t)(0x0000000000000001) +// Blocks. +#define MF_SOLID (uint_64_t)(0x0000000000000002) +// Can be hit. +#define MF_SHOOTABLE (uint_64_t)(0x0000000000000004) +// Don't use the sector links (invisible but touchable). +#define MF_NOSECTOR (uint_64_t)(0x0000000000000008) +// Don't use the blocklinks (inert but displayable) +#define MF_NOBLOCKMAP (uint_64_t)(0x0000000000000010) + +// Not to be activated by sound, deaf monster. +#define MF_AMBUSH (uint_64_t)(0x0000000000000020) +// Will try to attack right back. +#define MF_JUSTHIT (uint_64_t)(0x0000000000000040) +// Will take at least one step before attacking. +#define MF_JUSTATTACKED (uint_64_t)(0x0000000000000080) +// On level spawning (initial position), +// hang from ceiling instead of stand on floor. +#define MF_SPAWNCEILING (uint_64_t)(0x0000000000000100) +// Don't apply gravity (every tic), +// that is, object will float, keeping current height +// or changing it actively. +#define MF_NOGRAVITY (uint_64_t)(0x0000000000000200) + +// Movement flags. +// This allows jumps from high places. +#define MF_DROPOFF (uint_64_t)(0x0000000000000400) +// For players, will pick up items. +#define MF_PICKUP (uint_64_t)(0x0000000000000800) +// Player cheat. ??? +#define MF_NOCLIP (uint_64_t)(0x0000000000001000) +// Player: keep info about sliding along walls. +#define MF_SLIDE (uint_64_t)(0x0000000000002000) +// Allow moves to any height, no gravity. +// For active floaters, e.g. cacodemons, pain elementals. +#define MF_FLOAT (uint_64_t)(0x0000000000004000) +// Don't cross lines +// ??? or look at heights on teleport. +#define MF_TELEPORT (uint_64_t)(0x0000000000008000) +// Don't hit same species, explode on block. +// Player missiles as well as fireballs of various kinds. +#define MF_MISSILE (uint_64_t)(0x0000000000010000) +// Dropped by a demon, not level spawned. +// E.g. ammo clips dropped by dying former humans. +#define MF_DROPPED (uint_64_t)(0x0000000000020000) +// Use fuzzy draw (shadow demons or spectres), +// temporary player invisibility powerup. +#define MF_SHADOW (uint_64_t)(0x0000000000040000) +// Flag: don't bleed when shot (use puff), +// barrels and shootable furniture shall not bleed. +#define MF_NOBLOOD (uint_64_t)(0x0000000000080000) +// Don't stop moving halfway off a step, +// that is, have dead bodies slide down all the way. +#define MF_CORPSE (uint_64_t)(0x0000000000100000) +// Floating to a height for a move, ??? +// don't auto float to target's height. +#define MF_INFLOAT (uint_64_t)(0x0000000000200000) + +// On kill, count this enemy object +// towards intermission kill total. +// Happy gathering. +#define MF_COUNTKILL (uint_64_t)(0x0000000000400000) + +// On picking up, count this item object +// towards intermission item total. +#define MF_COUNTITEM (uint_64_t)(0x0000000000800000) + +// Special handling: skull in flight. +// Neither a cacodemon nor a missile. +#define MF_SKULLFLY (uint_64_t)(0x0000000001000000) + +// Don't spawn this object +// in death match mode (e.g. key cards). +#define MF_NOTDMATCH (uint_64_t)(0x0000000002000000) + +// Player sprites in multiplayer modes are modified +// using an internal color lookup table for re-indexing. +// If 0x4 0x8 or 0xc, +// use a translation table for player colormaps +#define MF_TRANSLATION (uint_64_t)(0x000000000c000000) +#define MF_TRANSLATION1 (uint_64_t)(0x0000000004000000) +#define MF_TRANSLATION2 (uint_64_t)(0x0000000008000000) +// Hmm ???. +#define MF_TRANSSHIFT 26 + +#define MF_UNUSED2 (uint_64_t)(0x0000000010000000) +#define MF_UNUSED3 (uint_64_t)(0x0000000020000000) + + // Translucent sprite? // phares +#define MF_TRANSLUCENT (uint_64_t)(0x0000000040000000) + +// this is free LONGLONG(0x0000000100000000) + +// these are greater than an int. That's why the flags below are now uint_64_t + +#define MF_TOUCHY LONGLONG(0x0000000100000000) +#define MF_BOUNCES LONGLONG(0x0000000200000000) +#define MF_FRIEND LONGLONG(0x0000000400000000) + +// killough 9/15/98: Same, but internal flags, not intended for .deh +// (some degree of opaqueness is good, to avoid compatibility woes) + +enum { + MIF_FALLING = 1, // Object is falling + MIF_ARMED = 2, // Object is armed (for MF_TOUCHY objects) +}; + +// Map Object definition. +// +// +// killough 2/20/98: +// +// WARNING: Special steps must be taken in p_saveg.c if C pointers are added to +// this mobj_s struct, or else savegames will crash when loaded. See p_saveg.c. +// Do not add "struct mobj_s *fooptr" without adding code to p_saveg.c to +// convert the pointers to ordinals and back for savegames. This was the whole +// reason behind monsters going to sleep when loading savegames (the "target" +// pointer was simply nullified after loading, to prevent Doom from crashing), +// and the whole reason behind loadgames crashing on savegames of AV attacks. +// + +// killough 9/8/98: changed some fields to shorts, +// for better memory usage (if only for cache). +/* cph 2006/08/28 - move Prev[XYZ] fields to the end of the struct. Add any + * other new fields to the end, and make sure you don't break savegames! */ + +typedef struct mobj_s +{ + // List: thinker links. + thinker_t thinker; + + // Info for drawing: position. + fixed_t x; + fixed_t y; + fixed_t z; + + // More list: links in sector (if needed) + struct mobj_s* snext; + struct mobj_s** sprev; // killough 8/10/98: change to ptr-to-ptr + + //More drawing info: to determine current sprite. + angle_t angle; // orientation + spritenum_t sprite; // used to find patch_t and flip value + int frame; // might be ORed with FF_FULLBRIGHT + + // Interaction info, by BLOCKMAP. + // Links in blocks (if needed). + struct mobj_s* bnext; + struct mobj_s** bprev; // killough 8/11/98: change to ptr-to-ptr + + struct subsector_s* subsector; + + // The closest interval over all contacted Sectors. + fixed_t floorz; + fixed_t ceilingz; + + // killough 11/98: the lowest floor over all contacted Sectors. + fixed_t dropoffz; + + // For movement checking. + fixed_t radius; + fixed_t height; + + // Momentums, used to update position. + fixed_t momx; + fixed_t momy; + fixed_t momz; + + // If == validcount, already checked. + int validcount; + + mobjtype_t type; + mobjinfo_t* info; // &mobjinfo[mobj->type] + + int tics; // state tic counter + state_t* state; + uint_64_t flags; + int intflags; // killough 9/15/98: internal flags + int health; + + // Movement direction, movement generation (zig-zagging). + short movedir; // 0-7 + short movecount; // when 0, select a new dir + short strafecount; // killough 9/8/98: monster strafing + + // Thing being chased/attacked (or NULL), + // also the originator for missiles. + struct mobj_s* target; + + // Reaction time: if non 0, don't attack yet. + // Used by player to freeze a bit after teleporting. + short reactiontime; + + // If >0, the current target will be chased no + // matter what (even if shot by another object) + short threshold; + + // killough 9/9/98: How long a monster pursues a target. + short pursuecount; + + short gear; // killough 11/98: used in torque simulation + + // Additional info record for player avatars only. + // Only valid if type == MT_PLAYER + struct player_s* player; + + // Player number last looked for. + short lastlook; + + // For nightmare respawn. + mapthing_t spawnpoint; + + // Thing being chased/attacked for tracers. + struct mobj_s* tracer; + + // new field: last known enemy -- killough 2/15/98 + struct mobj_s* lastenemy; + + // killough 8/2/98: friction properties part of sectors, + // not objects -- removed friction properties from here + // e6y: restored friction properties here + // Friction values for the sector the object is in + int friction; // phares 3/17/98 + int movefactor; + + // a linked list of sectors where this object appears + struct msecnode_s* touching_sectorlist; // phares 3/14/98 + + fixed_t PrevX; + fixed_t PrevY; + fixed_t PrevZ; + + fixed_t pad; // cph - needed so I can get the size unambiguously on amd64 + + // SEE WARNING ABOVE ABOUT POINTER FIELDS!!! +} mobj_t; + +// External declarations (fomerly in p_local.h) -- killough 5/2/98 + +#define VIEWHEIGHT (41*FRACUNIT) + +#define GRAVITY FRACUNIT +#define MAXMOVE (30*FRACUNIT) + +#define ONFLOORZ INT_MIN +#define ONCEILINGZ INT_MAX + +// Time interval for item respawning. +#define ITEMQUESIZE 128 + +#define FLOATSPEED (FRACUNIT*4) +#define STOPSPEED (FRACUNIT/16) + +// killough 11/98: +// For torque simulation: + +#define OVERDRIVE 6 +#define MAXGEAR (OVERDRIVE+16) + +// killough 11/98: +// Whether an object is "sentient" or not. Used for environmental influences. +#define sentient(mobj) ((mobj)->health > 0 && (mobj)->info->seestate) + +extern int iquehead; +extern int iquetail; + +void P_RespawnSpecials(void); +mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); +void P_RemoveMobj(mobj_t *th); +boolean P_SetMobjState(mobj_t *mobj, statenum_t state); +void P_MobjThinker(mobj_t *mobj); +void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z); +void P_SpawnBlood(fixed_t x, fixed_t y, fixed_t z, int damage); +mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type); +void P_SpawnPlayerMissile(mobj_t *source, mobjtype_t type); +boolean P_IsDoomnumAllowed(int doomnum); +void P_SpawnMapThing (const mapthing_t* mthing); +void P_SpawnPlayer(int n, const mapthing_t *mthing); +void P_CheckMissileSpawn(mobj_t*); // killough 8/2/98 +void P_ExplodeMissile(mobj_t*); // killough +#endif + diff --git a/src/p_plats.c b/src/p_plats.c new file mode 100644 index 00000000..9bea2409 --- /dev/null +++ b/src/p_plats.c @@ -0,0 +1,437 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Plats (i.e. elevator platforms) code, raising/lowering. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_random.h" +#include "r_main.h" +#include "p_spec.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" + +platlist_t *activeplats; // killough 2/14/98: made global again + +// +// T_PlatRaise() +// +// Action routine to move a plat up and down +// +// Passed a plat structure containing all pertinent information about the move +// No return +// +// jff 02/08/98 all cases with labels beginning with gen added to support +// generalized line type behaviors. + +void T_PlatRaise(plat_t* plat) +{ + result_e res; + + // handle plat moving, up, down, waiting, or in stasis, + switch(plat->status) + { + case up: // plat moving up + res = T_MovePlane(plat->sector,plat->speed,plat->high,plat->crush,0,1); + + // if a pure raise type, make the plat moving sound + if (plat->type == raiseAndChange + || plat->type == raiseToNearestAndChange) + { + if (!(leveltime&7)) + S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_stnmov); + } + + // if encountered an obstacle, and not a crush type, reverse direction + if (res == crushed && (!plat->crush)) + { + plat->count = plat->wait; + plat->status = down; + S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstart); + } + else // else handle reaching end of up stroke + { + if (res == pastdest) // end of stroke + { + // if not an instant toggle type, wait, make plat stop sound + if (plat->type!=toggleUpDn) + { + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t *)&plat->sector->soundorg, sfx_pstop); + } + else // else go into stasis awaiting next toggle activation + { + plat->oldstatus = plat->status;//jff 3/14/98 after action wait + plat->status = in_stasis; //for reactivation of toggle + } + + // lift types and pure raise types are done at end of up stroke + // only the perpetual type waits then goes back up + switch(plat->type) + { + case blazeDWUS: + case downWaitUpStay: + case raiseAndChange: + case raiseToNearestAndChange: + case genLift: + P_RemoveActivePlat(plat); // killough + default: + break; + } + } + } + break; + + case down: // plat moving down + res = T_MovePlane(plat->sector,plat->speed,plat->low,false,0,-1); + + // handle reaching end of down stroke + if (res == pastdest) + { + // if not an instant toggle, start waiting, make plat stop sound + if (plat->type!=toggleUpDn) //jff 3/14/98 toggle up down + { // is silent, instant, no waiting + plat->count = plat->wait; + plat->status = waiting; + S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstop); + } + else // instant toggles go into stasis awaiting next activation + { + plat->oldstatus = plat->status;//jff 3/14/98 after action wait + plat->status = in_stasis; //for reactivation of toggle + } + + //jff 1/26/98 remove the plat if it bounced so it can be tried again + //only affects plats that raise and bounce + //killough 1/31/98: relax compatibility to demo_compatibility + + // remove the plat if its a pure raise type + if (!comp[comp_floors]) + { + switch(plat->type) + { + case raiseAndChange: + case raiseToNearestAndChange: + P_RemoveActivePlat(plat); + default: + break; + } + } + } + break; + + case waiting: // plat is waiting + if (!--plat->count) // downcount and check for delay elapsed + { + if (plat->sector->floorheight == plat->low) + plat->status = up; // if at bottom, start up + else + plat->status = down; // if at top, start down + + // make plat start sound + S_StartSound((mobj_t *)&plat->sector->soundorg,sfx_pstart); + } + break; //jff 1/27/98 don't pickup code added later to in_stasis + + case in_stasis: // do nothing if in stasis + break; + } +} + + +// +// EV_DoPlat +// +// Handle Plat linedef types +// +// Passed the linedef that activated the plat, the type of plat action, +// and for some plat types, an amount to raise +// Returns true if a thinker is started, or restarted from stasis +// +int EV_DoPlat +( line_t* line, + plattype_e type, + int amount ) +{ + plat_t* plat; + int secnum; + int rtn; + sector_t* sec; + + secnum = -1; + rtn = 0; + + + // Activate all plats that are in_stasis + switch(type) + { + case perpetualRaise: + P_ActivateInStasis(line->tag); + break; + + case toggleUpDn: + P_ActivateInStasis(line->tag); + rtn=1; + break; + + default: + break; + } + + // act on all sectors tagged the same as the activating linedef + while ((secnum = P_FindSectorFromLineTag(line,secnum)) >= 0) + { + sec = §ors[secnum]; + + // don't start a second floor function if already moving + if (P_SectorActive(floor_special,sec)) //jff 2/23/98 multiple thinkers + continue; + + // Create a thinker + rtn = 1; + plat = Z_Malloc( sizeof(*plat), PU_LEVSPEC, 0); + memset(plat, 0, sizeof(*plat)); + P_AddThinker(&plat->thinker); + + plat->type = type; + plat->sector = sec; + plat->sector->floordata = plat; //jff 2/23/98 multiple thinkers + plat->thinker.function = T_PlatRaise; + plat->crush = false; + plat->tag = line->tag; + + //jff 1/26/98 Avoid raise plat bouncing a head off a ceiling and then + //going down forever -- default low to plat height when triggered + plat->low = sec->floorheight; + + // set up plat according to type + switch(type) + { + case raiseToNearestAndChange: + plat->speed = PLATSPEED/2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = P_FindNextHighestFloor(sec,sec->floorheight); + plat->wait = 0; + plat->status = up; + sec->special = 0; + //jff 3/14/98 clear old field as well + sec->oldspecial = 0; + + S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov); + break; + + case raiseAndChange: + plat->speed = PLATSPEED/2; + sec->floorpic = sides[line->sidenum[0]].sector->floorpic; + plat->high = sec->floorheight + amount*FRACUNIT; + plat->wait = 0; + plat->status = up; + + S_StartSound((mobj_t *)&sec->soundorg,sfx_stnmov); + break; + + case downWaitUpStay: + plat->speed = PLATSPEED * 4; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35*PLATWAIT; + plat->status = down; + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + break; + + case blazeDWUS: + plat->speed = PLATSPEED * 8; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = sec->floorheight; + plat->wait = 35*PLATWAIT; + plat->status = down; + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + break; + + case perpetualRaise: + plat->speed = PLATSPEED; + plat->low = P_FindLowestFloorSurrounding(sec); + + if (plat->low > sec->floorheight) + plat->low = sec->floorheight; + + plat->high = P_FindHighestFloorSurrounding(sec); + + if (plat->high < sec->floorheight) + plat->high = sec->floorheight; + + plat->wait = 35*PLATWAIT; + plat->status = P_Random(pr_plats)&1; + + S_StartSound((mobj_t *)&sec->soundorg,sfx_pstart); + break; + + case toggleUpDn: //jff 3/14/98 add new type to support instant toggle + plat->speed = PLATSPEED; //not used + plat->wait = 35*PLATWAIT; //not used + plat->crush = true; //jff 3/14/98 crush anything in the way + + // set up toggling between ceiling, floor inclusive + plat->low = sec->ceilingheight; + plat->high = sec->floorheight; + plat->status = down; + break; + + default: + break; + } + P_AddActivePlat(plat); // add plat to list of active plats + } + return rtn; +} + +// The following were all rewritten by Lee Killough +// to use the new structure which places no limits +// on active plats. It also avoids spending as much +// time searching for active plats. Previously a +// fixed-size array was used, with NULL indicating +// empty entries, while now a doubly-linked list +// is used. + +// +// P_ActivateInStasis() +// +// Activate a plat that has been put in stasis +// (stopped perpetual floor, instant floor/ceil toggle) +// +// Passed the tag of the plat that should be reactivated +// Returns nothing +// +void P_ActivateInStasis(int tag) +{ + platlist_t *pl; + for (pl=activeplats; pl; pl=pl->next) // search the active plats + { + plat_t *plat = pl->plat; // for one in stasis with right tag + if (plat->tag == tag && plat->status == in_stasis) + { + if (plat->type==toggleUpDn) //jff 3/14/98 reactivate toggle type + plat->status = plat->oldstatus==up? down : up; + else + plat->status = plat->oldstatus; + plat->thinker.function = T_PlatRaise; + } + } +} + +// +// EV_StopPlat() +// +// Handler for "stop perpetual floor" linedef type +// +// Passed the linedef that stopped the plat +// Returns true if a plat was put in stasis +// +// jff 2/12/98 added int return value, fixed return +// +int EV_StopPlat(line_t* line) +{ + platlist_t *pl; + for (pl=activeplats; pl; pl=pl->next) // search the active plats + { + plat_t *plat = pl->plat; // for one with the tag not in stasis + if (plat->status != in_stasis && plat->tag == line->tag) + { + plat->oldstatus = plat->status; // put it in stasis + plat->status = in_stasis; + plat->thinker.function = NULL; + } + } + return 1; +} + +// +// P_AddActivePlat() +// +// Add a plat to the head of the active plat list +// +// Passed a pointer to the plat to add +// Returns nothing +// +void P_AddActivePlat(plat_t* plat) +{ + platlist_t *list = malloc(sizeof *list); + list->plat = plat; + plat->list = list; + if ((list->next = activeplats)) + list->next->prev = &list->next; + list->prev = &activeplats; + activeplats = list; +} + +// +// P_RemoveActivePlat() +// +// Remove a plat from the active plat list +// +// Passed a pointer to the plat to remove +// Returns nothing +// +void P_RemoveActivePlat(plat_t* plat) +{ + platlist_t *list = plat->list; + plat->sector->floordata = NULL; //jff 2/23/98 multiple thinkers + P_RemoveThinker(&plat->thinker); + if ((*list->prev = list->next)) + list->next->prev = list->prev; + free(list); +} + +// +// P_RemoveAllActivePlats() +// +// Remove all plats from the active plat list +// +// Passed nothing, returns nothing +// +void P_RemoveAllActivePlats(void) +{ + while (activeplats) + { + platlist_t *next = activeplats->next; + free(activeplats); + activeplats = next; + } +} diff --git a/src/p_pspr.c b/src/p_pspr.c new file mode 100644 index 00000000..d2ff8d2f --- /dev/null +++ b/src/p_pspr.c @@ -0,0 +1,857 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Weapon sprite animation, weapon objects. + * Action functions for weapons. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_map.h" +#include "p_inter.h" +#include "p_pspr.h" +#include "p_enemy.h" +#include "m_random.h" +#include "s_sound.h" +#include "sounds.h" +#include "d_event.h" +#include "r_demo.h" + +#define LOWERSPEED (FRACUNIT*6) +#define RAISESPEED (FRACUNIT*6) +#define WEAPONBOTTOM (FRACUNIT*128) +#define WEAPONTOP (FRACUNIT*32) + +#define BFGCELLS bfgcells /* Ty 03/09/98 externalized in p_inter.c */ + +extern void P_Thrust(player_t *, angle_t, fixed_t); + +// The following array holds the recoil values // phares + +static const int recoil_values[] = { // phares + 10, // wp_fist + 10, // wp_pistol + 30, // wp_shotgun + 10, // wp_chaingun + 100,// wp_missile + 20, // wp_plasma + 100,// wp_bfg + 0, // wp_chainsaw + 80 // wp_supershotgun +}; + +// +// P_SetPsprite +// + +static void P_SetPsprite(player_t *player, int position, statenum_t stnum) +{ + pspdef_t *psp = &player->psprites[position]; + + do + { + state_t *state; + + if (!stnum) + { + // object removed itself + psp->state = NULL; + break; + } + + state = &states[stnum]; + psp->state = state; + psp->tics = state->tics; // could be 0 + + if (state->misc1) + { + // coordinate set + psp->sx = state->misc1 << FRACBITS; + psp->sy = state->misc2 << FRACBITS; + } + + // Call action routine. + // Modified handling. + if (state->action) + { + state->action(player, psp); + if (!psp->state) + break; + } + stnum = psp->state->nextstate; + } + while (!psp->tics); // an initial state of 0 could cycle through +} + +// +// P_BringUpWeapon +// Starts bringing the pending weapon up +// from the bottom of the screen. +// Uses player +// + +static void P_BringUpWeapon(player_t *player) +{ + statenum_t newstate; + + if (player->pendingweapon == wp_nochange) + player->pendingweapon = player->readyweapon; + + if (player->pendingweapon == wp_chainsaw) + S_StartSound (player->mo, sfx_sawup); + + newstate = weaponinfo[player->pendingweapon].upstate; + + player->pendingweapon = wp_nochange; + // killough 12/98: prevent pistol from starting visibly at bottom of screen: + player->psprites[ps_weapon].sy = + mbf_features ? WEAPONBOTTOM+FRACUNIT*2 : WEAPONBOTTOM; + + P_SetPsprite(player, ps_weapon, newstate); +} + +// The first set is where the weapon preferences from // killough, +// default.cfg are stored. These values represent the keys used // phares +// in DOOM2 to bring up the weapon, i.e. 6 = plasma gun. These // | +// are NOT the wp_* constants. // V + +int weapon_preferences[2][NUMWEAPONS+1] = { + {6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // !compatibility preferences + {6, 9, 4, 3, 2, 8, 5, 7, 1, 0}, // compatibility preferences +}; + +// P_SwitchWeapon checks current ammo levels and gives you the +// most preferred weapon with ammo. It will not pick the currently +// raised weapon. When called from P_CheckAmmo this won't matter, +// because the raised weapon has no ammo anyway. When called from +// G_BuildTiccmd you want to toggle to a different weapon regardless. + +int P_SwitchWeapon(player_t *player) +{ + int *prefer = weapon_preferences[demo_compatibility!=0]; // killough 3/22/98 + int currentweapon = player->readyweapon; + int newweapon = currentweapon; + int i = NUMWEAPONS+1; // killough 5/2/98 + + // killough 2/8/98: follow preferences and fix BFG/SSG bugs + + do + switch (*prefer++) + { + case 1: + if (!player->powers[pw_strength]) // allow chainsaw override + break; + case 0: + newweapon = wp_fist; + break; + case 2: + if (player->ammo[am_clip]) + newweapon = wp_pistol; + break; + case 3: + if (player->weaponowned[wp_shotgun] && player->ammo[am_shell]) + newweapon = wp_shotgun; + break; + case 4: + if (player->weaponowned[wp_chaingun] && player->ammo[am_clip]) + newweapon = wp_chaingun; + break; + case 5: + if (player->weaponowned[wp_missile] && player->ammo[am_misl]) + newweapon = wp_missile; + break; + case 6: + if (player->weaponowned[wp_plasma] && player->ammo[am_cell] && + gamemode != shareware) + newweapon = wp_plasma; + break; + case 7: + if (player->weaponowned[wp_bfg] && gamemode != shareware && + player->ammo[am_cell] >= (demo_compatibility ? 41 : 40)) + newweapon = wp_bfg; + break; + case 8: + if (player->weaponowned[wp_chainsaw]) + newweapon = wp_chainsaw; + break; + case 9: + if (player->weaponowned[wp_supershotgun] && gamemode == commercial && + player->ammo[am_shell] >= (demo_compatibility ? 3 : 2)) + newweapon = wp_supershotgun; + break; + } + while (newweapon==currentweapon && --i); // killough 5/2/98 + return newweapon; +} + +// killough 5/2/98: whether consoleplayer prefers weapon w1 over weapon w2. +int P_WeaponPreferred(int w1, int w2) +{ + return + (weapon_preferences[0][0] != ++w2 && (weapon_preferences[0][0] == ++w1 || + (weapon_preferences[0][1] != w2 && (weapon_preferences[0][1] == w1 || + (weapon_preferences[0][2] != w2 && (weapon_preferences[0][2] == w1 || + (weapon_preferences[0][3] != w2 && (weapon_preferences[0][3] == w1 || + (weapon_preferences[0][4] != w2 && (weapon_preferences[0][4] == w1 || + (weapon_preferences[0][5] != w2 && (weapon_preferences[0][5] == w1 || + (weapon_preferences[0][6] != w2 && (weapon_preferences[0][6] == w1 || + (weapon_preferences[0][7] != w2 && (weapon_preferences[0][7] == w1 + )))))))))))))))); +} + +// P_GetAmmoLevel +// split out of P_CheckAmmo +// given a player and a weapon type, returns a number [0,100] +// describing how fully loaded the weapon is. +// special cased to return 0 for weapons that cannot be fired. +// also used by the status bar and HUD for number colouring. + +int P_GetAmmoLevel(player_t *player, weapontype_t weapon) +{ + ammotype_t ammo = weaponinfo[weapon].ammo; + int current, min, max, result; + + if (weapon == wp_bfg) // Minimal amount for one shot varies. + min = BFGCELLS; + else if (weapon == wp_supershotgun) // Double barrel. + min = 2; + else + min = 1; // Regular + + current = player->ammo[ammo]; + max = player->maxammo[ammo]; + + if (ammo == am_noammo // no ammunition => always full + || current >= max // weapon is full + || max == 0) // avoid div-by-zero + result = 100; + else if (current < min) // weapon is empty + result = 0; + else + { + // this division may still give 0 for sufficiently large + // values of max. make sure the weapon can still be fired. + result = (100 * current) / max; + if (result < 1) + result = 1; + } + + return result; +} + +// +// P_CheckAmmo +// Returns true if there is enough ammo to shoot. +// If not, selects the next weapon to use. +// (only in demo_compatibility mode -- killough 3/22/98) +// + +boolean P_CheckAmmo(player_t *player) +{ + if (P_GetAmmoLevel(player, player->readyweapon) > 0) // has enough ammo + return true; + + // Out of ammo, pick a weapon to change to. + // + // killough 3/22/98: for old demos we do the switch here and now; + // for Boom games we cannot do this, and have different player + // preferences across demos or networks, so we have to use the + // G_BuildTiccmd() interface instead of making the switch here. + + if (demo_compatibility) + { + player->pendingweapon = P_SwitchWeapon(player); // phares + // Now set appropriate weapon overlay. + P_SetPsprite(player,ps_weapon,weaponinfo[player->readyweapon].downstate); + } + + return false; +} + +// +// P_FireWeapon. +// + +static void P_FireWeapon(player_t *player) +{ + statenum_t newstate; + + if (!P_CheckAmmo(player)) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK1); + newstate = weaponinfo[player->readyweapon].atkstate; + P_SetPsprite(player, ps_weapon, newstate); + P_NoiseAlert(player->mo, player->mo); +} + +// +// P_DropWeapon +// Player died, so put the weapon away. +// + +void P_DropWeapon(player_t *player) +{ + P_SetPsprite(player, ps_weapon, weaponinfo[player->readyweapon].downstate); +} + +// +// A_WeaponReady +// The player can fire the weapon +// or change to another weapon at this time. +// Follows after getting weapon up, +// or after previous attack/fire sequence. +// + +void A_WeaponReady(player_t *player, pspdef_t *psp) +{ + // get out of attack state + if (player->mo->state == &states[S_PLAY_ATK1] + || player->mo->state == &states[S_PLAY_ATK2] ) + P_SetMobjState(player->mo, S_PLAY); + + if (player->readyweapon == wp_chainsaw && psp->state == &states[S_SAW]) + S_StartSound(player->mo, sfx_sawidl); + + // check for change + // if player is dead, put the weapon away + + if (player->pendingweapon != wp_nochange || !player->health) + { + // change weapon (pending weapon should already be validated) + statenum_t newstate = weaponinfo[player->readyweapon].downstate; + P_SetPsprite(player, ps_weapon, newstate); + return; + } + + // check for fire + // the missile launcher and bfg do not auto fire + + if (player->cmd.buttons & BT_ATTACK) + { + if (!player->attackdown || (player->readyweapon != wp_missile && + player->readyweapon != wp_bfg)) + { + player->attackdown = true; + P_FireWeapon(player); + return; + } + } + else + player->attackdown = false; + + // bob the weapon based on movement speed + { + int angle = (128*leveltime) & FINEMASK; + psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]); + angle &= FINEANGLES/2-1; + psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]); + } +} + +// +// A_ReFire +// The player can re-fire the weapon +// without lowering it entirely. +// + +void A_ReFire(player_t *player, pspdef_t *psp) +{ + // check for fire + // (if a weaponchange is pending, let it go through instead) + + if ( (player->cmd.buttons & BT_ATTACK) + && player->pendingweapon == wp_nochange && player->health) + { + player->refire++; + P_FireWeapon(player); + } + else + { + player->refire = 0; + P_CheckAmmo(player); + } +} + +void A_CheckReload(player_t *player, pspdef_t *psp) +{ + if (!P_CheckAmmo(player) && compatibility_level >= prboom_4_compatibility) { + /* cph 2002/08/08 - In old Doom, P_CheckAmmo would start the weapon lowering + * immediately. This was lost in Boom when the weapon switching logic was + * rewritten. But we must tell Doom that we don't need to complete the + * reload frames for the weapon here. G_BuildTiccmd will set ->pendingweapon + * for us later on. */ + P_SetPsprite(player,ps_weapon,weaponinfo[player->readyweapon].downstate); + } +} + +// +// A_Lower +// Lowers current weapon, +// and changes weapon at bottom. +// + +void A_Lower(player_t *player, pspdef_t *psp) +{ + psp->sy += LOWERSPEED; + + // Is already down. + if (psp->sy < WEAPONBOTTOM) + return; + + // Player is dead. + if (player->playerstate == PST_DEAD) + { + psp->sy = WEAPONBOTTOM; + return; // don't bring weapon back up + } + + // The old weapon has been lowered off the screen, + // so change the weapon and start raising it + + if (!player->health) + { // Player is dead, so keep the weapon off screen. + P_SetPsprite(player, ps_weapon, S_NULL); + return; + } + + player->readyweapon = player->pendingweapon; + + P_BringUpWeapon(player); +} + +// +// A_Raise +// + +void A_Raise(player_t *player, pspdef_t *psp) +{ + statenum_t newstate; + + psp->sy -= RAISESPEED; + + if (psp->sy > WEAPONTOP) + return; + + psp->sy = WEAPONTOP; + + // The weapon has been raised all the way, + // so change to the ready state. + + newstate = weaponinfo[player->readyweapon].readystate; + + P_SetPsprite(player, ps_weapon, newstate); +} + + +// Weapons now recoil, amount depending on the weapon. // phares +// // | +// The P_SetPsprite call in each of the weapon firing routines // V +// was moved here so the recoil could be synched with the +// muzzle flash, rather than the pressing of the trigger. +// The BFG delay caused this to be necessary. + +static void A_FireSomething(player_t* player,int adder) +{ + P_SetPsprite(player, ps_flash, + weaponinfo[player->readyweapon].flashstate+adder); + + // killough 3/27/98: prevent recoil in no-clipping mode + if (!(player->mo->flags & MF_NOCLIP)) + if (!compatibility && weapon_recoil) + P_Thrust(player, + ANG180+player->mo->angle, // ^ + 2048*recoil_values[player->readyweapon]); // | +} // phares + +// +// A_GunFlash +// + +void A_GunFlash(player_t *player, pspdef_t *psp) +{ + P_SetMobjState(player->mo, S_PLAY_ATK2); + + A_FireSomething(player,0); // phares +} + +// +// WEAPON ATTACKS +// + +// +// A_Punch +// + +void A_Punch(player_t *player, pspdef_t *psp) +{ + angle_t angle; + int t, slope, damage = (P_Random(pr_punch)%10+1)<<1; + + if (player->powers[pw_strength]) + damage *= 10; + + angle = player->mo->angle; + + // killough 5/5/98: remove dependence on order of evaluation: + t = P_Random(pr_punchangle); + angle += (t - P_Random(pr_punchangle))<<18; + + /* killough 8/2/98: make autoaiming prefer enemies */ + if (!mbf_features || + (slope = P_AimLineAttack(player->mo, angle, MELEERANGE, MF_FRIEND), + !linetarget)) + slope = P_AimLineAttack(player->mo, angle, MELEERANGE, 0); + + P_LineAttack(player->mo, angle, MELEERANGE, slope, damage); + + if (!linetarget) + return; + + S_StartSound(player->mo, sfx_punch); + + // turn to face target + + player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + R_SmoothPlaying_Reset(player); // e6y +} + +// +// A_Saw +// + +void A_Saw(player_t *player, pspdef_t *psp) +{ + int slope, damage = 2*(P_Random(pr_saw)%10+1); + angle_t angle = player->mo->angle; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_saw); + angle += (t - P_Random(pr_saw))<<18; + + /* Use meleerange + 1 so that the puff doesn't skip the flash + * killough 8/2/98: make autoaiming prefer enemies */ + if (!mbf_features || + (slope = P_AimLineAttack(player->mo, angle, MELEERANGE+1, MF_FRIEND), + !linetarget)) + slope = P_AimLineAttack(player->mo, angle, MELEERANGE+1, 0); + + P_LineAttack(player->mo, angle, MELEERANGE+1, slope, damage); + + if (!linetarget) + { + S_StartSound(player->mo, sfx_sawful); + return; + } + + S_StartSound(player->mo, sfx_sawhit); + + // turn to face target + angle = R_PointToAngle2(player->mo->x, player->mo->y, + linetarget->x, linetarget->y); + + if (angle - player->mo->angle > ANG180) { + if (angle - player->mo->angle < -ANG90/20) + player->mo->angle = angle + ANG90/21; + else + player->mo->angle -= ANG90/20; + } else { + if (angle - player->mo->angle > ANG90/20) + player->mo->angle = angle - ANG90/21; + else + player->mo->angle += ANG90/20; + } + + player->mo->flags |= MF_JUSTATTACKED; + R_SmoothPlaying_Reset(player); // e6y +} + +// +// A_FireMissile +// + +void A_FireMissile(player_t *player, pspdef_t *psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo]--; + P_SpawnPlayerMissile(player->mo, MT_ROCKET); +} + +// +// A_FireBFG +// + +void A_FireBFG(player_t *player, pspdef_t *psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo] -= BFGCELLS; + P_SpawnPlayerMissile(player->mo, MT_BFG); +} + +// +// A_FirePlasma +// + +void A_FirePlasma(player_t *player, pspdef_t *psp) +{ + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,P_Random(pr_plasma)&1); // phares + P_SpawnPlayerMissile(player->mo, MT_PLASMA); +} + +// +// P_BulletSlope +// Sets a slope so a near miss is at aproximately +// the height of the intended target +// + +static fixed_t bulletslope; + +static void P_BulletSlope(mobj_t *mo) +{ + angle_t an = mo->angle; // see which target is to be aimed at + + /* killough 8/2/98: make autoaiming prefer enemies */ + uint_64_t mask = mbf_features ? MF_FRIEND : 0; + + do + { + bulletslope = P_AimLineAttack(mo, an, 16*64*FRACUNIT, mask); + if (!linetarget) + bulletslope = P_AimLineAttack(mo, an += 1<<26, 16*64*FRACUNIT, mask); + if (!linetarget) + bulletslope = P_AimLineAttack(mo, an -= 2<<26, 16*64*FRACUNIT, mask); + } + while (mask && (mask=0, !linetarget)); /* killough 8/2/98 */ +} + +// +// P_GunShot +// + +static void P_GunShot(mobj_t *mo, boolean accurate) +{ + int damage = 5*(P_Random(pr_gunshot)%3+1); + angle_t angle = mo->angle; + + if (!accurate) + { // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_misfire); + angle += (t - P_Random(pr_misfire))<<18; + } + + P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage); +} + +// +// A_FirePistol +// + +void A_FirePistol(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_pistol); + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,0); // phares + P_BulletSlope(player->mo); + P_GunShot(player->mo, !player->refire); +} + +// +// A_FireShotgun +// + +void A_FireShotgun(player_t *player, pspdef_t *psp) +{ + int i; + + S_StartSound(player->mo, sfx_shotgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,0); // phares + + P_BulletSlope(player->mo); + + for (i=0; i<7; i++) + P_GunShot(player->mo, false); +} + +// +// A_FireShotgun2 +// + +void A_FireShotgun2(player_t *player, pspdef_t *psp) +{ + int i; + + S_StartSound(player->mo, sfx_dshtgn); + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo] -= 2; + + A_FireSomething(player,0); // phares + + P_BulletSlope(player->mo); + + for (i=0; i<20; i++) + { + int damage = 5*(P_Random(pr_shotgun)%3+1); + angle_t angle = player->mo->angle; + // killough 5/5/98: remove dependence on order of evaluation: + int t = P_Random(pr_shotgun); + angle += (t - P_Random(pr_shotgun))<<19; + t = P_Random(pr_shotgun); + P_LineAttack(player->mo, angle, MISSILERANGE, bulletslope + + ((t - P_Random(pr_shotgun))<<5), damage); + } +} + +// +// A_FireCGun +// + +void A_FireCGun(player_t *player, pspdef_t *psp) +{ + if (player->ammo[weaponinfo[player->readyweapon].ammo] || comp[comp_sound]) + S_StartSound(player->mo, sfx_pistol); + + if (!player->ammo[weaponinfo[player->readyweapon].ammo]) + return; + + P_SetMobjState(player->mo, S_PLAY_ATK2); + player->ammo[weaponinfo[player->readyweapon].ammo]--; + + A_FireSomething(player,psp->state - &states[S_CHAIN1]); // phares + + P_BulletSlope(player->mo); + + P_GunShot(player->mo, !player->refire); +} + +void A_Light0(player_t *player, pspdef_t *psp) +{ + player->extralight = 0; +} + +void A_Light1 (player_t *player, pspdef_t *psp) +{ + player->extralight = 1; +} + +void A_Light2 (player_t *player, pspdef_t *psp) +{ + player->extralight = 2; +} + +// +// A_BFGSpray +// Spawn a BFG explosion on every monster in view +// + +void A_BFGSpray(mobj_t *mo) +{ + int i; + + for (i=0 ; i<40 ; i++) // offset angles from its attack angle + { + int j, damage; + angle_t an = mo->angle - ANG90/2 + ANG90/40*i; + + // mo->target is the originator (player) of the missile + + // killough 8/2/98: make autoaiming prefer enemies + if (!mbf_features || + (P_AimLineAttack(mo->target, an, 16*64*FRACUNIT, MF_FRIEND), + !linetarget)) + P_AimLineAttack(mo->target, an, 16*64*FRACUNIT, 0); + + if (!linetarget) + continue; + + P_SpawnMobj(linetarget->x, linetarget->y, + linetarget->z + (linetarget->height>>2), MT_EXTRABFG); + + for (damage=j=0; j<15; j++) + damage += (P_Random(pr_bfg)&7) + 1; + + P_DamageMobj(linetarget, mo->target, mo->target, damage); + } +} + +// +// A_BFGsound +// + +void A_BFGsound(player_t *player, pspdef_t *psp) +{ + S_StartSound(player->mo, sfx_bfg); +} + +// +// P_SetupPsprites +// Called at start of level for each player. +// + +void P_SetupPsprites(player_t *player) +{ + int i; + + // remove all psprites + for (i=0; ipsprites[i].state = NULL; + + // spawn the gun + player->pendingweapon = player->readyweapon; + P_BringUpWeapon(player); +} + +// +// P_MovePsprites +// Called every tic by player thinking routine. +// + +void P_MovePsprites(player_t *player) +{ + pspdef_t *psp = player->psprites; + int i; + + // a null state means not active + // drop tic count and possibly change state + // a -1 tic count never changes + + for (i=0; istate && psp->tics != -1 && !--psp->tics) + P_SetPsprite(player, i, psp->state->nextstate); + + player->psprites[ps_flash].sx = player->psprites[ps_weapon].sx; + player->psprites[ps_flash].sy = player->psprites[ps_weapon].sy; +} diff --git a/src/p_pspr.h b/src/p_pspr.h new file mode 100644 index 00000000..1d9c6429 --- /dev/null +++ b/src/p_pspr.h @@ -0,0 +1,120 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Sprite animation. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_PSPR__ +#define __P_PSPR__ + +/* Basic data types. + * Needs fixed point, and BAM angles. */ + +#include "m_fixed.h" +#include "tables.h" + +/* Needs to include the precompiled sprite animation tables. + * + * Header generated by multigen utility. + * This includes all the data for thing animation, + * i.e. the Thing Atrributes table and the Frame Sequence table. + */ + +#include "info.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* + * Frame flags: + * handles maximum brightness (torches, muzzle flare, light sources) + */ + +#define FF_FULLBRIGHT 0x8000 /* flag in thing->frame */ +#define FF_FRAMEMASK 0x7fff + +/* + * Overlay psprites are scaled shapes + * drawn directly on the view screen, + * coordinates are given for a 320*200 view screen. + */ + +typedef enum +{ + ps_weapon, + ps_flash, + NUMPSPRITES +} psprnum_t; + +typedef struct +{ + state_t *state; /* a NULL state means not active */ + int tics; + fixed_t sx; + fixed_t sy; +} pspdef_t; + +extern int weapon_preferences[2][NUMWEAPONS+1]; /* killough 5/2/98 */ +int P_WeaponPreferred(int w1, int w2); + +struct player_s; +int P_SwitchWeapon(struct player_s *player); +int P_GetAmmoLevel(struct player_s *player, weapontype_t weapon); +boolean P_CheckAmmo(struct player_s *player); +void P_SetupPsprites(struct player_s *curplayer); +void P_MovePsprites(struct player_s *curplayer); +void P_DropWeapon(struct player_s *player); + +void A_Light0(); +void A_WeaponReady(); +void A_Lower(); +void A_Raise(); +void A_Punch(); +void A_ReFire(); +void A_FirePistol(); +void A_Light1(); +void A_FireShotgun(); +void A_Light2(); +void A_FireShotgun2(); +void A_CheckReload(); +void A_OpenShotgun2(); +void A_LoadShotgun2(); +void A_CloseShotgun2(); +void A_FireCGun(); +void A_GunFlash(); +void A_FireMissile(); +void A_Saw(); +void A_FirePlasma(); +void A_BFGsound(); +void A_FireBFG(); +void A_BFGSpray(); + +#endif diff --git a/src/p_saveg.c b/src/p_saveg.c new file mode 100644 index 00000000..cfa1ee44 --- /dev/null +++ b/src/p_saveg.c @@ -0,0 +1,1032 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Archiving: SaveGame I/O. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_saveg.h" +#include "m_random.h" +#include "am_map.h" +#include "p_enemy.h" +#include "lprintf.h" + +byte *save_p; + +// Pads save_p to a 4-byte boundary +// so that the load/save works on SGI&Gecko. +#define PADSAVEP() do { save_p += (4 - ((int) save_p & 3)) & 3; } while (0) +// +// P_ArchivePlayers +// +void P_ArchivePlayers (void) +{ + int i; + + CheckSaveGame(sizeof(player_t) * MAXPLAYERS); // killough + for (i=0 ; ipsprites[j].state) + dest->psprites[j].state = + (state_t *)(dest->psprites[j].state-states); + } +} + +// +// P_UnArchivePlayers +// +void P_UnArchivePlayers (void) +{ + int i; + + for (i=0 ; ifloorheight + sizeof sec->ceilingheight) + * numsectors + sizeof(short)*3*numlines + 4; + + for (i=0; itextureoffset + sizeof si->rowoffset; + if (lines[i].sidenum[1] != NO_INDEX) + size += + sizeof(short)*3 + sizeof si->textureoffset + sizeof si->rowoffset; + } + + CheckSaveGame(size); // killough + + PADSAVEP(); // killough 3/22/98 + + put = (short *)save_p; + + // do sectors + for (i=0, sec = sectors ; ifloorheight, sizeof sec->floorheight); + put = (void *)((char *) put + sizeof sec->floorheight); + memcpy(put, &sec->ceilingheight, sizeof sec->ceilingheight); + put = (void *)((char *) put + sizeof sec->ceilingheight); + + *put++ = sec->floorpic; + *put++ = sec->ceilingpic; + *put++ = sec->lightlevel; + *put++ = sec->special; // needed? yes -- transfer types + *put++ = sec->tag; // needed? need them -- killough + } + + // do lines + for (i=0, li = lines ; iflags; + *put++ = li->special; + *put++ = li->tag; + + for (j=0; j<2; j++) + if (li->sidenum[j] != NO_INDEX) + { + si = &sides[li->sidenum[j]]; + + // killough 10/98: save full sidedef offsets, + // preserving fractional scroll offsets + + memcpy(put, &si->textureoffset, sizeof si->textureoffset); + put = (void *)((char *) put + sizeof si->textureoffset); + memcpy(put, &si->rowoffset, sizeof si->rowoffset); + put = (void *)((char *) put + sizeof si->rowoffset); + + *put++ = si->toptexture; + *put++ = si->bottomtexture; + *put++ = si->midtexture; + } + } + save_p = (byte *) put; +} + + + +// +// P_UnArchiveWorld +// +void P_UnArchiveWorld (void) +{ + int i; + sector_t *sec; + line_t *li; + short *get; + + PADSAVEP(); // killough 3/22/98 + + get = (short *) save_p; + + // do sectors + for (i=0, sec = sectors ; ifloorheight, get, sizeof sec->floorheight); + get = (void *)((char *) get + sizeof sec->floorheight); + memcpy(&sec->ceilingheight, get, sizeof sec->ceilingheight); + get = (void *)((char *) get + sizeof sec->ceilingheight); + + sec->floorpic = *get++; + sec->ceilingpic = *get++; + sec->lightlevel = *get++; + sec->special = *get++; + sec->tag = *get++; + sec->ceilingdata = 0; //jff 2/22/98 now three thinker fields, not two + sec->floordata = 0; + sec->lightingdata = 0; + sec->soundtarget = 0; + } + + // do lines + for (i=0, li = lines ; iflags = *get++; + li->special = *get++; + li->tag = *get++; + for (j=0 ; j<2 ; j++) + if (li->sidenum[j] != NO_INDEX) + { + side_t *si = &sides[li->sidenum[j]]; + + // killough 10/98: load full sidedef offsets, including fractions + + memcpy(&si->textureoffset, get, sizeof si->textureoffset); + get = (void *)((char *) get + sizeof si->textureoffset); + memcpy(&si->rowoffset, get, sizeof si->rowoffset); + get = (void *)((char *) get + sizeof si->rowoffset); + + si->toptexture = *get++; + si->bottomtexture = *get++; + si->midtexture = *get++; + } + } + save_p = (byte *) get; +} + +// +// Thinkers +// + +typedef enum { + tc_end, + tc_mobj +} thinkerclass_t; + +// phares 9/13/98: Moved this code outside of P_ArchiveThinkers so the +// thinker indices could be used by the code that saves sector info. + +static int number_of_thinkers; + +void P_ThinkerToIndex(void) +{ + thinker_t *th; + + // killough 2/14/98: + // count the number of thinkers, and mark each one with its index, using + // the prev field as a placeholder, since it can be restored later. + + number_of_thinkers = 0; + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + th->prev = (thinker_t *) ++number_of_thinkers; +} + +// phares 9/13/98: Moved this code outside of P_ArchiveThinkers so the +// thinker indices could be used by the code that saves sector info. + +void P_IndexToThinker(void) +{ + // killough 2/14/98: restore prev pointers + thinker_t *th; + thinker_t *prev = &thinkercap; + + for (th = thinkercap.next ; th != &thinkercap ; prev=th, th=th->next) + th->prev = prev; +} + +// +// P_ArchiveThinkers +// +// 2/14/98 killough: substantially modified to fix savegame bugs + +void P_ArchiveThinkers (void) +{ + thinker_t *th; + + CheckSaveGame(sizeof brain); // killough 3/26/98: Save boss brain state + memcpy(save_p, &brain, sizeof brain); + save_p += sizeof brain; + + /* check that enough room is available in savegame buffer + * - killough 2/14/98 + * cph - use number_of_thinkers saved by P_ThinkerToIndex above + * size per object is sizeof(mobj_t) - 2*sizeof(void*) - 4*sizeof(fixed_t) plus + * padded type (4) plus 5*sizeof(void*), i.e. sizeof(mobj_t) + 4 + + * 3*sizeof(void*) + * cph - +1 for the tc_end + */ + CheckSaveGame(number_of_thinkers*(sizeof(mobj_t)-3*sizeof(fixed_t)+4+3*sizeof(void*)) +1); + + // save off the current thinkers + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (th->function == P_MobjThinker) + { + mobj_t *mobj; + + *save_p++ = tc_mobj; + PADSAVEP(); + mobj = (mobj_t *)save_p; + /* cph 2006/07/30 - + * The end of mobj_t changed from + * boolean invisible; + * mobj_t* lastenemy; + * mobj_t* above_monster; + * mobj_t* below_monster; + * void* touching_sectorlist; + * to + * mobj_t* lastenemy; + * void* touching_sectorlist; + * fixed_t PrevX, PrevY, PrevZ, padding; + * at prboom 2.4.4. There is code here to preserve the savegame format. + * + * touching_sectorlist is reconstructed anyway, so we now leave off the + * last 2 words of mobj_t, write 5 words of 0 and then write lastenemy + * into the second of these. + */ + memcpy (mobj, th, sizeof(*mobj) - 2*sizeof(void*)); + save_p += sizeof(*mobj) - 2*sizeof(void*) - 4*sizeof(fixed_t); + memset (save_p, 0, 5*sizeof(void*)); + mobj->state = (state_t *)(mobj->state - states); + + // killough 2/14/98: convert pointers into indices. + // Fixes many savegame problems, by properly saving + // target and tracer fields. Note: we store NULL if + // the thinker pointed to by these fields is not a + // mobj thinker. + + if (mobj->target) + mobj->target = mobj->target->thinker.function == + P_MobjThinker ? + (mobj_t *) mobj->target->thinker.prev : NULL; + + if (mobj->tracer) + mobj->tracer = mobj->tracer->thinker.function == + P_MobjThinker ? + (mobj_t *) mobj->tracer->thinker.prev : NULL; + + // killough 2/14/98: new field: save last known enemy. Prevents + // monsters from going to sleep after killing monsters and not + // seeing player anymore. + + if (((mobj_t*)th)->lastenemy && ((mobj_t*)th)->lastenemy->thinker.function == P_MobjThinker) { + memcpy (save_p + sizeof(void*), &(((mobj_t*)th)->lastenemy->thinker.prev), sizeof(void*)); + } + + // killough 2/14/98: end changes + + save_p += 5*sizeof(void*); + + if (mobj->player) + mobj->player = (player_t *)((mobj->player-players) + 1); + } + + // add a terminating marker + *save_p++ = tc_end; + + // killough 9/14/98: save soundtargets + { + int i; + CheckSaveGame(numsectors * sizeof(mobj_t *)); // killough 9/14/98 + for (i = 0; i < numsectors; i++) + { + mobj_t *target = sectors[i].soundtarget; + // Fix crash on reload when a soundtarget points to a removed corpse + // (prboom bug #1590350) + if (target && target->thinker.function == P_MobjThinker) + target = (mobj_t *) target->thinker.prev; + else + target = NULL; + memcpy(save_p, &target, sizeof target); + save_p += sizeof target; + } + } +} + +/* + * killough 11/98 + * + * Same as P_SetTarget() in p_tick.c, except that the target is nullified + * first, so that no old target's reference count is decreased (when loading + * savegames, old targets are indices, not really pointers to targets). + */ + +static void P_SetNewTarget(mobj_t **mop, mobj_t *targ) +{ + *mop = NULL; + P_SetTarget(mop, targ); +} + +// +// P_UnArchiveThinkers +// +// 2/14/98 killough: substantially modified to fix savegame bugs +// + +// savegame file stores ints in the corresponding * field; this function +// safely casts them back to int. +static int P_GetMobj(mobj_t* mi, size_t s) +{ + size_t i = (size_t)mi; + if (i >= s) I_Error("Corrupt savegame"); + return i; +} + +void P_UnArchiveThinkers (void) +{ + thinker_t *th; + mobj_t **mobj_p; // killough 2/14/98: Translation table + size_t size; // killough 2/14/98: size of or index into table + + totallive = 0; + // killough 3/26/98: Load boss brain state + memcpy(&brain, save_p, sizeof brain); + save_p += sizeof brain; + + // remove all the current thinkers + for (th = thinkercap.next; th != &thinkercap; ) + { + thinker_t *next = th->next; + if (th->function == P_MobjThinker) + { + P_RemoveMobj ((mobj_t *) th); + P_RemoveThinkerDelayed(th); // fix mobj leak + } + else + Z_Free (th); + th = next; + } + P_InitThinkers (); + + // killough 2/14/98: count number of thinkers by skipping through them + { + byte *sp = save_p; // save pointer and skip header + for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 + { // skip all entries, adding up count + PADSAVEP(); + /* cph 2006/07/30 - see comment below for change in layout of mobj_t */ + save_p += sizeof(mobj_t)+3*sizeof(void*)-4*sizeof(fixed_t); + } + + if (*--save_p != tc_end) + I_Error ("P_UnArchiveThinkers: Unknown tclass %i in savegame", *save_p); + + // first table entry special: 0 maps to NULL + *(mobj_p = malloc(size * sizeof *mobj_p)) = 0; // table of pointers + save_p = sp; // restore save pointer + } + + // read in saved thinkers + for (size = 1; *save_p++ == tc_mobj; size++) // killough 2/14/98 + { + mobj_t *mobj = Z_Malloc(sizeof(mobj_t), PU_LEVEL, NULL); + + // killough 2/14/98 -- insert pointers to thinkers into table, in order: + mobj_p[size] = mobj; + + PADSAVEP(); + /* cph 2006/07/30 - + * The end of mobj_t changed from + * boolean invisible; + * mobj_t* lastenemy; + * mobj_t* above_monster; + * mobj_t* below_monster; + * void* touching_sectorlist; + * to + * mobj_t* lastenemy; + * void* touching_sectorlist; + * fixed_t PrevX, PrevY, PrevZ; + * at prboom 2.4.4. There is code here to preserve the savegame format. + * + * touching_sectorlist is reconstructed anyway, so we now read in all + * but the last 5 words from the savegame (filling all but the last 2 + * fields of our current mobj_t. We then pull lastenemy from the 2nd of + * the 5 leftover words, and skip the others. + */ + memcpy (mobj, save_p, sizeof(mobj_t)-2*sizeof(void*)-4*sizeof(fixed_t)); + save_p += sizeof(mobj_t)-sizeof(void*)-4*sizeof(fixed_t); + memcpy (&(mobj->lastenemy), save_p, sizeof(void*)); + save_p += 4*sizeof(void*); + mobj->state = states + (int) mobj->state; + + if (mobj->player) + (mobj->player = &players[(int) mobj->player - 1]) -> mo = mobj; + + P_SetThingPosition (mobj); + mobj->info = &mobjinfo[mobj->type]; + + // killough 2/28/98: + // Fix for falling down into a wall after savegame loaded: + // mobj->floorz = mobj->subsector->sector->floorheight; + // mobj->ceilingz = mobj->subsector->sector->ceilingheight; + + mobj->thinker.function = P_MobjThinker; + P_AddThinker (&mobj->thinker); + + if (!((mobj->flags ^ MF_COUNTKILL) & (MF_FRIEND | MF_COUNTKILL | MF_CORPSE))) + totallive++; + } + + // killough 2/14/98: adjust target and tracer fields, plus + // lastenemy field, to correctly point to mobj thinkers. + // NULL entries automatically handled by first table entry. + // + // killough 11/98: use P_SetNewTarget() to set fields + + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + { + P_SetNewTarget(&((mobj_t *) th)->target, + mobj_p[P_GetMobj(((mobj_t *)th)->target,size)]); + + P_SetNewTarget(&((mobj_t *) th)->tracer, + mobj_p[P_GetMobj(((mobj_t *)th)->tracer,size)]); + + P_SetNewTarget(&((mobj_t *) th)->lastenemy, + mobj_p[P_GetMobj(((mobj_t *)th)->lastenemy,size)]); + } + + { // killough 9/14/98: restore soundtargets + int i; + for (i = 0; i < numsectors; i++) + { + mobj_t *target; + memcpy(&target, save_p, sizeof target); + save_p += sizeof target; + // Must verify soundtarget. See P_ArchiveThinkers. + P_SetNewTarget(§ors[i].soundtarget, mobj_p[P_GetMobj(target,size)]); + } + } + + free(mobj_p); // free translation table + + // killough 3/26/98: Spawn icon landings: + if (gamemode == commercial) + P_SpawnBrainTargets(); +} + +// +// P_ArchiveSpecials +// +enum { + tc_ceiling, + tc_door, + tc_floor, + tc_plat, + tc_flash, + tc_strobe, + tc_glow, + tc_elevator, //jff 2/22/98 new elevator type thinker + tc_scroll, // killough 3/7/98: new scroll effect thinker + tc_pusher, // phares 3/22/98: new push/pull effect thinker + tc_flicker, // killough 10/4/98 + tc_endspecials +} specials_e; + +// +// Things to handle: +// +// T_MoveCeiling, (ceiling_t: sector_t * swizzle), - active list +// T_VerticalDoor, (vldoor_t: sector_t * swizzle), +// T_MoveFloor, (floormove_t: sector_t * swizzle), +// T_LightFlash, (lightflash_t: sector_t * swizzle), +// T_StrobeFlash, (strobe_t: sector_t *), +// T_Glow, (glow_t: sector_t *), +// T_PlatRaise, (plat_t: sector_t *), - active list +// T_MoveElevator, (plat_t: sector_t *), - active list // jff 2/22/98 +// T_Scroll // killough 3/7/98 +// T_Pusher // phares 3/22/98 +// T_FireFlicker // killough 10/4/98 +// + +void P_ArchiveSpecials (void) +{ + thinker_t *th; + size_t size = 0; // killough + + // save off the current thinkers (memory size calculation -- killough) + + for (th = thinkercap.next ; th != &thinkercap ; th=th->next) + if (!th->function) + { + platlist_t *pl; + ceilinglist_t *cl; //jff 2/22/98 need this for ceilings too now + for (pl=activeplats; pl; pl=pl->next) + if (pl->plat == (plat_t *) th) // killough 2/14/98 + { + size += 4+sizeof(plat_t); + goto end; + } + for (cl=activeceilings; cl; cl=cl->next) // search for activeceiling + if (cl->ceiling == (ceiling_t *) th) //jff 2/22/98 + { + size += 4+sizeof(ceiling_t); + goto end; + } + end:; + } + else + size += + th->function==T_MoveCeiling ? 4+sizeof(ceiling_t) : + th->function==T_VerticalDoor ? 4+sizeof(vldoor_t) : + th->function==T_MoveFloor ? 4+sizeof(floormove_t): + th->function==T_PlatRaise ? 4+sizeof(plat_t) : + th->function==T_LightFlash ? 4+sizeof(lightflash_t): + th->function==T_StrobeFlash ? 4+sizeof(strobe_t) : + th->function==T_Glow ? 4+sizeof(glow_t) : + th->function==T_MoveElevator ? 4+sizeof(elevator_t): + th->function==T_Scroll ? 4+sizeof(scroll_t) : + th->function==T_Pusher ? 4+sizeof(pusher_t) : + th->function==T_FireFlicker? 4+sizeof(fireflicker_t) : + 0; + + CheckSaveGame(size + 1); // killough; cph: +1 for the tc_endspecials + + // save off the current thinkers + for (th=thinkercap.next; th!=&thinkercap; th=th->next) + { + if (!th->function) + { + platlist_t *pl; + ceilinglist_t *cl; //jff 2/22/98 add iter variable for ceilings + + // killough 2/8/98: fix plat original height bug. + // Since acv==NULL, this could be a plat in stasis. + // so check the active plats list, and save this + // plat (jff: or ceiling) even if it is in stasis. + + for (pl=activeplats; pl; pl=pl->next) + if (pl->plat == (plat_t *) th) // killough 2/14/98 + goto plat; + + for (cl=activeceilings; cl; cl=cl->next) + if (cl->ceiling == (ceiling_t *) th) //jff 2/22/98 + goto ceiling; + + continue; + } + + if (th->function == T_MoveCeiling) + { + ceiling_t *ceiling; + ceiling: // killough 2/14/98 + *save_p++ = tc_ceiling; + PADSAVEP(); + ceiling = (ceiling_t *)save_p; + memcpy (ceiling, th, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = (sector_t *)(ceiling->sector - sectors); + continue; + } + + if (th->function == T_VerticalDoor) + { + vldoor_t *door; + *save_p++ = tc_door; + PADSAVEP(); + door = (vldoor_t *) save_p; + memcpy (door, th, sizeof *door); + save_p += sizeof(*door); + door->sector = (sector_t *)(door->sector - sectors); + //jff 1/31/98 archive line remembered by door as well + door->line = (line_t *) (door->line ? door->line-lines : -1); + continue; + } + + if (th->function == T_MoveFloor) + { + floormove_t *floor; + *save_p++ = tc_floor; + PADSAVEP(); + floor = (floormove_t *)save_p; + memcpy (floor, th, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = (sector_t *)(floor->sector - sectors); + continue; + } + + if (th->function == T_PlatRaise) + { + plat_t *plat; + plat: // killough 2/14/98: added fix for original plat height above + *save_p++ = tc_plat; + PADSAVEP(); + plat = (plat_t *)save_p; + memcpy (plat, th, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = (sector_t *)(plat->sector - sectors); + continue; + } + + if (th->function == T_LightFlash) + { + lightflash_t *flash; + *save_p++ = tc_flash; + PADSAVEP(); + flash = (lightflash_t *)save_p; + memcpy (flash, th, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = (sector_t *)(flash->sector - sectors); + continue; + } + + if (th->function == T_StrobeFlash) + { + strobe_t *strobe; + *save_p++ = tc_strobe; + PADSAVEP(); + strobe = (strobe_t *)save_p; + memcpy (strobe, th, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = (sector_t *)(strobe->sector - sectors); + continue; + } + + if (th->function == T_Glow) + { + glow_t *glow; + *save_p++ = tc_glow; + PADSAVEP(); + glow = (glow_t *)save_p; + memcpy (glow, th, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = (sector_t *)(glow->sector - sectors); + continue; + } + + // killough 10/4/98: save flickers + if (th->function == T_FireFlicker) + { + fireflicker_t *flicker; + *save_p++ = tc_flicker; + PADSAVEP(); + flicker = (fireflicker_t *)save_p; + memcpy (flicker, th, sizeof(*flicker)); + save_p += sizeof(*flicker); + flicker->sector = (sector_t *)(flicker->sector - sectors); + continue; + } + + //jff 2/22/98 new case for elevators + if (th->function == T_MoveElevator) + { + elevator_t *elevator; //jff 2/22/98 + *save_p++ = tc_elevator; + PADSAVEP(); + elevator = (elevator_t *)save_p; + memcpy (elevator, th, sizeof(*elevator)); + save_p += sizeof(*elevator); + elevator->sector = (sector_t *)(elevator->sector - sectors); + continue; + } + + // killough 3/7/98: Scroll effect thinkers + if (th->function == T_Scroll) + { + *save_p++ = tc_scroll; + memcpy (save_p, th, sizeof(scroll_t)); + save_p += sizeof(scroll_t); + continue; + } + + // phares 3/22/98: Push/Pull effect thinkers + + if (th->function == T_Pusher) + { + *save_p++ = tc_pusher; + memcpy (save_p, th, sizeof(pusher_t)); + save_p += sizeof(pusher_t); + continue; + } + } + + // add a terminating marker + *save_p++ = tc_endspecials; +} + + +// +// P_UnArchiveSpecials +// +void P_UnArchiveSpecials (void) +{ + byte tclass; + + // read in saved thinkers + while ((tclass = *save_p++) != tc_endspecials) // killough 2/14/98 + switch (tclass) + { + case tc_ceiling: + PADSAVEP(); + { + ceiling_t *ceiling = Z_Malloc (sizeof(*ceiling), PU_LEVEL, NULL); + memcpy (ceiling, save_p, sizeof(*ceiling)); + save_p += sizeof(*ceiling); + ceiling->sector = §ors[(int)ceiling->sector]; + ceiling->sector->ceilingdata = ceiling; //jff 2/22/98 + + if (ceiling->thinker.function) + ceiling->thinker.function = T_MoveCeiling; + + P_AddThinker (&ceiling->thinker); + P_AddActiveCeiling(ceiling); + break; + } + + case tc_door: + PADSAVEP(); + { + vldoor_t *door = Z_Malloc (sizeof(*door), PU_LEVEL, NULL); + memcpy (door, save_p, sizeof(*door)); + save_p += sizeof(*door); + door->sector = §ors[(int)door->sector]; + + //jff 1/31/98 unarchive line remembered by door as well + door->line = (int)door->line!=-1? &lines[(int)door->line] : NULL; + + door->sector->ceilingdata = door; //jff 2/22/98 + door->thinker.function = T_VerticalDoor; + P_AddThinker (&door->thinker); + break; + } + + case tc_floor: + PADSAVEP(); + { + floormove_t *floor = Z_Malloc (sizeof(*floor), PU_LEVEL, NULL); + memcpy (floor, save_p, sizeof(*floor)); + save_p += sizeof(*floor); + floor->sector = §ors[(int)floor->sector]; + floor->sector->floordata = floor; //jff 2/22/98 + floor->thinker.function = T_MoveFloor; + P_AddThinker (&floor->thinker); + break; + } + + case tc_plat: + PADSAVEP(); + { + plat_t *plat = Z_Malloc (sizeof(*plat), PU_LEVEL, NULL); + memcpy (plat, save_p, sizeof(*plat)); + save_p += sizeof(*plat); + plat->sector = §ors[(int)plat->sector]; + plat->sector->floordata = plat; //jff 2/22/98 + + if (plat->thinker.function) + plat->thinker.function = T_PlatRaise; + + P_AddThinker (&plat->thinker); + P_AddActivePlat(plat); + break; + } + + case tc_flash: + PADSAVEP(); + { + lightflash_t *flash = Z_Malloc (sizeof(*flash), PU_LEVEL, NULL); + memcpy (flash, save_p, sizeof(*flash)); + save_p += sizeof(*flash); + flash->sector = §ors[(int)flash->sector]; + flash->thinker.function = T_LightFlash; + P_AddThinker (&flash->thinker); + break; + } + + case tc_strobe: + PADSAVEP(); + { + strobe_t *strobe = Z_Malloc (sizeof(*strobe), PU_LEVEL, NULL); + memcpy (strobe, save_p, sizeof(*strobe)); + save_p += sizeof(*strobe); + strobe->sector = §ors[(int)strobe->sector]; + strobe->thinker.function = T_StrobeFlash; + P_AddThinker (&strobe->thinker); + break; + } + + case tc_glow: + PADSAVEP(); + { + glow_t *glow = Z_Malloc (sizeof(*glow), PU_LEVEL, NULL); + memcpy (glow, save_p, sizeof(*glow)); + save_p += sizeof(*glow); + glow->sector = §ors[(int)glow->sector]; + glow->thinker.function = T_Glow; + P_AddThinker (&glow->thinker); + break; + } + + case tc_flicker: // killough 10/4/98 + PADSAVEP(); + { + fireflicker_t *flicker = Z_Malloc (sizeof(*flicker), PU_LEVEL, NULL); + memcpy (flicker, save_p, sizeof(*flicker)); + save_p += sizeof(*flicker); + flicker->sector = §ors[(int)flicker->sector]; + flicker->thinker.function = T_FireFlicker; + P_AddThinker (&flicker->thinker); + break; + } + + //jff 2/22/98 new case for elevators + case tc_elevator: + PADSAVEP(); + { + elevator_t *elevator = Z_Malloc (sizeof(*elevator), PU_LEVEL, NULL); + memcpy (elevator, save_p, sizeof(*elevator)); + save_p += sizeof(*elevator); + elevator->sector = §ors[(int)elevator->sector]; + elevator->sector->floordata = elevator; //jff 2/22/98 + elevator->sector->ceilingdata = elevator; //jff 2/22/98 + elevator->thinker.function = T_MoveElevator; + P_AddThinker (&elevator->thinker); + break; + } + + case tc_scroll: // killough 3/7/98: scroll effect thinkers + { + scroll_t *scroll = Z_Malloc (sizeof(scroll_t), PU_LEVEL, NULL); + memcpy (scroll, save_p, sizeof(scroll_t)); + save_p += sizeof(scroll_t); + scroll->thinker.function = T_Scroll; + P_AddThinker(&scroll->thinker); + break; + } + + case tc_pusher: // phares 3/22/98: new Push/Pull effect thinkers + { + pusher_t *pusher = Z_Malloc (sizeof(pusher_t), PU_LEVEL, NULL); + memcpy (pusher, save_p, sizeof(pusher_t)); + save_p += sizeof(pusher_t); + pusher->thinker.function = T_Pusher; + pusher->source = P_GetPushThing(pusher->affectee); + P_AddThinker(&pusher->thinker); + break; + } + + default: + I_Error("P_UnarchiveSpecials: Unknown tclass %i in savegame", tclass); + } +} + +// killough 2/16/98: save/restore random number generator state information + +void P_ArchiveRNG(void) +{ + CheckSaveGame(sizeof rng); + memcpy(save_p, &rng, sizeof rng); + save_p += sizeof rng; +} + +void P_UnArchiveRNG(void) +{ + memcpy(&rng, save_p, sizeof rng); + save_p += sizeof rng; +} + +// killough 2/22/98: Save/restore automap state +// killough 2/22/98: Save/restore automap state +void P_ArchiveMap(void) +{ + int zero = 0, one = 1; + CheckSaveGame(2 * sizeof zero + sizeof markpointnum + + markpointnum * sizeof *markpoints + + sizeof automapmode + sizeof one); + + memcpy(save_p, &automapmode, sizeof automapmode); + save_p += sizeof automapmode; + memcpy(save_p, &one, sizeof one); // CPhipps - used to be viewactive, now + save_p += sizeof one; // that's worked out locally by D_Display + memcpy(save_p, &zero, sizeof zero); // CPhipps - used to be followplayer + save_p += sizeof zero; // that is now part of automapmode + memcpy(save_p, &zero, sizeof zero); // CPhipps - used to be automap_grid, ditto + save_p += sizeof zero; + memcpy(save_p, &markpointnum, sizeof markpointnum); + save_p += sizeof markpointnum; + + if (markpointnum) + { + memcpy(save_p, markpoints, sizeof *markpoints * markpointnum); + save_p += markpointnum * sizeof *markpoints; + } +} + +void P_UnArchiveMap(void) +{ + int unused; + memcpy(&automapmode, save_p, sizeof automapmode); + save_p += sizeof automapmode; + memcpy(&unused, save_p, sizeof unused); + save_p += sizeof unused; + memcpy(&unused, save_p, sizeof unused); + save_p += sizeof unused; + memcpy(&unused, save_p, sizeof unused); + save_p += sizeof unused; + + if (automapmode & am_active) + AM_Start(); + + memcpy(&markpointnum, save_p, sizeof markpointnum); + save_p += sizeof markpointnum; + + if (markpointnum) + { + while (markpointnum >= markpointnum_max) + markpoints = realloc(markpoints, sizeof *markpoints * + (markpointnum_max = markpointnum_max ? markpointnum_max*2 : 16)); + memcpy(markpoints, save_p, markpointnum * sizeof *markpoints); + save_p += markpointnum * sizeof *markpoints; + } +} + diff --git a/src/p_saveg.h b/src/p_saveg.h new file mode 100644 index 00000000..dd986cf2 --- /dev/null +++ b/src/p_saveg.h @@ -0,0 +1,66 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Savegame I/O, archiving, persistence. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_SAVEG__ +#define __P_SAVEG__ + +#ifdef __GNUG__ +#pragma interface +#endif + +/* Persistent storage/archiving. + * These are the load / save game routines. */ +void P_ArchivePlayers(void); +void P_UnArchivePlayers(void); +void P_ArchiveWorld(void); +void P_UnArchiveWorld(void); +void P_ArchiveThinkers(void); +void P_UnArchiveThinkers(void); +void P_ArchiveSpecials(void); +void P_UnArchiveSpecials(void); +void P_ThinkerToIndex(void); /* phares 9/13/98: save soundtarget in savegame */ +void P_IndexToThinker(void); /* phares 9/13/98: save soundtarget in savegame */ + +/* 1/18/98 killough: add RNG info to savegame */ +void P_ArchiveRNG(void); +void P_UnArchiveRNG(void); + +/* 2/21/98 killough: add automap info to savegame */ +void P_ArchiveMap(void); +void P_UnArchiveMap(void); + +extern byte *save_p; +void CheckSaveGame(size_t,const char*, int); /* killough */ +#define CheckSaveGame(a) (CheckSaveGame)(a, __FILE__, __LINE__) + +#endif diff --git a/src/p_setup.c b/src/p_setup.c new file mode 100644 index 00000000..143cb7a0 --- /dev/null +++ b/src/p_setup.c @@ -0,0 +1,1878 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Do all the WAD I/O, get map description, + * set up initial state and misc. LUTs. + * + *-----------------------------------------------------------------------------*/ + +#include + +#include "doomstat.h" +#include "m_bbox.h" +#include "m_argv.h" +#include "g_game.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_things.h" +#include "p_maputl.h" +#include "p_map.h" +#include "p_setup.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_enemy.h" +#include "s_sound.h" +#include "lprintf.h" //jff 10/6/98 for debug outputs +#include "v_video.h" +#include "r_demo.h" +#include "r_fps.h" + +// +// MAP related Lookup tables. +// Store VERTEXES, LINEDEFS, SIDEDEFS, etc. +// + +int numvertexes; +vertex_t *vertexes; + +int numsegs; +seg_t *segs; + +int numsectors; +sector_t *sectors; + +int numsubsectors; +subsector_t *subsectors; + +int numnodes; +node_t *nodes; + +int numlines; +line_t *lines; + +int numsides; +side_t *sides; + + +//////////////////////////////////////////////////////////////////////////////////////////// +// figgi 08/21/00 -- constants and globals for glBsp support + +#define GL_VERT_OFFSET 4 + +int firstglvertex = 0; +boolean forceOldBsp = false; + +int nodes_glbsp; // version of GLBSP nodes detected, or 0 +int nodes_zdbsp; // version of ZDBSP nodes detected, or 0 + +// figgi 08/21/00 -- glSegs +typedef struct +{ + unsigned short v1; // start vertex (16 bit) + unsigned short v2; // end vertex (16 bit) + unsigned short linedef; // linedef, or -1 for minisegs + short side; // side on linedef: 0 for right, 1 for left + unsigned short partner; // corresponding partner seg, or -1 on one-sided walls +} glseg_t; + +// fixed 32 bit gl_vert format v2.0+ (glBsp 1.91) +typedef struct +{ + fixed_t x,y; +} mapglvertex_t; + +enum +{ + ML_GL_LABEL=0, // A separator name, GL_ExMx or GL_MAPxx + ML_GL_VERTS, // Extra Vertices + ML_GL_SEGS, // Segs, from linedefs & minisegs + ML_GL_SSECT, // SubSectors, list of segs + ML_GL_NODES // GL BSP nodes +}; + +// +// P_GetNodesVersion +// + +// read identifier (a 4 byte string at the start of a lump) +// passed a lump number and a pointer to 4-byte buffer +// returns NULL if it can't be read, otherwise returns the buffer +static void *ReadIdentifier(int lumpnum, void *buffer) +{ + const void *lump; + + if (W_LumpLength(lumpnum) >= 4 + && (lump = W_CacheLumpNum(lumpnum))) + { + memmove(buffer, lump, 4); + W_UnlockLumpNum(lumpnum); + return buffer; + } + return NULL; +} + +// check GL lumps are present (cf. P_CheckLevel) +static boolean CheckForGLBSPLumps(int gl_lumpnum) +{ + static const char *names[] = { "GL_VERT", "GL_SEGS", "GL_SSECT", "GL_NODES" }; + int i, e; + + for (i = 0, e = ML_GL_VERTS; e <= ML_GL_NODES; i++, e++) + if (gl_lumpnum + e >= numlumps + || strncasecmp(lumpinfo[gl_lumpnum + e].name, names[i], 8)) + return false; + + return true; +} + +// check for existence of GL (glbsp) nodes and return their version +static int GetGLBSPNodesVersion(int gl_lumpnum) +{ + char vert_id[4], seg_id[4]; + + if (ReadIdentifier(gl_lumpnum + ML_GL_VERTS, vert_id)) + { + if (memcmp(vert_id, "gNd5", 4) == 0) + return 5; + else if (memcmp(vert_id, "gNd4", 4) == 0) + return 4; + else if (memcmp(vert_id, "gNd2", 4) == 0) + { + // version 2 or 3; for 3, the segs start with gNd3 + if (ReadIdentifier(gl_lumpnum + ML_GL_SEGS, seg_id)) + { + if (memcmp(seg_id, "gNd3", 4) == 0) + return 3; + else if (memcmp(seg_id, "gNd", 3) == 0) + return 0; // unrecognised identifier + } + return 2; // no segs identifier, so version 2 + } + else if (memcmp(vert_id, "gNd", 3) == 0) + return 0; // unrecognised identifier + } + // no vertexes identifier, so version 1 + return 1; +} + +// Examine ordinary map nodes, looking for ZDBSP extensions. Return: +// 0: unknown or original BSP +// 1/2: uncompressed/compressed extended (XNOD/ZNOD) +// 3/4: uncompressed/compressed GL (XGLN/ZGLN) +// 5/6: uncompressed/compressed GL2 (XGL2/ZGL2) +static int GetZDBSPNodesVersion(int lumpnum) +{ + char node_id[4], ssec_id[4]; + + if (ReadIdentifier(lumpnum + ML_NODES, node_id)) + { + if (memcmp(node_id, "XNOD", 4) == 0) + return 1; + else if (memcmp(node_id, "ZNOD", 4) == 0) + return 2; + } + if (ReadIdentifier(lumpnum + ML_SSECTORS, ssec_id)) + { + if (memcmp(ssec_id, "XGLN", 4) == 0) + return 3; + else if (memcmp(ssec_id, "ZGLN", 4) == 0) + return 4; + else if (memcmp(ssec_id, "XGL2", 4) == 0) + return 5; + else if (memcmp(ssec_id, "ZGL2", 4) == 0) + return 6; + } + return 0; +} + +// P_GetNodesVersion +// sets nodes_glbsp and nodes_zdbsp from Check{GL,ZD}BSPNodesVersion +// sets level_error on unknown/unsupported data +static void P_GetNodesVersion(int lumpnum, int gl_lumpnum) +{ + int version; + int valid_gl = 0, valid_bsp = 0; + + nodes_glbsp = nodes_zdbsp = 0; // reset from previous map + + // check for GL nodes, if available/compatible + if (gl_lumpnum > lumpnum // A map's GL nodes are unlikely to preceed it. + && !forceOldBsp // FIXME why isn't this a comp_option? + && compatibility_level >= prboom_2_compatibility) + { + if (!CheckForGLBSPLumps(gl_lumpnum)) // do all required lumps exist? + lprintf(LO_WARN, + "P_GetNodesVersion: missing or invalid GLBSP nodes\n"); + else + { + // All lumps exist - try to read them, and get the version. + switch (version = GetGLBSPNodesVersion(gl_lumpnum)) + { + case 1: case 2: + // supported versions of GL nodes are v1 and v2 (don't forget v1!) + nodes_glbsp = version; + valid_gl = 1; + break; + + case 3: case 4: case 5: + // unsupported version of GL nodes - try to fall back to BSP + lprintf(LO_WARN, + "P_GetNodesVersion: GLBSP v%d nodes not supported\n", + version); + break; + + default: + lprintf(LO_WARN, + "P_GetNodesVersion: unknown GLBSP nodes version\n"); + break; + } + } + } + + switch (version = GetZDBSPNodesVersion(lumpnum)) + { + case 2: + lprintf(LO_WARN, + "P_GetNodesVersion: ZDBSP ZNOD nodes not supported\n"); + break; + + case 3: case 4: case 5: case 6: + lprintf(LO_WARN, + "P_GetNodesVersion: ZDBSP %cGL%c nodes not supported\n", + ((version % 2 == 1) ? 'X' : 'Z'), ((version >= 5) ? '2' : 'N')); + break; + + case 0: + case 1: // XNOD + nodes_zdbsp = version; + valid_bsp = 1; + break; + } + + if (!valid_gl && !valid_bsp) + I_Error("P_GetNodesVersion: cannot find supported nodes"); +} + +//////////////////////////////////////////////////////////////////////////////////////////// + + +// BLOCKMAP +// Created from axis aligned bounding box +// of the map, a rectangular array of +// blocks of size ... +// Used to speed up collision detection +// by spatial subdivision in 2D. +// +// Blockmap size. + +int bmapwidth, bmapheight; // size in mapblocks + +// killough 3/1/98: remove blockmap limit internally: +long *blockmap; // was short -- killough + +// offsets in blockmap are from here +long *blockmaplump; // was short -- killough + +fixed_t bmaporgx, bmaporgy; // origin of block map + +mobj_t **blocklinks; // for thing chains + +// +// REJECT +// For fast sight rejection. +// Speeds up enemy AI by skipping detailed +// LineOf Sight calculation. +// Without the special effect, this could +// be used as a PVS lookup as well. +// + +static int rejectlump = -1;// cph - store reject lump num if cached +const byte *rejectmatrix; // cph - const* + +// Maintain single and multi player starting spots. + +// 1/11/98 killough: Remove limit on deathmatch starts +mapthing_t *deathmatchstarts; // killough +size_t num_deathmatchstarts; // killough + +mapthing_t *deathmatch_p; +mapthing_t playerstarts[MAXPLAYERS]; + +// +// P_LoadVertexes +// +// killough 5/3/98: reformatted, cleaned up +// +static void P_LoadVertexes (int lump) +{ + const mapvertex_t *data; // cph - const + int i; + + // Determine number of lumps: + // total lump length / vertex record length. + numvertexes = W_LumpLength(lump) / sizeof(mapvertex_t); + + // Allocate zone memory for buffer. + vertexes = Z_Malloc(numvertexes*sizeof(vertex_t),PU_LEVEL,0); + + // Load data into cache. + // cph 2006/07/29 - cast to mapvertex_t here, making the loop below much neater + data = (const mapvertex_t *)W_CacheLumpNum(lump); + + // Copy and convert vertex coordinates, + // internal representation as fixed. + for (i=0; i= 0) // check for glVertices + { + gldata = W_CacheLumpNum(gllump); + + if (nodes_glbsp == 2) // 32 bit GL_VERT format (16.16 fixed) + { + const mapglvertex_t* mgl; + + numvertexes += (W_LumpLength(gllump) - GL_VERT_OFFSET)/sizeof(mapglvertex_t); + vertexes = Z_Malloc (numvertexes*sizeof(vertex_t),PU_LEVEL,0); + mgl = (const mapglvertex_t *) (gldata + GL_VERT_OFFSET); + + for (i = firstglvertex; i < numvertexes; i++) + { + vertexes[i].x = mgl->x; + vertexes[i].y = mgl->y; + mgl++; + } + } + else + { + numvertexes += W_LumpLength(gllump)/sizeof(mapvertex_t); + vertexes = Z_Malloc (numvertexes*sizeof(vertex_t),PU_LEVEL,0); + ml = (const mapvertex_t *)gldata; + + for (i = firstglvertex; i < numvertexes; i++) + { + vertexes[i].x = SHORT(ml->x)<y)<x)<y)<x - v2->x) / (float)FRACUNIT; + b = (float)(v1->y - v2->y) / (float)FRACUNIT; + r = (int)(sqrt(a*a+b*b) * (float)FRACUNIT); + return r; +} + + + +// +// P_LoadSegs +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadSegs (int lump) +{ + int i; + const mapseg_t *data; // cph - const + + numsegs = W_LumpLength(lump) / sizeof(mapseg_t); + segs = Z_Calloc(numsegs,sizeof(seg_t),PU_LEVEL,0); + data = (const mapseg_t *)W_CacheLumpNum(lump); // cph - wad lump handling updated + + if ((!data) || (!numsegs)) + I_Error("P_LoadSegs: no segs in level"); + + for (i=0; iiSegID = i; // proff 11/05/2000: needed for OpenGL + + v1 = (unsigned short)SHORT(ml->v1); + v2 = (unsigned short)SHORT(ml->v2); + li->v1 = &vertexes[v1]; + li->v2 = &vertexes[v2]; + + li->miniseg = false; // figgi -- there are no minisegs in classic BSP nodes + li->length = GetDistance(li->v2->x - li->v1->x, li->v2->y - li->v1->y); + li->angle = (SHORT(ml->angle))<<16; + li->offset =(SHORT(ml->offset))<<16; + linedef = (unsigned short)SHORT(ml->linedef); + ldef = &lines[linedef]; + li->linedef = ldef; + side = SHORT(ml->side); + li->sidedef = &sides[ldef->sidenum[side]]; + + /* cph 2006/09/30 - our frontsector can be the second side of the + * linedef, so must check for NO_INDEX in case we are incorrectly + * referencing the back of a 1S line */ + if (ldef->sidenum[side] != NO_INDEX) + li->frontsector = sides[ldef->sidenum[side]].sector; + else { + li->frontsector = 0; + lprintf(LO_WARN, "P_LoadSegs: front of seg %i has no sidedef\n", i); + } + + if (ldef->flags & ML_TWOSIDED && ldef->sidenum[side^1]!=NO_INDEX) + li->backsector = sides[ldef->sidenum[side^1]].sector; + else + li->backsector = 0; + } + + W_UnlockLumpNum(lump); // cph - release the data +} + + + +/******************************************* + * Name : P_LoadGLSegs * + * created : 08/13/00 * + * modified : 09/18/00, adapted for PrBoom * + * author : figgi * + * what : support for gl nodes * + *******************************************/ +static void P_LoadGLSegs(int lump) +{ + int i; + const glseg_t *ml; + line_t *ldef; + + numsegs = W_LumpLength(lump) / sizeof(glseg_t); + segs = Z_Malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0); + memset(segs, 0, numsegs * sizeof(seg_t)); + ml = (const glseg_t*)W_CacheLumpNum(lump); + + if ((!ml) || (!numsegs)) + I_Error("P_LoadGLSegs: no glsegs in level"); + + for(i = 0; i < numsegs; i++) + { // check for gl-vertices + segs[i].v1 = &vertexes[checkGLVertex(SHORT(ml->v1))]; + segs[i].v2 = &vertexes[checkGLVertex(SHORT(ml->v2))]; + segs[i].iSegID = i; + + if(ml->linedef != (unsigned short)-1) // skip minisegs + { + ldef = &lines[ml->linedef]; + segs[i].linedef = ldef; + segs[i].miniseg = false; + segs[i].angle = R_PointToAngle2(segs[i].v1->x,segs[i].v1->y,segs[i].v2->x,segs[i].v2->y); + + segs[i].sidedef = &sides[ldef->sidenum[ml->side]]; + segs[i].length = GetDistance(segs[i].v2->x - segs[i].v1->x, segs[i].v2->y - segs[i].v1->y); + segs[i].frontsector = sides[ldef->sidenum[ml->side]].sector; + if (ldef->flags & ML_TWOSIDED) + segs[i].backsector = sides[ldef->sidenum[ml->side^1]].sector; + else + segs[i].backsector = 0; + + if (ml->side) + segs[i].offset = GetOffset(segs[i].v1, ldef->v2); + else + segs[i].offset = GetOffset(segs[i].v1, ldef->v1); + } + else + { + segs[i].miniseg = true; + segs[i].angle = 0; + segs[i].offset = 0; + segs[i].length = 0; + segs[i].linedef = NULL; + segs[i].sidedef = NULL; + segs[i].frontsector = NULL; + segs[i].backsector = NULL; + } + ml++; + } + W_UnlockLumpNum(lump); +} + +// +// P_LoadSubsectors +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadSubsectors (int lump) +{ + /* cph 2006/07/29 - make data a const mapsubsector_t *, so the loop below is simpler & gives no constness warnings */ + const mapsubsector_t *data; + int i; + + numsubsectors = W_LumpLength (lump) / sizeof(mapsubsector_t); + subsectors = Z_Calloc(numsubsectors,sizeof(subsector_t),PU_LEVEL,0); + data = (const mapsubsector_t *)W_CacheLumpNum(lump); + + if ((!data) || (!numsubsectors)) + I_Error("P_LoadSubsectors: no subsectors in level"); + + for (i=0; iiSectorID=i; // proff 04/05/2000: needed for OpenGL + ss->floorheight = SHORT(ms->floorheight)<ceilingheight = SHORT(ms->ceilingheight)<floorpic = R_FlatNumForName(ms->floorpic); + ss->ceilingpic = R_FlatNumForName(ms->ceilingpic); + ss->lightlevel = SHORT(ms->lightlevel); + ss->special = SHORT(ms->special); + ss->oldspecial = SHORT(ms->special); + ss->tag = SHORT(ms->tag); + ss->thinglist = NULL; + ss->touching_thinglist = NULL; // phares 3/14/98 + + ss->nextsec = -1; //jff 2/26/98 add fields to support locking out + ss->prevsec = -1; // stair retriggering until build completes + + // killough 3/7/98: + ss->floor_xoffs = 0; + ss->floor_yoffs = 0; // floor and ceiling flats offsets + ss->ceiling_xoffs = 0; + ss->ceiling_yoffs = 0; + ss->heightsec = -1; // sector used to get floor and ceiling height + ss->floorlightsec = -1; // sector used to get floor lighting + // killough 3/7/98: end changes + + // killough 4/11/98 sector used to get ceiling lighting: + ss->ceilinglightsec = -1; + + // killough 4/4/98: colormaps: + ss->bottommap = ss->midmap = ss->topmap = 0; + + // killough 10/98: sky textures coming from sidedefs: + ss->sky = 0; + } + + W_UnlockLumpNum(lump); // cph - release the data +} + + +// +// P_LoadNodes +// +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadNodes (int lump) +{ + const byte *data; // cph - const* + int i; + + numnodes = W_LumpLength (lump) / sizeof(mapnode_t); + nodes = Z_Malloc (numnodes*sizeof(node_t),PU_LEVEL,0); + data = W_CacheLumpNum (lump); // cph - wad lump handling updated + + if ((!data) || (!numnodes)) + { + // allow trivial maps + if (numsubsectors == 1) + lprintf(LO_INFO, + "P_LoadNodes: trivial map (no nodes, one subsector)\n"); + else + I_Error("P_LoadNodes: no nodes in level"); + } + + for (i=0; ix = SHORT(mn->x)<y = SHORT(mn->y)<dx = SHORT(mn->dx)<dy = SHORT(mn->dy)<children[j] = (unsigned short)SHORT(mn->children[j]); + + // account for children's promotion to 32 bits + if (no->children[j] == 0xffff) + no->children[j] = 0xffffffff; + else if (no->children[j] & 0x8000) // old NF_SUBSECTOR + no->children[j] = (no->children[j] &~ 0x8000) | NF_SUBSECTOR; + + for (k=0 ; k<4 ; k++) + no->bbox[j][k] = SHORT(mn->bbox[j][k])<x = LONG(*(const fixed_t *)data); data += 4; len -= 4; + v->y = LONG(*(const fixed_t *)data); data += 4; len -= 4; + } + + if (newvert != vertexes) + { + for (i = 0; i < numlines; i++) { + lines[i].v1 = newvert + (lines[i].v1 - vertexes); + lines[i].v2 = newvert + (lines[i].v2 - vertexes); + } + vertexes = newvert; + } + newvert = vertexes + numorgvert; + numvertexes = numorgvert + numnewvert; + + numsubsectors = LONG(*(const unsigned int *)data); data += 4; len -= 4; + subsectors = Z_Calloc(numsubsectors, sizeof(*subsectors), PU_LEVEL, NULL); + + for (i = 0; i < numsubsectors; i++) { + subsectors[i].firstline = first_seg; + subsectors[i].numlines = LONG(*(const unsigned int *)data); data += 4; len -= 4; + first_seg += subsectors[i].numlines; + } + + numsegs = LONG(*(const unsigned int *)data); data += 4; len -= 4; + segs = Z_Calloc(numsegs, sizeof(*segs), PU_LEVEL, NULL); + + for (i = 0; i < numsegs; i++) + { + unsigned int v1, v2; + unsigned short ld; + unsigned char side; + seg_t *seg; + line_t *line; + + v1 = LONG(*(const unsigned int *)data); data += 4; len -= 4; + v2 = LONG(*(const unsigned int *)data); data += 4; len -= 4; + ld = SHORT(*(const unsigned short *)data); data += 2; len -= 2; + side = *(const unsigned char *)data; data += 1; len -= 1; + + seg = segs + i; + line = lines + ld; + + seg->v1 = vertexes + v1; + seg->v2 = vertexes + v2; + seg->miniseg = false; + seg->iSegID = i; // needed for OpenGL + seg->length = GetDistance(seg->v2->x - seg->v1->x, + seg->v2->y - seg->v1->y); + seg->angle = R_PointToAngle2(seg->v1->x, seg->v1->y, + seg->v2->x, seg->v2->y); + seg->linedef = line; + seg->sidedef = sides + line->sidenum[side]; + seg->frontsector = seg->sidedef->sector; + seg->backsector = ((line->flags & ML_TWOSIDED + && line->sidenum[side^1] != NO_INDEX) + ? sides[line->sidenum[side^1]].sector + : NULL); + seg->offset = (fixed_t)(FRACUNIT * + (side == 0 + // Right side - offset is distance from start of line to start of seg + ? GetDistance(line->v1->x - seg->v1->x, line->v1->y - seg->v1->y) + // Left side - offset is distance from end of line to start of seg + : GetDistance(line->v2->x - seg->v1->x, line->v2->y - seg->v1->y) + )); + } + + numnodes = LONG(*(const unsigned int *)data); data += 4; len -= 4; + nodes = Z_Calloc(numnodes, sizeof(*nodes), PU_LEVEL, NULL); + + for (i = 0; i < numnodes; i++) + { + node_t *node = nodes + i; + int j, k; + + node->x = SHORT(*(const short *)data)<y = SHORT(*(const short *)data)<dx = SHORT(*(const short *)data)<dy = SHORT(*(const short *)data)<bbox[j][k] = + SHORT(*(const short *)data)<children[j] = LONG(*(const unsigned int *)data); data += 4; len -= 4; + } + } + + W_UnlockLumpNum(lump); +} + + +/* + * P_LoadThings + * + * killough 5/3/98: reformatted, cleaned up + * cph 2001/07/07 - don't write into the lump cache, especially non-idepotent + * changes like byte order reversals. Take a copy to edit. + */ + +static void P_LoadThings (int lump) +{ + int i, numthings = W_LumpLength (lump) / sizeof(mapthing_t); + const mapthing_t *data = W_CacheLumpNum (lump); + + if ((!data) || (!numthings)) + I_Error("P_LoadThings: no things in level"); + + for (i=0; iflags = (unsigned short)SHORT(mld->flags); + ld->special = SHORT(mld->special); + ld->tag = SHORT(mld->tag); + v1 = ld->v1 = &vertexes[(unsigned short)SHORT(mld->v1)]; + v2 = ld->v2 = &vertexes[(unsigned short)SHORT(mld->v2)]; + ld->dx = v2->x - v1->x; + ld->dy = v2->y - v1->y; + + ld->slopetype = !ld->dx ? ST_VERTICAL : !ld->dy ? ST_HORIZONTAL : + FixedDiv(ld->dy, ld->dx) > 0 ? ST_POSITIVE : ST_NEGATIVE; + + if (v1->x < v2->x) + { + ld->bbox[BOXLEFT] = v1->x; + ld->bbox[BOXRIGHT] = v2->x; + } + else + { + ld->bbox[BOXLEFT] = v2->x; + ld->bbox[BOXRIGHT] = v1->x; + } + if (v1->y < v2->y) + { + ld->bbox[BOXBOTTOM] = v1->y; + ld->bbox[BOXTOP] = v2->y; + } + else + { + ld->bbox[BOXBOTTOM] = v2->y; + ld->bbox[BOXTOP] = v1->y; + } + + /* calculate sound origin of line to be its midpoint */ + //e6y: fix sound origin for large levels + // no need for comp_sound test, these are only used when comp_sound = 0 + ld->soundorg.x = ld->bbox[BOXLEFT] / 2 + ld->bbox[BOXRIGHT] / 2; + ld->soundorg.y = ld->bbox[BOXTOP] / 2 + ld->bbox[BOXBOTTOM] / 2; + + ld->iLineID=i; // proff 04/05/2000: needed for OpenGL + ld->sidenum[0] = SHORT(mld->sidenum[0]); + ld->sidenum[1] = SHORT(mld->sidenum[1]); + + { + /* cph 2006/09/30 - fix sidedef errors right away. + * cph 2002/07/20 - these errors are fatal if not fixed, so apply them + * in compatibility mode - a desync is better than a crash! */ + int j; + + for (j=0; j < 2; j++) + { + if (ld->sidenum[j] != NO_INDEX && ld->sidenum[j] >= numsides) { + ld->sidenum[j] = NO_INDEX; + lprintf(LO_WARN, "P_LoadLineDefs: linedef %d" + " has out-of-range sidedef number\n", i); + } + } + + // killough 11/98: fix common wad errors (missing sidedefs): + + if (ld->sidenum[0] == NO_INDEX) { + ld->sidenum[0] = 0; // Substitute dummy sidedef for missing right side + // cph - print a warning about the bug + lprintf(LO_WARN, "P_LoadLineDefs: linedef %d" + " missing first sidedef\n", i); + } + + if ((ld->sidenum[1] == NO_INDEX) && (ld->flags & ML_TWOSIDED)) { + ld->flags &= ~ML_TWOSIDED; // Clear 2s flag for missing left side + // cph - print a warning about the bug + lprintf(LO_WARN, "P_LoadLineDefs: linedef %d" + " has two-sided flag set, but no second sidedef\n", i); + } + } + + // killough 4/4/98: support special sidedef interpretation below + if (ld->sidenum[0] != NO_INDEX && ld->special) + sides[*ld->sidenum].special = ld->special; + } + + W_UnlockLumpNum(lump); // cph - release the lump +} + +// killough 4/4/98: delay using sidedefs until they are loaded +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadLineDefs2(int lump) +{ + int i = numlines; + register line_t *ld = lines; + for (;i--;ld++) + { + ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be NO_INDEX here + ld->backsector = ld->sidenum[1]!=NO_INDEX ? sides[ld->sidenum[1]].sector : 0; + switch (ld->special) + { // killough 4/11/98: handle special types + int lump, j; + + case 260: // killough 4/11/98: translucent 2s textures + break; + } + } +} + +// +// P_LoadSideDefs +// +// killough 4/4/98: split into two functions + +static void P_LoadSideDefs (int lump) +{ + numsides = W_LumpLength(lump) / sizeof(mapsidedef_t); + sides = Z_Calloc(numsides,sizeof(side_t),PU_LEVEL,0); +} + +// killough 4/4/98: delay using texture names until +// after linedefs are loaded, to allow overloading. +// killough 5/3/98: reformatted, cleaned up + +static void P_LoadSideDefs2(int lump) +{ + const byte *data = W_CacheLumpNum(lump); // cph - const*, wad lump handling updated + int i; + + for (i=0; itextureoffset = SHORT(msd->textureoffset)<rowoffset = SHORT(msd->rowoffset)<sector); + if (sector_num >= numsectors) { + lprintf(LO_WARN,"P_LoadSideDefs2: sidedef %i has out-of-range sector num %u\n", i, sector_num); + sector_num = 0; + } + sd->sector = sec = §ors[sector_num]; + } + + // killough 4/4/98: allow sidedef texture names to be overloaded + // killough 4/11/98: refined to allow colormaps to work as wall + // textures if invalid as colormaps but valid as textures. + switch (sd->special) + { + case 242: // variable colormap via 242 linedef + sd->bottomtexture = + (sec->bottommap = R_ColormapNumForName(msd->bottomtexture)) < 0 ? + sec->bottommap = 0, R_TextureNumForName(msd->bottomtexture): 0 ; + sd->midtexture = + (sec->midmap = R_ColormapNumForName(msd->midtexture)) < 0 ? + sec->midmap = 0, R_TextureNumForName(msd->midtexture) : 0 ; + sd->toptexture = + (sec->topmap = R_ColormapNumForName(msd->toptexture)) < 0 ? + sec->topmap = 0, R_TextureNumForName(msd->toptexture) : 0 ; + break; + + case 260: // killough 4/11/98: apply translucency to 2s normal texture + break; + + default: // normal cases + sd->midtexture = R_SafeTextureNumForName(msd->midtexture, i); + sd->toptexture = R_SafeTextureNumForName(msd->toptexture, i); + sd->bottomtexture = R_SafeTextureNumForName(msd->bottomtexture, i); + break; + } + } + + W_UnlockLumpNum(lump); // cph - release the lump +} + +// +// jff 10/6/98 +// New code added to speed up calculation of internal blockmap +// Algorithm is order of nlines*(ncols+nrows) not nlines*ncols*nrows +// + +#define blkshift 7 /* places to shift rel position for cell num */ +#define blkmask ((1<0 + // jff 10/12/98 0 ok with + 1 in rows,cols + +typedef struct linelist_t // type used to list lines in each block +{ + long num; + struct linelist_t *next; +} linelist_t; + +// +// Subroutine to add a line number to a block list +// It simply returns if the line is already in the block +// + +static void AddBlockLine +( + linelist_t **lists, + int *count, + int *done, + int blockno, + long lineno +) +{ + linelist_t *l; + + if (done[blockno]) + return; + + l = malloc(sizeof(linelist_t)); + l->num = lineno; + l->next = lists[blockno]; + lists[blockno] = l; + count[blockno]++; + done[blockno] = 1; +} + +// +// Actually construct the blockmap lump from the level data +// +// This finds the intersection of each linedef with the column and +// row lines at the left and bottom of each blockmap cell. It then +// adds the line to all block lists touching the intersection. +// + +static void P_CreateBlockMap(void) +{ + int xorg,yorg; // blockmap origin (lower left) + int nrows,ncols; // blockmap dimensions + linelist_t **blocklists=NULL; // array of pointers to lists of lines + int *blockcount=NULL; // array of counters of line lists + int *blockdone=NULL; // array keeping track of blocks/line + int NBlocks; // number of cells = nrows*ncols + long linetotal=0; // total length of all blocklists + int i,j; + int map_minx=INT_MAX; // init for map limits search + int map_miny=INT_MAX; + int map_maxx=INT_MIN; + int map_maxy=INT_MIN; + + // scan for map limits, which the blockmap must enclose + + for (i=0;i map_maxx) + map_maxx = t; + if ((t=vertexes[i].y) < map_miny) + map_miny = t; + else if (t > map_maxy) + map_maxy = t; + } + map_minx >>= FRACBITS; // work in map coords, not fixed_t + map_maxx >>= FRACBITS; + map_miny >>= FRACBITS; + map_maxy >>= FRACBITS; + + // set up blockmap area to enclose level plus margin + + xorg = map_minx-blkmargin; + yorg = map_miny-blkmargin; + ncols = (map_maxx+blkmargin-xorg+1+blkmask)>>blkshift; //jff 10/12/98 + nrows = (map_maxy+blkmargin-yorg+1+blkmask)>>blkshift; //+1 needed for + NBlocks = ncols*nrows; //map exactly 1 cell + + // create the array of pointers on NBlocks to blocklists + // also create an array of linelist counts on NBlocks + // finally make an array in which we can mark blocks done per line + + // CPhipps - calloc's + blocklists = calloc(NBlocks,sizeof(linelist_t *)); + blockcount = calloc(NBlocks,sizeof(int)); + blockdone = malloc(NBlocks*sizeof(int)); + + // initialize each blocklist, and enter the trailing -1 in all blocklists + // note the linked list of lines grows backwards + + for (i=0;inum = -1; + blocklists[i]->next = NULL; + blockcount[i]++; + } + + // For each linedef in the wad, determine all blockmap blocks it touches, + // and add the linedef number to the blocklists for those blocks + + for (i=0;ix>>FRACBITS; // lines[i] map coords + int y1 = lines[i].v1->y>>FRACBITS; + int x2 = lines[i].v2->x>>FRACBITS; + int y2 = lines[i].v2->y>>FRACBITS; + int dx = x2-x1; + int dy = y2-y1; + int vert = !dx; // lines[i] slopetype + int horiz = !dy; + int spos = (dx^dy) > 0; + int sneg = (dx^dy) < 0; + int bx,by; // block cell coords + int minx = x1>x2? x2 : x1; // extremal lines[i] coords + int maxx = x1>x2? x1 : x2; + int miny = y1>y2? y2 : y1; + int maxy = y1>y2? y1 : y2; + + // no blocks done for this linedef yet + + memset(blockdone,0,NBlocks*sizeof(int)); + + // The line always belongs to the blocks containing its endpoints + + bx = (x1-xorg)>>blkshift; + by = (y1-yorg)>>blkshift; + AddBlockLine(blocklists,blockcount,blockdone,by*ncols+bx,i); + bx = (x2-xorg)>>blkshift; + by = (y2-yorg)>>blkshift; + AddBlockLine(blocklists,blockcount,blockdone,by*ncols+bx,i); + + + // For each column, see where the line along its left edge, which + // it contains, intersects the Linedef i. Add i to each corresponding + // blocklist. + + if (!vert) // don't interesect vertical lines with columns + { + for (j=0;j>blkshift; // block row number + int yp = (y-yorg)&blkmask; // y position within block + + if (yb<0 || yb>nrows-1) // outside blockmap, continue + continue; + + if (xmaxx) // line doesn't touch column + continue; + + // The cell that contains the intersection point is always added + + AddBlockLine(blocklists,blockcount,blockdone,ncols*yb+j,i); + + // if the intersection is at a corner it depends on the slope + // (and whether the line extends past the intersection) which + // blocks are hit + + if (yp==0) // intersection at a corner + { + if (sneg) // \ - blocks x,y-, x-,y + { + if (yb>0 && miny0 && minx0 && j>0 && minx0 && minx0 && minx>blkshift; // block column number + int xp = (x-xorg)&blkmask; // x position within block + + if (xb<0 || xb>ncols-1) // outside blockmap, continue + continue; + + if (ymaxy) // line doesn't touch row + continue; + + // The cell that contains the intersection point is always added + + AddBlockLine(blocklists,blockcount,blockdone,ncols*j+xb,i); + + // if the intersection is at a corner it depends on the slope + // (and whether the line extends past the intersection) which + // blocks are hit + + if (xp==0) // intersection at a corner + { + if (sneg) // \ - blocks x,y-, x-,y + { + if (j>0 && miny0 && minx0 && miny0 && j>0 && miny0 && minynext; + blockmaplump[offs++] = bl->num; + free(bl); + bl = tmp; + } + } + + // free all temporary storage + + free (blocklists); + free (blockcount); + free (blockdone); +} + +// jff 10/6/98 +// End new code added to speed up calculation of internal blockmap + +// +// P_LoadBlockMap +// +// killough 3/1/98: substantially modified to work +// towards removing blockmap limit (a wad limitation) +// +// killough 3/30/98: Rewritten to remove blockmap limit, +// though current algorithm is brute-force and unoptimal. +// + +static void P_LoadBlockMap (int lump) +{ + long count; + + if (M_CheckParm("-blockmap") || W_LumpLength(lump)<8 || (count = W_LumpLength(lump)/2) >= 0x10000) //e6y + P_CreateBlockMap(); + else + { + long i; + // cph - const*, wad lump handling updated + const short *wadblockmaplump = W_CacheLumpNum(lump); + blockmaplump = Z_Malloc(sizeof(*blockmaplump) * count, PU_LEVEL, 0); + + // killough 3/1/98: Expand wad blockmap into larger internal one, + // by treating all offsets except -1 as unsigned and zero-extending + // them. This potentially doubles the size of blockmaps allowed, + // because Doom originally considered the offsets as always signed. + + blockmaplump[0] = SHORT(wadblockmaplump[0]); + blockmaplump[1] = SHORT(wadblockmaplump[1]); + blockmaplump[2] = (long)(SHORT(wadblockmaplump[2])) & 0xffff; + blockmaplump[3] = (long)(SHORT(wadblockmaplump[3])) & 0xffff; + + for (i=4 ; i= required) + return; // nothing to do + + // allocate a new block and copy the reject table into it; zero the rest + // PU_LEVEL => will be freed on level exit + newreject = Z_Malloc(required, PU_LEVEL, NULL); + rejectmatrix = (const byte *)memmove(newreject, rejectmatrix, length); + memset(newreject + length, 0, required - length); + // unlock the original lump, it is no longer needed + W_UnlockLumpNum(rejectlump); + rejectlump = -1; + + if (demo_compatibility) + { + // merged in RejectOverrunAddInt(), and the 4 calls to it, here + unsigned int rejectpad[4] = { + 0, // size, will be filled in using totallines + 0, // part of the header of a doom.exe z_zone block + 50, // DOOM_CONST_PU_LEVEL + 0x1d4a11 // DOOM_CONST_ZONEID + }; + unsigned int i, pad = 0, *src = rejectpad; + byte *dest = newreject + length; + + rejectpad[0] = ((totallines*4+3)&~3)+24; // doom.exe zone header size + + // copy at most 16 bytes from rejectpad + // emulating a 32-bit, little-endian architecture (can't memmove) + for (i = 0; i < required - length && i < 16; i++) { // 16 hard-coded + if (!(i&3)) // get the next 4 bytes to copy when i=0,4,8,12 + pad = *src++; + *dest++ = pad & 0xff; // store lowest-significant byte + pad >>= 8; // rotate the next byte down + } + } + lprintf(LO_WARN, "P_LoadReject: REJECT too short (%u<%u) - padded\n", + length, required); +} + +// +// P_GroupLines +// Builds sector line lists and subsector sector numbers. +// Finds block bounding boxes for sectors. +// +// killough 5/3/98: reformatted, cleaned up +// cph 18/8/99: rewritten to avoid O(numlines * numsectors) section +// It makes things more complicated, but saves seconds on big levels +// figgi 09/18/00 -- adapted for gl-nodes + +// cph - convenient sub-function +static void P_AddLineToSector(line_t* li, sector_t* sector) +{ + fixed_t *bbox = (void*)sector->blockbox; + + sector->lines[sector->linecount++] = li; + M_AddToBox (bbox, li->v1->x, li->v1->y); + M_AddToBox (bbox, li->v2->x, li->v2->y); +} + +// modified to return totallines (needed by P_LoadReject) +static int P_GroupLines (void) +{ + register line_t *li; + register sector_t *sector; + int i,j, total = numlines; + + // figgi + for (i=0 ; isidedef) + { + subsectors[i].sector = seg->sidedef->sector; + break; + } + seg++; + } + if(subsectors[i].sector == NULL) + I_Error("P_GroupLines: Subsector a part of no sector!\n"); + } + + // count number of lines in each sector + for (i=0,li=lines; ifrontsector->linecount++; + if (li->backsector && li->backsector != li->frontsector) + { + li->backsector->linecount++; + total++; + } + } + + { // allocate line tables for each sector + line_t **linebuffer = Z_Malloc(total*sizeof(line_t *), PU_LEVEL, 0); + + // e6y: REJECT overrun emulation code + // moved to P_LoadReject + + for (i=0, sector = sectors; ilines = linebuffer; + linebuffer += sector->linecount; + sector->linecount = 0; + M_ClearBox(sector->blockbox); + } + } + + // Enter those lines + for (i=0,li=lines; ifrontsector); + if (li->backsector && li->backsector != li->frontsector) + P_AddLineToSector(li, li->backsector); + } + + for (i=0, sector = sectors; iblockbox; // cph - For convenience, so + // I can sue the old code unchanged + int block; + + // set the degenmobj_t to the middle of the bounding box + if (comp[comp_sound]) + { + sector->soundorg.x = (bbox[BOXRIGHT]+bbox[BOXLEFT])/2; + sector->soundorg.y = (bbox[BOXTOP]+bbox[BOXBOTTOM])/2; + } + else + { + //e6y: fix sound origin for large levels + sector->soundorg.x = bbox[BOXRIGHT]/2+bbox[BOXLEFT]/2; + sector->soundorg.y = bbox[BOXTOP]/2+bbox[BOXBOTTOM]/2; + } + + // adjust bounding box to map blocks + block = (bbox[BOXTOP]-bmaporgy+MAXRADIUS)>>MAPBLOCKSHIFT; + block = block >= bmapheight ? bmapheight-1 : block; + sector->blockbox[BOXTOP]=block; + + block = (bbox[BOXBOTTOM]-bmaporgy-MAXRADIUS)>>MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXBOTTOM]=block; + + block = (bbox[BOXRIGHT]-bmaporgx+MAXRADIUS)>>MAPBLOCKSHIFT; + block = block >= bmapwidth ? bmapwidth-1 : block; + sector->blockbox[BOXRIGHT]=block; + + block = (bbox[BOXLEFT]-bmaporgx-MAXRADIUS)>>MAPBLOCKSHIFT; + block = block < 0 ? 0 : block; + sector->blockbox[BOXLEFT]=block; + } + + return total; // this value is needed by the reject overrun emulation code +} + +// +// killough 10/98 +// +// Remove slime trails. +// +// Slime trails are inherent to Doom's coordinate system -- i.e. there is +// nothing that a node builder can do to prevent slime trails ALL of the time, +// because it's a product of the integer coodinate system, and just because +// two lines pass through exact integer coordinates, doesn't necessarily mean +// that they will intersect at integer coordinates. Thus we must allow for +// fractional coordinates if we are to be able to split segs with node lines, +// as a node builder must do when creating a BSP tree. +// +// A wad file does not allow fractional coordinates, so node builders are out +// of luck except that they can try to limit the number of splits (they might +// also be able to detect the degree of roundoff error and try to avoid splits +// with a high degree of roundoff error). But we can use fractional coordinates +// here, inside the engine. It's like the difference between square inches and +// square miles, in terms of granularity. +// +// For each vertex of every seg, check to see whether it's also a vertex of +// the linedef associated with the seg (i.e, it's an endpoint). If it's not +// an endpoint, and it wasn't already moved, move the vertex towards the +// linedef by projecting it using the law of cosines. Formula: +// +// 2 2 2 2 +// dx x0 + dy x1 + dx dy (y0 - y1) dy y0 + dx y1 + dx dy (x0 - x1) +// {---------------------------------, ---------------------------------} +// 2 2 2 2 +// dx + dy dx + dy +// +// (x0,y0) is the vertex being moved, and (x1,y1)-(x1+dx,y1+dy) is the +// reference linedef. +// +// Segs corresponding to orthogonal linedefs (exactly vertical or horizontal +// linedefs), which comprise at least half of all linedefs in most wads, don't +// need to be considered, because they almost never contribute to slime trails +// (because then any roundoff error is parallel to the linedef, which doesn't +// cause slime). Skipping simple orthogonal lines lets the code finish quicker. +// +// Please note: This section of code is not interchangable with TeamTNT's +// code which attempts to fix the same problem. +// +// Firelines (TM) is a Rezistered Trademark of MBF Productions +// + +static void P_RemoveSlimeTrails(void) // killough 10/98 +{ + byte *hit = calloc(1, numvertexes); // Hitlist for vertices + int i; + for (i=0; idx && l->dy) // We can ignore orthogonal lines + { + vertex_t *v = segs[i].v1; + do + if (!hit[v - vertexes]) // If we haven't processed vertex + { + hit[v - vertexes] = 1; // Mark this vertex as processed + if (v != l->v1 && v != l->v2) // Exclude endpoints of linedefs + { // Project the vertex back onto the parent linedef + int_64_t dx2 = (l->dx >> FRACBITS) * (l->dx >> FRACBITS); + int_64_t dy2 = (l->dy >> FRACBITS) * (l->dy >> FRACBITS); + int_64_t dxy = (l->dx >> FRACBITS) * (l->dy >> FRACBITS); + int_64_t s = dx2 + dy2; + int x0 = v->x, y0 = v->y, x1 = l->v1->x, y1 = l->v1->y; + v->x = (int)((dx2 * x0 + dy2 * x1 + dxy * (y0 - y1)) / s); + v->y = (int)((dy2 * y0 + dx2 * y1 + dxy * (x0 - x1)) / s); + } + } // Obsfucated C contest entry: :) + while ((v != segs[i].v2) && (v = segs[i].v2)); + } + } + free(hit); +} + +// +// P_SetupLevel +// +// killough 5/3/98: reformatted, cleaned up + +void P_SetupLevel(int episode, int map, int playermask, skill_t skill) +{ + int i; + char lumpname[9]; + int lumpnum; + + char gl_lumpname[9]; + int gl_lumpnum; + + R_StopAllInterpolations(); + + totallive = totalkills = totalitems = totalsecret = wminfo.maxfrags = 0; + wminfo.partime = 180; + + for (i=0; i 0) + P_LoadVertexes2 (lumpnum+ML_VERTEXES,gl_lumpnum+ML_GL_VERTS); + else + P_LoadVertexes (lumpnum+ML_VERTEXES); + P_LoadSectors (lumpnum+ML_SECTORS); + P_LoadSideDefs (lumpnum+ML_SIDEDEFS); + P_LoadLineDefs (lumpnum+ML_LINEDEFS); + P_LoadSideDefs2 (lumpnum+ML_SIDEDEFS); + P_LoadLineDefs2 (lumpnum+ML_LINEDEFS); + P_LoadBlockMap (lumpnum+ML_BLOCKMAP); + + if (nodes_glbsp > 0) + { + P_LoadSubsectors(gl_lumpnum + ML_GL_SSECT); + P_LoadNodes(gl_lumpnum + ML_GL_NODES); + P_LoadGLSegs(gl_lumpnum + ML_GL_SEGS); + } + else if (nodes_zdbsp == 1) + { + P_LoadXNOD(lumpnum + ML_NODES); + } + else + { + P_LoadSubsectors(lumpnum + ML_SSECTORS); + P_LoadNodes(lumpnum + ML_NODES); + P_LoadSegs(lumpnum + ML_SEGS); + } + + // reject loading and underflow padding separated out into new function + // P_GroupLines modified to return a number the underflow padding needs + P_LoadReject(lumpnum, P_GroupLines()); + + // e6y + // Correction of desync on dv04-423.lmp/dv.wad + // http://www.doomworld.com/vb/showthread.php?s=&postid=627257#post627257 + if (compatibility_level>=lxdoom_1_compatibility || M_CheckParm("-force_remove_slime_trails") > 0) + P_RemoveSlimeTrails(); // killough 10/98: remove slime trails from wad + + // Note: you don't need to clear player queue slots -- + // a much simpler fix is in g_game.c -- killough 10/98 + + bodyqueslot = 0; + + /* cph - reset all multiplayer starts */ + memset(playerstarts,0,sizeof(playerstarts)); + deathmatch_p = deathmatchstarts; + for (i = 0; i < MAXPLAYERS; i++) + players[i].mo = NULL; + + P_MapStart(); + + P_LoadThings(lumpnum+ML_THINGS); + + // if deathmatch, randomly spawn the active players + if (deathmatch) + { + for (i=0; idx ? x == node->x ? 2 : x <= node->x ? node->dy > 0 : node->dy < 0 : + !node->dy ? ( compatibility_level < prboom_4_compatibility ? x : y) == node->y ? 2 : y <= node->y ? node->dx < 0 : node->dx > 0 : + (right = ((y - node->y) >> FRACBITS) * (node->dx >> FRACBITS)) < + (left = ((x - node->x) >> FRACBITS) * (node->dy >> FRACBITS)) ? 0 : + right == left ? 2 : 1; +} + +// +// P_CrossSubsector +// Returns true +// if strace crosses the given subsector successfully. +// +// killough 4/19/98: made static and cleaned up + +static boolean P_CrossSubsector(int num) +{ + seg_t *seg = segs + subsectors[num].firstline; + int count; + fixed_t opentop = 0, openbottom = 0; + const sector_t *front = NULL, *back = NULL; + + for (count = subsectors[num].numlines; --count >= 0; seg++) { // check lines + line_t *line = seg->linedef; + divline_t divl; + + if(!line) // figgi -- skip minisegs + continue; + + // allready checked other side? + if (line->validcount == validcount) + continue; + + line->validcount = validcount; + + /* OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test + * cph - this is causing demo desyncs on original Doom demos. + * Who knows why. Exclude test for those. + */ + if (!demo_compatibility) + if (line->bbox[BOXLEFT ] > los.bbox[BOXRIGHT ] || + line->bbox[BOXRIGHT ] < los.bbox[BOXLEFT ] || + line->bbox[BOXBOTTOM] > los.bbox[BOXTOP ] || + line->bbox[BOXTOP] < los.bbox[BOXBOTTOM]) + continue; + + // cph - do what we can before forced to check intersection + if (line->flags & ML_TWOSIDED) { + + // no wall to block sight with? + if ((front = seg->frontsector)->floorheight == + (back = seg->backsector)->floorheight && + front->ceilingheight == back->ceilingheight) + continue; + + // possible occluder + // because of ceiling height differences + opentop = front->ceilingheight < back->ceilingheight ? + front->ceilingheight : back->ceilingheight ; + + // because of floor height differences + openbottom = front->floorheight > back->floorheight ? + front->floorheight : back->floorheight ; + + // cph - reject if does not intrude in the z-space of the possible LOS + if ((opentop >= los.maxz) && (openbottom <= los.minz)) + continue; + } + + { // Forget this line if it doesn't cross the line of sight + const vertex_t *v1,*v2; + + v1 = line->v1; + v2 = line->v2; + + if (P_DivlineSide(v1->x, v1->y, &los.strace) == + P_DivlineSide(v2->x, v2->y, &los.strace)) + continue; + + divl.dx = v2->x - (divl.x = v1->x); + divl.dy = v2->y - (divl.y = v1->y); + + // line isn't crossed? + if (P_DivlineSide(los.strace.x, los.strace.y, &divl) == + P_DivlineSide(los.t2x, los.t2y, &divl)) + continue; + } + + // cph - if bottom >= top or top < minz or bottom > maxz then it must be + // solid wrt this LOS + if (!(line->flags & ML_TWOSIDED) || (openbottom >= opentop) || + (opentop < los.minz) || (openbottom > los.maxz)) + return false; + + { // crosses a two sided line + /* cph 2006/07/15 - oops, we missed this in 2.4.0 & .1; + * use P_InterceptVector2 for those compat levels only. */ + fixed_t frac = (compatibility_level == prboom_5_compatibility || compatibility_level == prboom_6_compatibility) ? + P_InterceptVector2(&los.strace, &divl) : + P_InterceptVector(&los.strace, &divl); + + if (front->floorheight != back->floorheight) { + fixed_t slope = FixedDiv(openbottom - los.sightzstart , frac); + if (slope > los.bottomslope) + los.bottomslope = slope; + } + + if (front->ceilingheight != back->ceilingheight) + { + fixed_t slope = FixedDiv(opentop - los.sightzstart , frac); + if (slope < los.topslope) + los.topslope = slope; + } + + if (los.topslope <= los.bottomslope) + return false; // stop + } + } + // passed the subsector ok + return true; +} + +// +// P_CrossBSPNode +// Returns true +// if strace crosses the given node successfully. +// +// killough 4/20/98: rewritten to remove tail recursion, clean up, and optimize +// cph - Made to use R_PointOnSide instead of P_DivlineSide, since the latter +// could return 2 which was ambigous, and the former is +// better optimised; also removes two casts :-) + +static boolean P_CrossBSPNode_LxDoom(int bspnum) +{ + while (!(bspnum & NF_SUBSECTOR)) + { + register const node_t *bsp = nodes + bspnum; + int side,side2; + side = R_PointOnSide(los.strace.x, los.strace.y, bsp); + side2 = R_PointOnSide(los.t2x, los.t2y, bsp); + if (side == side2) + bspnum = bsp->children[side]; // doesn't touch the other side + else // the partition plane is crossed here + if (!P_CrossBSPNode_LxDoom(bsp->children[side])) + return 0; // cross the starting side + else + bspnum = bsp->children[side^1]; // cross the ending side + } + return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); +} + +static boolean P_CrossBSPNode_PrBoom(int bspnum) +{ + while (!(bspnum & NF_SUBSECTOR)) + { + register const node_t *bsp = nodes + bspnum; + int side,side2; + side = P_DivlineSide(los.strace.x,los.strace.y,(const divline_t *)bsp)&1; + side2= P_DivlineSide(los.t2x, los.t2y, (const divline_t *) bsp); + if (side == side2) + bspnum = bsp->children[side]; // doesn't touch the other side + else // the partition plane is crossed here + if (!P_CrossBSPNode_PrBoom(bsp->children[side])) + return 0; // cross the starting side + else + bspnum = bsp->children[side^1]; // cross the ending side + } + return P_CrossSubsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); +} + +/* proff - Moved the compatibility check outside the functions + * this gives a slight speedup + */ +static boolean P_CrossBSPNode(int bspnum) +{ + /* cph - LxDoom used some R_* funcs here */ + if (compatibility_level == lxdoom_1_compatibility) + return P_CrossBSPNode_LxDoom(bspnum); + else + return P_CrossBSPNode_PrBoom(bspnum); +} + +// +// P_CheckSight +// Returns true +// if a straight line between t1 and t2 is unobstructed. +// Uses REJECT. +// +// killough 4/20/98: cleaned up, made to use new LOS struct + +boolean P_CheckSight(mobj_t *t1, mobj_t *t2) +{ + const sector_t *s1 = t1->subsector->sector; + const sector_t *s2 = t2->subsector->sector; + int pnum = (s1-sectors)*numsectors + (s2-sectors); + + // First check for trivial rejection. + // Determine subsector entries in REJECT table. + // + // Check in REJECT table. + + if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected + return false; + + // killough 4/19/98: make fake floors and ceilings block monster view + + if ((s1->heightsec != -1 && + ((t1->z + t1->height <= sectors[s1->heightsec].floorheight && + t2->z >= sectors[s1->heightsec].floorheight) || + (t1->z >= sectors[s1->heightsec].ceilingheight && + t2->z + t1->height <= sectors[s1->heightsec].ceilingheight))) + || + (s2->heightsec != -1 && + ((t2->z + t2->height <= sectors[s2->heightsec].floorheight && + t1->z >= sectors[s2->heightsec].floorheight) || + (t2->z >= sectors[s2->heightsec].ceilingheight && + t1->z + t2->height <= sectors[s2->heightsec].ceilingheight)))) + return false; + + /* killough 11/98: shortcut for melee situations + * same subsector? obviously visible + * cph - compatibility optioned for demo sync, cf HR06-UV.LMP */ + if ((t1->subsector == t2->subsector) && + (compatibility_level >= mbf_compatibility)) + return true; + + // An unobstructed LOS is possible. + // Now look from eyes of t1 to any part of t2. + + validcount++; + + los.topslope = (los.bottomslope = t2->z - (los.sightzstart = + t1->z + t1->height - + (t1->height>>2))) + t2->height; + los.strace.dx = (los.t2x = t2->x) - (los.strace.x = t1->x); + los.strace.dy = (los.t2y = t2->y) - (los.strace.y = t1->y); + + if (t1->x > t2->x) + los.bbox[BOXRIGHT] = t1->x, los.bbox[BOXLEFT] = t2->x; + else + los.bbox[BOXRIGHT] = t2->x, los.bbox[BOXLEFT] = t1->x; + + if (t1->y > t2->y) + los.bbox[BOXTOP] = t1->y, los.bbox[BOXBOTTOM] = t2->y; + else + los.bbox[BOXTOP] = t2->y, los.bbox[BOXBOTTOM] = t1->y; + + /* cph - calculate min and max z of the potential line of sight + * For old demos, we disable this optimisation by setting them to + * the extremes */ + switch (compatibility_level) { + case lxdoom_1_compatibility: + if (los.sightzstart < t2->z) { + los.maxz = t2->z + t2->height; los.minz = los.sightzstart; + } else if (los.sightzstart > t2->z + t2->height) { + los.maxz = los.sightzstart; los.minz = t2->z; + } else { + los.maxz = t2->z + t2->height; los.minz = t2->z; + } + break; + default: + los.maxz = INT_MAX; los.minz = INT_MIN; + } + + // the head node is the last node output + return P_CrossBSPNode(numnodes-1); +} diff --git a/src/p_spec.c b/src/p_spec.c new file mode 100644 index 00000000..f4caa422 --- /dev/null +++ b/src/p_spec.c @@ -0,0 +1,3353 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * -Loads and initializes texture and flat animation sequences + * -Implements utility functions for all linedef/sector special handlers + * -Dispatches walkover and gun line triggers + * -Initializes and implements special sector types + * -Implements donut linedef triggers + * -Initializes and implements BOOM linedef triggers for + * Scrollers/Conveyors + * Friction + * Wind/Current + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_setup.h" +#include "m_random.h" +#include "d_englsh.h" +#include "m_argv.h" +#include "w_wad.h" +#include "r_main.h" +#include "p_maputl.h" +#include "p_map.h" +#include "g_game.h" +#include "p_inter.h" +#include "s_sound.h" +#include "sounds.h" +#include "m_bbox.h" // phares 3/20/98 +#include "d_deh.h" +#include "r_plane.h" +#include "lprintf.h" + +// +// Animating textures and planes +// There is another anim_t used in wi_stuff, unrelated. +// +typedef struct +{ + boolean istexture; + int picnum; + int basepic; + int numpics; + int speed; + +} anim_t; + +// +// source animation definition +// +// +#ifdef _MSC_VER // proff: This is the same as __attribute__ ((packed)) in GNUC +#pragma pack(push) +#pragma pack(1) +#endif //_MSC_VER + +#if defined(__MWERKS__) +#pragma options align=packed +#endif + +typedef struct +{ + signed char istexture; //jff 3/23/98 make char for comparison // cph - make signed + char endname[9]; // if false, it is a flat + char startname[9]; + int speed; +} PACKEDATTR animdef_t; //jff 3/23/98 pack to read from memory + +#if defined(__MWERKS__) +#pragma options align=reset +#endif + +#ifdef _MSC_VER +#pragma pack(pop) +#endif //_MSC_VER + +#define MAXANIMS 32 // no longer a strict limit -- killough + +static anim_t* lastanim; +static anim_t* anims; // new structure w/o limits -- killough +static size_t maxanims; + +// killough 3/7/98: Initialize generalized scrolling +static void P_SpawnScrollers(void); + +static void P_SpawnFriction(void); // phares 3/16/98 +static void P_SpawnPushers(void); // phares 3/20/98 + +// +// P_InitPicAnims +// +// Load the table of animation definitions, checking for existence of +// the start and end of each frame. If the start doesn't exist the sequence +// is skipped, if the last doesn't exist, BOOM exits. +// +// Wall/Flat animation sequences, defined by name of first and last frame, +// The full animation sequence is given using all lumps between the start +// and end entry, in the order found in the WAD file. +// +// This routine modified to read its data from a predefined lump or +// PWAD lump called ANIMATED rather than a static table in this module to +// allow wad designers to insert or modify animation sequences. +// +// Lump format is an array of byte packed animdef_t structures, terminated +// by a structure with istexture == -1. The lump can be generated from a +// text source file using SWANTBLS.EXE, distributed with the BOOM utils. +// The standard list of switches and animations is contained in the example +// source text file DEFSWANI.DAT also in the BOOM util distribution. +// +// +void P_InitPicAnims (void) +{ + int i; + const animdef_t *animdefs; //jff 3/23/98 pointer to animation lump + int lump = W_GetNumForName("ANIMATED"); // cph - new wad lump handling + // Init animation + + //jff 3/23/98 read from predefined or wad lump instead of table + animdefs = (const animdef_t *)W_CacheLumpNum(lump); + + lastanim = anims; + for (i=0 ; animdefs[i].istexture != -1 ; i++) + { + // 1/11/98 killough -- removed limit by array-doubling + if (lastanim >= anims + maxanims) + { + size_t newmax = maxanims ? maxanims*2 : MAXANIMS; + anims = realloc(anims, newmax*sizeof(*anims)); // killough + lastanim = anims + maxanims; + maxanims = newmax; + } + + if (animdefs[i].istexture) + { + // different episode ? + if (R_CheckTextureNumForName(animdefs[i].startname) == -1) + continue; + + lastanim->picnum = R_TextureNumForName (animdefs[i].endname); + lastanim->basepic = R_TextureNumForName (animdefs[i].startname); + } + else + { + if ((W_CheckNumForName)(animdefs[i].startname, ns_flats) == -1) // killough 4/17/98 + continue; + + lastanim->picnum = R_FlatNumForName (animdefs[i].endname); + lastanim->basepic = R_FlatNumForName (animdefs[i].startname); + } + + lastanim->istexture = animdefs[i].istexture; + lastanim->numpics = lastanim->picnum - lastanim->basepic + 1; + + if (lastanim->numpics < 2) + I_Error ("P_InitPicAnims: bad cycle from %s to %s", + animdefs[i].startname, + animdefs[i].endname); + + lastanim->speed = LONG(animdefs[i].speed); // killough 5/5/98: add LONG() + lastanim++; + } + W_UnlockLumpNum(lump); +} + +/////////////////////////////////////////////////////////////// +// +// Linedef and Sector Special Implementation Utility Functions +// +/////////////////////////////////////////////////////////////// + +// +// getSide() +// +// Will return a side_t* +// given the number of the current sector, +// the line number, and the side (0/1) that you want. +// +// Note: if side=1 is specified, it must exist or results undefined +// +side_t* getSide +( int currentSector, + int line, + int side ) +{ + return &sides[ (sectors[currentSector].lines[line])->sidenum[side] ]; +} + + +// +// getSector() +// +// Will return a sector_t* +// given the number of the current sector, +// the line number and the side (0/1) that you want. +// +// Note: if side=1 is specified, it must exist or results undefined +// +sector_t* getSector +( int currentSector, + int line, + int side ) +{ + return sides[ (sectors[currentSector].lines[line])->sidenum[side] ].sector; +} + + +// +// twoSided() +// +// Given the sector number and the line number, +// it will tell you whether the line is two-sided or not. +// +// modified to return actual two-sidedness rather than presence +// of 2S flag unless compatibility optioned +// +int twoSided +( int sector, + int line ) +{ + //jff 1/26/98 return what is actually needed, whether the line + //has two sidedefs, rather than whether the 2S flag is set + + return comp[comp_model] ? + (sectors[sector].lines[line])->flags & ML_TWOSIDED + : + (sectors[sector].lines[line])->sidenum[1] != NO_INDEX; +} + + +// +// getNextSector() +// +// Return sector_t * of sector next to current across line. +// +// Note: returns NULL if not two-sided line, or both sides refer to sector +// +sector_t* getNextSector +( line_t* line, + sector_t* sec ) +{ + //jff 1/26/98 check unneeded since line->backsector already + //returns NULL if the line is not two sided, and does so from + //the actual two-sidedness of the line, rather than its 2S flag + + if (comp[comp_model]) + { + if (!(line->flags & ML_TWOSIDED)) + return NULL; + } + + if (line->frontsector == sec) { + if (comp[comp_model] || line->backsector!=sec) + return line->backsector; //jff 5/3/98 don't retn sec unless compatibility + else // fixes an intra-sector line breaking functions + return NULL; // like floor->highest floor + } + return line->frontsector; +} + + +// +// P_FindLowestFloorSurrounding() +// +// Returns the fixed point value of the lowest floor height +// in the sector passed or its surrounding sectors. +// +fixed_t P_FindLowestFloorSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = sec->floorheight; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->floorheight < floor) + floor = other->floorheight; + } + return floor; +} + + +// +// P_FindHighestFloorSurrounding() +// +// Passed a sector, returns the fixed point value of the largest +// floor height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists -32000*FRACUINT is returned +// if compatibility then -500*FRACUNIT is the smallest return possible +// +fixed_t P_FindHighestFloorSurrounding(sector_t *sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t floor = -500*FRACUNIT; + + //jff 1/26/98 Fix initial value for floor to not act differently + //in sections of wad that are below -500 units + if (!comp[comp_model]) /* jff 3/12/98 avoid ovf */ + floor = -32000*FRACUNIT; // in height calculations + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->floorheight > floor) + floor = other->floorheight; + } + return floor; +} + + +// +// P_FindNextHighestFloor() +// +// Passed a sector and a floor height, returns the fixed point value +// of the smallest floor height in a surrounding sector larger than +// the floor height passed. If no such height exists the floorheight +// passed is returned. +// +// Rewritten by Lee Killough to avoid fixed array and to be faster +// +fixed_t P_FindNextHighestFloor(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight > currentheight) + { + int height = other->floorheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight < height && + other->floorheight > currentheight) + height = other->floorheight; + return height; + } + /* cph - my guess at doom v1.2 - 1.4beta compatibility here. + * If there are no higher neighbouring sectors, Heretic just returned + * heightlist[0] (local variable), i.e. noise off the stack. 0 is right for + * RETURN01 E1M2, so let's take that. */ + return (compatibility_level < doom_1666_compatibility ? 0 : currentheight); +} + + +// +// P_FindNextLowestFloor() +// +// Passed a sector and a floor height, returns the fixed point value +// of the largest floor height in a surrounding sector smaller than +// the floor height passed. If no such height exists the floorheight +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextLowestFloor(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight < currentheight) + { + int height = other->floorheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->floorheight > height && + other->floorheight < currentheight) + height = other->floorheight; + return height; + } + return currentheight; +} + + +// +// P_FindNextLowestCeiling() +// +// Passed a sector and a ceiling height, returns the fixed point value +// of the largest ceiling height in a surrounding sector smaller than +// the ceiling height passed. If no such height exists the ceiling height +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextLowestCeiling(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight < currentheight) + { + int height = other->ceilingheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight > height && + other->ceilingheight < currentheight) + height = other->ceilingheight; + return height; + } + return currentheight; +} + + +// +// P_FindNextHighestCeiling() +// +// Passed a sector and a ceiling height, returns the fixed point value +// of the smallest ceiling height in a surrounding sector larger than +// the ceiling height passed. If no such height exists the ceiling height +// passed is returned. +// +// jff 02/03/98 Twiddled Lee's P_FindNextHighestFloor to make this +// +fixed_t P_FindNextHighestCeiling(sector_t *sec, int currentheight) +{ + sector_t *other; + int i; + + for (i=0 ;i < sec->linecount ; i++) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight > currentheight) + { + int height = other->ceilingheight; + while (++i < sec->linecount) + if ((other = getNextSector(sec->lines[i],sec)) && + other->ceilingheight < height && + other->ceilingheight > currentheight) + height = other->ceilingheight; + return height; + } + return currentheight; +} + + +// +// P_FindLowestCeilingSurrounding() +// +// Passed a sector, returns the fixed point value of the smallest +// ceiling height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists 32000*FRACUINT is returned +// but if compatibility then INT_MAX is the return +// +fixed_t P_FindLowestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = INT_MAX; + + /* jff 3/12/98 avoid ovf in height calculations */ + if (!comp[comp_model]) height = 32000*FRACUNIT; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->ceilingheight < height) + height = other->ceilingheight; + } + return height; +} + + +// +// P_FindHighestCeilingSurrounding() +// +// Passed a sector, returns the fixed point value of the largest +// ceiling height in the surrounding sectors, not including that passed +// +// NOTE: if no surrounding sector exists -32000*FRACUINT is returned +// but if compatibility then 0 is the smallest return possible +// +fixed_t P_FindHighestCeilingSurrounding(sector_t* sec) +{ + int i; + line_t* check; + sector_t* other; + fixed_t height = 0; + + /* jff 1/26/98 Fix initial value for floor to not act differently + * in sections of wad that are below 0 units + * jff 3/12/98 avoid ovf in height calculations */ + if (!comp[comp_model]) height = -32000*FRACUNIT; + + for (i=0 ;i < sec->linecount ; i++) + { + check = sec->lines[i]; + other = getNextSector(check,sec); + + if (!other) + continue; + + if (other->ceilingheight > height) + height = other->ceilingheight; + } + return height; +} + + +// +// P_FindShortestTextureAround() +// +// Passed a sector number, returns the shortest lower texture on a +// linedef bounding the sector. +// +// Note: If no lower texture exists 32000*FRACUNIT is returned. +// but if compatibility then INT_MAX is returned +// +// jff 02/03/98 Add routine to find shortest lower texture +// +fixed_t P_FindShortestTextureAround(int secnum) +{ + int minsize = INT_MAX; + side_t* side; + int i; + sector_t *sec = §ors[secnum]; + + if (!comp[comp_model]) + minsize = 32000<linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum,i,0); + if (side->bottomtexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + side = getSide(secnum,i,1); + if (side->bottomtexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->bottomtexture] < minsize) + minsize = textureheight[side->bottomtexture]; + } + } + return minsize; +} + + +// +// P_FindShortestUpperAround() +// +// Passed a sector number, returns the shortest upper texture on a +// linedef bounding the sector. +// +// Note: If no upper texture exists 32000*FRACUNIT is returned. +// but if compatibility then INT_MAX is returned +// +// jff 03/20/98 Add routine to find shortest upper texture +// +fixed_t P_FindShortestUpperAround(int secnum) +{ + int minsize = INT_MAX; + side_t* side; + int i; + sector_t *sec = §ors[secnum]; + + if (!comp[comp_model]) + minsize = 32000<linecount; i++) + { + if (twoSided(secnum, i)) + { + side = getSide(secnum,i,0); + if (side->toptexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->toptexture] < minsize) + minsize = textureheight[side->toptexture]; + side = getSide(secnum,i,1); + if (side->toptexture > 0) //jff 8/14/98 texture 0 is a placeholder + if (textureheight[side->toptexture] < minsize) + minsize = textureheight[side->toptexture]; + } + } + return minsize; +} + + +// +// P_FindModelFloorSector() +// +// Passed a floor height and a sector number, return a pointer to a +// a sector with that floor height across the lowest numbered two sided +// line surrounding the sector. +// +// Note: If no sector at that height bounds the sector passed, return NULL +// +// jff 02/03/98 Add routine to find numeric model floor +// around a sector specified by sector number +// jff 3/14/98 change first parameter to plain height to allow call +// from routine not using floormove_t +// +sector_t *P_FindModelFloorSector(fixed_t floordestheight,int secnum) +{ + int i; + sector_t *sec=NULL; + int linecount; + + sec = §ors[secnum]; //jff 3/2/98 woops! better do this + //jff 5/23/98 don't disturb sec->linecount while searching + // but allow early exit in old demos + linecount = sec->linecount; + for (i = 0; i < (demo_compatibility && sec->linecountlinecount : linecount); i++) + { + if ( twoSided(secnum, i) ) + { + if (getSide(secnum,i,0)->sector-sectors == secnum) + sec = getSector(secnum,i,1); + else + sec = getSector(secnum,i,0); + + if (sec->floorheight == floordestheight) + return sec; + } + } + return NULL; +} + + +// +// P_FindModelCeilingSector() +// +// Passed a ceiling height and a sector number, return a pointer to a +// a sector with that ceiling height across the lowest numbered two sided +// line surrounding the sector. +// +// Note: If no sector at that height bounds the sector passed, return NULL +// +// jff 02/03/98 Add routine to find numeric model ceiling +// around a sector specified by sector number +// used only from generalized ceiling types +// jff 3/14/98 change first parameter to plain height to allow call +// from routine not using ceiling_t +// +sector_t *P_FindModelCeilingSector(fixed_t ceildestheight,int secnum) +{ + int i; + sector_t *sec=NULL; + int linecount; + + sec = §ors[secnum]; //jff 3/2/98 woops! better do this + //jff 5/23/98 don't disturb sec->linecount while searching + // but allow early exit in old demos + linecount = sec->linecount; + for (i = 0; i < (demo_compatibility && sec->linecountlinecount : linecount); i++) + { + if ( twoSided(secnum, i) ) + { + if (getSide(secnum,i,0)->sector-sectors == secnum) + sec = getSector(secnum,i,1); + else + sec = getSector(secnum,i,0); + + if (sec->ceilingheight == ceildestheight) + return sec; + } + } + return NULL; +} + +// +// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO +// + +// Find the next sector with the same tag as a linedef. +// Rewritten by Lee Killough to use chained hashing to improve speed + +int P_FindSectorFromLineTag(const line_t *line, int start) +{ + start = start >= 0 ? sectors[start].nexttag : + sectors[(unsigned) line->tag % (unsigned) numsectors].firsttag; + while (start >= 0 && sectors[start].tag != line->tag) + start = sectors[start].nexttag; + return start; +} + +// killough 4/16/98: Same thing, only for linedefs + +int P_FindLineFromLineTag(const line_t *line, int start) +{ + start = start >= 0 ? lines[start].nexttag : + lines[(unsigned) line->tag % (unsigned) numlines].firsttag; + while (start >= 0 && lines[start].tag != line->tag) + start = lines[start].nexttag; + return start; +} + +// Hash the sector tags across the sectors and linedefs. +static void P_InitTagLists(void) +{ + register int i; + + for (i=numsectors; --i>=0; ) // Initially make all slots empty. + sectors[i].firsttag = -1; + for (i=numsectors; --i>=0; ) // Proceed from last to first sector + { // so that lower sectors appear first + int j = (unsigned) sectors[i].tag % (unsigned) numsectors; // Hash func + sectors[i].nexttag = sectors[j].firsttag; // Prepend sector to chain + sectors[j].firsttag = i; + } + + // killough 4/17/98: same thing, only for linedefs + + for (i=numlines; --i>=0; ) // Initially make all slots empty. + lines[i].firsttag = -1; + for (i=numlines; --i>=0; ) // Proceed from last to first linedef + { // so that lower linedefs appear first + int j = (unsigned) lines[i].tag % (unsigned) numlines; // Hash func + lines[i].nexttag = lines[j].firsttag; // Prepend linedef to chain + lines[j].firsttag = i; + } +} + +// +// P_FindMinSurroundingLight() +// +// Passed a sector and a light level, returns the smallest light level +// in a surrounding sector less than that passed. If no smaller light +// level exists, the light level passed is returned. +// +int P_FindMinSurroundingLight +( sector_t* sector, + int max ) +{ + int i; + int min; + line_t* line; + sector_t* check; + + min = max; + for (i=0 ; i < sector->linecount ; i++) + { + line = sector->lines[i]; + check = getNextSector(line,sector); + + if (!check) + continue; + + if (check->lightlevel < min) + min = check->lightlevel; + } + return min; +} + + +// +// P_CanUnlockGenDoor() +// +// Passed a generalized locked door linedef and a player, returns whether +// the player has the keys necessary to unlock that door. +// +// Note: The linedef passed MUST be a generalized locked door type +// or results are undefined. +// +// jff 02/05/98 routine added to test for unlockability of +// generalized locked doors +// +boolean P_CanUnlockGenDoor +( line_t* line, + player_t* player) +{ + // does this line special distinguish between skulls and keys? + int skulliscard = (line->special & LockedNKeys)>>LockedNKeysShift; + + // determine for each case of lock type if player's keys are adequate + switch((line->special & LockedKey)>>LockedKeyShift) + { + case AnyKey: + if + ( + !player->cards[it_redcard] && + !player->cards[it_redskull] && + !player->cards[it_bluecard] && + !player->cards[it_blueskull] && + !player->cards[it_yellowcard] && + !player->cards[it_yellowskull] + ) + { + player->message = s_PD_ANY; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case RCard: + if + ( + !player->cards[it_redcard] && + (!skulliscard || !player->cards[it_redskull]) + ) + { + player->message = skulliscard? s_PD_REDK : s_PD_REDC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case BCard: + if + ( + !player->cards[it_bluecard] && + (!skulliscard || !player->cards[it_blueskull]) + ) + { + player->message = skulliscard? s_PD_BLUEK : s_PD_BLUEC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case YCard: + if + ( + !player->cards[it_yellowcard] && + (!skulliscard || !player->cards[it_yellowskull]) + ) + { + player->message = skulliscard? s_PD_YELLOWK : s_PD_YELLOWC; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case RSkull: + if + ( + !player->cards[it_redskull] && + (!skulliscard || !player->cards[it_redcard]) + ) + { + player->message = skulliscard? s_PD_REDK : s_PD_REDS; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case BSkull: + if + ( + !player->cards[it_blueskull] && + (!skulliscard || !player->cards[it_bluecard]) + ) + { + player->message = skulliscard? s_PD_BLUEK : s_PD_BLUES; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case YSkull: + if + ( + !player->cards[it_yellowskull] && + (!skulliscard || !player->cards[it_yellowcard]) + ) + { + player->message = skulliscard? s_PD_YELLOWK : s_PD_YELLOWS; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + case AllKeys: + if + ( + !skulliscard && + ( + !player->cards[it_redcard] || + !player->cards[it_redskull] || + !player->cards[it_bluecard] || + !player->cards[it_blueskull] || + !player->cards[it_yellowcard] || + !player->cards[it_yellowskull] + ) + ) + { + player->message = s_PD_ALL6; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + if + ( + skulliscard && + ( + (!player->cards[it_redcard] && + !player->cards[it_redskull]) || + (!player->cards[it_bluecard] && + !player->cards[it_blueskull]) || + (!player->cards[it_yellowcard] && + !player->cards[it_yellowskull]) + ) + ) + { + player->message = s_PD_ALL3; // Ty 03/27/98 - externalized + S_StartSound(player->mo,sfx_oof); // killough 3/20/98 + return false; + } + break; + } + return true; +} + + +// +// P_SectorActive() +// +// Passed a linedef special class (floor, ceiling, lighting) and a sector +// returns whether the sector is already busy with a linedef special of the +// same class. If old demo compatibility true, all linedef special classes +// are the same. +// +// jff 2/23/98 added to prevent old demos from +// succeeding in starting multiple specials on one sector +// +boolean PUREFUNC P_SectorActive(special_e t, const sector_t *sec) +{ + if (demo_compatibility) // return whether any thinker is active + return sec->floordata != NULL || sec->ceilingdata != NULL || sec->lightingdata != NULL; + else + switch (t) // return whether thinker of same type is active + { + case floor_special: + return sec->floordata != NULL; + case ceiling_special: + return sec->ceilingdata != NULL; + case lighting_special: + return sec->lightingdata != NULL; + } + return true; // don't know which special, must be active, shouldn't be here +} + + +// +// P_CheckTag() +// +// Passed a line, returns true if the tag is non-zero or the line special +// allows no tag without harm. If compatibility, all linedef specials are +// allowed to have zero tag. +// +// Note: Only line specials activated by walkover, pushing, or shooting are +// checked by this routine. +// +// jff 2/27/98 Added to check for zero tag allowed for regular special types +// +int P_CheckTag(line_t *line) +{ + /* tag not zero, allowed, or + * killough 11/98: compatibility option */ + if (comp[comp_zerotags] || line->tag) + return 1; + + switch(line->special) + { + case 1: // Manual door specials + case 26: + case 27: + case 28: + case 31: + case 32: + case 33: + case 34: + case 117: + case 118: + + case 139: // Lighting specials + case 170: + case 79: + case 35: + case 138: + case 171: + case 81: + case 13: + case 192: + case 169: + case 80: + case 12: + case 194: + case 173: + case 157: + case 104: + case 193: + case 172: + case 156: + case 17: + + case 195: // Thing teleporters + case 174: + case 97: + case 39: + case 126: + case 125: + case 210: + case 209: + case 208: + case 207: + + case 11: // Exits + case 52: + case 197: + case 51: + case 124: + case 198: + + case 48: // Scrolling walls + case 85: + return 1; // zero tag allowed + + default: + break; + } + return 0; // zero tag not allowed +} + + +// +// P_IsSecret() +// +// Passed a sector, returns if the sector secret type is still active, i.e. +// secret type is set and the secret has not yet been obtained. +// +// jff 3/14/98 added to simplify checks for whether sector is secret +// in automap and other places +// +boolean PUREFUNC P_IsSecret(const sector_t *sec) +{ + return (sec->special==9 || (sec->special&SECRET_MASK)); +} + + +// +// P_WasSecret() +// +// Passed a sector, returns if the sector secret type is was active, i.e. +// secret type was set and the secret has been obtained already. +// +// jff 3/14/98 added to simplify checks for whether sector is secret +// in automap and other places +// +boolean PUREFUNC P_WasSecret(const sector_t *sec) +{ + return (sec->oldspecial==9 || (sec->oldspecial&SECRET_MASK)); +} + + +////////////////////////////////////////////////////////////////////////// +// +// Events +// +// Events are operations triggered by using, crossing, +// or shooting special lines, or by timed thinkers. +// +///////////////////////////////////////////////////////////////////////// + +// +// P_CrossSpecialLine - Walkover Trigger Dispatcher +// +// Called every time a thing origin is about +// to cross a line with a non 0 special, whether a walkover type or not. +// +// jff 02/12/98 all W1 lines were fixed to check the result from the EV_ +// function before clearing the special. This avoids losing the function +// of the line, should the sector already be active when the line is +// crossed. Change is qualified by demo_compatibility. +// +// CPhipps - take a line_t pointer instead of a line number, as in MBF +void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing) +{ + int ok; + + // Things that should never trigger lines + if (!thing->player) + { + // Things that should NOT trigger specials... + switch(thing->type) + { + case MT_ROCKET: + case MT_PLASMA: + case MT_BFG: + case MT_TROOPSHOT: + case MT_HEADSHOT: + case MT_BRUISERSHOT: + return; + break; + + default: break; + } + } + + //jff 02/04/98 add check here for generalized lindef types + if (!demo_compatibility) // generalized types not recognized if old demo + { + // pointer to line function is NULL by default, set non-null if + // line special is walkover generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player) + { + if (!(line->special & DoorMonster)) + return; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return; + } + if (!line->tag) //3/2/98 move outside the monster check + return; + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player) + return; // monsters disallowed from unlocking doors + if (((line->special&TriggerType)==WalkOnce) || ((line->special&TriggerType)==WalkMany)) + { //jff 4/1/98 check for being a walk type before reporting door type + if (!P_CanUnlockGenDoor(line,thing->player)) + return; + } + else + return; + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player) + if (!(line->special & LiftMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all walk generalized types require tag + return; + linefunc = EV_DoGenStairs; + } + + if (linefunc) // if it was a valid generalized type + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case WalkOnce: + if (linefunc(line)) + line->special = 0; // clear special if a walk once type + return; + case WalkMany: + linefunc(line); + return; + default: // if not a walk type, do nothing here + return; + } + } + + if (!thing->player) + { + ok = 0; + switch(line->special) + { + case 39: // teleport trigger + case 97: // teleport retrigger + case 125: // teleport monsteronly trigger + case 126: // teleport monsteronly retrigger + case 4: // raise door + case 10: // plat down-wait-up-stay trigger + case 88: // plat down-wait-up-stay retrigger + //jff 3/5/98 add ability of monsters etc. to use teleporters + case 208: //silent thing teleporters + case 207: + case 243: //silent line-line teleporter + case 244: //jff 3/6/98 make fit within DCK's 256 linedef types + case 262: //jff 4/14/98 add monster only + case 263: //jff 4/14/98 silent thing,line,line rev types + case 264: //jff 4/14/98 plus player/monster silent line + case 265: // reversed types + case 266: + case 267: + case 268: + case 269: + ok = 1; + break; + } + if (!ok) + return; + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return; + + // Dispatch on the line special value to the line's action routine + // If a once only function, and successful, clear the line special + + switch (line->special) + { + // Regular walk once triggers + + case 2: + // Open Door + if (EV_DoDoor(line,open) || demo_compatibility) + line->special = 0; + break; + + case 3: + // Close Door + if (EV_DoDoor(line,close) || demo_compatibility) + line->special = 0; + break; + + case 4: + // Raise Door + if (EV_DoDoor(line,normal) || demo_compatibility) + line->special = 0; + break; + + case 5: + // Raise Floor + if (EV_DoFloor(line,raiseFloor) || demo_compatibility) + line->special = 0; + break; + + case 6: + // Fast Ceiling Crush & Raise + if (EV_DoCeiling(line,fastCrushAndRaise) || demo_compatibility) + line->special = 0; + break; + + case 8: + // Build Stairs + if (EV_BuildStairs(line,build8) || demo_compatibility) + line->special = 0; + break; + + case 10: + // PlatDownWaitUp + if (EV_DoPlat(line,downWaitUpStay,0) || demo_compatibility) + line->special = 0; + break; + + case 12: + // Light Turn On - brightest near + if (EV_LightTurnOn(line,0) || demo_compatibility) + line->special = 0; + break; + + case 13: + // Light Turn On 255 + if (EV_LightTurnOn(line,255) || demo_compatibility) + line->special = 0; + break; + + case 16: + // Close Door 30 + if (EV_DoDoor(line,close30ThenOpen) || demo_compatibility) + line->special = 0; + break; + + case 17: + // Start Light Strobing + if (EV_StartLightStrobing(line) || demo_compatibility) + line->special = 0; + break; + + case 19: + // Lower Floor + if (EV_DoFloor(line,lowerFloor) || demo_compatibility) + line->special = 0; + break; + + case 22: + // Raise floor to nearest height and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0) || demo_compatibility) + line->special = 0; + break; + + case 25: + // Ceiling Crush and Raise + if (EV_DoCeiling(line,crushAndRaise) || demo_compatibility) + line->special = 0; + break; + + case 30: + // Raise floor to shortest texture height + // on either side of lines. + if (EV_DoFloor(line,raiseToTexture) || demo_compatibility) + line->special = 0; + break; + + case 35: + // Lights Very Dark + if (EV_LightTurnOn(line,35) || demo_compatibility) + line->special = 0; + break; + + case 36: + // Lower Floor (TURBO) + if (EV_DoFloor(line,turboLower) || demo_compatibility) + line->special = 0; + break; + + case 37: + // LowerAndChange + if (EV_DoFloor(line,lowerAndChange) || demo_compatibility) + line->special = 0; + break; + + case 38: + // Lower Floor To Lowest + if (EV_DoFloor(line, lowerFloorToLowest) || demo_compatibility) + line->special = 0; + break; + + case 39: + // TELEPORT! //jff 02/09/98 fix using up with wrong side crossing + if (EV_Teleport(line, side, thing) || demo_compatibility) + line->special = 0; + break; + + case 40: + // RaiseCeilingLowerFloor + if (demo_compatibility) + { + EV_DoCeiling( line, raiseToHighest ); + EV_DoFloor( line, lowerFloorToLowest ); //jff 02/12/98 doesn't work + line->special = 0; + } + else + if (EV_DoCeiling(line, raiseToHighest)) + line->special = 0; + break; + + case 44: + // Ceiling Crush + if (EV_DoCeiling(line, lowerAndCrush) || demo_compatibility) + line->special = 0; + break; + + case 52: + // EXIT! + // killough 10/98: prevent zombies from exiting levels + if (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie])) + G_ExitLevel (); + break; + + case 53: + // Perpetual Platform Raise + if (EV_DoPlat(line,perpetualRaise,0) || demo_compatibility) + line->special = 0; + break; + + case 54: + // Platform Stop + if (EV_StopPlat(line) || demo_compatibility) + line->special = 0; + break; + + case 56: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush) || demo_compatibility) + line->special = 0; + break; + + case 57: + // Ceiling Crush Stop + if (EV_CeilingCrushStop(line) || demo_compatibility) + line->special = 0; + break; + + case 58: + // Raise Floor 24 + if (EV_DoFloor(line,raiseFloor24) || demo_compatibility) + line->special = 0; + break; + + case 59: + // Raise Floor 24 And Change + if (EV_DoFloor(line,raiseFloor24AndChange) || demo_compatibility) + line->special = 0; + break; + + case 100: + // Build Stairs Turbo 16 + if (EV_BuildStairs(line,turbo16) || demo_compatibility) + line->special = 0; + break; + + case 104: + // Turn lights off in sector(tag) + if (EV_TurnTagLightsOff(line) || demo_compatibility) + line->special = 0; + break; + + case 108: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor(line,blazeRaise) || demo_compatibility) + line->special = 0; + break; + + case 109: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen) || demo_compatibility) + line->special = 0; + break; + + case 110: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose) || demo_compatibility) + line->special = 0; + break; + + case 119: + // Raise floor to nearest surr. floor + if (EV_DoFloor(line,raiseFloorToNearest) || demo_compatibility) + line->special = 0; + break; + + case 121: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0) || demo_compatibility) + line->special = 0; + break; + + case 124: + // Secret EXIT + // killough 10/98: prevent zombies from exiting levels + // CPhipps - change for lxdoom's compatibility handling + if (!(thing->player && thing->player->health <= 0 && !comp[comp_zombie])) + G_SecretExitLevel (); + break; + + case 125: + // TELEPORT MonsterONLY + if (!thing->player && + (EV_Teleport(line, side, thing) || demo_compatibility)) + line->special = 0; + break; + + case 130: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo) || demo_compatibility) + line->special = 0; + break; + + case 141: + // Silent Ceiling Crush & Raise + if (EV_DoCeiling(line,silentCrushAndRaise) || demo_compatibility) + line->special = 0; + break; + + // Regular walk many retriggerable + + case 72: + // Ceiling Crush + EV_DoCeiling( line, lowerAndCrush ); + break; + + case 73: + // Ceiling Crush and Raise + EV_DoCeiling(line,crushAndRaise); + break; + + case 74: + // Ceiling Crush Stop + EV_CeilingCrushStop(line); + break; + + case 75: + // Close Door + EV_DoDoor(line,close); + break; + + case 76: + // Close Door 30 + EV_DoDoor(line,close30ThenOpen); + break; + + case 77: + // Fast Ceiling Crush & Raise + EV_DoCeiling(line,fastCrushAndRaise); + break; + + case 79: + // Lights Very Dark + EV_LightTurnOn(line,35); + break; + + case 80: + // Light Turn On - brightest near + EV_LightTurnOn(line,0); + break; + + case 81: + // Light Turn On 255 + EV_LightTurnOn(line,255); + break; + + case 82: + // Lower Floor To Lowest + EV_DoFloor( line, lowerFloorToLowest ); + break; + + case 83: + // Lower Floor + EV_DoFloor(line,lowerFloor); + break; + + case 84: + // LowerAndChange + EV_DoFloor(line,lowerAndChange); + break; + + case 86: + // Open Door + EV_DoDoor(line,open); + break; + + case 87: + // Perpetual Platform Raise + EV_DoPlat(line,perpetualRaise,0); + break; + + case 88: + // PlatDownWaitUp + EV_DoPlat(line,downWaitUpStay,0); + break; + + case 89: + // Platform Stop + EV_StopPlat(line); + break; + + case 90: + // Raise Door + EV_DoDoor(line,normal); + break; + + case 91: + // Raise Floor + EV_DoFloor(line,raiseFloor); + break; + + case 92: + // Raise Floor 24 + EV_DoFloor(line,raiseFloor24); + break; + + case 93: + // Raise Floor 24 And Change + EV_DoFloor(line,raiseFloor24AndChange); + break; + + case 94: + // Raise Floor Crush + EV_DoFloor(line,raiseFloorCrush); + break; + + case 95: + // Raise floor to nearest height + // and change texture. + EV_DoPlat(line,raiseToNearestAndChange,0); + break; + + case 96: + // Raise floor to shortest texture height + // on either side of lines. + EV_DoFloor(line,raiseToTexture); + break; + + case 97: + // TELEPORT! + EV_Teleport( line, side, thing ); + break; + + case 98: + // Lower Floor (TURBO) + EV_DoFloor(line,turboLower); + break; + + case 105: + // Blazing Door Raise (faster than TURBO!) + EV_DoDoor (line,blazeRaise); + break; + + case 106: + // Blazing Door Open (faster than TURBO!) + EV_DoDoor (line,blazeOpen); + break; + + case 107: + // Blazing Door Close (faster than TURBO!) + EV_DoDoor (line,blazeClose); + break; + + case 120: + // Blazing PlatDownWaitUpStay. + EV_DoPlat(line,blazeDWUS,0); + break; + + case 126: + // TELEPORT MonsterONLY. + if (!thing->player) + EV_Teleport( line, side, thing ); + break; + + case 128: + // Raise To Nearest Floor + EV_DoFloor(line,raiseFloorToNearest); + break; + + case 129: + // Raise Floor Turbo + EV_DoFloor(line,raiseFloorTurbo); + break; + + // Extended walk triggers + + // jff 1/29/98 added new linedef types to fill all functions out so that + // all have varieties SR, S1, WR, W1 + + // killough 1/31/98: "factor out" compatibility test, by + // adding inner switch qualified by compatibility flag. + // relax test to demo_compatibility + + // killough 2/16/98: Fix problems with W1 types being cleared too early + + default: + if (!demo_compatibility) + switch (line->special) + { + // Extended walk once triggers + + case 142: + // Raise Floor 512 + // 142 W1 EV_DoFloor(raiseFloor512) + if (EV_DoFloor(line,raiseFloor512)) + line->special = 0; + break; + + case 143: + // Raise Floor 24 and change + // 143 W1 EV_DoPlat(raiseAndChange,24) + if (EV_DoPlat(line,raiseAndChange,24)) + line->special = 0; + break; + + case 144: + // Raise Floor 32 and change + // 144 W1 EV_DoPlat(raiseAndChange,32) + if (EV_DoPlat(line,raiseAndChange,32)) + line->special = 0; + break; + + case 145: + // Lower Ceiling to Floor + // 145 W1 EV_DoCeiling(lowerToFloor) + if (EV_DoCeiling( line, lowerToFloor )) + line->special = 0; + break; + + case 146: + // Lower Pillar, Raise Donut + // 146 W1 EV_DoDonut() + if (EV_DoDonut(line)) + line->special = 0; + break; + + case 199: + // Lower ceiling to lowest surrounding ceiling + // 199 W1 EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + line->special = 0; + break; + + case 200: + // Lower ceiling to highest surrounding floor + // 200 W1 EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + line->special = 0; + break; + + case 207: + // killough 2/16/98: W1 silent teleporter (normal kind) + if (EV_SilentTeleport(line, side, thing)) + line->special = 0; + break; + + //jff 3/16/98 renumber 215->153 + case 153: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Trig) + // 153 W1 Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + line->special = 0; + break; + + case 239: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Numeric) + // 239 W1 Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + line->special = 0; + break; + + case 219: + // Lower floor to next lower neighbor + // 219 W1 Lower Floor Next Lower Neighbor + if (EV_DoFloor(line,lowerFloorToNearest)) + line->special = 0; + break; + + case 227: + // Raise elevator next floor + // 227 W1 Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + line->special = 0; + break; + + case 231: + // Lower elevator next floor + // 231 W1 Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + line->special = 0; + break; + + case 235: + // Elevator to current floor + // 235 W1 Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + line->special = 0; + break; + + case 243: //jff 3/6/98 make fit within DCK's 256 linedef types + // killough 2/16/98: W1 silent teleporter (linedef-linedef kind) + if (EV_SilentLineTeleport(line, side, thing, false)) + line->special = 0; + break; + + case 262: //jff 4/14/98 add silent line-line reversed + if (EV_SilentLineTeleport(line, side, thing, true)) + line->special = 0; + break; + + case 264: //jff 4/14/98 add monster-only silent line-line reversed + if (!thing->player && + EV_SilentLineTeleport(line, side, thing, true)) + line->special = 0; + break; + + case 266: //jff 4/14/98 add monster-only silent line-line + if (!thing->player && + EV_SilentLineTeleport(line, side, thing, false)) + line->special = 0; + break; + + case 268: //jff 4/14/98 add monster-only silent + if (!thing->player && EV_SilentTeleport(line, side, thing)) + line->special = 0; + break; + + //jff 1/29/98 end of added W1 linedef types + + // Extended walk many retriggerable + + //jff 1/29/98 added new linedef types to fill all functions + //out so that all have varieties SR, S1, WR, W1 + + case 147: + // Raise Floor 512 + // 147 WR EV_DoFloor(raiseFloor512) + EV_DoFloor(line,raiseFloor512); + break; + + case 148: + // Raise Floor 24 and Change + // 148 WR EV_DoPlat(raiseAndChange,24) + EV_DoPlat(line,raiseAndChange,24); + break; + + case 149: + // Raise Floor 32 and Change + // 149 WR EV_DoPlat(raiseAndChange,32) + EV_DoPlat(line,raiseAndChange,32); + break; + + case 150: + // Start slow silent crusher + // 150 WR EV_DoCeiling(silentCrushAndRaise) + EV_DoCeiling(line,silentCrushAndRaise); + break; + + case 151: + // RaiseCeilingLowerFloor + // 151 WR EV_DoCeiling(raiseToHighest), + // EV_DoFloor(lowerFloortoLowest) + EV_DoCeiling( line, raiseToHighest ); + EV_DoFloor( line, lowerFloorToLowest ); + break; + + case 152: + // Lower Ceiling to Floor + // 152 WR EV_DoCeiling(lowerToFloor) + EV_DoCeiling( line, lowerToFloor ); + break; + + //jff 3/16/98 renumber 153->256 + case 256: + // Build stairs, step 8 + // 256 WR EV_BuildStairs(build8) + EV_BuildStairs(line,build8); + break; + + //jff 3/16/98 renumber 154->257 + case 257: + // Build stairs, step 16 + // 257 WR EV_BuildStairs(turbo16) + EV_BuildStairs(line,turbo16); + break; + + case 155: + // Lower Pillar, Raise Donut + // 155 WR EV_DoDonut() + EV_DoDonut(line); + break; + + case 156: + // Start lights strobing + // 156 WR Lights EV_StartLightStrobing() + EV_StartLightStrobing(line); + break; + + case 157: + // Lights to dimmest near + // 157 WR Lights EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + break; + + case 201: + // Lower ceiling to lowest surrounding ceiling + // 201 WR EV_DoCeiling(lowerToLowest) + EV_DoCeiling(line,lowerToLowest); + break; + + case 202: + // Lower ceiling to highest surrounding floor + // 202 WR EV_DoCeiling(lowerToMaxFloor) + EV_DoCeiling(line,lowerToMaxFloor); + break; + + case 208: + // killough 2/16/98: WR silent teleporter (normal kind) + EV_SilentTeleport(line, side, thing); + break; + + case 212: //jff 3/14/98 create instant toggle floor type + // Toggle floor between C and F instantly + // 212 WR Instant Toggle Floor + EV_DoPlat(line,toggleUpDn,0); + break; + + //jff 3/16/98 renumber 216->154 + case 154: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Trigger) + // 154 WR Change Texture/Type Only + EV_DoChange(line,trigChangeOnly); + break; + + case 240: //jff 3/15/98 create texture change no motion type + // Texture/Type Change Only (Numeric) + // 240 WR Change Texture/Type Only + EV_DoChange(line,numChangeOnly); + break; + + case 220: + // Lower floor to next lower neighbor + // 220 WR Lower Floor Next Lower Neighbor + EV_DoFloor(line,lowerFloorToNearest); + break; + + case 228: + // Raise elevator next floor + // 228 WR Raise Elevator next floor + EV_DoElevator(line,elevateUp); + break; + + case 232: + // Lower elevator next floor + // 232 WR Lower Elevator next floor + EV_DoElevator(line,elevateDown); + break; + + case 236: + // Elevator to current floor + // 236 WR Elevator to current floor + EV_DoElevator(line,elevateCurrent); + break; + + case 244: //jff 3/6/98 make fit within DCK's 256 linedef types + // killough 2/16/98: WR silent teleporter (linedef-linedef kind) + EV_SilentLineTeleport(line, side, thing, false); + break; + + case 263: //jff 4/14/98 add silent line-line reversed + EV_SilentLineTeleport(line, side, thing, true); + break; + + case 265: //jff 4/14/98 add monster-only silent line-line reversed + if (!thing->player) + EV_SilentLineTeleport(line, side, thing, true); + break; + + case 267: //jff 4/14/98 add monster-only silent line-line + if (!thing->player) + EV_SilentLineTeleport(line, side, thing, false); + break; + + case 269: //jff 4/14/98 add monster-only silent + if (!thing->player) + EV_SilentTeleport(line, side, thing); + break; + + //jff 1/29/98 end of added WR linedef types + } + break; + } +} + +// +// P_ShootSpecialLine - Gun trigger special dispatcher +// +// Called when a thing shoots a special line with bullet, shell, saw, or fist. +// +// jff 02/12/98 all G1 lines were fixed to check the result from the EV_ +// function before clearing the special. This avoids losing the function +// of the line, should the sector already be in motion when the line is +// impacted. Change is qualified by demo_compatibility. +// +void P_ShootSpecialLine +( mobj_t* thing, + line_t* line ) +{ + //jff 02/04/98 add check here for generalized linedef + if (!demo_compatibility) + { + // pointer to line function is NULL by default, set non-null if + // line special is gun triggered generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player) + { + if (!(line->special & DoorMonster)) + return; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return; + } + if (!line->tag) //jff 3/2/98 all gun generalized types require tag + return; + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player) + return; // monsters disallowed from unlocking doors + if (((line->special&TriggerType)==GunOnce) || ((line->special&TriggerType)==GunMany)) + { //jff 4/1/98 check for being a gun type before reporting door type + if (!P_CanUnlockGenDoor(line,thing->player)) + return; + } + else + return; + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player) + if (!(line->special & LiftMonster)) + return; // monsters disallowed + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenStairs; + } + else if ((unsigned)line->special >= GenCrusherBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return; // monsters disallowed + if (!line->tag) //jff 2/27/98 all gun generalized types require tag + return; + linefunc = EV_DoGenCrusher; + } + + if (linefunc) + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case GunOnce: + if (linefunc(line)) + P_ChangeSwitchTexture(line,0); + return; + case GunMany: + if (linefunc(line)) + P_ChangeSwitchTexture(line,1); + return; + default: // if not a gun type, do nothing here + return; + } + } + + // Impacts that other things can activate. + if (!thing->player) + { + int ok = 0; + switch(line->special) + { + case 46: + // 46 GR Open door on impact weapon is monster activatable + ok = 1; + break; + } + if (!ok) + return; + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return; + + switch(line->special) + { + case 24: + // 24 G1 raise floor to highest adjacent + if (EV_DoFloor(line,raiseFloor) || demo_compatibility) + P_ChangeSwitchTexture(line,0); + break; + + case 46: + // 46 GR open door, stay open + EV_DoDoor(line,open); + P_ChangeSwitchTexture(line,1); + break; + + case 47: + // 47 G1 raise floor to nearest and change texture and type + if (EV_DoPlat(line,raiseToNearestAndChange,0) || demo_compatibility) + P_ChangeSwitchTexture(line,0); + break; + + //jff 1/30/98 added new gun linedefs here + // killough 1/31/98: added demo_compatibility check, added inner switch + + default: + if (!demo_compatibility) + switch (line->special) + { + case 197: + // Exit to next level + // killough 10/98: prevent zombies from exiting levels + if(thing->player && thing->player->health<=0 && !comp[comp_zombie]) + break; + P_ChangeSwitchTexture(line,0); + G_ExitLevel(); + break; + + case 198: + // Exit to secret level + // killough 10/98: prevent zombies from exiting levels + if(thing->player && thing->player->health<=0 && !comp[comp_zombie]) + break; + P_ChangeSwitchTexture(line,0); + G_SecretExitLevel(); + break; + //jff end addition of new gun linedefs + } + break; + } +} + + +// +// P_PlayerInSpecialSector() +// +// Called every tick frame +// that the player origin is in a special sector +// +// Changed to ignore sector types the engine does not recognize +// +void P_PlayerInSpecialSector (player_t* player) +{ + sector_t* sector; + + sector = player->mo->subsector->sector; + + // Falling, not all the way down yet? + // Sector specials don't apply in mid-air + if (player->mo->z != sector->floorheight) + return; + + // Has hit ground. + //jff add if to handle old vs generalized types + if (sector->special<32) // regular sector specials + { + switch (sector->special) + { + case 5: + // 5/10 unit damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 10); + break; + + case 7: + // 2/5 unit damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 5); + break; + + case 16: + // 10/20 unit damage per 31 ticks + case 4: + // 10/20 unit damage plus blinking light (light already spawned) + if (!player->powers[pw_ironfeet] + || (P_Random(pr_slimehurt)<5) ) // even with suit, take damage + { + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + } + break; + + case 9: + // Tally player in secret sector, clear secret special + player->secretcount++; + sector->special = 0; + break; + + case 11: + // Exit on health < 11, take 10/20 damage per 31 ticks + if (comp[comp_god]) /* killough 2/21/98: add compatibility switch */ + player->cheats &= ~CF_GODMODE; // on godmode cheat clearing + // does not affect invulnerability + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + + if (player->health <= 10) + G_ExitLevel(); + break; + + default: + //jff 1/24/98 Don't exit as DOOM2 did, just ignore + break; + }; + } + else //jff 3/14/98 handle extended sector types for secrets and damage + { + switch ((sector->special&DAMAGE_MASK)>>DAMAGE_SHIFT) + { + case 0: // no damage + break; + case 1: // 2/5 damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 5); + break; + case 2: // 5/10 damage per 31 ticks + if (!player->powers[pw_ironfeet]) + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 10); + break; + case 3: // 10/20 damage per 31 ticks + if (!player->powers[pw_ironfeet] + || (P_Random(pr_slimehurt)<5)) // take damage even with suit + { + if (!(leveltime&0x1f)) + P_DamageMobj (player->mo, NULL, NULL, 20); + } + break; + } + if (sector->special&SECRET_MASK) + { + player->secretcount++; + sector->special &= ~SECRET_MASK; + if (sector->special<32) // if all extended bits clear, + sector->special=0; // sector is not special anymore + } + + // phares 3/19/98: + // + // If FRICTION_MASK or PUSH_MASK is set, we don't care at this + // point, since the code to deal with those situations is + // handled by Thinkers. + + } +} + +// +// P_UpdateSpecials() +// +// Check level timer, frag counter, +// animate flats, scroll walls, +// change button textures +// +// Reads and modifies globals: +// levelTimer, levelTimeCount, +// levelFragLimit, levelFragLimitCount +// + +static boolean levelTimer; +static int levelTimeCount; +boolean levelFragLimit; // Ty 03/18/98 Added -frags support +int levelFragLimitCount; // Ty 03/18/98 Added -frags support + +void P_UpdateSpecials (void) +{ + anim_t* anim; + int pic; + int i; + + // Downcount level timer, exit level if elapsed + if (levelTimer == true) + { + levelTimeCount--; + if (!levelTimeCount) + G_ExitLevel(); + } + + // Check frag counters, if frag limit reached, exit level // Ty 03/18/98 + // Seems like the total frags should be kept in a simple + // array somewhere, but until they are... + if (levelFragLimit == true) // we used -frags so compare count + { + int k,m,fragcount,exitflag=false; + for (k=0;k= levelFragLimitCount) exitflag = true; + if (exitflag == true) break; // skip out of the loop--we're done + } + if (exitflag == true) + G_ExitLevel(); + } + + // Animate flats and textures globally + for (anim = anims ; anim < lastanim ; anim++) + { + for (i=anim->basepic ; ibasepic+anim->numpics ; i++) + { + pic = anim->basepic + ( (leveltime/anim->speed + i)%anim->numpics ); + if (anim->istexture) + texturetranslation[i] = pic; + else + flattranslation[i] = pic; + } + } + + // Check buttons (retriggerable switches) and change texture on timeout + for (i = 0; i < MAXBUTTONS; i++) + if (buttonlist[i].btimer) + { + buttonlist[i].btimer--; + if (!buttonlist[i].btimer) + { + switch(buttonlist[i].where) + { + case top: + sides[buttonlist[i].line->sidenum[0]].toptexture = + buttonlist[i].btexture; + break; + + case middle: + sides[buttonlist[i].line->sidenum[0]].midtexture = + buttonlist[i].btexture; + break; + + case bottom: + sides[buttonlist[i].line->sidenum[0]].bottomtexture = + buttonlist[i].btexture; + break; + } + { + /* don't take the address of the switch's sound origin, + * unless in a compatibility mode. */ + mobj_t *so = (mobj_t *)buttonlist[i].soundorg; + if (comp[comp_sound] || compatibility_level < prboom_6_compatibility) + /* since the buttonlist array is usually zeroed out, + * button popouts generally appear to come from (0,0) */ + so = (mobj_t *)&buttonlist[i].soundorg; + S_StartSound(so, sfx_swtchn); + } + memset(&buttonlist[i],0,sizeof(button_t)); + } + } +} + +////////////////////////////////////////////////////////////////////// +// +// Sector and Line special thinker spawning at level startup +// +////////////////////////////////////////////////////////////////////// + +// +// P_SpawnSpecials +// After the map has been loaded, +// scan for specials that spawn thinkers +// + +// Parses command line parameters. +void P_SpawnSpecials (void) +{ + sector_t* sector; + int i; + int episode; + + episode = 1; + if (W_CheckNumForName("texture2") >= 0) + episode = 2; + + // See if -timer needs to be used. + levelTimer = false; + + i = M_CheckParm("-avg"); // Austin Virtual Gaming 20 min timer on DM play + if (i && deathmatch) + { + levelTimer = true; + levelTimeCount = 20 * 60 * TICRATE; + } + + i = M_CheckParm("-timer"); // user defined timer on game play + if (i && deathmatch) + { + int time; + time = atoi(myargv[i+1]) * 60 * TICRATE; + levelTimer = true; + levelTimeCount = time; + } + + // See if -frags has been used + levelFragLimit = false; + i = M_CheckParm("-frags"); // Ty 03/18/98 Added -frags support + if (i && deathmatch) + { + int frags; + frags = atoi(myargv[i+1]); + if (frags <= 0) frags = 10; // default 10 if no count provided + levelFragLimit = true; + levelFragLimitCount = frags; + } + + + // Init special sectors. + sector = sectors; + for (i=0 ; ispecial) + continue; + + if (sector->special&SECRET_MASK) //jff 3/15/98 count extended + totalsecret++; // secret sectors too + + switch (sector->special&31) + { + case 1: + // random off + P_SpawnLightFlash (sector); + break; + + case 2: + // strobe fast + P_SpawnStrobeFlash(sector,FASTDARK,0); + break; + + case 3: + // strobe slow + P_SpawnStrobeFlash(sector,SLOWDARK,0); + break; + + case 4: + // strobe fast/death slime + P_SpawnStrobeFlash(sector,FASTDARK,0); + sector->special |= 3<special<32) //jff 3/14/98 bits don't count unless not + totalsecret++; // a generalized sector type + break; + + case 10: + // door close in 30 seconds + P_SpawnDoorCloseIn30 (sector); + break; + + case 12: + // sync strobe slow + P_SpawnStrobeFlash (sector, SLOWDARK, 1); + break; + + case 13: + // sync strobe fast + P_SpawnStrobeFlash (sector, FASTDARK, 1); + break; + + case 14: + // door raise in 5 minutes + P_SpawnDoorRaiseIn5Mins (sector, i); + break; + + case 17: + // fire flickering + P_SpawnFireFlicker(sector); + break; + } + } + + P_RemoveAllActiveCeilings(); // jff 2/22/98 use killough's scheme + + P_RemoveAllActivePlats(); // killough + + for (i = 0;i < MAXBUTTONS;i++) + memset(&buttonlist[i],0,sizeof(button_t)); + + // P_InitTagLists() must be called before P_FindSectorFromLineTag() + // or P_FindLineFromLineTag() can be called. + + P_InitTagLists(); // killough 1/30/98: Create xref tables for tags + + P_SpawnScrollers(); // killough 3/7/98: Add generalized scrollers + + P_SpawnFriction(); // phares 3/12/98: New friction model using linedefs + + P_SpawnPushers(); // phares 3/20/98: New pusher model using linedefs + + for (i=0; i= 0;) + sectors[s].heightsec = sec; + break; + + // killough 3/16/98: Add support for setting + // floor lighting independently (e.g. lava) + case 213: + sec = sides[*lines[i].sidenum].sector-sectors; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].floorlightsec = sec; + break; + + // killough 4/11/98: Add support for setting + // ceiling lighting independently + case 261: + sec = sides[*lines[i].sidenum].sector-sectors; + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].ceilinglightsec = sec; + break; + + // killough 10/98: + // + // Support for sky textures being transferred from sidedefs. + // Allows scrolling and other effects (but if scrolling is + // used, then the same sector tag needs to be used for the + // sky sector, the sky-transfer linedef, and the scroll-effect + // linedef). Still requires user to use F_SKY1 for the floor + // or ceiling texture, to distinguish floor and ceiling sky. + + case 271: // Regular sky + case 272: // Same, only flipped + for (s = -1; (s = P_FindSectorFromLineTag(lines+i,s)) >= 0;) + sectors[s].sky = i | PL_SKYFLAT; + break; + } +} + +// killough 2/28/98: +// +// This function, with the help of r_plane.c and r_bsp.c, supports generalized +// scrolling floors and walls, with optional mobj-carrying properties, e.g. +// conveyor belts, rivers, etc. A linedef with a special type affects all +// tagged sectors the same way, by creating scrolling and/or object-carrying +// properties. Multiple linedefs may be used on the same sector and are +// cumulative, although the special case of scrolling a floor and carrying +// things on it, requires only one linedef. The linedef's direction determines +// the scrolling direction, and the linedef's length determines the scrolling +// speed. This was designed so that an edge around the sector could be used to +// control the direction of the sector's scrolling, which is usually what is +// desired. +// +// Process the active scrollers. +// +// This is the main scrolling code +// killough 3/7/98 + +void T_Scroll(scroll_t *s) +{ + fixed_t dx = s->dx, dy = s->dy; + + if (s->control != -1) + { // compute scroll amounts based on a sector's height changes + fixed_t height = sectors[s->control].floorheight + + sectors[s->control].ceilingheight; + fixed_t delta = height - s->last_height; + s->last_height = height; + dx = FixedMul(dx, delta); + dy = FixedMul(dy, delta); + } + + // killough 3/14/98: Add acceleration + if (s->accel) + { + s->vdx = dx += s->vdx; + s->vdy = dy += s->vdy; + } + + if (!(dx | dy)) // no-op if both (x,y) offsets 0 + return; + + switch (s->type) + { + side_t *side; + sector_t *sec; + fixed_t height, waterheight; // killough 4/4/98: add waterheight + msecnode_t *node; + mobj_t *thing; + + case sc_side: // killough 3/7/98: Scroll wall texture + side = sides + s->affectee; + side->textureoffset += dx; + side->rowoffset += dy; + break; + + case sc_floor: // killough 3/7/98: Scroll floor texture + sec = sectors + s->affectee; + sec->floor_xoffs += dx; + sec->floor_yoffs += dy; + break; + + case sc_ceiling: // killough 3/7/98: Scroll ceiling texture + sec = sectors + s->affectee; + sec->ceiling_xoffs += dx; + sec->ceiling_yoffs += dy; + break; + + case sc_carry: + + // killough 3/7/98: Carry things on floor + // killough 3/20/98: use new sector list which reflects true members + // killough 3/27/98: fix carrier bug + // killough 4/4/98: Underwater, carry things even w/o gravity + + sec = sectors + s->affectee; + height = sec->floorheight; + waterheight = sec->heightsec != -1 && + sectors[sec->heightsec].floorheight > height ? + sectors[sec->heightsec].floorheight : INT_MIN; + + for (node = sec->touching_thinglist; node; node = node->m_snext) + if (!((thing = node->m_thing)->flags & MF_NOCLIP) && + (!(thing->flags & MF_NOGRAVITY || thing->z > height) || + thing->z < waterheight)) + { + // Move objects only if on floor or underwater, + // non-floating, and clipped. + thing->momx += dx; + thing->momy += dy; + } + break; + + case sc_carry_ceiling: // to be added later + break; + } +} + +// +// Add_Scroller() +// +// Add a generalized scroller to the thinker list. +// +// type: the enumerated type of scrolling: floor, ceiling, floor carrier, +// wall, floor carrier & scroller +// +// (dx,dy): the direction and speed of the scrolling or its acceleration +// +// control: the sector whose heights control this scroller's effect +// remotely, or -1 if no control sector +// +// affectee: the index of the affected object (sector or sidedef) +// +// accel: non-zero if this is an accelerative effect +// + +static void Add_Scroller(int type, fixed_t dx, fixed_t dy, + int control, int affectee, int accel) +{ + scroll_t *s = Z_Malloc(sizeof *s, PU_LEVSPEC, 0); + s->thinker.function = T_Scroll; + s->type = type; + s->dx = dx; + s->dy = dy; + s->accel = accel; + s->vdx = s->vdy = 0; + if ((s->control = control) != -1) + s->last_height = + sectors[control].floorheight + sectors[control].ceilingheight; + s->affectee = affectee; + P_AddThinker(&s->thinker); +} + +// Adds wall scroller. Scroll amount is rotated with respect to wall's +// linedef first, so that scrolling towards the wall in a perpendicular +// direction is translated into vertical motion, while scrolling along +// the wall in a parallel direction is translated into horizontal motion. +// +// killough 5/25/98: cleaned up arithmetic to avoid drift due to roundoff +// +// killough 10/98: +// fix scrolling aliasing problems, caused by long linedefs causing overflowing + +static void Add_WallScroller(fixed_t dx, fixed_t dy, const line_t *l, + int control, int accel) +{ + fixed_t x = D_abs(l->dx), y = D_abs(l->dy), d; + if (y > x) + d = x, x = y, y = d; + d = FixedDiv(x, finesine[(tantoangle[FixedDiv(y,x) >> DBITS] + ANG90) + >> ANGLETOFINESHIFT]); + + // CPhipps - Import scroller calc overflow fix, compatibility optioned + if (compatibility_level >= lxdoom_1_compatibility) { + x = (fixed_t)(((int_64_t)dy * -(int_64_t)l->dy - (int_64_t)dx * (int_64_t)l->dx) / (int_64_t)d); // killough 10/98: + y = (fixed_t)(((int_64_t)dy * (int_64_t)l->dx - (int_64_t)dx * (int_64_t)l->dy) / (int_64_t)d); // Use long long arithmetic + } else { + x = -FixedDiv(FixedMul(dy, l->dy) + FixedMul(dx, l->dx), d); + y = -FixedDiv(FixedMul(dx, l->dy) - FixedMul(dy, l->dx), d); + } + Add_Scroller(sc_side, x, y, control, *l->sidenum, accel); +} + +// Amount (dx,dy) vector linedef is shifted right to get scroll amount +#define SCROLL_SHIFT 5 + +// Factor to scale scrolling effect into mobj-carrying properties = 3/32. +// (This is so scrolling floors and objects on them can move at same speed.) +#define CARRYFACTOR ((fixed_t)(FRACUNIT*.09375)) + +// Initialize the scrollers +static void P_SpawnScrollers(void) +{ + int i; + line_t *l = lines; + + for (i=0;idx >> SCROLL_SHIFT; // direction and speed of scrolling + fixed_t dy = l->dy >> SCROLL_SHIFT; + int control = -1, accel = 0; // no control sector or acceleration + int special = l->special; + + // killough 3/7/98: Types 245-249 are same as 250-254 except that the + // first side's sector's heights cause scrolling when they change, and + // this linedef controls the direction and speed of the scrolling. The + // most complicated linedef since donuts, but powerful :) + // + // killough 3/15/98: Add acceleration. Types 214-218 are the same but + // are accelerative. + + if (special >= 245 && special <= 249) // displacement scrollers + { + special += 250-245; + control = sides[*l->sidenum].sector - sectors; + } + else + if (special >= 214 && special <= 218) // accelerative scrollers + { + accel = 1; + special += 250-214; + control = sides[*l->sidenum].sector - sectors; + } + + switch (special) + { + register int s; + + case 250: // scroll effect ceiling + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_ceiling, -dx, dy, control, s, accel); + break; + + case 251: // scroll effect floor + case 253: // scroll and carry objects on floor + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_floor, -dx, dy, control, s, accel); + if (special != 253) + break; + + case 252: // carry objects on floor + dx = FixedMul(dx,CARRYFACTOR); + dy = FixedMul(dy,CARRYFACTOR); + for (s=-1; (s = P_FindSectorFromLineTag(l,s)) >= 0;) + Add_Scroller(sc_carry, dx, dy, control, s, accel); + break; + + // killough 3/1/98: scroll wall according to linedef + // (same direction and speed as scrolling floors) + case 254: + for (s=-1; (s = P_FindLineFromLineTag(l,s)) >= 0;) + if (s != i) + Add_WallScroller(dx, dy, lines+s, control, accel); + break; + + case 255: // killough 3/2/98: scroll according to sidedef offsets + s = lines[i].sidenum[0]; + Add_Scroller(sc_side, -sides[s].textureoffset, + sides[s].rowoffset, -1, s, accel); + break; + + case 48: // scroll first side + Add_Scroller(sc_side, FRACUNIT, 0, -1, lines[i].sidenum[0], accel); + break; + + case 85: // jff 1/30/98 2-way scroll + Add_Scroller(sc_side, -FRACUNIT, 0, -1, lines[i].sidenum[0], accel); + break; + } + } +} + +// e6y +// restored boom's friction code + +///////////////////////////// +// +// Add a friction thinker to the thinker list +// +// Add_Friction adds a new friction thinker to the list of active thinkers. +// + +static void Add_Friction(int friction, int movefactor, int affectee) +{ + friction_t *f = Z_Malloc(sizeof *f, PU_LEVSPEC, 0); + + f->thinker.function/*.acp1*/ = /*(actionf_p1) */T_Friction; + f->friction = friction; + f->movefactor = movefactor; + f->affectee = affectee; + P_AddThinker(&f->thinker); +} + +///////////////////////////// +// +// This is where abnormal friction is applied to objects in the sectors. +// A friction thinker has been spawned for each sector where less or +// more friction should be applied. The amount applied is proportional to +// the length of the controlling linedef. + +void T_Friction(friction_t *f) +{ + sector_t *sec; + mobj_t *thing; + msecnode_t* node; + + if (compatibility || !variable_friction) + return; + + sec = sectors + f->affectee; + + // Be sure the special sector type is still turned on. If so, proceed. + // Else, bail out; the sector type has been changed on us. + + if (!(sec->special & FRICTION_MASK)) + return; + + // Assign the friction value to players on the floor, non-floating, + // and clipped. Normally the object's friction value is kept at + // ORIG_FRICTION and this thinker changes it for icy or muddy floors. + + // In Phase II, you can apply friction to Things other than players. + + // When the object is straddling sectors with the same + // floorheight that have different frictions, use the lowest + // friction value (muddy has precedence over icy). + + node = sec->touching_thinglist; // things touching this sector + while (node) + { + thing = node->m_thing; + if (thing->player && + !(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)) && + thing->z <= sec->floorheight) + { + if ((thing->friction == ORIG_FRICTION) || // normal friction? + (f->friction < thing->friction)) + { + thing->friction = f->friction; + thing->movefactor = f->movefactor; + } + } + node = node->m_snext; + } +} + + +// killough 3/7/98 -- end generalized scroll effects + +//////////////////////////////////////////////////////////////////////////// +// +// FRICTION EFFECTS +// +// phares 3/12/98: Start of friction effects +// +// As the player moves, friction is applied by decreasing the x and y +// momentum values on each tic. By varying the percentage of decrease, +// we can simulate muddy or icy conditions. In mud, the player slows +// down faster. In ice, the player slows down more slowly. +// +// The amount of friction change is controlled by the length of a linedef +// with type 223. A length < 100 gives you mud. A length > 100 gives you ice. +// +// Also, each sector where these effects are to take place is given a +// new special type _______. Changing the type value at runtime allows +// these effects to be turned on or off. +// +// Sector boundaries present problems. The player should experience these +// friction changes only when his feet are touching the sector floor. At +// sector boundaries where floor height changes, the player can find +// himself still 'in' one sector, but with his feet at the floor level +// of the next sector (steps up or down). To handle this, Thinkers are used +// in icy/muddy sectors. These thinkers examine each object that is touching +// their sectors, looking for players whose feet are at the same level as +// their floors. Players satisfying this condition are given new friction +// values that are applied by the player movement code later. +// +// killough 8/28/98: +// +// Completely redid code, which did not need thinkers, and which put a heavy +// drag on CPU. Friction is now a property of sectors, NOT objects inside +// them. All objects, not just players, are affected by it, if they touch +// the sector's floor. Code simpler and faster, only calling on friction +// calculations when an object needs friction considered, instead of doing +// friction calculations on every sector during every tic. +// +// Although this -might- ruin Boom demo sync involving friction, it's the only +// way, short of code explosion, to fix the original design bug. Fixing the +// design bug in Boom's original friction code, while maintaining demo sync +// under every conceivable circumstance, would double or triple code size, and +// would require maintenance of buggy legacy code which is only useful for old +// demos. Doom demos, which are more important IMO, are not affected by this +// change. +// +///////////////////////////// +// +// Initialize the sectors where friction is increased or decreased + +static void P_SpawnFriction(void) +{ + int i; + line_t *l = lines; + + // killough 8/28/98: initialize all sectors to normal friction first + for (i = 0; i < numsectors; i++) + { + sectors[i].friction = ORIG_FRICTION; + sectors[i].movefactor = ORIG_FRICTION_FACTOR; + } + + for (i = 0 ; i < numlines ; i++,l++) + if (l->special == 223) + { + int length = P_AproxDistance(l->dx,l->dy)>>FRACBITS; + int friction = (0x1EB8*length)/0x80 + 0xD000; + int movefactor, s; + + // The following check might seem odd. At the time of movement, + // the move distance is multiplied by 'friction/0x10000', so a + // higher friction value actually means 'less friction'. + + if (friction > ORIG_FRICTION) // ice + movefactor = ((0x10092 - friction)*(0x70))/0x158; + else + movefactor = ((friction - 0xDB34)*(0xA))/0x80; + + if (mbf_features) + { // killough 8/28/98: prevent odd situations + if (friction > FRACUNIT) + friction = FRACUNIT; + if (friction < 0) + friction = 0; + if (movefactor < 32) + movefactor = 32; + } + + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + { + // killough 8/28/98: + // + // Instead of spawning thinkers, which are slow and expensive, + // modify the sector's own friction values. Friction should be + // a property of sectors, not objects which reside inside them. + // Original code scanned every object in every friction sector + // on every tic, adjusting its friction, putting unnecessary + // drag on CPU. New code adjusts friction of sector only once + // at level startup, and then uses this friction value. + + //e6y: boom's friction code for boom compatibility + if (!demo_compatibility && !mbf_features) + Add_Friction(friction,movefactor,s); + + sectors[s].friction = friction; + sectors[s].movefactor = movefactor; + } + } +} + +// +// phares 3/12/98: End of friction effects +// +//////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////// +// +// PUSH/PULL EFFECT +// +// phares 3/20/98: Start of push/pull effects +// +// This is where push/pull effects are applied to objects in the sectors. +// +// There are four kinds of push effects +// +// 1) Pushing Away +// +// Pushes you away from a point source defined by the location of an +// MT_PUSH Thing. The force decreases linearly with distance from the +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PUSH. The force is felt only if the point +// MT_PUSH can see the target object. +// +// 2) Pulling toward +// +// Same as Pushing Away except you're pulled toward an MT_PULL point +// source. This force crosses sector boundaries and is felt w/in a circle +// whose center is at the MT_PULL. The force is felt only if the point +// MT_PULL can see the target object. +// +// 3) Wind +// +// Pushes you in a constant direction. Full force above ground, half +// force on the ground, nothing if you're below it (water). +// +// 4) Current +// +// Pushes you in a constant direction. No force above ground, full +// force if on the ground or below it (water). +// +// The magnitude of the force is controlled by the length of a controlling +// linedef. The force vector for types 3 & 4 is determined by the angle +// of the linedef, and is constant. +// +// For each sector where these effects occur, the sector special type has +// to have the PUSH_MASK bit set. If this bit is turned off by a switch +// at run-time, the effect will not occur. The controlling sector for +// types 1 & 2 is the sector containing the MT_PUSH/MT_PULL Thing. + + +#define PUSH_FACTOR 7 + +///////////////////////////// +// +// Add a push thinker to the thinker list + +static void Add_Pusher(int type, int x_mag, int y_mag, mobj_t* source, int affectee) +{ + pusher_t *p = Z_Malloc(sizeof *p, PU_LEVSPEC, 0); + + p->thinker.function = T_Pusher; + p->source = source; + p->type = type; + p->x_mag = x_mag>>FRACBITS; + p->y_mag = y_mag>>FRACBITS; + p->magnitude = P_AproxDistance(p->x_mag,p->y_mag); + if (source) // point source exist? + { + p->radius = (p->magnitude)<<(FRACBITS+1); // where force goes to zero + p->x = p->source->x; + p->y = p->source->y; + } + p->affectee = affectee; + P_AddThinker(&p->thinker); +} + +///////////////////////////// +// +// PIT_PushThing determines the angle and magnitude of the effect. +// The object's x and y momentum values are changed. +// +// tmpusher belongs to the point source (MT_PUSH/MT_PULL). +// +// killough 10/98: allow to affect things besides players + +pusher_t* tmpusher; // pusher structure for blockmap searches + +static boolean PIT_PushThing(mobj_t* thing) +{ + /* killough 10/98: made more general */ + if (!mbf_features ? + thing->player && !(thing->flags & (MF_NOCLIP | MF_NOGRAVITY)) : + (sentient(thing) || thing->flags & MF_SHOOTABLE) && + !(thing->flags & MF_NOCLIP)) + { + angle_t pushangle; + fixed_t speed; + fixed_t sx = tmpusher->x; + fixed_t sy = tmpusher->y; + + speed = (tmpusher->magnitude - + ((P_AproxDistance(thing->x - sx,thing->y - sy) + >>FRACBITS)>>1))<<(FRACBITS-PUSH_FACTOR-1); + + // killough 10/98: make magnitude decrease with square + // of distance, making it more in line with real nature, + // so long as it's still in range with original formula. + // + // Removes angular distortion, and makes effort required + // to stay close to source, grow increasingly hard as you + // get closer, as expected. Still, it doesn't consider z :( + + if (speed > 0 && mbf_features) + { + int x = (thing->x-sx) >> FRACBITS; + int y = (thing->y-sy) >> FRACBITS; + speed = (int)(((uint_64_t) tmpusher->magnitude << 23) / (x*x+y*y+1)); + } + + // If speed <= 0, you're outside the effective radius. You also have + // to be able to see the push/pull source point. + + if (speed > 0 && P_CheckSight(thing,tmpusher->source)) + { + pushangle = R_PointToAngle2(thing->x,thing->y,sx,sy); + if (tmpusher->source->type == MT_PUSH) + pushangle += ANG180; // away + pushangle >>= ANGLETOFINESHIFT; + thing->momx += FixedMul(speed,finecosine[pushangle]); + thing->momy += FixedMul(speed,finesine[pushangle]); + } + } + return true; +} + +///////////////////////////// +// +// T_Pusher looks for all objects that are inside the radius of +// the effect. +// + +void T_Pusher(pusher_t *p) +{ + sector_t *sec; + mobj_t *thing; + msecnode_t* node; + int xspeed,yspeed; + int xl,xh,yl,yh,bx,by; + int radius; + int ht = 0; + + if (demo_compatibility || !allow_pushers) + return; + + sec = sectors + p->affectee; + + // Be sure the special sector type is still turned on. If so, proceed. + // Else, bail out; the sector type has been changed on us. + + if (!(sec->special & PUSH_MASK)) + return; + + // For constant pushers (wind/current) there are 3 situations: + // + // 1) Affected Thing is above the floor. + // + // Apply the full force if wind, no force if current. + // + // 2) Affected Thing is on the ground. + // + // Apply half force if wind, full force if current. + // + // 3) Affected Thing is below the ground (underwater effect). + // + // Apply no force if wind, full force if current. + + if (p->type == p_push) + { + + // Seek out all pushable things within the force radius of this + // point pusher. Crosses sectors, so use blockmap. + + tmpusher = p; // MT_PUSH/MT_PULL point source + radius = p->radius; // where force goes to zero + tmbbox[BOXTOP] = p->y + radius; + tmbbox[BOXBOTTOM] = p->y - radius; + tmbbox[BOXRIGHT] = p->x + radius; + tmbbox[BOXLEFT] = p->x - radius; + + xl = (tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; + xh = (tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; + yl = (tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; + yh = (tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; + for (bx=xl ; bx<=xh ; bx++) + for (by=yl ; by<=yh ; by++) + P_BlockThingsIterator(bx,by,PIT_PushThing); + return; + } + + // constant pushers p_wind and p_current + + if (sec->heightsec != -1) // special water sector? + ht = sectors[sec->heightsec].floorheight; + node = sec->touching_thinglist; // things touching this sector + for ( ; node ; node = node->m_snext) + { + thing = node->m_thing; + if (!thing->player || (thing->flags & (MF_NOGRAVITY | MF_NOCLIP))) + continue; + if (p->type == p_wind) + { + if (sec->heightsec == -1) // NOT special water sector + if (thing->z > thing->floorz) // above ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else // on ground + { + xspeed = (p->x_mag)>>1; // half force + yspeed = (p->y_mag)>>1; + } + else // special water sector + { + if (thing->z > ht) // above ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else if (thing->player->viewz < ht) // underwater + xspeed = yspeed = 0; // no force + else // wading in water + { + xspeed = (p->x_mag)>>1; // half force + yspeed = (p->y_mag)>>1; + } + } + } + else // p_current + { + if (sec->heightsec == -1) // NOT special water sector + if (thing->z > sec->floorheight) // above ground + xspeed = yspeed = 0; // no force + else // on ground + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + else // special water sector + if (thing->z > ht) // above ground + xspeed = yspeed = 0; // no force + else // underwater + { + xspeed = p->x_mag; // full force + yspeed = p->y_mag; + } + } + thing->momx += xspeed<<(FRACBITS-PUSH_FACTOR); + thing->momy += yspeed<<(FRACBITS-PUSH_FACTOR); + } +} + +///////////////////////////// +// +// P_GetPushThing() returns a pointer to an MT_PUSH or MT_PULL thing, +// NULL otherwise. + +mobj_t* P_GetPushThing(int s) +{ + mobj_t* thing; + sector_t* sec; + + sec = sectors + s; + thing = sec->thinglist; + while (thing) + { + switch(thing->type) + { + case MT_PUSH: + case MT_PULL: + return thing; + default: + break; + } + thing = thing->snext; + } + return NULL; +} + +///////////////////////////// +// +// Initialize the sectors where pushers are present +// + +static void P_SpawnPushers(void) +{ + int i; + line_t *l = lines; + register int s; + mobj_t* thing; + + for (i = 0 ; i < numlines ; i++,l++) + switch(l->special) + { + case 224: // wind + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + Add_Pusher(p_wind,l->dx,l->dy,NULL,s); + break; + case 225: // current + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + Add_Pusher(p_current,l->dx,l->dy,NULL,s); + break; + case 226: // push/pull + for (s = -1; (s = P_FindSectorFromLineTag(l,s)) >= 0 ; ) + { + thing = P_GetPushThing(s); + if (thing) // No MT_P* means no effect + Add_Pusher(p_push,l->dx,l->dy,thing,s); + } + break; + } +} + +// +// phares 3/20/98: End of Pusher effects +// +//////////////////////////////////////////////////////////////////////////// diff --git a/src/p_spec.h b/src/p_spec.h new file mode 100644 index 00000000..1d5aa2bc --- /dev/null +++ b/src/p_spec.h @@ -0,0 +1,1141 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: definitions, declarations and prototypes for specials + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_SPEC__ +#define __P_SPEC__ + +#include "r_defs.h" +#include "d_player.h" + +// Define values for map objects +#define MO_TELEPORTMAN 14 + +// p_floor + +#define ELEVATORSPEED (FRACUNIT*4) +#define FLOORSPEED FRACUNIT + +// p_ceilng + +#define CEILSPEED FRACUNIT +#define CEILWAIT 150 + +// p_doors + +#define VDOORSPEED (FRACUNIT*2) +#define VDOORWAIT 150 + +// p_plats + +#define PLATWAIT 3 +#define PLATSPEED FRACUNIT + +// p_switch + +// 4 players, 4 buttons each at once, max. +// killough 2/14/98: redefine in terms of MAXPLAYERS +#define MAXBUTTONS (MAXPLAYERS*4) + +// 1 second, in ticks. +#define BUTTONTIME TICRATE + +// p_lights + +#define GLOWSPEED 8 +#define STROBEBRIGHT 5 +#define FASTDARK 15 +#define SLOWDARK 35 + +//jff 3/14/98 add bits and shifts for generalized sector types + +#define DAMAGE_MASK 0x60 +#define DAMAGE_SHIFT 5 +#define SECRET_MASK 0x80 +#define SECRET_SHIFT 7 +#define FRICTION_MASK 0x100 +#define FRICTION_SHIFT 8 +#define PUSH_MASK 0x200 +#define PUSH_SHIFT 9 + +//jff 02/04/98 Define masks, shifts, for fields in +// generalized linedef types + +#define GenEnd 0x8000 +#define GenFloorBase 0x6000 +#define GenCeilingBase 0x4000 +#define GenDoorBase 0x3c00 +#define GenLockedBase 0x3800 +#define GenLiftBase 0x3400 +#define GenStairsBase 0x3000 +#define GenCrusherBase 0x2F80 + +#define TriggerType 0x0007 +#define TriggerTypeShift 0 + +// define masks and shifts for the floor type fields + +#define FloorCrush 0x1000 +#define FloorChange 0x0c00 +#define FloorTarget 0x0380 +#define FloorDirection 0x0040 +#define FloorModel 0x0020 +#define FloorSpeed 0x0018 + +#define FloorCrushShift 12 +#define FloorChangeShift 10 +#define FloorTargetShift 7 +#define FloorDirectionShift 6 +#define FloorModelShift 5 +#define FloorSpeedShift 3 + +// define masks and shifts for the ceiling type fields + +#define CeilingCrush 0x1000 +#define CeilingChange 0x0c00 +#define CeilingTarget 0x0380 +#define CeilingDirection 0x0040 +#define CeilingModel 0x0020 +#define CeilingSpeed 0x0018 + +#define CeilingCrushShift 12 +#define CeilingChangeShift 10 +#define CeilingTargetShift 7 +#define CeilingDirectionShift 6 +#define CeilingModelShift 5 +#define CeilingSpeedShift 3 + +// define masks and shifts for the lift type fields + +#define LiftTarget 0x0300 +#define LiftDelay 0x00c0 +#define LiftMonster 0x0020 +#define LiftSpeed 0x0018 + +#define LiftTargetShift 8 +#define LiftDelayShift 6 +#define LiftMonsterShift 5 +#define LiftSpeedShift 3 + +// define masks and shifts for the stairs type fields + +#define StairIgnore 0x0200 +#define StairDirection 0x0100 +#define StairStep 0x00c0 +#define StairMonster 0x0020 +#define StairSpeed 0x0018 + +#define StairIgnoreShift 9 +#define StairDirectionShift 8 +#define StairStepShift 6 +#define StairMonsterShift 5 +#define StairSpeedShift 3 + +// define masks and shifts for the crusher type fields + +#define CrusherSilent 0x0040 +#define CrusherMonster 0x0020 +#define CrusherSpeed 0x0018 + +#define CrusherSilentShift 6 +#define CrusherMonsterShift 5 +#define CrusherSpeedShift 3 + +// define masks and shifts for the door type fields + +#define DoorDelay 0x0300 +#define DoorMonster 0x0080 +#define DoorKind 0x0060 +#define DoorSpeed 0x0018 + +#define DoorDelayShift 8 +#define DoorMonsterShift 7 +#define DoorKindShift 5 +#define DoorSpeedShift 3 + +// define masks and shifts for the locked door type fields + +#define LockedNKeys 0x0200 +#define LockedKey 0x01c0 +#define LockedKind 0x0020 +#define LockedSpeed 0x0018 + +#define LockedNKeysShift 9 +#define LockedKeyShift 6 +#define LockedKindShift 5 +#define LockedSpeedShift 3 + +// define names for the TriggerType field of the general linedefs + +typedef enum +{ + WalkOnce, + WalkMany, + SwitchOnce, + SwitchMany, + GunOnce, + GunMany, + PushOnce, + PushMany, +} triggertype_e; + +// define names for the Speed field of the general linedefs + +typedef enum +{ + SpeedSlow, + SpeedNormal, + SpeedFast, + SpeedTurbo, +} motionspeed_e; + +// define names for the Target field of the general floor + +typedef enum +{ + FtoHnF, + FtoLnF, + FtoNnF, + FtoLnC, + FtoC, + FbyST, + Fby24, + Fby32, +} floortarget_e; + +// define names for the Changer Type field of the general floor + +typedef enum +{ + FNoChg, + FChgZero, + FChgTxt, + FChgTyp, +} floorchange_e; + +// define names for the Change Model field of the general floor + +typedef enum +{ + FTriggerModel, + FNumericModel, +} floormodel_t; + +// define names for the Target field of the general ceiling + +typedef enum +{ + CtoHnC, + CtoLnC, + CtoNnC, + CtoHnF, + CtoF, + CbyST, + Cby24, + Cby32, +} ceilingtarget_e; + +// define names for the Changer Type field of the general ceiling + +typedef enum +{ + CNoChg, + CChgZero, + CChgTxt, + CChgTyp, +} ceilingchange_e; + +// define names for the Change Model field of the general ceiling + +typedef enum +{ + CTriggerModel, + CNumericModel, +} ceilingmodel_t; + +// define names for the Target field of the general lift + +typedef enum +{ + F2LnF, + F2NnF, + F2LnC, + LnF2HnF, +} lifttarget_e; + +// define names for the door Kind field of the general ceiling + +typedef enum +{ + OdCDoor, + ODoor, + CdODoor, + CDoor, +} doorkind_e; + +// define names for the locked door Kind field of the general ceiling + +typedef enum +{ + AnyKey, + RCard, + BCard, + YCard, + RSkull, + BSkull, + YSkull, + AllKeys, +} keykind_e; + +////////////////////////////////////////////////////////////////// +// +// enums for classes of linedef triggers +// +////////////////////////////////////////////////////////////////// + +//jff 2/23/98 identify the special classes that can share sectors + +typedef enum +{ + floor_special, + ceiling_special, + lighting_special, +} special_e; + +//jff 3/15/98 pure texture/type change for better generalized support +typedef enum +{ + trigChangeOnly, + numChangeOnly, +} change_e; + +// p_plats + +typedef enum +{ + up, + down, + waiting, + in_stasis +} plat_e; + +typedef enum +{ + perpetualRaise, + downWaitUpStay, + raiseAndChange, + raiseToNearestAndChange, + blazeDWUS, + genLift, //jff added to support generalized Plat types + genPerpetual, + toggleUpDn, //jff 3/14/98 added to support instant toggle type + +} plattype_e; + +// p_doors + +typedef enum +{ + normal, + close30ThenOpen, + close, + open, + raiseIn5Mins, + blazeRaise, + blazeOpen, + blazeClose, + + //jff 02/05/98 add generalize door types + genRaise, + genBlazeRaise, + genOpen, + genBlazeOpen, + genClose, + genBlazeClose, + genCdO, + genBlazeCdO, +} vldoor_e; + +// p_ceilng + +typedef enum +{ + lowerToFloor, + raiseToHighest, + lowerToLowest, + lowerToMaxFloor, + lowerAndCrush, + crushAndRaise, + fastCrushAndRaise, + silentCrushAndRaise, + + //jff 02/04/98 add types for generalized ceiling mover + genCeiling, + genCeilingChg, + genCeilingChg0, + genCeilingChgT, + + //jff 02/05/98 add types for generalized ceiling mover + genCrusher, + genSilentCrusher, + +} ceiling_e; + +// p_floor + +typedef enum +{ + // lower floor to highest surrounding floor + lowerFloor, + + // lower floor to lowest surrounding floor + lowerFloorToLowest, + + // lower floor to highest surrounding floor VERY FAST + turboLower, + + // raise floor to lowest surrounding CEILING + raiseFloor, + + // raise floor to next highest surrounding floor + raiseFloorToNearest, + + //jff 02/03/98 lower floor to next lowest neighbor + lowerFloorToNearest, + + //jff 02/03/98 lower floor 24 absolute + lowerFloor24, + + //jff 02/03/98 lower floor 32 absolute + lowerFloor32Turbo, + + // raise floor to shortest height texture around it + raiseToTexture, + + // lower floor to lowest surrounding floor + // and change floorpic + lowerAndChange, + + raiseFloor24, + + //jff 02/03/98 raise floor 32 absolute + raiseFloor32Turbo, + + raiseFloor24AndChange, + raiseFloorCrush, + + // raise to next highest floor, turbo-speed + raiseFloorTurbo, + donutRaise, + raiseFloor512, + + //jff 02/04/98 add types for generalized floor mover + genFloor, + genFloorChg, + genFloorChg0, + genFloorChgT, + + //new types for stair builders + buildStair, + genBuildStair, +} floor_e; + +typedef enum +{ + build8, // slowly build by 8 + turbo16 // quickly build by 16 + +} stair_e; + +typedef enum +{ + elevateUp, + elevateDown, + elevateCurrent, +} elevator_e; + +////////////////////////////////////////////////////////////////// +// +// general enums +// +////////////////////////////////////////////////////////////////// + +// texture type enum +typedef enum +{ + top, + middle, + bottom + +} bwhere_e; + +// crush check returns +typedef enum +{ + ok, + crushed, + pastdest +} result_e; + +////////////////////////////////////////////////////////////////// +// +// linedef and sector special data types +// +////////////////////////////////////////////////////////////////// + +// p_switch + +// switch animation structure type + +#if defined(__MWERKS__) +#pragma options align=packed +#endif + +typedef struct +{ + char name1[9]; + char name2[9]; + short episode; +} PACKEDATTR switchlist_t; //jff 3/23/98 pack to read from memory + +#if defined(__MWERKS__) +#pragma options align=reset +#endif + +typedef struct +{ + line_t* line; + bwhere_e where; + int btexture; + int btimer; + mobj_t* soundorg; + +} button_t; + +// p_lights + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int maxlight; + int minlight; + +} fireflicker_t; + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int maxlight; + int minlight; + int maxtime; + int mintime; + +} lightflash_t; + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int count; + int minlight; + int maxlight; + int darktime; + int brighttime; + +} strobe_t; + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + int minlight; + int maxlight; + int direction; + +} glow_t; + +// p_plats + +typedef struct +{ + thinker_t thinker; + sector_t* sector; + fixed_t speed; + fixed_t low; + fixed_t high; + int wait; + int count; + plat_e status; + plat_e oldstatus; + boolean crush; + int tag; + plattype_e type; + + struct platlist *list; // killough +} plat_t; + +// New limit-free plat structure -- killough + +typedef struct platlist { + plat_t *plat; + struct platlist *next,**prev; +} platlist_t; + +// p_ceilng + +typedef struct +{ + thinker_t thinker; + vldoor_e type; + sector_t* sector; + fixed_t topheight; + fixed_t speed; + + // 1 = up, 0 = waiting at top, -1 = down + int direction; + + // tics to wait at the top + int topwait; + // (keep in case a door going down is reset) + // when it reaches 0, start going down + int topcountdown; + + //jff 1/31/98 keep track of line door is triggered by + line_t *line; + + /* killough 10/98: sector tag for gradual lighting effects */ + int lighttag; +} vldoor_t; + +// p_doors + +typedef struct +{ + thinker_t thinker; + ceiling_e type; + sector_t* sector; + fixed_t bottomheight; + fixed_t topheight; + fixed_t speed; + fixed_t oldspeed; + boolean crush; + + //jff 02/04/98 add these to support ceiling changers + int newspecial; + int oldspecial; //jff 3/14/98 add to fix bug in change transfers + short texture; + + // 1 = up, 0 = waiting, -1 = down + int direction; + + // ID + int tag; + int olddirection; + struct ceilinglist *list; // jff 2/22/98 copied from killough's plats +} ceiling_t; + +typedef struct ceilinglist { + ceiling_t *ceiling; + struct ceilinglist *next,**prev; +} ceilinglist_t; + +// p_floor + +typedef struct +{ + thinker_t thinker; + floor_e type; + boolean crush; + sector_t* sector; + int direction; + int newspecial; + int oldspecial; //jff 3/14/98 add to fix bug in change transfers + short texture; + fixed_t floordestheight; + fixed_t speed; + +} floormove_t; + +typedef struct +{ + thinker_t thinker; + elevator_e type; + sector_t* sector; + int direction; + fixed_t floordestheight; + fixed_t ceilingdestheight; + fixed_t speed; +} elevator_t; + +// p_spec + +// killough 3/7/98: Add generalized scroll effects + +typedef struct { + thinker_t thinker; // Thinker structure for scrolling + fixed_t dx, dy; // (dx,dy) scroll speeds + int affectee; // Number of affected sidedef, sector, tag, or whatever + int control; // Control sector (-1 if none) used to control scrolling + fixed_t last_height; // Last known height of control sector + fixed_t vdx, vdy; // Accumulated velocity if accelerative + int accel; // Whether it's accelerative + enum + { + sc_side, + sc_floor, + sc_ceiling, + sc_carry, + sc_carry_ceiling, // killough 4/11/98: carry objects hanging on ceilings + } type; // Type of scroll effect +} scroll_t; + +// phares 3/12/98: added new model of friction for ice/sludge effects + +typedef struct { + thinker_t thinker; // Thinker structure for friction + int friction; // friction value (E800 = normal) + int movefactor; // inertia factor when adding to momentum + int affectee; // Number of affected sector +} friction_t; + +// phares 3/20/98: added new model of Pushers for push/pull effects + +typedef struct { + thinker_t thinker; // Thinker structure for Pusher + enum + { + p_push, + p_pull, + p_wind, + p_current, + } type; + mobj_t* source; // Point source if point pusher + int x_mag; // X Strength + int y_mag; // Y Strength + int magnitude; // Vector strength for point pusher + int radius; // Effective radius for point pusher + int x; // X of point source if point pusher + int y; // Y of point source if point pusher + int affectee; // Number of affected sector +} pusher_t; + +////////////////////////////////////////////////////////////////// +// +// external data declarations +// +////////////////////////////////////////////////////////////////// + +// list of retriggerable buttons active +extern button_t buttonlist[MAXBUTTONS]; + +extern platlist_t *activeplats; // killough 2/14/98 + +extern ceilinglist_t *activeceilings; // jff 2/22/98 + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special utility function prototypes +// +//////////////////////////////////////////////////////////////// + +int twoSided +( int sector, + int line ); + +sector_t* getSector +( int currentSector, + int line, + int side ); + +side_t* getSide +( int currentSector, + int line, + int side ); + +fixed_t P_FindLowestFloorSurrounding +( sector_t* sec ); + +fixed_t P_FindHighestFloorSurrounding +( sector_t* sec ); + +fixed_t P_FindNextHighestFloor +( sector_t* sec, + int currentheight ); + +fixed_t P_FindNextLowestFloor +( sector_t* sec, + int currentheight ); + +fixed_t P_FindLowestCeilingSurrounding +( sector_t* sec ); // jff 2/04/98 + +fixed_t P_FindHighestCeilingSurrounding +( sector_t* sec ); // jff 2/04/98 + +fixed_t P_FindNextLowestCeiling +( sector_t *sec, + int currentheight ); // jff 2/04/98 + +fixed_t P_FindNextHighestCeiling +( sector_t *sec, + int currentheight ); // jff 2/04/98 + +fixed_t P_FindShortestTextureAround +( int secnum ); // jff 2/04/98 + +fixed_t P_FindShortestUpperAround +( int secnum ); // jff 2/04/98 + +sector_t* P_FindModelFloorSector +( fixed_t floordestheight, + int secnum ); //jff 02/04/98 + +sector_t* P_FindModelCeilingSector +( fixed_t ceildestheight, + int secnum ); //jff 02/04/98 + +int P_FindSectorFromLineTag +( const line_t *line, + int start ); // killough 4/17/98 + +int P_FindLineFromLineTag +( const line_t *line, + int start ); // killough 4/17/98 + +int P_FindMinSurroundingLight +( sector_t* sector, + int max ); + +sector_t* getNextSector +( line_t* line, + sector_t* sec ); + +int P_CheckTag +(line_t *line); // jff 2/27/98 + +boolean P_CanUnlockGenDoor +( line_t* line, + player_t* player); + +boolean PUREFUNC P_SectorActive +( special_e t, + const sector_t* s ); + +boolean PUREFUNC P_IsSecret +( const sector_t *sec ); + +boolean PUREFUNC P_WasSecret +( const sector_t *sec ); + +void P_ChangeSwitchTexture +( line_t* line, + int useAgain ); + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special action function prototypes +// +//////////////////////////////////////////////////////////////// + +// p_lights + +void T_LightFlash +( lightflash_t* flash ); + +void T_StrobeFlash +( strobe_t* flash ); + +// jff 8/8/98 add missing thinker for flicker +void T_FireFlicker +( fireflicker_t* flick ); + +void T_Glow +( glow_t* g ); + +// p_plats + +void T_PlatRaise +( plat_t* plat ); + +// p_doors + +void T_VerticalDoor +( vldoor_t* door ); + +// p_ceilng + +void T_MoveCeiling +( ceiling_t* ceiling ); + +// p_floor + +result_e T_MovePlane +( sector_t* sector, + fixed_t speed, + fixed_t dest, + boolean crush, + int floorOrCeiling, + int direction ); + +void T_MoveFloor +( floormove_t* floor ); + +void T_MoveElevator +( elevator_t* elevator ); + +// p_spec + +void T_Scroll +( scroll_t * ); // killough 3/7/98: scroll effect thinker + +void T_Friction +( friction_t * ); // phares 3/12/98: friction thinker + +void T_Pusher +( pusher_t * ); // phares 3/20/98: Push thinker + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special handler prototypes +// +//////////////////////////////////////////////////////////////// + +// p_telept + +int EV_Teleport +( line_t* line, + int side, + mobj_t* thing ); + +// killough 2/14/98: Add silent teleporter +int EV_SilentTeleport +( line_t* line, + int side, + mobj_t* thing ); + +// killough 1/31/98: Add silent line teleporter +int EV_SilentLineTeleport +( line_t* line, + int side, + mobj_t* thing, + boolean reverse); + +// p_floor + +int +EV_DoElevator +( line_t* line, + elevator_e type ); + +int EV_BuildStairs +( line_t* line, + stair_e type ); + +int EV_DoFloor +( line_t* line, + floor_e floortype ); + +// p_ceilng + +int EV_DoCeiling +( line_t* line, + ceiling_e type ); + +int EV_CeilingCrushStop +( line_t* line ); + +// p_doors + +int EV_VerticalDoor +( line_t* line, + mobj_t* thing ); + +int EV_DoDoor +( line_t* line, + vldoor_e type ); + +int EV_DoLockedDoor +( line_t* line, + vldoor_e type, + mobj_t* thing ); + +// p_lights + +int EV_StartLightStrobing +( line_t* line ); + +int EV_TurnTagLightsOff +( line_t* line ); + +int EV_LightTurnOn +( line_t* line, + int bright ); + +int EV_LightTurnOnPartway(line_t* line, fixed_t level); // killough 10/10/98 + +// p_floor + +int EV_DoChange +( line_t* line, + change_e changetype ); + +int EV_DoDonut +( line_t* line ); + +// p_plats + +int EV_DoPlat +( line_t* line, + plattype_e type, + int amount ); + +int EV_StopPlat +( line_t* line ); + +// p_genlin + +int EV_DoGenFloor +( line_t* line ); + +int EV_DoGenCeiling +( line_t* line ); + +int EV_DoGenLift +( line_t* line ); + +int EV_DoGenStairs +( line_t* line ); + +int EV_DoGenCrusher +( line_t* line ); + +int EV_DoGenDoor +( line_t* line ); + +int EV_DoGenLockedDoor +( line_t* line ); + +//////////////////////////////////////////////////////////////// +// +// Linedef and sector special thinker spawning +// +//////////////////////////////////////////////////////////////// + +// at game start +void P_InitPicAnims +( void ); + +void P_InitSwitchList +( void ); + +// at map load +void P_SpawnSpecials +( void ); + +// every tic +void P_UpdateSpecials +( void ); + +// when needed +boolean P_UseSpecialLine +( mobj_t* thing, + line_t* line, + int side ); + +void P_ShootSpecialLine +( mobj_t* thing, + line_t* line ); + +void P_CrossSpecialLine(line_t *line, int side, mobj_t *thing); + +void P_PlayerInSpecialSector +( player_t* player ); + +// p_lights + +void P_SpawnFireFlicker +( sector_t* sector ); + +void P_SpawnLightFlash +( sector_t* sector ); + +void P_SpawnStrobeFlash +( sector_t* sector, + int fastOrSlow, + int inSync ); + +void P_SpawnGlowingLight +( sector_t* sector ); + +// p_plats + +void P_AddActivePlat +( plat_t* plat ); + +void P_RemoveActivePlat +( plat_t* plat ); + +void P_RemoveAllActivePlats +( void ); // killough + +void P_ActivateInStasis +( int tag ); + +// p_doors + +void P_SpawnDoorCloseIn30 +( sector_t* sec ); + +void P_SpawnDoorRaiseIn5Mins +( sector_t* sec, + int secnum ); + +// p_ceilng + +void P_RemoveActiveCeiling +( ceiling_t* ceiling ); //jff 2/22/98 + +void P_RemoveAllActiveCeilings +( void ); //jff 2/22/98 + +void P_AddActiveCeiling +( ceiling_t* c ); + +int P_ActivateInStasisCeiling +( line_t* line ); + +mobj_t* P_GetPushThing(int); // phares 3/23/98 + +#endif diff --git a/src/p_switch.c b/src/p_switch.c new file mode 100644 index 00000000..7dfb2f9c --- /dev/null +++ b/src/p_switch.c @@ -0,0 +1,1150 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Switches, buttons. Two-state animation. Exits. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "p_spec.h" +#include "g_game.h" +#include "s_sound.h" +#include "sounds.h" +#include "lprintf.h" + +// killough 2/8/98: Remove switch limit + +static int *switchlist; // killough +static int max_numswitches; // killough +static int numswitches; // killough + +button_t buttonlist[MAXBUTTONS]; + +// +// P_InitSwitchList() +// +// Only called at game initialization in order to list the set of switches +// and buttons known to the engine. This enables their texture to change +// when activated, and in the case of buttons, change back after a timeout. +// +// This routine modified to read its data from a predefined lump or +// PWAD lump called SWITCHES rather than a static table in this module to +// allow wad designers to insert or modify switches. +// +// Lump format is an array of byte packed switchlist_t structures, terminated +// by a structure with episode == -0. The lump can be generated from a +// text source file using SWANTBLS.EXE, distributed with the BOOM utils. +// The standard list of switches and animations is contained in the example +// source text file DEFSWANI.DAT also in the BOOM util distribution. +// +// Rewritten by Lee Killough to remove limit 2/8/98 +// +void P_InitSwitchList(void) +{ + int i, index = 0; + int episode = (gamemode == registered || gamemode==retail) ? + 2 : gamemode == commercial ? 3 : 1; + const switchlist_t *alphSwitchList; //jff 3/23/98 pointer to switch table + int lump = W_GetNumForName("SWITCHES"); // cph - new wad lump handling + + //jff 3/23/98 read the switch table from a predefined lump + alphSwitchList = (const switchlist_t *)W_CacheLumpNum(lump); + + for (i=0;;i++) + { + if (index+1 >= max_numswitches) + switchlist = realloc(switchlist, sizeof *switchlist * + (max_numswitches = max_numswitches ? max_numswitches*2 : 8)); + if (SHORT(alphSwitchList[i].episode) <= episode) //jff 5/11/98 endianess + { + int texture1, texture2; + + if (!SHORT(alphSwitchList[i].episode)) + break; + + // Ignore switches referencing unknown texture names, instead of exiting. + // Warn if either one is missing, but only add if both are valid. + texture1 = R_CheckTextureNumForName(alphSwitchList[i].name1); + if (texture1 == -1) + lprintf(LO_WARN, "P_InitSwitchList: unknown texture %s\n", + alphSwitchList[i].name1); + texture2 = R_CheckTextureNumForName(alphSwitchList[i].name2); + if (texture2 == -1) + lprintf(LO_WARN, "P_InitSwitchList: unknown texture %s\n", + alphSwitchList[i].name2); + if (texture1 != -1 && texture2 != -1) { + switchlist[index++] = texture1; + switchlist[index++] = texture2; + } + } + } + + numswitches = index/2; + switchlist[index] = -1; + W_UnlockLumpNum(lump); +} + +// +// P_StartButton() +// +// Start a button (retriggerable switch) counting down till it turns off. +// +// Passed the linedef the button is on, which texture on the sidedef contains +// the button, the texture number of the button, and the time the button is +// to remain active in gametics. +// No return. +// +static void P_StartButton +( line_t* line, + bwhere_e w, + int texture, + int time ) +{ + int i; + + // See if button is already pressed + for (i = 0;i < MAXBUTTONS;i++) + if (buttonlist[i].btimer && buttonlist[i].line == line) + return; + + for (i = 0;i < MAXBUTTONS;i++) + if (!buttonlist[i].btimer) // use first unused element of list + { + buttonlist[i].line = line; + buttonlist[i].where = w; + buttonlist[i].btexture = texture; + buttonlist[i].btimer = time; + /* use sound origin of line itself - no need to compatibility-wrap + * as the popout code gets it wrong whatever its value */ + buttonlist[i].soundorg = (mobj_t *)&line->soundorg; + return; + } + + I_Error("P_StartButton: no button slots left!"); +} + +// +// P_ChangeSwitchTexture() +// +// Function that changes switch wall texture on activation. +// +// Passed the line which the switch is on, and whether its retriggerable. +// If not retriggerable, this function clears the line special to insure that +// +// No return +// +void P_ChangeSwitchTexture +( line_t* line, + int useAgain ) +{ + /* Rearranged a bit to avoid too much code duplication */ + mobj_t *soundorg; + int i, sound; + short *texture, *ttop, *tmid, *tbot; + bwhere_e position; + + ttop = &sides[line->sidenum[0]].toptexture; + tmid = &sides[line->sidenum[0]].midtexture; + tbot = &sides[line->sidenum[0]].bottomtexture; + + sound = sfx_swtchn; + /* use the sound origin of the linedef (its midpoint) + * unless in a compatibility mode */ + soundorg = (mobj_t *)&line->soundorg; + if (comp[comp_sound] || compatibility_level < prboom_6_compatibility) { + /* usually NULL, unless there is another button already pressed in, + * in which case it's the sound origin of that button press... */ + soundorg = buttonlist->soundorg; + } else { + // EXIT SWITCH? + /* don't do this unless you're in a compatibility mode */ + // proff - this works as advertised, but I don't like the sound + // if (line->special == 11 || line->special == 51) // exit or secret exit + // sound = sfx_swtchx; + } + + /* don't zero line->special until after exit switch test */ + if (!useAgain) + line->special = 0; + + /* search for a texture to change */ + texture = NULL; position = 0; + for (i = 0;i < numswitches*2;i++) { /* this could be more efficient... */ + if (switchlist[i] == *ttop) { + texture = ttop; position = top; break; + } else if (switchlist[i] == *tmid) { + texture = tmid; position = middle; break; + } else if (switchlist[i] == *tbot) { + texture = tbot; position = bottom; break; + } + } + if (texture == NULL) + return; /* no switch texture was found to change */ + *texture = switchlist[i^1]; + + S_StartSound(soundorg, sound); + + if (useAgain) + P_StartButton(line, position, switchlist[i], BUTTONTIME); +} + + +// +// P_UseSpecialLine +// +// +// Called when a thing uses (pushes) a special line. +// Only the front sides of lines are usable. +// Dispatches to the appropriate linedef function handler. +// +// Passed the thing using the line, the line being used, and the side used +// Returns true if a thinker was created +// +boolean +P_UseSpecialLine +( mobj_t* thing, + line_t* line, + int side ) +{ + + // e6y + // b.m. side test was broken in boom201 + if ((demoplayback ? (demover != 201) : (compatibility_level != boom_201_compatibility))) + if (side) //jff 6/1/98 fix inadvertent deletion of side test + return false; + + //jff 02/04/98 add check here for generalized floor/ceil mover + if (!demo_compatibility) + { + // pointer to line function is NULL by default, set non-null if + // line special is push or switch generalized linedef type + int (*linefunc)(line_t *line)=NULL; + + // check each range of generalized linedefs + if ((unsigned)line->special >= GenEnd) + { + // Out of range for GenFloors + } + else if ((unsigned)line->special >= GenFloorBase) + { + if (!thing->player) + if ((line->special & FloorChange) || !(line->special & FloorModel)) + return false; // FloorModel is "Allow Monsters" if FloorChange is 0 + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenFloor; + } + else if ((unsigned)line->special >= GenCeilingBase) + { + if (!thing->player) + if ((line->special & CeilingChange) || !(line->special & CeilingModel)) + return false; // CeilingModel is "Allow Monsters" if CeilingChange is 0 + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenCeiling; + } + else if ((unsigned)line->special >= GenDoorBase) + { + if (!thing->player) + { + if (!(line->special & DoorMonster)) + return false; // monsters disallowed from this door + if (line->flags & ML_SECRET) // they can't open secret doors either + return false; + } + if (!line->tag && ((line->special&6)!=6)) //jff 3/2/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenDoor; + } + else if ((unsigned)line->special >= GenLockedBase) + { + if (!thing->player) + return false; // monsters disallowed from unlocking doors + if (!P_CanUnlockGenDoor(line,thing->player)) + return false; + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + + linefunc = EV_DoGenLockedDoor; + } + else if ((unsigned)line->special >= GenLiftBase) + { + if (!thing->player) + if (!(line->special & LiftMonster)) + return false; // monsters disallowed + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenLift; + } + else if ((unsigned)line->special >= GenStairsBase) + { + if (!thing->player) + if (!(line->special & StairMonster)) + return false; // monsters disallowed + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenStairs; + } + else if ((unsigned)line->special >= GenCrusherBase) + { + if (!thing->player) + if (!(line->special & CrusherMonster)) + return false; // monsters disallowed + if (!line->tag && ((line->special&6)!=6)) //jff 2/27/98 all non-manual + return false; // generalized types require tag + linefunc = EV_DoGenCrusher; + } + + if (linefunc) + switch((line->special & TriggerType) >> TriggerTypeShift) + { + case PushOnce: + if (!side) + if (linefunc(line)) + line->special = 0; + return true; + case PushMany: + if (!side) + linefunc(line); + return true; + case SwitchOnce: + if (linefunc(line)) + P_ChangeSwitchTexture(line,0); + return true; + case SwitchMany: + if (linefunc(line)) + P_ChangeSwitchTexture(line,1); + return true; + default: // if not a switch/push type, do nothing here + return false; + } + } + + // Switches that other things can activate. + if (!thing->player) + { + // never open secret doors + if (line->flags & ML_SECRET) + return false; + + switch(line->special) + { + case 1: // MANUAL DOOR RAISE + case 32: // MANUAL BLUE + case 33: // MANUAL RED + case 34: // MANUAL YELLOW + //jff 3/5/98 add ability to use teleporters for monsters + case 195: // switch teleporters + case 174: + case 210: // silent switch teleporters + case 209: + break; + + default: + return false; + break; + } + } + + if (!P_CheckTag(line)) //jff 2/27/98 disallow zero tag on some types + return false; + + // Dispatch to handler according to linedef type + switch (line->special) + { + // Manual doors, push type with no tag + case 1: // Vertical Door + case 26: // Blue Door/Locked + case 27: // Yellow Door /Locked + case 28: // Red Door /Locked + + case 31: // Manual door open + case 32: // Blue locked door open + case 33: // Red locked door open + case 34: // Yellow locked door open + + case 117: // Blazing door raise + case 118: // Blazing door open + EV_VerticalDoor (line, thing); + break; + + // Switches (non-retriggerable) + case 7: + // Build Stairs + if (EV_BuildStairs(line,build8)) + P_ChangeSwitchTexture(line,0); + break; + + case 9: + // Change Donut + if (EV_DoDonut(line)) + P_ChangeSwitchTexture(line,0); + break; + + case 11: + /* Exit level + * killough 10/98: prevent zombies from exiting levels + */ + if (thing->player && thing->player->health <= 0 && !comp[comp_zombie]) + { + S_StartSound(thing, sfx_noway); + return false; + } + + P_ChangeSwitchTexture(line,0); + G_ExitLevel (); + break; + + case 14: + // Raise Floor 32 and change texture + if (EV_DoPlat(line,raiseAndChange,32)) + P_ChangeSwitchTexture(line,0); + break; + + case 15: + // Raise Floor 24 and change texture + if (EV_DoPlat(line,raiseAndChange,24)) + P_ChangeSwitchTexture(line,0); + break; + + case 18: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line,0); + break; + + case 20: + // Raise Plat next highest floor and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 21: + // PlatDownWaitUpStay + if (EV_DoPlat(line,downWaitUpStay,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 23: + // Lower Floor to Lowest + if (EV_DoFloor(line,lowerFloorToLowest)) + P_ChangeSwitchTexture(line,0); + break; + + case 29: + // Raise Door + if (EV_DoDoor(line,normal)) + P_ChangeSwitchTexture(line,0); + break; + + case 41: + // Lower Ceiling to Floor + if (EV_DoCeiling(line,lowerToFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 71: + // Turbo Lower Floor + if (EV_DoFloor(line,turboLower)) + P_ChangeSwitchTexture(line,0); + break; + + case 49: + // Ceiling Crush And Raise + if (EV_DoCeiling(line,crushAndRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 50: + // Close Door + if (EV_DoDoor(line,close)) + P_ChangeSwitchTexture(line,0); + break; + + case 51: + /* Secret EXIT + * killough 10/98: prevent zombies from exiting levels + */ + if (thing->player && thing->player->health <= 0 && !comp[comp_zombie]) + { + S_StartSound(thing, sfx_noway); + return false; + } + + P_ChangeSwitchTexture(line,0); + G_SecretExitLevel (); + break; + + case 55: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush)) + P_ChangeSwitchTexture(line,0); + break; + + case 101: + // Raise Floor + if (EV_DoFloor(line,raiseFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 102: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line,lowerFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 103: + // Open Door + if (EV_DoDoor(line,open)) + P_ChangeSwitchTexture(line,0); + break; + + case 111: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor (line,blazeRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 112: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen)) + P_ChangeSwitchTexture(line,0); + break; + + case 113: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose)) + P_ChangeSwitchTexture(line,0); + break; + + case 122: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 127: + // Build Stairs Turbo 16 + if (EV_BuildStairs(line,turbo16)) + P_ChangeSwitchTexture(line,0); + break; + + case 131: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo)) + P_ChangeSwitchTexture(line,0); + break; + + case 133: + // BlzOpenDoor BLUE + case 135: + // BlzOpenDoor RED + case 137: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor (line,blazeOpen,thing)) + P_ChangeSwitchTexture(line,0); + break; + + case 140: + // Raise Floor 512 + if (EV_DoFloor(line,raiseFloor512)) + P_ChangeSwitchTexture(line,0); + break; + + // killough 1/31/98: factored out compatibility check; + // added inner switch, relaxed check to demo_compatibility + + default: + if (!demo_compatibility) + switch (line->special) + { + //jff 1/29/98 added linedef types to fill all functions out so that + // all possess SR, S1, WR, W1 types + + case 158: + // Raise Floor to shortest lower texture + // 158 S1 EV_DoFloor(raiseToTexture), CSW(0) + if (EV_DoFloor(line,raiseToTexture)) + P_ChangeSwitchTexture(line,0); + break; + + case 159: + // Raise Floor to shortest lower texture + // 159 S1 EV_DoFloor(lowerAndChange) + if (EV_DoFloor(line,lowerAndChange)) + P_ChangeSwitchTexture(line,0); + break; + + case 160: + // Raise Floor 24 and change + // 160 S1 EV_DoFloor(raiseFloor24AndChange) + if (EV_DoFloor(line,raiseFloor24AndChange)) + P_ChangeSwitchTexture(line,0); + break; + + case 161: + // Raise Floor 24 + // 161 S1 EV_DoFloor(raiseFloor24) + if (EV_DoFloor(line,raiseFloor24)) + P_ChangeSwitchTexture(line,0); + break; + + case 162: + // Moving floor min n to max n + // 162 S1 EV_DoPlat(perpetualRaise,0) + if (EV_DoPlat(line,perpetualRaise,0)) + P_ChangeSwitchTexture(line,0); + break; + + case 163: + // Stop Moving floor + // 163 S1 EV_DoPlat(perpetualRaise,0) + EV_StopPlat(line); + P_ChangeSwitchTexture(line,0); + break; + + case 164: + // Start fast crusher + // 164 S1 EV_DoCeiling(fastCrushAndRaise) + if (EV_DoCeiling(line,fastCrushAndRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 165: + // Start slow silent crusher + // 165 S1 EV_DoCeiling(silentCrushAndRaise) + if (EV_DoCeiling(line,silentCrushAndRaise)) + P_ChangeSwitchTexture(line,0); + break; + + case 166: + // Raise ceiling, Lower floor + // 166 S1 EV_DoCeiling(raiseToHighest), EV_DoFloor(lowerFloortoLowest) + if (EV_DoCeiling(line, raiseToHighest) || + EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line,0); + break; + + case 167: + // Lower floor and Crush + // 167 S1 EV_DoCeiling(lowerAndCrush) + if (EV_DoCeiling(line, lowerAndCrush)) + P_ChangeSwitchTexture(line,0); + break; + + case 168: + // Stop crusher + // 168 S1 EV_CeilingCrushStop() + if (EV_CeilingCrushStop(line)) + P_ChangeSwitchTexture(line,0); + break; + + case 169: + // Lights to brightest neighbor sector + // 169 S1 EV_LightTurnOn(0) + EV_LightTurnOn(line,0); + P_ChangeSwitchTexture(line,0); + break; + + case 170: + // Lights to near dark + // 170 S1 EV_LightTurnOn(35) + EV_LightTurnOn(line,35); + P_ChangeSwitchTexture(line,0); + break; + + case 171: + // Lights on full + // 171 S1 EV_LightTurnOn(255) + EV_LightTurnOn(line,255); + P_ChangeSwitchTexture(line,0); + break; + + case 172: + // Start Lights Strobing + // 172 S1 EV_StartLightStrobing() + EV_StartLightStrobing(line); + P_ChangeSwitchTexture(line,0); + break; + + case 173: + // Lights to Dimmest Near + // 173 S1 EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + P_ChangeSwitchTexture(line,0); + break; + + case 174: + // Teleport + // 174 S1 EV_Teleport(side,thing) + if (EV_Teleport(line,side,thing)) + P_ChangeSwitchTexture(line,0); + break; + + case 175: + // Close Door, Open in 30 secs + // 175 S1 EV_DoDoor(close30ThenOpen) + if (EV_DoDoor(line,close30ThenOpen)) + P_ChangeSwitchTexture(line,0); + break; + + case 189: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Trigger) + // 189 S1 Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + P_ChangeSwitchTexture(line,0); + break; + + case 203: + // Lower ceiling to lowest surrounding ceiling + // 203 S1 EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + P_ChangeSwitchTexture(line,0); + break; + + case 204: + // Lower ceiling to highest surrounding floor + // 204 S1 EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + P_ChangeSwitchTexture(line,0); + break; + + case 209: + // killough 1/31/98: silent teleporter + //jff 209 S1 SilentTeleport + if (EV_SilentTeleport(line, side, thing)) + P_ChangeSwitchTexture(line,0); + break; + + case 241: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Numeric) + // 241 S1 Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + P_ChangeSwitchTexture(line,0); + break; + + case 221: + // Lower floor to next lowest floor + // 221 S1 Lower Floor To Nearest Floor + if (EV_DoFloor(line,lowerFloorToNearest)) + P_ChangeSwitchTexture(line,0); + break; + + case 229: + // Raise elevator next floor + // 229 S1 Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + P_ChangeSwitchTexture(line,0); + break; + + case 233: + // Lower elevator next floor + // 233 S1 Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + P_ChangeSwitchTexture(line,0); + break; + + case 237: + // Elevator to current floor + // 237 S1 Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + P_ChangeSwitchTexture(line,0); + break; + + + // jff 1/29/98 end of added S1 linedef types + + //jff 1/29/98 added linedef types to fill all functions out so that + // all possess SR, S1, WR, W1 types + + case 78: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Numeric) + // 78 SR Change Texture/Type Only + if (EV_DoChange(line,numChangeOnly)) + P_ChangeSwitchTexture(line,1); + break; + + case 176: + // Raise Floor to shortest lower texture + // 176 SR EV_DoFloor(raiseToTexture), CSW(1) + if (EV_DoFloor(line,raiseToTexture)) + P_ChangeSwitchTexture(line,1); + break; + + case 177: + // Raise Floor to shortest lower texture + // 177 SR EV_DoFloor(lowerAndChange) + if (EV_DoFloor(line,lowerAndChange)) + P_ChangeSwitchTexture(line,1); + break; + + case 178: + // Raise Floor 512 + // 178 SR EV_DoFloor(raiseFloor512) + if (EV_DoFloor(line,raiseFloor512)) + P_ChangeSwitchTexture(line,1); + break; + + case 179: + // Raise Floor 24 and change + // 179 SR EV_DoFloor(raiseFloor24AndChange) + if (EV_DoFloor(line,raiseFloor24AndChange)) + P_ChangeSwitchTexture(line,1); + break; + + case 180: + // Raise Floor 24 + // 180 SR EV_DoFloor(raiseFloor24) + if (EV_DoFloor(line,raiseFloor24)) + P_ChangeSwitchTexture(line,1); + break; + + case 181: + // Moving floor min n to max n + // 181 SR EV_DoPlat(perpetualRaise,0) + + EV_DoPlat(line,perpetualRaise,0); + P_ChangeSwitchTexture(line,1); + break; + + case 182: + // Stop Moving floor + // 182 SR EV_DoPlat(perpetualRaise,0) + EV_StopPlat(line); + P_ChangeSwitchTexture(line,1); + break; + + case 183: + // Start fast crusher + // 183 SR EV_DoCeiling(fastCrushAndRaise) + if (EV_DoCeiling(line,fastCrushAndRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 184: + // Start slow crusher + // 184 SR EV_DoCeiling(crushAndRaise) + if (EV_DoCeiling(line,crushAndRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 185: + // Start slow silent crusher + // 185 SR EV_DoCeiling(silentCrushAndRaise) + if (EV_DoCeiling(line,silentCrushAndRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 186: + // Raise ceiling, Lower floor + // 186 SR EV_DoCeiling(raiseToHighest), EV_DoFloor(lowerFloortoLowest) + if (EV_DoCeiling(line, raiseToHighest) || + EV_DoFloor(line, lowerFloorToLowest)) + P_ChangeSwitchTexture(line,1); + break; + + case 187: + // Lower floor and Crush + // 187 SR EV_DoCeiling(lowerAndCrush) + if (EV_DoCeiling(line, lowerAndCrush)) + P_ChangeSwitchTexture(line,1); + break; + + case 188: + // Stop crusher + // 188 SR EV_CeilingCrushStop() + if (EV_CeilingCrushStop(line)) + P_ChangeSwitchTexture(line,1); + break; + + case 190: //jff 3/15/98 create texture change no motion type + // Texture Change Only (Trigger) + // 190 SR Change Texture/Type Only + if (EV_DoChange(line,trigChangeOnly)) + P_ChangeSwitchTexture(line,1); + break; + + case 191: + // Lower Pillar, Raise Donut + // 191 SR EV_DoDonut() + if (EV_DoDonut(line)) + P_ChangeSwitchTexture(line,1); + break; + + case 192: + // Lights to brightest neighbor sector + // 192 SR EV_LightTurnOn(0) + EV_LightTurnOn(line,0); + P_ChangeSwitchTexture(line,1); + break; + + case 193: + // Start Lights Strobing + // 193 SR EV_StartLightStrobing() + EV_StartLightStrobing(line); + P_ChangeSwitchTexture(line,1); + break; + + case 194: + // Lights to Dimmest Near + // 194 SR EV_TurnTagLightsOff() + EV_TurnTagLightsOff(line); + P_ChangeSwitchTexture(line,1); + break; + + case 195: + // Teleport + // 195 SR EV_Teleport(side,thing) + if (EV_Teleport(line,side,thing)) + P_ChangeSwitchTexture(line,1); + break; + + case 196: + // Close Door, Open in 30 secs + // 196 SR EV_DoDoor(close30ThenOpen) + if (EV_DoDoor(line,close30ThenOpen)) + P_ChangeSwitchTexture(line,1); + break; + + case 205: + // Lower ceiling to lowest surrounding ceiling + // 205 SR EV_DoCeiling(lowerToLowest) + if (EV_DoCeiling(line,lowerToLowest)) + P_ChangeSwitchTexture(line,1); + break; + + case 206: + // Lower ceiling to highest surrounding floor + // 206 SR EV_DoCeiling(lowerToMaxFloor) + if (EV_DoCeiling(line,lowerToMaxFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 210: + // killough 1/31/98: silent teleporter + //jff 210 SR SilentTeleport + if (EV_SilentTeleport(line, side, thing)) + P_ChangeSwitchTexture(line,1); + break; + + case 211: //jff 3/14/98 create instant toggle floor type + // Toggle Floor Between C and F Instantly + // 211 SR Toggle Floor Instant + if (EV_DoPlat(line,toggleUpDn,0)) + P_ChangeSwitchTexture(line,1); + break; + + case 222: + // Lower floor to next lowest floor + // 222 SR Lower Floor To Nearest Floor + if (EV_DoFloor(line,lowerFloorToNearest)) + P_ChangeSwitchTexture(line,1); + break; + + case 230: + // Raise elevator next floor + // 230 SR Raise Elevator next floor + if (EV_DoElevator(line,elevateUp)) + P_ChangeSwitchTexture(line,1); + break; + + case 234: + // Lower elevator next floor + // 234 SR Lower Elevator next floor + if (EV_DoElevator(line,elevateDown)) + P_ChangeSwitchTexture(line,1); + break; + + case 238: + // Elevator to current floor + // 238 SR Elevator to current floor + if (EV_DoElevator(line,elevateCurrent)) + P_ChangeSwitchTexture(line,1); + break; + + case 258: + // Build stairs, step 8 + // 258 SR EV_BuildStairs(build8) + if (EV_BuildStairs(line,build8)) + P_ChangeSwitchTexture(line,1); + break; + + case 259: + // Build stairs, step 16 + // 259 SR EV_BuildStairs(turbo16) + if (EV_BuildStairs(line,turbo16)) + P_ChangeSwitchTexture(line,1); + break; + + // 1/29/98 jff end of added SR linedef types + + } + break; + + // Buttons (retriggerable switches) + case 42: + // Close Door + if (EV_DoDoor(line,close)) + P_ChangeSwitchTexture(line,1); + break; + + case 43: + // Lower Ceiling to Floor + if (EV_DoCeiling(line,lowerToFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 45: + // Lower Floor to Surrounding floor height + if (EV_DoFloor(line,lowerFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 60: + // Lower Floor to Lowest + if (EV_DoFloor(line,lowerFloorToLowest)) + P_ChangeSwitchTexture(line,1); + break; + + case 61: + // Open Door + if (EV_DoDoor(line,open)) + P_ChangeSwitchTexture(line,1); + break; + + case 62: + // PlatDownWaitUpStay + if (EV_DoPlat(line,downWaitUpStay,1)) + P_ChangeSwitchTexture(line,1); + break; + + case 63: + // Raise Door + if (EV_DoDoor(line,normal)) + P_ChangeSwitchTexture(line,1); + break; + + case 64: + // Raise Floor to ceiling + if (EV_DoFloor(line,raiseFloor)) + P_ChangeSwitchTexture(line,1); + break; + + case 66: + // Raise Floor 24 and change texture + if (EV_DoPlat(line,raiseAndChange,24)) + P_ChangeSwitchTexture(line,1); + break; + + case 67: + // Raise Floor 32 and change texture + if (EV_DoPlat(line,raiseAndChange,32)) + P_ChangeSwitchTexture(line,1); + break; + + case 65: + // Raise Floor Crush + if (EV_DoFloor(line,raiseFloorCrush)) + P_ChangeSwitchTexture(line,1); + break; + + case 68: + // Raise Plat to next highest floor and change texture + if (EV_DoPlat(line,raiseToNearestAndChange,0)) + P_ChangeSwitchTexture(line,1); + break; + + case 69: + // Raise Floor to next highest floor + if (EV_DoFloor(line, raiseFloorToNearest)) + P_ChangeSwitchTexture(line,1); + break; + + case 70: + // Turbo Lower Floor + if (EV_DoFloor(line,turboLower)) + P_ChangeSwitchTexture(line,1); + break; + + case 114: + // Blazing Door Raise (faster than TURBO!) + if (EV_DoDoor (line,blazeRaise)) + P_ChangeSwitchTexture(line,1); + break; + + case 115: + // Blazing Door Open (faster than TURBO!) + if (EV_DoDoor (line,blazeOpen)) + P_ChangeSwitchTexture(line,1); + break; + + case 116: + // Blazing Door Close (faster than TURBO!) + if (EV_DoDoor (line,blazeClose)) + P_ChangeSwitchTexture(line,1); + break; + + case 123: + // Blazing PlatDownWaitUpStay + if (EV_DoPlat(line,blazeDWUS,0)) + P_ChangeSwitchTexture(line,1); + break; + + case 132: + // Raise Floor Turbo + if (EV_DoFloor(line,raiseFloorTurbo)) + P_ChangeSwitchTexture(line,1); + break; + + case 99: + // BlzOpenDoor BLUE + case 134: + // BlzOpenDoor RED + case 136: + // BlzOpenDoor YELLOW + if (EV_DoLockedDoor (line,blazeOpen,thing)) + P_ChangeSwitchTexture(line,1); + break; + + case 138: + // Light Turn On + EV_LightTurnOn(line,255); + P_ChangeSwitchTexture(line,1); + break; + + case 139: + // Light Turn Off + EV_LightTurnOn(line,35); + P_ChangeSwitchTexture(line,1); + break; + } + return true; +} diff --git a/src/p_telept.c b/src/p_telept.c new file mode 100644 index 00000000..def8e159 --- /dev/null +++ b/src/p_telept.c @@ -0,0 +1,345 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Teleportation. + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "p_spec.h" +#include "p_maputl.h" +#include "p_map.h" +#include "r_main.h" +#include "p_tick.h" +#include "s_sound.h" +#include "sounds.h" +#include "p_user.h" +#include "r_demo.h" + +static mobj_t* P_TeleportDestination(line_t* line) +{ + int i; + for (i = -1; (i = P_FindSectorFromLineTag(line, i)) >= 0;) { + register thinker_t* th = NULL; + while ((th = P_NextThinker(th,th_misc)) != NULL) + if (th->function == P_MobjThinker) { + register mobj_t* m = (mobj_t*)th; + if (m->type == MT_TELEPORTMAN && + m->subsector->sector-sectors == i) + return m; + } + } + return NULL; +} +// +// TELEPORTATION +// +// killough 5/3/98: reformatted, cleaned up + +int EV_Teleport(line_t *line, int side, mobj_t *thing) +{ + mobj_t *m; + + // don't teleport missiles + // Don't teleport if hit back of line, + // so you can get out of teleporter. + if (side || thing->flags & MF_MISSILE) + return 0; + + // killough 1/31/98: improve performance by using + // P_FindSectorFromLineTag instead of simple linear search. + + if ((m = P_TeleportDestination(line)) != NULL) + { + fixed_t oldx = thing->x, oldy = thing->y, oldz = thing->z; + player_t *player = thing->player; + + // killough 5/12/98: exclude voodoo dolls: + if (player && player->mo != thing) + player = NULL; + + if (!P_TeleportMove(thing, m->x, m->y, false)) /* killough 8/9/98 */ + return 0; + + if (compatibility_level != finaldoom_compatibility) + thing->z = thing->floorz; + + if (player) + player->viewz = thing->z + player->viewheight; + + // spawn teleport fog and emit sound at source + S_StartSound(P_SpawnMobj(oldx, oldy, oldz, MT_TFOG), sfx_telept); + + // spawn teleport fog and emit sound at destination + S_StartSound(P_SpawnMobj(m->x + + 20*finecosine[m->angle>>ANGLETOFINESHIFT], + m->y + + 20*finesine[m->angle>>ANGLETOFINESHIFT], + thing->z, MT_TFOG), + sfx_telept); + + /* don't move for a bit + * cph - DEMOSYNC - BOOM had (player) here? */ + if (thing->player) + thing->reactiontime = 18; + + thing->angle = m->angle; + + thing->momx = thing->momy = thing->momz = 0; + + /* killough 10/98: kill all bobbing momentum too */ + if (player) + player->momx = player->momy = 0; + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + return 1; + } + return 0; +} + +// +// Silent TELEPORTATION, by Lee Killough +// Primarily for rooms-over-rooms etc. +// + +int EV_SilentTeleport(line_t *line, int side, mobj_t *thing) +{ + mobj_t *m; + + // don't teleport missiles + // Don't teleport if hit back of line, + // so you can get out of teleporter. + + if (side || thing->flags & MF_MISSILE) + return 0; + + if ((m = P_TeleportDestination(line)) != NULL) + { + // Height of thing above ground, in case of mid-air teleports: + fixed_t z = thing->z - thing->floorz; + + // Get the angle between the exit thing and source linedef. + // Rotate 90 degrees, so that walking perpendicularly across + // teleporter linedef causes thing to exit in the direction + // indicated by the exit thing. + angle_t angle = + R_PointToAngle2(0, 0, line->dx, line->dy) - m->angle + ANG90; + + // Sine, cosine of angle adjustment + fixed_t s = finesine[angle>>ANGLETOFINESHIFT]; + fixed_t c = finecosine[angle>>ANGLETOFINESHIFT]; + + // Momentum of thing crossing teleporter linedef + fixed_t momx = thing->momx; + fixed_t momy = thing->momy; + + // Whether this is a player, and if so, a pointer to its player_t + player_t *player = thing->player; + + // Attempt to teleport, aborting if blocked + if (!P_TeleportMove(thing, m->x, m->y, false)) /* killough 8/9/98 */ + return 0; + + // Rotate thing according to difference in angles + thing->angle += angle; + + // Adjust z position to be same height above ground as before + thing->z = z + thing->floorz; + + // Rotate thing's momentum to come out of exit just like it entered + thing->momx = FixedMul(momx, c) - FixedMul(momy, s); + thing->momy = FixedMul(momy, c) + FixedMul(momx, s); + + // Adjust player's view, in case there has been a height change + // Voodoo dolls are excluded by making sure player->mo == thing. + if (player && player->mo == thing) + { + // Save the current deltaviewheight, used in stepping + fixed_t deltaviewheight = player->deltaviewheight; + + // Clear deltaviewheight, since we don't want any changes + player->deltaviewheight = 0; + + // Set player's view according to the newly set parameters + P_CalcHeight(player); + + // Reset the delta to have the same dynamics as before + player->deltaviewheight = deltaviewheight; + } + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + return 1; + } + return 0; +} + +// +// Silent linedef-based TELEPORTATION, by Lee Killough +// Primarily for rooms-over-rooms etc. +// This is the complete player-preserving kind of teleporter. +// It has advantages over the teleporter with thing exits. +// + +// maximum fixed_t units to move object to avoid hiccups +#define FUDGEFACTOR 10 + +int EV_SilentLineTeleport(line_t *line, int side, mobj_t *thing, + boolean reverse) +{ + int i; + line_t *l; + + if (side || thing->flags & MF_MISSILE) + return 0; + + for (i = -1; (i = P_FindLineFromLineTag(line, i)) >= 0;) + if ((l=lines+i) != line && l->backsector) + { + // Get the thing's position along the source linedef + fixed_t pos = D_abs(line->dx) > D_abs(line->dy) ? + FixedDiv(thing->x - line->v1->x, line->dx) : + FixedDiv(thing->y - line->v1->y, line->dy) ; + + // Get the angle between the two linedefs, for rotating + // orientation and momentum. Rotate 180 degrees, and flip + // the position across the exit linedef, if reversed. + angle_t angle = (reverse ? pos = FRACUNIT-pos, 0 : ANG180) + + R_PointToAngle2(0, 0, l->dx, l->dy) - + R_PointToAngle2(0, 0, line->dx, line->dy); + + // Interpolate position across the exit linedef + fixed_t x = l->v2->x - FixedMul(pos, l->dx); + fixed_t y = l->v2->y - FixedMul(pos, l->dy); + + // Sine, cosine of angle adjustment + fixed_t s = finesine[angle>>ANGLETOFINESHIFT]; + fixed_t c = finecosine[angle>>ANGLETOFINESHIFT]; + + // Maximum distance thing can be moved away from interpolated + // exit, to ensure that it is on the correct side of exit linedef + int fudge = FUDGEFACTOR; + + // Whether this is a player, and if so, a pointer to its player_t. + // Voodoo dolls are excluded by making sure thing->player->mo==thing. + player_t *player = thing->player && thing->player->mo == thing ? + thing->player : NULL; + + // Whether walking towards first side of exit linedef steps down + int stepdown = + l->frontsector->floorheight < l->backsector->floorheight; + + // Height of thing above ground + fixed_t z = thing->z - thing->floorz; + + // Side to exit the linedef on positionally. + // + // Notes: + // + // This flag concerns exit position, not momentum. Due to + // roundoff error, the thing can land on either the left or + // the right side of the exit linedef, and steps must be + // taken to make sure it does not end up on the wrong side. + // + // Exit momentum is always towards side 1 in a reversed + // teleporter, and always towards side 0 otherwise. + // + // Exiting positionally on side 1 is always safe, as far + // as avoiding oscillations and stuck-in-wall problems, + // but may not be optimum for non-reversed teleporters. + // + // Exiting on side 0 can cause oscillations if momentum + // is towards side 1, as it is with reversed teleporters. + // + // Exiting on side 1 slightly improves player viewing + // when going down a step on a non-reversed teleporter. + + int side = reverse || (player && stepdown); + + // Make sure we are on correct side of exit linedef. + while (P_PointOnLineSide(x, y, l) != side && --fudge>=0) + if (D_abs(l->dx) > D_abs(l->dy)) + y -= ((l->dx < 0) != side) ? -1 : 1; + else + x += ((l->dy < 0) != side) ? -1 : 1; + + // Attempt to teleport, aborting if blocked + if (!P_TeleportMove(thing, x, y, false)) /* killough 8/9/98 */ + return 0; + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + // Adjust z position to be same height above ground as before. + // Ground level at the exit is measured as the higher of the + // two floor heights at the exit linedef. + thing->z = z + sides[l->sidenum[stepdown]].sector->floorheight; + + // Rotate thing's orientation according to difference in linedef angles + thing->angle += angle; + + // Momentum of thing crossing teleporter linedef + x = thing->momx; + y = thing->momy; + + // Rotate thing's momentum to come out of exit just like it entered + thing->momx = FixedMul(x, c) - FixedMul(y, s); + thing->momy = FixedMul(y, c) + FixedMul(x, s); + + // Adjust a player's view, in case there has been a height change + if (player) + { + // Save the current deltaviewheight, used in stepping + fixed_t deltaviewheight = player->deltaviewheight; + + // Clear deltaviewheight, since we don't want any changes now + player->deltaviewheight = 0; + + // Set player's view according to the newly set parameters + P_CalcHeight(player); + + // Reset the delta to have the same dynamics as before + player->deltaviewheight = deltaviewheight; + } + + // e6y + if (player && player->mo == thing) + R_ResetAfterTeleport(player); + + return 1; + } + return 0; +} diff --git a/src/p_tick.c b/src/p_tick.c new file mode 100644 index 00000000..60460467 --- /dev/null +++ b/src/p_tick.c @@ -0,0 +1,291 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000,2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Thinker, Ticker. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "p_user.h" +#include "p_spec.h" +#include "p_tick.h" +#include "p_map.h" +#include "r_fps.h" + +int leveltime; + +static boolean newthinkerpresent; + +// +// THINKERS +// All thinkers should be allocated by Z_Malloc +// so they can be operated on uniformly. +// The actual structures will vary in size, +// but the first element must be thinker_t. +// + +// killough 8/29/98: we maintain several separate threads, each containing +// a special class of thinkers, to allow more efficient searches. +thinker_t thinkerclasscap[th_all+1]; + +// +// P_InitThinkers +// + +void P_InitThinkers(void) +{ + int i; + + for (i=0; ifunction == P_RemoveThinkerDelayed ? th_delete : + thinker->function == P_MobjThinker && + ((mobj_t *) thinker)->health > 0 && + (((mobj_t *) thinker)->flags & MF_COUNTKILL || + ((mobj_t *) thinker)->type == MT_SKULL) ? + ((mobj_t *) thinker)->flags & MF_FRIEND ? + th_friends : th_enemies : th_misc; + + { + /* Remove from current thread, if in one */ + if ((th = thinker->cnext)!= NULL) + (th->cprev = thinker->cprev)->cnext = th; + } + + // Add to appropriate thread + th = &thinkerclasscap[class]; + th->cprev->cnext = thinker; + thinker->cnext = th; + thinker->cprev = th->cprev; + th->cprev = thinker; +} + +// +// P_AddThinker +// Adds a new thinker at the end of the list. +// + +void P_AddThinker(thinker_t* thinker) +{ + thinkercap.prev->next = thinker; + thinker->next = &thinkercap; + thinker->prev = thinkercap.prev; + thinkercap.prev = thinker; + + thinker->references = 0; // killough 11/98: init reference counter to 0 + + // killough 8/29/98: set sentinel pointers, and then add to appropriate list + thinker->cnext = thinker->cprev = NULL; + P_UpdateThinker(thinker); + newthinkerpresent = true; +} + +// +// killough 11/98: +// +// Make currentthinker external, so that P_RemoveThinkerDelayed +// can adjust currentthinker when thinkers self-remove. + +static thinker_t *currentthinker; + +// +// P_RemoveThinkerDelayed() +// +// Called automatically as part of the thinker loop in P_RunThinkers(), +// on nodes which are pending deletion. +// +// If this thinker has no more pointers referencing it indirectly, +// remove it, and set currentthinker to one node preceeding it, so +// that the next step in P_RunThinkers() will get its successor. +// + +void P_RemoveThinkerDelayed(thinker_t *thinker) +{ + if (!thinker->references) + { + { /* Remove from main thinker list */ + thinker_t *next = thinker->next; + /* Note that currentthinker is guaranteed to point to us, + * and since we're freeing our memory, we had better change that. So + * point it to thinker->prev, so the iterator will correctly move on to + * thinker->prev->next = thinker->next */ + (next->prev = currentthinker = thinker->prev)->next = next; + } + { + /* Remove from current thinker class list */ + thinker_t *th = thinker->cnext; + (th->cprev = thinker->cprev)->cnext = th; + } + Z_Free(thinker); + } +} + +// +// P_RemoveThinker +// +// Deallocation is lazy -- it will not actually be freed +// until its thinking turn comes up. +// +// killough 4/25/98: +// +// Instead of marking the function with -1 value cast to a function pointer, +// set the function to P_RemoveThinkerDelayed(), so that later, it will be +// removed automatically as part of the thinker process. +// + +void P_RemoveThinker(thinker_t *thinker) +{ + R_StopInterpolationIfNeeded(thinker); + thinker->function = P_RemoveThinkerDelayed; + + P_UpdateThinker(thinker); +} + +/* cph 2002/01/13 - iterator for thinker list + * WARNING: Do not modify thinkers between calls to this functin + */ +thinker_t* P_NextThinker(thinker_t* th, th_class cl) +{ + thinker_t* top = &thinkerclasscap[cl]; + if (!th) th = top; + th = cl == th_all ? th->next : th->cnext; + return th == top ? NULL : th; +} + +/* + * P_SetTarget + * + * This function is used to keep track of pointer references to mobj thinkers. + * In Doom, objects such as lost souls could sometimes be removed despite + * their still being referenced. In Boom, 'target' mobj fields were tested + * during each gametic, and any objects pointed to by them would be prevented + * from being removed. But this was incomplete, and was slow (every mobj was + * checked during every gametic). Now, we keep a count of the number of + * references, and delay removal until the count is 0. + */ + +void P_SetTarget(mobj_t **mop, mobj_t *targ) +{ + if (*mop) // If there was a target already, decrease its refcount + (*mop)->thinker.references--; + if ((*mop = targ)) // Set new target and if non-NULL, increase its counter + targ->thinker.references++; +} + +// +// P_RunThinkers +// +// killough 4/25/98: +// +// Fix deallocator to stop using "next" pointer after node has been freed +// (a Doom bug). +// +// Process each thinker. For thinkers which are marked deleted, we must +// load the "next" pointer prior to freeing the node. In Doom, the "next" +// pointer was loaded AFTER the thinker was freed, which could have caused +// crashes. +// +// But if we are not deleting the thinker, we should reload the "next" +// pointer after calling the function, in case additional thinkers are +// added at the end of the list. +// +// killough 11/98: +// +// Rewritten to delete nodes implicitly, by making currentthinker +// external and using P_RemoveThinkerDelayed() implicitly. +// + +static void P_RunThinkers (void) +{ + for (currentthinker = thinkercap.next; + currentthinker != &thinkercap; + currentthinker = currentthinker->next) + { + if (newthinkerpresent) + R_ActivateThinkerInterpolations(currentthinker); + if (currentthinker->function) + currentthinker->function(currentthinker); + } + newthinkerpresent = false; +} + +// +// P_Ticker +// + +void P_Ticker (void) +{ + int i; + + /* pause if in menu and at least one tic has been run + * + * killough 9/29/98: note that this ties in with basetic, + * since G_Ticker does the pausing during recording or + * playback, and compenates by incrementing basetic. + * + * All of this complicated mess is used to preserve demo sync. + */ + + if (paused || (menuactive && !demoplayback && !netgame && + players[consoleplayer].viewz != 1)) + return; + + R_UpdateInterpolations (); + + P_MapStart(); + // not if this is an intermission screen + if(gamestate==GS_LEVEL) + for (i=0; i>= ANGLETOFINESHIFT; + player->mo->momx += FixedMul(move,finecosine[angle]); + player->mo->momy += FixedMul(move,finesine[angle]); +} + + +/* + * P_Bob + * Same as P_Thrust, but only affects bobbing. + * + * killough 10/98: We apply thrust separately between the real physical player + * and the part which affects bobbing. This way, bobbing only comes from player + * motion, nothing external, avoiding many problems, e.g. bobbing should not + * occur on conveyors, unless the player walks on one, and bobbing should be + * reduced at a regular rate, even on ice (where the player coasts). + */ + +static void P_Bob(player_t *player, angle_t angle, fixed_t move) +{ + //e6y + if (!mbf_features) + return; + + player->momx += FixedMul(move,finecosine[angle >>= ANGLETOFINESHIFT]); + player->momy += FixedMul(move,finesine[angle]); +} + +// +// P_CalcHeight +// Calculate the walking / running height adjustment +// + +void P_CalcHeight (player_t* player) +{ + int angle; + fixed_t bob; + + // Regular movement bobbing + // (needs to be calculated for gun swing + // even if not on ground) + // OPTIMIZE: tablify angle + // Note: a LUT allows for effects + // like a ramp with low health. + + + /* killough 10/98: Make bobbing depend only on player-applied motion. + * + * Note: don't reduce bobbing here if on ice: if you reduce bobbing here, + * it causes bobbing jerkiness when the player moves from ice to non-ice, + * and vice-versa. + */ + if (!demo_compatibility && !player_bobbing) + player->bob = 0; + else + { + fixed_t x = mbf_features ? player->momx : player->mo->momx; + fixed_t y = mbf_features ? player->momy : player->mo->momy; + + player->bob = (FixedMul(x,x) + FixedMul(y,y)) >> 2; + } + + //e6y + if (compatibility_level >= boom_202_compatibility && + compatibility_level <= lxdoom_1_compatibility && + player->mo->friction > ORIG_FRICTION) // ice? + { + if (player->bob > (MAXBOB>>2)) + player->bob = MAXBOB>>2; + } + else + + if (player->bob > MAXBOB) + player->bob = MAXBOB; + + if (!onground || player->cheats & CF_NOMOMENTUM) + { + player->viewz = player->mo->z + VIEWHEIGHT; + + if (player->viewz > player->mo->ceilingz-4*FRACUNIT) + player->viewz = player->mo->ceilingz-4*FRACUNIT; + +// The following line was in the Id source and appears // phares 2/25/98 +// to be a bug. player->viewz is checked in a similar +// manner at a different exit below. + +// player->viewz = player->mo->z + player->viewheight; + return; + } + + angle = (FINEANGLES/20*leveltime)&FINEMASK; + bob = FixedMul(player->bob/2,finesine[angle]); + + // move viewheight + + if (player->playerstate == PST_LIVE) + { + player->viewheight += player->deltaviewheight; + + if (player->viewheight > VIEWHEIGHT) + { + player->viewheight = VIEWHEIGHT; + player->deltaviewheight = 0; + } + + if (player->viewheight < VIEWHEIGHT/2) + { + player->viewheight = VIEWHEIGHT/2; + if (player->deltaviewheight <= 0) + player->deltaviewheight = 1; + } + + if (player->deltaviewheight) + { + player->deltaviewheight += FRACUNIT/4; + if (!player->deltaviewheight) + player->deltaviewheight = 1; + } + } + + player->viewz = player->mo->z + player->viewheight + bob; + + if (player->viewz > player->mo->ceilingz-4*FRACUNIT) + player->viewz = player->mo->ceilingz-4*FRACUNIT; +} + + +// +// P_MovePlayer +// +// Adds momentum if the player is not in the air +// +// killough 10/98: simplified + +void P_MovePlayer (player_t* player) +{ + ticcmd_t *cmd = &player->cmd; + mobj_t *mo = player->mo; + + mo->angle += cmd->angleturn << 16; + onground = mo->z <= mo->floorz; + + // e6y + if (demo_smoothturns && player == &players[displayplayer]) + R_SmoothPlaying_Add(cmd->angleturn << 16); + + // killough 10/98: + // + // We must apply thrust to the player and bobbing separately, to avoid + // anomalies. The thrust applied to bobbing is always the same strength on + // ice, because the player still "works just as hard" to move, while the + // thrust applied to the movement varies with 'movefactor'. + + //e6y + if ((!demo_compatibility && !mbf_features) || (cmd->forwardmove | cmd->sidemove)) // killough 10/98 + { + if (onground || mo->flags & MF_BOUNCES) // killough 8/9/98 + { + int friction, movefactor = P_GetMoveFactor(mo, &friction); + + // killough 11/98: + // On sludge, make bobbing depend on efficiency. + // On ice, make it depend on effort. + + int bobfactor = + friction < ORIG_FRICTION ? movefactor : ORIG_FRICTION_FACTOR; + + if (cmd->forwardmove) + { + P_Bob(player,mo->angle,cmd->forwardmove*bobfactor); + P_Thrust(player,mo->angle,cmd->forwardmove*movefactor); + } + + if (cmd->sidemove) + { + P_Bob(player,mo->angle-ANG90,cmd->sidemove*bobfactor); + P_Thrust(player,mo->angle-ANG90,cmd->sidemove*movefactor); + } + } + if (mo->state == states+S_PLAY) + P_SetMobjState(mo,S_PLAY_RUN1); + } +} + +#define ANG5 (ANG90/18) + +// +// P_DeathThink +// Fall on your face when dying. +// Decrease POV height to floor height. +// + +void P_DeathThink (player_t* player) +{ + angle_t angle; + angle_t delta; + + P_MovePsprites (player); + + // fall to the ground + + if (player->viewheight > 6*FRACUNIT) + player->viewheight -= FRACUNIT; + + if (player->viewheight < 6*FRACUNIT) + player->viewheight = 6*FRACUNIT; + + player->deltaviewheight = 0; + onground = (player->mo->z <= player->mo->floorz); + P_CalcHeight (player); + + if (player->attacker && player->attacker != player->mo) + { + angle = R_PointToAngle2 (player->mo->x, + player->mo->y, + player->attacker->x, + player->attacker->y); + + delta = angle - player->mo->angle; + + if (delta < ANG5 || delta > (unsigned)-ANG5) + { + // Looking at killer, + // so fade damage flash down. + + player->mo->angle = angle; + + if (player->damagecount) + player->damagecount--; + } + else if (delta < ANG180) + player->mo->angle += ANG5; + else + player->mo->angle -= ANG5; + } + else if (player->damagecount) + player->damagecount--; + + if (player->cmd.buttons & BT_USE) + player->playerstate = PST_REBORN; + R_SmoothPlaying_Reset(player); // e6y +} + + +// +// P_PlayerThink +// + +void P_PlayerThink (player_t* player) +{ + ticcmd_t* cmd; + weapontype_t newweapon; + + if (movement_smooth && &players[displayplayer] == player) + { + original_view_vars.viewx = player->mo->x; + original_view_vars.viewy = player->mo->y; + original_view_vars.viewz = player->viewz; + original_view_vars.viewangle = R_SmoothPlaying_Get(player->mo->angle) + viewangleoffset; + } + + // killough 2/8/98, 3/21/98: + if (player->cheats & CF_NOCLIP) + player->mo->flags |= MF_NOCLIP; + else + player->mo->flags &= ~MF_NOCLIP; + + // chain saw run forward + + cmd = &player->cmd; + if (player->mo->flags & MF_JUSTATTACKED) + { + cmd->angleturn = 0; + cmd->forwardmove = 0xc800/512; + cmd->sidemove = 0; + player->mo->flags &= ~MF_JUSTATTACKED; + } + + if (player->playerstate == PST_DEAD) + { + P_DeathThink (player); + return; + } + + // Move around. + // Reactiontime is used to prevent movement + // for a bit after a teleport. + + if (player->mo->reactiontime) + player->mo->reactiontime--; + else + P_MovePlayer (player); + + P_CalcHeight (player); // Determines view height and bobbing + + // Determine if there's anything about the sector you're in that's + // going to affect you, like painful floors. + + if (player->mo->subsector->sector->special) + P_PlayerInSpecialSector (player); + + // Check for weapon change. + + if (cmd->buttons & BT_CHANGE) + { + // The actual changing of the weapon is done + // when the weapon psprite can do it + // (read: not in the middle of an attack). + + newweapon = (cmd->buttons & BT_WEAPONMASK)>>BT_WEAPONSHIFT; + + // killough 3/22/98: For demo compatibility we must perform the fist + // and SSG weapons switches here, rather than in G_BuildTiccmd(). For + // other games which rely on user preferences, we must use the latter. + + if (demo_compatibility) + { // compatibility mode -- required for old demos -- killough + if (newweapon == wp_fist && player->weaponowned[wp_chainsaw] && + (player->readyweapon != wp_chainsaw || + !player->powers[pw_strength])) + newweapon = wp_chainsaw; + if (gamemode == commercial && + newweapon == wp_shotgun && + player->weaponowned[wp_supershotgun] && + player->readyweapon != wp_supershotgun) + newweapon = wp_supershotgun; + } + + // killough 2/8/98, 3/22/98 -- end of weapon selection changes + + if (player->weaponowned[newweapon] && newweapon != player->readyweapon) + + // Do not go to plasma or BFG in shareware, + // even if cheated. + + if ((newweapon != wp_plasma && newweapon != wp_bfg) + || (gamemode != shareware) ) + player->pendingweapon = newweapon; + } + + // check for use + + if (cmd->buttons & BT_USE) + { + if (!player->usedown) + { + P_UseLines (player); + player->usedown = true; + } + } + else + player->usedown = false; + + // cycle psprites + + P_MovePsprites (player); + + // Counters, time dependent power ups. + + // Strength counts up to diminish fade. + + if (player->powers[pw_strength]) + player->powers[pw_strength]++; + + // killough 1/98: Make idbeholdx toggle: + + if (player->powers[pw_invulnerability] > 0) // killough + player->powers[pw_invulnerability]--; + + if (player->powers[pw_invisibility] > 0) // killough + if (! --player->powers[pw_invisibility] ) + player->mo->flags &= ~MF_SHADOW; + + if (player->powers[pw_infrared] > 0) // killough + player->powers[pw_infrared]--; + + if (player->powers[pw_ironfeet] > 0) // killough + player->powers[pw_ironfeet]--; + + if (player->damagecount) + player->damagecount--; + + if (player->bonuscount) + player->bonuscount--; + + // Handling colormaps. + // killough 3/20/98: reformat to terse C syntax + + player->fixedcolormap = player->powers[pw_invulnerability] > 4*32 || + player->powers[pw_invulnerability] & 8 ? INVERSECOLORMAP : + player->powers[pw_infrared] > 4*32 || player->powers[pw_infrared] & 8; +} diff --git a/src/p_user.h b/src/p_user.h new file mode 100644 index 00000000..b0540e89 --- /dev/null +++ b/src/p_user.h @@ -0,0 +1,47 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Player related stuff. + * Bobbing POV/weapon, movement. + * Pending weapon. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __P_USER__ +#define __P_USER__ + +#include "d_player.h" + +void P_PlayerThink(player_t *player); +void P_CalcHeight(player_t *player); +void P_DeathThink(player_t *player); +void P_MovePlayer(player_t *player); +void P_Thrust(player_t *player, angle_t angle, fixed_t move); + +#endif /* __P_USER__ */ diff --git a/src/prboom.wad b/src/prboom.wad new file mode 100644 index 0000000000000000000000000000000000000000..8e2f81e5d68ed5ca1886be59e6c4372a9a172d39 GIT binary patch literal 228743 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 NeLefftic = doom_htonl(tic); p->type = t; p->reserved[0] = 0; p->reserved[1] = 0; } + +#ifndef GAME_OPTIONS_SIZE +// From g_game.h +#define GAME_OPTIONS_SIZE 64 +#endif + +struct setup_packet_s { + byte players, yourplayer, skill, episode, level, deathmatch, complevel, ticdup, extratic; + byte game_options[GAME_OPTIONS_SIZE]; + byte numwads; + byte wadnames[1]; // Actually longer +}; + +/* cph - convert network byte stream to usable ticcmd_t and visa-versa + * - the functions are functionally identical apart from parameters + * - the void* param can be unaligned. By using void* as the parameter + * it means gcc won't assume alignment so won't make false assumptions + * when optimising. So I'm told. + */ +inline static void RawToTic(ticcmd_t* dst, const void* src) +{ + memcpy(dst,src,sizeof *dst); + dst->angleturn = doom_ntohs(dst->angleturn); + dst->consistancy = doom_ntohs(dst->consistancy); +} + +inline static void TicToRaw(void* dst, const ticcmd_t* src) +{ + /* We have to make a copy of the source struct, then do byte swaps, + * and fnially copy to the destination (can't do the swaps in the + * destination, because it might not be aligned). + */ + ticcmd_t tmp = *src; + tmp.angleturn = doom_ntohs(tmp.angleturn); + tmp.consistancy = doom_ntohs(tmp.consistancy); + memcpy(dst,&tmp,sizeof tmp); +} diff --git a/src/r_bsp.c b/src/r_bsp.c new file mode 100644 index 00000000..a21e8749 --- /dev/null +++ b/src/r_bsp.c @@ -0,0 +1,565 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * BSP traversal, handling of LineSegs for rendering. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "m_bbox.h" +#include "r_main.h" +#include "r_segs.h" +#include "r_plane.h" +#include "r_things.h" +#include "r_bsp.h" // cph - sanity checking +#include "v_video.h" +#include "lprintf.h" + +seg_t *curline; +side_t *sidedef; +line_t *linedef; +sector_t *frontsector; +sector_t *backsector; +drawseg_t *ds_p; + +// killough 4/7/98: indicates doors closed wrt automap bugfix: +// cph - replaced by linedef rendering flags - int doorclosed; + +// killough: New code which removes 2s linedef limit +drawseg_t *drawsegs; +unsigned maxdrawsegs; +// drawseg_t drawsegs[MAXDRAWSEGS]; // old code -- killough + +// +// R_ClearDrawSegs +// + +void R_ClearDrawSegs(void) +{ + ds_p = drawsegs; +} + +// CPhipps - +// Instead of clipsegs, let's try using an array with one entry for each column, +// indicating whether it's blocked by a solid wall yet or not. + +byte solidcol[MAX_SCREENWIDTH]; + +// CPhipps - +// R_ClipWallSegment +// +// Replaces the old R_Clip*WallSegment functions. It draws bits of walls in those +// columns which aren't solid, and updates the solidcol[] array appropriately + +static void R_ClipWallSegment(int first, int last, boolean solid) +{ + byte *p; + while (first < last) { + if (solidcol[first]) { + if (!(p = memchr(solidcol+first, 0, last-first))) return; // All solid + first = p - solidcol; + } else { + int to; + if (!(p = memchr(solidcol+first, 1, last-first))) to = last; + else to = p - solidcol; + R_StoreWallRange(first, to-1); + if (solid) { + memset(solidcol+first,1,to-first); + } + first = to; + } + } +} + +// +// R_ClearClipSegs +// + +void R_ClearClipSegs (void) +{ + memset(solidcol, 0, SCREENWIDTH); +} + +// killough 1/18/98 -- This function is used to fix the automap bug which +// showed lines behind closed doors simply because the door had a dropoff. +// +// cph - converted to R_RecalcLineFlags. This recalculates all the flags for +// a line, including closure and texture tiling. + +static void R_RecalcLineFlags(void) +{ + linedef->r_validcount = gametic; + + /* First decide if the line is closed, normal, or invisible */ + if (!(linedef->flags & ML_TWOSIDED) + || backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight + || ( + // if door is closed because back is shut: + backsector->ceilingheight <= backsector->floorheight + + // preserve a kind of transparent door/lift special effect: + && (backsector->ceilingheight >= frontsector->ceilingheight || + curline->sidedef->toptexture) + + && (backsector->floorheight <= frontsector->floorheight || + curline->sidedef->bottomtexture) + + // properly render skies (consider door "open" if both ceilings are sky): + && (backsector->ceilingpic !=skyflatnum || + frontsector->ceilingpic!=skyflatnum) + ) + ) + linedef->r_flags = RF_CLOSED; + else { + // Reject empty lines used for triggers + // and special events. + // Identical floor and ceiling on both sides, + // identical light levels on both sides, + // and no middle texture. + // CPhipps - recode for speed, not certain if this is portable though + if (backsector->ceilingheight != frontsector->ceilingheight + || backsector->floorheight != frontsector->floorheight + || curline->sidedef->midtexture + || memcmp(&backsector->floor_xoffs, &frontsector->floor_xoffs, + sizeof(frontsector->floor_xoffs) + sizeof(frontsector->floor_yoffs) + + sizeof(frontsector->ceiling_xoffs) + sizeof(frontsector->ceiling_yoffs) + + sizeof(frontsector->ceilingpic) + sizeof(frontsector->floorpic) + + sizeof(frontsector->lightlevel) + sizeof(frontsector->floorlightsec) + + sizeof(frontsector->ceilinglightsec))) { + linedef->r_flags = 0; return; + } else + linedef->r_flags = RF_IGNORE; + } + + /* cph - I'm too lazy to try and work with offsets in this */ + if (curline->sidedef->rowoffset) return; + + /* Now decide on texture tiling */ + if (linedef->flags & ML_TWOSIDED) { + int c; + + /* Does top texture need tiling */ + if ((c = frontsector->ceilingheight - backsector->ceilingheight) > 0 && + (textureheight[texturetranslation[curline->sidedef->toptexture]] > c)) + linedef->r_flags |= RF_TOP_TILE; + + /* Does bottom texture need tiling */ + if ((c = frontsector->floorheight - backsector->floorheight) > 0 && + (textureheight[texturetranslation[curline->sidedef->bottomtexture]] > c)) + linedef->r_flags |= RF_BOT_TILE; + } else { + int c; + /* Does middle texture need tiling */ + if ((c = frontsector->ceilingheight - frontsector->floorheight) > 0 && + (textureheight[texturetranslation[curline->sidedef->midtexture]] > c)) + linedef->r_flags |= RF_MID_TILE; + } +} + +// +// killough 3/7/98: Hack floor/ceiling heights for deep water etc. +// +// If player's view height is underneath fake floor, lower the +// drawn ceiling to be just under the floor height, and replace +// the drawn floor and ceiling textures, and light level, with +// the control sector's. +// +// Similar for ceiling, only reflected. +// +// killough 4/11/98, 4/13/98: fix bugs, add 'back' parameter +// + +sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, + int *floorlightlevel, int *ceilinglightlevel, + boolean back) +{ + if (floorlightlevel) + *floorlightlevel = sec->floorlightsec == -1 ? + sec->lightlevel : sectors[sec->floorlightsec].lightlevel; + + if (ceilinglightlevel) + *ceilinglightlevel = sec->ceilinglightsec == -1 ? // killough 4/11/98 + sec->lightlevel : sectors[sec->ceilinglightsec].lightlevel; + + if (sec->heightsec != -1) + { + const sector_t *s = §ors[sec->heightsec]; + int heightsec = viewplayer->mo->subsector->sector->heightsec; + int underwater = heightsec!=-1 && viewz<=sectors[heightsec].floorheight; + + // Replace sector being drawn, with a copy to be hacked + *tempsec = *sec; + + // Replace floor and ceiling height with other sector's heights. + tempsec->floorheight = s->floorheight; + tempsec->ceilingheight = s->ceilingheight; + + // killough 11/98: prevent sudden light changes from non-water sectors: + if (underwater && (tempsec-> floorheight = sec->floorheight, + tempsec->ceilingheight = s->floorheight-1, !back)) + { // head-below-floor hack + tempsec->floorpic = s->floorpic; + tempsec->floor_xoffs = s->floor_xoffs; + tempsec->floor_yoffs = s->floor_yoffs; + + if (underwater) { + if (s->ceilingpic == skyflatnum) { + tempsec->floorheight = tempsec->ceilingheight+1; + tempsec->ceilingpic = tempsec->floorpic; + tempsec->ceiling_xoffs = tempsec->floor_xoffs; + tempsec->ceiling_yoffs = tempsec->floor_yoffs; + } else { + tempsec->ceilingpic = s->ceilingpic; + tempsec->ceiling_xoffs = s->ceiling_xoffs; + tempsec->ceiling_yoffs = s->ceiling_yoffs; + } + } + + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel) + *floorlightlevel = s->floorlightsec == -1 ? s->lightlevel : + sectors[s->floorlightsec].lightlevel; // killough 3/16/98 + + if (ceilinglightlevel) + *ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel : + sectors[s->ceilinglightsec].lightlevel; // killough 4/11/98 + } + else + if (heightsec != -1 && viewz >= sectors[heightsec].ceilingheight && + sec->ceilingheight > s->ceilingheight) + { // Above-ceiling hack + tempsec->ceilingheight = s->ceilingheight; + tempsec->floorheight = s->ceilingheight + 1; + + tempsec->floorpic = tempsec->ceilingpic = s->ceilingpic; + tempsec->floor_xoffs = tempsec->ceiling_xoffs = s->ceiling_xoffs; + tempsec->floor_yoffs = tempsec->ceiling_yoffs = s->ceiling_yoffs; + + if (s->floorpic != skyflatnum) + { + tempsec->ceilingheight = sec->ceilingheight; + tempsec->floorpic = s->floorpic; + tempsec->floor_xoffs = s->floor_xoffs; + tempsec->floor_yoffs = s->floor_yoffs; + } + + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel) + *floorlightlevel = s->floorlightsec == -1 ? s->lightlevel : + sectors[s->floorlightsec].lightlevel; // killough 3/16/98 + + if (ceilinglightlevel) + *ceilinglightlevel = s->ceilinglightsec == -1 ? s->lightlevel : + sectors[s->ceilinglightsec].lightlevel; // killough 4/11/98 + } + sec = tempsec; // Use other sector + } + return sec; +} + +// +// R_AddLine +// Clips the given segment +// and adds any visible pieces to the line list. +// + +static void R_AddLine (seg_t *line) +{ + int x1; + int x2; + angle_t angle1; + angle_t angle2; + angle_t span; + angle_t tspan; + static sector_t tempsec; // killough 3/8/98: ceiling/water hack + + curline = line; + + angle1 = R_PointToAngle (line->v1->x, line->v1->y); + angle2 = R_PointToAngle (line->v2->x, line->v2->y); + + // Clip to view edges. + span = angle1 - angle2; + + // Back side, i.e. backface culling + if (span >= ANG180) + return; + + // Global angle needed by segcalc. + rw_angle1 = angle1; + angle1 -= viewangle; + angle2 -= viewangle; + + tspan = angle1 + clipangle; + if (tspan > 2*clipangle) + { + tspan -= 2*clipangle; + + // Totally off the left edge? + if (tspan >= span) + return; + + angle1 = clipangle; + } + + tspan = clipangle - angle2; + if (tspan > 2*clipangle) + { + tspan -= 2*clipangle; + + // Totally off the left edge? + if (tspan >= span) + return; + angle2 = 0-clipangle; + } + + // The seg is in the view range, + // but not necessarily visible. + + angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; + angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; + + // killough 1/31/98: Here is where "slime trails" can SOMETIMES occur: + x1 = viewangletox[angle1]; + x2 = viewangletox[angle2]; + + // Does not cross a pixel? + if (x1 >= x2) // killough 1/31/98 -- change == to >= for robustness + return; + + backsector = line->backsector; + + // Single sided line? + if (backsector) + // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water + backsector = R_FakeFlat(backsector, &tempsec, NULL, NULL, true); + + /* cph - roll up linedef properties in flags */ + if ((linedef = curline->linedef)->r_validcount != gametic) + R_RecalcLineFlags(); + + if (linedef->r_flags & RF_IGNORE) + { + return; + } + else + R_ClipWallSegment (x1, x2, linedef->r_flags & RF_CLOSED); +} + +// +// R_CheckBBox +// Checks BSP node/subtree bounding box. +// Returns true +// if some part of the bbox might be visible. +// + +static const int checkcoord[12][4] = // killough -- static const +{ + {3,0,2,1}, + {3,0,2,0}, + {3,1,2,0}, + {0}, + {2,0,2,1}, + {0,0,0,0}, + {3,1,3,0}, + {0}, + {2,0,3,1}, + {2,1,3,1}, + {2,1,3,0} +}; + +// killough 1/28/98: static // CPhipps - const parameter, reformatted +static boolean R_CheckBBox(const fixed_t *bspcoord) +{ + angle_t angle1, angle2; + + { + int boxpos; + const int* check; + + // Find the corners of the box + // that define the edges from current viewpoint. + boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 : viewx < bspcoord[BOXRIGHT ] ? 1 : 2) + + (viewy >= bspcoord[BOXTOP ] ? 0 : viewy > bspcoord[BOXBOTTOM] ? 4 : 8); + + if (boxpos == 5) + return true; + + check = checkcoord[boxpos]; + angle1 = R_PointToAngle (bspcoord[check[0]], bspcoord[check[1]]) - viewangle; + angle2 = R_PointToAngle (bspcoord[check[2]], bspcoord[check[3]]) - viewangle; + } + + // cph - replaced old code, which was unclear and badly commented + // Much more efficient code now + if ((signed)angle1 < (signed)angle2) { /* it's "behind" us */ + /* Either angle1 or angle2 is behind us, so it doesn't matter if we + * change it to the corect sign + */ + if ((angle1 >= ANG180) && (angle1 < ANG270)) + angle1 = INT_MAX; /* which is ANG180-1 */ + else + angle2 = INT_MIN; + } + + if ((signed)angle2 >= (signed)clipangle) return false; // Both off left edge + if ((signed)angle1 <= -(signed)clipangle) return false; // Both off right edge + if ((signed)angle1 >= (signed)clipangle) angle1 = clipangle; // Clip at left edge + if ((signed)angle2 <= -(signed)clipangle) angle2 = 0-clipangle; // Clip at right edge + + // Find the first clippost + // that touches the source post + // (adjacent pixels are touching). + angle1 = (angle1+ANG90)>>ANGLETOFINESHIFT; + angle2 = (angle2+ANG90)>>ANGLETOFINESHIFT; + { + int sx1 = viewangletox[angle1]; + int sx2 = viewangletox[angle2]; + // const cliprange_t *start; + + // Does not cross a pixel. + if (sx1 == sx2) + return false; + + if (!memchr(solidcol+sx1, 0, sx2-sx1)) return false; + // All columns it covers are already solidly covered + } + + return true; +} + +// +// R_Subsector +// Determine floor/ceiling planes. +// Add sprites of things in sector. +// Draw one or more line segments. +// +// killough 1/31/98 -- made static, polished + +static void R_Subsector(int num) +{ + int count; + seg_t *line; + subsector_t *sub; + sector_t tempsec; // killough 3/7/98: deep water hack + int floorlightlevel; // killough 3/16/98: set floor lightlevel + int ceilinglightlevel; // killough 4/11/98 + + sub = &subsectors[num]; + frontsector = sub->sector; + count = sub->numlines; + line = &segs[sub->firstline]; + + // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect + frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, + &ceilinglightlevel, false); // killough 4/11/98 + + // killough 3/7/98: Add (x,y) offsets to flats, add deep water check + // killough 3/16/98: add floorlightlevel + // killough 10/98: add support for skies transferred from sidedefs + + floorplane = frontsector->floorheight < viewz || // killough 3/7/98 + (frontsector->heightsec != -1 && + sectors[frontsector->heightsec].ceilingpic == skyflatnum) ? + R_FindPlane(frontsector->floorheight, + frontsector->floorpic == skyflatnum && // kilough 10/98 + frontsector->sky & PL_SKYFLAT ? frontsector->sky : + frontsector->floorpic, + floorlightlevel, // killough 3/16/98 + frontsector->floor_xoffs, // killough 3/7/98 + frontsector->floor_yoffs + ) : NULL; + + ceilingplane = frontsector->ceilingheight > viewz || + frontsector->ceilingpic == skyflatnum || + (frontsector->heightsec != -1 && + sectors[frontsector->heightsec].floorpic == skyflatnum) ? + R_FindPlane(frontsector->ceilingheight, // killough 3/8/98 + frontsector->ceilingpic == skyflatnum && // kilough 10/98 + frontsector->sky & PL_SKYFLAT ? frontsector->sky : + frontsector->ceilingpic, + ceilinglightlevel, // killough 4/11/98 + frontsector->ceiling_xoffs, // killough 3/7/98 + frontsector->ceiling_yoffs + ) : NULL; + + // killough 9/18/98: Fix underwater slowdown, by passing real sector + // instead of fake one. Improve sprite lighting by basing sprite + // lightlevels on floor & ceiling lightlevels in the surrounding area. + // + // 10/98 killough: + // + // NOTE: TeamTNT fixed this bug incorrectly, messing up sprite lighting!!! + // That is part of the 242 effect!!! If you simply pass sub->sector to + // the old code you will not get correct lighting for underwater sprites!!! + // Either you must pass the fake sector and handle validcount here, on the + // real sector, or you must account for the lighting in some other way, + // like passing it as an argument. + + R_AddSprites(sub, (floorlightlevel+ceilinglightlevel)/2); + while (count--) + { + if (line->miniseg == false) + R_AddLine (line); + line++; + curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so R_ColourMap doesn't try using it for other things */ + } +} + +// +// RenderBSPNode +// Renders all subsectors below a given node, +// traversing subtree recursively. +// Just call with BSP root. +// +// killough 5/2/98: reformatted, removed tail recursion + +void R_RenderBSPNode(int bspnum) +{ + while (!(bspnum & NF_SUBSECTOR)) // Found a subsector? + { + const node_t *bsp = &nodes[bspnum]; + + // Decide which side the view point is on. + int side = R_PointOnSide(viewx, viewy, bsp); + // Recursively divide front space. + R_RenderBSPNode(bsp->children[side]); + + // Possibly divide back space. + + if (!R_CheckBBox(bsp->bbox[side^1])) + return; + + bspnum = bsp->children[side^1]; + } + R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); +} diff --git a/src/r_bsp.h b/src/r_bsp.h new file mode 100644 index 00000000..4ba91906 --- /dev/null +++ b/src/r_bsp.h @@ -0,0 +1,64 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh module, BSP traversal and handling. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_BSP__ +#define __R_BSP__ + +#ifdef __GNUG__ +#pragma interface +#endif + +extern seg_t *curline; +extern side_t *sidedef; +extern line_t *linedef; +extern sector_t *frontsector; +extern sector_t *backsector; + +/* old code -- killough: + * extern drawseg_t drawsegs[MAXDRAWSEGS]; + * new code -- killough: */ +extern drawseg_t *drawsegs; +extern unsigned maxdrawsegs; + +extern byte solidcol[MAX_SCREENWIDTH]; + +extern drawseg_t *ds_p; + +void R_ClearClipSegs(void); +void R_ClearDrawSegs(void); +void R_RenderBSPNode(int bspnum); + +/* killough 4/13/98: fake floors/ceilings for deep water / fake ceilings: */ +sector_t *R_FakeFlat(sector_t *, sector_t *, int *, int *, boolean); + +#endif diff --git a/src/r_data.c b/src/r_data.c new file mode 100644 index 00000000..a5937e5f --- /dev/null +++ b/src/r_data.c @@ -0,0 +1,614 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Preparation of data for rendering, + * generation of lookups, caching, retrieval by name. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_draw.h" +#include "r_main.h" +#include "r_sky.h" +#include "i_system.h" +#include "r_bsp.h" +#include "r_things.h" +#include "p_tick.h" +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "p_tick.h" + +// +// Graphics. +// DOOM graphics for walls and sprites +// is stored in vertical runs of opaque pixels (posts). +// A column is composed of zero or more posts, +// a patch or sprite is composed of zero or more columns. +// + +// +// Texture definition. +// Each texture is composed of one or more patches, +// with patches being lumps stored in the WAD. +// The lumps are referenced by number, and patched +// into the rectangular texture space using origin +// and possibly other attributes. +// + +typedef struct +{ + short originx; + short originy; + short patch; + short stepdir; // unused in Doom but might be used in Phase 2 Boom + short colormap; // unused in Doom but might be used in Phase 2 Boom +} PACKEDATTR mappatch_t; + + +typedef struct +{ + char name[8]; + char pad2[4]; // unused + short width; + short height; + char pad[4]; // unused in Doom but might be used in Boom Phase 2 + short patchcount; + mappatch_t patches[1]; +} PACKEDATTR maptexture_t; + +// A maptexturedef_t describes a rectangular texture, which is composed +// of one or more mappatch_t structures that arrange graphic patches. + +// killough 4/17/98: make firstcolormaplump,lastcolormaplump external +int firstcolormaplump, lastcolormaplump; // killough 4/17/98 + +int firstflat, lastflat, numflats; +int firstspritelump, lastspritelump, numspritelumps; +int numtextures; +texture_t **textures; // proff - 04/05/2000 removed static for OpenGL +fixed_t *textureheight; //needed for texture pegging (and TFE fix - killough) +int *flattranslation; // for global animation +int *texturetranslation; + +// +// R_GetTextureColumn +// + +const byte *R_GetTextureColumn(const rpatch_t *texpatch, int col) { + while (col < 0) + col += texpatch->width; + col &= texpatch->widthmask; + + return texpatch->columns[col].pixels; +} + +// +// R_InitTextures +// Initializes the texture list +// with the textures from the world map. +// + +static void R_InitTextures (void) +{ + const maptexture_t *mtexture; + texture_t *texture; + const mappatch_t *mpatch; + texpatch_t *patch; + int i, j; + int maptex_lump[2] = {-1, -1}; + const int *maptex; + const int *maptex1, *maptex2; + char name[9]; + int names_lump; // cph - new wad lump handling + const char *names; // cph - + const char *name_p;// const*'s + int *patchlookup; + int totalwidth; + int nummappatches; + int offset; + int maxoff, maxoff2; + int numtextures1, numtextures2; + const int *directory; + int errors = 0; + + // Load the patch names from pnames.lmp. + name[8] = 0; + names = W_CacheLumpNum(names_lump = W_GetNumForName("PNAMES")); + nummappatches = LONG(*((const int *)names)); + name_p = names+4; + patchlookup = malloc(nummappatches*sizeof(*patchlookup)); // killough + + for (i=0 ; i maxoff) + I_Error("R_InitTextures: Bad texture directory"); + + mtexture = (const maptexture_t *) ( (const byte *)maptex + offset); + + texture = textures[i] = + Z_Malloc(sizeof(texture_t) + + sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1), + PU_STATIC, 0); + + texture->width = SHORT(mtexture->width); + texture->height = SHORT(mtexture->height); + texture->patchcount = SHORT(mtexture->patchcount); + + /* Mattias Engdegrd emailed me of the following explenation of + * why memcpy doesnt work on some systems: + * "I suppose it is the mad unaligned allocation + * going on (and which gcc in some way manages to cope with + * through the __attribute__ ((packed))), and which it forgets + * when optimizing memcpy (to a single word move) since it appears + * to be aligned. Technically a gcc bug, but I can't blame it when + * it's stressed with that amount of + * non-standard nonsense." + * So in short the unaligned struct confuses gcc's optimizer so + * i took the memcpy out alltogether to avoid future problems-Jess + */ + /* The above was #ifndef SPARC, but i got a mail from + * Putera Joseph F NPRI containing: + * I had to use the memcpy function on a sparc machine. The + * other one would give me a core dump. + * cph - I find it hard to believe that sparc memcpy is broken, + * but I don't believe the pointers to memcpy have to be aligned + * either. Use fast memcpy on other machines anyway. + */ +/* + proff - I took this out, because Oli Kraus (olikraus@yahoo.com) told + me the memcpy produced a buserror. Since this function isn't time- + critical I'm using the for loop now. +*/ +/* +#ifndef GCC + memcpy(texture->name, mtexture->name, sizeof(texture->name)); +#else +*/ + { + int j; + for(j=0;jname);j++) + texture->name[j]=mtexture->name[j]; + } +/* #endif */ + + mpatch = mtexture->patches; + patch = texture->patches; + + for (j=0 ; jpatchcount ; j++, mpatch++, patch++) + { + patch->originx = SHORT(mpatch->originx); + patch->originy = SHORT(mpatch->originy); + patch->patch = patchlookup[SHORT(mpatch->patch)]; + if (patch->patch == -1) + { + //jff 8/3/98 use logical output routine + lprintf(LO_ERROR,"\nR_InitTextures: Missing patch %d in texture %.8s", + SHORT(mpatch->patch), texture->name); // killough 4/17/98 + ++errors; + } + } + + for (j=1; j*2 <= texture->width; j<<=1) + ; + texture->widthmask = j-1; + textureheight[i] = texture->height<width; + } + + free(patchlookup); // killough + + for (i=0; i<2; i++) // cph - release the TEXTUREx lumps + if (maptex_lump[i] != -1) + W_UnlockLumpNum(maptex_lump[i]); + + if (errors) + I_Error("R_InitTextures: %d errors", errors); + + if (errors) + I_Error("R_InitTextures: %d errors", errors); + + // Create translation table for global animation. + // killough 4/9/98: make column offsets 32-bit; + // clean up malloc-ing to use sizeof + + texturetranslation = + Z_Malloc((numtextures+1)*sizeof*texturetranslation, PU_STATIC, 0); + + for (i=0 ; iindex = -1; + while (--i >= 0) + { + int j = W_LumpNameHash(textures[i]->name) % (unsigned) numtextures; + textures[i]->next = textures[j]->index; // Prepend to chain + textures[j]->index = i; + } +} + +// +// R_InitFlats +// +static void R_InitFlats(void) +{ + int i; + + firstflat = W_GetNumForName("F_START") + 1; + lastflat = W_GetNumForName("F_END") - 1; + numflats = lastflat - firstflat + 1; + + // Create translation table for global animation. + // killough 4/9/98: make column offsets 32-bit; + // clean up malloc-ing to use sizeof + + flattranslation = + Z_Malloc((numflats+1)*sizeof(*flattranslation), PU_STATIC, 0); + + for (i=0 ; i x ? l : x > u ? u : x); } + +const lighttable_t* R_ColourMap(int lightlevel, fixed_t spryscale) +{ + if (fixedcolormap) return fixedcolormap; + else { + if (curline) { + if (curline->v1->y == curline->v2->y) + lightlevel -= 1 << LIGHTSEGSHIFT; + else + if (curline->v1->x == curline->v2->x) + lightlevel += 1 << LIGHTSEGSHIFT; + } + + lightlevel += extralight << LIGHTSEGSHIFT; + + /* cph 2001/11/17 - + * Work out what colour map to use, remembering to clamp it to the number of + * colour maps we actually have. This formula is basically the one from the + * original source, just brought into one place. The main difference is it + * throws away less precision in the lightlevel half, so it supports 32 + * light levels in WADs compared to Doom's 16. + * + * Note we can make it more accurate if we want - we should keep all the + * precision until the final step, so slight scale differences can count + * against slight light level variations. + */ + return fullcolormap + between(0,NUMCOLORMAPS-1, + ((256-lightlevel)*2*NUMCOLORMAPS/256) - 4 + - (FixedMul(spryscale,pspriteiscale)/2 >> LIGHTSCALESHIFT) + )*256; + } +} + +// +// R_InitData +// Locates all the lumps +// that will be used by all views +// Must be called after W_Init. +// + +void R_InitData(void) +{ + lprintf(LO_INFO, "Textures "); + R_InitTextures(); + lprintf(LO_INFO, "Flats "); + R_InitFlats(); + lprintf(LO_INFO, "Sprites "); + R_InitSpriteLumps(); + R_InitColormaps(); // killough 3/20/98 +} + +// +// R_FlatNumForName +// Retrieval, get a flat number for a flat name. +// +// killough 4/17/98: changed to use ns_flats namespace +// + +int R_FlatNumForName(const char *name) // killough -- const added +{ + int i = (W_CheckNumForName)(name, ns_flats); + if (i == -1) + I_Error("R_FlatNumForName: %.8s not found", name); + return i - firstflat; +} + +// +// R_CheckTextureNumForName +// Check whether texture is available. +// Filter out NoTexture indicator. +// +// Rewritten by Lee Killough to use hash table for fast lookup. Considerably +// reduces the time needed to start new levels. See w_wad.c for comments on +// the hashing algorithm, which is also used for lump searches. +// +// killough 1/21/98, 1/31/98 +// + +int PUREFUNC R_CheckTextureNumForName(const char *name) +{ + int i = NO_TEXTURE; + if (*name != '-') // "NoTexture" marker. + { + i = textures[W_LumpNameHash(name) % (unsigned) numtextures]->index; + while (i >= 0 && strncasecmp(textures[i]->name,name,8)) + i = textures[i]->next; + } + return i; +} + +// +// R_TextureNumForName +// Calls R_CheckTextureNumForName, +// aborts with error message. +// + +int PUREFUNC R_TextureNumForName(const char *name) // const added -- killough +{ + int i = R_CheckTextureNumForName(name); + if (i == -1) + I_Error("R_TextureNumForName: %.8s not found", name); + return i; +} + +// +// R_SafeTextureNumForName +// Calls R_CheckTextureNumForName, and changes any error to NO_TEXTURE +int PUREFUNC R_SafeTextureNumForName(const char *name, int snum) +{ + int i = R_CheckTextureNumForName(name); + if (i == -1) { + i = NO_TEXTURE; // e6y - return "no texture" + lprintf(LO_DEBUG,"bad texture '%s' in sidedef %d\n",name,snum); + } + return i; +} + +// +// R_PrecacheLevel +// Preloads all relevant graphics for the level. +// +// Totally rewritten by Lee Killough to use less memory, +// to avoid using alloca(), and to improve performance. +// cph - new wad lump handling, calls cache functions but acquires no locks + +static inline void precache_lump(int l) +{ + W_CacheLumpNum(l); W_UnlockLumpNum(l); +} + +void R_PrecacheLevel(void) +{ + register int i; + register byte *hitlist; + + if (demoplayback) + return; + + { + size_t size = numflats > numsprites ? numflats : numsprites; + hitlist = malloc((size_t)numtextures > size ? numtextures : size); + } + + // Precache flats. + + memset(hitlist, 0, numflats); + + for (i = numsectors; --i >= 0; ) + hitlist[sectors[i].floorpic] = hitlist[sectors[i].ceilingpic] = 1; + + for (i = numflats; --i >= 0; ) + if (hitlist[i]) + precache_lump(firstflat + i); + + // Precache textures. + + memset(hitlist, 0, numtextures); + + for (i = numsides; --i >= 0;) + hitlist[sides[i].bottomtexture] = + hitlist[sides[i].toptexture] = + hitlist[sides[i].midtexture] = 1; + + // Sky texture is always present. + // Note that F_SKY1 is the name used to + // indicate a sky floor/ceiling as a flat, + // while the sky texture is stored like + // a wall texture, with an episode dependend + // name. + + hitlist[skytexture] = 1; + + for (i = numtextures; --i >= 0; ) + if (hitlist[i]) + { + texture_t *texture = textures[i]; + int j = texture->patchcount; + while (--j >= 0) + precache_lump(texture->patches[j].patch); + } + + // Precache sprites. + memset(hitlist, 0, numsprites); + + { + thinker_t *th = NULL; + while ((th = P_NextThinker(th,th_all)) != NULL) + if (th->function == P_MobjThinker) + hitlist[((mobj_t *)th)->sprite] = 1; + } + + for (i=numsprites; --i >= 0;) + if (hitlist[i]) + { + int j = sprites[i].numframes; + while (--j >= 0) + { + short *sflump = sprites[i].spriteframes[j].lump; + int k = 7; + do + precache_lump(firstspritelump + sflump[k]); + while (--k >= 0); + } + } + free(hitlist); +} + +// Proff - Added for OpenGL +void R_SetPatchNum(patchnum_t *patchnum, const char *name) +{ + const rpatch_t *patch = R_CachePatchName(name); + patchnum->width = patch->width; + patchnum->height = patch->height; + patchnum->leftoffset = patch->leftoffset; + patchnum->topoffset = patch->topoffset; + patchnum->lumpnum = W_GetNumForName(name); + R_UnlockPatchName(name); +} diff --git a/src/r_data.h b/src/r_data.h new file mode 100644 index 00000000..1c10ae26 --- /dev/null +++ b/src/r_data.h @@ -0,0 +1,108 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh module, data I/O, caching, retrieval of graphics + * by name. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __R_DATA__ +#define __R_DATA__ + +#include "r_defs.h" +#include "r_state.h" +#include "r_patch.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// A single patch from a texture definition, basically +// a rectangular area within the texture rectangle. +typedef struct +{ + int originx, originy; // Block origin, which has already accounted + int patch; // for the internal origin of the patch. +} texpatch_t; + +// +// Texture definition. +// A DOOM wall texture is a list of patches +// which are to be combined in a predefined order. +// + +typedef struct +{ + char name[8]; // Keep name for switch changing, etc. + int next, index; // killough 1/31/98: used in hashing algorithm + // CPhipps - moved arrays with per-texture entries to elements here + unsigned widthmask; + // CPhipps - end of additions + short width, height; + short patchcount; // All the patches[patchcount] are drawn + texpatch_t patches[1]; // back-to-front into the cached texture. +} texture_t; + +extern int numtextures; +extern texture_t **textures; + + +const byte *R_GetTextureColumn(const rpatch_t *texpatch, int col); + + +// I/O, setting up the stuff. +void R_InitData (void); +void R_PrecacheLevel (void); + + +// Retrieval. +// Floor/ceiling opaque texture tiles, +// lookup by name. For animation? +int R_FlatNumForName (const char* name); // killough -- const added + + +// R_*TextureNumForName returns the texture number for the texture name, or NO_TEXTURE if +// there is no texture (i.e. "-") specified. +/* cph 2006/07/23 - defined value for no-texture marker (texture "-" in the WAD file) */ +#define NO_TEXTURE 0 +int PUREFUNC R_TextureNumForName (const char *name); // killough -- const added; cph - now PUREFUNC +int PUREFUNC R_SafeTextureNumForName (const char *name, int snum); +int PUREFUNC R_CheckTextureNumForName (const char *name); + +int R_ColormapNumForName(const char *name); // killough 4/4/98 +/* cph 2001/11/17 - new func to do lighting calcs and get suitable colour map */ +const lighttable_t* R_ColourMap(int lightlevel, fixed_t spryscale); + +extern const byte *main_tranmap, *tranmap; + +/* Proff - Added for OpenGL - cph - const char* param */ +void R_SetPatchNum(patchnum_t *patchnum, const char *name); + +#endif diff --git a/src/r_defs.h b/src/r_defs.h new file mode 100644 index 00000000..6fdecec4 --- /dev/null +++ b/src/r_defs.h @@ -0,0 +1,427 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh/rendering module, shared data struct definitions. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_DEFS__ +#define __R_DEFS__ + +// Screenwidth. +#include "doomdef.h" + +// Some more or less basic data types +// we depend on. +#include "m_fixed.h" + +// We rely on the thinker data struct +// to handle sound origins in sectors. +#include "d_think.h" + +// SECTORS do store MObjs anyway. +#include "p_mobj.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// Silhouette, needed for clipping Segs (mainly) +// and sprites representing things. +#define SIL_NONE 0 +#define SIL_BOTTOM 1 +#define SIL_TOP 2 +#define SIL_BOTH 3 + +#define MAXDRAWSEGS 256 + +// +// INTERNAL MAP TYPES +// used by play and refresh +// + +// +// Your plain vanilla vertex. +// Note: transformed values not buffered locally, +// like some DOOM-alikes ("wt", "WebView") do. +// +typedef struct +{ + fixed_t x, y; +} vertex_t; + +// Each sector has a degenmobj_t in its center for sound origin purposes. +typedef struct +{ + thinker_t thinker; // not used for anything + fixed_t x, y, z; +} degenmobj_t; + +// +// The SECTORS record, at runtime. +// Stores things/mobjs. +// + +typedef struct +{ + int iSectorID; // proff 04/05/2000: needed for OpenGL and used in debugmode by the HUD to draw sectornum + boolean no_toptextures; + boolean no_bottomtextures; + fixed_t floorheight; + fixed_t ceilingheight; + int nexttag,firsttag; // killough 1/30/98: improves searches for tags. + int soundtraversed; // 0 = untraversed, 1,2 = sndlines-1 + mobj_t *soundtarget; // thing that made a sound (or null) + int blockbox[4]; // mapblock bounding box for height changes + degenmobj_t soundorg; // origin for any sounds played by the sector + int validcount; // if == validcount, already checked + mobj_t *thinglist; // list of mobjs in sector + + /* killough 8/28/98: friction is a sector property, not an mobj property. + * these fields used to be in mobj_t, but presented performance problems + * when processed as mobj properties. Fix is to make them sector properties. + */ + int friction,movefactor; + + // thinker_t for reversable actions + void *floordata; // jff 2/22/98 make thinkers on + void *ceilingdata; // floors, ceilings, lighting, + void *lightingdata; // independent of one another + + // jff 2/26/98 lockout machinery for stairbuilding + int stairlock; // -2 on first locked -1 after thinker done 0 normally + int prevsec; // -1 or number of sector for previous step + int nextsec; // -1 or number of next step sector + + // killough 3/7/98: support flat heights drawn at another sector's heights + int heightsec; // other sector, or -1 if no other sector + + int bottommap, midmap, topmap; // killough 4/4/98: dynamic colormaps + + // list of mobjs that are at least partially in the sector + // thinglist is a subset of touching_thinglist + struct msecnode_s *touching_thinglist; // phares 3/14/98 + + int linecount; + struct line_s **lines; + + // killough 10/98: support skies coming from sidedefs. Allows scrolling + // skies and other effects. No "level info" kind of lump is needed, + // because you can use an arbitrary number of skies per level with this + // method. This field only applies when skyflatnum is used for floorpic + // or ceilingpic, because the rest of Doom needs to know which is sky + // and which isn't, etc. + + int sky; + + // killough 3/7/98: floor and ceiling texture offsets + fixed_t floor_xoffs, floor_yoffs; + fixed_t ceiling_xoffs, ceiling_yoffs; + + // killough 4/11/98: support for lightlevels coming from another sector + int floorlightsec, ceilinglightsec; + + short floorpic; + short ceilingpic; + short lightlevel; + short special; + short oldspecial; //jff 2/16/98 remembers if sector WAS secret (automap) + short tag; +} sector_t; + +// +// The SideDef. +// + +typedef struct +{ + fixed_t textureoffset; // add this to the calculated texture column + fixed_t rowoffset; // add this to the calculated texture top + short toptexture; // Texture indices. We do not maintain names here. + short bottomtexture; + short midtexture; + sector_t* sector; // Sector the SideDef is facing. + + // killough 4/4/98, 4/11/98: highest referencing special linedef's type, + // or lump number of special effect. Allows texture names to be overloaded + // for other functions. + + int special; + +} side_t; + +// +// Move clipping aid for LineDefs. +// +typedef enum +{ + ST_HORIZONTAL, + ST_VERTICAL, + ST_POSITIVE, + ST_NEGATIVE +} slopetype_t; + +typedef struct line_s +{ + int iLineID; // proff 04/05/2000: needed for OpenGL + vertex_t *v1, *v2; // Vertices, from v1 to v2. + fixed_t dx, dy; // Precalculated v2 - v1 for side checking. + unsigned short flags; // Animation related. + short special; + short tag; + unsigned short sidenum[2]; // Visual appearance: SideDefs. + fixed_t bbox[4]; // A bounding box, for the linedef's extent + slopetype_t slopetype; // To aid move clipping. + sector_t *frontsector; // Front and back sector. + sector_t *backsector; + int validcount; // if == validcount, already checked + void *specialdata; // thinker_t for reversable actions + int firsttag,nexttag; // killough 4/17/98: improves searches for tags. + int r_validcount; // cph: if == gametic, r_flags already done + enum { // cph: + RF_TOP_TILE = 1, // Upper texture needs tiling + RF_MID_TILE = 2, // Mid texture needs tiling + RF_BOT_TILE = 4, // Lower texture needs tiling + RF_IGNORE = 8, // Renderer can skip this line + RF_CLOSED =16, // Line blocks view + } r_flags; + degenmobj_t soundorg; // sound origin for switches/buttons +} line_t; + + +// phares 3/14/98 +// +// Sector list node showing all sectors an object appears in. +// +// There are two threads that flow through these nodes. The first thread +// starts at touching_thinglist in a sector_t and flows through the m_snext +// links to find all mobjs that are entirely or partially in the sector. +// The second thread starts at touching_sectorlist in an mobj_t and flows +// through the m_tnext links to find all sectors a thing touches. This is +// useful when applying friction or push effects to sectors. These effects +// can be done as thinkers that act upon all objects touching their sectors. +// As an mobj moves through the world, these nodes are created and +// destroyed, with the links changed appropriately. +// +// For the links, NULL means top or end of list. + +typedef struct msecnode_s +{ + sector_t *m_sector; // a sector containing this object + struct mobj_s *m_thing; // this object + struct msecnode_s *m_tprev; // prev msecnode_t for this thing + struct msecnode_s *m_tnext; // next msecnode_t for this thing + struct msecnode_s *m_sprev; // prev msecnode_t for this sector + struct msecnode_s *m_snext; // next msecnode_t for this sector + boolean visited; // killough 4/4/98, 4/7/98: used in search algorithms +} msecnode_t; + +// +// The LineSeg. +// +typedef struct +{ + vertex_t *v1, *v2; + fixed_t offset; + angle_t angle; + side_t* sidedef; + line_t* linedef; + + int iSegID; // proff 11/05/2000: needed for OpenGL + // figgi -- needed for glnodes + float length; + boolean miniseg; + + + // Sector references. + // Could be retrieved from linedef, too + // (but that would be slower -- killough) + // backsector is NULL for one sided lines + + sector_t *frontsector, *backsector; +} seg_t; + + +// +// A SubSector. +// References a Sector. +// Basically, this is a list of LineSegs, +// indicating the visible walls that define +// (all or some) sides of a convex BSP leaf. +// + +typedef struct subsector_s +{ + sector_t *sector; + int numlines, firstline; +} subsector_t; + + +// +// BSP node. +// +typedef struct +{ + fixed_t x, y, dx, dy; // Partition line. + fixed_t bbox[2][4]; // Bounding box for each child. + unsigned int children[2]; // If NF_SUBSECTOR it is a subsector. +} node_t; + +// +// OTHER TYPES +// + +// This could be wider for >8 bit display. +// Indeed, true color support is posibble +// precalculating 24bpp lightmap/colormap LUT. +// from darkening PLAYPAL to all black. +// Could use even more than 32 levels. + +typedef byte lighttable_t; + +// +// Masked 2s linedefs +// + +typedef struct drawseg_s +{ + seg_t *curline; + int x1, x2; + fixed_t scale1, scale2, scalestep; + int silhouette; // 0=none, 1=bottom, 2=top, 3=both + fixed_t bsilheight; // do not clip sprites above this + fixed_t tsilheight; // do not clip sprites below this + + // Added for filtering (fractional texture u coord) support - POPE + fixed_t rw_offset, rw_distance, rw_centerangle; + + // Pointers to lists for sprite clipping, + // all three adjusted so [x1] is first value. + + int *sprtopclip, *sprbottomclip, *maskedtexturecol; // dropoff overflow +} drawseg_t; + +// proff: Added for OpenGL +typedef struct +{ + int width,height; + int leftoffset,topoffset; + int lumpnum; +} patchnum_t; + +// +// A vissprite_t is a thing that will be drawn during a refresh. +// i.e. a sprite object that is partly visible. +// + +typedef struct vissprite_s +{ + mobj_t *thing; + boolean flip; + int x1, x2; + fixed_t gx, gy; // for line side calculation + fixed_t gz, gzt; // global bottom / top for silhouette clipping + fixed_t startfrac; // horizontal position of x1 + fixed_t scale; + fixed_t xiscale; // negative if flipped + fixed_t texturemid; + int patch; + uint_64_t mobjflags; + + // for color translation and shadow draw, maxbright frames as well + const lighttable_t *colormap; + + // killough 3/27/98: height sector for underwater/fake ceiling support + int heightsec; + + boolean isplayersprite; +} vissprite_t; + +// +// Sprites are patches with a special naming convention +// so they can be recognized by R_InitSprites. +// The base name is NNNNFx or NNNNFxFx, with +// x indicating the rotation, x = 0, 1-7. +// The sprite and frame specified by a thing_t +// is range checked at run time. +// A sprite is a patch_t that is assumed to represent +// a three dimensional object and may have multiple +// rotations pre drawn. +// Horizontal flipping is used to save space, +// thus NNNNF2F5 defines a mirrored patch. +// Some sprites will only have one picture used +// for all views: NNNNF0 +// + +typedef struct +{ + // If false use 0 for any position. + // Note: as eight entries are available, + // we might as well insert the same name eight times. + boolean rotate; + + // Lump to use for view angles 0-7. + short lump[8]; + + // Flip bit (1 = flip) to use for view angles 0-7. + byte flip[8]; + +} spriteframe_t; + +// +// A sprite definition: +// a number of animation frames. +// + +typedef struct +{ + int numframes; + spriteframe_t *spriteframes; +} spritedef_t; + +// +// Now what is a visplane, anyway? +// +// Go to http://classicgaming.com/doom/editing/ to find out -- killough +// + +typedef struct visplane +{ + struct visplane *next; // Next visplane in hash chain -- killough + int picnum, lightlevel, minx, maxx; + fixed_t height; + fixed_t xoffs, yoffs; // killough 2/28/98: Support scrolling flats + unsigned int pad1; // leave pads for [minx-1]/[maxx+1] + unsigned int top[MAX_SCREENWIDTH]; + unsigned int pad2, pad3; // killough 2/8/98, 4/25/98 + unsigned int bottom[MAX_SCREENWIDTH]; + unsigned int pad4; // dropoff overflow +} visplane_t; + +#endif diff --git a/src/r_demo.c b/src/r_demo.c new file mode 100644 index 00000000..766f0bb8 --- /dev/null +++ b/src/r_demo.c @@ -0,0 +1,88 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Demo stuff + * + *--------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "r_demo.h" +#include "r_fps.h" + +int demo_smoothturns = false; +int demo_smoothturnsfactor = 6; + +static int smooth_playing_turns[SMOOTH_PLAYING_MAXFACTOR]; +static int_64_t smooth_playing_sum; +static int smooth_playing_index; +static angle_t smooth_playing_angle; + +void R_SmoothPlaying_Reset(player_t *player) +{ + if (demo_smoothturns && demoplayback) + { + if (!player) + player = &players[displayplayer]; + + if (player==&players[displayplayer]) + { + smooth_playing_angle = players[displayplayer].mo->angle; + memset(smooth_playing_turns, 0, sizeof(smooth_playing_turns[0]) * SMOOTH_PLAYING_MAXFACTOR); + smooth_playing_sum = 0; + smooth_playing_index = 0; + } + } +} + +void R_SmoothPlaying_Add(int delta) +{ + if (demo_smoothturns && demoplayback) + { + smooth_playing_sum -= smooth_playing_turns[smooth_playing_index]; + smooth_playing_turns[smooth_playing_index] = delta; + smooth_playing_index = (smooth_playing_index + 1)%(demo_smoothturnsfactor); + smooth_playing_sum += delta; + smooth_playing_angle += (int)(smooth_playing_sum/(demo_smoothturnsfactor)); + } +} + +angle_t R_SmoothPlaying_Get(angle_t defangle) +{ + if (demo_smoothturns && demoplayback) + return smooth_playing_angle; + else + return defangle; +} + +void R_ResetAfterTeleport(player_t *player) +{ + R_ResetViewInterpolation(); + R_SmoothPlaying_Reset(player); +} diff --git a/src/r_demo.h b/src/r_demo.h new file mode 100644 index 00000000..e5be4cb3 --- /dev/null +++ b/src/r_demo.h @@ -0,0 +1,45 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Demo stuff + * + *--------------------------------------------------------------------- + */ + +#include "doomstat.h" + +#define SMOOTH_PLAYING_MAXFACTOR 16 + +extern int demo_smoothturns; +extern int demo_smoothturnsfactor; + +void R_SmoothPlaying_Reset(player_t *player); +void R_SmoothPlaying_Add(int delta); +angle_t R_SmoothPlaying_Get(angle_t defangle); +void R_ResetAfterTeleport(player_t *player); diff --git a/src/r_draw.c b/src/r_draw.c new file mode 100644 index 00000000..8dcf2460 --- /dev/null +++ b/src/r_draw.c @@ -0,0 +1,483 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The actual span/column drawing functions. + * Here find the main potential for optimization, + * e.g. inline assembly, different algorithms. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_draw.h" +#include "r_filter.h" +#include "v_video.h" +#include "st_stuff.h" +#include "g_game.h" +#include "am_map.h" +#include "lprintf.h" + +// +// All drawing to the view buffer is accomplished in this file. +// The other refresh files only know about ccordinates, +// not the architecture of the frame buffer. +// Conveniently, the frame buffer is a linear one, +// and we need only the base address, +// and the total size == width*height*depth/8., +// + +byte *viewimage; +int viewwidth; +int scaledviewwidth; +int viewheight; + +// Color tables for different players, +// translate a limited part to another +// (color ramps used for suit colors). +// + +// +// R_DrawColumn +// Source is the top of the column to scale. +// + +// SoM: OPTIMIZE for ANYRES +typedef enum +{ + COL_NONE, + COL_OPAQUE, + COL_TRANS, + COL_FLEXTRANS, + COL_FUZZ, + COL_FLEXADD +} columntype_e; + +static int temp_x = 0; +static int tempyl[4], tempyh[4]; +static byte byte_tempbuf[MAX_SCREENHEIGHT * 4]; +static unsigned short short_tempbuf[MAX_SCREENHEIGHT * 4]; +static unsigned int int_tempbuf[MAX_SCREENHEIGHT * 4]; +static int startx = 0; +static int temptype = COL_NONE; +static int commontop, commonbot; +// SoM 7-28-04: Fix the fuzz problem. +static const byte *tempfuzzmap; + +// +// Spectre/Invisibility. +// + +#define FUZZTABLE 50 +#define FUZZOFF (SCREENWIDTH) + +static const int fuzzoffset_org[FUZZTABLE] = { + FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, + FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, + FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, + FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF +}; + +static int fuzzoffset[FUZZTABLE]; + +static int fuzzpos = 0; + +// render pipelines +#define RDC_STANDARD 1 +#define RDC_TRANSLATED 4 +#define RDC_FUZZ 8 +// no color mapping +#define RDC_NOCOLMAP 16 +// filter modes +#define RDC_DITHERZ 32 +#define RDC_BILINEAR 64 +#define RDC_ROUNDED 128 + +draw_vars_t drawvars = { + NULL, // short_topleft + NULL, // int_topleft + RDRAW_FILTER_POINT, // filterwall + RDRAW_FILTER_POINT, // filterfloor + RDRAW_FILTER_POINT, // filtersprite + RDRAW_FILTER_POINT, // filterz + RDRAW_FILTER_POINT, // filterpatch + + RDRAW_MASKEDCOLUMNEDGE_SQUARE, // sprite_edges + RDRAW_MASKEDCOLUMNEDGE_SQUARE, // patch_edges + + // 49152 = FRACUNIT * 0.75 + // 81920 = FRACUNIT * 1.25 + 49152 // mag_threshold +}; + +// +// Error functions that will abort if R_FlushColumns tries to flush +// columns without a column type. +// + +static void R_FlushWholeError(void) +{ + I_Error("R_FlushWholeColumns called without being initialized.\n"); +} + +static void R_FlushHTError(void) +{ + I_Error("R_FlushHTColumns called without being initialized.\n"); +} + +static void R_QuadFlushError(void) +{ + I_Error("R_FlushQuadColumn called without being initialized.\n"); +} + +static void (*R_FlushWholeColumns)(void) = R_FlushWholeError; +static void (*R_FlushHTColumns)(void) = R_FlushHTError; +static void (*R_FlushQuadColumn)(void) = R_QuadFlushError; + +static void R_FlushColumns(void) +{ + if(temp_x != 4 || commontop >= commonbot) + R_FlushWholeColumns(); + else + { + R_FlushHTColumns(); + R_FlushQuadColumn(); + } + temp_x = 0; +} + +// +// R_ResetColumnBuffer +// +// haleyjd 09/13/04: new function to call from main rendering loop +// which gets rid of the unnecessary reset of various variables during +// column drawing. +// +void R_ResetColumnBuffer(void) +{ + // haleyjd 10/06/05: this must not be done if temp_x == 0! + if(temp_x) + R_FlushColumns(); + temptype = COL_NONE; + R_FlushWholeColumns = R_FlushWholeError; + R_FlushHTColumns = R_FlushHTError; + R_FlushQuadColumn = R_QuadFlushError; +} + +#define R_DRAWCOLUMN_PIPELINE RDC_STANDARD +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad15 +#include "r_drawflush.inl" + +#define R_DRAWCOLUMN_PIPELINE RDC_FUZZ +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz15 +#include "r_drawflush.inl" + +// +// R_DrawColumn +// + +// +// A column is a vertical slice/span from a wall texture that, +// given the DOOM style restrictions on the view orientation, +// will always have constant z depth. +// Thus a special case loop for very fast rendering can +// be used. It has also been used with Wolfenstein 3D. +// + +byte *translationtables; + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_STANDARD +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_STANDARD + +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad15 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +// +// R_DrawTranslatedColumn +// Used to draw player sprites +// with the green colorramp mapped to others. +// Could be used with different translation +// tables, e.g. the lighter colored version +// of the BaronOfHell, the HellKnight, uses +// identical sprites, kinda brightened up. +// + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_TRANSLATED +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_TRANSLATED + +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawTranslatedColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWhole15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHT15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuad15 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +// +// Framebuffer postprocessing. +// Creates a fuzzy image by copying pixels +// from adjacent ones to left and right. +// Used with an all black colormap, this +// could create the SHADOW effect, +// i.e. spectres and invisible players. +// + +#define R_DRAWCOLUMN_PIPELINE_TYPE RDC_PIPELINE_FUZZ +#define R_DRAWCOLUMN_PIPELINE_BASE RDC_FUZZ + +#define R_DRAWCOLUMN_FUNCNAME_COMPOSITE(postfix) R_DrawFuzzColumn15 ## postfix +#define R_FLUSHWHOLE_FUNCNAME R_FlushWholeFuzz15 +#define R_FLUSHHEADTAIL_FUNCNAME R_FlushHTFuzz15 +#define R_FLUSHQUAD_FUNCNAME R_FlushQuadFuzz15 +#include "r_drawcolpipeline.inl" + +#undef R_DRAWCOLUMN_PIPELINE_BASE +#undef R_DRAWCOLUMN_PIPELINE_TYPE + +static R_DrawColumn_f drawcolumnfuncs[RDRAW_FILTER_MAXFILTERS][RDRAW_FILTER_MAXFILTERS][RDC_PIPELINE_MAXPIPELINES] = { + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn15_PointUV, + R_DrawTranslatedColumn15_PointUV, + R_DrawFuzzColumn15_PointUV,}, + {R_DrawColumn15_LinearUV, + R_DrawTranslatedColumn15_LinearUV, + R_DrawFuzzColumn15_LinearUV,}, + {R_DrawColumn15_RoundedUV, + R_DrawTranslatedColumn15_RoundedUV, + R_DrawFuzzColumn15_RoundedUV,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn15_PointUV_PointZ, + R_DrawTranslatedColumn15_PointUV_PointZ, + R_DrawFuzzColumn15_PointUV_PointZ,}, + {R_DrawColumn15_LinearUV_PointZ, + R_DrawTranslatedColumn15_LinearUV_PointZ, + R_DrawFuzzColumn15_LinearUV_PointZ,}, + {R_DrawColumn15_RoundedUV_PointZ, + R_DrawTranslatedColumn15_RoundedUV_PointZ, + R_DrawFuzzColumn15_RoundedUV_PointZ,}, + }, + { + {NULL, NULL, NULL, NULL,}, + {R_DrawColumn15_PointUV_LinearZ, + R_DrawTranslatedColumn15_PointUV_LinearZ, + R_DrawFuzzColumn15_PointUV_LinearZ,}, + {R_DrawColumn15_LinearUV_LinearZ, + R_DrawTranslatedColumn15_LinearUV_LinearZ, + R_DrawFuzzColumn15_LinearUV_LinearZ,}, + {R_DrawColumn15_RoundedUV_LinearZ, + R_DrawTranslatedColumn15_RoundedUV_LinearZ, + R_DrawFuzzColumn15_RoundedUV_LinearZ,}, + }, +}; + +R_DrawColumn_f R_GetDrawColumnFunc(enum column_pipeline_e type, + enum draw_filter_type_e filter, + enum draw_filter_type_e filterz) { + R_DrawColumn_f result = drawcolumnfuncs[filterz][filter][type]; + if (result == NULL) + I_Error("R_GetDrawColumnFunc: undefined function (%d, %d, %d)", + type, filter, filterz); + return result; +} + +void R_SetDefaultDrawColumnVars(draw_column_vars_t *dcvars) { + dcvars->x = dcvars->yl = dcvars->yh = dcvars->z = 0; + dcvars->iscale = dcvars->texturemid = dcvars->texheight = dcvars->texu = 0; + dcvars->source = dcvars->prevsource = dcvars->nextsource = NULL; + dcvars->colormap = dcvars->nextcolormap = colormaps[0]; + dcvars->translation = NULL; + dcvars->edgeslope = dcvars->drawingmasked = 0; + dcvars->edgetype = drawvars.sprite_edges; +} + +// +// R_InitTranslationTables +// Creates the translation tables to map +// the green color ramp to gray, brown, red. +// Assumes a given structure of the PLAYPAL. +// Could be read from a lump instead. +// + +byte playernumtotrans[MAXPLAYERS]; +extern lighttable_t *(*c_zlight)[LIGHTLEVELS][MAXLIGHTZ]; + +void R_InitTranslationTables (void) +{ + int i, j; +#define MAXTRANS 3 + byte transtocolour[MAXTRANS]; + + // killough 5/2/98: + // Remove dependency of colormaps aligned on 256-byte boundary + + if (translationtables == NULL) // CPhipps - allow multiple calls + translationtables = Z_Malloc(256*MAXTRANS, PU_STATIC, 0); + + for (i=0; i= 0x70 && i<= 0x7f) + { + // CPhipps - configurable player colours + translationtables[i] = colormaps[0][((i&0xf)<<9) + transtocolour[0]]; + translationtables[i+256] = colormaps[0][((i&0xf)<<9) + transtocolour[1]]; + translationtables[i+512] = colormaps[0][((i&0xf)<<9) + transtocolour[2]]; + } + else // Keep all other colors as is. + translationtables[i]=translationtables[i+256]=translationtables[i+512]=i; +} + +// +// R_DrawSpan +// With DOOM style restrictions on view orientation, +// the floors and ceilings consist of horizontal slices +// or spans with constant z depth. +// However, rotation around the world z axis is possible, +// thus this mapping, while simpler and faster than +// perspective correct texture mapping, has to traverse +// the texture at an angle in all but a few cases. +// In consequence, flats are not stored by column (like walls), +// and the inner loop has to step in texture space u and v. +// + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_PointUV_PointZ +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_PointUV_LinearZ +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_LinearUV_PointZ +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_LinearUV_LinearZ +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_RoundedUV_PointZ +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED) +#include "r_drawspan.inl" + +#define R_DRAWSPAN_FUNCNAME R_DrawSpan15_RoundedUV_LinearZ +#define R_DRAWSPAN_PIPELINE (RDC_STANDARD | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawspan.inl" + +static R_DrawSpan_f drawspanfuncs[RDRAW_FILTER_MAXFILTERS][RDRAW_FILTER_MAXFILTERS] = { + { + NULL, + NULL, + NULL, + NULL, + }, + { + NULL, + R_DrawSpan15_PointUV_PointZ, + R_DrawSpan15_LinearUV_PointZ, + R_DrawSpan15_RoundedUV_PointZ, + }, + { + NULL, + R_DrawSpan15_PointUV_LinearZ, + R_DrawSpan15_LinearUV_LinearZ, + R_DrawSpan15_RoundedUV_LinearZ, + }, + { + NULL, + NULL, + NULL, + NULL, + }, +}; + +R_DrawSpan_f R_GetDrawSpanFunc(enum draw_filter_type_e filter, + enum draw_filter_type_e filterz) { + R_DrawSpan_f result = drawspanfuncs[filterz][filter]; + if (result == NULL) + I_Error("R_GetDrawSpanFunc: undefined function (%d, %d)", + filter, filterz); + return result; +} + +void R_DrawSpan(draw_span_vars_t *dsvars) { + R_GetDrawSpanFunc(drawvars.filterfloor, drawvars.filterz)(dsvars); +} + +// +// R_InitBuffer +// Creats lookup tables that avoid +// multiplies and other hazzles +// for getting the framebuffer address +// of a pixel to draw. +// + +void R_InitBuffer(int width, int height) +{ + int i=0; + // Handle resize, + // e.g. smaller view windows + // with border and/or status bar. + + // Same with base row offset. + + drawvars.short_topleft = (unsigned short *)(screens[0].data); + drawvars.int_topleft = (unsigned int *)(screens[0].data); + + for (i=0; i rdraw_magThresh), then it + // drops back to point filtering. + fixed_t mag_threshold; +} draw_vars_t; + +extern draw_vars_t drawvars; + +extern byte playernumtotrans[MAXPLAYERS]; // CPhipps - what translation table for what player +extern byte *translationtables; + +typedef void (*R_DrawColumn_f)(draw_column_vars_t *dcvars); +R_DrawColumn_f R_GetDrawColumnFunc(enum column_pipeline_e type, + enum draw_filter_type_e filter, + enum draw_filter_type_e filterz); + +// Span blitting for rows, floor/ceiling. No Spectre effect needed. +typedef void (*R_DrawSpan_f)(draw_span_vars_t *dsvars); +R_DrawSpan_f R_GetDrawSpanFunc(enum draw_filter_type_e filter, + enum draw_filter_type_e filterz); +void R_DrawSpan(draw_span_vars_t *dsvars); + +void R_InitBuffer(int width, int height); + +// Initialize color translation tables, for player rendering etc. +void R_InitTranslationTables(void); + +// haleyjd 09/13/04: new function to call from main rendering loop +// which gets rid of the unnecessary reset of various variables during +// column drawing. +void R_ResetColumnBuffer(void); + +#endif diff --git a/src/r_drawcolpipeline.inl b/src/r_drawcolpipeline.inl new file mode 100644 index 00000000..56b51ed6 --- /dev/null +++ b/src/r_drawcolpipeline.inl @@ -0,0 +1,50 @@ + +// no color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_PointUV) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_NOCOLMAP) +#include "r_drawcolumn.inl" + +// simple depth color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_PointUV_PointZ) +#define R_DRAWCOLUMN_PIPELINE R_DRAWCOLUMN_PIPELINE_BASE +#include "r_drawcolumn.inl" + +// z-dither +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_PointUV_LinearZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_DITHERZ) +#include "r_drawcolumn.inl" + +// bilinear with no color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_LinearUV) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_BILINEAR | RDC_NOCOLMAP) +#include "r_drawcolumn.inl" + +// bilinear with simple depth color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_LinearUV_PointZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_BILINEAR) +#include "r_drawcolumn.inl" + +// bilinear + z-dither +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_LinearUV_LinearZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_BILINEAR | RDC_DITHERZ) +#include "r_drawcolumn.inl" + +// rounded with no color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_RoundedUV) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_ROUNDED | RDC_NOCOLMAP) +#include "r_drawcolumn.inl" + +// rounded with simple depth color mapping +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_RoundedUV_PointZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_ROUNDED) +#include "r_drawcolumn.inl" + +// rounded + z-dither +#define R_DRAWCOLUMN_FUNCNAME R_DRAWCOLUMN_FUNCNAME_COMPOSITE(_RoundedUV_LinearZ) +#define R_DRAWCOLUMN_PIPELINE (R_DRAWCOLUMN_PIPELINE_BASE | RDC_ROUNDED | RDC_DITHERZ) +#include "r_drawcolumn.inl" + +#undef R_FLUSHWHOLE_FUNCNAME +#undef R_FLUSHHEADTAIL_FUNCNAME +#undef R_FLUSHQUAD_FUNCNAME +#undef R_DRAWCOLUMN_FUNCNAME_COMPOSITE diff --git a/src/r_drawcolumn.inl b/src/r_drawcolumn.inl new file mode 100644 index 00000000..1a43bbe0 --- /dev/null +++ b/src/r_drawcolumn.inl @@ -0,0 +1,330 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#define SCREENTYPE unsigned short +#define TEMPBUF short_tempbuf + +#define GETDESTCOLOR15(col) (col) + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLATED) +#define GETCOL8_MAPPED(col) (translation[(col)]) +#else +#define GETCOL8_MAPPED(col) (col) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_NOCOLMAP) + #define GETCOL8_DEPTH(col) GETCOL8_MAPPED(col) +#else + #if (R_DRAWCOLUMN_PIPELINE & RDC_DITHERZ) + #define GETCOL8_DEPTH(col) (dither_colormaps[filter_getDitheredPixelLevel(x, y, fracz)][GETCOL8_MAPPED(col)]) + #else + #define GETCOL8_DEPTH(col) colormap[GETCOL8_MAPPED(col)] + #endif +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) + #define GETCOL15(frac, nextfrac) filter_getFilteredForColumn15(GETCOL8_DEPTH,frac,nextfrac) +#elif (R_DRAWCOLUMN_PIPELINE & RDC_ROUNDED) + #define GETCOL15(frac, nextfrac) VID_PAL15(GETCOL8_DEPTH(filter_getRoundedForColumn(frac,nextfrac)), VID_COLORWEIGHTMASK) +#else + #define GETCOL15(frac, nextfrac) VID_PAL15(GETCOL8_DEPTH(source[(frac)>>FRACBITS]), VID_COLORWEIGHTMASK) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED|RDC_DITHERZ)) + #define INCY(y) (y++) +#else + #define INCY(y) +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) +#define COLTYPE (COL_TRANS) +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) +#define COLTYPE (COL_FUZZ) +#else +#define COLTYPE (COL_OPAQUE) +#endif + +#define GETCOL(frac, nextfrac) GETCOL15(frac, nextfrac) +#define GETDESTCOLOR(col) GETDESTCOLOR15(col) + +static void R_DRAWCOLUMN_FUNCNAME(draw_column_vars_t *dcvars) +{ + int count; + SCREENTYPE *dest; // killough + fixed_t frac; + const fixed_t fracstep = dcvars->iscale; +#if ((R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR)) + const fixed_t slope_texu = (dcvars->source == dcvars->nextsource) ? 0 : dcvars->texu & 0xffff; +#else + const fixed_t slope_texu = dcvars->texu; +#endif + + // drop back to point filtering if we're minifying +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED)) + if (dcvars->iscale > drawvars.mag_threshold) { + R_GetDrawColumnFunc(R_DRAWCOLUMN_PIPELINE_TYPE, + RDRAW_FILTER_POINT, + drawvars.filterz)(dcvars); + return; + } +#endif + +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // Adjust borders. Low... + if (!dcvars->yl) + dcvars->yl = 1; + + // .. and high. + if (dcvars->yh == viewheight-1) + dcvars->yh = viewheight - 2; +#endif + + // leban 1/17/99: + // removed the + 1 here, adjusted the if test, and added an increment + // later. this helps a compiler pipeline a bit better. the x86 + // assembler also does this. + + count = dcvars->yh - dcvars->yl; + + // leban 1/17/99: + // this case isn't executed too often. depending on how many instructions + // there are between here and the second if test below, this case could + // be moved down and might save instructions overall. since there are + // probably different wads that favor one way or the other, i'll leave + // this alone for now. + if (count < 0) // Zero length, column does not exceed a pixel. + return; + + // Determine scaling, which is the only mapping to be done. + #if (R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) + frac = dcvars->texturemid - (FRACUNIT>>1) + (dcvars->yl-centery)*fracstep; + #else + frac = dcvars->texturemid + (dcvars->yl-centery)*fracstep; + #endif + + if (dcvars->drawingmasked && dcvars->edgetype == RDRAW_MASKEDCOLUMNEDGE_SLOPED) { + // slope the top and bottom column edge based on the fractional u coordinate + // and dcvars->edgeslope, which were set in R_DrawMaskedColumn + // in r_things.c + if (dcvars->yl != 0) { + if (dcvars->edgeslope & RDRAW_EDGESLOPE_TOP_UP) { + // [/#] + int shift = ((0xffff-(slope_texu & 0xffff))/dcvars->iscale); + dcvars->yl += shift; + count -= shift; + frac += 0xffff-(slope_texu & 0xffff); + } + else if (dcvars->edgeslope & RDRAW_EDGESLOPE_TOP_DOWN) { + // [#\] + int shift = ((slope_texu & 0xffff)/dcvars->iscale); + dcvars->yl += shift; + count -= shift; + frac += slope_texu & 0xffff; + } + } + if (dcvars->yh != viewheight-1) { + if (dcvars->edgeslope & RDRAW_EDGESLOPE_BOT_UP) { + // [#/] + int shift = ((0xffff-(slope_texu & 0xffff))/dcvars->iscale); + dcvars->yh -= shift; + count -= shift; + } + else if (dcvars->edgeslope & RDRAW_EDGESLOPE_BOT_DOWN) { + // [\#] + int shift = ((slope_texu & 0xffff)/dcvars->iscale); + dcvars->yh -= shift; + count -= shift; + } + } + if (count <= 0) return; + } + + // Framebuffer destination address. + // SoM: MAGIC + { + // haleyjd: reordered predicates + if(temp_x == 4 || + (temp_x && (temptype != COLTYPE || temp_x + startx != dcvars->x))) + R_FlushColumns(); + + if(!temp_x) + { + startx = dcvars->x; + tempyl[0] = commontop = dcvars->yl; + tempyh[0] = commonbot = dcvars->yh; + temptype = COLTYPE; +#if (R_DRAWCOLUMN_PIPELINE & RDC_TRANSLUCENT) + temptranmap = tranmap; +#elif (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + tempfuzzmap = fullcolormap; // SoM 7-28-04: Fix the fuzz problem. +#endif + R_FlushWholeColumns = R_FLUSHWHOLE_FUNCNAME; + R_FlushHTColumns = R_FLUSHHEADTAIL_FUNCNAME; + R_FlushQuadColumn = R_FLUSHQUAD_FUNCNAME; + dest = &TEMPBUF[dcvars->yl << 2]; + } else { + tempyl[temp_x] = dcvars->yl; + tempyh[temp_x] = dcvars->yh; + + if(dcvars->yl > commontop) + commontop = dcvars->yl; + if(dcvars->yh < commonbot) + commonbot = dcvars->yh; + + dest = &TEMPBUF[(dcvars->yl << 2) + temp_x]; + } + temp_x += 1; + } + +// do nothing else when drawin fuzz columns +#if (!(R_DRAWCOLUMN_PIPELINE & RDC_FUZZ)) + { + const byte *source = dcvars->source; + const lighttable_t *colormap = dcvars->colormap; + const byte *translation = dcvars->translation; +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED|RDC_DITHERZ)) + int y = dcvars->yl; + const int x = dcvars->x; +#endif +#if (R_DRAWCOLUMN_PIPELINE & RDC_DITHERZ) + const int fracz = (dcvars->z >> 6) & 255; + const byte *dither_colormaps[2] = { dcvars->colormap, dcvars->nextcolormap }; +#endif +#if (R_DRAWCOLUMN_PIPELINE & RDC_BILINEAR) + const byte *nextsource = dcvars->nextsource; + const unsigned int filter_fracu = (dcvars->source == dcvars->nextsource) ? 0 : dcvars->texu & 0xffff; +#endif +#if (R_DRAWCOLUMN_PIPELINE & RDC_ROUNDED) + const byte *prevsource = dcvars->prevsource; + const byte *nextsource = dcvars->nextsource; + const unsigned int filter_fracu = (dcvars->source == dcvars->nextsource) ? 0 : (dcvars->texu>>8) & 0xff; +#endif + + count++; + + // Inner loop that does the actual texture mapping, + // e.g. a DDA-lile scaling. + // This is as fast as it gets. (Yeah, right!!! -- killough) + // + // killough 2/1/98: more performance tuning + + if (dcvars->texheight == 128) { + #define FIXEDT_128MASK ((127<texheight == 0) { + /* cph - another special case */ + while (count--) { + *dest = GETDESTCOLOR(GETCOL(frac, (frac+FRACUNIT))); + INCY(y); + dest += 4; + frac += fracstep; + } + } else { + unsigned heightmask = dcvars->texheight-1; // CPhipps - specify type + if (! (dcvars->texheight & heightmask) ) { // power of 2 -- killough + fixed_t fixedt_heightmask = (heightmask<=0) { // texture height is a power of 2 -- killough + *dest = GETDESTCOLOR(GETCOL(frac & fixedt_heightmask, (frac+FRACUNIT) & fixedt_heightmask)); + INCY(y); + dest += 4; + frac += fracstep; + *dest = GETDESTCOLOR(GETCOL(frac & fixedt_heightmask, (frac+FRACUNIT) & fixedt_heightmask)); + INCY(y); + dest += 4; + frac += fracstep; + } + if (count & 1) + *dest = GETDESTCOLOR(GETCOL(frac & fixedt_heightmask, (frac+FRACUNIT) & fixedt_heightmask)); + INCY(y); + } else { + fixed_t nextfrac = 0; + + heightmask++; + heightmask <<= FRACBITS; + + if (frac < 0) + while ((frac += heightmask) < 0); + else + while (frac >= (int)heightmask) + frac -= heightmask; + +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED)) + nextfrac = frac + FRACUNIT; + while (nextfrac >= (int)heightmask) + nextfrac -= heightmask; +#endif + +#define INCFRAC(f) if ((f += fracstep) >= (int)heightmask) f -= heightmask; + + while (count--) { + // Re-map color indices from wall texture column + // using a lighting/special effects LUT. + + // heightmask is the Tutti-Frutti fix -- killough + + *dest = GETDESTCOLOR(GETCOL(frac, nextfrac)); + INCY(y); + dest += 4; + INCFRAC(frac); +#if (R_DRAWCOLUMN_PIPELINE & (RDC_BILINEAR|RDC_ROUNDED)) + INCFRAC(nextfrac); +#endif + } + } + } + } +#endif // (!(R_DRAWCOLUMN_PIPELINE & RDC_FUZZ)) +} + +#undef GETDESTCOLOR32 +#undef GETDESTCOLOR16 +#undef GETDESTCOLOR15 +#undef GETDESTCOLOR8 +#undef GETDESTCOLOR +#undef GETCOL8_MAPPED +#undef GETCOL8_DEPTH +#undef GETCOL32 +#undef GETCOL16 +#undef GETCOL15 +#undef GETCOL8 +#undef GETCOL +#undef INCY +#undef INCFRAC +#undef COLTYPE +#undef TEMPBUF +#undef SCREENTYPE + +#undef R_DRAWCOLUMN_FUNCNAME +#undef R_DRAWCOLUMN_PIPELINE diff --git a/src/r_drawflush.inl b/src/r_drawflush.inl new file mode 100644 index 00000000..48e02f3e --- /dev/null +++ b/src/r_drawflush.inl @@ -0,0 +1,208 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft +#define TEMPBUF short_tempbuf + +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) +#define GETDESTCOLOR15(col) GETBLENDED15_9406(col, 0) +#else +#define GETDESTCOLOR15(col) (col) +#endif + +#define GETDESTCOLOR(col) GETDESTCOLOR15(col) + +// +// R_FlushWholeOpaque +// +// Flushes the entire columns in the buffer, one at a time. +// This is used when a quad flush isn't possible. +// Opaque version -- no remapping whatsoever. +// +static void R_FLUSHWHOLE_FUNCNAME(void) +{ + SCREENTYPE *source; + SCREENTYPE *dest; + int count, yl; + + while(--temp_x >= 0) + { + yl = tempyl[temp_x]; + source = &TEMPBUF[temp_x + (yl << 2)]; + dest = drawvars.TOPLEFT + yl * SURFACE_SHORT_PITCH + startx + temp_x; + count = tempyh[temp_x] - yl + 1; + + while(--count >= 0) + { +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // SoM 7-28-04: Fix the fuzz problem. + *dest = GETDESTCOLOR(dest[fuzzoffset[fuzzpos]]); + + // Clamp table lookup index. + if(++fuzzpos == FUZZTABLE) + fuzzpos = 0; +#else + *dest = *source; +#endif + + source += 4; + dest += SURFACE_SHORT_PITCH; + } + } +} + +// +// R_FlushHTOpaque +// +// Flushes the head and tail of columns in the buffer in +// preparation for a quad flush. +// Opaque version -- no remapping whatsoever. +// +static void R_FLUSHHEADTAIL_FUNCNAME(void) +{ + SCREENTYPE *source; + SCREENTYPE *dest; + int count, colnum = 0; + int yl, yh; + + while(colnum < 4) + { + yl = tempyl[colnum]; + yh = tempyh[colnum]; + + // flush column head + if(yl < commontop) + { + source = &TEMPBUF[colnum + (yl << 2)]; + dest = drawvars.TOPLEFT + yl * SURFACE_SHORT_PITCH + startx + colnum; + count = commontop - yl; + + while(--count >= 0) + { +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // SoM 7-28-04: Fix the fuzz problem. + *dest = GETDESTCOLOR(dest[fuzzoffset[fuzzpos]]); + + // Clamp table lookup index. + if(++fuzzpos == FUZZTABLE) + fuzzpos = 0; +#else + *dest = *source; +#endif + + source += 4; + dest += SURFACE_SHORT_PITCH; + } + } + + // flush column tail + if(yh > commonbot) + { + source = &TEMPBUF[colnum + ((commonbot + 1) << 2)]; + dest = drawvars.TOPLEFT + (commonbot + 1) * SURFACE_SHORT_PITCH + startx + colnum; + count = yh - commonbot; + + while(--count >= 0) + { +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + // SoM 7-28-04: Fix the fuzz problem. + *dest = GETDESTCOLOR(dest[fuzzoffset[fuzzpos]]); + + // Clamp table lookup index. + if(++fuzzpos == FUZZTABLE) + fuzzpos = 0; +#else + *dest = *source; +#endif + + source += 4; + dest += SURFACE_SHORT_PITCH; + } + } + ++colnum; + } +} + +static void R_FLUSHQUAD_FUNCNAME(void) +{ + SCREENTYPE *source = &TEMPBUF[commontop << 2]; + SCREENTYPE *dest = drawvars.TOPLEFT + commontop * SURFACE_SHORT_PITCH + startx; + int count; +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + int fuzz1, fuzz2, fuzz3, fuzz4; + + fuzz1 = fuzzpos; + fuzz2 = (fuzz1 + tempyl[1]) % FUZZTABLE; + fuzz3 = (fuzz2 + tempyl[2]) % FUZZTABLE; + fuzz4 = (fuzz3 + tempyl[3]) % FUZZTABLE; +#endif + + count = commonbot - commontop + 1; + +#if (R_DRAWCOLUMN_PIPELINE & RDC_FUZZ) + while(--count >= 0) + { + dest[0] = GETDESTCOLOR(dest[0 + fuzzoffset[fuzz1]]); + dest[1] = GETDESTCOLOR(dest[1 + fuzzoffset[fuzz2]]); + dest[2] = GETDESTCOLOR(dest[2 + fuzzoffset[fuzz3]]); + dest[3] = GETDESTCOLOR(dest[3 + fuzzoffset[fuzz4]]); + fuzz1 = (fuzz1 + 1) % FUZZTABLE; + fuzz2 = (fuzz2 + 1) % FUZZTABLE; + fuzz3 = (fuzz3 + 1) % FUZZTABLE; + fuzz4 = (fuzz4 + 1) % FUZZTABLE; + source += 4 * sizeof(byte); + dest += SURFACE_SHORT_PITCH * sizeof(byte); + } +#else + while(--count >= 0) + { + dest[0] = source[0]; + dest[1] = source[1]; + dest[2] = source[2]; + dest[3] = source[3]; + source += 4; + dest += SURFACE_SHORT_PITCH; + } +#endif +} + +#undef GETDESTCOLOR15 +#undef GETDESTCOLOR + +#undef TEMPBUF +#undef PITCH +#undef TOPLEFT +#undef SCREENTYPE + +#undef R_DRAWCOLUMN_PIPELINE +#undef R_FLUSHWHOLE_FUNCNAME +#undef R_FLUSHHEADTAIL_FUNCNAME +#undef R_FLUSHQUAD_FUNCNAME diff --git a/src/r_drawspan.inl b/src/r_drawspan.inl new file mode 100644 index 00000000..87891ffb --- /dev/null +++ b/src/r_drawspan.inl @@ -0,0 +1,133 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +// +// R_DrawSpan +// + +#define SCREENTYPE unsigned short +#define TOPLEFT short_topleft + +#if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + #define GETDEPTHMAP(col) dither_colormaps[filter_getDitheredPixelLevel(x1, y, fracz)][(col)] +#else + #define GETDEPTHMAP(col) colormap[(col)] +#endif + +#define GETCOL_POINT(col) VID_PAL15(GETDEPTHMAP(col), VID_COLORWEIGHTMASK) +#define GETCOL_LINEAR(col) filter_getFilteredForSpan15(GETDEPTHMAP, xfrac, yfrac) + +#if (R_DRAWSPAN_PIPELINE & RDC_BILINEAR) + #define GETCOL(col) GETCOL_LINEAR(col) +#else + #define GETCOL(col) GETCOL_POINT(col) +#endif + +static void R_DRAWSPAN_FUNCNAME(draw_span_vars_t *dsvars) +{ +#if (R_DRAWSPAN_PIPELINE & (RDC_ROUNDED|RDC_BILINEAR)) + // drop back to point filtering if we're minifying + // 49152 = FRACUNIT * 0.75 + if ((D_abs(dsvars->xstep) > drawvars.mag_threshold) + || (D_abs(dsvars->ystep) > drawvars.mag_threshold)) + { + R_GetDrawSpanFunc(RDRAW_FILTER_POINT, + drawvars.filterz)(dsvars); + return; + } +#endif + { + unsigned count = dsvars->x2 - dsvars->x1 + 1; + fixed_t xfrac = dsvars->xfrac; + fixed_t yfrac = dsvars->yfrac; + const fixed_t xstep = dsvars->xstep; + const fixed_t ystep = dsvars->ystep; + const byte *source = dsvars->source; + const byte *colormap = dsvars->colormap; + SCREENTYPE *dest = drawvars.TOPLEFT + dsvars->y* SURFACE_SHORT_PITCH + dsvars->x1; +#if (R_DRAWSPAN_PIPELINE & (RDC_DITHERZ|RDC_BILINEAR)) + const int y = dsvars->y; + int x1 = dsvars->x1; +#endif +#if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + const int fracz = (dsvars->z >> 12) & 255; + const byte *dither_colormaps[2] = { dsvars->colormap, dsvars->nextcolormap }; +#endif + + while (count) { +#if (R_DRAWSPAN_PIPELINE & RDC_BILINEAR) + // truecolor bilinear filtered + *dest++ = GETCOL(0); + xfrac += xstep; + yfrac += ystep; + count--; + #if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + x1--; + #endif +#elif (R_DRAWSPAN_PIPELINE & RDC_ROUNDED) + *dest++ = GETCOL(filter_getRoundedForSpan(xfrac, yfrac)); + xfrac += xstep; + yfrac += ystep; + count--; + #if (R_DRAWSPAN_PIPELINE & RDC_DITHERZ) + x1--; + #endif +#else + #if (R_DRAWSPAN_PIPELINE & RDC_BILINEAR) + // 8 bit bilinear + const fixed_t xtemp = ((xfrac >> 16) + (filter_getDitheredPixelLevel(x1, y, ((xfrac>>8)&0xff)))) & 63; + const fixed_t ytemp = ((yfrac >> 10) + 64*(filter_getDitheredPixelLevel(x1, y, ((yfrac>>8)&0xff)))) & 4032; + #else + const fixed_t xtemp = (xfrac >> 16) & 63; + const fixed_t ytemp = (yfrac >> 10) & 4032; + #endif + const fixed_t spot = xtemp | ytemp; + xfrac += xstep; + yfrac += ystep; + *dest++ = GETCOL(source[spot]); + count--; + #if (R_DRAWSPAN_PIPELINE & (RDC_DITHERZ|RDC_BILINEAR)) + x1--; + #endif +#endif + } + } +} + +#undef GETDEPTHMAP +#undef GETCOL_LINEAR +#undef GETCOL_POINT +#undef GETCOL +#undef PITCH +#undef TOPLEFT +#undef SCREENTYPE + +#undef R_DRAWSPAN_PIPELINE +#undef R_DRAWSPAN_FUNCNAME diff --git a/src/r_filter.c b/src/r_filter.c new file mode 100644 index 00000000..87fbb68b --- /dev/null +++ b/src/r_filter.c @@ -0,0 +1,119 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#include "doomtype.h" +#include "r_filter.h" + +#define DMR 16 +byte filter_ditherMatrix[DITHER_DIM][DITHER_DIM] = { + { 0*DMR, 14*DMR, 3*DMR, 13*DMR, }, {11*DMR, 5*DMR, 8*DMR, 6*DMR, }, + {12*DMR, 2*DMR, 15*DMR, 1*DMR, }, { 7*DMR, 9*DMR, 4*DMR, 10*DMR, }, +}; + +byte filter_roundedUVMap[FILTER_UVDIM*FILTER_UVDIM]; +byte filter_roundedRowMap[4*16]; + +void R_FilterInit(void) { + int i,j,s,t; + + // scale2x takes the following source: + // A B C + // D E F + // G H I + // + // and doubles the size of E to produce: + // E0 E1 + // E2 E3 + // + // E0 = D == B && B != F && D != H ? D : E; + // E1 = B == F && B != D && F != H ? F : E; + // E2 = D == H && D != B && H != F ? D : E; + // E3 = H == F && D != H && B != F ? F : E; + // + // to make this comparison regimen faster, we encode source color + // equivalency into a single byte with the getCode() macro + // + // #define getCode(b,f,h,d) ( (b == f)<<0 | (f == h)<<1 | (h == d)<<2 | (d == b)<<3 ) + + // encode the scale2x conditionals into a lookup code + for (i=0; i<16; i++) { + // E0 = D == B && B != F && D != H ? D : E; // 10-0 => 1000 or 1010 => 8 or A + filter_roundedRowMap[0*16+i] = (i == 0x8 || i == 0xA) ? 0 : 1; + // E1 = B == F && B != D && F != H ? F : E; // 0-01 => 0101 or 0001 => 5 or 1 + filter_roundedRowMap[1*16+i] = (i == 0x5 || i == 0x1) ? 2 : 1; + // E2 = D == H && D != B && H != F ? D : E; // 010- => 0101 or 0100 => 5 or 4 + filter_roundedRowMap[2*16+i] = (i == 0x4 || i == 0x5) ? 0 : 1; + // E3 = H == F && D != H && B != F ? F : E; // -010 => 1010 or 0010 => A or 2 + filter_roundedRowMap[3*16+i] = (i == 0xA || i == 0x2) ? 2 : 1; + } + + // fill the uvMap. this will return: + /* 0/\1 + /4 \ + \ / + 2\/3 */ + // .. based on the uv coordinates + for (i=0; i=0 && t>=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (s+t > FILTER_UVDIM/2) ? 0 : 4; + else if (s>=0 && t<=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (s-t > FILTER_UVDIM/2) ? 2 : 4; + else if (s<=0 && t>=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (-s+t > FILTER_UVDIM/2) ? 1 : 4; + else if (s<=0 && t<=0) filter_roundedUVMap[i*FILTER_UVDIM+j] = (-s-t > FILTER_UVDIM/2) ? 3 : 4; + else filter_roundedUVMap[i*FILTER_UVDIM+j] = 4; + } + } +} + +byte *filter_getScale2xQuadColors(byte e, byte b, byte f, byte h, byte d) { + // A B C + // D E F + // G H I + // perform the Scale2x algorithm (quickly) to get the new quad to represent E + static byte quad[5]; + static byte rowColors[3]; + int code; + + rowColors[0] = d; + rowColors[1] = e; + rowColors[2] = f; + + #define getCode(b,f,h,d) ( (b == f)<<0 | (f == h)<<1 | (h == d)<<2 | (d == b)<<3 ) + + code = getCode(b,f,h,d); + quad[0] = rowColors[filter_roundedRowMap[0*16+code]]; + quad[1] = rowColors[filter_roundedRowMap[1*16+code]]; + quad[2] = rowColors[filter_roundedRowMap[2*16+code]]; + quad[3] = rowColors[filter_roundedRowMap[3*16+code]]; + quad[4] = e; + + return quad; +} diff --git a/src/r_filter.h b/src/r_filter.h new file mode 100644 index 00000000..038e3d79 --- /dev/null +++ b/src/r_filter.h @@ -0,0 +1,116 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#ifndef R_FILTER_H +#define R_FILTER_H + +#define DITHER_DIM 4 + +extern byte filter_ditherMatrix[DITHER_DIM][DITHER_DIM]; +#define FILTER_UVBITS 6 +#define FILTER_UVDIM (1<> 8), which was empirically +// derived. the "-dcvars.yl" is apparently required to offset some minor +// shaking in coordinate y-axis and prevents dithering seams +#define FILTER_GETV(x,y,texV,nextRowTexV) \ + (filter_getDitheredPixelLevel(x, y, (((texV) - yl) >> 8)&0xff) ? ((nextRowTexV)>>FRACBITS) : ((texV)>>FRACBITS)) + +// Choose current column or next column to the right based on dither of the +// fractional texture U coord +#define filter_getDitheredForColumn(x, y, texV, nextRowTexV) \ + dither_sources[(filter_getDitheredPixelLevel(x, y, filter_fracu))][FILTER_GETV(x,y,texV,nextRowTexV)] + +#define filter_getRoundedForColumn(texV, nextRowTexV) \ + filter_getScale2xQuadColors( \ + source[ ((texV)>>FRACBITS) ], \ + source[ (MAX(0, ((texV)>>FRACBITS)-1)) ], \ + nextsource[ ((texV)>>FRACBITS) ], \ + source[ ((nextRowTexV)>>FRACBITS) ], \ + prevsource[ ((texV)>>FRACBITS) ] \ + ) \ + [ filter_roundedUVMap[ \ + ((filter_fracu>>(8-FILTER_UVBITS))<>8) & 0xff)>>(8-FILTER_UVBITS)) \ + ] ] + +#define filter_getRoundedForSpan(texU, texV) \ + filter_getScale2xQuadColors( \ + source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0) ], \ + source[ (((texU)>>16)&0x3f) | ((((texV)-FRACUNIT)>>10)&0xfc0) ], \ + source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0) ], \ + source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0) ], \ + source[ ((((texU)-FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0) ] \ + ) \ + [ filter_roundedUVMap[ \ + (((((texU)>>8) & 0xff)>>(8-FILTER_UVBITS))<>8) & 0xff)>>(8-FILTER_UVBITS)) \ + ] ] + +byte *filter_getScale2xQuadColors(byte e, byte b, byte f, byte h, byte d); + +#define filter_getFilteredForColumn15(depthmap, texV, nextRowTexV) ( \ + VID_PAL15( depthmap(nextsource[(nextRowTexV)>>FRACBITS]), (filter_fracu*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL15( depthmap(source[(nextRowTexV)>>FRACBITS]), ((0xffff-filter_fracu)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL15( depthmap(source[(texV)>>FRACBITS]), ((0xffff-filter_fracu)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) ) + \ + VID_PAL15( depthmap(nextsource[(texV)>>FRACBITS]), (filter_fracu*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS) )) + +#define filter_getFilteredForSpan15(depthmap, texU, texV) ( \ + VID_PAL15( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL15( depthmap(source[ (((texU)>>16)&0x3f) | ((((texV)+FRACUNIT)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*((texV)&0xffff))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL15( depthmap(source[ (((texU)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)((0xffff-((texU)&0xffff))*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS)) + \ + VID_PAL15( depthmap(source[ ((((texU)+FRACUNIT)>>16)&0x3f) | (((texV)>>10)&0xfc0)]), (unsigned int)(((texU)&0xffff)*(0xffff-((texV)&0xffff)))>>(32-VID_COLORWEIGHTBITS))) + +// do red and blue at once for slight speedup + +#define GETBLENDED15_5050(col1, col2) \ + ((((col1&0x7c1f)+(col2&0x7c1f))>>1)&0x7c1f) | \ + ((((col1&0x03e0)+(col2&0x03e0))>>1)&0x03e0) + +#define GETBLENDED15_3268(col1, col2) \ + ((((col1&0x7c1f)*5+(col2&0x7c1f)*11)>>4)&0x7c1f) | \ + ((((col1&0x03e0)*5+(col2&0x03e0)*11)>>4)&0x03e0) + +#define GETBLENDED15_9406(col1, col2) \ + ((((col1&0x7c1f)*15+(col2&0x7c1f))>>4)&0x7c1f) | \ + ((((col1&0x03e0)*15+(col2&0x03e0))>>4)&0x03e0) + +#endif diff --git a/src/r_fps.c b/src/r_fps.c new file mode 100644 index 00000000..f7190c5f --- /dev/null +++ b/src/r_fps.c @@ -0,0 +1,451 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Uncapped framerate stuff + * + *--------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "r_defs.h" +#include "r_state.h" +#include "p_spec.h" +#include "r_demo.h" +#include "r_fps.h" + +int movement_smooth = false; + +typedef enum +{ + INTERP_SectorFloor, + INTERP_SectorCeiling, + INTERP_Vertex, + INTERP_WallPanning, + INTERP_FloorPanning, + INTERP_CeilingPanning +} interpolation_type_e; + +typedef struct +{ + interpolation_type_e type; + void *address; +} interpolation_t; + +static int numinterpolations = 0; + +tic_vars_t tic_vars; + +view_vars_t original_view_vars; + +void D_Display(void); + +#define REALTICK_CLOCK_RATE 100 + +void R_InitInterpolation(void) +{ + tic_vars.msec = REALTICK_CLOCK_RATE * TICRATE / 100000.0f; +} + +typedef fixed_t fixed2_t[2]; +static fixed2_t *oldipos; +static fixed2_t *bakipos; +static interpolation_t *curipos; + +static boolean NoInterpolateView; +static boolean didInterp; +boolean WasRenderedInTryRunTics; + +void R_InterpolateView (player_t *player, fixed_t frac) +{ + if (movement_smooth) + { + if (NoInterpolateView) + { + NoInterpolateView = false; + original_view_vars.viewx = player->mo->x; + original_view_vars.viewy = player->mo->y; + original_view_vars.viewz = player->viewz; + + original_view_vars.viewangle = player->mo->angle + viewangleoffset; + } + + viewx = original_view_vars.viewx + FixedMul (frac, player->mo->x - original_view_vars.viewx); + viewy = original_view_vars.viewy + FixedMul (frac, player->mo->y - original_view_vars.viewy); + viewz = original_view_vars.viewz + FixedMul (frac, player->viewz - original_view_vars.viewz); + + viewangle = original_view_vars.viewangle + FixedMul (frac, R_SmoothPlaying_Get(player->mo->angle) + viewangleoffset - original_view_vars.viewangle); + } + else + { + viewx = player->mo->x; + viewy = player->mo->y; + viewz = player->viewz; + viewangle = R_SmoothPlaying_Get(player->mo->angle); + } +} + +void R_ResetViewInterpolation () +{ + NoInterpolateView = true; +} + +static void R_CopyInterpToOld (int i) +{ + switch (curipos[i].type) + { + case INTERP_SectorFloor: + oldipos[i][0] = ((sector_t*)curipos[i].address)->floorheight; + break; + case INTERP_SectorCeiling: + oldipos[i][0] = ((sector_t*)curipos[i].address)->ceilingheight; + break; + case INTERP_Vertex: + oldipos[i][0] = ((vertex_t*)curipos[i].address)->x; + oldipos[i][1] = ((vertex_t*)curipos[i].address)->y; + break; + case INTERP_WallPanning: + oldipos[i][0] = ((side_t*)curipos[i].address)->rowoffset; + oldipos[i][1] = ((side_t*)curipos[i].address)->textureoffset; + break; + case INTERP_FloorPanning: + oldipos[i][0] = ((sector_t*)curipos[i].address)->floor_xoffs; + oldipos[i][1] = ((sector_t*)curipos[i].address)->floor_yoffs; + break; + case INTERP_CeilingPanning: + oldipos[i][0] = ((sector_t*)curipos[i].address)->ceiling_xoffs; + oldipos[i][1] = ((sector_t*)curipos[i].address)->ceiling_yoffs; + break; + } +} + +static void R_CopyBakToInterp (int i) +{ + switch (curipos[i].type) + { + case INTERP_SectorFloor: + ((sector_t*)curipos[i].address)->floorheight = bakipos[i][0]; + break; + case INTERP_SectorCeiling: + ((sector_t*)curipos[i].address)->ceilingheight = bakipos[i][0]; + break; + case INTERP_Vertex: + ((vertex_t*)curipos[i].address)->x = bakipos[i][0]; + ((vertex_t*)curipos[i].address)->y = bakipos[i][1]; + break; + case INTERP_WallPanning: + ((side_t*)curipos[i].address)->rowoffset = bakipos[i][0]; + ((side_t*)curipos[i].address)->textureoffset = bakipos[i][1]; + break; + case INTERP_FloorPanning: + ((sector_t*)curipos[i].address)->floor_xoffs = bakipos[i][0]; + ((sector_t*)curipos[i].address)->floor_yoffs = bakipos[i][1]; + break; + case INTERP_CeilingPanning: + ((sector_t*)curipos[i].address)->ceiling_xoffs = bakipos[i][0]; + ((sector_t*)curipos[i].address)->ceiling_yoffs = bakipos[i][1]; + break; + } +} + +static void R_DoAnInterpolation (int i, fixed_t smoothratio) +{ + fixed_t pos; + fixed_t *adr1 = NULL; + fixed_t *adr2 = NULL; + + switch (curipos[i].type) + { + case INTERP_SectorFloor: + adr1 = &((sector_t*)curipos[i].address)->floorheight; + break; + case INTERP_SectorCeiling: + adr1 = &((sector_t*)curipos[i].address)->ceilingheight; + break; + case INTERP_Vertex: + adr1 = &((vertex_t*)curipos[i].address)->x; +//// adr2 = &((vertex_t*)curipos[i].Address)->y; + break; + case INTERP_WallPanning: + adr1 = &((side_t*)curipos[i].address)->rowoffset; + adr2 = &((side_t*)curipos[i].address)->textureoffset; + break; + case INTERP_FloorPanning: + adr1 = &((sector_t*)curipos[i].address)->floor_xoffs; + adr2 = &((sector_t*)curipos[i].address)->floor_yoffs; + break; + case INTERP_CeilingPanning: + adr1 = &((sector_t*)curipos[i].address)->ceiling_xoffs; + adr2 = &((sector_t*)curipos[i].address)->ceiling_yoffs; + break; + + default: + return; + } + + if (adr1) + { + pos = bakipos[i][0] = *adr1; + *adr1 = oldipos[i][0] + FixedMul (pos - oldipos[i][0], smoothratio); + } + + if (adr2) + { + pos = bakipos[i][1] = *adr2; + *adr2 = oldipos[i][1] + FixedMul (pos - oldipos[i][1], smoothratio); + } +} + +void R_UpdateInterpolations() +{ + int i; + if (!movement_smooth) + return; + for (i = numinterpolations-1; i >= 0; --i) + R_CopyInterpToOld (i); +} + +int interpolations_max = 0; + +static void R_SetInterpolation(interpolation_type_e type, void *posptr) +{ + int i; + if (!movement_smooth) + return; + + if (numinterpolations >= interpolations_max) { + interpolations_max = interpolations_max ? interpolations_max * 2 : 256; + + oldipos = (fixed2_t*)realloc(oldipos, sizeof(*oldipos) * interpolations_max); + bakipos = (fixed2_t*)realloc(bakipos, sizeof(*bakipos) * interpolations_max); + curipos = (interpolation_t*)realloc(curipos, sizeof(*curipos) * interpolations_max); + } + + for(i = numinterpolations-1; i >= 0; i--) + if (curipos[i].address == posptr && curipos[i].type == type) + return; + + curipos[numinterpolations].address = posptr; + curipos[numinterpolations].type = type; + R_CopyInterpToOld (numinterpolations); + numinterpolations++; +} + +static void R_StopInterpolation(interpolation_type_e type, void *posptr) +{ + int i; + + if (!movement_smooth) + return; + + for(i=numinterpolations-1; i>= 0; --i) + { + if (curipos[i].address == posptr && curipos[i].type == type) + { + numinterpolations--; + oldipos[i][0] = oldipos[numinterpolations][0]; + oldipos[i][1] = oldipos[numinterpolations][1]; + bakipos[i][0] = bakipos[numinterpolations][0]; + bakipos[i][1] = bakipos[numinterpolations][1]; + curipos[i] = curipos[numinterpolations]; + break; + } + } +} + +void R_StopAllInterpolations(void) +{ + int i; + + if (!movement_smooth) + return; + + for(i=numinterpolations-1; i>= 0; --i) + { + numinterpolations--; + oldipos[i][0] = oldipos[numinterpolations][0]; + oldipos[i][1] = oldipos[numinterpolations][1]; + bakipos[i][0] = bakipos[numinterpolations][0]; + bakipos[i][1] = bakipos[numinterpolations][1]; + curipos[i] = curipos[numinterpolations]; + } +} + +void R_DoInterpolations(fixed_t smoothratio) +{ + int i; + if (!movement_smooth) + return; + + if (smoothratio == FRACUNIT) + { + didInterp = false; + return; + } + + didInterp = true; + + for (i = numinterpolations-1; i >= 0; --i) + { + R_DoAnInterpolation (i, smoothratio); + } +} + +void R_RestoreInterpolations() +{ + int i; + + if (!movement_smooth) + return; + + if (didInterp) + { + didInterp = false; + for (i = numinterpolations-1; i >= 0; --i) + { + R_CopyBakToInterp (i); + } + } +} + +void R_ActivateSectorInterpolations() +{ + int i; + sector_t *sec; + + if (!movement_smooth) + return; + + for (i=0, sec = sectors ; ifloordata) + R_SetInterpolation (INTERP_SectorFloor, sec); + if (sec->ceilingdata) + R_SetInterpolation (INTERP_SectorCeiling, sec); + } +} + +static void R_InterpolationGetData(thinker_t *th, + interpolation_type_e *type1, interpolation_type_e *type2, + void **posptr1, void **posptr2) +{ + *posptr1 = NULL; + *posptr2 = NULL; + + if (th->function == T_MoveFloor) + { + *type1 = INTERP_SectorFloor; + *posptr1 = ((floormove_t *)th)->sector; + } + else + if (th->function == T_PlatRaise) + { + *type1 = INTERP_SectorFloor; + *posptr1 = ((plat_t *)th)->sector; + } + else + if (th->function == T_MoveCeiling) + { + *type1 = INTERP_SectorCeiling; + *posptr1 = ((ceiling_t *)th)->sector; + } + else + if (th->function == T_VerticalDoor) + { + *type1 = INTERP_SectorCeiling; + *posptr1 = ((vldoor_t *)th)->sector; + } + else + if (th->function == T_MoveElevator) + { + *type1 = INTERP_SectorFloor; + *posptr1 = ((elevator_t *)th)->sector; + *type2 = INTERP_SectorCeiling; + *posptr2 = ((elevator_t *)th)->sector; + } + else + if (th->function == T_Scroll) + { + switch (((scroll_t *)th)->type) + { + case sc_side: + *type1 = INTERP_WallPanning; + *posptr1 = sides + ((scroll_t *)th)->affectee; + break; + case sc_floor: + *type1 = INTERP_FloorPanning; + *posptr1 = sectors + ((scroll_t *)th)->affectee; + break; + case sc_ceiling: + *type1 = INTERP_CeilingPanning; + *posptr1 = sectors + ((scroll_t *)th)->affectee; + break; + default: ; + } + } +} + +void R_ActivateThinkerInterpolations(thinker_t *th) +{ + void *posptr1; + void *posptr2; + interpolation_type_e type1, type2; + + if (!movement_smooth) + return; + + R_InterpolationGetData(th, &type1, &type2, &posptr1, &posptr2); + + if(posptr1) + { + R_SetInterpolation (type1, posptr1); + + if(posptr2) + R_SetInterpolation (type2, posptr2); + } +} + +void R_StopInterpolationIfNeeded(thinker_t *th) +{ + void *posptr1; + void *posptr2; + interpolation_type_e type1, type2; + + if (!movement_smooth) + return; + + R_InterpolationGetData(th, &type1, &type2, &posptr1, &posptr2); + + if(posptr1) + { + R_StopInterpolation (type1, posptr1); + if(posptr2) + R_StopInterpolation (type2, posptr2); + } +} + diff --git a/src/r_fps.h b/src/r_fps.h new file mode 100644 index 00000000..bfbeab00 --- /dev/null +++ b/src/r_fps.h @@ -0,0 +1,76 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze, Andrey Budko + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Uncapped framerate stuff + * + *--------------------------------------------------------------------- + */ + +#ifndef __R_FPS__ +#define __R_FPS__ + +#include "doomstat.h" + +extern int movement_smooth; + +typedef struct { + fixed_t viewx; + fixed_t viewy; + fixed_t viewz; + angle_t viewangle; + angle_t viewpitch; +} view_vars_t; + +extern view_vars_t original_view_vars; + +typedef struct { + unsigned int start; + unsigned int next; + unsigned int step; + fixed_t frac; + float msec; +} tic_vars_t; + +extern tic_vars_t tic_vars; + +void R_InitInterpolation(void); +void R_InterpolateView (player_t *player, fixed_t frac); + +extern boolean WasRenderedInTryRunTics; + +void R_ResetViewInterpolation (); +void R_UpdateInterpolations(); +void R_StopAllInterpolations(void); +void R_DoInterpolations(fixed_t smoothratio); +void R_RestoreInterpolations(); +void R_ActivateSectorInterpolations(); +void R_ActivateThinkerInterpolations(thinker_t *th); +void R_StopInterpolationIfNeeded(thinker_t *th); + +#endif diff --git a/src/r_main.c b/src/r_main.c new file mode 100644 index 00000000..76942a86 --- /dev/null +++ b/src/r_main.c @@ -0,0 +1,587 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Rendering main loop and setup functions, + * utility functions (BSP, geometry, trigonometry). + * See tables.c, too. + * + *-----------------------------------------------------------------------------*/ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include "doomstat.h" +#include "d_net.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_things.h" +#include "r_plane.h" +#include "r_bsp.h" +#include "r_draw.h" +#include "m_bbox.h" +#include "r_sky.h" +#include "v_video.h" +#include "lprintf.h" +#include "st_stuff.h" +#include "i_main.h" +#include "i_system.h" +#include "g_game.h" +#include "r_demo.h" +#include "r_fps.h" + +// Fineangles in the SCREENWIDTH wide window. +#define FIELDOFVIEW 2048 + +// killough: viewangleoffset is a legacy from the pre-v1.2 days, when Doom +// had Left/Mid/Right viewing. +/-ANG90 offsets were placed here on each +// node, by d_net.c, to set up a L/M/R session. + +int viewangleoffset; +int validcount = 1; // increment every time a check is made +const lighttable_t *fixedcolormap; +int centerx, centery; +fixed_t centerxfrac, centeryfrac; +fixed_t viewheightfrac; //e6y: for correct clipping of things +fixed_t projection; +// proff 11/06/98: Added for high-res +fixed_t projectiony; +fixed_t viewx, viewy, viewz; +angle_t viewangle; +fixed_t viewcos, viewsin; +player_t *viewplayer; +extern lighttable_t **walllights; + +static mobj_t *oviewer; + +// +// precalculated math tables +// + +angle_t clipangle; + +// The viewangletox[viewangle + FINEANGLES/4] lookup +// maps the visible view angles to screen X coordinates, +// flattening the arc to a flat projection plane. +// There will be many angles mapped to the same X. + +int viewangletox[FINEANGLES/2]; + +// The xtoviewangleangle[] table maps a screen pixel +// to the lowest viewangle that maps back to x ranges +// from clipangle to -clipangle. + +angle_t xtoviewangle[MAX_SCREENWIDTH+1]; // killough 2/8/98 + +// killough 3/20/98: Support dynamic colormaps, e.g. deep water +// killough 4/4/98: support dynamic number of them as well + +int numcolormaps; +const lighttable_t *(*c_zlight)[LIGHTLEVELS][MAXLIGHTZ]; +const lighttable_t *(*zlight)[MAXLIGHTZ]; +const lighttable_t *fullcolormap; +const lighttable_t **colormaps; + +// killough 3/20/98, 4/4/98: end dynamic colormaps + +int extralight; // bumped light from gun blasts + +// +// R_PointOnSide +// Traverse BSP (sub) tree, +// check point against partition plane. +// Returns side 0 (front) or 1 (back). +// +// killough 5/2/98: reformatted +// + +PUREFUNC int R_PointOnSide(fixed_t x, fixed_t y, const node_t *node) +{ + if (!node->dx) + return x <= node->x ? node->dy > 0 : node->dy < 0; + + if (!node->dy) + return y <= node->y ? node->dx < 0 : node->dx > 0; + + x -= node->x; + y -= node->y; + + // Try to quickly decide by looking at sign bits. + if ((node->dy ^ node->dx ^ x ^ y) < 0) + return (node->dy ^ x) < 0; // (left is negative) + return FixedMul(y, node->dx>>FRACBITS) >= FixedMul(node->dy>>FRACBITS, x); +} + +// killough 5/2/98: reformatted + +PUREFUNC int R_PointOnSegSide(fixed_t x, fixed_t y, const seg_t *line) +{ + fixed_t lx = line->v1->x; + fixed_t ly = line->v1->y; + fixed_t ldx = line->v2->x - lx; + fixed_t ldy = line->v2->y - ly; + + if (!ldx) + return x <= lx ? ldy > 0 : ldy < 0; + + if (!ldy) + return y <= ly ? ldx < 0 : ldx > 0; + + x -= lx; + y -= ly; + + // Try to quickly decide by looking at sign bits. + if ((ldy ^ ldx ^ x ^ y) < 0) + return (ldy ^ x) < 0; // (left is negative) + return FixedMul(y, ldx>>FRACBITS) >= FixedMul(ldy>>FRACBITS, x); +} + +// +// R_PointToAngle +// To get a global angle from cartesian coordinates, +// the coordinates are flipped until they are in +// the first octant of the coordinate system, then +// the y (<=x) is scaled and divided by x to get a +// tangent (slope) value which is looked up in the +// tantoangle[] table. The +1 size of tantoangle[] +// is to handle the case when x==y without additional +// checking. +// +// killough 5/2/98: reformatted, cleaned up + +#include + +angle_t R_PointToAngle(fixed_t x, fixed_t y) +{ + static fixed_t oldx, oldy; + static angle_t oldresult; + + x -= viewx; y -= viewy; + + if ( /* !render_precise && */ + // e6y: here is where "slime trails" can SOMETIMES occur + (x < INT_MAX/4 && x > -INT_MAX/4 && y < INT_MAX/4 && y > -INT_MAX/4) + ) + { + // old R_PointToAngle + return (x || y) ? + x >= 0 ? + y >= 0 ? + (x > y) ? tantoangle[SlopeDiv(y,x)] : // octant 0 + ANG90-1-tantoangle[SlopeDiv(x,y)] : // octant 1 + x > (y = -y) ? 0-tantoangle[SlopeDiv(y,x)] : // octant 8 + ANG270+tantoangle[SlopeDiv(x,y)] : // octant 7 + y >= 0 ? (x = -x) > y ? ANG180-1-tantoangle[SlopeDiv(y,x)] : // octant 3 + ANG90 + tantoangle[SlopeDiv(x,y)] : // octant 2 + (x = -x) > (y = -y) ? ANG180+tantoangle[ SlopeDiv(y,x)] : // octant 4 + ANG270-1-tantoangle[SlopeDiv(x,y)] : // octant 5 + 0; + } + + // R_PointToAngleEx merged into R_PointToAngle + // e6y: The precision of the code above is abysmal so use the CRT atan2 function instead! + if (oldx != x || oldy != y) + { + oldx = x; + oldy = y; + oldresult = (int)(atan2(y, x) * ANG180/M_PI); + } + return oldresult; +} + +angle_t R_PointToAngle2(fixed_t viewx, fixed_t viewy, fixed_t x, fixed_t y) +{ + return (y -= viewy, (x -= viewx) || y) ? + x >= 0 ? + y >= 0 ? + (x > y) ? tantoangle[SlopeDiv(y,x)] : // octant 0 + ANG90-1-tantoangle[SlopeDiv(x,y)] : // octant 1 + x > (y = -y) ? 0-tantoangle[SlopeDiv(y,x)] : // octant 8 + ANG270+tantoangle[SlopeDiv(x,y)] : // octant 7 + y >= 0 ? (x = -x) > y ? ANG180-1-tantoangle[SlopeDiv(y,x)] : // octant 3 + ANG90 + tantoangle[SlopeDiv(x,y)] : // octant 2 + (x = -x) > (y = -y) ? ANG180+tantoangle[ SlopeDiv(y,x)] : // octant 4 + ANG270-1-tantoangle[SlopeDiv(x,y)] : // octant 5 + 0; +} + +// +// R_InitTextureMapping +// +// killough 5/2/98: reformatted + +static void R_InitTextureMapping (void) +{ + register int i,x; + fixed_t focallength; + + // Use tangent table to generate viewangletox: + // viewangletox will give the next greatest x + // after the view angle. + // + // Calc focallength + // so FIELDOFVIEW angles covers SCREENWIDTH. + + focallength = FixedDiv(centerxfrac, finetangent[FINEANGLES/4+FIELDOFVIEW/2]); + + for (i=0 ; i FRACUNIT*2) + t = -1; + else + if (finetangent[i] < -FRACUNIT*2) + t = viewwidth+1; + else + { + t = FixedMul(finetangent[i], focallength); + t = (centerxfrac - t + FRACUNIT-1) >> FRACBITS; + if (t < -1) + t = -1; + else + if (t > viewwidth+1) + t = viewwidth+1; + } + viewangletox[i] = t; + } + + // Scan viewangletox[] to generate xtoviewangle[]: + // xtoviewangle will give the smallest view angle + // that maps to x. + + for (x=0; x<=viewwidth; x++) + { + for (i=0; viewangletox[i] > x; i++) + ; + xtoviewangle[x] = (i<>= LIGHTSCALESHIFT)/DISTMAP; + + if (level < 0) + level = 0; + else + if (level >= NUMCOLORMAPS) + level = NUMCOLORMAPS-1; + + // killough 3/20/98: Initialize multiple colormaps + level *= 256; + for (t=0; t>ANGLETOFINESHIFT]); + distscale[i] = FixedDiv(FRACUNIT,cosadj); + } + +} + +// +// R_Init +// + +extern int screenblocks; + +void R_Init (void) +{ + // CPhipps - R_DrawColumn isn't constant anymore, so must + // initialise in code + // current column draw function + lprintf(LO_INFO, "\nR_LoadTrigTables: "); + R_LoadTrigTables(); + lprintf(LO_INFO, "\nR_InitData: "); + R_InitData(); + R_SetViewSize(screenblocks); + lprintf(LO_INFO, "\nR_Init: R_InitPlanes "); + R_InitPlanes(); + lprintf(LO_INFO, "R_InitLightTables "); + R_InitLightTables(); + lprintf(LO_INFO, "R_InitSkyMap "); + R_InitSkyMap(); + lprintf(LO_INFO, "R_InitTranslationsTables "); + R_InitTranslationTables(); + lprintf(LO_INFO, "R_InitPatches "); + R_InitPatches(); +} + +// +// R_PointInSubsector +// +// killough 5/2/98: reformatted, cleaned up + +subsector_t *R_PointInSubsector(fixed_t x, fixed_t y) +{ + int nodenum = numnodes-1; + + // special case for trivial maps (single subsector, no nodes) + if (numnodes == 0) + return subsectors; + + while (!(nodenum & NF_SUBSECTOR)) + nodenum = nodes[nodenum].children[R_PointOnSide(x, y, nodes+nodenum)]; + return &subsectors[nodenum & ~NF_SUBSECTOR]; +} + +// +// R_SetupFrame +// + +static void R_SetupFrame (player_t *player) +{ + int cm; + boolean NoInterpolate = paused || (menuactive && !demoplayback); + + viewplayer = player; + + if (player->mo != oviewer || NoInterpolate) + { + R_ResetViewInterpolation (); + oviewer = player->mo; + } + tic_vars.frac = I_GetTimeFrac (); + if (NoInterpolate) + tic_vars.frac = FRACUNIT; + R_InterpolateView (player, tic_vars.frac); + + extralight = player->extralight; + + viewsin = finesine[viewangle>>ANGLETOFINESHIFT]; + viewcos = finecosine[viewangle>>ANGLETOFINESHIFT]; + + R_DoInterpolations(tic_vars.frac); + + // killough 3/20/98, 4/4/98: select colormap based on player status + + if (player->mo->subsector->sector->heightsec != -1) + { + const sector_t *s = player->mo->subsector->sector->heightsec + sectors; + cm = viewz < s->floorheight ? s->bottommap : viewz > s->ceilingheight ? + s->topmap : s->midmap; + if (cm < 0 || cm > numcolormaps) + cm = 0; + } + else + cm = 0; + + fullcolormap = colormaps[cm]; + zlight = c_zlight[cm]; + + if (player->fixedcolormap) + { + fixedcolormap = fullcolormap // killough 3/20/98: use fullcolormap + + player->fixedcolormap*256*sizeof(lighttable_t); + } + else + fixedcolormap = 0; + + validcount++; +} + +int autodetect_hom = 0; // killough 2/7/98: HOM autodetection flag + +// +// R_ShowStats +// +int rendered_visplanes, rendered_segs, rendered_vissprites; +boolean rendering_stats; + +static void R_ShowStats(void) +{ +//e6y +#define KEEPTIMES 10 + static int keeptime[KEEPTIMES], showtime; + int now = I_GetTime(); + + if (now - showtime > 35) { + doom_printf("Frame rate %d fps\nSegs %d, Visplanes %d, Sprites %d", + (35*KEEPTIMES)/(now - keeptime[0]), rendered_segs, + rendered_visplanes, rendered_vissprites); + showtime = now; + } + memmove(keeptime, keeptime+1, sizeof(keeptime[0]) * (KEEPTIMES-1)); + keeptime[KEEPTIMES-1] = now; +} + +// +// R_RenderView +// +void R_RenderPlayerView (player_t* player) +{ + R_SetupFrame (player); + + // Clear buffers. + R_ClearClipSegs (); + R_ClearDrawSegs (); + R_ClearPlanes (); + R_ClearSprites (); + + rendered_segs = rendered_visplanes = 0; + if (autodetect_hom) + { // killough 2/10/98: add flashing red HOM indicators + unsigned char color=(gametic % 20) < 9 ? 0xb0 : 0; + V_FillRect(0, 0, 0, viewwidth, viewheight, color); + } + + // check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + // The head node is the last node output. + R_RenderBSPNode (numnodes-1); + R_ResetColumnBuffer(); + + // Check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + R_DrawPlanes (); + + // Check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + R_DrawMasked (); + R_ResetColumnBuffer(); + + // Check for new console commands. +#ifdef HAVE_NET + NetUpdate (); +#endif + + if (rendering_stats) R_ShowStats(); + + R_RestoreInterpolations(); +} diff --git a/src/r_main.h b/src/r_main.h new file mode 100644 index 00000000..1efde437 --- /dev/null +++ b/src/r_main.h @@ -0,0 +1,118 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Renderer main interface. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_MAIN__ +#define __R_MAIN__ + +#include "d_player.h" +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +// +// POV related. +// + +extern fixed_t viewcos; +extern fixed_t viewsin; +extern int viewwidth; +extern int viewheight; +extern int centerx; +extern int centery; +extern fixed_t centerxfrac; +extern fixed_t centeryfrac; +extern fixed_t viewheightfrac; //e6y: for correct clipping of things +extern fixed_t projection; +// proff 11/06/98: Added for high-res +extern fixed_t projectiony; +extern int validcount; + +// +// Rendering stats +// + +extern int rendered_visplanes, rendered_segs, rendered_vissprites; +extern boolean rendering_stats; + +// +// Lighting LUT. +// Used for z-depth cuing per column/row, +// and other lighting effects (sector ambient, flash). +// + +// Lighting constants. + +#define LIGHTLEVELS 16 +#define LIGHTSEGSHIFT 4 +#define MAXLIGHTSCALE 48 +#define LIGHTSCALESHIFT 12 +#define MAXLIGHTZ 128 +#define LIGHTZSHIFT 20 + +// killough 3/20/98: Allow colormaps to be dynamic (e.g. underwater) +extern const lighttable_t *(*zlight)[MAXLIGHTZ]; +extern const lighttable_t *fullcolormap; +extern int numcolormaps; // killough 4/4/98: dynamic number of maps +extern const lighttable_t **colormaps; +// killough 3/20/98, 4/4/98: end dynamic colormaps + +extern int extralight; +extern const lighttable_t *fixedcolormap; + +// Number of diminishing brightness levels. +// There a 0-31, i.e. 32 LUT in the COLORMAP lump. + +#define NUMCOLORMAPS 32 + +// +// Utility functions. +// + +PUREFUNC int R_PointOnSide(fixed_t x, fixed_t y, const node_t *node); +PUREFUNC int R_PointOnSegSide(fixed_t x, fixed_t y, const seg_t *line); +angle_t R_PointToAngle(fixed_t x, fixed_t y); +angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2); +subsector_t *R_PointInSubsector(fixed_t x, fixed_t y); + +// +// REFRESH - the actual rendering functions. +// + +void R_RenderPlayerView(player_t *player); // Called by G_Drawer. +void R_Init(void); // Called by startup code. +void R_SetViewSize(int blocks); // Called by M_Responder. +void R_ExecuteSetViewSize(void); // cph - called by D_Display to complete a view resize + +#endif diff --git a/src/r_patch.c b/src/r_patch.c new file mode 100644 index 00000000..ce928043 --- /dev/null +++ b/src/r_patch.c @@ -0,0 +1,759 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2002 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + +#include "z_zone.h" +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_sky.h" +#include "r_bsp.h" +#include "r_things.h" +#include "p_tick.h" +#include "i_system.h" +#include "r_draw.h" +#include "lprintf.h" +#include "r_patch.h" +#include + +// posts are runs of non masked source pixels +typedef struct +{ + byte topdelta; // -1 is the last post in a column + byte length; // length data bytes follows +} post_t; + +// column_t is a list of 0 or more post_t, (byte)-1 terminated +typedef post_t column_t; + +// +// Patches. +// A patch holds one or more columns. +// Patches are used for sprites and all masked pictures, +// and we compose textures from the TEXTURE1/2 lists +// of patches. +// + +typedef struct +{ + short width, height; // bounding box size + short leftoffset; // pixels to the left of origin + short topoffset; // pixels below the origin + int columnofs[8]; // only [width] used +} patch_t; + +//--------------------------------------------------------------------------- +// Re-engineered patch support +//--------------------------------------------------------------------------- +static rpatch_t *patches = 0; + +static rpatch_t *texture_composites = 0; + +//--------------------------------------------------------------------------- +void R_InitPatches(void) { + if (!patches) + { + patches = (rpatch_t*)malloc(numlumps * sizeof(rpatch_t)); + // clear out new patches to signal they're uninitialized + memset(patches, 0, sizeof(rpatch_t)*numlumps); + } + if (!texture_composites) + { + texture_composites = (rpatch_t*)malloc(numtextures * sizeof(rpatch_t)); + // clear out new patches to signal they're uninitialized + memset(texture_composites, 0, sizeof(rpatch_t)*numtextures); + } +} + +//--------------------------------------------------------------------------- +void R_FlushAllPatches(void) { + int i; + + if (patches) + { + for (i=0; i < numlumps; i++) + if (patches[i].locks > 0) + I_Error("R_FlushAllPatches: patch number %i still locked",i); + free(patches); + patches = NULL; + } + if (texture_composites) + { + for (i=0; iwidth; + R_UnlockPatchNum(lump); + return width; +} + +//--------------------------------------------------------------------------- +int R_NumPatchHeight(int lump) +{ + const rpatch_t *patch = R_CachePatchNum(lump); + int height = patch->height; + R_UnlockPatchNum(lump); + return height; +} + +//--------------------------------------------------------------------------- +static int getPatchIsNotTileable(const patch_t *patch) { + int x=0, numPosts, lastColumnDelta = 0; + const column_t *column; + int cornerCount = 0; + int hasAHole = 0; + + for (x=0; xwidth); x++) { + column = (const column_t *)((const byte *)patch + LONG(patch->columnofs[x])); + if (!x) lastColumnDelta = column->topdelta; + else if (lastColumnDelta != column->topdelta) hasAHole = 1; + + numPosts = 0; + while (column->topdelta != 0xff) { + // check to see if a corner pixel filled + if (x == 0 && column->topdelta == 0) cornerCount++; + else if (x == 0 && column->topdelta + column->length >= SHORT(patch->height)) cornerCount++; + else if (x == SHORT(patch->width)-1 && column->topdelta == 0) cornerCount++; + else if (x == SHORT(patch->width)-1 && column->topdelta + column->length >= SHORT(patch->height)) cornerCount++; + + if (numPosts++) hasAHole = 1; + column = (const column_t *)((const byte *)column + column->length + 4); + } + } + + if (cornerCount == 4) return 0; + return hasAHole; +} + +//--------------------------------------------------------------------------- +static int getIsSolidAtSpot(const column_t *column, int spot) { + if (!column) return 0; + while (column->topdelta != 0xff) { + if (spot < column->topdelta) return 0; + if ((spot >= column->topdelta) && (spot <= column->topdelta + column->length)) return 1; + column = (const column_t*)((const byte*)column + 3 + column->length + 1); + } + return 0; +} + +//--------------------------------------------------------------------------- +// Used to determine whether a column edge (top or bottom) should slope +// up or down for smoothed masked edges - POPE +//--------------------------------------------------------------------------- +static int getColumnEdgeSlope(const column_t *prevcolumn, const column_t *nextcolumn, int spot) { + int holeToLeft = !getIsSolidAtSpot(prevcolumn, spot); + int holeToRight = !getIsSolidAtSpot(nextcolumn, spot); + + if (holeToLeft && !holeToRight) return 1; + if (!holeToLeft && holeToRight) return -1; + return 0; +} + +//--------------------------------------------------------------------------- +static void createPatch(int id) { + rpatch_t *patch; + const int patchNum = id; + const patch_t *oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); + const column_t *oldColumn, *oldPrevColumn, *oldNextColumn; + int x, y; + int pixelDataSize; + int columnsDataSize; + int postsDataSize; + int dataSize; + int *numPostsInColumn; + int numPostsTotal; + const unsigned char *oldColumnPixelData; + int numPostsUsedSoFar; + int edgeSlope; + + patch = &patches[id]; + // proff - 2003-02-16 What about endianess? + patch->width = SHORT(oldPatch->width); + patch->widthmask = 0; + patch->height = SHORT(oldPatch->height); + patch->leftoffset = SHORT(oldPatch->leftoffset); + patch->topoffset = SHORT(oldPatch->topoffset); + patch->isNotTileable = getPatchIsNotTileable(oldPatch); + + // work out how much memory we need to allocate for this patch's data + pixelDataSize = (patch->width * patch->height + 4) & ~3; + columnsDataSize = sizeof(rcolumn_t) * patch->width; + + // count the number of posts in each column + numPostsInColumn = (int*)malloc(sizeof(int) * patch->width); + numPostsTotal = 0; + + for (x=0; xwidth; x++) { + oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); + numPostsInColumn[x] = 0; + while (oldColumn->topdelta != 0xff) { + numPostsInColumn[x]++; + numPostsTotal++; + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + } + } + + postsDataSize = numPostsTotal * sizeof(rpost_t); + + // allocate our data chunk + dataSize = pixelDataSize + columnsDataSize + postsDataSize; + patch->data = (unsigned char*)Z_Malloc(dataSize, PU_CACHE, (void **)&patch->data); + memset(patch->data, 0, dataSize); + + // set out pixel, column, and post pointers into our data array + patch->pixels = patch->data; + patch->columns = (rcolumn_t*)((unsigned char*)patch->pixels + pixelDataSize); + patch->posts = (rpost_t*)((unsigned char*)patch->columns + columnsDataSize); + + // sanity check that we've got all the memory allocated we need + assert((((byte*)patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)patch->data) == dataSize); + + memset(patch->pixels, 0xff, (patch->width*patch->height)); + + // fill in the pixels, posts, and columns + numPostsUsedSoFar = 0; + for (x=0; xwidth; x++) { + int top = -1; + + oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); + + if (patch->isNotTileable) { + // non-tiling + if (x == 0) oldPrevColumn = 0; + else oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x-1])); + if (x == patch->width-1) oldNextColumn = 0; + else oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x+1])); + } + else { + // tiling + int prevColumnIndex = x-1; + int nextColumnIndex = x+1; + while (prevColumnIndex < 0) prevColumnIndex += patch->width; + while (nextColumnIndex >= patch->width) nextColumnIndex -= patch->width; + oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[prevColumnIndex])); + oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[nextColumnIndex])); + } + + // setup the column's data + patch->columns[x].pixels = patch->pixels + (x*patch->height) + 0; + patch->columns[x].numPosts = numPostsInColumn[x]; + patch->columns[x].posts = patch->posts + numPostsUsedSoFar; + + while (oldColumn->topdelta != 0xff) { + + //e6y: support for DeePsea's true tall patches + if (oldColumn->topdelta <= top) + { + top += oldColumn->topdelta; + } + else + { + top = oldColumn->topdelta; + } + + // set up the post's data + patch->posts[numPostsUsedSoFar].topdelta = top; + patch->posts[numPostsUsedSoFar].length = oldColumn->length; + patch->posts[numPostsUsedSoFar].slope = 0; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, top); + if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_UP; + else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_TOP_DOWN; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, top+oldColumn->length); + if (edgeSlope == 1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_UP; + else if (edgeSlope == -1) patch->posts[numPostsUsedSoFar].slope |= RDRAW_EDGESLOPE_BOT_DOWN; + + // fill in the post's pixels + oldColumnPixelData = (const byte *)oldColumn + 3; + for (y=0; ylength; y++) { + patch->pixels[x * patch->height + top + y] = oldColumnPixelData[y]; + } + + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + numPostsUsedSoFar++; + } + } + + if (1 || patch->isNotTileable) { + const rcolumn_t *column, *prevColumn; + + // copy the patch image down and to the right where there are + // holes to eliminate the black halo from bilinear filtering + for (x=0; xwidth; x++) { + //oldColumn = (const column_t *)((const byte *)oldPatch + oldPatch->columnofs[x]); + + column = R_GetPatchColumnClamped(patch, x); + prevColumn = R_GetPatchColumnClamped(patch, x-1); + + if (column->pixels[0] == 0xff) { + // force the first pixel (which is a hole), to use + // the color from the next solid spot in the column + for (y=0; yheight; y++) { + if (column->pixels[y] != 0xff) { + column->pixels[0] = column->pixels[y]; + break; + } + } + } + + // copy from above or to the left + for (y=1; yheight; y++) { + //if (getIsSolidAtSpot(oldColumn, y)) continue; + if (column->pixels[y] != 0xff) continue; + + // this pixel is a hole + + if (x && prevColumn->pixels[y-1] != 0xff) { + // copy the color from the left + column->pixels[y] = prevColumn->pixels[y]; + } + else { + // copy the color from above + column->pixels[y] = column->pixels[y-1]; + } + } + } + + // verify that the patch truly is non-rectangular since + // this determines tiling later on + } + + W_UnlockLumpNum(patchNum); + free(numPostsInColumn); +} + +typedef struct { + unsigned short patches; + unsigned short posts; + unsigned short posts_used; +} count_t; + +static void switchPosts(rpost_t *post1, rpost_t *post2) { + rpost_t dummy; + + dummy.topdelta = post1->topdelta; + dummy.length = post1->length; + dummy.slope = post1->slope; + post1->topdelta = post2->topdelta; + post1->length = post2->length; + post1->slope = post2->slope; + post2->topdelta = dummy.topdelta; + post2->length = dummy.length; + post2->slope = dummy.slope; +} + +static void removePostFromColumn(rcolumn_t *column, int post) { + int i; + if (post < column->numPosts) + for (i=post; i<(column->numPosts-1); i++) { + rpost_t *post1 = &column->posts[i]; + rpost_t *post2 = &column->posts[i+1]; + post1->topdelta = post2->topdelta; + post1->length = post2->length; + post1->slope = post2->slope; + } + column->numPosts--; +} + +//--------------------------------------------------------------------------- +static void createTextureCompositePatch(int id) { + rpatch_t *composite_patch; + texture_t *texture; + texpatch_t *texpatch; + int patchNum; + const patch_t *oldPatch; + const column_t *oldColumn, *oldPrevColumn, *oldNextColumn; + int i, x, y; + int oy, count; + int pixelDataSize; + int columnsDataSize; + int postsDataSize; + int dataSize; + int numPostsTotal; + const unsigned char *oldColumnPixelData; + int numPostsUsedSoFar; + int edgeSlope; + count_t *countsInColumn; + + + composite_patch = &texture_composites[id]; + + texture = textures[id]; + + composite_patch->width = texture->width; + composite_patch->height = texture->height; + composite_patch->widthmask = texture->widthmask; + composite_patch->leftoffset = 0; + composite_patch->topoffset = 0; + composite_patch->isNotTileable = 0; + + // work out how much memory we need to allocate for this patch's data + pixelDataSize = (composite_patch->width * composite_patch->height + 4) & ~3; + columnsDataSize = sizeof(rcolumn_t) * composite_patch->width; + + // count the number of posts in each column + countsInColumn = (count_t *)calloc(sizeof(count_t), composite_patch->width); + numPostsTotal = 0; + + for (i=0; ipatchcount; i++) { + texpatch = &texture->patches[i]; + patchNum = texpatch->patch; + oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); + + for (x=0; xwidth); x++) { + int tx = texpatch->originx + x; + + if (tx < 0) + continue; + if (tx >= composite_patch->width) + break; + + countsInColumn[tx].patches++; + + oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); + while (oldColumn->topdelta != 0xff) { + countsInColumn[tx].posts++; + numPostsTotal++; + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + } + } + + W_UnlockLumpNum(patchNum); + } + + postsDataSize = numPostsTotal * sizeof(rpost_t); + + // allocate our data chunk + dataSize = pixelDataSize + columnsDataSize + postsDataSize; + composite_patch->data = (unsigned char*)Z_Malloc(dataSize, PU_STATIC, (void **)&composite_patch->data); + memset(composite_patch->data, 0, dataSize); + + // set out pixel, column, and post pointers into our data array + composite_patch->pixels = composite_patch->data; + composite_patch->columns = (rcolumn_t*)((unsigned char*)composite_patch->pixels + pixelDataSize); + composite_patch->posts = (rpost_t*)((unsigned char*)composite_patch->columns + columnsDataSize); + + // sanity check that we've got all the memory allocated we need + assert((((byte*)composite_patch->posts + numPostsTotal*sizeof(rpost_t)) - (byte*)composite_patch->data) == dataSize); + + memset(composite_patch->pixels, 0xff, (composite_patch->width*composite_patch->height)); + + numPostsUsedSoFar = 0; + + for (x=0; xwidth; x++) { + // setup the column's data + composite_patch->columns[x].pixels = composite_patch->pixels + (x*composite_patch->height); + composite_patch->columns[x].numPosts = countsInColumn[x].posts; + composite_patch->columns[x].posts = composite_patch->posts + numPostsUsedSoFar; + numPostsUsedSoFar += countsInColumn[x].posts; + } + + // fill in the pixels, posts, and columns + for (i=0; ipatchcount; i++) { + texpatch = &texture->patches[i]; + patchNum = texpatch->patch; + oldPatch = (const patch_t*)W_CacheLumpNum(patchNum); + + for (x=0; xwidth); x++) { + int top = -1; + int tx = texpatch->originx + x; + + if (tx < 0) + continue; + if (tx >= composite_patch->width) + break; + + oldColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[x])); + + { + // tiling + int prevColumnIndex = x-1; + int nextColumnIndex = x+1; + while (prevColumnIndex < 0) prevColumnIndex += SHORT(oldPatch->width); + while (nextColumnIndex >= SHORT(oldPatch->width)) nextColumnIndex -= SHORT(oldPatch->width); + oldPrevColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[prevColumnIndex])); + oldNextColumn = (const column_t *)((const byte *)oldPatch + LONG(oldPatch->columnofs[nextColumnIndex])); + } + + while (oldColumn->topdelta != 0xff) { + rpost_t *post = &composite_patch->columns[tx].posts[countsInColumn[tx].posts_used]; + + //e6y: support for DeePsea's true tall patches + if (oldColumn->topdelta <= top) + { + top += oldColumn->topdelta; + } + else + { + top = oldColumn->topdelta; + } + + oldColumnPixelData = (const byte *)oldColumn + 3; + oy = texpatch->originy; + count = oldColumn->length; + // the original renderer had several bugs which we reproduce here + if (countsInColumn[tx].patches > 1) { + // when there are multiple patches, then we need to handle the + // column differently + if (i == 0) { + // draw first patch at original position, it will be partly + // overdrawn below + for (y=0; y= composite_patch->height) + break; + composite_patch->pixels[tx * composite_patch->height + ty] = oldColumnPixelData[y]; + } + } + // do the buggy clipping + if ((oy + top) < 0) { + count += oy; + oy = 0; + } + } else { + // with a single patch only negative y origins are wrong + oy = 0; + } + // set up the post's data + post->topdelta = top + oy; + post->length = count; + if ((post->topdelta + post->length) > composite_patch->height) { + if (post->topdelta > composite_patch->height) + post->length = 0; + else + post->length = composite_patch->height - post->topdelta; + } + if (post->topdelta < 0) { + if ((post->topdelta + post->length) <= 0) + post->length = 0; + else + post->length -= post->topdelta; + post->topdelta = 0; + } + post->slope = 0; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, top); + if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_TOP_UP; + else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_TOP_DOWN; + + edgeSlope = getColumnEdgeSlope(oldPrevColumn, oldNextColumn, top+count); + if (edgeSlope == 1) post->slope |= RDRAW_EDGESLOPE_BOT_UP; + else if (edgeSlope == -1) post->slope |= RDRAW_EDGESLOPE_BOT_DOWN; + + // fill in the post's pixels + for (y=0; y= composite_patch->height) + break; + composite_patch->pixels[tx * composite_patch->height + ty] = oldColumnPixelData[y]; + } + + oldColumn = (const column_t *)((const byte *)oldColumn + oldColumn->length + 4); + countsInColumn[tx].posts_used++; + assert(countsInColumn[tx].posts_used <= countsInColumn[tx].posts); + } + } + + W_UnlockLumpNum(patchNum); + } + + for (x=0; xwidth; x++) { + rcolumn_t *column; + + if (countsInColumn[x].patches <= 1) + continue; + + // cleanup posts on multipatch columns + column = &composite_patch->columns[x]; + + i = 0; + while (i<(column->numPosts-1)) { + rpost_t *post1 = &column->posts[i]; + rpost_t *post2 = &column->posts[i+1]; + int length; + + if ((post2->topdelta - post1->topdelta) < 0) + switchPosts(post1, post2); + + if ((post1->topdelta + post1->length) >= post2->topdelta) { + length = (post1->length + post2->length) - ((post1->topdelta + post1->length) - post2->topdelta); + if (post1->length < length) { + post1->slope = post2->slope; + post1->length = length; + } + removePostFromColumn(column, i+1); + i = 0; + continue; + } + i++; + } + } + + if (1 || composite_patch->isNotTileable) { + const rcolumn_t *column, *prevColumn; + + // copy the patch image down and to the right where there are + // holes to eliminate the black halo from bilinear filtering + for (x=0; xwidth; x++) { + //oldColumn = (const column_t *)((const byte *)oldPatch + oldPatch->columnofs[x]); + + column = R_GetPatchColumnClamped(composite_patch, x); + prevColumn = R_GetPatchColumnClamped(composite_patch, x-1); + + if (column->pixels[0] == 0xff) { + // force the first pixel (which is a hole), to use + // the color from the next solid spot in the column + for (y=0; yheight; y++) { + if (column->pixels[y] != 0xff) { + column->pixels[0] = column->pixels[y]; + break; + } + } + } + + // copy from above or to the left + for (y=1; yheight; y++) { + //if (getIsSolidAtSpot(oldColumn, y)) continue; + if (column->pixels[y] != 0xff) continue; + + // this pixel is a hole + + if (x && prevColumn->pixels[y-1] != 0xff) { + // copy the color from the left + column->pixels[y] = prevColumn->pixels[y]; + } + else { + // copy the color from above + column->pixels[y] = column->pixels[y-1]; + } + } + } + + // verify that the patch truly is non-rectangular since + // this determines tiling later on + } + + free(countsInColumn); +} + +//--------------------------------------------------------------------------- +const rpatch_t *R_CachePatchNum(int id) { + const int locks = 1; + + if (!patches) + I_Error("R_CachePatchNum: Patches not initialized"); + + if (!patches[id].data) + createPatch(id); + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (!patches[id].locks && locks) { + Z_ChangeTag(patches[id].data,PU_STATIC); + } + patches[id].locks += locks; + + return &patches[id]; +} + +void R_UnlockPatchNum(int id) +{ + const int unlocks = 1; + patches[id].locks -= unlocks; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (unlocks && !patches[id].locks) + Z_ChangeTag(patches[id].data, PU_CACHE); +} + +//--------------------------------------------------------------------------- +const rpatch_t *R_CacheTextureCompositePatchNum(int id) { + const int locks = 1; + + if (!texture_composites) + I_Error("R_CacheTextureCompositePatchNum: Composite patches not initialized"); + + if (!texture_composites[id].data) + createTextureCompositePatch(id); + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (!texture_composites[id].locks && locks) { + Z_ChangeTag(texture_composites[id].data,PU_STATIC); + } + texture_composites[id].locks += locks; + + return &texture_composites[id]; + +} + +void R_UnlockTextureCompositePatchNum(int id) +{ + const int unlocks = 1; + texture_composites[id].locks -= unlocks; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (unlocks && !texture_composites[id].locks) + Z_ChangeTag(texture_composites[id].data, PU_CACHE); +} + +//--------------------------------------------------------------------------- +const rcolumn_t *R_GetPatchColumnWrapped(const rpatch_t *patch, int columnIndex) { + while (columnIndex < 0) columnIndex += patch->width; + columnIndex %= patch->width; + return &patch->columns[columnIndex]; +} + +//--------------------------------------------------------------------------- +const rcolumn_t *R_GetPatchColumnClamped(const rpatch_t *patch, int columnIndex) { + if (columnIndex < 0) columnIndex = 0; + if (columnIndex >= patch->width) columnIndex = patch->width-1; + return &patch->columns[columnIndex]; +} + +//--------------------------------------------------------------------------- +const rcolumn_t *R_GetPatchColumn(const rpatch_t *patch, int columnIndex) { + if (patch->isNotTileable) return R_GetPatchColumnClamped(patch, columnIndex); + else return R_GetPatchColumnWrapped(patch, columnIndex); +} + diff --git a/src/r_patch.h b/src/r_patch.h new file mode 100644 index 00000000..4900f13e --- /dev/null +++ b/src/r_patch.h @@ -0,0 +1,108 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef R_PATCH_H +#define R_PATCH_H + +// Used to specify the sloping of the top and bottom of a column post +typedef enum { + RDRAW_EDGESLOPE_TOP_UP = (1<<0), + RDRAW_EDGESLOPE_TOP_DOWN = (1<<1), + RDRAW_EDGESLOPE_BOT_UP = (1<<2), + RDRAW_EDGESLOPE_BOT_DOWN = (1<<3), + RDRAW_EDGESLOPE_TOP_MASK = 0x3, + RDRAW_EDGESLOPE_BOT_MASK = 0xc, +} edgeslope_t; + +typedef struct { + int topdelta; + int length; + edgeslope_t slope; +} rpost_t; + +typedef struct { + int numPosts; + rpost_t *posts; + unsigned char *pixels; +} rcolumn_t; + +typedef struct { + int width; + int height; + unsigned widthmask; + + unsigned char isNotTileable; + + int leftoffset; + int topoffset; + + // this is the single malloc'ed/free'd array + // for this patch + unsigned char *data; + + // these are pointers into the data array + unsigned char *pixels; + rcolumn_t *columns; + rpost_t *posts; + + unsigned int locks; +} rpatch_t; + + +const rpatch_t *R_CachePatchNum(int id); +void R_UnlockPatchNum(int id); +#define R_CachePatchName(name) R_CachePatchNum(W_GetNumForName(name)) +#define R_UnlockPatchName(name) R_UnlockPatchNum(W_GetNumForName(name)) + +const rpatch_t *R_CacheTextureCompositePatchNum(int id); +void R_UnlockTextureCompositePatchNum(int id); + + +// Size query funcs +int R_NumPatchWidth(int lump) ; +int R_NumPatchHeight(int lump); +#define R_NamePatchWidth(name) R_NumPatchWidth(W_GetNumForName(name)) +#define R_NamePatchHeight(name) R_NumPatchHeight(W_GetNumForName(name)) + + +const rcolumn_t *R_GetPatchColumnWrapped(const rpatch_t *patch, int columnIndex); +const rcolumn_t *R_GetPatchColumnClamped(const rpatch_t *patch, int columnIndex); + + +// returns R_GetPatchColumnWrapped for square, non-holed textures +// and R_GetPatchColumnClamped otherwise +const rcolumn_t *R_GetPatchColumn(const rpatch_t *patch, int columnIndex); + + +void R_InitPatches(); +void R_FlushAllPatches(); + +#endif diff --git a/src/r_plane.c b/src/r_plane.c new file mode 100644 index 00000000..eff1eac6 --- /dev/null +++ b/src/r_plane.c @@ -0,0 +1,461 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Here is a core component: drawing the floors and ceilings, + * while maintaining a per column clipping list only. + * Moreover, the sky areas have to be determined. + * + * MAXVISPLANES is no longer a limit on the number of visplanes, + * but a limit on the number of hash slots; larger numbers mean + * better performance usually but after a point they are wasted, + * and memory and time overheads creep in. + * + * For more information on visplanes, see: + * + * http://classicgaming.com/doom/editing/ + * + * Lee Killough + * + *-----------------------------------------------------------------------------*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "z_zone.h" /* memory allocation wrappers -- killough */ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_draw.h" +#include "r_things.h" +#include "r_sky.h" +#include "r_plane.h" +#include "v_video.h" +#include "lprintf.h" + +#define MAXVISPLANES 128 /* must be a power of 2 */ + +static visplane_t *visplanes[MAXVISPLANES]; // killough +static visplane_t *freetail; // killough +static visplane_t **freehead = &freetail; // killough +visplane_t *floorplane, *ceilingplane; + +// killough -- hash function for visplanes +// Empirically verified to be fairly uniform: + +#define visplane_hash(picnum,lightlevel,height) \ + ((unsigned)((picnum)*3+(lightlevel)+(height)*7) & (MAXVISPLANES-1)) + +size_t maxopenings; +int *openings,*lastopening; // dropoff overflow + +// Clip values are the solid pixel bounding the range. +// floorclip starts out SCREENHEIGHT +// ceilingclip starts out -1 + +int floorclip[MAX_SCREENWIDTH], ceilingclip[MAX_SCREENWIDTH]; // dropoff overflow + +// spanstart holds the start of a plane span; initialized to 0 at start + +static int spanstart[MAX_SCREENHEIGHT]; // killough 2/8/98 + +// +// texture mapping +// + +static const lighttable_t **planezlight; +static fixed_t planeheight; + +// killough 2/8/98: make variables static + +static fixed_t basexscale, baseyscale; +static fixed_t cachedheight[MAX_SCREENHEIGHT]; +static fixed_t cacheddistance[MAX_SCREENHEIGHT]; +static fixed_t cachedxstep[MAX_SCREENHEIGHT]; +static fixed_t cachedystep[MAX_SCREENHEIGHT]; +static fixed_t xoffs,yoffs; // killough 2/28/98: flat offsets + +fixed_t yslope[MAX_SCREENHEIGHT], distscale[MAX_SCREENWIDTH]; + +// +// R_InitPlanes +// Only at game startup. +// +void R_InitPlanes (void) +{ +} + +// +// R_MapPlane +// +// Uses global vars: +// planeheight +// dsvars.source +// basexscale +// baseyscale +// viewx +// viewy +// xoffs +// yoffs +// +// BASIC PRIMITIVE +// + +static void R_MapPlane(int y, int x1, int x2, draw_span_vars_t *dsvars) +{ + angle_t angle; + fixed_t distance, length; + unsigned index; + + if (planeheight != cachedheight[y]) + { + cachedheight[y] = planeheight; + distance = cacheddistance[y] = FixedMul (planeheight, yslope[y]); + dsvars->xstep = cachedxstep[y] = FixedMul (distance,basexscale); + dsvars->ystep = cachedystep[y] = FixedMul (distance,baseyscale); + } + else + { + distance = cacheddistance[y]; + dsvars->xstep = cachedxstep[y]; + dsvars->ystep = cachedystep[y]; + } + + length = FixedMul (distance,distscale[x1]); + angle = (viewangle + xtoviewangle[x1])>>ANGLETOFINESHIFT; + + // killough 2/28/98: Add offsets + dsvars->xfrac = viewx + FixedMul(finecosine[angle], length) + xoffs; + dsvars->yfrac = -viewy - FixedMul(finesine[angle], length) + yoffs; + + if (drawvars.filterfloor == RDRAW_FILTER_LINEAR) { + dsvars->xfrac -= (FRACUNIT>>1); + dsvars->yfrac -= (FRACUNIT>>1); + } + + if (!(dsvars->colormap = fixedcolormap)) + { + dsvars->z = distance; + index = distance >> LIGHTZSHIFT; + if (index >= MAXLIGHTZ ) + index = MAXLIGHTZ-1; + dsvars->colormap = planezlight[index]; + dsvars->nextcolormap = planezlight[index+1 >= MAXLIGHTZ ? MAXLIGHTZ-1 : index+1]; + } + else + { + dsvars->z = 0; + } + + dsvars->y = y; + dsvars->x1 = x1; + dsvars->x2 = x2; + + R_DrawSpan(dsvars); +} + +// +// R_ClearPlanes +// At begining of frame. +// + +void R_ClearPlanes(void) +{ + int i; + + // opening / clipping determination + for (i=0 ; inext; + + lastopening = openings; + + // texture calculation + memset (cachedheight, 0, sizeof(cachedheight)); + + // scale will be unit scale at SCREENWIDTH/2 distance + basexscale = FixedDiv (viewsin,projection); + baseyscale = FixedDiv (viewcos,projection); +} + +// New function, by Lee Killough + +static visplane_t *new_visplane(unsigned hash) +{ + visplane_t *check = freetail; + if (!check) + check = calloc(1, sizeof *check); + else + if (!(freetail = freetail->next)) + freehead = &freetail; + check->next = visplanes[hash]; + visplanes[hash] = check; + return check; +} + +/* + * R_DupPlane + * + * cph 2003/04/18 - create duplicate of existing visplane and set initial range + */ +visplane_t *R_DupPlane(const visplane_t *pl, int start, int stop) +{ + unsigned hash = visplane_hash(pl->picnum, pl->lightlevel, pl->height); + visplane_t *new_pl = new_visplane(hash); + + new_pl->height = pl->height; + new_pl->picnum = pl->picnum; + new_pl->lightlevel = pl->lightlevel; + new_pl->xoffs = pl->xoffs; // killough 2/28/98 + new_pl->yoffs = pl->yoffs; + new_pl->minx = start; + new_pl->maxx = stop; + memset(new_pl->top, 0xff, sizeof new_pl->top); + return new_pl; +} +// +// R_FindPlane +// +// killough 2/28/98: Add offsets + +visplane_t *R_FindPlane(fixed_t height, int picnum, int lightlevel, + fixed_t xoffs, fixed_t yoffs) +{ + visplane_t *check; + unsigned hash; // killough + + if (picnum == skyflatnum || picnum & PL_SKYFLAT) + height = lightlevel = 0; // killough 7/19/98: most skies map together + + // New visplane algorithm uses hash table -- killough + hash = visplane_hash(picnum,lightlevel,height); + + for (check=visplanes[hash]; check; check=check->next) // killough + if (height == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + xoffs == check->xoffs && // killough 2/28/98: Add offset checks + yoffs == check->yoffs) + return check; + + check = new_visplane(hash); // killough + + check->height = height; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->minx = viewwidth; // Was SCREENWIDTH -- killough 11/98 + check->maxx = -1; + check->xoffs = xoffs; // killough 2/28/98: Save offsets + check->yoffs = yoffs; + + memset (check->top, 0xff, sizeof check->top); + + return check; +} + +// +// R_CheckPlane +// +visplane_t *R_CheckPlane(visplane_t *pl, int start, int stop) +{ + int intrl, intrh, unionl, unionh, x; + + if (start < pl->minx) + intrl = pl->minx, unionl = start; + else + unionl = pl->minx, intrl = start; + + if (stop > pl->maxx) + intrh = pl->maxx, unionh = stop; + else + unionh = pl->maxx, intrh = stop; + + for (x=intrl ; x <= intrh && pl->top[x] == 0xffffffffu; x++) // dropoff overflow + ; + + if (x > intrh) { /* Can use existing plane; extend range */ + pl->minx = unionl; pl->maxx = unionh; + return pl; + } else /* Cannot use existing plane; create a new one */ + return R_DupPlane(pl,start,stop); +} + +// +// R_MakeSpans +// + +static void R_MakeSpans(int x, unsigned int t1, unsigned int b1, + unsigned int t2, unsigned int b2, + draw_span_vars_t *dsvars) +{ + for (; t1 < t2 && t1 <= b1; t1++) + R_MapPlane(t1, spanstart[t1], x-1, dsvars); + for (; b1 > b2 && b1 >= t1; b1--) + R_MapPlane(b1, spanstart[b1] ,x-1, dsvars); + while (t2 < t1 && t2 <= b2) + spanstart[t2++] = x; + while (b2 > b1 && b2 >= t2) + spanstart[b2--] = x; +} + +// New function, by Lee Killough + +static void R_DoDrawPlane(visplane_t *pl) +{ + register int x; + draw_column_vars_t dcvars; + R_DrawColumn_f colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterwall, drawvars.filterz); + + R_SetDefaultDrawColumnVars(&dcvars); + + if (pl->minx <= pl->maxx) { + if (pl->picnum == skyflatnum || pl->picnum & PL_SKYFLAT) { // sky flat + int texture; + const rpatch_t *tex_patch; + angle_t an, flip; + + // killough 10/98: allow skies to come from sidedefs. + // Allows scrolling and/or animated skies, as well as + // arbitrary multiple skies per level without having + // to use info lumps. + + an = viewangle; + + if (pl->picnum & PL_SKYFLAT) + { + // Sky Linedef + const line_t *l = &lines[pl->picnum & ~PL_SKYFLAT]; + + // Sky transferred from first sidedef + const side_t *s = *l->sidenum + sides; + + // Texture comes from upper texture of reference sidedef + texture = texturetranslation[s->toptexture]; + + // Horizontal offset is turned into an angle offset, + // to allow sky rotation as well as careful positioning. + // However, the offset is scaled very small, so that it + // allows a long-period of sky rotation. + + an += s->textureoffset; + + // Vertical offset allows careful sky positioning. + + dcvars.texturemid = s->rowoffset - 28*FRACUNIT; + + // We sometimes flip the picture horizontally. + // + // Doom always flipped the picture, so we make it optional, + // to make it easier to use the new feature, while to still + // allow old sky textures to be used. + + flip = l->special==272 ? 0u : ~0u; + } + else + { // Normal Doom sky, only one allowed per level + dcvars.texturemid = skytexturemid; // Default y-offset + texture = skytexture; // Default texture + flip = 0; // Doom flips it + } + + /* Sky is always drawn full bright, i.e. colormaps[0] is used. + * Because of this hack, sky is not affected by INVUL inverse mapping. + * Until Boom fixed this. Compat option added in MBF. */ + + if (comp[comp_skymap] || !(dcvars.colormap = fixedcolormap)) + dcvars.colormap = fullcolormap; // killough 3/20/98 + + dcvars.nextcolormap = dcvars.colormap; // for filtering -- POPE + + //dcvars.texturemid = skytexturemid; + dcvars.texheight = textureheight[skytexture]>>FRACBITS; // killough + dcvars.iscale = pspriteiscale; + + tex_patch = R_CacheTextureCompositePatchNum(texture); + + // killough 10/98: Use sky scrolling offset, and possibly flip picture + for (x = pl->minx; (dcvars.x = x) <= pl->maxx; x++) + if ((dcvars.yl = pl->top[x]) != -1 && dcvars.yl <= (dcvars.yh = pl->bottom[x])) // dropoff overflow + { + dcvars.source = R_GetTextureColumn(tex_patch, ((an + xtoviewangle[x])^flip) >> ANGLETOSKYSHIFT); + dcvars.prevsource = R_GetTextureColumn(tex_patch, ((an + xtoviewangle[x-1])^flip) >> ANGLETOSKYSHIFT); + dcvars.nextsource = R_GetTextureColumn(tex_patch, ((an + xtoviewangle[x+1])^flip) >> ANGLETOSKYSHIFT); + colfunc(&dcvars); + } + + R_UnlockTextureCompositePatchNum(texture); + + } else { // regular flat + + int stop, light; + draw_span_vars_t dsvars; + + dsvars.source = W_CacheLumpNum(firstflat + flattranslation[pl->picnum]); + + xoffs = pl->xoffs; // killough 2/28/98: Add offsets + yoffs = pl->yoffs; + planeheight = D_abs(pl->height-viewz); + light = (pl->lightlevel >> LIGHTSEGSHIFT) + extralight; + + if (light >= LIGHTLEVELS) + light = LIGHTLEVELS-1; + + if (light < 0) + light = 0; + + stop = pl->maxx + 1; + planezlight = zlight[light]; + pl->top[pl->minx-1] = pl->top[stop] = 0xffffffffu; // dropoff overflow + + for (x = pl->minx ; x <= stop ; x++) + R_MakeSpans(x,pl->top[x-1],pl->bottom[x-1], + pl->top[x],pl->bottom[x], &dsvars); + + W_UnlockLumpNum(firstflat + flattranslation[pl->picnum]); + } + } +} + +// +// RDrawPlanes +// At the end of each frame. +// + +void R_DrawPlanes (void) +{ + visplane_t *pl; + int i; + for (i=0;inext, rendered_visplanes++) + R_DoDrawPlane(pl); +} diff --git a/src/r_plane.h b/src/r_plane.h new file mode 100644 index 00000000..7a2a515e --- /dev/null +++ b/src/r_plane.h @@ -0,0 +1,67 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh, visplane stuff (floor, ceilings). + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_PLANE__ +#define __R_PLANE__ + +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* killough 10/98: special mask indicates sky flat comes from sidedef */ +#define PL_SKYFLAT (0x80000000) + +/* Visplane related. */ +extern int *lastopening; // dropoff overflow + +extern int floorclip[], ceilingclip[]; // dropoff overflow +extern fixed_t yslope[], distscale[]; + +void R_InitPlanes(void); +void R_ClearPlanes(void); +void R_DrawPlanes (void); + +visplane_t *R_FindPlane( + fixed_t height, + int picnum, + int lightlevel, + fixed_t xoffs, /* killough 2/28/98: add x-y offsets */ + fixed_t yoffs + ); + +visplane_t *R_CheckPlane(visplane_t *pl, int start, int stop); +visplane_t *R_DupPlane(const visplane_t *pl, int start, int stop); + +#endif diff --git a/src/r_segs.c b/src/r_segs.c new file mode 100644 index 00000000..7e9b2321 --- /dev/null +++ b/src/r_segs.c @@ -0,0 +1,825 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2004 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * All the clipping: columns, horizontal spans, sky columns. + * + *-----------------------------------------------------------------------------*/ +// +// 4/25/98, 5/2/98 killough: reformatted, beautified + +#include "doomstat.h" +#include "r_main.h" +#include "r_bsp.h" +#include "r_segs.h" +#include "r_plane.h" +#include "r_things.h" +#include "r_draw.h" +#include "w_wad.h" +#include "v_video.h" +#include "lprintf.h" + +// OPTIMIZE: closed two sided lines as single sided + +// killough 1/6/98: replaced globals with statics where appropriate + +// True if any of the segs textures might be visible. +static boolean segtextured; +static boolean markfloor; // False if the back side is the same plane. +static boolean markceiling; +static boolean maskedtexture; +static int toptexture; +static int bottomtexture; +static int midtexture; + +static fixed_t toptexheight, midtexheight, bottomtexheight; // cph + +angle_t rw_normalangle; // angle to line origin +int rw_angle1; +fixed_t rw_distance; + +// +// regular wall +// +static int rw_x; +static int rw_stopx; +static angle_t rw_centerangle; +static fixed_t rw_offset; +static fixed_t rw_scale; +static fixed_t rw_scalestep; +static fixed_t rw_midtexturemid; +static fixed_t rw_toptexturemid; +static fixed_t rw_bottomtexturemid; +static int rw_lightlevel; +static int worldtop; +static int worldbottom; +static int worldhigh; +static int worldlow; +static fixed_t pixhigh; +static fixed_t pixlow; +static fixed_t pixhighstep; +static fixed_t pixlowstep; +static fixed_t topfrac; +static fixed_t topstep; +static fixed_t bottomfrac; +static fixed_t bottomstep; +static int *maskedtexturecol; // dropoff overflow + +// +// R_ScaleFromGlobalAngle +// Returns the texture mapping scale +// for the current line (horizontal span) +// at the given angle. +// rw_distance must be calculated first. +// +// killough 5/2/98: reformatted, cleaned up +// CPhipps - moved here from r_main.c + +static fixed_t R_ScaleFromGlobalAngle(angle_t visangle) +{ + int anglea = ANG90 + (visangle-viewangle); + int angleb = ANG90 + (visangle-rw_normalangle); + int den = FixedMul(rw_distance, finesine[anglea>>ANGLETOFINESHIFT]); +// proff 11/06/98: Changed for high-res + fixed_t num = FixedMul(projectiony, finesine[angleb>>ANGLETOFINESHIFT]); + return den > num>>16 ? (num = FixedDiv(num, den)) > 64*FRACUNIT ? + 64*FRACUNIT : num < 256 ? 256 : num : 64*FRACUNIT; +} + +// +// R_RenderMaskedSegRange +// + +void R_RenderMaskedSegRange(drawseg_t *ds, int x1, int x2) +{ + int texnum; + sector_t tempsec; // killough 4/13/98 + const rpatch_t *patch; + R_DrawColumn_f colfunc; + draw_column_vars_t dcvars; + angle_t angle; + + R_SetDefaultDrawColumnVars(&dcvars); + + // Calculate light table. + // Use different light tables + // for horizontal / vertical / diagonal. Diagonal? + + curline = ds->curline; // OPTIMIZE: get rid of LIGHTSEGSHIFT globally + + // killough 4/11/98: draw translucent 2s normal textures + + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterwall, drawvars.filterz); + + frontsector = curline->frontsector; + backsector = curline->backsector; + + // cph 2001/11/25 - middle textures did not animate in v1.2 + texnum = curline->sidedef->midtexture; + if (!comp[comp_maskedanim]) + texnum = texturetranslation[texnum]; + + // killough 4/13/98: get correct lightlevel for 2s normal textures + rw_lightlevel = R_FakeFlat(frontsector, &tempsec, NULL, NULL, false) ->lightlevel; + + maskedtexturecol = ds->maskedtexturecol; + + rw_scalestep = ds->scalestep; + spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep; + mfloorclip = ds->sprbottomclip; + mceilingclip = ds->sprtopclip; + + // find positioning + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + dcvars.texturemid = frontsector->floorheight > backsector->floorheight + ? frontsector->floorheight : backsector->floorheight; + dcvars.texturemid = dcvars.texturemid + textureheight[texnum] - viewz; + } + else + { + dcvars.texturemid =frontsector->ceilingheightceilingheight + ? frontsector->ceilingheight : backsector->ceilingheight; + dcvars.texturemid = dcvars.texturemid - viewz; + } + + dcvars.texturemid += curline->sidedef->rowoffset; + + if (fixedcolormap) { + dcvars.colormap = fixedcolormap; + dcvars.nextcolormap = dcvars.colormap; // for filtering -- POPE + } + + patch = R_CacheTextureCompositePatchNum(texnum); + + // draw the columns + for (dcvars.x = x1 ; dcvars.x <= x2 ; dcvars.x++, spryscale += rw_scalestep) + if (maskedtexturecol[dcvars.x] != INT_MAX) // dropoff overflow + { + // calculate texture offset - POPE + angle = (ds->rw_centerangle + xtoviewangle[dcvars.x]) >> ANGLETOFINESHIFT; + dcvars.texu = ds->rw_offset - FixedMul(finetangent[angle], ds->rw_distance); + if (drawvars.filterwall == RDRAW_FILTER_LINEAR) + dcvars.texu -= (FRACUNIT>>1); + + if (!fixedcolormap) + dcvars.z = spryscale; // for filtering -- POPE + dcvars.colormap = R_ColourMap(rw_lightlevel,spryscale); + dcvars.nextcolormap = R_ColourMap(rw_lightlevel+1,spryscale); // for filtering -- POPE + + // killough 3/2/98: + // + // This calculation used to overflow and cause crashes in Doom: + // + // sprtopscreen = centeryfrac - FixedMul(dcvars.texturemid, spryscale); + // + // This code fixes it, by using double-precision intermediate + // arithmetic and by skipping the drawing of 2s normals whose + // mapping to screen coordinates is totally out of range: + + { + int_64_t t = ((int_64_t) centeryfrac << FRACBITS) - + (int_64_t) dcvars.texturemid * spryscale; + if (t + (int_64_t) textureheight[texnum] * spryscale < 0 || + t > (int_64_t) MAX_SCREENHEIGHT << FRACBITS*2) + continue; // skip if the texture is out of screen's range + sprtopscreen = (long)(t >> FRACBITS); + } + + dcvars.iscale = 0xffffffffu / (unsigned) spryscale; + + // killough 1/25/98: here's where Medusa came in, because + // it implicitly assumed that the column was all one patch. + // Originally, Doom did not construct complete columns for + // multipatched textures, so there were no header or trailer + // bytes in the column referred to below, which explains + // the Medusa effect. The fix is to construct true columns + // when forming multipatched textures (see r_data.c). + + // draw the texture + R_DrawMaskedColumn( + patch, + colfunc, + &dcvars, + R_GetPatchColumnWrapped(patch, maskedtexturecol[dcvars.x]), + R_GetPatchColumnWrapped(patch, maskedtexturecol[dcvars.x]-1), + R_GetPatchColumnWrapped(patch, maskedtexturecol[dcvars.x]+1) + ); + + maskedtexturecol[dcvars.x] = INT_MAX; // dropoff overflow + } + + R_UnlockTextureCompositePatchNum(texnum); + + curline = NULL; /* cph 2001/11/18 - must clear curline now we're done with it, so R_ColourMap doesn't try using it for other things */ +} + +// +// R_RenderSegLoop +// Draws zero, one, or two textures (and possibly a masked texture) for walls. +// Can draw or mark the starting pixel of floor and ceiling textures. +// CALLED: CORE LOOPING ROUTINE. +// + +#define HEIGHTBITS 12 +#define HEIGHTUNIT (1<>HEIGHTBITS; + int yl = (topfrac+HEIGHTUNIT-1)>>HEIGHTBITS; + + // no space above wall? + int bottom,top = ceilingclip[rw_x]+1; + + if (yl < top) + yl = top; + + if (markceiling) + { + bottom = yl-1; + + if (bottom >= floorclip[rw_x]) + bottom = floorclip[rw_x]-1; + + if (top <= bottom) + { + ceilingplane->top[rw_x] = top; + ceilingplane->bottom[rw_x] = bottom; + } + // SoM: this should be set here + ceilingclip[rw_x] = bottom; + } + +// yh = bottomfrac>>HEIGHTBITS; + + bottom = floorclip[rw_x]-1; + if (yh > bottom) + yh = bottom; + + if (markfloor) + { + + top = yh < ceilingclip[rw_x] ? ceilingclip[rw_x] : yh; + + if (++top <= bottom) + { + floorplane->top[rw_x] = top; + floorplane->bottom[rw_x] = bottom; + } + // SoM: This should be set here to prevent overdraw + floorclip[rw_x] = top; + } + + // texturecolumn and lighting are independent of wall tiers + if (segtextured) + { + // calculate texture offset + angle_t angle =(rw_centerangle+xtoviewangle[rw_x])>>ANGLETOFINESHIFT; + + texturecolumn = rw_offset-FixedMul(finetangent[angle],rw_distance); + if (drawvars.filterwall == RDRAW_FILTER_LINEAR) + texturecolumn -= (FRACUNIT>>1); + dcvars.texu = texturecolumn; // for filtering -- POPE + texturecolumn >>= FRACBITS; + + dcvars.colormap = R_ColourMap(rw_lightlevel,rw_scale); + dcvars.nextcolormap = R_ColourMap(rw_lightlevel+1,rw_scale); // for filtering -- POPE + dcvars.z = rw_scale; // for filtering -- POPE + + dcvars.x = rw_x; + dcvars.iscale = 0xffffffffu / (unsigned)rw_scale; + } + + // draw the wall tiers + if (midtexture) + { + + dcvars.yl = yl; // single sided line + dcvars.yh = yh; + dcvars.texturemid = rw_midtexturemid; + tex_patch = R_CacheTextureCompositePatchNum(midtexture); + dcvars.source = R_GetTextureColumn(tex_patch, texturecolumn); + dcvars.prevsource = R_GetTextureColumn(tex_patch, texturecolumn-1); + dcvars.nextsource = R_GetTextureColumn(tex_patch, texturecolumn+1); + dcvars.texheight = midtexheight; + colfunc (&dcvars); + R_UnlockTextureCompositePatchNum(midtexture); + tex_patch = NULL; + ceilingclip[rw_x] = viewheight; + floorclip[rw_x] = -1; + } + else + { + + // two sided line + if (toptexture) + { + // top wall + int mid = pixhigh>>HEIGHTBITS; + pixhigh += pixhighstep; + + if (mid >= floorclip[rw_x]) + mid = floorclip[rw_x]-1; + + if (mid >= yl) + { + dcvars.yl = yl; + dcvars.yh = mid; + dcvars.texturemid = rw_toptexturemid; + tex_patch = R_CacheTextureCompositePatchNum(toptexture); + dcvars.source = R_GetTextureColumn(tex_patch,texturecolumn); + dcvars.prevsource = R_GetTextureColumn(tex_patch,texturecolumn-1); + dcvars.nextsource = R_GetTextureColumn(tex_patch,texturecolumn+1); + dcvars.texheight = toptexheight; + colfunc (&dcvars); + R_UnlockTextureCompositePatchNum(toptexture); + tex_patch = NULL; + ceilingclip[rw_x] = mid; + } + else + ceilingclip[rw_x] = yl-1; + } + else // no top wall + { + + if (markceiling) + ceilingclip[rw_x] = yl-1; + } + + if (bottomtexture) // bottom wall + { + int mid = (pixlow+HEIGHTUNIT-1)>>HEIGHTBITS; + pixlow += pixlowstep; + + // no space above wall? + if (mid <= ceilingclip[rw_x]) + mid = ceilingclip[rw_x]+1; + + if (mid <= yh) + { + dcvars.yl = mid; + dcvars.yh = yh; + dcvars.texturemid = rw_bottomtexturemid; + tex_patch = R_CacheTextureCompositePatchNum(bottomtexture); + dcvars.source = R_GetTextureColumn(tex_patch, texturecolumn); + dcvars.prevsource = R_GetTextureColumn(tex_patch, texturecolumn-1); + dcvars.nextsource = R_GetTextureColumn(tex_patch, texturecolumn+1); + dcvars.texheight = bottomtexheight; + colfunc (&dcvars); + R_UnlockTextureCompositePatchNum(bottomtexture); + tex_patch = NULL; + floorclip[rw_x] = mid; + } + else + floorclip[rw_x] = yh+1; + } + else // no bottom wall + { + if (markfloor) + floorclip[rw_x] = yh+1; + } + + // cph - if we completely blocked further sight through this column, + // add this info to the solid columns array for r_bsp.c + if ((markceiling || markfloor) && + (floorclip[rw_x] <= ceilingclip[rw_x] + 1)) { + solidcol[rw_x] = 1; didsolidcol = 1; + } + + // save texturecol for backdrawing of masked mid texture + if (maskedtexture) + maskedtexturecol[rw_x] = texturecolumn; + } + + rw_scale += rw_scalestep; + topfrac += topstep; + bottomfrac += bottomstep; + } +} + +// killough 5/2/98: move from r_main.c, made static, simplified + +static fixed_t R_PointToDist(fixed_t x, fixed_t y) +{ + fixed_t dx = D_abs(x - viewx); + fixed_t dy = D_abs(y - viewy); + + if (dy > dx) + { + fixed_t t = dx; + dx = dy; + dy = t; + } + + return FixedDiv(dx, finesine[(tantoangle[FixedDiv(dy,dx) >> DBITS] + + ANG90) >> ANGLETOFINESHIFT]); +} + +// +// R_StoreWallRange +// A wall segment will be drawn +// between start and stop pixels (inclusive). +// +void R_StoreWallRange(const int start, const int stop) +{ + fixed_t hyp; + angle_t offsetangle; + + if (ds_p == drawsegs+maxdrawsegs) // killough 1/98 -- fix 2s line HOM + { + unsigned pos = ds_p - drawsegs; // jff 8/9/98 fix from ZDOOM1.14a + unsigned newmax = maxdrawsegs ? maxdrawsegs*2 : 128; // killough + drawsegs = realloc(drawsegs,newmax*sizeof(*drawsegs)); + ds_p = drawsegs + pos; // jff 8/9/98 fix from ZDOOM1.14a + maxdrawsegs = newmax; + } + + if(curline->miniseg == false) // figgi -- skip minisegs + curline->linedef->flags |= ML_MAPPED; + + sidedef = curline->sidedef; + linedef = curline->linedef; + + // mark the segment as visible for auto map + linedef->flags |= ML_MAPPED; + + // calculate rw_distance for scale calculation + rw_normalangle = curline->angle + ANG90; + + offsetangle = rw_normalangle-rw_angle1; + + if (D_abs(offsetangle) > ANG90) + offsetangle = ANG90; + + hyp = (viewx==curline->v1->x && viewy==curline->v1->y)? + 0 : R_PointToDist (curline->v1->x, curline->v1->y); + rw_distance = FixedMul(hyp, finecosine[offsetangle>>ANGLETOFINESHIFT]); + + ds_p->x1 = rw_x = start; + ds_p->x2 = stop; + ds_p->curline = curline; + rw_stopx = stop+1; + + { // killough 1/6/98, 2/1/98: remove limit on openings + extern int *openings; // dropoff overflow + extern size_t maxopenings; + size_t pos = lastopening - openings; + size_t need = (rw_stopx - start)*4 + pos; + if (need > maxopenings) + { + drawseg_t *ds; //jff 8/9/98 needed for fix from ZDoom + int *oldopenings = openings; // dropoff overflow + int *oldlast = lastopening; // dropoff overflow + + do + maxopenings = maxopenings ? maxopenings*2 : 16384; + while (need > maxopenings); + openings = realloc(openings, maxopenings * sizeof(*openings)); + lastopening = openings + pos; + + // jff 8/9/98 borrowed fix for openings from ZDOOM1.14 + // [RH] We also need to adjust the openings pointers that + // were already stored in drawsegs. + for (ds = drawsegs; ds < ds_p; ds++) + { +#define ADJUST(p) if (ds->p + ds->x1 >= oldopenings && ds->p + ds->x1 <= oldlast)\ + ds->p = ds->p - oldopenings + openings; + ADJUST (maskedtexturecol); + ADJUST (sprtopclip); + ADJUST (sprbottomclip); + } +#undef ADJUST + } + } // killough: end of code to remove limits on openings + + // calculate scale at both ends and step + + ds_p->scale1 = rw_scale = + R_ScaleFromGlobalAngle (viewangle + xtoviewangle[start]); + + if (stop > start) + { + ds_p->scale2 = R_ScaleFromGlobalAngle (viewangle + xtoviewangle[stop]); + ds_p->scalestep = rw_scalestep = (ds_p->scale2-rw_scale) / (stop-start); + } + else + ds_p->scale2 = ds_p->scale1; + + // calculate texture boundaries + // and decide if floor / ceiling marks are needed + + worldtop = frontsector->ceilingheight - viewz; + worldbottom = frontsector->floorheight - viewz; + + midtexture = toptexture = bottomtexture = maskedtexture = 0; + ds_p->maskedtexturecol = NULL; + + if (!backsector) + { + // single sided line + midtexture = texturetranslation[sidedef->midtexture]; + midtexheight = (linedef->r_flags & RF_MID_TILE) ? 0 : textureheight[midtexture] >> FRACBITS; + + // a single sided line is terminal, so it must mark ends + markfloor = markceiling = true; + + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + fixed_t vtop = frontsector->floorheight + + textureheight[sidedef->midtexture]; + rw_midtexturemid = vtop - viewz; + } + else // top of texture at top + rw_midtexturemid = worldtop; + + rw_midtexturemid += FixedMod(sidedef->rowoffset, textureheight[midtexture]); + + ds_p->silhouette = SIL_BOTH; + ds_p->sprtopclip = screenheightarray; + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = INT_MAX; + ds_p->tsilheight = INT_MIN; + } + else // two sided line + { + ds_p->sprtopclip = ds_p->sprbottomclip = NULL; + ds_p->silhouette = 0; + + if (linedef->r_flags & RF_CLOSED) { /* cph - closed 2S line e.g. door */ + // cph - killough's (outdated) comment follows - this deals with both + // "automap fixes", his and mine + // killough 1/17/98: this test is required if the fix + // for the automap bug (r_bsp.c) is used, or else some + // sprites will be displayed behind closed doors. That + // fix prevents lines behind closed doors with dropoffs + // from being displayed on the automap. + + ds_p->silhouette = SIL_BOTH; + ds_p->sprbottomclip = negonearray; + ds_p->bsilheight = INT_MAX; + ds_p->sprtopclip = screenheightarray; + ds_p->tsilheight = INT_MIN; + + } else { /* not solid - old code */ + + if (frontsector->floorheight > backsector->floorheight) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = frontsector->floorheight; + } + else + if (backsector->floorheight > viewz) + { + ds_p->silhouette = SIL_BOTTOM; + ds_p->bsilheight = INT_MAX; + } + + if (frontsector->ceilingheight < backsector->ceilingheight) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = frontsector->ceilingheight; + } + else + if (backsector->ceilingheight < viewz) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = INT_MIN; + } + } + + worldhigh = backsector->ceilingheight - viewz; + worldlow = backsector->floorheight - viewz; + + // hack to allow height changes in outdoor areas + if (frontsector->ceilingpic == skyflatnum + && backsector->ceilingpic == skyflatnum) + worldtop = worldhigh; + + markfloor = worldlow != worldbottom + || backsector->floorpic != frontsector->floorpic + || backsector->lightlevel != frontsector->lightlevel + + // killough 3/7/98: Add checks for (x,y) offsets + || backsector->floor_xoffs != frontsector->floor_xoffs + || backsector->floor_yoffs != frontsector->floor_yoffs + + // killough 4/15/98: prevent 2s normals + // from bleeding through deep water + || frontsector->heightsec != -1 + + // killough 4/17/98: draw floors if different light levels + || backsector->floorlightsec != frontsector->floorlightsec + ; + + markceiling = worldhigh != worldtop + || backsector->ceilingpic != frontsector->ceilingpic + || backsector->lightlevel != frontsector->lightlevel + + // killough 3/7/98: Add checks for (x,y) offsets + || backsector->ceiling_xoffs != frontsector->ceiling_xoffs + || backsector->ceiling_yoffs != frontsector->ceiling_yoffs + + // killough 4/15/98: prevent 2s normals + // from bleeding through fake ceilings + || (frontsector->heightsec != -1 && + frontsector->ceilingpic!=skyflatnum) + + // killough 4/17/98: draw ceilings if different light levels + || backsector->ceilinglightsec != frontsector->ceilinglightsec + ; + + if (backsector->ceilingheight <= frontsector->floorheight + || backsector->floorheight >= frontsector->ceilingheight) + markceiling = markfloor = true; // closed door + + if (worldhigh < worldtop) // top texture + { + toptexture = texturetranslation[sidedef->toptexture]; + toptexheight = (linedef->r_flags & RF_TOP_TILE) ? 0 : textureheight[toptexture] >> FRACBITS; + rw_toptexturemid = linedef->flags & ML_DONTPEGTOP ? worldtop : + backsector->ceilingheight+textureheight[sidedef->toptexture]-viewz; + rw_toptexturemid += FixedMod(sidedef->rowoffset, textureheight[toptexture]); + } + + if (worldlow > worldbottom) // bottom texture + { + bottomtexture = texturetranslation[sidedef->bottomtexture]; + bottomtexheight = (linedef->r_flags & RF_BOT_TILE) ? 0 : textureheight[bottomtexture] >> FRACBITS; + rw_bottomtexturemid = linedef->flags & ML_DONTPEGBOTTOM ? worldtop : + worldlow; + rw_bottomtexturemid += FixedMod(sidedef->rowoffset, textureheight[bottomtexture]); + } + + // allocate space for masked texture tables + if (sidedef->midtexture) // masked midtexture + { + maskedtexture = true; + ds_p->maskedtexturecol = maskedtexturecol = lastopening - rw_x; + lastopening += rw_stopx - rw_x; + } + } + + // calculate rw_offset (only needed for textured lines) + segtextured = midtexture | toptexture | bottomtexture | maskedtexture; + + if (segtextured) + { + rw_offset = FixedMul (hyp, -finesine[offsetangle >>ANGLETOFINESHIFT]); + + rw_offset += sidedef->textureoffset + curline->offset; + + rw_centerangle = ANG90 + viewangle - rw_normalangle; + + rw_lightlevel = frontsector->lightlevel; + } + + // Remember the vars used to determine fractional U texture + // coords for later - POPE + ds_p->rw_offset = rw_offset; + ds_p->rw_distance = rw_distance; + ds_p->rw_centerangle = rw_centerangle; + + // if a floor / ceiling plane is on the wrong side of the view + // plane, it is definitely invisible and doesn't need to be marked. + + // killough 3/7/98: add deep water check + if (frontsector->heightsec == -1) + { + if (frontsector->floorheight >= viewz) // above view plane + markfloor = false; + if (frontsector->ceilingheight <= viewz && + frontsector->ceilingpic != skyflatnum) // below view plane + markceiling = false; + } + + // calculate incremental stepping values for texture edges + worldtop >>= 4; + worldbottom >>= 4; + + topstep = -FixedMul (rw_scalestep, worldtop); + topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale); + + bottomstep = -FixedMul (rw_scalestep,worldbottom); + bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale); + + if (backsector) + { + worldhigh >>= 4; + worldlow >>= 4; + + if (worldhigh < worldtop) + { + pixhigh = (centeryfrac>>4) - FixedMul (worldhigh, rw_scale); + pixhighstep = -FixedMul (rw_scalestep,worldhigh); + } + if (worldlow > worldbottom) + { + pixlow = (centeryfrac>>4) - FixedMul (worldlow, rw_scale); + pixlowstep = -FixedMul (rw_scalestep,worldlow); + } + } + + // render it + if (markceiling) { + if (ceilingplane) // killough 4/11/98: add NULL ptr checks + ceilingplane = R_CheckPlane (ceilingplane, rw_x, rw_stopx-1); + else + markceiling = 0; + } + + if (markfloor) { + if (floorplane) // killough 4/11/98: add NULL ptr checks + /* cph 2003/04/18 - ceilingplane and floorplane might be the same + * visplane (e.g. if both skies); R_CheckPlane doesn't know about + * modifications to the plane that might happen in parallel with the check + * being made, so we have to override it and split them anyway if that is + * a possibility, otherwise the floor marking would overwrite the ceiling + * marking, resulting in HOM. */ + if (markceiling && ceilingplane == floorplane) + floorplane = R_DupPlane (floorplane, rw_x, rw_stopx-1); + else + floorplane = R_CheckPlane (floorplane, rw_x, rw_stopx-1); + else + markfloor = 0; + } + + didsolidcol = 0; + R_RenderSegLoop(); + + /* cph - if a column was made solid by this wall, we _must_ save full clipping info */ + if (backsector && didsolidcol) { + if (!(ds_p->silhouette & SIL_BOTTOM)) { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = backsector->floorheight; + } + if (!(ds_p->silhouette & SIL_TOP)) { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = backsector->ceilingheight; + } + } + + // save sprite clipping info + if ((ds_p->silhouette & SIL_TOP || maskedtexture) && !ds_p->sprtopclip) + { + memcpy (lastopening, ceilingclip+start, sizeof(int)*(rw_stopx-start)); // dropoff overflow + ds_p->sprtopclip = lastopening - start; + lastopening += rw_stopx - start; + } + if ((ds_p->silhouette & SIL_BOTTOM || maskedtexture) && !ds_p->sprbottomclip) + { + memcpy (lastopening, floorclip+start, sizeof(int)*(rw_stopx-start)); // dropoff overflow + ds_p->sprbottomclip = lastopening - start; + lastopening += rw_stopx - start; + } + if (maskedtexture && !(ds_p->silhouette & SIL_TOP)) + { + ds_p->silhouette |= SIL_TOP; + ds_p->tsilheight = INT_MIN; + } + if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM)) + { + ds_p->silhouette |= SIL_BOTTOM; + ds_p->bsilheight = INT_MAX; + } + ds_p++; +} diff --git a/src/r_segs.h b/src/r_segs.h new file mode 100644 index 00000000..c5b29a65 --- /dev/null +++ b/src/r_segs.h @@ -0,0 +1,44 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh module, drawing LineSegs from BSP. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_SEGS__ +#define __R_SEGS__ + +#ifdef __GNUG__ +#pragma interface +#endif + +void R_RenderMaskedSegRange(drawseg_t *ds, int x1, int x2); +void R_StoreWallRange(const int start, const int stop); + +#endif diff --git a/src/r_sky.c b/src/r_sky.c new file mode 100644 index 00000000..bc33c634 --- /dev/null +++ b/src/r_sky.c @@ -0,0 +1,56 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Sky rendering. The DOOM sky is a texture map like any + * wall, wrapping around. A 1024 columns equal 360 degrees. + * The default sky map is 256 columns and repeats 4 times + * on a 320 screen? + * + *-----------------------------------------------------------------------------*/ + +#ifdef __GNUG__ +#pragma implementation "r_sky.h" +#endif +#include "r_sky.h" + +// +// sky mapping +// +int skyflatnum; +int skytexture; +int skytexturemid; + +// +// R_InitSkyMap +// Called whenever the view size changes. +// +void R_InitSkyMap (void) +{ + skytexturemid = 100*FRACUNIT; +} diff --git a/src/r_sky.h b/src/r_sky.h new file mode 100644 index 00000000..1a69ae44 --- /dev/null +++ b/src/r_sky.h @@ -0,0 +1,55 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Sky rendering. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_SKY__ +#define __R_SKY__ + +#include "m_fixed.h" + +#ifdef __GNUG__ +#pragma interface +#endif + +/* SKY, store the number for name. */ +#define SKYFLATNAME "F_SKY1" + +/* The sky map is 256*128*4 maps. */ +#define ANGLETOSKYSHIFT 22 + +extern int skytexture; +extern int skytexturemid; + +/* Called whenever the view size changes. */ +void R_InitSkyMap(void); + +#endif diff --git a/src/r_state.h b/src/r_state.h new file mode 100644 index 00000000..44784f2b --- /dev/null +++ b/src/r_state.h @@ -0,0 +1,116 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh/render internal state variables (global). + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __R_STATE__ +#define __R_STATE__ + +// Need data structure definitions. +#include "d_player.h" +#include "r_data.h" + +#ifdef __GNUG__ +#pragma interface +#endif + + +// +// Refresh internal data structures, +// for rendering. +// + +// needed for texture pegging +extern fixed_t *textureheight; + +extern int scaledviewwidth; + +extern int firstflat, numflats; + +// for global animation +extern int *flattranslation; +extern int *texturetranslation; + +// Sprite.... +extern int firstspritelump; +extern int lastspritelump; +extern int numspritelumps; + +// +// Lookup tables for map data. +// +extern int numsprites; +extern spritedef_t *sprites; + +extern int numvertexes; +extern vertex_t *vertexes; + +extern int numsegs; +extern seg_t *segs; + +extern int numsectors; +extern sector_t *sectors; + +extern int numsubsectors; +extern subsector_t *subsectors; + +extern int numnodes; +extern node_t *nodes; + +extern int numlines; +extern line_t *lines; + +extern int numsides; +extern side_t *sides; + + +// +// POV data. +// +extern fixed_t viewx; +extern fixed_t viewy; +extern fixed_t viewz; +extern angle_t viewangle; +extern player_t *viewplayer; +extern angle_t clipangle; +extern int viewangletox[FINEANGLES/2]; +extern angle_t xtoviewangle[MAX_SCREENWIDTH+1]; // killough 2/8/98 +extern fixed_t rw_distance; +extern angle_t rw_normalangle; + +// angle to line origin +extern int rw_angle1; + +extern visplane_t *floorplane; +extern visplane_t *ceilingplane; + +#endif diff --git a/src/r_things.c b/src/r_things.c new file mode 100644 index 00000000..0572a27a --- /dev/null +++ b/src/r_things.c @@ -0,0 +1,999 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Refresh of things, i.e. objects represented by sprites. + * + *-----------------------------------------------------------------------------*/ + +#include "doomstat.h" +#include "w_wad.h" +#include "r_main.h" +#include "r_bsp.h" +#include "r_segs.h" +#include "r_draw.h" +#include "r_things.h" +#include "r_fps.h" +#include "v_video.h" +#include "lprintf.h" + +#define MINZ (FRACUNIT*4) +#define BASEYCENTER 100 + +typedef struct { + int x1; + int x2; + int column; + int topclip; + int bottomclip; +} maskdraw_t; + +// +// Sprite rotation 0 is facing the viewer, +// rotation 1 is one angle turn CLOCKWISE around the axis. +// This is not the same as the angle, +// which increases counter clockwise (protractor). +// There was a lot of stuff grabbed wrong, so I changed it... +// + +fixed_t pspritescale; +fixed_t pspriteiscale; +// proff 11/06/98: Added for high-res +fixed_t pspriteyscale; + +// constant arrays +// used for psprite clipping and initializing clipping + +int negonearray[MAX_SCREENWIDTH]; // killough 2/8/98: // dropoff overflow +int screenheightarray[MAX_SCREENWIDTH]; // change to MAX_* // dropoff overflow + +// +// INITIALIZATION FUNCTIONS +// + +// variables used to look up and range check thing_t sprites patches + +spritedef_t *sprites; +int numsprites; + +#define MAX_SPRITE_FRAMES 29 /* Macroized -- killough 1/25/98 */ + +static spriteframe_t sprtemp[MAX_SPRITE_FRAMES]; +static int maxframe; + +// +// R_InstallSpriteLump +// Local function for R_InitSprites. +// + +static void R_InstallSpriteLump(int lump, unsigned frame, + unsigned rotation, boolean flipped) +{ + if (frame >= MAX_SPRITE_FRAMES || rotation > 8) + I_Error("R_InstallSpriteLump: Bad frame characters in lump %i", lump); + + if ((int) frame > maxframe) + maxframe = frame; + + if (rotation == 0) + { // the lump should be used for all rotations + int r; + for (r=0 ; r<8 ; r++) + if (sprtemp[frame].lump[r]==-1) + { + sprtemp[frame].lump[r] = lump - firstspritelump; + sprtemp[frame].flip[r] = (byte) flipped; + sprtemp[frame].rotate = false; //jff 4/24/98 if any subbed, rotless + } + return; + } + + // the lump is only used for one rotation + + if (sprtemp[frame].lump[--rotation] == -1) + { + sprtemp[frame].lump[rotation] = lump - firstspritelump; + sprtemp[frame].flip[rotation] = (byte) flipped; + sprtemp[frame].rotate = true; //jff 4/24/98 only change if rot used + } +} + +// +// R_InitSpriteDefs +// Pass a null terminated list of sprite names +// (4 chars exactly) to be used. +// +// Builds the sprite rotation matrixes to account +// for horizontally flipped sprites. +// +// Will report an error if the lumps are inconsistent. +// Only called at startup. +// +// Sprite lump names are 4 characters for the actor, +// a letter for the frame, and a number for the rotation. +// +// A sprite that is flippable will have an additional +// letter/number appended. +// +// The rotation character can be 0 to signify no rotations. +// +// 1/25/98, 1/31/98 killough : Rewritten for performance +// +// Empirically verified to have excellent hash +// properties across standard Doom sprites: + +#define R_SpriteNameHash(s) ((unsigned)((s)[0]-((s)[1]*3-(s)[3]*2-(s)[2])*2)) + +static void R_InitSpriteDefs(const char * const * namelist) +{ + size_t numentries = lastspritelump-firstspritelump+1; + struct { int index, next; } *hash; + int i; + + if (!numentries || !*namelist) + return; + + // count the number of sprite names + for (i=0; namelist[i]; i++) + ; + + numsprites = i; + + sprites = Z_Malloc(numsprites *sizeof(*sprites), PU_STATIC, NULL); + + // Create hash table based on just the first four letters of each sprite + // killough 1/31/98 + + hash = malloc(sizeof(*hash)*numentries); // allocate hash table + + for (i=0; (size_t)i= 0) + { + memset(sprtemp, -1, sizeof(sprtemp)); + maxframe = -1; + do + { + register lumpinfo_t *lump = lumpinfo + j + firstspritelump; + + // Fast portable comparison -- killough + // (using int pointer cast is nonportable): + + if (!((lump->name[0] ^ spritename[0]) | + (lump->name[1] ^ spritename[1]) | + (lump->name[2] ^ spritename[2]) | + (lump->name[3] ^ spritename[3]))) + { + R_InstallSpriteLump(j+firstspritelump, + lump->name[4] - 'A', + lump->name[5] - '0', + false); + if (lump->name[6]) + R_InstallSpriteLump(j+firstspritelump, + lump->name[6] - 'A', + lump->name[7] - '0', + true); + } + } + while ((j = hash[j].next) >= 0); + + // check the frames that were found for completeness + if ((sprites[i].numframes = ++maxframe)) // killough 1/31/98 + { + int frame; + for (frame = 0; frame < maxframe; frame++) + switch ((int) sprtemp[frame].rotate) + { + case -1: + // no rotations were found for that frame at all + I_Error ("R_InitSprites: No patches found " + "for %.8s frame %c", namelist[i], frame+'A'); + break; + + case 0: + // only the first rotation is needed + break; + + case 1: + // must have all 8 frames + { + int rotation; + for (rotation=0 ; rotation<8 ; rotation++) + if (sprtemp[frame].lump[rotation] == -1) + I_Error ("R_InitSprites: Sprite %.8s frame %c " + "is missing rotations", + namelist[i], frame+'A'); + break; + } + } + // allocate space for the frames present and copy sprtemp to it + sprites[i].spriteframes = + Z_Malloc (maxframe * sizeof(spriteframe_t), PU_STATIC, NULL); + memcpy (sprites[i].spriteframes, sprtemp, + maxframe*sizeof(spriteframe_t)); + } + } + } + free(hash); // free hash table +} + +// +// GAME FUNCTIONS +// + +static vissprite_t *vissprites, **vissprite_ptrs; // killough +static size_t num_vissprite, num_vissprite_alloc, num_vissprite_ptrs; + +// +// R_InitSprites +// Called at program start. +// + +void R_InitSprites(const char * const *namelist) +{ + int i; + for (i=0; i= num_vissprite_alloc) // killough + { + size_t num_vissprite_alloc_prev = num_vissprite_alloc; + + num_vissprite_alloc = num_vissprite_alloc ? num_vissprite_alloc*2 : 128; + vissprites = realloc(vissprites,num_vissprite_alloc*sizeof(*vissprites)); + + //e6y: set all fields to zero + memset(vissprites + num_vissprite_alloc_prev, 0, + (num_vissprite_alloc - num_vissprite_alloc_prev)*sizeof(*vissprites)); + } + return vissprites + num_vissprite++; +} + +// +// R_DrawMaskedColumn +// Used for sprites and masked mid textures. +// Masked means: partly transparent, i.e. stored +// in posts/runs of opaque pixels. +// + +int *mfloorclip; // dropoff overflow +int *mceilingclip; // dropoff overflow +fixed_t spryscale; +fixed_t sprtopscreen; + +void R_DrawMaskedColumn( + const rpatch_t *patch, + R_DrawColumn_f colfunc, + draw_column_vars_t *dcvars, + const rcolumn_t *column, + const rcolumn_t *prevcolumn, + const rcolumn_t *nextcolumn +) +{ + int i; + int topscreen; + int bottomscreen; + fixed_t basetexturemid = dcvars->texturemid; + + dcvars->texheight = patch->height; // killough 11/98 + for (i=0; inumPosts; i++) { + const rpost_t *post = &column->posts[i]; + + // calculate unclipped screen coordinates for post + topscreen = sprtopscreen + spryscale*post->topdelta; + bottomscreen = topscreen + spryscale*post->length; + + dcvars->yl = (topscreen+FRACUNIT-1)>>FRACBITS; + dcvars->yh = (bottomscreen-1)>>FRACBITS; + + if (dcvars->yh >= mfloorclip[dcvars->x]) + dcvars->yh = mfloorclip[dcvars->x]-1; + + if (dcvars->yl <= mceilingclip[dcvars->x]) + dcvars->yl = mceilingclip[dcvars->x]+1; + + // killough 3/2/98, 3/27/98: Failsafe against overflow/crash: + if (dcvars->yl <= dcvars->yh && dcvars->yh < viewheight) + { + dcvars->source = column->pixels + post->topdelta; + dcvars->prevsource = prevcolumn->pixels + post->topdelta; + dcvars->nextsource = nextcolumn->pixels + post->topdelta; + + dcvars->texturemid = basetexturemid - (post->topdelta<edgeslope = post->slope; + // Drawn by either R_DrawColumn + // or (SHADOW) R_DrawFuzzColumn. + dcvars->drawingmasked = 1; // POPE + colfunc (dcvars); + dcvars->drawingmasked = 0; // POPE + } + } + dcvars->texturemid = basetexturemid; +} + +// +// R_DrawVisSprite +// mfloorclip and mceilingclip should also be set. +// +// CPhipps - new wad lump handling, *'s to const*'s +static void R_DrawVisSprite(vissprite_t *vis, int x1, int x2) +{ + int texturecolumn; + fixed_t frac; + const rpatch_t *patch = R_CachePatchNum(vis->patch+firstspritelump); + R_DrawColumn_f colfunc; + draw_column_vars_t dcvars; + enum draw_filter_type_e filter; + enum draw_filter_type_e filterz; + + R_SetDefaultDrawColumnVars(&dcvars); + if (vis->isplayersprite) { + dcvars.edgetype = drawvars.patch_edges; + filter = drawvars.filterpatch; + filterz = RDRAW_FILTER_POINT; + } else { + dcvars.edgetype = drawvars.sprite_edges; + filter = drawvars.filtersprite; + filterz = drawvars.filterz; + } + + dcvars.colormap = vis->colormap; + dcvars.nextcolormap = dcvars.colormap; // for filtering -- POPE + + // killough 4/11/98: rearrange and handle translucent sprites + // mixed with translucent/non-translucenct 2s normals + + if (!dcvars.colormap) // NULL colormap = shadow draw + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_FUZZ, filter, filterz); // killough 3/14/98 + else + if (vis->mobjflags & MF_TRANSLATION) + { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLATED, filter, filterz); + dcvars.translation = translationtables - 256 + + ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT-8) ); + } + else + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, filter, filterz); // killough 3/14/98, 4/11/98 + +// proff 11/06/98: Changed for high-res + dcvars.iscale = FixedDiv (FRACUNIT, vis->scale); + dcvars.texturemid = vis->texturemid; + frac = vis->startfrac; + if (filter == RDRAW_FILTER_LINEAR) + frac -= (FRACUNIT>>1); + spryscale = vis->scale; + sprtopscreen = centeryfrac - FixedMul(dcvars.texturemid,spryscale); + + for (dcvars.x=vis->x1 ; dcvars.x<=vis->x2 ; dcvars.x++, frac += vis->xiscale) + { + texturecolumn = frac>>FRACBITS; + dcvars.texu = frac; + + R_DrawMaskedColumn( + patch, + colfunc, + &dcvars, + R_GetPatchColumnClamped(patch, texturecolumn), + R_GetPatchColumnClamped(patch, texturecolumn-1), + R_GetPatchColumnClamped(patch, texturecolumn+1) + ); + } + R_UnlockPatchNum(vis->patch+firstspritelump); // cph - release lump +} + +// +// R_ProjectSprite +// Generates a vissprite for a thing if it might be visible. +// + +static void R_ProjectSprite (mobj_t* thing, int lightlevel) +{ + fixed_t gzt; // killough 3/27/98 + fixed_t tx; + fixed_t xscale; + int x1; + int x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + boolean flip; + vissprite_t *vis; + fixed_t iscale; + int heightsec; // killough 3/27/98 + + // transform the origin point + fixed_t tr_x, tr_y; + fixed_t fx, fy, fz; + fixed_t gxt, gyt; + fixed_t tz; + int width; + + if (movement_smooth) + { + fx = thing->PrevX + FixedMul (tic_vars.frac, thing->x - thing->PrevX); + fy = thing->PrevY + FixedMul (tic_vars.frac, thing->y - thing->PrevY); + fz = thing->PrevZ + FixedMul (tic_vars.frac, thing->z - thing->PrevZ); + } + else + { + fx = thing->x; + fy = thing->y; + fz = thing->z; + } + tr_x = fx - viewx; + tr_y = fy - viewy; + + gxt = FixedMul(tr_x,viewcos); + gyt = -FixedMul(tr_y,viewsin); + + tz = gxt-gyt; + + // thing is behind view plane? + if (tz < MINZ) + return; + + xscale = FixedDiv(projection, tz); + + gxt = -FixedMul(tr_x,viewsin); + gyt = FixedMul(tr_y,viewcos); + tx = -(gyt+gxt); + + // too far off the side? + if (D_abs(tx)>(tz<<2)) + return; + + // decide which patch to use for sprite relative to player + sprdef = &sprites[thing->sprite]; + + if (!sprdef->spriteframes) + I_Error ("R_ProjectSprite: Missing spriteframes %i : %i", thing->sprite, + thing->frame); + + sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; + + if (sprframe->rotate) + { + // choose a different rotation based on player view + angle_t ang = R_PointToAngle(fx, fy); + unsigned rot = (ang-thing->angle+(unsigned)(ANG45/2)*9)>>29; + lump = sprframe->lump[rot]; + flip = (boolean) sprframe->flip[rot]; + } + else + { + // use single rotation for all views + lump = sprframe->lump[0]; + flip = (boolean) sprframe->flip[0]; + } + + { + const rpatch_t* patch = R_CachePatchNum(lump+firstspritelump); + + /* calculate edges of the shape + * cph 2003/08/1 - fraggle points out that this offset must be flipped + * if the sprite is flipped; e.g. FreeDoom imp is messed up by this. */ + if (flip) { + tx -= (patch->width - patch->leftoffset) << FRACBITS; + } else { + tx -= patch->leftoffset << FRACBITS; + } + x1 = (centerxfrac + FixedMul(tx,xscale)) >> FRACBITS; + + tx += patch->width<> FRACBITS) - 1; + + gzt = fz + (patch->topoffset << FRACBITS); + width = patch->width; + R_UnlockPatchNum(lump+firstspritelump); + } + + // off the side? + if (x1 > viewwidth || x2 < 0) + return; + + // killough 4/9/98: clip things which are out of view due to height + // e6y: fix of hanging decoration disappearing in Batman Doom MAP02 + // centeryfrac -> viewheightfrac + if (fz > viewz + FixedDiv(viewheightfrac, xscale) || + gzt < viewz - FixedDiv(viewheightfrac-viewheight, xscale)) + return; + + // killough 3/27/98: exclude things totally separated + // from the viewer, by either water or fake ceilings + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + + heightsec = thing->subsector->sector->heightsec; + + if (heightsec != -1) // only clip things which are in special sectors + { + int phs = viewplayer->mo->subsector->sector->heightsec; + if (phs != -1 && viewz < sectors[phs].floorheight ? + fz >= sectors[heightsec].floorheight : + gzt < sectors[heightsec].floorheight) + return; + if (phs != -1 && viewz > sectors[phs].ceilingheight ? + gzt < sectors[heightsec].ceilingheight && + viewz >= sectors[heightsec].ceilingheight : + fz >= sectors[heightsec].ceilingheight) + return; + } + + // store information in a vissprite + vis = R_NewVisSprite (); + + // killough 3/27/98: save sector for special clipping later + vis->heightsec = heightsec; + + vis->mobjflags = thing->flags; +// proff 11/06/98: Changed for high-res + vis->scale = FixedDiv(projectiony, tz); + vis->gx = fx; + vis->gy = fy; + vis->gz = fz; + vis->gzt = gzt; // killough 3/27/98 + vis->texturemid = vis->gzt - viewz; + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; + iscale = FixedDiv (FRACUNIT, xscale); + + if (flip) + { + vis->startfrac = (width<xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale*(vis->x1-x1); + vis->patch = lump; + + // get light level + if (thing->flags & MF_SHADOW) + vis->colormap = NULL; // shadow draw + else if (fixedcolormap) + vis->colormap = fixedcolormap; // fixed map + else if (thing->frame & FF_FULLBRIGHT) + vis->colormap = fullcolormap; // full bright // killough 3/20/98 + else + { // diminished light + vis->colormap = R_ColourMap(lightlevel,xscale); + } +} + +// +// R_AddSprites +// During BSP traversal, this adds sprites by sector. +// +// killough 9/18/98: add lightlevel as parameter, fixing underwater lighting +void R_AddSprites(subsector_t* subsec, int lightlevel) +{ + sector_t* sec=subsec->sector; + mobj_t *thing; + + // BSP is traversed by subsector. + // A sector might have been split into several + // subsectors during BSP building. + // Thus we check whether its already added. + + if (sec->validcount == validcount) + return; + + // Well, now it will be done. + sec->validcount = validcount; + + // Handle all things in sector. + + for (thing = sec->thinglist; thing; thing = thing->snext) + R_ProjectSprite(thing, lightlevel); +} + +// +// R_DrawPSprite +// + +static void R_DrawPSprite (pspdef_t *psp, int lightlevel) +{ + int x1, x2; + spritedef_t *sprdef; + spriteframe_t *sprframe; + int lump; + boolean flip; + vissprite_t *vis; + vissprite_t avis; + int width; + fixed_t topoffset; + + avis.isplayersprite = true; + + // decide which patch to use + + sprdef = &sprites[psp->state->sprite]; + + sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK]; + + lump = sprframe->lump[0]; + flip = (boolean) sprframe->flip[0]; + + { + const rpatch_t* patch = R_CachePatchNum(lump+firstspritelump); + // calculate edges of the shape + fixed_t tx; + tx = psp->sx-160*FRACUNIT; + + tx -= patch->leftoffset<>FRACBITS; + + tx += patch->width<>FRACBITS) - 1; + + width = patch->width; + topoffset = patch->topoffset< viewwidth) + return; + + // store information in a vissprite + vis = &avis; + vis->mobjflags = 0; + // killough 12/98: fix psprite positioning problem + vis->texturemid = (BASEYCENTER<sy-topoffset); + vis->x1 = x1 < 0 ? 0 : x1; + vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2; +// proff 11/06/98: Added for high-res + vis->scale = pspriteyscale; + + if (flip) + { + vis->xiscale = -pspriteiscale; + vis->startfrac = (width<xiscale = pspriteiscale; + vis->startfrac = 0; + } + + if (vis->x1 > x1) + vis->startfrac += vis->xiscale*(vis->x1-x1); + + vis->patch = lump; + + if (viewplayer->powers[pw_invisibility] > 4*32 + || viewplayer->powers[pw_invisibility] & 8) + vis->colormap = NULL; // shadow draw + else if (fixedcolormap) + vis->colormap = fixedcolormap; // fixed color + else if (psp->state->frame & FF_FULLBRIGHT) + vis->colormap = fullcolormap; // full bright // killough 3/20/98 + else + // add a fudge factor to better match the original game + vis->colormap = R_ColourMap(lightlevel, + FixedMul(pspritescale, 0x2b000)); // local light + + // proff 11/99: don't use software stuff in OpenGL + R_DrawVisSprite(vis, vis->x1, vis->x2); +} + +// +// R_DrawPlayerSprites +// + +void R_DrawPlayerSprites(void) +{ + int i, lightlevel = viewplayer->mo->subsector->sector->lightlevel; + pspdef_t *psp; + + // clip to screen bounds + mfloorclip = screenheightarray; + mceilingclip = negonearray; + + // add all active psprites + for (i=0, psp=viewplayer->psprites; istate) + R_DrawPSprite (psp, lightlevel); +} + +// +// R_SortVisSprites +// +// Rewritten by Lee Killough to avoid using unnecessary +// linked lists, and to use faster sorting algorithm. +// + +#define bcopyp(d, s, n) memcpy(d, s, (n) * sizeof(void *)) + +// killough 9/2/98: merge sort + +static void msort(vissprite_t **s, vissprite_t **t, int n) +{ + if (n >= 16) + { + int n1 = n/2, n2 = n - n1; + vissprite_t **s1 = s, **s2 = s + n1, **d = t; + + msort(s1, t, n1); + msort(s2, t, n2); + + while ((*s1)->scale > (*s2)->scale ? + (*d++ = *s1++, --n1) : (*d++ = *s2++, --n2)); + + if (n2) + bcopyp(d, s2, n2); + else + bcopyp(d, s1, n1); + + bcopyp(s, t, n); + } + else + { + int i; + for (i = 1; i < n; i++) + { + vissprite_t *temp = s[i]; + if (s[i-1]->scale < temp->scale) + { + int j = i; + while ((s[j] = s[j-1])->scale < temp->scale && --j); + s[j] = temp; + } + } + } +} + +void R_SortVisSprites (void) +{ + if (num_vissprite) + { + int i = num_vissprite; + + // If we need to allocate more pointers for the vissprites, + // allocate as many as were allocated for sprites -- killough + // killough 9/22/98: allocate twice as many + + if (num_vissprite_ptrs < num_vissprite*2) + { + free(vissprite_ptrs); // better than realloc -- no preserving needed + vissprite_ptrs = malloc((num_vissprite_ptrs = num_vissprite_alloc*2) + * sizeof *vissprite_ptrs); + } + + while (--i>=0) + vissprite_ptrs[i] = vissprites+i; + + // killough 9/22/98: replace qsort with merge sort, since the keys + // are roughly in order to begin with, due to BSP rendering. + + msort(vissprite_ptrs, vissprite_ptrs + num_vissprite, num_vissprite); + } +} + +// +// R_DrawSprite +// + +static void R_DrawSprite (vissprite_t* spr) +{ + drawseg_t *ds; + int clipbot[MAX_SCREENWIDTH]; // killough 2/8/98: // dropoff overflow + int cliptop[MAX_SCREENWIDTH]; // change to MAX_* // dropoff overflow + int x; + int r1; + int r2; + fixed_t scale; + fixed_t lowscale; + + for (x = spr->x1 ; x<=spr->x2 ; x++) + clipbot[x] = cliptop[x] = -2; + + // Scan drawsegs from end to start for obscuring segs. + // The first drawseg that has a greater scale is the clip seg. + + // Modified by Lee Killough: + // (pointer check was originally nonportable + // and buggy, by going past LEFT end of array): + + // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) old buggy code + + for (ds=ds_p ; ds-- > drawsegs ; ) // new -- killough + { // determine if the drawseg obscures the sprite + if (ds->x1 > spr->x2 || ds->x2 < spr->x1 || + (!ds->silhouette && !ds->maskedtexturecol)) + continue; // does not cover sprite + + r1 = ds->x1 < spr->x1 ? spr->x1 : ds->x1; + r2 = ds->x2 > spr->x2 ? spr->x2 : ds->x2; + + if (ds->scale1 > ds->scale2) + { + lowscale = ds->scale2; + scale = ds->scale1; + } + else + { + lowscale = ds->scale1; + scale = ds->scale2; + } + + if (scale < spr->scale || (lowscale < spr->scale && + !R_PointOnSegSide (spr->gx, spr->gy, ds->curline))) + { + if (ds->maskedtexturecol) // masked mid texture? + R_RenderMaskedSegRange(ds, r1, r2); + continue; // seg is behind sprite + } + + // clip this piece of the sprite + // killough 3/27/98: optimized and made much shorter + + if (ds->silhouette&SIL_BOTTOM && spr->gz < ds->bsilheight) //bottom sil + for (x=r1 ; x<=r2 ; x++) + if (clipbot[x] == -2) + clipbot[x] = ds->sprbottomclip[x]; + + if (ds->silhouette&SIL_TOP && spr->gzt > ds->tsilheight) // top sil + for (x=r1 ; x<=r2 ; x++) + if (cliptop[x] == -2) + cliptop[x] = ds->sprtopclip[x]; + } + + // killough 3/27/98: + // Clip the sprite against deep water and/or fake ceilings. + // killough 4/9/98: optimize by adding mh + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + // killough 11/98: fix disappearing sprites + + if (spr->heightsec != -1) // only things in specially marked sectors + { + fixed_t h,mh; + int phs = viewplayer->mo->subsector->sector->heightsec; + if ((mh = sectors[spr->heightsec].floorheight) > spr->gz && + (h = centeryfrac - FixedMul(mh-=viewz, spr->scale)) >= 0 && + (h >>= FRACBITS) < viewheight) { + if (mh <= 0 || (phs != -1 && viewz > sectors[phs].floorheight)) + { // clip bottom + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (clipbot[x] == -2 || h < clipbot[x]) + clipbot[x] = h; + } + else // clip top + if (phs != -1 && viewz <= sectors[phs].floorheight) // killough 11/98 + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (cliptop[x] == -2 || h > cliptop[x]) + cliptop[x] = h; + } + + if ((mh = sectors[spr->heightsec].ceilingheight) < spr->gzt && + (h = centeryfrac - FixedMul(mh-viewz, spr->scale)) >= 0 && + (h >>= FRACBITS) < viewheight) { + if (phs != -1 && viewz >= sectors[phs].ceilingheight) + { // clip bottom + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (clipbot[x] == -2 || h < clipbot[x]) + clipbot[x] = h; + } + else // clip top + for (x=spr->x1 ; x<=spr->x2 ; x++) + if (cliptop[x] == -2 || h > cliptop[x]) + cliptop[x] = h; + } + } + // killough 3/27/98: end special clipping for deep water / fake ceilings + + // all clipping has been performed, so draw the sprite + // check for unclipped columns + + for (x = spr->x1 ; x<=spr->x2 ; x++) { + if (clipbot[x] == -2) + clipbot[x] = viewheight; + + if (cliptop[x] == -2) + cliptop[x] = -1; + } + + mfloorclip = clipbot; + mceilingclip = cliptop; + R_DrawVisSprite (spr, spr->x1, spr->x2); +} + +// +// R_DrawMasked +// + +void R_DrawMasked(void) +{ + int i; + drawseg_t *ds; + + R_SortVisSprites(); + + // draw all vissprites back to front + + rendered_vissprites = num_vissprite; + for (i = num_vissprite ;--i>=0; ) + R_DrawSprite(vissprite_ptrs[i]); // killough + + // render any remaining masked mid textures + + // Modified by Lee Killough: + // (pointer check was originally nonportable + // and buggy, by going past LEFT end of array): + + // for (ds=ds_p-1 ; ds >= drawsegs ; ds--) old buggy code + + for (ds=ds_p ; ds-- > drawsegs ; ) // new -- killough + if (ds->maskedtexturecol) + R_RenderMaskedSegRange(ds, ds->x1, ds->x2); + + // draw the psprites on top of everything + // but does not draw on side views + if (!viewangleoffset) + R_DrawPlayerSprites (); +} diff --git a/src/r_things.h b/src/r_things.h new file mode 100644 index 00000000..a6c504e6 --- /dev/null +++ b/src/r_things.h @@ -0,0 +1,72 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Rendering of moving objects, sprites. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __R_THINGS__ +#define __R_THINGS__ + +#ifdef __GNUG__ +#pragma interface +#endif + +#include "r_draw.h" + +/* Constant arrays used for psprite clipping and initializing clipping. */ + +extern int negonearray[MAX_SCREENWIDTH]; /* killough 2/8/98: */ // dropoff overflow +extern int screenheightarray[MAX_SCREENWIDTH]; /* change to MAX_* */ // dropoff overflow + +/* Vars for R_DrawMaskedColumn */ + +extern int *mfloorclip; // dropoff overflow +extern int *mceilingclip; // dropoff overflow +extern fixed_t spryscale; +extern fixed_t sprtopscreen; +extern fixed_t pspritescale; +extern fixed_t pspriteiscale; +/* proff 11/06/98: Added for high-res */ +extern fixed_t pspriteyscale; + +void R_DrawMaskedColumn(const rpatch_t *patch, + R_DrawColumn_f colfunc, + draw_column_vars_t *dcvars, + const rcolumn_t *column, + const rcolumn_t *prevcolumn, + const rcolumn_t *nextcolumn); +void R_SortVisSprites(void); +void R_AddSprites(subsector_t* subsec, int lightlevel); +void R_DrawPlayerSprites(void); +void R_InitSprites(const char * const * namelist); +void R_ClearSprites(void); +void R_DrawMasked(void); + +#endif diff --git a/src/s_sound.c b/src/s_sound.c new file mode 100644 index 00000000..6eaf908d --- /dev/null +++ b/src/s_sound.c @@ -0,0 +1,688 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: Platform-independent sound code + * + *-----------------------------------------------------------------------------*/ + +// killough 3/7/98: modified to allow arbitrary listeners in spy mode +// killough 5/2/98: reindented, removed useless code, beautified + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "s_sound.h" +#include "i_sound.h" +#include "i_system.h" +#include "d_main.h" +#include "r_main.h" +#include "m_random.h" +#include "w_wad.h" +#include "lprintf.h" + +// when to clip out sounds +// Does not fit the large outdoor areas. +#define S_CLIPPING_DIST (1200<>FRACBITS) + +// Adjustable by menu. +#define NORM_PITCH 128 +#define NORM_PRIORITY 64 +#define NORM_SEP 128 +#define S_STEREO_SWING (96<= prboom_2_compatibility && sfx_id == sfx_noway); // killough 4/25/98 + sfx_id &= ~PICKUP_SOUND; + + // check for bogus sound # + if (sfx_id < 1 || sfx_id > NUMSFX) + I_Error("S_StartSoundAtVolume: Bad sfx #: %d", sfx_id); + + sfx = &S_sfx[sfx_id]; + + // Initialize sound parameters + if (sfx->link) + { + pitch = sfx->pitch; + priority = sfx->priority; + volume += sfx->volume; + + if (volume < 1) + return; + + if (volume > snd_SfxVolume) + volume = snd_SfxVolume; + } + else + { + pitch = NORM_PITCH; + priority = NORM_PRIORITY; + } + + // Check to see if it is audible, modify the params + // killough 3/7/98, 4/25/98: code rearranged slightly + + if (!origin || origin == players[displayplayer].mo) { + sep = NORM_SEP; + volume *= 8; + } else + if (!S_AdjustSoundParams(players[displayplayer].mo, origin, &volume, + &sep, &pitch)) + return; + else + if ( origin->x == players[displayplayer].mo->x && + origin->y == players[displayplayer].mo->y) + sep = NORM_SEP; + + // hacks to vary the sfx pitches + if (sfx_id >= sfx_sawup && sfx_id <= sfx_sawhit) + pitch += 8 - (M_Random()&15); + else + if (sfx_id != sfx_itemup && sfx_id != sfx_tink) + pitch += 16 - (M_Random()&31); + + if (pitch<0) + pitch = 0; + + if (pitch>255) + pitch = 255; + + // kill old sound + for (cnum=0 ; cnumlumpnum < 0 && (sfx->lumpnum = I_GetSfxLumpNum(sfx)) < 0) + return; + + // increase the usefulness + if (sfx->usefulness++ < 0) + sfx->usefulness = 1; + + // Assigns the handle to one of the channels in the mix/output buffer. + { // e6y: [Fix] Crash with zero-length sounds. + int h = I_StartSound(sfx_id, cnum, volume, sep, pitch, priority); + if (h != -1) channels[cnum].handle = h; + } +} + +void S_StartSound(void *origin, int sfx_id) +{ + S_StartSoundAtVolume(origin, sfx_id, snd_SfxVolume); +} + +void S_StopSound(void *origin) +{ + int cnum; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + + for (cnum=0 ; cnumhandle); + mus_paused = true; + } +} + +void S_ResumeSound(void) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (mus_playing && mus_paused) + { + I_ResumeSong(mus_playing->handle); + mus_paused = false; + } +} + + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener_p) +{ + mobj_t *listener = (mobj_t*) listener_p; + int cnum; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + +#ifdef UPDATE_MUSIC + I_UpdateMusic(); +#endif + + for (cnum=0 ; cnumsfxinfo)) + { + if (I_SoundIsPlaying(c->handle)) + { + // initialize parameters + int volume = snd_SfxVolume; + int pitch = NORM_PITCH; + int sep = NORM_SEP; + + if (sfx->link) + { + pitch = sfx->pitch; + volume += sfx->volume; + if (volume < 1) + { + S_StopChannel(cnum); + continue; + } + else + if (volume > snd_SfxVolume) + volume = snd_SfxVolume; + } + + // check non-local sounds for distance clipping + // or modify their params + if (c->origin && listener_p != c->origin) { // killough 3/20/98 + if (!S_AdjustSoundParams(listener, c->origin, + &volume, &sep, &pitch)) + S_StopChannel(cnum); + else + I_UpdateSoundParams(c->handle, volume, sep, pitch); + } + } + else // if channel is allocated but sound has stopped, free it + S_StopChannel(cnum); + } + } +} + + + +void S_SetMusicVolume(int volume) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + if (volume < 0 || volume > 15) + I_Error("S_SetMusicVolume: Attempt to set music volume at %d", volume); + I_SetMusicVolume(volume); + snd_MusicVolume = volume; +} + + + +void S_SetSfxVolume(int volume) +{ + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + if (volume < 0 || volume > 127) + I_Error("S_SetSfxVolume: Attempt to set sfx volume at %d", volume); + snd_SfxVolume = volume; +} + + + +// Starts some music with the music id found in sounds.h. +// +void S_StartMusic(int m_id) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + S_ChangeMusic(m_id, false); +} + + + +void S_ChangeMusic(int musicnum, int looping) +{ + musicinfo_t *music; + int music_file_failed; // cournia - if true load the default MIDI music + char* music_filename; // cournia + + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (musicnum <= mus_None || musicnum >= NUMMUSIC) + I_Error("S_ChangeMusic: Bad music number %d", musicnum); + + music = &S_music[musicnum]; + + if (mus_playing == music) + return; + + // shutdown old music + S_StopMusic(); + + // get lumpnum if neccessary + if (!music->lumpnum) + { + char namebuf[9]; + sprintf(namebuf, "d_%s", music->name); + music->lumpnum = W_GetNumForName(namebuf); + } + + music_file_failed = 1; + + // proff_fs - only load when from IWAD + if (lumpinfo[music->lumpnum].source == source_iwad) + { + // cournia - check to see if we can play a higher quality music file + // rather than the default MIDI + music_filename = I_FindFile(S_music_files[musicnum], ""); + if (music_filename) + { + music_file_failed = I_RegisterMusic(music_filename, music); + free(music_filename); + } + } + + if (music_file_failed) + { + //cournia - could not load music file, play default MIDI music + + // load & register it + music->data = W_CacheLumpNum(music->lumpnum); + music->handle = I_RegisterSong(music->data, W_LumpLength(music->lumpnum)); + } + + // play it + I_PlaySong(music->handle, looping); + + mus_playing = music; +} + + +void S_StopMusic(void) +{ + //jff 1/22/98 return if music is not enabled + if (!mus_card || nomusicparm) + return; + + if (mus_playing) + { + if (mus_paused) + I_ResumeSong(mus_playing->handle); + + I_StopSong(mus_playing->handle); + I_UnRegisterSong(mus_playing->handle); + if (mus_playing->lumpnum >= 0) + W_UnlockLumpNum(mus_playing->lumpnum); // cph - release the music data + + mus_playing->data = 0; + mus_playing = 0; + } +} + + + +void S_StopChannel(int cnum) +{ + int i; + channel_t *c = &channels[cnum]; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return; + + if (c->sfxinfo) + { + // stop the sound playing + if (I_SoundIsPlaying(c->handle)) + I_StopSound(c->handle); + + // check to see + // if other channels are playing the sound + for (i=0 ; isfxinfo == channels[i].sfxinfo) + break; + + // degrade usefulness of sound data + c->sfxinfo->usefulness--; + c->sfxinfo = 0; + } +} + +// +// Changes volume, stereo-separation, and pitch variables +// from the norm of a sound effect to be played. +// If the sound is not audible, returns a 0. +// Otherwise, modifies parameters and returns 1. +// + +int S_AdjustSoundParams(mobj_t *listener, mobj_t *source, + int *vol, int *sep, int *pitch) +{ + fixed_t adx, ady,approx_dist; + angle_t angle; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return 0; + + // e6y + // Fix crash when the program wants to S_AdjustSoundParams() for player + // which is not displayplayer and displayplayer was not spawned at the moment. + // It happens in multiplayer demos only. + // + // Stack trace is: + /* P_SetupLevel() \ P_LoadThings() \ P_SpawnMapThing() \ P_SpawnPlayer(players[0]) \ + P_SetupPsprites() \ P_BringUpWeapon() \ S_StartSound(players[0]->mo, sfx_sawup) \ + S_StartSoundAtVolume() \ S_AdjustSoundParams(players[displayplayer]->mo, ...); */ + // players[displayplayer]->mo is NULL + // + // There is no more crash on e1cmnet3.lmp between e1m2 and e1m3 + // http://competn.doom2.net/pub/compet-n/doom/coop/movies/e1cmnet3.zip + if (!listener) + return 0; + + // calculate the distance to sound origin + // and clip it if necessary + adx = D_abs(listener->x - source->x); + ady = D_abs(listener->y - source->y); + + // From _GG1_ p.428. Appox. eucledian distance fast. + approx_dist = adx + ady - ((adx < ady ? adx : ady)>>1); + + if (!approx_dist) // killough 11/98: handle zero-distance as special case + { + *sep = NORM_SEP; + *vol = snd_SfxVolume; + return *vol > 0; + } + + if (approx_dist > S_CLIPPING_DIST) + return 0; + + // angle of source to listener + angle = R_PointToAngle2(listener->x, listener->y, source->x, source->y); + + if (angle <= listener->angle) + angle += 0xffffffff; + angle -= listener->angle; + angle >>= ANGLETOFINESHIFT; + + // stereo separation + *sep = 128 - (FixedMul(S_STEREO_SWING,finesine[angle])>>FRACBITS); + + // volume calculation + if (approx_dist < S_CLOSE_DIST) + *vol = snd_SfxVolume*8; + else + // distance effect + *vol = (snd_SfxVolume * ((S_CLIPPING_DIST-approx_dist)>>FRACBITS) * 8) + / S_ATTENUATOR; + + return (*vol > 0); +} + +// +// S_getChannel : +// If none available, return -1. Otherwise channel #. +// +// killough 4/25/98: made static, added is_pickup argument + +static int S_getChannel(void *origin, sfxinfo_t *sfxinfo, int is_pickup) +{ + // channel number to use + int cnum; + channel_t *c; + + //jff 1/22/98 return if sound is not enabled + if (!snd_card || nosfxparm) + return -1; + + // Find an open channel + for (cnum=0; cnumpriority >= sfxinfo->priority) + break; + if (cnum == numChannels) + return -1; // No lower priority. Sorry, Charlie. + else + S_StopChannel(cnum); // Otherwise, kick out lower priority. + } + + c = &channels[cnum]; // channel is decided to be cnum. + c->sfxinfo = sfxinfo; + c->origin = origin; + c->is_pickup = is_pickup; // killough 4/25/98 + return cnum; +} diff --git a/src/s_sound.h b/src/s_sound.h new file mode 100644 index 00000000..29d32195 --- /dev/null +++ b/src/s_sound.h @@ -0,0 +1,100 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The not so system specific sound interface. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __S_SOUND__ +#define __S_SOUND__ + +#ifdef __GNUG__ +#pragma interface +#endif + +// +// Initializes sound stuff, including volume +// Sets channels, SFX and music volume, +// allocates channel buffer, sets S_sfx lookup. +// +void S_Init(int sfxVolume, int musicVolume); + +// Kills all sounds +void S_Stop(void); + +// +// Per level startup code. +// Kills playing sounds at start of level, +// determines music if any, changes music. +// +void S_Start(void); + +// +// Start sound for thing at +// using from sounds.h +// +void S_StartSound(void *origin, int sound_id); + +// Will start a sound at a given volume. +void S_StartSoundAtVolume(void *origin, int sound_id, int volume); + +// killough 4/25/98: mask used to indicate sound origin is player item pickup +#define PICKUP_SOUND (0x8000) + +// Stop sound for thing at +void S_StopSound(void* origin); + +// Start music using from sounds.h +void S_StartMusic(int music_id); + +// Start music using from sounds.h, and set whether looping +void S_ChangeMusic(int music_id, int looping); + +// Stops the music fer sure. +void S_StopMusic(void); + +// Stop and resume music, during game PAUSE. +void S_PauseSound(void); +void S_ResumeSound(void); + +// +// Updates music & sounds +// +void S_UpdateSounds(void* listener); +void S_SetMusicVolume(int volume); +void S_SetSfxVolume(int volume); + +// machine-independent sound params +extern int default_numChannels; +extern int numChannels; + +//jff 3/17/98 holds last IDMUS number, or -1 +extern int idmusnum; + +#endif diff --git a/src/sounds.c b/src/sounds.c new file mode 100644 index 00000000..4e58efed --- /dev/null +++ b/src/sounds.c @@ -0,0 +1,237 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Created by a sound utility. + * Kept as a sample, DOOM2 sounds. + * + *-----------------------------------------------------------------------------*/ + +// killough 5/3/98: reformatted + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomtype.h" +#include "sounds.h" + +// +// Information about all the music +// + +musicinfo_t S_music[] = { + { 0 }, + { "e1m1", 0 }, + { "e1m2", 0 }, + { "e1m3", 0 }, + { "e1m4", 0 }, + { "e1m5", 0 }, + { "e1m6", 0 }, + { "e1m7", 0 }, + { "e1m8", 0 }, + { "e1m9", 0 }, + { "e2m1", 0 }, + { "e2m2", 0 }, + { "e2m3", 0 }, + { "e2m4", 0 }, + { "e2m5", 0 }, + { "e2m6", 0 }, + { "e2m7", 0 }, + { "e2m8", 0 }, + { "e2m9", 0 }, + { "e3m1", 0 }, + { "e3m2", 0 }, + { "e3m3", 0 }, + { "e3m4", 0 }, + { "e3m5", 0 }, + { "e3m6", 0 }, + { "e3m7", 0 }, + { "e3m8", 0 }, + { "e3m9", 0 }, + { "inter", 0 }, + { "intro", 0 }, + { "bunny", 0 }, + { "victor", 0 }, + { "introa", 0 }, + { "runnin", 0 }, + { "stalks", 0 }, + { "countd", 0 }, + { "betwee", 0 }, + { "doom", 0 }, + { "the_da", 0 }, + { "shawn", 0 }, + { "ddtblu", 0 }, + { "in_cit", 0 }, + { "dead", 0 }, + { "stlks2", 0 }, + { "theda2", 0 }, + { "doom2", 0 }, + { "ddtbl2", 0 }, + { "runni2", 0 }, + { "dead2", 0 }, + { "stlks3", 0 }, + { "romero", 0 }, + { "shawn2", 0 }, + { "messag", 0 }, + { "count2", 0 }, + { "ddtbl3", 0 }, + { "ampie", 0 }, + { "theda3", 0 }, + { "adrian", 0 }, + { "messg2", 0 }, + { "romer2", 0 }, + { "tense", 0 }, + { "shawn3", 0 }, + { "openin", 0 }, + { "evil", 0 }, + { "ultima", 0 }, + { "read_m", 0 }, + { "dm2ttl", 0 }, + { "dm2int", 0 }, +}; + + +// +// Information about all the sfx +// + +sfxinfo_t S_sfx[] = { + // S_sfx[0] needs to be a dummy for odd reasons. + { "none", false, 0, 0, -1, -1, 0 }, + + { "pistol", false, 64, 0, -1, -1, 0 }, + { "shotgn", false, 64, 0, -1, -1, 0 }, + { "sgcock", false, 64, 0, -1, -1, 0 }, + { "dshtgn", false, 64, 0, -1, -1, 0 }, + { "dbopn", false, 64, 0, -1, -1, 0 }, + { "dbcls", false, 64, 0, -1, -1, 0 }, + { "dbload", false, 64, 0, -1, -1, 0 }, + { "plasma", false, 64, 0, -1, -1, 0 }, + { "bfg", false, 64, 0, -1, -1, 0 }, + { "sawup", false, 64, 0, -1, -1, 0 }, + { "sawidl", false, 118, 0, -1, -1, 0 }, + { "sawful", false, 64, 0, -1, -1, 0 }, + { "sawhit", false, 64, 0, -1, -1, 0 }, + { "rlaunc", false, 64, 0, -1, -1, 0 }, + { "rxplod", false, 70, 0, -1, -1, 0 }, + { "firsht", false, 70, 0, -1, -1, 0 }, + { "firxpl", false, 70, 0, -1, -1, 0 }, + { "pstart", false, 100, 0, -1, -1, 0 }, + { "pstop", false, 100, 0, -1, -1, 0 }, + { "doropn", false, 100, 0, -1, -1, 0 }, + { "dorcls", false, 100, 0, -1, -1, 0 }, + { "stnmov", false, 119, 0, -1, -1, 0 }, + { "swtchn", false, 78, 0, -1, -1, 0 }, + { "swtchx", false, 78, 0, -1, -1, 0 }, + { "plpain", false, 96, 0, -1, -1, 0 }, + { "dmpain", false, 96, 0, -1, -1, 0 }, + { "popain", false, 96, 0, -1, -1, 0 }, + { "vipain", false, 96, 0, -1, -1, 0 }, + { "mnpain", false, 96, 0, -1, -1, 0 }, + { "pepain", false, 96, 0, -1, -1, 0 }, + { "slop", false, 78, 0, -1, -1, 0 }, + { "itemup", true, 78, 0, -1, -1, 0 }, + { "wpnup", true, 78, 0, -1, -1, 0 }, + { "oof", false, 96, 0, -1, -1, 0 }, + { "telept", false, 32, 0, -1, -1, 0 }, + { "posit1", true, 98, 0, -1, -1, 0 }, + { "posit2", true, 98, 0, -1, -1, 0 }, + { "posit3", true, 98, 0, -1, -1, 0 }, + { "bgsit1", true, 98, 0, -1, -1, 0 }, + { "bgsit2", true, 98, 0, -1, -1, 0 }, + { "sgtsit", true, 98, 0, -1, -1, 0 }, + { "cacsit", true, 98, 0, -1, -1, 0 }, + { "brssit", true, 94, 0, -1, -1, 0 }, + { "cybsit", true, 92, 0, -1, -1, 0 }, + { "spisit", true, 90, 0, -1, -1, 0 }, + { "bspsit", true, 90, 0, -1, -1, 0 }, + { "kntsit", true, 90, 0, -1, -1, 0 }, + { "vilsit", true, 90, 0, -1, -1, 0 }, + { "mansit", true, 90, 0, -1, -1, 0 }, + { "pesit", true, 90, 0, -1, -1, 0 }, + { "sklatk", false, 70, 0, -1, -1, 0 }, + { "sgtatk", false, 70, 0, -1, -1, 0 }, + { "skepch", false, 70, 0, -1, -1, 0 }, + { "vilatk", false, 70, 0, -1, -1, 0 }, + { "claw", false, 70, 0, -1, -1, 0 }, + { "skeswg", false, 70, 0, -1, -1, 0 }, + { "pldeth", false, 32, 0, -1, -1, 0 }, + { "pdiehi", false, 32, 0, -1, -1, 0 }, + { "podth1", false, 70, 0, -1, -1, 0 }, + { "podth2", false, 70, 0, -1, -1, 0 }, + { "podth3", false, 70, 0, -1, -1, 0 }, + { "bgdth1", false, 70, 0, -1, -1, 0 }, + { "bgdth2", false, 70, 0, -1, -1, 0 }, + { "sgtdth", false, 70, 0, -1, -1, 0 }, + { "cacdth", false, 70, 0, -1, -1, 0 }, + { "skldth", false, 70, 0, -1, -1, 0 }, + { "brsdth", false, 32, 0, -1, -1, 0 }, + { "cybdth", false, 32, 0, -1, -1, 0 }, + { "spidth", false, 32, 0, -1, -1, 0 }, + { "bspdth", false, 32, 0, -1, -1, 0 }, + { "vildth", false, 32, 0, -1, -1, 0 }, + { "kntdth", false, 32, 0, -1, -1, 0 }, + { "pedth", false, 32, 0, -1, -1, 0 }, + { "skedth", false, 32, 0, -1, -1, 0 }, + { "posact", true, 120, 0, -1, -1, 0 }, + { "bgact", true, 120, 0, -1, -1, 0 }, + { "dmact", true, 120, 0, -1, -1, 0 }, + { "bspact", true, 100, 0, -1, -1, 0 }, + { "bspwlk", true, 100, 0, -1, -1, 0 }, + { "vilact", true, 100, 0, -1, -1, 0 }, + { "noway", false, 78, 0, -1, -1, 0 }, + { "barexp", false, 60, 0, -1, -1, 0 }, + { "punch", false, 64, 0, -1, -1, 0 }, + { "hoof", false, 70, 0, -1, -1, 0 }, + { "metal", false, 70, 0, -1, -1, 0 }, + { "chgun", false, 64, &S_sfx[sfx_pistol], 150, 0, 0 }, + { "tink", false, 60, 0, -1, -1, 0 }, + { "bdopn", false, 100, 0, -1, -1, 0 }, + { "bdcls", false, 100, 0, -1, -1, 0 }, + { "itmbk", false, 100, 0, -1, -1, 0 }, + { "flame", false, 32, 0, -1, -1, 0 }, + { "flamst", false, 32, 0, -1, -1, 0 }, + { "getpow", false, 60, 0, -1, -1, 0 }, + { "bospit", false, 70, 0, -1, -1, 0 }, + { "boscub", false, 70, 0, -1, -1, 0 }, + { "bossit", false, 70, 0, -1, -1, 0 }, + { "bospn", false, 70, 0, -1, -1, 0 }, + { "bosdth", false, 70, 0, -1, -1, 0 }, + { "manatk", false, 70, 0, -1, -1, 0 }, + { "mandth", false, 70, 0, -1, -1, 0 }, + { "sssit", false, 70, 0, -1, -1, 0 }, + { "ssdth", false, 70, 0, -1, -1, 0 }, + { "keenpn", false, 70, 0, -1, -1, 0 }, + { "keendt", false, 70, 0, -1, -1, 0 }, + { "skeact", false, 70, 0, -1, -1, 0 }, + { "skesit", false, 70, 0, -1, -1, 0 }, + { "skeatk", false, 70, 0, -1, -1, 0 }, + { "radio", false, 60, 0, -1, -1, 0 }, + +}; diff --git a/src/sounds.h b/src/sounds.h new file mode 100644 index 00000000..99b4ca41 --- /dev/null +++ b/src/sounds.h @@ -0,0 +1,296 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Created by the sound utility written by Dave Taylor. + * Kept as a sample, DOOM2 sounds. Frozen. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __SOUNDS__ +#define __SOUNDS__ + +// +// SoundFX struct. +// + +struct sfxinfo_struct; + +typedef struct sfxinfo_struct sfxinfo_t; + +struct sfxinfo_struct { + + // up to 6-character name + const char *name; // CPhipps - const + + // Sfx singularity (only one at a time) + int singularity; + + // Sfx priority + int priority; + + // referenced sound if a link + sfxinfo_t *link; + + // pitch if a link + int pitch; + + // volume if a link + int volume; + + // sound data + void *data; + + // this is checked every second to see if sound + // can be thrown out (if 0, then decrement, if -1, + // then throw out, if > 0, then it is in use) + int usefulness; + + // lump number of sfx + int lumpnum; +}; + +// +// MusicInfo struct. +// + +typedef struct { + // up to 6-character name + const char *name; // CPhipps - const + + // lump number of music + int lumpnum; + + /* music data - cphipps 4/11 made const void* */ + const void *data; + + // music handle once registered + int handle; +} musicinfo_t; + +// the complete set of sound effects +extern sfxinfo_t S_sfx[]; + +// the complete set of music +extern musicinfo_t S_music[]; + +// +// Identifiers for all music in game. +// + +typedef enum { + mus_None, + mus_e1m1, + mus_e1m2, + mus_e1m3, + mus_e1m4, + mus_e1m5, + mus_e1m6, + mus_e1m7, + mus_e1m8, + mus_e1m9, + mus_e2m1, + mus_e2m2, + mus_e2m3, + mus_e2m4, + mus_e2m5, + mus_e2m6, + mus_e2m7, + mus_e2m8, + mus_e2m9, + mus_e3m1, + mus_e3m2, + mus_e3m3, + mus_e3m4, + mus_e3m5, + mus_e3m6, + mus_e3m7, + mus_e3m8, + mus_e3m9, + mus_inter, + mus_intro, + mus_bunny, + mus_victor, + mus_introa, + mus_runnin, + mus_stalks, + mus_countd, + mus_betwee, + mus_doom, + mus_the_da, + mus_shawn, + mus_ddtblu, + mus_in_cit, + mus_dead, + mus_stlks2, + mus_theda2, + mus_doom2, + mus_ddtbl2, + mus_runni2, + mus_dead2, + mus_stlks3, + mus_romero, + mus_shawn2, + mus_messag, + mus_count2, + mus_ddtbl3, + mus_ampie, + mus_theda3, + mus_adrian, + mus_messg2, + mus_romer2, + mus_tense, + mus_shawn3, + mus_openin, + mus_evil, + mus_ultima, + mus_read_m, + mus_dm2ttl, + mus_dm2int, + NUMMUSIC +} musicenum_t; + +// +// Identifiers for all sfx in game. +// + +typedef enum { + sfx_None, + sfx_pistol, + sfx_shotgn, + sfx_sgcock, + sfx_dshtgn, + sfx_dbopn, + sfx_dbcls, + sfx_dbload, + sfx_plasma, + sfx_bfg, + sfx_sawup, + sfx_sawidl, + sfx_sawful, + sfx_sawhit, + sfx_rlaunc, + sfx_rxplod, + sfx_firsht, + sfx_firxpl, + sfx_pstart, + sfx_pstop, + sfx_doropn, + sfx_dorcls, + sfx_stnmov, + sfx_swtchn, + sfx_swtchx, + sfx_plpain, + sfx_dmpain, + sfx_popain, + sfx_vipain, + sfx_mnpain, + sfx_pepain, + sfx_slop, + sfx_itemup, + sfx_wpnup, + sfx_oof, + sfx_telept, + sfx_posit1, + sfx_posit2, + sfx_posit3, + sfx_bgsit1, + sfx_bgsit2, + sfx_sgtsit, + sfx_cacsit, + sfx_brssit, + sfx_cybsit, + sfx_spisit, + sfx_bspsit, + sfx_kntsit, + sfx_vilsit, + sfx_mansit, + sfx_pesit, + sfx_sklatk, + sfx_sgtatk, + sfx_skepch, + sfx_vilatk, + sfx_claw, + sfx_skeswg, + sfx_pldeth, + sfx_pdiehi, + sfx_podth1, + sfx_podth2, + sfx_podth3, + sfx_bgdth1, + sfx_bgdth2, + sfx_sgtdth, + sfx_cacdth, + sfx_skldth, + sfx_brsdth, + sfx_cybdth, + sfx_spidth, + sfx_bspdth, + sfx_vildth, + sfx_kntdth, + sfx_pedth, + sfx_skedth, + sfx_posact, + sfx_bgact, + sfx_dmact, + sfx_bspact, + sfx_bspwlk, + sfx_vilact, + sfx_noway, + sfx_barexp, + sfx_punch, + sfx_hoof, + sfx_metal, + sfx_chgun, + sfx_tink, + sfx_bdopn, + sfx_bdcls, + sfx_itmbk, + sfx_flame, + sfx_flamst, + sfx_getpow, + sfx_bospit, + sfx_boscub, + sfx_bossit, + sfx_bospn, + sfx_bosdth, + sfx_manatk, + sfx_mandth, + sfx_sssit, + sfx_ssdth, + sfx_keenpn, + sfx_keendt, + sfx_skeact, + sfx_skesit, + sfx_skeatk, + sfx_radio, + + NUMSFX +} sfxenum_t; + +#endif diff --git a/src/st_lib.c b/src/st_lib.c new file mode 100644 index 00000000..b0afd95c --- /dev/null +++ b/src/st_lib.c @@ -0,0 +1,359 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The status bar widget code. + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "v_video.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "st_lib.h" +#include "r_main.h" +#include "lprintf.h" + +int sts_always_red; //jff 2/18/98 control to disable status color changes +int sts_pct_always_gray; // killough 2/21/98: always gray %'s? bug or feature? + +// +// STlib_init() +// +void STlib_init(void) +{ + // cph - no longer hold STMINUS pointer +} + +// +// STlib_initNum() +// +// Initializes an st_number_t widget +// +// Passed the widget, its position, the patches for the digits, a pointer +// to the value displayed, a pointer to the on/off control, and the width +// Returns nothing +// +void STlib_initNum +( st_number_t* n, + int x, + int y, + const patchnum_t* pl, + int* num, + boolean* on, + int width ) +{ + n->x = x; + n->y = y; + n->oldnum = 0; + n->width = width; + n->num = num; + n->on = on; + n->p = pl; +} + +/* + * STlib_drawNum() + * + * A fairly efficient way to draw a number based on differences from the + * old number. + * + * Passed a st_number_t widget, a color range for output, and a flag + * indicating whether refresh is needed. + * Returns nothing + * + * jff 2/16/98 add color translation to digit output + * cphipps 10/99 - const pointer to colour trans table, made function static + */ +static void STlib_drawNum +( st_number_t* n, + int cm, + boolean refresh ) +{ + + int numdigits = n->width; + int num = *n->num; + + int w = n->p[0].width; + int h = n->p[0].height; + int x = n->x; + + int neg; + + // leban 1/20/99: + // strange that somebody went through all the work to draw only the + // differences, and then went and constantly redrew all the numbers. + // return without drawing if the number didn't change and the bar + // isn't refreshing. + if(n->oldnum == num && !refresh) + return; + + // CPhipps - compact some code, use num instead of *n->num + if ((neg = (n->oldnum = num) < 0)) + { + if (numdigits == 2 && num < -9) + num = -9; + else if (numdigits == 3 && num < -99) + num = -99; + + num = -num; + } + + // clear the area + x = n->x - numdigits*w; + + V_CopyRect(x, n->y - ST_Y, BG, w*numdigits, h, x, n->y, FG, VPT_NONE); + + // if non-number, do not draw it + if (num == 1994) + return; + + x = n->x; + + //jff 2/16/98 add color translation to digit output + // in the special case of 0, you draw 0 + if (!num) + // CPhipps - patch drawing updated, reformatted + V_DrawNumPatch(x - w, n->y, FG, n->p[0].lumpnum, cm, + (((cm!=CR_DEFAULT) && !sts_always_red) ? VPT_TRANS : VPT_NONE)); + + // draw the new number + //jff 2/16/98 add color translation to digit output + while (num && numdigits--) { + // CPhipps - patch drawing updated, reformatted + x -= w; + V_DrawNumPatch(x, n->y, FG, n->p[num % 10].lumpnum, cm, + (((cm!=CR_DEFAULT) && !sts_always_red) ? VPT_TRANS : VPT_NONE)); + num /= 10; + } + + // draw a minus sign if necessary + //jff 2/16/98 add color translation to digit output + // cph - patch drawing updated, load by name instead of acquiring pointer earlier + if (neg) + V_DrawNamePatch(x - w, n->y, FG, "STTMINUS", cm, + (((cm!=CR_DEFAULT) && !sts_always_red) ? VPT_TRANS : VPT_NONE)); +} + +/* + * STlib_updateNum() + * + * Draws a number conditionally based on the widget's enable + * + * Passed a number widget, the output color range, and a refresh flag + * Returns nothing + * + * jff 2/16/98 add color translation to digit output + * cphipps 10/99 - make that pointer const + */ +void STlib_updateNum +( st_number_t* n, + int cm, + boolean refresh ) +{ + if (*n->on) STlib_drawNum(n, cm, refresh); +} + +// +// STlib_initPercent() +// +// Initialize a st_percent_t number with percent sign widget +// +// Passed a st_percent_t widget, the position, the digit patches, a pointer +// to the number to display, a pointer to the enable flag, and patch +// for the percent sign. +// Returns nothing. +// +void STlib_initPercent +( st_percent_t* p, + int x, + int y, + const patchnum_t* pl, + int* num, + boolean* on, + const patchnum_t* percent ) +{ + STlib_initNum(&p->n, x, y, pl, num, on, 3); + p->p = percent; +} + +/* + * STlib_updatePercent() + * + * Draws a number/percent conditionally based on the widget's enable + * + * Passed a precent widget, the output color range, and a refresh flag + * Returns nothing + * + * jff 2/16/98 add color translation to digit output + * cphipps - const for pointer to the colour translation table + */ + +void STlib_updatePercent +( st_percent_t* per, + int cm, + int refresh ) +{ + if (*per->n.on && (refresh || (per->n.oldnum != *per->n.num))) { + // killough 2/21/98: fix percents not updated; + /* CPhipps - make %'s only be updated if number changed */ + // CPhipps - patch drawing updated + V_DrawNumPatch(per->n.x, per->n.y, FG, per->p->lumpnum, + sts_pct_always_gray ? CR_GRAY : cm, + (sts_always_red ? VPT_NONE : VPT_TRANS)); + } + + STlib_updateNum(&per->n, cm, refresh); +} + +// +// STlib_initMultIcon() +// +// Initialize a st_multicon_t widget, used for a multigraphic display +// like the status bar's keys. +// +// Passed a st_multicon_t widget, the position, the graphic patches, a pointer +// to the numbers representing what to display, and pointer to the enable flag +// Returns nothing. +// +void STlib_initMultIcon +( st_multicon_t* i, + int x, + int y, + const patchnum_t* il, + int* inum, + boolean* on ) +{ + i->x = x; + i->y = y; + i->oldinum = -1; + i->inum = inum; + i->on = on; + i->p = il; +} + +// +// STlib_updateMultIcon() +// +// Draw a st_multicon_t widget, used for a multigraphic display +// like the status bar's keys. Displays each when the control +// numbers change or refresh is true +// +// Passed a st_multicon_t widget, and a refresh flag +// Returns nothing. +// +void STlib_updateMultIcon +( st_multicon_t* mi, + boolean refresh ) +{ + int w; + int h; + int x; + int y; + + if (*mi->on && (mi->oldinum != *mi->inum || refresh)) + { + if (mi->oldinum != -1) + { + x = mi->x - mi->p[mi->oldinum].leftoffset; + y = mi->y - mi->p[mi->oldinum].topoffset; + w = mi->p[mi->oldinum].width; + h = mi->p[mi->oldinum].height; + + V_CopyRect(x, y-ST_Y, BG, w, h, x, y, FG, VPT_NONE); + } + if (*mi->inum != -1) // killough 2/16/98: redraw only if != -1 + V_DrawNumPatch(mi->x, mi->y, FG, mi->p[*mi->inum].lumpnum, CR_DEFAULT, VPT_NONE); + mi->oldinum = *mi->inum; + } +} + +// +// STlib_initBinIcon() +// +// Initialize a st_binicon_t widget, used for a multinumber display +// like the status bar's weapons, that are present or not. +// +// Passed a st_binicon_t widget, the position, the digit patches, a pointer +// to the flags representing what is displayed, and pointer to the enable flag +// Returns nothing. +// +void STlib_initBinIcon +( st_binicon_t* b, + int x, + int y, + const patchnum_t* i, + boolean* val, + boolean* on ) +{ + b->x = x; + b->y = y; + b->oldval = 0; + b->val = val; + b->on = on; + b->p = i; +} + +// +// STlib_updateBinIcon() +// +// DInitialize a st_binicon_t widget, used for a multinumber display +// like the status bar's weapons, that are present or not. +// +// Draw a st_binicon_t widget, used for a multinumber display +// like the status bar's weapons that are present or not. Displays each +// when the control flag changes or refresh is true +// +// Passed a st_binicon_t widget, and a refresh flag +// Returns nothing. +// +void STlib_updateBinIcon +( st_binicon_t* bi, + boolean refresh ) +{ + int x; + int y; + int w; + int h; + + if (*bi->on && (bi->oldval != *bi->val || refresh)) + { + x = bi->x - bi->p->leftoffset; + y = bi->y - bi->p->topoffset; + w = bi->p->width; + h = bi->p->height; + + if (*bi->val) + V_DrawNumPatch(bi->x, bi->y, FG, bi->p->lumpnum, CR_DEFAULT, VPT_NONE); + else + V_CopyRect(x, y-ST_Y, BG, w, h, x, y, FG, VPT_NONE); + + bi->oldval = *bi->val; + } +} diff --git a/src/st_lib.h b/src/st_lib.h new file mode 100644 index 00000000..769a75ed --- /dev/null +++ b/src/st_lib.h @@ -0,0 +1,209 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * The status bar widget definitions and prototypes + * + *-----------------------------------------------------------------------------*/ + +#ifndef __STLIB__ +#define __STLIB__ + +// We are referring to patches. +#include "r_defs.h" +#include "v_video.h" // color ranges + +// +// Background and foreground screen numbers +// +#define BG 4 +#define FG 0 + +// +// Typedefs of widgets +// + +// Number widget + +typedef struct +{ + // upper right-hand corner + // of the number (right-justified) + int x; + int y; + + // max # of digits in number + int width; + + // last number value + int oldnum; + + // pointer to current value + int* num; + + // pointer to boolean stating + // whether to update number + boolean* on; + + // list of patches for 0-9 + const patchnum_t* p; + + // user data + int data; +} st_number_t; + +// Percent widget ("child" of number widget, +// or, more precisely, contains a number widget.) +typedef struct +{ + // number information + st_number_t n; + + // percent sign graphic + const patchnum_t* p; +} st_percent_t; + +// Multiple Icon widget +typedef struct +{ + // center-justified location of icons + int x; + int y; + + // last icon number + int oldinum; + + // pointer to current icon + int* inum; + + // pointer to boolean stating + // whether to update icon + boolean* on; + + // list of icons + const patchnum_t* p; + + // user data + int data; + +} st_multicon_t; + +// Binary Icon widget + +typedef struct +{ + // center-justified location of icon + int x; + int y; + + // last icon value + boolean oldval; + + // pointer to current icon status + boolean* val; + + // pointer to boolean + // stating whether to update icon + boolean* on; + + const patchnum_t* p; // icon + int data; // user data +} st_binicon_t; + +// +// Widget creation, access, and update routines +// + +// Initializes widget library. +// More precisely, initialize STMINUS, +// everything else is done somewhere else. +// +void STlib_init(void); + +// Number widget routines +void STlib_initNum +( st_number_t* n, + int x, + int y, + const patchnum_t* pl, + int* num, + boolean* on, + int width ); + +void STlib_updateNum +( st_number_t* n, + int cm, + boolean refresh ); + + +// Percent widget routines +void STlib_initPercent +( st_percent_t* p, + int x, + int y, + const patchnum_t* pl, + int* num, + boolean* on, + const patchnum_t* percent ); + + +void STlib_updatePercent +( st_percent_t* per, + int cm, + int refresh ); + + +// Multiple Icon widget routines +void STlib_initMultIcon +( st_multicon_t* mi, + int x, + int y, + const patchnum_t* il, + int* inum, + boolean* on ); + + +void STlib_updateMultIcon +( st_multicon_t* mi, + boolean refresh ); + +// Binary Icon widget routines + +void STlib_initBinIcon +( st_binicon_t* b, + int x, + int y, + const patchnum_t* i, + boolean* val, + boolean* on ); + +void STlib_updateBinIcon +( st_binicon_t* bi, + boolean refresh ); + +#endif diff --git a/src/st_stuff.c b/src/st_stuff.c new file mode 100644 index 00000000..798f070a --- /dev/null +++ b/src/st_stuff.c @@ -0,0 +1,1098 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animatin. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + *-----------------------------------------------------------------------------*/ + +#include "doomdef.h" +#include "doomstat.h" +#include "m_random.h" +#include "i_video.h" +#include "w_wad.h" +#include "st_stuff.h" +#include "st_lib.h" +#include "r_main.h" +#include "am_map.h" +#include "m_cheat.h" +#include "s_sound.h" +#include "sounds.h" +#include "dstrings.h" +#include "r_draw.h" + +// +// STATUS BAR DATA +// + +// Palette indices. +// For damage/bonus red-/gold-shifts +#define STARTREDPALS 1 +#define STARTBONUSPALS 9 +#define NUMREDPALS 8 +#define NUMBONUSPALS 4 +// Radiation suit, green shift. +#define RADIATIONPAL 13 + +// Location of status bar +#define ST_X 0 +#define ST_X2 104 + +#define ST_FX 143 +#define ST_FY 169 + +// Should be set to patch width +// for tall numbers later on +#define ST_TALLNUMWIDTH (tallnum[0]->width) + +// Number of status faces. +#define ST_NUMPAINFACES 5 +#define ST_NUMSTRAIGHTFACES 3 +#define ST_NUMTURNFACES 2 +#define ST_NUMSPECIALFACES 3 + +#define ST_FACESTRIDE \ + (ST_NUMSTRAIGHTFACES+ST_NUMTURNFACES+ST_NUMSPECIALFACES) + +#define ST_NUMEXTRAFACES 2 + +#define ST_NUMFACES \ + (ST_FACESTRIDE*ST_NUMPAINFACES+ST_NUMEXTRAFACES) + +#define ST_TURNOFFSET (ST_NUMSTRAIGHTFACES) +#define ST_OUCHOFFSET (ST_TURNOFFSET + ST_NUMTURNFACES) +#define ST_EVILGRINOFFSET (ST_OUCHOFFSET + 1) +#define ST_RAMPAGEOFFSET (ST_EVILGRINOFFSET + 1) +#define ST_GODFACE (ST_NUMPAINFACES*ST_FACESTRIDE) +#define ST_DEADFACE (ST_GODFACE+1) + +#define ST_FACESX 143 +#define ST_FACESY 168 + +#define ST_EVILGRINCOUNT (2*TICRATE) +#define ST_STRAIGHTFACECOUNT (TICRATE/2) +#define ST_TURNCOUNT (1*TICRATE) +#define ST_OUCHCOUNT (1*TICRATE) +#define ST_RAMPAGEDELAY (2*TICRATE) + +#define ST_MUCHPAIN 20 + +// Location and size of statistics, +// justified according to widget type. +// Problem is, within which space? STbar? Screen? +// Note: this could be read in by a lump. +// Problem is, is the stuff rendered +// into a buffer, +// or into the frame buffer? +// I dunno, why don't you go and find out!!! killough + +// AMMO number pos. +#define ST_AMMOWIDTH 3 +#define ST_AMMOX 44 +#define ST_AMMOY 171 + +// HEALTH number pos. +#define ST_HEALTHWIDTH 3 +#define ST_HEALTHX 90 +#define ST_HEALTHY 171 + +// Weapon pos. +#define ST_ARMSX 111 +#define ST_ARMSY 172 +#define ST_ARMSBGX 104 +#define ST_ARMSBGY 168 +#define ST_ARMSXSPACE 12 +#define ST_ARMSYSPACE 10 + +// Frags pos. +#define ST_FRAGSX 138 +#define ST_FRAGSY 171 +#define ST_FRAGSWIDTH 2 + +// ARMOR number pos. +#define ST_ARMORWIDTH 3 +#define ST_ARMORX 221 +#define ST_ARMORY 171 + +// Key icon positions. +#define ST_KEY0WIDTH 8 +#define ST_KEY0HEIGHT 5 +#define ST_KEY0X 239 +#define ST_KEY0Y 171 +#define ST_KEY1WIDTH ST_KEY0WIDTH +#define ST_KEY1X 239 +#define ST_KEY1Y 181 +#define ST_KEY2WIDTH ST_KEY0WIDTH +#define ST_KEY2X 239 +#define ST_KEY2Y 191 + +// Ammunition counter. +#define ST_AMMO0WIDTH 3 +#define ST_AMMO0HEIGHT 6 +#define ST_AMMO0X 288 +#define ST_AMMO0Y 173 +#define ST_AMMO1WIDTH ST_AMMO0WIDTH +#define ST_AMMO1X 288 +#define ST_AMMO1Y 179 +#define ST_AMMO2WIDTH ST_AMMO0WIDTH +#define ST_AMMO2X 288 +#define ST_AMMO2Y 191 +#define ST_AMMO3WIDTH ST_AMMO0WIDTH +#define ST_AMMO3X 288 +#define ST_AMMO3Y 185 + +// Indicate maximum ammunition. +// Only needed because backpack exists. +#define ST_MAXAMMO0WIDTH 3 +#define ST_MAXAMMO0HEIGHT 5 +#define ST_MAXAMMO0X 314 +#define ST_MAXAMMO0Y 173 +#define ST_MAXAMMO1WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO1X 314 +#define ST_MAXAMMO1Y 179 +#define ST_MAXAMMO2WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO2X 314 +#define ST_MAXAMMO2Y 191 +#define ST_MAXAMMO3WIDTH ST_MAXAMMO0WIDTH +#define ST_MAXAMMO3X 314 +#define ST_MAXAMMO3Y 185 + +// killough 2/8/98: weapon info position macros UNUSED, removed here + +// main player in game +static player_t *plyr; + +// ST_Start() has just been called +static boolean st_firsttime; + +// used to execute ST_Init() only once +static int veryfirsttime = 1; + +// CPhipps - no longer do direct PLAYPAL handling here + +// used for timing +static unsigned int st_clock; + +// used for making messages go away +static int st_msgcounter=0; + +// used when in chat +static st_chatstateenum_t st_chatstate; + +// whether in automap or first-person +static st_stateenum_t st_gamestate; + +// whether left-side main status bar is active +static boolean st_statusbaron; + +// whether status bar chat is active +static boolean st_chat; + +// value of st_chat before message popped up +static boolean st_oldchat; + +// whether chat window has the cursor on +static boolean st_cursoron; + +// !deathmatch +static boolean st_notdeathmatch; + +// !deathmatch && st_statusbaron +static boolean st_armson; + +// !deathmatch +static boolean st_fragson; + +// 0-9, tall numbers +static patchnum_t tallnum[10]; + +// tall % sign +static patchnum_t tallpercent; + +// 0-9, short, yellow (,different!) numbers +static patchnum_t shortnum[10]; + +// 3 key-cards, 3 skulls, 3 card/skull combos +// jff 2/24/98 extend number of patches by three skull/card combos +static patchnum_t keys[NUMCARDS+3]; + +// face status patches +static patchnum_t faces[ST_NUMFACES]; + +// face background +static patchnum_t faceback; // CPhipps - single background, translated for different players + +//e6y: status bar background +static patchnum_t stbarbg; + +// main bar right +static patchnum_t armsbg; + +// weapon ownership patches +static patchnum_t arms[6][2]; + +// ready-weapon widget +static st_number_t w_ready; + +//jff 2/16/98 status color change levels +int ammo_red; // ammo percent less than which status is red +int ammo_yellow; // ammo percent less is yellow more green +int health_red; // health amount less than which status is red +int health_yellow; // health amount less than which status is yellow +int health_green; // health amount above is blue, below is green +int armor_red; // armor amount less than which status is red +int armor_yellow; // armor amount less than which status is yellow +int armor_green; // armor amount above is blue, below is green + + // in deathmatch only, summary of frags stats +static st_number_t w_frags; + +// health widget +static st_percent_t w_health; + +// arms background +static st_binicon_t w_armsbg; + +// weapon ownership widgets +static st_multicon_t w_arms[6]; + +// face status widget +static st_multicon_t w_faces; + +// keycard widgets +static st_multicon_t w_keyboxes[3]; + +// armor widget +static st_percent_t w_armor; + +// ammo widgets +static st_number_t w_ammo[4]; + +// max ammo widgets +static st_number_t w_maxammo[4]; + + // number of frags so far in deathmatch +static int st_fragscount; + +// used to use appopriately pained face +static int st_oldhealth = -1; + +// used for evil grin +static boolean oldweaponsowned[NUMWEAPONS]; + + // count until face changes +static int st_facecount = 0; + +// current face index, used by w_faces +static int st_faceindex = 0; + +// holds key-type for each key box on bar +static int keyboxes[3]; + +// a random number per tick +static int st_randomnumber; + +extern char *mapnames[]; + +// +// STATUS BAR CODE +// + +static void ST_Stop(void); + +static void ST_refreshBackground(void) +{ + int y=0; + + if (st_statusbaron) + { + // proff 05/17/2000: draw to the frontbuffer in OpenGL + V_DrawNumPatch(ST_X, y, BG, stbarbg.lumpnum, CR_DEFAULT, VPT_NONE); + if (st_armson) + V_DrawNumPatch(ST_ARMSBGX, y, BG, armsbg.lumpnum, CR_DEFAULT, VPT_NONE); + + // killough 3/7/98: make face background change with displayplayer + if (netgame) + { + V_DrawNumPatch(ST_FX, y, BG, faceback.lumpnum, + displayplayer ? CR_LIMIT+displayplayer : CR_DEFAULT, + displayplayer ? (VPT_TRANS) : VPT_NONE); + } + V_CopyRect(ST_X, y, BG, ST_SCALED_WIDTH, ST_SCALED_HEIGHT, ST_X, ST_SCALED_Y, FG, VPT_NONE); + } +} + + +// Respond to keyboard input events, +// intercept cheats. +boolean ST_Responder(event_t *ev) +{ + // Filter automap on/off. + if (ev->type == ev_keyup && (ev->data1 & 0xffff0000) == AM_MSGHEADER) + { + switch(ev->data1) + { + case AM_MSGENTERED: + st_gamestate = AutomapState; + st_firsttime = true; + break; + + case AM_MSGEXITED: + st_gamestate = FirstPersonState; + break; + } + } + else // if a user keypress... + if (ev->type == ev_keydown) // Try cheat responder in m_cheat.c + return M_FindCheats(ev->data1); // killough 4/17/98, 5/2/98 + return false; +} + +static int ST_calcPainOffset(void) +{ + static int lastcalc; + static int oldhealth = -1; + int health = plyr->health > 100 ? 100 : plyr->health; + + if (health != oldhealth) + { + lastcalc = ST_FACESTRIDE * (((100 - health) * ST_NUMPAINFACES) / 101); + oldhealth = health; + } + return lastcalc; +} + +// +// This is a not-very-pretty routine which handles +// the face states and their timing. +// the precedence of expressions is: +// dead > evil grin > turned head > straight ahead +// + +static void ST_updateFaceWidget(void) +{ + int i; + angle_t badguyangle; + angle_t diffang; + static int lastattackdown = -1; + static int priority = 0; + boolean doevilgrin; + + if (priority < 10) + { + // dead + if (!plyr->health) + { + priority = 9; + st_faceindex = ST_DEADFACE; + st_facecount = 1; + } + } + + if (priority < 9) + { + if (plyr->bonuscount) + { + // picking up bonus + doevilgrin = false; + + for (i=0;iweaponowned[i]) + { + doevilgrin = true; + oldweaponsowned[i] = plyr->weaponowned[i]; + } + } + if (doevilgrin) + { + // evil grin if just picked up weapon + priority = 8; + st_facecount = ST_EVILGRINCOUNT; + st_faceindex = ST_calcPainOffset() + ST_EVILGRINOFFSET; + } + } + + } + + if (priority < 8) + { + if (plyr->damagecount && plyr->attacker && plyr->attacker != plyr->mo) + { + // being attacked + priority = 7; + + // haleyjd 10/12/03: classic DOOM problem of missing OUCH face + // was due to inversion of this test: + // if(plyr->health - st_oldhealth > ST_MUCHPAIN) + if(st_oldhealth - plyr->health > ST_MUCHPAIN) + { + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + badguyangle = R_PointToAngle2(plyr->mo->x, + plyr->mo->y, + plyr->attacker->x, + plyr->attacker->y); + + if (badguyangle > plyr->mo->angle) + { + // whether right or left + diffang = badguyangle - plyr->mo->angle; + i = diffang > ANG180; + } + else + { + // whether left or right + diffang = plyr->mo->angle - badguyangle; + i = diffang <= ANG180; + } // confusing, aint it? + + + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset(); + + if (diffang < ANG45) + { + // head-on + st_faceindex += ST_RAMPAGEOFFSET; + } + else if (i) + { + // turn face right + st_faceindex += ST_TURNOFFSET; + } + else + { + // turn face left + st_faceindex += ST_TURNOFFSET+1; + } + } + } + } + + if (priority < 7) + { + // getting hurt because of your own damn stupidity + if (plyr->damagecount) + { + // haleyjd 10/12/03: classic DOOM problem of missing OUCH face + // was due to inversion of this test: + // if(plyr->health - st_oldhealth > ST_MUCHPAIN) + if(st_oldhealth - plyr->health > ST_MUCHPAIN) + { + priority = 7; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_OUCHOFFSET; + } + else + { + priority = 6; + st_facecount = ST_TURNCOUNT; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + } + + } + + } + + if (priority < 6) + { + // rapid firing + if (plyr->attackdown) + { + if (lastattackdown==-1) + lastattackdown = ST_RAMPAGEDELAY; + else if (!--lastattackdown) + { + priority = 5; + st_faceindex = ST_calcPainOffset() + ST_RAMPAGEOFFSET; + st_facecount = 1; + lastattackdown = 1; + } + } + else + lastattackdown = -1; + + } + + if (priority < 5) + { + // invulnerability + if ((plyr->cheats & CF_GODMODE) + || plyr->powers[pw_invulnerability]) + { + priority = 4; + + st_faceindex = ST_GODFACE; + st_facecount = 1; + + } + + } + + // look left or look right if the facecount has timed out + if (!st_facecount) + { + st_faceindex = ST_calcPainOffset() + (st_randomnumber % 3); + st_facecount = ST_STRAIGHTFACECOUNT; + priority = 0; + } + + st_facecount--; + +} + +int sts_traditional_keys; // killough 2/28/98: traditional status bar keys + +static void ST_updateWidgets(void) +{ + static int largeammo = 1994; // means "n/a" + int i; + + // must redirect the pointer if the ready weapon has changed. + // if (w_ready.data != plyr->readyweapon) + // { + if (weaponinfo[plyr->readyweapon].ammo == am_noammo) + w_ready.num = &largeammo; + else + w_ready.num = &plyr->ammo[weaponinfo[plyr->readyweapon].ammo]; + //{ + // static int tic=0; + // static int dir=-1; + // if (!(tic&15)) + // plyr->ammo[weaponinfo[plyr->readyweapon].ammo]+=dir; + // if (plyr->ammo[weaponinfo[plyr->readyweapon].ammo] == -100) + // dir = 1; + // tic++; + // } + w_ready.data = plyr->readyweapon; + + // if (*w_ready.on) + // STlib_updateNum(&w_ready, true); + // refresh weapon change + // } + + // update keycard multiple widgets + for (i=0;i<3;i++) + { + keyboxes[i] = plyr->cards[i] ? i : -1; + + //jff 2/24/98 select double key + //killough 2/28/98: preserve traditional keys by config option + + if (plyr->cards[i+3]) + keyboxes[i] = keyboxes[i]==-1 || sts_traditional_keys ? i+3 : i+6; + } + + // refresh everything if this is him coming back to life + ST_updateFaceWidget(); + + // used by the w_armsbg widget + st_notdeathmatch = !deathmatch; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + st_fragscount = 0; + + for (i=0 ; ifrags[i]; + else + st_fragscount -= plyr->frags[i]; + } + + // get rid of chat window if up because of message + if (!--st_msgcounter) + st_chat = st_oldchat; + +} + +void ST_Ticker(void) +{ + st_clock++; + st_randomnumber = M_Random(); + ST_updateWidgets(); + st_oldhealth = plyr->health; +} + +int st_palette = 0; + +static void ST_doPaletteStuff(void) +{ + int palette; + int cnt = plyr->damagecount; + + if (plyr->powers[pw_strength]) + { + // slowly fade the berzerk out + int bzc = 12 - (plyr->powers[pw_strength]>>6); + if (bzc > cnt) + cnt = bzc; + } + + if (cnt) + { + palette = (cnt+7)>>3; + if (palette >= NUMREDPALS) + palette = NUMREDPALS-1; + + /* cph 2006/08/06 - if in the menu, reduce the red tint - navigating to + * load a game can be tricky if the screen is all red */ + if (menuactive) palette >>=1; + + palette += STARTREDPALS; + } + else + if (plyr->bonuscount) + { + palette = (plyr->bonuscount+7)>>3; + if (palette >= NUMBONUSPALS) + palette = NUMBONUSPALS-1; + palette += STARTBONUSPALS; + } + else + if (plyr->powers[pw_ironfeet] > 4*32 || plyr->powers[pw_ironfeet] & 8) + palette = RADIATIONPAL; + else + palette = 0; + + if (palette != st_palette) { + V_SetPalette(st_palette = palette); // CPhipps - use new palette function + + // have to redraw the entire status bar when the palette changes + // in truecolor modes - POPE + st_firsttime = true; + } +} + +static void ST_drawWidgets(boolean refresh) +{ + int i, ammolevel; + + // used by w_arms[] widgets + st_armson = st_statusbaron && !deathmatch; + + // used by w_frags widget + st_fragson = deathmatch && st_statusbaron; + + //jff 2/16/98 make color of ammo depend on amount + //djsd 12/01/10 add empty, full. + ammolevel = P_GetAmmoLevel(plyr, w_ready.data); // handles BFG/SSG + if (ammolevel == 0) + STlib_updateNum(&w_ready, CR_GRAY, refresh); + else if (ammolevel >= 100) + STlib_updateNum(&w_ready, CR_BLUE2, refresh); + else if (ammolevel < ammo_red) + STlib_updateNum(&w_ready, CR_RED, refresh); + else if (ammolevel < ammo_yellow) + STlib_updateNum(&w_ready, CR_GOLD, refresh); + else + STlib_updateNum(&w_ready, CR_GREEN, refresh); + + for (i=0;i<4;i++) + { + STlib_updateNum(&w_ammo[i], CR_DEFAULT, refresh); //jff 2/16/98 no xlation + STlib_updateNum(&w_maxammo[i], CR_DEFAULT, refresh); + } + + //jff 2/16/98 make color of health depend on amount + if (*w_health.n.numweaponowned[i]; + + for (i=0;i<3;i++) + keyboxes[i] = -1; + + STlib_init(); +} + +static void ST_createWidgets(void) +{ + int i; + + // ready weapon ammo + STlib_initNum(&w_ready, + ST_AMMOX, + ST_AMMOY, + tallnum, + &plyr->ammo[weaponinfo[plyr->readyweapon].ammo], + &st_statusbaron, + ST_AMMOWIDTH ); + + // the last weapon type + w_ready.data = plyr->readyweapon; + + // health percentage + STlib_initPercent(&w_health, + ST_HEALTHX, + ST_HEALTHY, + tallnum, + &plyr->health, + &st_statusbaron, + &tallpercent); + + // arms background + STlib_initBinIcon(&w_armsbg, + ST_ARMSBGX, + ST_ARMSBGY, + &armsbg, + &st_notdeathmatch, + &st_statusbaron); + + // weapons owned + for(i=0;i<6;i++) + { + STlib_initMultIcon(&w_arms[i], + ST_ARMSX+(i%3)*ST_ARMSXSPACE, + ST_ARMSY+(i/3)*ST_ARMSYSPACE, + arms[i], (int *) &plyr->weaponowned[i+1], + &st_armson); + } + + // frags sum + STlib_initNum(&w_frags, + ST_FRAGSX, + ST_FRAGSY, + tallnum, + &st_fragscount, + &st_fragson, + ST_FRAGSWIDTH); + + // faces + STlib_initMultIcon(&w_faces, + ST_FACESX, + ST_FACESY, + faces, + &st_faceindex, + &st_statusbaron); + + // armor percentage - should be colored later + STlib_initPercent(&w_armor, + ST_ARMORX, + ST_ARMORY, + tallnum, + &plyr->armorpoints, + &st_statusbaron, &tallpercent); + + // keyboxes 0-2 + STlib_initMultIcon(&w_keyboxes[0], + ST_KEY0X, + ST_KEY0Y, + keys, + &keyboxes[0], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[1], + ST_KEY1X, + ST_KEY1Y, + keys, + &keyboxes[1], + &st_statusbaron); + + STlib_initMultIcon(&w_keyboxes[2], + ST_KEY2X, + ST_KEY2Y, + keys, + &keyboxes[2], + &st_statusbaron); + + // ammo count (all four kinds) + STlib_initNum(&w_ammo[0], + ST_AMMO0X, + ST_AMMO0Y, + shortnum, + &plyr->ammo[0], + &st_statusbaron, + ST_AMMO0WIDTH); + + STlib_initNum(&w_ammo[1], + ST_AMMO1X, + ST_AMMO1Y, + shortnum, + &plyr->ammo[1], + &st_statusbaron, + ST_AMMO1WIDTH); + + STlib_initNum(&w_ammo[2], + ST_AMMO2X, + ST_AMMO2Y, + shortnum, + &plyr->ammo[2], + &st_statusbaron, + ST_AMMO2WIDTH); + + STlib_initNum(&w_ammo[3], + ST_AMMO3X, + ST_AMMO3Y, + shortnum, + &plyr->ammo[3], + &st_statusbaron, + ST_AMMO3WIDTH); + + // max ammo count (all four kinds) + STlib_initNum(&w_maxammo[0], + ST_MAXAMMO0X, + ST_MAXAMMO0Y, + shortnum, + &plyr->maxammo[0], + &st_statusbaron, + ST_MAXAMMO0WIDTH); + + STlib_initNum(&w_maxammo[1], + ST_MAXAMMO1X, + ST_MAXAMMO1Y, + shortnum, + &plyr->maxammo[1], + &st_statusbaron, + ST_MAXAMMO1WIDTH); + + STlib_initNum(&w_maxammo[2], + ST_MAXAMMO2X, + ST_MAXAMMO2Y, + shortnum, + &plyr->maxammo[2], + &st_statusbaron, + ST_MAXAMMO2WIDTH); + + STlib_initNum(&w_maxammo[3], + ST_MAXAMMO3X, + ST_MAXAMMO3Y, + shortnum, + &plyr->maxammo[3], + &st_statusbaron, + ST_MAXAMMO3WIDTH); +} + +static boolean st_stopped = true; + +void ST_Start(void) +{ + if (!st_stopped) + ST_Stop(); + ST_initData(); + ST_createWidgets(); + st_stopped = false; +} + +static void ST_Stop(void) +{ + if (st_stopped) + return; + V_SetPalette(0); + st_stopped = true; +} + +void ST_Init(void) +{ + veryfirsttime = 0; + ST_loadData(); +} diff --git a/src/st_stuff.h b/src/st_stuff.h new file mode 100644 index 00000000..3d7e689a --- /dev/null +++ b/src/st_stuff.h @@ -0,0 +1,102 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Status bar code. + * Does the face/direction indicator animatin. + * Does palette indicators as well (red pain/berserk, bright pickup) + * + *-----------------------------------------------------------------------------*/ + +#ifndef __STSTUFF_H__ +#define __STSTUFF_H__ + +#include "doomtype.h" +#include "d_event.h" + +// Size of statusbar. +// Now sensitive for scaling. + +// proff 08/18/98: Changed for high-res +#define ST_HEIGHT 32 +#define ST_WIDTH 320 +#define ST_Y (200 - ST_HEIGHT) +#define ST_SCALED_HEIGHT (ST_HEIGHT*SCREENHEIGHT/200) +#define ST_SCALED_WIDTH SCREENWIDTH +#define ST_SCALED_Y (SCREENHEIGHT - ST_SCALED_HEIGHT) + +// +// STATUS BAR +// + +// Called by main loop. +boolean ST_Responder(event_t* ev); + +// Called by main loop. +void ST_Ticker(void); + +// Called by main loop. +void ST_Drawer(boolean st_statusbaron, boolean refresh, boolean fullmenu); + +// Called when the console player is spawned on each level. +void ST_Start(void); + +// Called by startup code. +void ST_Init(void); + +// States for status bar code. +typedef enum +{ + AutomapState, + FirstPersonState +} st_stateenum_t; + +// States for the chat code. +typedef enum +{ + StartChatState, + WaitDestState, + GetChatState +} st_chatstateenum_t; + +// killough 5/2/98: moved from m_misc.c: + +extern int health_red; // health amount less than which status is red +extern int health_yellow; // health amount less than which status is yellow +extern int health_green; // health amount above is blue, below is green +extern int armor_red; // armor amount less than which status is red +extern int armor_yellow; // armor amount less than which status is yellow +extern int armor_green; // armor amount above is blue, below is green +extern int ammo_red; // ammo percent less than which status is red +extern int ammo_yellow; // ammo percent less is yellow more green +extern int sts_always_red;// status numbers do not change colors +extern int sts_pct_always_gray;// status percents do not change colors +extern int sts_traditional_keys; // display keys the traditional way + +extern int st_palette; // cph 2006/04/06 - make palette visible +#endif diff --git a/src/tables.c b/src/tables.c new file mode 100644 index 00000000..2cf59e1d --- /dev/null +++ b/src/tables.c @@ -0,0 +1,128 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Lookup tables. + * Do not try to look them up :-). + * In the order of appearance: + * + * int finetangent[4096] - Tangens LUT. + * Should work with BAM fairly well (12 of 16bit, + * effectively, by shifting). + * + * int finesine[10240] - Sine lookup. + * Guess what, serves as cosine, too. + * Remarkable thing is, how to use BAMs with this? + * + * int tantoangle[2049] - ArcTan LUT, + * maps tan(angle) to angle fast. Gotta search. + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include "w_wad.h" +#include "tables.h" + +// killough 5/3/98: reformatted + +int SlopeDiv(unsigned num, unsigned den) +{ + unsigned ans; + + if (den < 512) + return SLOPERANGE; + ans = (num<<3)/(den>>8); + return ans <= SLOPERANGE ? ans : SLOPERANGE; +} + +fixed_t finetangent[4096]; + +//const fixed_t *const finecosine = &finesine[FINEANGLES/4]; + +fixed_t finesine[10240]; + +angle_t tantoangle[2049]; + +#include "m_swap.h" +#include "lprintf.h" + +// R_LoadTrigTables +// Load trig tables from a wad file lump +// CPhipps 24/12/98 - fix endianness (!) +// +void R_LoadTrigTables(void) +{ + int lump; + { + lump = (W_CheckNumForName)("SINETABL",ns_prboom); + if (lump == -1) I_Error("Failed to locate trig tables"); + if (W_LumpLength(lump) != sizeof(finesine)) + I_Error("R_LoadTrigTables: Invalid SINETABL"); + W_ReadLump(lump,(unsigned char*)finesine); + } + { + lump = (W_CheckNumForName)("TANGTABL",ns_prboom); + if (lump == -1) I_Error("Failed to locate trig tables"); + if (W_LumpLength(lump) != sizeof(finetangent)) + I_Error("R_LoadTrigTables: Invalid TANGTABL"); + W_ReadLump(lump,(unsigned char*)finetangent); + } + { + lump = (W_CheckNumForName)("TANTOANG",ns_prboom); + if (lump == -1) I_Error("Failed to locate trig tables"); + if (W_LumpLength(lump) != sizeof(tantoangle)) + I_Error("R_LoadTrigTables: Invalid TANTOANG"); + W_ReadLump(lump,(unsigned char*)tantoangle); + } + // Endianness correction - might still be non-portable, but is fast where possible + { + size_t n; + lprintf(LO_INFO, "Endianness..."); + + // This test doesn't assume the endianness of the tables, but deduces them from + // en entry. I hope this is portable. + if ((10 < finesine[1]) && (finesine[1] < 100)) { + lprintf(LO_INFO, "ok."); + return; // Endianness is correct + } + + // Must correct endianness of every long loaded (!) +#define CORRECT_TABLE_ENDIAN(tbl) \ + for (n = 0; nname; p++) + *p->map = W_CacheLumpName(p->name); +} + +// +// V_CopyRect +// +// Copies a source rectangle in a screen buffer to a destination +// rectangle in another screen buffer. Source origin in srcx,srcy, +// destination origin in destx,desty, common size in width and height. +// Source buffer specfified by srcscrn, destination buffer by destscrn. +// +// Marks the destination rectangle on the screen dirty. +// +// No return. +// +void V_CopyRect(int srcx, int srcy, int srcscrn, int width, + int height, int destx, int desty, int destscrn, + enum patch_translation_e flags) +{ + byte *src; + byte *dest; + + src = screens[srcscrn].data + SURFACE_BYTE_PITCH * srcy + srcx * SURFACE_PIXEL_DEPTH; + dest = screens[destscrn].data + SURFACE_BYTE_PITCH * desty + destx * SURFACE_PIXEL_DEPTH; + + for ( ; height>0 ; height--) + { + memcpy (dest, src, width * SURFACE_PIXEL_DEPTH); + src += SURFACE_BYTE_PITCH; + dest += SURFACE_BYTE_PITCH; + } +} + +/* + * V_DrawBackground tiles a 64x64 patch over the entire screen, providing the + * background for the Help and Setup screens, and plot text betwen levels. + * cphipps - used to have M_DrawBackground, but that was used the framebuffer + * directly, so this is my code from the equivalent function in f_finale.c + */ + +// FIXME: restore v_video.inl +static inline short GETCOL15(byte col) + { return VID_PAL15(col, VID_COLORWEIGHTMASK); } + +// draw a stretched 64x64 flat to the top left corner of the screen +#define V_DRAWFLAT(SCRN, TYPE, GETCOL) { \ + const int width = (64 * SCREENWIDTH) / 320; \ + int height = (64 * SCREENHEIGHT) / 200; \ + fixed_t dx = (320 << FRACBITS) / SCREENWIDTH; \ + TYPE *dest = (TYPE *)screens[SCRN].data; \ + \ + while (height--) { \ + const byte *const src_row = src + 64*((height*200)/SCREENHEIGHT); \ + TYPE *const dst_row = dest + SURFACE_SHORT_PITCH * height; \ + int x; \ + fixed_t tx; \ + \ + for (x=0, tx=0; x> FRACBITS]; \ + dst_row[x] = GETCOL(col); \ + } \ + } \ +} + +void V_DrawBackground(const char* flatname, int scrn) +{ + /* erase the entire screen to a tiled background */ + const byte *src; + int x,y; + int lump; + const int w = (64*SCREENWIDTH/320), h = (64*SCREENHEIGHT/200); + + // killough 4/17/98: + src = W_CacheLumpNum(lump = firstflat + R_FlatNumForName(flatname)); + + /* V_DrawBlock(0, 0, scrn, 64, 64, src, 0); */ + V_DRAWFLAT(scrn, short, GETCOL15); + /* end V_DrawBlock */ + + for (y=0 ; ytopoffset; + x -= patch->leftoffset; + + // CPhipps - null translation pointer => no translation + if (!trans) + flags &= ~VPT_TRANS; + + // CPhipps - move stretched patch drawing code here + // - reformat initialisers, move variables into inner blocks + + int col; + int w = (patch->width << 16) - 1; // CPhipps - -1 for faster flipping + int left, right, top, bottom; + int DX = 1 << 16; + int DXI = 1 << 16; + int DY = 1 << 16; + int DYI = 1 << 16; + R_DrawColumn_f colfunc; + draw_column_vars_t dcvars; + draw_vars_t olddrawvars = drawvars; + + R_SetDefaultDrawColumnVars(&dcvars); + + drawvars.short_topleft = (unsigned short *)screens[scrn].data; + drawvars.int_topleft = (unsigned int *)screens[scrn].data; + + + if (flags & VPT_TRANS) { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_TRANSLATED, drawvars.filterpatch, RDRAW_FILTER_NONE); + dcvars.translation = trans; + } else { + colfunc = R_GetDrawColumnFunc(RDC_PIPELINE_STANDARD, drawvars.filterpatch, RDRAW_FILTER_NONE); + } + + left = ( x * DX ) >> FRACBITS; + top = ( y * DY ) >> FRACBITS; + right = ( (x + patch->width) * DX ) >> FRACBITS; + bottom = ( (y + patch->height) * DY ) >> FRACBITS; + + dcvars.texheight = patch->height; + dcvars.iscale = DYI; + dcvars.drawingmasked = MAX(patch->width, patch->height) > 8; + dcvars.edgetype = drawvars.patch_edges; + + if (drawvars.filterpatch == RDRAW_FILTER_LINEAR) { + // bias the texture u coordinate + if (patch->isNotTileable) + col = -(FRACUNIT>>1); + else + col = (patch->width<>1); + } + else { + col = 0; + } + + for (dcvars.x=left; dcvars.x>16): (col>>16); + const rcolumn_t *column = R_GetPatchColumn(patch, colindex); + const rcolumn_t *prevcolumn = R_GetPatchColumn(patch, colindex-1); + const rcolumn_t *nextcolumn = R_GetPatchColumn(patch, colindex+1); + + // ignore this column if it's to the left of our clampRect + if (dcvars.x < 0) + continue; + if (dcvars.x >= SCREENWIDTH) + break; + + dcvars.texu = ((flags & VPT_FLIP) ? ((patch->width<width<numPosts; i++) { + const rpost_t *post = &column->posts[i]; + int yoffset = 0; + + dcvars.yl = (((y + post->topdelta) * DY)>>FRACBITS); + dcvars.yh = (((y + post->topdelta + post->length) * DY - (FRACUNIT>>1))>>FRACBITS); + dcvars.edgeslope = post->slope; + + if ((dcvars.yh < 0) || (dcvars.yh < top)) + continue; + if ((dcvars.yl >= SCREENHEIGHT) || (dcvars.yl >= bottom)) + continue; + + if (dcvars.yh >= bottom) { + dcvars.yh = bottom-1; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK; + } + if (dcvars.yh >= SCREENHEIGHT) { + dcvars.yh = SCREENHEIGHT-1; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_BOT_MASK; + } + + if (dcvars.yl < 0) { + yoffset = 0-dcvars.yl; + dcvars.yl = 0; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK; + } + if (dcvars.yl < top) { + yoffset = top-dcvars.yl; + dcvars.yl = top; + dcvars.edgeslope &= ~RDRAW_EDGESLOPE_TOP_MASK; + } + + dcvars.source = column->pixels + post->topdelta + yoffset; + dcvars.prevsource = prevcolumn ? prevcolumn->pixels + post->topdelta + yoffset: dcvars.source; + dcvars.nextsource = nextcolumn ? nextcolumn->pixels + post->topdelta + yoffset: dcvars.source; + + dcvars.texturemid = -((dcvars.yl-centery)*dcvars.iscale); + + colfunc(&dcvars); + } + } + + R_ResetColumnBuffer(); + drawvars = olddrawvars; +} + +// CPhipps - some simple, useful wrappers for that function, for drawing patches from wads + +// CPhipps - GNU C only suppresses generating a copy of a function if it is +// static inline; other compilers have different behaviour. +// This inline is _only_ for the function below + +void V_DrawNumPatch(int x, int y, int scrn, int lump, + int cm, enum patch_translation_e flags) +{ + V_DrawMemPatch(x, y, scrn, R_CachePatchNum(lump), cm, flags); + R_UnlockPatchNum(lump); +} + +unsigned short *V_Palette15 = NULL; +static unsigned short *Palettes15 = NULL; +static int currentPaletteIndex = 0; +// +// V_UpdateTrueColorPalette +// +void V_UpdateTrueColorPalette(void) { + int i, w, p; + byte r,g,b; + int nr,ng,nb; + float t; + int paletteNum = currentPaletteIndex; + static int usegammaOnLastPaletteGeneration = -1; + + int pplump = W_GetNumForName("PLAYPAL"); + int gtlump = (W_CheckNumForName)("GAMMATBL",ns_prboom); + const byte *pal = W_CacheLumpNum(pplump); + // opengl doesn't use the gamma + const byte *const gtable = + (const byte *)W_CacheLumpNum(gtlump) + + (256*(usegamma)) + ; + + int numPals = W_LumpLength(pplump) / (3*256); + const float dontRoundAbove = 220; + float roundUpR, roundUpG, roundUpB; + + if (usegammaOnLastPaletteGeneration != usegamma) { + if (Palettes15) free(Palettes15); + Palettes15 = NULL; + usegammaOnLastPaletteGeneration = usegamma; + } + + if (!Palettes15) { + // set short palette + Palettes15 = (short*)malloc(numPals*256*sizeof(short)*VID_NUMCOLORWEIGHTS); + for (p=0; p dontRoundAbove) ? 0 : 0.5f; + roundUpG = (g > dontRoundAbove) ? 0 : 0.5f; + roundUpB = (b > dontRoundAbove) ? 0 : 0.5f; + + for (w=0; w>3)*t+roundUpR); + ng = (int)((g>>3)*t+roundUpG); + nb = (int)((b>>3)*t+roundUpB); + Palettes15[((p*256+i)*VID_NUMCOLORWEIGHTS)+w] = ( + (nr<<10) | (ng<<5) | nb + ); + } + } + } + } + V_Palette15 = Palettes15 + paletteNum*256*VID_NUMCOLORWEIGHTS; + + W_UnlockLumpNum(pplump); + W_UnlockLumpNum(gtlump); +} + + +//--------------------------------------------------------------------------- +// V_DestroyTrueColorPalette +//--------------------------------------------------------------------------- +static void V_DestroyTrueColorPalette(void) { + if (Palettes15) free(Palettes15); + Palettes15 = NULL; + V_Palette15 = NULL; +} + +void V_DestroyUnusedTrueColorPalettes(void) { +} + +// +// V_SetPalette +// +// CPhipps - New function to set the palette to palette number pal. +// Handles loading of PLAYPAL and calls I_SetPalette + +void V_SetPalette(int pal) +{ + currentPaletteIndex = pal; + + I_SetPalette(pal); + // V_SetPalette can be called as part of the gamma setting before + // we've loaded any wads, which prevents us from reading the palette - POPE + if (W_CheckNumForName("PLAYPAL") >= 0) { + V_UpdateTrueColorPalette(); + } +} + +// +// V_FillRect +// +// CPhipps - New function to fill a rectangle with a given colour +void V_FillRect(int scrn, int x, int y, int width, int height, byte colour) +{ + unsigned short* dest = (unsigned short *)screens[scrn].data + x + y * SURFACE_SHORT_PITCH; + int w; + short c = VID_PAL15(colour, VID_COLORWEIGHTMASK); + while (height--) { + for (w=0; wnot_on_heap) + if (( SURFACE_BYTE_PITCH * scrn->height) > 0) + scrn->data = malloc( SURFACE_BYTE_PITCH * scrn->height); +} + +// +// V_AllocScreens +// +void V_AllocScreens(void) { + int i; + + for (i=0; inot_on_heap) { + free(scrn->data); + scrn->data = NULL; + } +} + +// +// V_FreeScreens +// +void V_FreeScreens(void) { + int i; + + for (i=0; ib.x - fl->a.x; + ax = 2 * (dx<0 ? -dx : dx); + sx = dx<0 ? -1 : 1; + + dy = fl->b.y - fl->a.y; + ay = 2 * (dy<0 ? -dy : dy); + sy = dy<0 ? -1 : 1; + + x = fl->a.x; + y = fl->a.y; + + if (ax > ay) + { + d = ay - ax/2; + while (1) + { + PUTDOT(x,y,color); + if (x == fl->b.x) return; + if (d>=0) + { + y += sy; + d -= ax; + } + x += sx; + d += ay; + } + } + else + { + d = ax - ay/2; + while (1) + { + PUTDOT(x, y, color); + if (y == fl->b.y) return; + if (d >= 0) + { + x += sx; + d -= ay; + } + y += sy; + d += ax; + } + } +} diff --git a/src/v_video.h b/src/v_video.h new file mode 100644 index 00000000..915f0d88 --- /dev/null +++ b/src/v_video.h @@ -0,0 +1,176 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Gamma correction LUT. + * Color range translation support + * Functions to draw patches (by post) directly to screen. + * Functions to blit a block to the screen. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __V_VIDEO__ +#define __V_VIDEO__ + +#include "doomtype.h" +#include "doomdef.h" +// Needed because we are refering to patches. +#include "r_data.h" + +// +// VIDEO +// + +#define CENTERY (100) // SCREENHEIGHT/2 = 200/2 = 100 + +// Screen 0 is the screen updated by I_Update screen. +// Screen 1 is an extra buffer. + +// array of pointers to color translation tables +extern const byte *colrngs[]; + +// symbolic indices into color translation table pointer array +typedef enum +{ + CR_BRICK, //0 + CR_TAN, //1 + CR_GRAY, //2 + CR_GREEN, //3 + CR_BROWN, //4 + CR_GOLD, //5 + CR_RED, //6 + CR_BLUE, //7 + CR_ORANGE, //8 + CR_YELLOW, //9 + CR_BLUE2, //10 // proff + CR_LIMIT //11 //jff 2/27/98 added for range check +} crange_idx_e; +//jff 1/16/98 end palette color range additions + +#define CR_DEFAULT CR_RED /* default value for out of range colors */ + +typedef struct { + byte *data; // pointer to the screen content + boolean not_on_heap; // if set, no malloc or free is preformed and + // data never set to NULL. Used i.e. with SDL doublebuffer. + int height; // the height of the surface, used when mallocing +} screeninfo_t; + +#define NUM_SCREENS 6 +extern screeninfo_t screens[NUM_SCREENS]; +extern int usegamma; + +// Varying bit-depth support -POPE +// +// For bilinear filtering, each palette color is pre-weighted and put in a +// table for fast blending operations. These macros decide how many weights +// to create for each color. The lower the number, the lower the blend +// accuracy, which can produce very bad artifacts in texture filtering. +#define VID_NUMCOLORWEIGHTS 64 +#define VID_COLORWEIGHTMASK (VID_NUMCOLORWEIGHTS-1) +#define VID_COLORWEIGHTBITS 6 + +// Palettes for converting from 8 bit color to 16 and 32 bit. Also +// contains the weighted versions of each palette color for filtering +// operations +extern unsigned short *V_Palette15; + +#define VID_PAL15(color, weight) V_Palette15[ (color)*VID_NUMCOLORWEIGHTS + (weight) ] + +extern const char *default_videomode; + +void V_InitMode(void); + +// video mode query interface +int V_GetNumPixelBits(void); +int V_GetPixelDepth(void); + +//jff 4/24/98 loads color translation lumps +void V_InitColorTranslation(void); + +// Allocates buffer screens, call before R_Init. +void V_Init (void); + +// V_CopyRect +extern void V_CopyRect(int srcx, int srcy, int srcscrn, + int width, int height, + int destx, int desty, int destscrn, + enum patch_translation_e flags); + +extern void V_FillRect_f(int scrn, int x, int y, + int width, int height, byte colour); + +// CPhipps - patch drawing +// Consolidated into the 3 really useful functions: + +// V_DrawNumPatch - Draws the patch from lump num +extern void V_DrawNumPatch(int x, int y, int scrn, + int lump, int cm, + enum patch_translation_e flags); + +// V_DrawNamePatch - Draws the patch from lump "name" +#define V_DrawNamePatch(x,y,s,n,t,f) V_DrawNumPatch(x,y,s,W_GetNumForName(n),t,f) + +/* cph - + * Functions to return width & height of a patch. + * Doesn't really belong here, but is often used in conjunction with + * this code. + */ +#define V_NamePatchWidth(name) R_NumPatchWidth(W_GetNumForName(name)) +#define V_NamePatchHeight(name) R_NumPatchHeight(W_GetNumForName(name)) + +/* cphipps 10/99: function to tile a flat over the screen */ +extern void V_DrawBackground(const char* flatname, int scrn); + +void V_DestroyUnusedTrueColorPalettes(void); +// CPhipps - function to set the palette to palette number pal. +void V_SetPalette(int pal); + +// CPhipps - function to plot a pixel + +// V_PlotPixel +extern void V_PlotPixel(int,int,int,byte); + +typedef struct +{ + int x, y; +} fpoint_t; + +typedef struct +{ + fpoint_t a, b; +} fline_t; + +extern void V_DrawLine(fline_t* fl, int color); + +void V_AllocScreen(screeninfo_t *scrn); +void V_AllocScreens(); +void V_FreeScreen(screeninfo_t *scrn); +void V_FreeScreens(); + +#endif diff --git a/src/version.c b/src/version.c new file mode 100644 index 00000000..142017e0 --- /dev/null +++ b/src/version.c @@ -0,0 +1,38 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Date stamp + * + *----------------------------------------------------------------------------- + */ + + +#include "version.h" + +const char version_date[] = __DATE__; diff --git a/src/version.h b/src/version.h new file mode 100644 index 00000000..f7ad1615 --- /dev/null +++ b/src/version.h @@ -0,0 +1,40 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Doom version indicators. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __DOOMVERSION__ +#define __DOOMVERSION__ + +extern const char version_date[]; + +#endif diff --git a/src/w_memcache.c b/src/w_memcache.c new file mode 100644 index 00000000..2f70f9f5 --- /dev/null +++ b/src/w_memcache.c @@ -0,0 +1,114 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2001 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Handles in-memory caching of WAD lumps + * + *----------------------------------------------------------------------------- + */ + +// use config.h if autoconf made one -- josh +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomstat.h" +#include "doomtype.h" + +#ifdef __GNUG__ +#pragma implementation "w_wad.h" +#endif +#include "w_wad.h" +#include "z_zone.h" +#include "lprintf.h" + +static struct { + void *cache; + unsigned int locks; +} *cachelump; + +/* W_InitCache + * + * cph 2001/07/07 - split from W_Init + */ +void W_InitCache(void) +{ + // set up caching + cachelump = calloc(sizeof *cachelump, numlumps); + if (!cachelump) + I_Error ("W_Init: Couldn't allocate lumpcache"); +} + +void W_DoneCache(void) +{ +} + +/* W_CacheLumpNum + * killough 4/25/98: simplified + * CPhipps - modified for new lump locking scheme + * returns a const* + */ + +const void *W_CacheLumpNum(int lump) +{ + const int locks = 1; + + if (!cachelump[lump].cache) // read the lump in + W_ReadLump(lump, Z_Malloc(W_LumpLength(lump), PU_CACHE, &cachelump[lump].cache)); + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (!cachelump[lump].locks && locks) { + Z_ChangeTag(cachelump[lump].cache,PU_STATIC); + } + cachelump[lump].locks += locks; + + return cachelump[lump].cache; +} + +const void *W_LockLumpNum(int lump) +{ + return W_CacheLumpNum(lump); +} + +/* + * W_UnlockLumpNum + * + * CPhipps - this changes (should reduce) the number of locks on a lump + */ + +void W_UnlockLumpNum(int lump) +{ + const int unlocks = 1; + cachelump[lump].locks -= unlocks; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (unlocks && !cachelump[lump].locks) + Z_ChangeTag(cachelump[lump].cache, PU_CACHE); +} + diff --git a/src/w_mmap.c b/src/w_mmap.c new file mode 100644 index 00000000..909fd8ca --- /dev/null +++ b/src/w_mmap.c @@ -0,0 +1,270 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 2001 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Transparent access to data in WADs using mmap + * + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#endif + +#include "doomstat.h" +#include "doomtype.h" + +#ifdef __GNUG__ +#pragma implementation "w_wad.h" +#endif +#include "w_wad.h" +#include "z_zone.h" +#include "lprintf.h" +#include "i_system.h" + +static struct { + void *cache; + int locks; +} *cachelump; + +#ifdef _WIN32 +typedef struct { + HANDLE hnd; + OFSTRUCT fileinfo; + HANDLE hnd_map; + void *data; +} mmap_info_t; + +mmap_info_t *mapped_wad; + +void W_DoneCache(void) +{ + size_t i; + + if (cachelump) { + free(cachelump); + cachelump = NULL; + } + + if (!mapped_wad) + return; + for (i=0; ihandle > maxfd) maxfd = lumpinfo[i].wadfile->handle; + } + mapped_wad = calloc(maxfd+1,sizeof *mapped_wad); + { + int i; + for (i=0; ihandle; + if (!mapped_wad[fd]) + if ((mapped_wad[fd] = mmap(NULL,I_Filelength(fd),PROT_READ,MAP_SHARED,fd,0)) == MAP_FAILED) + I_Error("W_InitCache: failed to mmap"); + } + } + } +} + +void W_DoneCache(void) +{ + { + int i; + for (i=0; ihandle; + if (mapped_wad[fd]) { + if (munmap(mapped_wad[fd],I_Filelength(fd))) + I_Error("W_DoneCache: failed to munmap"); + mapped_wad[fd] = NULL; + } + } + } + free(mapped_wad); +} + +const void* W_CacheLumpNum(int lump) +{ + if (!lumpinfo[lump].wadfile) + return NULL; + + return + (const void *) ( + ((const byte *) (mapped_wad[lumpinfo[lump].wadfile->handle])) + + lumpinfo[lump].position + ); +} +#endif + +/* + * W_LockLumpNum + * + * This copies the lump into a malloced memory region and returns its address + * instead of returning a pointer into the memory mapped area + * + */ +const void* W_LockLumpNum(int lump) +{ + size_t len = W_LumpLength(lump); + const void *data = W_CacheLumpNum(lump); + + if (!cachelump[lump].cache) { + // read the lump in + Z_Malloc(len, PU_CACHE, &cachelump[lump].cache); + memcpy(cachelump[lump].cache, data, len); + } + + /* cph - if wasn't locked but now is, tell z_zone to hold it */ + if (cachelump[lump].locks <= 0) { + Z_ChangeTag(cachelump[lump].cache,PU_STATIC); + // reset lock counter + cachelump[lump].locks = 1; + } else { + // increment lock counter + cachelump[lump].locks += 1; + } + + return cachelump[lump].cache; +} + +void W_UnlockLumpNum(int lump) { + if (cachelump[lump].locks == -1) + return; // this lump is memory mapped + + cachelump[lump].locks -= 1; + /* cph - Note: must only tell z_zone to make purgeable if currently locked, + * else it might already have been purged + */ + if (cachelump[lump].locks == 0) + Z_ChangeTag(cachelump[lump].cache, PU_CACHE); +} + diff --git a/src/w_wad.c b/src/w_wad.c new file mode 100644 index 00000000..cf702c8a --- /dev/null +++ b/src/w_wad.c @@ -0,0 +1,514 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2001 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Handles WAD file header, directory, lump I/O. + * + *----------------------------------------------------------------------------- + */ + +// use config.h if autoconf made one -- josh +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef _MSC_VER +#include +#include +#endif +#include + +#include "doomstat.h" +#include "d_net.h" +#include "doomtype.h" +#include "i_system.h" + +#ifdef __GNUG__ +#pragma implementation "w_wad.h" +#endif +#include "w_wad.h" +#include "lprintf.h" + +#include + +// +// GLOBALS +// + +// Location of each lump on disk. +lumpinfo_t *lumpinfo; +int numlumps; // killough + +void ExtractFileBase (const char *path, char *dest) +{ + const char *src = path + strlen(path) - 1; + int length; + + // back up until a \ or the start + while (src != path && src[-1] != ':' // killough 3/22/98: allow c:filename + && *(src-1) != '\\' + && *(src-1) != '/') + { + src--; + } + + // copy up to eight characters + memset(dest,0,8); + length = 0; + + while ((*src) && (*src != '.') && (++length<9)) + { + *dest++ = toupper(*src); + *src++; + } + /* cph - length check removed, just truncate at 8 chars. + * If there are 8 or more chars, we'll copy 8, and no zero termination + */ +} + +// +// 1/18/98 killough: adds a default extension to a path +// Note: Backslashes are treated specially, for MS-DOS. +// + +char *AddDefaultExtension(char *path, const char *ext) +{ + char *p = path; + while (*p++); + while (p-->path && *p!='/' && *p!='\\') + if (*p=='.') + return path; + if (*ext!='.') + strcat(path,"."); + return strcat(path,ext); +} + +// +// LUMP BASED ROUTINES. +// + +// +// W_AddFile +// All files are optional, but at least one file must be +// found (PWAD, if all required lumps are present). +// Files with a .wad extension are wadlink files +// with multiple lumps. +// Other files are single lumps with the base filename +// for the lump name. +// +// Reload hack removed by Lee Killough +// CPhipps - source is an enum +// +// proff - changed using pointer to wadfile_info_t +static void W_AddFile(wadfile_info_t *wadfile) +// killough 1/31/98: static, const +{ + wadinfo_t header; + lumpinfo_t* lump_p; + unsigned i; + int length; + int startlump; + filelump_t *fileinfo, *fileinfo2free=NULL; //killough + filelump_t singleinfo; + + // open the file and add to directory + +//precache into memory instead of reading from disk + wadfile->handle = fopen(wadfile->name, "rb"); + +#ifdef HAVE_NET + if (wadfile->handle == 0 && D_NetGetWad(wadfile->name)) // CPhipps + wadfile->handle = fopen(wadfile->name, "rb"); +#endif + + if (wadfile->handle == 0) + { + if ( strlen(wadfile->name)<=4 || // add error check -- killough + (strcasecmp(wadfile->name+strlen(wadfile->name)-4 , ".lmp" ) && + strcasecmp(wadfile->name+strlen(wadfile->name)-4 , ".gwa" ) ) + ) + I_Error("W_AddFile: couldn't open %s",wadfile->name); + return; + } + + struct stat statbuf; + stat(wadfile->name, &statbuf); + wadfile->length = statbuf.st_size; + wadfile->data = malloc(statbuf.st_size); + fread(wadfile->data, statbuf.st_size, 1, wadfile->handle); + + //jff 8/3/98 use logical output routine + lprintf (LO_INFO," adding %s\n",wadfile->name); + startlump = numlumps; + + if ( strlen(wadfile->name)<=4 || + ( + strcasecmp(wadfile->name+strlen(wadfile->name)-4,".wad") && + strcasecmp(wadfile->name+strlen(wadfile->name)-4,".gwa") + ) + ) + { + // single lump file + fileinfo = &singleinfo; + singleinfo.filepos = 0; + singleinfo.size = wadfile->length; + ExtractFileBase(wadfile->name, singleinfo.name); + numlumps++; + } + else + { + // WAD file + memcpy(&header, wadfile->data, sizeof(header)); + if (strncmp(header.identification,"IWAD",4) && + strncmp(header.identification,"PWAD",4)) + I_Error("W_AddFile: Wad file %s doesn't have IWAD or PWAD id", wadfile->name); + header.numlumps = LONG(header.numlumps); + header.infotableofs = LONG(header.infotableofs); + length = header.numlumps*sizeof(filelump_t); + fileinfo2free = fileinfo = malloc(length); // killough + memcpy(fileinfo, &wadfile->data[header.infotableofs], length); + numlumps += header.numlumps; + } + + // Fill in lumpinfo + lumpinfo = realloc(lumpinfo, numlumps*sizeof(lumpinfo_t)); + + lump_p = &lumpinfo[startlump]; + + for (i=startlump ; (int)iwadfile = wadfile; // killough 4/25/98 + lump_p->position = LONG(fileinfo->filepos); + lump_p->size = LONG(fileinfo->size); + lump_p->li_namespace = ns_global; // killough 4/17/98 + strncpy (lump_p->name, fileinfo->name, 8); + lump_p->source = wadfile->src; // Ty 08/29/98 + } + + free(fileinfo2free); // killough +} + +// jff 1/23/98 Create routines to reorder the master directory +// putting all flats into one marked block, and all sprites into another. +// This will allow loading of sprites and flats from a PWAD with no +// other changes to code, particularly fast hashes of the lumps. +// +// killough 1/24/98 modified routines to be a little faster and smaller + +static int IsMarker(const char *marker, const char *name) +{ + return !strncasecmp(name, marker, 8) || + // doubled first character test for single-character prefixes only + // FF_* is valid alias for F_*, but HI_* should not allow HHI_* + (marker[1] == '_' && *name == *marker && !strncasecmp(name+1, marker, 7)); +} + +// killough 4/17/98: add namespace tags + +static void W_CoalesceMarkedResource(const char *start_marker, + const char *end_marker, int li_namespace) +{ + lumpinfo_t *marked = malloc(sizeof(*marked) * numlumps); + size_t i, num_marked = 0, num_unmarked = 0; + int is_marked = 0, mark_end = 0; + lumpinfo_t *lump = lumpinfo; + + for (i=numlumps; i--; lump++) + if (IsMarker(start_marker, lump->name)) // start marker found + { // If this is the first start marker, add start marker to marked lumps + if (!num_marked) + { + strncpy(marked->name, start_marker, 8); + marked->size = 0; // killough 3/20/98: force size to be 0 + marked->li_namespace = ns_global; // killough 4/17/98 + marked->wadfile = NULL; + num_marked = 1; + } + is_marked = 1; // start marking lumps + } + else + if (IsMarker(end_marker, lump->name)) // end marker found + { + mark_end = 1; // add end marker below + is_marked = 0; // stop marking lumps + } + else + if (is_marked) // if we are marking lumps, + { // move lump to marked list + marked[num_marked] = *lump; + marked[num_marked++].li_namespace = li_namespace; // killough 4/17/98 + } + else + lumpinfo[num_unmarked++] = *lump; // else move down THIS list + + // Append marked list to end of unmarked list + memcpy(lumpinfo + num_unmarked, marked, num_marked * sizeof(*marked)); + + free(marked); // free marked list + + numlumps = num_unmarked + num_marked; // new total number of lumps + + if (mark_end) // add end marker + { + lumpinfo[numlumps].size = 0; // killough 3/20/98: force size to be 0 + lumpinfo[numlumps].wadfile = NULL; + lumpinfo[numlumps].li_namespace = ns_global; // killough 4/17/98 + strncpy(lumpinfo[numlumps++].name, end_marker, 8); + } +} + +// Hash function used for lump names. +// Must be mod'ed with table size. +// Can be used for any 8-character names. +// by Lee Killough + +unsigned W_LumpNameHash(const char *s) +{ + unsigned hash; + (void) ((hash = toupper(s[0]), s[1]) && + (hash = hash*3+toupper(s[1]), s[2]) && + (hash = hash*2+toupper(s[2]), s[3]) && + (hash = hash*2+toupper(s[3]), s[4]) && + (hash = hash*2+toupper(s[4]), s[5]) && + (hash = hash*2+toupper(s[5]), s[6]) && + (hash = hash*2+toupper(s[6]), + hash = hash*2+toupper(s[7])) + ); + return hash; +} + +// +// W_CheckNumForName +// Returns -1 if name not found. +// +// Rewritten by Lee Killough to use hash table for performance. Significantly +// cuts down on time -- increases Doom performance over 300%. This is the +// single most important optimization of the original Doom sources, because +// lump name lookup is used so often, and the original Doom used a sequential +// search. For large wads with > 1000 lumps this meant an average of over +// 500 were probed during every search. Now the average is under 2 probes per +// search. There is no significant benefit to packing the names into longwords +// with this new hashing algorithm, because the work to do the packing is +// just as much work as simply doing the string comparisons with the new +// algorithm, which minimizes the expected number of comparisons to under 2. +// +// killough 4/17/98: add namespace parameter to prevent collisions +// between different resources such as flats, sprites, colormaps +// + +// W_FindNumFromName, an iterative version of W_CheckNumForName +// returns list of lump numbers for a given name (latest first) +// +int (W_FindNumFromName)(const char *name, int li_namespace, int i) +{ + // Hash function maps the name to one of possibly numlump chains. + // It has been tuned so that the average chain length never exceeds 2. + + // proff 2001/09/07 - check numlumps==0, this happens when called before WAD loaded + if (numlumps == 0) + i = -1; + else + { + if (i < 0) + i = lumpinfo[W_LumpNameHash(name) % (unsigned) numlumps].index; + else + i = lumpinfo[i].next; + + // We search along the chain until end, looking for case-insensitive + // matches which also match a namespace tag. Separate hash tables are + // not used for each namespace, because the performance benefit is not + // worth the overhead, considering namespace collisions are rare in + // Doom wads. + + while (i >= 0 && (strncasecmp(lumpinfo[i].name, name, 8) || + lumpinfo[i].li_namespace != li_namespace)) + i = lumpinfo[i].next; + } + + // Return the matching lump, or -1 if none found. + + return i; +} + +// +// killough 1/31/98: Initialize lump hash table +// + +void W_HashLumps(void) +{ + int i; + + for (i=0; i= 0; i = next) + if (next == lump) + break; + + return i; +} + + +// W_Init +// Loads each of the files in the wadfiles array. +// All files are optional, but at least one file +// must be found. +// Files with a .wad extension are idlink files +// with multiple lumps. +// Other files are single lumps with the base filename +// for the lump name. +// Lump names can appear multiple times. +// The name searcher looks backwards, so a later file +// does override all earlier ones. +// +// CPhipps - modified to use the new wadfiles array +// +wadfile_info_t *wadfiles=NULL; + +size_t numwadfiles = 0; // CPhipps - size of the wadfiles array (dynamic, no limit) + +void W_Init(void) +{ + // CPhipps - start with nothing + + numlumps = 0; lumpinfo = NULL; + + { // CPhipps - new wadfiles array used + // open all the files, load headers, and count lumps + int i; + for (i=0; (size_t)i= numlumps) + I_Error ("W_LumpLength: %i >= numlumps",lump); + return lumpinfo[lump].size; +} + +// +// W_ReadLump +// Loads the lump into the given buffer, +// which must be >= W_LumpLength(). +// + +void W_ReadLump(int lump, void *dest) +{ + lumpinfo_t *l = lumpinfo + lump; + + { + if (l->wadfile) + memcpy(dest, &l->wadfile->data[l->position], l->size); + } +} + diff --git a/src/w_wad.h b/src/w_wad.h new file mode 100644 index 00000000..cbd7a643 --- /dev/null +++ b/src/w_wad.h @@ -0,0 +1,157 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * WAD I/O functions. + * + *-----------------------------------------------------------------------------*/ + + +#ifndef __W_WAD__ +#define __W_WAD__ + +#ifdef __GNUG__ +#pragma interface +#endif + +#include + +// +// TYPES +// + +typedef struct +{ + char identification[4]; // Should be "IWAD" or "PWAD". + int numlumps; + int infotableofs; +} wadinfo_t; + +typedef struct +{ + int filepos; + int size; + char name[8]; +} filelump_t; + +// +// WADFILE I/O related stuff. +// + +// CPhipps - defined enum in wider scope +// Ty 08/29/98 - add source field to identify where this lump came from +typedef enum { + // CPhipps - define elements in order of 'how new/unusual' + source_iwad=0, // iwad file load + source_pre, // predefined lump + source_auto_load, // lump auto-loaded by config file + source_pwad, // pwad file load + source_lmp, // lmp file load + source_net // CPhipps +} wad_source_t; + +// CPhipps - changed wad init +// We _must_ have the wadfiles[] the same as those actually loaded, so there +// is no point having these separate entities. This belongs here. +typedef struct { + const char* name; + wad_source_t src; + FILE* handle; + unsigned char *data; + int position; + int length; +} wadfile_info_t; + +extern wadfile_info_t *wadfiles; + +extern size_t numwadfiles; // CPhipps - size of the wadfiles array + +void W_Init(void); // CPhipps - uses the above array +void W_ReleaseAllWads(void); // Proff - Added for iwad switching +void W_InitCache(void); +void W_DoneCache(void); + +typedef struct +{ + // WARNING: order of some fields important (see info.c). + + char name[9]; + int size; + + // killough 1/31/98: hash table fields, used for ultra-fast hash table lookup + int index, next; + + // killough 4/17/98: namespace tags, to prevent conflicts between resources + enum { + ns_global=0, + ns_sprites, + ns_flats, + ns_colormaps, + ns_hires, + ns_prboom + } li_namespace; // haleyjd 05/21/02: renamed from "namespace" + + wadfile_info_t *wadfile; + int position; + wad_source_t source; +} lumpinfo_t; + +extern lumpinfo_t *lumpinfo; +extern int numlumps; + +// killough 4/17/98: if W_CheckNumForName() called with only +// one argument, pass ns_global as the default namespace + +#define W_FindNumFromName(name, lump) (W_FindNumFromName)(name, ns_global, lump) +int (W_FindNumFromName)(const char *name, int ns, int lump); +int W_ListNumFromName(const char *name, int lump); +#define W_CheckNumForName(name) (W_CheckNumForName)(name, ns_global) +static inline +int (W_CheckNumForName)(const char *name, int ns) + { return (W_FindNumFromName)(name, ns, -1); } +int W_GetNumForName (const char* name); +int W_LumpLength (int lump); +void W_ReadLump (int lump, void *dest); +// CPhipps - modified for 'new' lump locking +const void* W_CacheLumpNum (int lump); +const void* W_LockLumpNum(int lump); +void W_UnlockLumpNum(int lump); + +// CPhipps - convenience macros +//#define W_CacheLumpNum(num) (W_CacheLumpNum)((num),1) +#define W_CacheLumpName(name) W_CacheLumpNum (W_GetNumForName(name)) + +//#define W_UnlockLumpNum(num) (W_UnlockLumpNum)((num),1) +#define W_UnlockLumpName(name) W_UnlockLumpNum (W_GetNumForName(name)) + +char *AddDefaultExtension(char *, const char *); // killough 1/18/98 +void ExtractFileBase(const char *, char *); // killough +unsigned W_LumpNameHash(const char *s); // killough 1/31/98 +void W_HashLumps(void); // cph 2001/07/07 - made public + +#endif diff --git a/src/wi_stuff.c b/src/wi_stuff.c new file mode 100644 index 00000000..84c18414 --- /dev/null +++ b/src/wi_stuff.c @@ -0,0 +1,2003 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Intermission screens. + * + *----------------------------------------------------------------------------- + */ + +#include "doomstat.h" +#include "m_random.h" +#include "w_wad.h" +#include "g_game.h" +#include "r_main.h" +#include "v_video.h" +#include "wi_stuff.h" +#include "s_sound.h" +#include "sounds.h" +#include "lprintf.h" // jff 08/03/98 - declaration of lprintf +#include "r_draw.h" + +// Ty 03/17/98: flag that new par times have been loaded in d_deh +extern boolean deh_pars; + +// +// Data needed to add patches to full screen intermission pics. +// Patches are statistics messages, and animations. +// Loads of by-pixel layout and placement, offsets etc. +// + +// +// Different vetween registered DOOM (1994) and +// Ultimate DOOM - Final edition (retail, 1995?). +// This is supposedly ignored for commercial +// release (aka DOOM II), which had 34 maps +// in one episode. So there. +#define NUMEPISODES 4 +#define NUMMAPS 9 + + +// Not used +// in tics +//U #define PAUSELEN (TICRATE*2) +//U #define SCORESTEP 100 +//U #define ANIMPERIOD 32 +// pixel distance from "(YOU)" to "PLAYER N" +//U #define STARDIST 10 +//U #define WK 1 + + +// GLOBAL LOCATIONS +#define WI_TITLEY 2 +#define WI_SPACINGY 33 + +// SINGLE-PLAYER STUFF +#define SP_STATSX 50 +#define SP_STATSY 50 + +#define SP_TIMEX 8 +// proff/nicolas 09/20/98 -- changed for hi-res +#define SP_TIMEY 160 +//#define SP_TIMEY (SCREENHEIGHT-32) + + +// NET GAME STUFF +#define NG_STATSY 50 +#define NG_STATSX (32 + V_NamePatchWidth(star)/2 + 32*!dofrags) + +#define NG_SPACINGX 64 + + +// Used to display the frags matrix at endgame +// DEATHMATCH STUFF +#define DM_MATRIXX 42 +#define DM_MATRIXY 68 + +#define DM_SPACINGX 40 + +#define DM_TOTALSX 269 + +#define DM_KILLERSX 10 +#define DM_KILLERSY 100 +#define DM_VICTIMSX 5 +#define DM_VICTIMSY 50 + + +// These animation variables, structures, etc. are used for the +// DOOM/Ultimate DOOM intermission screen animations. This is +// totally different from any sprite or texture/flat animations +typedef enum +{ + ANIM_ALWAYS, // determined by patch entry + ANIM_RANDOM, // occasional + ANIM_LEVEL // continuous +} animenum_t; + +typedef struct +{ + int x; // x/y coordinate pair structure + int y; +} point_t; + + +// +// Animation. +// There is another anim_t used in p_spec. +// +typedef struct +{ + animenum_t type; + + // period in tics between animations + int period; + + // number of animation frames + int nanims; + + // location of animation + point_t loc; + + // ALWAYS: n/a, + // RANDOM: period deviation (<256), + // LEVEL: level + int data1; + + // ALWAYS: n/a, + // RANDOM: random base period, + // LEVEL: n/a + int data2; + + /* actual graphics for frames of animations + * cphipps - const + */ + patchnum_t p[3]; + + // following must be initialized to zero before use! + + // next value of bcnt (used in conjunction with period) + int nexttic; + + // last drawn animation frame + int lastdrawn; + + // next frame number to animate + int ctr; + + // used by RANDOM and LEVEL when animating + int state; +} anim_t; + + +static point_t lnodes[NUMEPISODES][NUMMAPS] = +{ + // Episode 0 World Map + { + { 185, 164 }, // location of level 0 (CJ) + { 148, 143 }, // location of level 1 (CJ) + { 69, 122 }, // location of level 2 (CJ) + { 209, 102 }, // location of level 3 (CJ) + { 116, 89 }, // location of level 4 (CJ) + { 166, 55 }, // location of level 5 (CJ) + { 71, 56 }, // location of level 6 (CJ) + { 135, 29 }, // location of level 7 (CJ) + { 71, 24 } // location of level 8 (CJ) + }, + + // Episode 1 World Map should go here + { + { 254, 25 }, // location of level 0 (CJ) + { 97, 50 }, // location of level 1 (CJ) + { 188, 64 }, // location of level 2 (CJ) + { 128, 78 }, // location of level 3 (CJ) + { 214, 92 }, // location of level 4 (CJ) + { 133, 130 }, // location of level 5 (CJ) + { 208, 136 }, // location of level 6 (CJ) + { 148, 140 }, // location of level 7 (CJ) + { 235, 158 } // location of level 8 (CJ) + }, + + // Episode 2 World Map should go here + { + { 156, 168 }, // location of level 0 (CJ) + { 48, 154 }, // location of level 1 (CJ) + { 174, 95 }, // location of level 2 (CJ) + { 265, 75 }, // location of level 3 (CJ) + { 130, 48 }, // location of level 4 (CJ) + { 279, 23 }, // location of level 5 (CJ) + { 198, 48 }, // location of level 6 (CJ) + { 140, 25 }, // location of level 7 (CJ) + { 281, 136 } // location of level 8 (CJ) + } +}; + + +// +// Animation locations for episode 0 (1). +// Using patches saves a lot of space, +// as they replace 320x200 full screen frames. +// +static anim_t epsd0animinfo[] = +{ + { ANIM_ALWAYS, TICRATE/3, 3, { 224, 104 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 184, 160 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 112, 136 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 72, 112 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 88, 96 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 64, 48 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 192, 40 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 136, 16 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 80, 16 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 64, 24 } } +}; + +static anim_t epsd1animinfo[] = +{ + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 1 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 2 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 3 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 4 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 5 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 6 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 7 }, + { ANIM_LEVEL, TICRATE/3, 3, { 192, 144 }, 8 }, + { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 8 } +}; + +static anim_t epsd2animinfo[] = +{ + { ANIM_ALWAYS, TICRATE/3, 3, { 104, 168 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 40, 136 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 160, 96 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 104, 80 } }, + { ANIM_ALWAYS, TICRATE/3, 3, { 120, 32 } }, + { ANIM_ALWAYS, TICRATE/4, 3, { 40, 0 } } +}; + +static int NUMANIMS[NUMEPISODES] = +{ + sizeof(epsd0animinfo)/sizeof(anim_t), + sizeof(epsd1animinfo)/sizeof(anim_t), + sizeof(epsd2animinfo)/sizeof(anim_t) +}; + +static anim_t *anims[NUMEPISODES] = +{ + epsd0animinfo, + epsd1animinfo, + epsd2animinfo +}; + + +// +// GENERAL DATA +// + +// +// Locally used stuff. +// +#define FB 0 + + +// States for single-player +#define SP_KILLS 0 +#define SP_ITEMS 2 +#define SP_SECRET 4 +#define SP_FRAGS 6 +#define SP_TIME 8 +#define SP_PAR ST_TIME + +#define SP_PAUSE 1 + +// in seconds +#define SHOWNEXTLOCDELAY 4 +//#define SHOWLASTLOCDELAY SHOWNEXTLOCDELAY + + +// used to accelerate or skip a stage +int acceleratestage; // killough 3/28/98: made global + +// wbs->pnum +static int me; + + // specifies current state +static stateenum_t state; + +// contains information passed into intermission +static wbstartstruct_t* wbs; + +static wbplayerstruct_t* plrs; // wbs->plyr[] + +// used for general timing +static int cnt; + +// used for timing of background animation +static int bcnt; + +// signals to refresh everything for one frame +static int firstrefresh; + +static int cnt_time; +static int cnt_total_time; +static int cnt_par; +static int cnt_pause; + +// +// GRAPHICS +// + +// You Are Here graphic +static const char* yah[2] = { "WIURH0", "WIURH1" }; + +// splat +static const char* splat = "WISPLAT"; + +// %, : graphics +static const char percent[] = {"WIPCNT"}; +static const char colon[] = {"WICOLON"}; + +// 0-9 graphic +static patchnum_t num[10]; + +// minus sign +static const char wiminus[] = {"WIMINUS"}; + +// "Finished!" graphics +static const char finished[] = {"WIF"}; + +// "Entering" graphic +static const char entering[] = {"WIENTER"}; + +// "secret" +static const char sp_secret[] = {"WISCRT2"}; + +// "Kills", "Scrt", "Items", "Frags" +static const char kills[] = {"WIOSTK"}; +static const char secret[] = {"WIOSTS"}; +static const char items[] = {"WIOSTI"}; +static const char frags[] = {"WIFRGS"}; + +// Time sucks. +static const char time1[] = {"WITIME"}; +static const char par[] = {"WIPAR"}; +static const char sucks[] = {"WISUCKS"}; + +// "killers", "victims" +static const char killers[] = {"WIKILRS"}; +static const char victims[] = {"WIVCTMS"}; + +// "Total", your face, your dead face +static const char total[] = {"WIMSTT"}; +static const char star[] = {"STFST01"}; +static const char bstar[] = {"STFDEAD0"}; + +// "red P[1..MAXPLAYERS]" +static const char facebackp[] = {"STPB0"}; + +// +// CODE +// + +static void WI_endDeathmatchStats(void); +static void WI_endNetgameStats(void); +#define WI_endStats WI_endNetgameStats + +/* ==================================================================== + * WI_levelNameLump + * Purpore: Returns the name of the graphic lump containing the name of + * the given level. + * Args: Episode and level, and buffer (must by 9 chars) to write to + * Returns: void + */ +void WI_levelNameLump(int epis, int map, char* buf) +{ + if (gamemode == commercial) { + sprintf(buf, "CWILV%2.2d", map); + } else { + sprintf(buf, "WILV%d%d", epis, map); + } +} + +// ==================================================================== +// WI_slamBackground +// Purpose: Put the full-screen background up prior to patches +// Args: none +// Returns: void +// +static void WI_slamBackground(void) +{ + char name[9]; // limited to 8 characters + + if (gamemode == commercial || (gamemode == retail && wbs->epsd == 3)) + strcpy(name, "INTERPIC"); + else + sprintf(name, "WIMAP%d", wbs->epsd); + + // background + V_DrawNamePatch(0, 0, FB, name, CR_DEFAULT, VPT_NONE); +} + + +// ==================================================================== +// WI_Responder +// Purpose: Draw animations on intermission background screen +// Args: ev -- event pointer, not actually used here. +// Returns: False -- dummy routine +// +// The ticker is used to detect keys +// because of timing issues in netgames. +boolean WI_Responder(event_t* ev) +{ + return false; +} + + +// ==================================================================== +// WI_drawLF +// Purpose: Draw the "Finished" level name before showing stats +// Args: none +// Returns: void +// +void WI_drawLF(void) +{ + int y = WI_TITLEY; + char lname[9]; + + // draw + /* cph - get the graphic lump name and use it */ + WI_levelNameLump(wbs->epsd, wbs->last, lname); + + if (W_CheckNumForName(lname) >= 0) // don't die on missing level names + { + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(lname))/2, y, + FB, lname, CR_DEFAULT, VPT_NONE); + + // draw "Finished!" + y += (5*V_NamePatchHeight(lname))/4; + } + + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(finished))/2, y, + FB, finished, CR_DEFAULT, VPT_NONE); +} + + +// ==================================================================== +// WI_drawEL +// Purpose: Draw introductory "Entering" and level name +// Args: none +// Returns: void +// +void WI_drawEL(void) +{ + int y = WI_TITLEY; + char lname[9]; + + /* cph - get the graphic lump name */ + WI_levelNameLump(wbs->epsd, wbs->next, lname); + + // draw "Entering" + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(entering))/2, + y, FB, entering, CR_DEFAULT, VPT_NONE); + + if (W_CheckNumForName(lname) < 0) // don't die on missing level names + return; + + // draw level + y += (5*V_NamePatchHeight(lname))/4; + + // CPhipps - patch drawing updated + V_DrawNamePatch((320 - V_NamePatchWidth(lname))/2, y, FB, + lname, CR_DEFAULT, VPT_NONE); +} + + +/* ==================================================================== + * WI_drawOnLnode + * Purpose: Draw patches at a location based on episode/map + * Args: n -- index to map# within episode + * c[] -- array of names of patches to be drawn + * Returns: void + */ +void +WI_drawOnLnode // draw stuff at a location by episode/map# +( int n, + const char* const c[] ) +{ + int i; + boolean fits = false; + + i = 0; + do + { + int left; + int top; + int right; + int bottom; + const rpatch_t* patch = R_CachePatchName(c[i]); + + left = lnodes[wbs->epsd][n].x - patch->leftoffset; + top = lnodes[wbs->epsd][n].y - patch->topoffset; + right = left + patch->width; + bottom = top + patch->height; + R_UnlockPatchName(c[i]); + + if (left >= 0 + && right < 320 + && top >= 0 + && bottom < 200) + { + fits = true; + } + else + { + i++; + } + } while (!fits && i!=2); + + if (fits && i<2) + { + // CPhipps - patch drawing updated + V_DrawNamePatch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y, + FB, c[i], CR_DEFAULT, VPT_NONE); + } + else + { + // DEBUG + //jff 8/3/98 use logical output routine + lprintf(LO_DEBUG,"Could not place patch on level %d\n", n+1); + } +} + + +// ==================================================================== +// WI_initAnimatedBack +// Purpose: Initialize pointers and styles for background animation +// Args: none +// Returns: void +// +void WI_initAnimatedBack(void) +{ + int i; + anim_t* a; + + if (gamemode == commercial) // no animation for DOOM2 + return; + + if (wbs->epsd > 2) + return; + + for (i=0;iepsd];i++) + { + a = &anims[wbs->epsd][i]; + + // init variables + a->ctr = -1; + + // specify the next time to draw it + if (a->type == ANIM_ALWAYS) + a->nexttic = bcnt + 1 + (M_Random()%a->period); + else + if (a->type == ANIM_RANDOM) + a->nexttic = bcnt + 1 + a->data2+(M_Random()%a->data1); + else + if (a->type == ANIM_LEVEL) + a->nexttic = bcnt + 1; + } +} + + +// ==================================================================== +// WI_updateAnimatedBack +// Purpose: Figure out what animation we do on this iteration +// Args: none +// Returns: void +// +void WI_updateAnimatedBack(void) +{ + int i; + anim_t* a; + + if (gamemode == commercial) + return; + + if (wbs->epsd > 2) + return; + + for (i=0;iepsd];i++) + { + a = &anims[wbs->epsd][i]; + + if (bcnt == a->nexttic) + { + switch (a->type) + { + case ANIM_ALWAYS: + if (++a->ctr >= a->nanims) a->ctr = 0; + a->nexttic = bcnt + a->period; + break; + + case ANIM_RANDOM: + a->ctr++; + if (a->ctr == a->nanims) + { + a->ctr = -1; + a->nexttic = bcnt+a->data2+(M_Random()%a->data1); + } + else + a->nexttic = bcnt + a->period; + break; + + case ANIM_LEVEL: + // gawd-awful hack for level anims + if (!(state == StatCount && i == 7) + && wbs->next == a->data1) + { + a->ctr++; + if (a->ctr == a->nanims) a->ctr--; + a->nexttic = bcnt + a->period; + } + break; + } + } + } +} + + +// ==================================================================== +// WI_drawAnimatedBack +// Purpose: Actually do the animation (whew!) +// Args: none +// Returns: void +// +void WI_drawAnimatedBack(void) +{ + int i; + anim_t* a; + + if (gamemode==commercial) //jff 4/25/98 Someone forgot commercial an enum + return; + + if (wbs->epsd > 2) + return; + + for (i=0 ; iepsd] ; i++) + { + a = &anims[wbs->epsd][i]; + + if (a->ctr >= 0) + // CPhipps - patch drawing updated + V_DrawNumPatch(a->loc.x, a->loc.y, FB, a->p[a->ctr].lumpnum, CR_DEFAULT, VPT_NONE); + } +} + + +// ==================================================================== +// WI_drawNum +// Purpose: Draws a number. If digits > 0, then use that many digits +// minimum, otherwise only use as many as necessary +// Args: x, y -- location +// n -- the number to be drawn +// digits -- number of digits minimum or zero +// Returns: new x position after drawing (note we are going to the left) +// CPhipps - static +static int WI_drawNum (int x, int y, int n, int digits) +{ + int fontwidth = num[0].width; + int neg; + int temp; + + if (digits < 0) + { + if (!n) + { + // make variable-length zeros 1 digit long + digits = 1; + } + else + { + // figure out # of digits in # + digits = 0; + temp = n; + + while (temp) + { + temp /= 10; + digits++; + } + } + } + + neg = n < 0; + if (neg) + n = -n; + + // if non-number, do not draw it + if (n == 1994) + return 0; + + // draw the new number + while (digits--) + { + x -= fontwidth; + // CPhipps - patch drawing updated + V_DrawNumPatch(x, y, FB, num[ n % 10 ].lumpnum, CR_DEFAULT, VPT_NONE); + n /= 10; + } + + // draw a minus sign if necessary + if (neg) + // CPhipps - patch drawing updated + V_DrawNamePatch(x-=8, y, FB, wiminus, CR_DEFAULT, VPT_NONE); + + return x; +} + + +// ==================================================================== +// WI_drawPercent +// Purpose: Draws a percentage, really just a call to WI_drawNum +// after putting a percent sign out there +// Args: x, y -- location +// p -- the percentage value to be drawn, no negatives +// Returns: void +// CPhipps - static +static void WI_drawPercent(int x, int y, int p) +{ + if (p < 0) + return; + + // CPhipps - patch drawing updated + V_DrawNamePatch(x, y, FB, percent, CR_DEFAULT, VPT_NONE); + WI_drawNum(x, y, p, -1); +} + + +// ==================================================================== +// WI_drawTime +// Purpose: Draws the level completion time or par time, or "Sucks" +// if 1 hour or more +// Args: x, y -- location +// t -- the time value to be drawn +// Returns: void +// +// CPhipps - static +// - largely rewritten to display hours and use slightly better algorithm + +static void WI_drawTime(int x, int y, int t) +{ + int n; + + if (t<0) + return; + + if (t < 100*60*60) + for(;;) { + n = t % 60; + t /= 60; + x = WI_drawNum(x, y, n, (t || n>9) ? 2 : 1) - V_NamePatchWidth(colon); + + // draw + if (t) + // CPhipps - patch drawing updated + V_DrawNamePatch(x, y, FB, colon, CR_DEFAULT, VPT_NONE); + else break; + } + else // "sucks" (maybe should be "addicted", even I've never had a 100 hour game ;) + V_DrawNamePatch(x - V_NamePatchWidth(sucks), + y, FB, sucks, CR_DEFAULT, VPT_NONE); +} + + +// ==================================================================== +// WI_End +// Purpose: Unloads data structures (inverse of WI_Start) +// Args: none +// Returns: void +// +void WI_End(void) +{ + if (deathmatch) + WI_endDeathmatchStats(); + else if (netgame) + WI_endNetgameStats(); + else + WI_endStats(); +} + + +// ==================================================================== +// WI_initNoState +// Purpose: Clear state, ready for end of level activity +// Args: none +// Returns: void +// +void WI_initNoState(void) +{ + state = NoState; + acceleratestage = 0; + cnt = 10; +} + + +// ==================================================================== +// WI_drawTimeStats +// Purpose: Put the times on the screen +// Args: time, total time, par time, in seconds +// Returns: void +// +// cph - pulled from WI_drawStats below + +static void WI_drawTimeStats(int cnt_time, int cnt_total_time, int cnt_par) +{ + V_DrawNamePatch(SP_TIMEX, SP_TIMEY, FB, time1, CR_DEFAULT, VPT_NONE); + WI_drawTime(320/2 - SP_TIMEX, SP_TIMEY, cnt_time); + + V_DrawNamePatch(SP_TIMEX, (SP_TIMEY+200)/2, FB, total, CR_DEFAULT, VPT_NONE); + WI_drawTime(320/2 - SP_TIMEX, (SP_TIMEY+200)/2, cnt_total_time); + + // Ty 04/11/98: redid logic: should skip only if with pwad but + // without deh patch + // killough 2/22/98: skip drawing par times on pwads + // Ty 03/17/98: unless pars changed with deh patch + + if (!(modifiedgame && !deh_pars)) + { + if (wbs->epsd < 3) + { + V_DrawNamePatch(320/2 + SP_TIMEX, SP_TIMEY, FB, par, CR_DEFAULT, VPT_NONE); + WI_drawTime(320 - SP_TIMEX, SP_TIMEY, cnt_par); + } + } +} + +// ==================================================================== +// WI_updateNoState +// Purpose: Cycle until end of level activity is done +// Args: none +// Returns: void +// +void WI_updateNoState(void) +{ + + WI_updateAnimatedBack(); + + if (!--cnt) + G_WorldDone(); +} + +static boolean snl_pointeron = false; + + +// ==================================================================== +// WI_initShowNextLoc +// Purpose: Prepare to show the next level's location +// Args: none +// Returns: void +// +void WI_initShowNextLoc(void) +{ + if ((gamemode != commercial) && (gamemap == 8)) { + G_WorldDone(); + return; + } + + state = ShowNextLoc; + acceleratestage = 0; + + // e6y: That was pretty easy - only a HEX editor and luck + // There is no more desync on ddt-tas.zip\e4tux231.lmp + // --------- tasdoom.idb --------- + // .text:00031194 loc_31194: ; CODE XREF: WI_updateStats+3A9j + // .text:00031194 mov ds:state, 1 + // .text:0003119E mov ds:acceleratestage, 0 + // .text:000311A8 mov ds:cnt, 3Ch + // nowhere no hide + if (compatibility_level == tasdoom_compatibility) + cnt = 60; + else + cnt = SHOWNEXTLOCDELAY * TICRATE; + + WI_initAnimatedBack(); +} + + +// ==================================================================== +// WI_updateShowNextLoc +// Purpose: Prepare to show the next level's location +// Args: none +// Returns: void +// +void WI_updateShowNextLoc(void) +{ + WI_updateAnimatedBack(); + + if (!--cnt || acceleratestage) + WI_initNoState(); + else + snl_pointeron = (cnt & 31) < 20; +} + + +// ==================================================================== +// WI_drawShowNextLoc +// Purpose: Show the next level's location on animated backgrounds +// Args: none +// Returns: void +// +void WI_drawShowNextLoc(void) +{ + int i; + int last; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + if ( gamemode != commercial) + { + if (wbs->epsd > 2) + { + WI_drawEL(); // "Entering..." if not E1 or E2 + return; + } + + last = (wbs->last == 8) ? wbs->next - 1 : wbs->last; + + // draw a splat on taken cities. + for (i=0 ; i<=last ; i++) + WI_drawOnLnode(i, &splat); + + // splat the secret level? + if (wbs->didsecret) + WI_drawOnLnode(8, &splat); + + // draw flashing ptr + if (snl_pointeron) + WI_drawOnLnode(wbs->next, yah); + } + + // draws which level you are entering.. + if ( (gamemode != commercial) + || wbs->next != 30) // check for MAP30 end game + WI_drawEL(); +} + +// ==================================================================== +// WI_drawNoState +// Purpose: Draw the pointer and next location +// Args: none +// Returns: void +// +void WI_drawNoState(void) +{ + snl_pointeron = true; + WI_drawShowNextLoc(); +} + +// ==================================================================== +// WI_fragSum +// Purpose: Calculate frags for this player based on the current totals +// of all the other players. Subtract self-frags. +// Args: playernum -- the player to be calculated +// Returns: the total frags for this player +// +int WI_fragSum(int playernum) +{ + int i; + int frags = 0; + + for (i=0 ; i 999) // Ty 03/17/98 3-digit frag count + dm_frags[i][j] = 999; + + if (dm_frags[i][j] < -999) + dm_frags[i][j] = -999; + + stillticking = true; + } + } + dm_totals[i] = WI_fragSum(i); + + if (dm_totals[i] > 999) + dm_totals[i] = 999; + + if (dm_totals[i] < -999) + dm_totals[i] = -999; // Ty 03/17/98 end 3-digit frag count + } + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + dm_state++; + } + } + else if (dm_state == 4) + { + if (acceleratestage) + { + S_StartSound(0, sfx_slop); + + if ( gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (dm_state & 1) + { + if (!--cnt_pause) + { + dm_state++; + cnt_pause = TICRATE; + } + } +} + + +// ==================================================================== +// WI_drawDeathmatchStats +// Purpose: Draw the stats on the screen in a matrix +// Args: none +// Returns: void +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +void WI_drawDeathmatchStats(void) +{ + int i; + int j; + int x; + int y; + int w; + + int lh; // line height + int halfface = V_NamePatchWidth(facebackp)/2; + + lh = WI_SPACINGY; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + WI_drawLF(); + + // draw stat titles (top line) + V_DrawNamePatch(DM_TOTALSX-V_NamePatchWidth(total)/2, + DM_MATRIXY-WI_SPACINGY+10, FB, total, CR_DEFAULT, VPT_NONE); + + V_DrawNamePatch(DM_KILLERSX, DM_KILLERSY, FB, killers, CR_DEFAULT, VPT_NONE); + V_DrawNamePatch(DM_VICTIMSX, DM_VICTIMSY, FB, victims, CR_DEFAULT, VPT_NONE); + + // draw P? + x = DM_MATRIXX + DM_SPACINGX; + y = DM_MATRIXY; + + for (i=0 ; i 'int' for cnt_kills, cnt_items and cnt_secret +// +// Original sources use 'int' type for cnt_kills instead of 'short' +// I don't know who have made change of type, but this change +// leads to desynch if 'kills' percentage is more than 32767. +// Actually PrBoom will be in an infinite cycle at calculation of +// percentage if the player will not press for acceleration, because +// the condition (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) +// will be always false in this case. +// +// If you will kill 800 monsters on MAP30 on Ultra-Violence skill and +// will not press , vanilla will count up to 80000%, but PrBoom +// will be in infinite cycle of counting: +// (0, 1, 2, ..., 32766, 32767, -32768, -32767, ..., -1, 0, 1, ...) +// Negative numbers will not be displayed. + +static int *cnt_kills; +static int *cnt_items; +static int *cnt_secret; +static int *cnt_frags; +static int dofrags; +static int ng_state; + +// ==================================================================== +// CPhipps - WI_endNetgameStats +// Purpose: Clean up coop game stats +// Args: none +// Returns: void +// +static void WI_endNetgameStats(void) +{ + free(cnt_frags); cnt_frags = NULL; + free(cnt_secret); cnt_secret = NULL; + free(cnt_items); cnt_items = NULL; + free(cnt_kills); cnt_kills = NULL; +} + +// ==================================================================== +// WI_initNetgameStats +// Purpose: Prepare for coop game stats +// Args: none +// Returns: void +// +void WI_initNetgameStats(void) +{ + int i; + + state = StatCount; + acceleratestage = 0; + ng_state = 1; + + cnt_pause = TICRATE; + + // CPhipps - allocate these dynamically, blank with calloc + cnt_kills = calloc(MAXPLAYERS, sizeof(*cnt_kills)); + cnt_items = calloc(MAXPLAYERS, sizeof(*cnt_items)); + cnt_secret= calloc(MAXPLAYERS, sizeof(*cnt_secret)); + cnt_frags = calloc(MAXPLAYERS, sizeof(*cnt_frags)); + + for (i=0 ; imaxkills; + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + + // killough 2/22/98: Make secrets = 100% if maxsecret = 0: + cnt_secret[i] = wbs->maxsecret ? + (plrs[i].ssecret * 100) / wbs->maxsecret : 100; + if (dofrags) + cnt_frags[i] = WI_fragSum(i); // we had frags + } + S_StartSound(0, sfx_barexp); // bang + ng_state = 10; + } + + if (ng_state == 2) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); // pop + + stillticking = false; + + for (i=0 ; i= (plrs[i].skills * 100) / wbs->maxkills) + cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills; + else + stillticking = true; // still got stuff to tally + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 4) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i=0 ; i= (plrs[i].sitems * 100) / wbs->maxitems) + cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state++; + } + } + else if (ng_state == 6) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i=0 ; i= (wbs->maxsecret ? (plrs[i].ssecret * 100) / wbs->maxsecret : compatibility_level < lxdoom_1_compatibility ? 0 : 100)) + cnt_secret[i] = wbs->maxsecret ? (plrs[i].ssecret * 100) / wbs->maxsecret : 100; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_barexp); + ng_state += 1 + 2*!dofrags; + } + } + else if (ng_state == 8) + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + stillticking = false; + + for (i=0 ; i= (fsum = WI_fragSum(i))) + cnt_frags[i] = fsum; + else + stillticking = true; + } + + if (!stillticking) + { + S_StartSound(0, sfx_pldeth); + ng_state++; + } + } + else if (ng_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + if ( gamemode == commercial ) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (ng_state & 1) + { + if (!--cnt_pause) + { + ng_state++; + cnt_pause = TICRATE; + } + } +} + + +// ==================================================================== +// WI_drawNetgameStats +// Purpose: Put the coop stats on the screen +// Args: none +// Returns: void +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +void WI_drawNetgameStats(void) +{ + int i; + int x; + int y; + int pwidth = V_NamePatchWidth(percent); + int fwidth = V_NamePatchWidth(facebackp); + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + // draw stat titles (top line) + V_DrawNamePatch(NG_STATSX+NG_SPACINGX-V_NamePatchWidth(kills), + NG_STATSY, FB, kills, CR_DEFAULT, VPT_NONE); + + V_DrawNamePatch(NG_STATSX+2*NG_SPACINGX-V_NamePatchWidth(items), + NG_STATSY, FB, items, CR_DEFAULT, VPT_NONE); + + V_DrawNamePatch(NG_STATSX+3*NG_SPACINGX-V_NamePatchWidth(secret), + NG_STATSY, FB, secret, CR_DEFAULT, VPT_NONE); + + if (dofrags) + V_DrawNamePatch(NG_STATSX+4*NG_SPACINGX-V_NamePatchWidth(frags), + NG_STATSY, FB, frags, CR_DEFAULT, VPT_NONE); + + // draw stats + y = NG_STATSY + V_NamePatchHeight(kills); + + for (i=0 ; itotaltimes / TICRATE, wbs->partime / TICRATE); +} + +static int sp_state; + +// ==================================================================== +// WI_initStats +// Purpose: Get ready for single player stats +// Args: none +// Returns: void +// Comment: Seems like we could do all these stats in a more generic +// set of routines that weren't duplicated for dm, coop, sp +// +void WI_initStats(void) +{ + state = StatCount; + acceleratestage = 0; + sp_state = 1; + + // CPhipps - allocate (awful code, I know, but saves changing it all) and initialise + *(cnt_kills = malloc(sizeof(*cnt_kills))) = + *(cnt_items = malloc(sizeof(*cnt_items))) = + *(cnt_secret= malloc(sizeof(*cnt_secret))) = -1; + cnt_time = cnt_par = cnt_total_time = -1; + cnt_pause = TICRATE; + + WI_initAnimatedBack(); +} + +// ==================================================================== +// WI_updateStats +// Purpose: Calculate solo stats +// Args: none +// Returns: void +// +void WI_updateStats(void) +{ + WI_updateAnimatedBack(); + + if (acceleratestage && sp_state != 10) + { + acceleratestage = 0; + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + + // killough 2/22/98: Make secrets = 100% if maxsecret = 0: + cnt_secret[0] = (wbs->maxsecret ? + (plrs[me].ssecret * 100) / wbs->maxsecret : 100); + + cnt_total_time = wbs->totaltimes / TICRATE; + cnt_time = plrs[me].stime / TICRATE; + cnt_par = wbs->partime / TICRATE; + S_StartSound(0, sfx_barexp); + sp_state = 10; + } + + if (sp_state == 2) + { + cnt_kills[0] += 2; + + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills) + { + cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 4) + { + cnt_items[0] += 2; + + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems) + { + cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems; + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 6) + { + cnt_secret[0] += 2; + + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + + // killough 2/22/98: Make secrets = 100% if maxsecret = 0: + if ((!wbs->maxsecret && compatibility_level < lxdoom_1_compatibility) || + cnt_secret[0] >= (wbs->maxsecret ? + (plrs[me].ssecret * 100) / wbs->maxsecret : 100)) + { + cnt_secret[0] = (wbs->maxsecret ? + (plrs[me].ssecret * 100) / wbs->maxsecret : 100); + S_StartSound(0, sfx_barexp); + sp_state++; + } + } + else if (sp_state == 8) + { + int time_done, total_done, par_done; // finished counting? + int time_just, total_just, par_just; // _just_ finished counting? + + // Test if counter is below target, then increment, and test again + // If the first is false and the second true, we've just gone over +#define UPDATE_COUNT(count, inc, target, done, just) \ + (just) = ((count) >= (target)); \ + (count) += (inc); \ + (done) = ((count) >= (target)); \ + (just) = (!(just) && (done)); \ + if ((done)) (count) = (target); + + UPDATE_COUNT(cnt_time, 3, (plrs[me].stime / TICRATE), + time_done, time_just); + UPDATE_COUNT(cnt_total_time, 3, (wbs->totaltimes / TICRATE), + total_done, total_just); + UPDATE_COUNT(cnt_par, 3, (wbs->partime / TICRATE), + par_done, par_just); +#undef UPDATE_COUNT + + // If all three timers are finished, play explosion and bump state + // Ignore total time or par time if not counted or displayed + if (time_done + && (total_done || compatibility_level < lxdoom_1_compatibility) + && (par_done || (modifiedgame && !deh_pars))) + { + // Only play explosion once when counter has just finished + if (time_just + || (total_just && compatibility_level >= lxdoom_1_compatibility) + || (par_just && (!modifiedgame || deh_pars))) + S_StartSound(0, sfx_barexp); + + // Fast-forward total time in old complevels + if (compatibility_level < lxdoom_1_compatibility) + cnt_total_time = (wbs->totaltimes / TICRATE); + + // Only bump state if par timer has actually finished (demosync) + if (par_done) + { + sp_state++; + // Make sure all the counters have reached their targets + cnt_time = (plrs[me].stime / TICRATE); + cnt_total_time = (wbs->totaltimes / TICRATE); + cnt_par = (wbs->partime / TICRATE); + } + } + // Otherwise, if any of the timers are still going, play gunshots + else + { + if (!(bcnt&3)) + S_StartSound(0, sfx_pistol); + } + } + else if (sp_state == 10) + { + if (acceleratestage) + { + S_StartSound(0, sfx_sgcock); + + if (gamemode == commercial) + WI_initNoState(); + else + WI_initShowNextLoc(); + } + } + else if (sp_state & 1) + { + if (!--cnt_pause) + { + sp_state++; + cnt_pause = TICRATE; + } + } +} + +// ==================================================================== +// WI_drawStats +// Purpose: Put the solo stats on the screen +// Args: none +// Returns: void +// +// proff/nicolas 09/20/98 -- changed for hi-res +// CPhipps - patch drawing updated +void WI_drawStats(void) +{ + // line height + int lh; + + lh = (3*num[0].height)/2; + + WI_slamBackground(); + + // draw animated background + WI_drawAnimatedBack(); + + WI_drawLF(); + + V_DrawNamePatch(SP_STATSX, SP_STATSY, FB, kills, CR_DEFAULT, VPT_NONE); + if (cnt_kills) + WI_drawPercent(320 - SP_STATSX, SP_STATSY, cnt_kills[0]); + + V_DrawNamePatch(SP_STATSX, SP_STATSY+lh, FB, items, CR_DEFAULT, VPT_NONE); + if (cnt_items) + WI_drawPercent(320 - SP_STATSX, SP_STATSY+lh, cnt_items[0]); + + V_DrawNamePatch(SP_STATSX, SP_STATSY+2*lh, FB, sp_secret, CR_DEFAULT, VPT_NONE); + if (cnt_secret) + WI_drawPercent(320 - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]); + + WI_drawTimeStats(cnt_time, cnt_total_time, cnt_par); +} + +// ==================================================================== +// WI_checkForAccelerate +// Purpose: See if the player has hit either the attack or use key +// or mouse button. If so we set acceleratestage to 1 and +// all those display routines above jump right to the end. +// Args: none +// Returns: void +// +void WI_checkForAccelerate(void) +{ + int i; + player_t *player; + + // check for button presses to skip delays + for (i=0, player = players ; icmd.buttons & BT_ATTACK) + { + if (!player->attackdown) + acceleratestage = 1; + player->attackdown = true; + } + else + player->attackdown = false; + + if (player->cmd.buttons & BT_USE) + { + if (!player->usedown) + acceleratestage = 1; + player->usedown = true; + } + else + player->usedown = false; + } + } +} + +// ==================================================================== +// WI_Ticker +// Purpose: Do various updates every gametic, for stats, animation, +// checking that intermission music is running, etc. +// Args: none +// Returns: void +// +void WI_Ticker(void) +{ + // counter for general background animation + bcnt++; + + if (bcnt == 1) + { + // intermission music + if ( gamemode == commercial ) + S_ChangeMusic(mus_dm2int, true); + else + S_ChangeMusic(mus_inter, true); + } + + WI_checkForAccelerate(); + + switch (state) + { + case StatCount: + if (deathmatch) WI_updateDeathmatchStats(); + else if (netgame) WI_updateNetgameStats(); + else WI_updateStats(); + break; + + case ShowNextLoc: + WI_updateShowNextLoc(); + break; + + case NoState: + WI_updateNoState(); + break; + } +} + +/* ==================================================================== + * WI_loadData + * Purpose: Initialize intermission data such as background graphics, + * patches, map names, etc. + * Args: none + * Returns: void + * + * CPhipps - modified for new wad lump handling. + * - no longer preload most graphics, other funcs can use + * them by name + */ + +void WI_loadData(void) +{ + int i; + int j; + char name[9]; // limited to 8 characters + anim_t* a; + + if (gamemode != commercial) + { + if (wbs->epsd < 3) + { + for (j=0;jepsd];j++) + { + a = &anims[wbs->epsd][j]; + for (i=0;inanims;i++) + { + // MONDO HACK! + if (wbs->epsd != 1 || j != 8) + { + // animations + sprintf(name, "WIA%d%.2d%.2d", wbs->epsd, j, i); + R_SetPatchNum(&a->p[i], name); + } + else + { + // HACK ALERT! + a->p[i] = anims[1][4].p[i]; + } + } + } + } + } + + for (i=0;i<10;i++) + { + // numbers 0-9 + sprintf(name, "WINUM%d", i); + R_SetPatchNum(&num[i], name); + } +} + + +// ==================================================================== +// WI_Drawer +// Purpose: Call the appropriate stats drawing routine depending on +// what kind of game is being played (DM, coop, solo) +// Args: none +// Returns: void +// +void WI_Drawer (void) +{ + switch (state) + { + case StatCount: + if (deathmatch) + WI_drawDeathmatchStats(); + else if (netgame) + WI_drawNetgameStats(); + else + WI_drawStats(); + break; + + case ShowNextLoc: + WI_drawShowNextLoc(); + break; + + case NoState: + WI_drawNoState(); + break; + } +} + + +// ==================================================================== +// WI_initVariables +// Purpose: Initialize the intermission information structure +// Note: wbstartstruct_t is defined in d_player.h +// Args: wbstartstruct -- pointer to the structure with the data +// Returns: void +// +void WI_initVariables(wbstartstruct_t* wbstartstruct) +{ + + wbs = wbstartstruct; + + acceleratestage = 0; + cnt = bcnt = 0; + firstrefresh = 1; + me = wbs->pnum; + plrs = wbs->plyr; + + if (!wbs->maxkills) + wbs->maxkills = 1; // probably only useful in MAP30 + + if (!wbs->maxitems) + wbs->maxitems = 1; + + if ( gamemode != retail ) + if (wbs->epsd > 2) + wbs->epsd -= 3; +} + +// ==================================================================== +// WI_Start +// Purpose: Call the various init routines +// Note: wbstartstruct_t is defined in d_player.h +// Args: wbstartstruct -- pointer to the structure with the +// intermission data +// Returns: void +// +void WI_Start(wbstartstruct_t* wbstartstruct) +{ + WI_initVariables(wbstartstruct); + WI_loadData(); + + if (deathmatch) + WI_initDeathmatchStats(); + else if (netgame) + WI_initNetgameStats(); + else + WI_initStats(); +} diff --git a/src/wi_stuff.h b/src/wi_stuff.h new file mode 100644 index 00000000..c3363c82 --- /dev/null +++ b/src/wi_stuff.h @@ -0,0 +1,64 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Intermission screens. + * + *-----------------------------------------------------------------------------*/ + +#ifndef __WI_STUFF__ +#define __WI_STUFF__ + +//#include "v_video.h" + +#include "doomdef.h" + +// States for the intermission + +typedef enum +{ + NoState = -1, + StatCount, + ShowNextLoc + +} stateenum_t; + +// Called by main loop, animate the intermission. +void WI_Ticker (void); + +// Called by main loop, +// draws the intermission directly into the screen buffer. +void WI_Drawer (void); + +// Setup for an intermission screen. +void WI_Start(wbstartstruct_t* wbstartstruct); + +// Release intermission screen memory +void WI_End(void); + +#endif diff --git a/src/z_bmalloc.c b/src/z_bmalloc.c new file mode 100644 index 00000000..f45eecd9 --- /dev/null +++ b/src/z_bmalloc.c @@ -0,0 +1,115 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * This is designed to be a fast allocator for small, regularly used block sizes + *----------------------------------------------------------------------------- + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "doomtype.h" +#include "z_zone.h" +#include "z_bmalloc.h" +#include "lprintf.h" + +typedef struct bmalpool_s { + struct bmalpool_s *nextpool; + size_t blocks; + byte used[0]; +} bmalpool_t; + +inline static void* getelem(bmalpool_t *p, size_t size, size_t n) +{ + return (((byte*)p) + sizeof(bmalpool_t) + sizeof(byte)*(p->blocks) + size*n); +} + +inline static PUREFUNC int iselem(const bmalpool_t *pool, size_t size, const void* p) +{ + // CPhipps - need portable # of bytes between pointers + int dif = (const char*)p - (const char*)pool; + + dif -= sizeof(bmalpool_t); + dif -= pool->blocks; + if (dif<0) return -1; + dif /= size; + return (((size_t)dif >= pool->blocks) ? -1 : dif); +} + +enum { unused_block = 0, used_block = 1}; + +void* Z_BMalloc(struct block_memory_alloc_s *pzone) +{ + register bmalpool_t **pool = (bmalpool_t **)&(pzone->firstpool); + while (*pool != NULL) { + byte *p = memchr((*pool)->used, unused_block, (*pool)->blocks); // Scan for unused marker + if (p) { + int n = p - (*pool)->used; + (*pool)->used[n] = used_block; + return getelem(*pool, pzone->size, n); + } else + pool = &((*pool)->nextpool); + } + { + // Nothing available, must allocate a new pool + bmalpool_t *newpool; + + // CPhipps: Allocate new memory, initialised to 0 + + *pool = newpool = Z_Calloc(sizeof(*newpool) + (sizeof(byte) + pzone->size)*(pzone->perpool), + 1, pzone->tag, NULL); + newpool->nextpool = NULL; // NULL = (void*)0 so this is redundant + + // Return element 0 from this pool to satisfy the request + newpool->used[0] = used_block; + newpool->blocks = pzone->perpool; + return getelem(newpool, pzone->size, 0); + } +} + +void Z_BFree(struct block_memory_alloc_s *pzone, void* p) +{ + register bmalpool_t **pool = (bmalpool_t**)&(pzone->firstpool); + + while (*pool != NULL) { + int n = iselem(*pool, pzone->size, p); + if (n >= 0) { + (*pool)->used[n] = unused_block; + if (memchr(((*pool)->used), used_block, (*pool)->blocks) == NULL) { + // Block is all unused, can be freed + bmalpool_t *oldpool = *pool; + *pool = (*pool)->nextpool; + Z_Free(oldpool); + } + return; + } else pool = &((*pool)->nextpool); + } + I_Error("Z_BFree: Free not in zone %s", pzone->desc); +} diff --git a/src/z_bmalloc.h b/src/z_bmalloc.h new file mode 100644 index 00000000..ed30c9fb --- /dev/null +++ b/src/z_bmalloc.h @@ -0,0 +1,52 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Block memory allocator + * This is designed to be a fast allocator for small, regularly used block sizes + *-----------------------------------------------------------------------------*/ + +struct block_memory_alloc_s { + void *firstpool; + size_t size; + size_t perpool; + int tag; + const char *desc; +}; + +#define DECLARE_BLOCK_MEMORY_ALLOC_ZONE(name) extern struct block_memory_alloc_s name +#define IMPLEMENT_BLOCK_MEMORY_ALLOC_ZONE(name, size, tag, num, desc) \ +struct block_memory_alloc_s name = { NULL, size, num, tag, desc} +#define NULL_BLOCK_MEMORY_ALLOC_ZONE(name) name.firstpool = NULL + +void* Z_BMalloc(struct block_memory_alloc_s *pzone); + +inline static void* Z_BCalloc(struct block_memory_alloc_s *pzone) +{ void *p = Z_BMalloc(pzone); memset(p,0,pzone->size); return p; } + +void Z_BFree(struct block_memory_alloc_s *pzone, void* p); diff --git a/src/z_zone.c b/src/z_zone.c new file mode 100644 index 00000000..55f937ad --- /dev/null +++ b/src/z_zone.c @@ -0,0 +1,346 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Zone Memory Allocation. Neat. + * + * Neat enough to be rewritten by Lee Killough... + * + * Must not have been real neat :) + * + * Made faster and more general, and added wrappers for all of Doom's + * memory allocation functions, including malloc() and similar functions. + * Added line and file numbers, in case of error. Added performance + * statistics and tunables. + *----------------------------------------------------------------------------- + */ + + +// use config.h if autoconf made one -- josh +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "z_zone.h" +#include "doomstat.h" +#include "m_argv.h" +#include "v_video.h" +#include "g_game.h" +#include "lprintf.h" + +// Tunables + +// Alignment of zone memory (benefit may be negated by HEADER_SIZE, CHUNK_SIZE) +#define CACHE_ALIGN 32 + +// Minimum chunk size at which blocks are allocated +#define CHUNK_SIZE 32 + +// Minimum size a block must be to become part of a split +#define MIN_BLOCK_SPLIT (1024) + +// How much RAM to leave aside for other libraries +#define LEAVE_ASIDE (128*1024) + +// Amount to subtract when retrying failed attempts to allocate initial pool +#define RETRY_AMOUNT (256*1024) + +// signature for block header +#define ZONEID 0x931d4a11 + +// Number of mallocs & frees kept in history buffer (must be a power of 2) +#define ZONE_HISTORY 4 + +// End Tunables + +typedef struct memblock { + +#ifdef ZONEIDCHECK + unsigned id; +#endif + + struct memblock *next,*prev; + size_t size; + void **user; + unsigned char tag; + +} memblock_t; + +/* size of block header + * cph - base on sizeof(memblock_t), which can be larger than CHUNK_SIZE on + * 64bit architectures */ +static const size_t HEADER_SIZE = (sizeof(memblock_t)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); + +static memblock_t *blockbytag[PU_MAX]; + +// 0 means unlimited, any other value is a hard limit +//static int memory_size = 8192*1024; +static int memory_size = 0; +static int free_memory = 0; + + +void Z_DumpHistory(char *buf) +{ +} + + +void Z_Close(void) +{ +#if 0 + (free)(zonebase); + zone = rover = zonebase = NULL; +#endif +} + +void Z_Init(void) +{ +} + +/* Z_Malloc + * You can pass a NULL user if the tag is < PU_PURGELEVEL. + * + * cph - the algorithm here was a very simple first-fit round-robin + * one - just keep looping around, freeing everything we can until + * we get a large enough space + * + * This has been changed now; we still do the round-robin first-fit, + * but we only free the blocks we actually end up using; we don't + * free all the stuff we just pass on the way. + */ + +void *(Z_Malloc)(size_t size, int tag, void **user) +{ + memblock_t *block = NULL; + +#ifdef ZONEIDCHECK + if (tag >= PU_PURGELEVEL && !user) + I_Error ("Z_Malloc: An owner is required for purgable blocks" + ); +#endif + + if (!size) + return user ? *user = NULL : NULL; // malloc(0) returns NULL + + size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size + + if (memory_size > 0 && ((free_memory + memory_size) < (int)(size + HEADER_SIZE))) + { + memblock_t *end_block; + block = blockbytag[PU_CACHE]; + if (block) + { + end_block = block->prev; + while (1) + { + memblock_t *next = block->next; + (Z_Free)((char *) block + HEADER_SIZE); + if (((free_memory + memory_size) >= (int)(size + HEADER_SIZE)) || (block == end_block)) + break; + block = next; // Advance to next block + } + } + block = NULL; + } + + while (!(block = (malloc)(size + HEADER_SIZE))) { + if (!blockbytag[PU_CACHE]) + I_Error ("Z_Malloc: Failure trying to allocate %lu bytes" + ,(unsigned long) size + ); + Z_FreeTags(PU_CACHE,PU_CACHE); + } + + if (!blockbytag[tag]) + { + blockbytag[tag] = block; + block->next = block->prev = block; + } + else + { + blockbytag[tag]->prev->next = block; + block->prev = blockbytag[tag]->prev; + block->next = blockbytag[tag]; + blockbytag[tag]->prev = block; + } + + block->size = size; + + free_memory -= block->size; + +#ifdef ZONEIDCHECK + block->id = ZONEID; // signature required in block header +#endif + block->tag = tag; // tag + block->user = user; // user + block = (memblock_t *)((char *) block + HEADER_SIZE); + if (user) // if there is a user + *user = block; // set user to point to new block + + return block; +} + +void (Z_Free)(void *p) +{ + memblock_t *block = (memblock_t *)((char *) p - HEADER_SIZE); + + if (!p) + return; + + +#ifdef ZONEIDCHECK + if (block->id != ZONEID) + I_Error("Z_Free: freed a pointer without ZONEID" + ); + block->id = 0; // Nullify id so another free fails +#endif + + if (block->user) // Nullify user if one exists + *block->user = NULL; + + if (block == block->next) + blockbytag[block->tag] = NULL; + else + if (blockbytag[block->tag] == block) + blockbytag[block->tag] = block->next; + block->prev->next = block->next; + block->next->prev = block->prev; + + free_memory += block->size; + + (free)(block); +} + +void (Z_FreeTags)(int lowtag, int hightag) +{ + + if (lowtag <= PU_FREE) + lowtag = PU_FREE+1; + + if (hightag > PU_CACHE) + hightag = PU_CACHE; + + for (;lowtag <= hightag; lowtag++) + { + memblock_t *block, *end_block; + block = blockbytag[lowtag]; + if (!block) + continue; + end_block = block->prev; + while (1) + { + memblock_t *next = block->next; + (Z_Free)((char *) block + HEADER_SIZE); + if (block == end_block) + break; + block = next; // Advance to next block + } + } +} + +void (Z_ChangeTag)(void *ptr, int tag + ) +{ + memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); + + // proff - added sanity check, this can happen when an empty lump is locked + if (!ptr) + return; + + // proff - do nothing if tag doesn't differ + if (tag == block->tag) + return; + +#ifdef ZONEIDCHECK + if (block->id != ZONEID) + I_Error ("Z_ChangeTag: freed a pointer without ZONEID" + ); + + if (tag >= PU_PURGELEVEL && !block->user) + I_Error ("Z_ChangeTag: an owner is required for purgable blocks\n" + ); + +#endif // ZONEIDCHECK + + if (block == block->next) + blockbytag[block->tag] = NULL; + else + if (blockbytag[block->tag] == block) + blockbytag[block->tag] = block->next; + block->prev->next = block->next; + block->next->prev = block->prev; + + if (!blockbytag[tag]) + { + blockbytag[tag] = block; + block->next = block->prev = block; + } + else + { + blockbytag[tag]->prev->next = block; + block->prev = blockbytag[tag]->prev; + block->next = blockbytag[tag]; + blockbytag[tag]->prev = block; + } + + block->tag = tag; +} + +void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user + ) +{ + void *p = (Z_Malloc)(n, tag, user DA(file, line)); + if (ptr) + { + memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); + memcpy(p, ptr, n <= block->size ? n : block->size); + (Z_Free)(ptr DA(file, line)); + if (user) // in case Z_Free nullified same user + *user=p; + } + return p; +} + +void *(Z_Calloc)(size_t n1, size_t n2, int tag, void **user + ) +{ + return + (n1*=n2) ? memset((Z_Malloc)(n1, tag, user DA(file, line)), 0, n1) : NULL; +} + +char *(Z_Strdup)(const char *s, int tag, void **user + ) +{ + return strcpy((Z_Malloc)(strlen(s)+1, tag, user DA(file, line)), s); +} + +void (Z_CheckHeap)(void) +{ +} diff --git a/src/z_zone.h b/src/z_zone.h new file mode 100644 index 00000000..c98e79dd --- /dev/null +++ b/src/z_zone.h @@ -0,0 +1,97 @@ +/* Emacs style mode select -*- C++ -*- + *----------------------------------------------------------------------------- + * + * + * PrBoom: a Doom port merged with LxDoom and LSDLDoom + * based on BOOM, a modified and improved DOOM engine + * Copyright (C) 1999 by + * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman + * Copyright (C) 1999-2000 by + * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze + * Copyright 2005, 2006 by + * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * DESCRIPTION: + * Zone Memory Allocation, perhaps NeXT ObjectiveC inspired. + * Remark: this was the only stuff that, according + * to John Carmack, might have been useful for + * Quake. + * + * Rewritten by Lee Killough, though, since it was not efficient enough. + * + *---------------------------------------------------------------------*/ + +#ifndef __Z_ZONE__ +#define __Z_ZONE__ + +#ifndef __GNUC__ +#define __attribute__(x) +#endif + +// Include system definitions so that prototypes become +// active before macro replacements below are in effect. + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include +#include + +// ZONE MEMORY +// PU - purge tags. + +enum {PU_FREE, PU_STATIC, PU_SOUND, PU_MUSIC, PU_LEVEL, PU_LEVSPEC, PU_CACHE, + /* Must always be last -- killough */ PU_MAX}; + +#define PU_PURGELEVEL PU_CACHE /* First purgable tag's level */ + +#define DA(x,y) +#define DAC(x,y) + +void *(Z_Malloc)(size_t size, int tag, void **ptr DA(const char *, int)); +void (Z_Free)(void *ptr DA(const char *, int)); +void (Z_FreeTags)(int lowtag, int hightag DA(const char *, int)); +void (Z_ChangeTag)(void *ptr, int tag DA(const char *, int)); +void (Z_Init)(void); +void Z_Close(void); +void *(Z_Calloc)(size_t n, size_t n2, int tag, void **user DA(const char *, int)); +void *(Z_Realloc)(void *p, size_t n, int tag, void **user DA(const char *, int)); +char *(Z_Strdup)(const char *s, int tag, void **user DA(const char *, int)); +void (Z_CheckHeap)(DAC(const char *,int)); // killough 3/22/98: add file/line info +void Z_DumpHistory(char *); + +// Remove all definitions before including system definitions + +#undef malloc +#undef free +#undef realloc +#undef calloc +#undef strdup + +#define malloc(n) Z_Malloc(n,PU_STATIC,0) +#define free(p) Z_Free(p) +#define realloc(p,n) Z_Realloc(p,n,PU_STATIC,0) +#define calloc(n1,n2) Z_Calloc(n1,n2,PU_STATIC,0) +#define strdup(s) Z_Strdup(s,PU_STATIC,0) + + +void Z_ZoneHistory(char *); + +#endif diff --git a/tests/boxes.pl b/tests/boxes.pl new file mode 100755 index 00000000..5da071f2 --- /dev/null +++ b/tests/boxes.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $SQUARES = 100; +my $WAD = "boxes.wad"; +my @LMPS = ("MAP01", "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", + "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP"); + +my %lmp; + +for (my ($s,$i) = (0,0); $i < $SQUARES; $i++) { + for (my $j = 0; $j < $SQUARES; $j++) { + rect($s++, 16*$i, 16*$j, 16*$i+8, 16*$j+8); + } +} + +$lmp{"THINGS"} = pack("S5", 4, 4, 0, 1, 7); + +open(F, ">$WAD"); +my $ptr = 12 + 16*scalar @LMPS; +print F pack("a4L2", "PWAD", scalar @LMPS, 12); +for (@LMPS) { + if (exists $lmp{$_}) { + print F pack("L2a8", $ptr, length $lmp{$_}, $_); + $ptr += length $lmp{$_}; + } else { + print F pack("L2a8", $ptr, 0, $_); + } +} +for (@LMPS) { print F $lmp{$_} if exists $lmp{$_}; } +close F; + +sub rect +{ + my ($n, $x0, $y0, $x1, $y1) = @_; + + $lmp{"SECTORS"}.=pack("S2a8a8S3", + 0, 128, "FLOOR4_8", "CEIL3_5", 192, 0, 0); + + $lmp{"VERTEXES"}.=pack("S2"x4, + $x0, $y0, + $x0, $y1, + $x1, $y1, + $x1, $y0); + + $lmp{"LINEDEFS"}.=pack("S7"x4, + 4*$n, 4*$n+1, 1, 0, 0, 4*$n, 65535, + 4*$n+1, 4*$n+2, 1, 0, 0, 4*$n+1, 65535, + 4*$n+2, 4*$n+3, 1, 0, 0, 4*$n+2, 65535, + 4*$n+3, 4*$n, 1, 0, 0, 4*$n+3, 65535); + + $lmp{"SIDEDEFS"}.=pack("S2a8a8a8S"x4, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n); +} + +__END__ diff --git a/tests/crosses.pl b/tests/crosses.pl new file mode 100755 index 00000000..825f62c3 --- /dev/null +++ b/tests/crosses.pl @@ -0,0 +1,76 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +my $grid = shift; +$grid = 75 if (!defined($grid)); + +my $WAD = "crosses.wad"; +my @LMPS = ("MAP01", "THINGS", "LINEDEFS", "SIDEDEFS", "VERTEXES", + "SEGS", "SSECTORS", "NODES", "SECTORS", "REJECT", "BLOCKMAP"); + +my %lmp; + +for (my ($s,$i) = (0,0); $i < $grid; $i++) { + for (my $j = 0; $j < $grid; $j++) { + rect($s++, 80*$i, 80*$j, 80*$i+64, 80*$j+64); + } +} + +$lmp{"THINGS"} = pack("S5", 32, 32, 0, 1, 7); + +open(F, ">$WAD"); +my $ptr = 12 + 16*scalar @LMPS; +print F pack("a4L2", "PWAD", scalar @LMPS, 12); +for (@LMPS) { + if (exists $lmp{$_}) { + print F pack("L2a8", $ptr, length $lmp{$_}, $_); + $ptr += length $lmp{$_}; + } else { + print F pack("L2a8", $ptr, 0, $_); + } +} +for (@LMPS) { print F $lmp{$_} if exists $lmp{$_}; } +close F; + +sub rect +{ + my ($n, $x0, $y0, $x1, $y1) = @_; + + $lmp{"SECTORS"}.=pack("S2a8a8S3", + 0, 128, "FLOOR4_8", "CEIL3_5", 192, 0, 0); + + $lmp{"VERTEXES"}.=pack("S2"x5, + $x0, $y0, + $x0, $y1, + $x1, $y1, + $x1, $y0, + ($x0+$x1)/2, ($y0+$y1)/2); + + $lmp{"LINEDEFS"}.=pack("S7"x8, + 5*$n, 5*$n+1, 1, 0, 0, 12*$n, 65535, + 5*$n+1, 5*$n+2, 1, 0, 0, 12*$n+1, 65535, + 5*$n+2, 5*$n+3, 1, 0, 0, 12*$n+2, 65535, + 5*$n+3, 5*$n, 1, 0, 0, 12*$n+3, 65535, + 5*$n, 5*$n+4, 4, 0, 0, 12*$n+4, 12*$n+5, + 5*$n+1, 5*$n+4, 4, 0, 0, 12*$n+6, 12*$n+7, + 5*$n+2, 5*$n+4, 4, 0, 0, 12*$n+8, 12*$n+9, + 5*$n+3, 5*$n+4, 4, 0, 0, 12*$n+10,12*$n+11); + + $lmp{"SIDEDEFS"}.=pack("S2a8a8a8S"x12, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "STARTAN3", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n, + 0, 0, "-", "-", "-", $n); +} + +__END__ diff --git a/tests/demo-testing.csv b/tests/demo-testing.csv new file mode 100644 index 00000000..f50eb83a --- /dev/null +++ b/tests/demo-testing.csv @@ -0,0 +1,24 @@ +"Compat level tested","IWAD","PWAD","PWAD url","Demo","Demo URL","Player","Reason","Bug URL","Bug ref" +"ultdoom","doom.wad",,,"DEMO4",,,"Desyncs with most ports — the correct ending has the marine killed by the imp standing below him and doing a twisting dive into the acid",, +"finaldoom","plutonia.wad",,,"DEMO1",,,"Barrel stuck in ceiling in second courtyard causes desync with Boom/MBF","mbf-bugs.html","Compatibility bug in T_MovePlane" +"ultdoom","doom.wad",,,"n4m1-035.lmp","COMPETN/doom/nmare/n4m1-035.zip","Drew DeVore","telefrag p_map.c overlapping globals bug","mbf-bugs.html","Overlapping uses of global variables in p_map.c" +"finaldoom","plutonia.wad",,,"pl27-051.lmp","COMPETN/plutonia/speed/pl27-051.zip","Andre Budko","final doom teleport height emulation","mbf-bugs.html","Final Doom teleport dome desyncs" +"doom2_19","doom2.wad",,,"30cnvin.lmp","COMPETN/doom2/coop/movies/30cn3519.zip","Vincent Catalaa, Jim Leonard","intermission screen desync at end of MAP29 in prboom 2.2.4","mbf-bugs.html","Intermission screen secrets desync" +"doom2_19","doom2.wad",,,"30cn-xv.lmp","COMPETN/doom2/coop/movies/30cn3519.zip","Vincent Catalaa, Jim Leonard","intermission screen desync at end of MAP29 in prboom 2.2.4","mbf-bugs.html","Intermission screen secrets desync" +"doom2_19","doom2.wad",,,"lv19-509.lmp","COMPETN/doom2/max/lv19-509.zip","Radek Pecka","Prboom 2.3.0 switch bug",, +"doom2_19","doom2.wad","mm.wad","IDGAMES/themes/mm/mm_allup.zip","DEMO3",,,"Third demo failed with lxdoom 1.3.0 LOS optimisations",, +"doom2_19","doom2.wad","mm2.wad","IDGAMES/themes/mm/mm2.zip","DEMO1",,,"Failed for a while with lxdoom",, +"doom2_19","doom2.wad",,,"30ns6936.lmp","COMPETN/doom2/movie/30ns6936.zip","Henning Skogsto","MBF player bobbing bug","mbf-bugs.html","MBF player bobbing rewrite causes demo sync problems" +"doom2_19","doom2.wad","mm.wad","IDGAMES/themes/mm/mm_allup.zip","mm10-uv.lmp","IDGAMES/themes/mm/mmuvlmps.zip","Yonatan Donner","Stair building compat issues","mbf-bugs.html","Compatibility bug in EV_BuildStairs" +"doom2_19","doom2.wad","mm.wad","IDGAMES/themes/mm/mm_allup.zip","mm12-uv.lmp","IDGAMES/themes/mm/mmuvlmps.zip","Yonatan Donner","Stair building compat issues","mbf-bugs.html","Compatibility bug in EV_BuildStairs" +"doom2_19","doom2.wad","mm.wad","IDGAMES/themes/mm/mm_allup.zip","mm13-uv.lmp","IDGAMES/themes/mm/mmuvlmps.zip","Yonatan Donner","Stair building compat issues","mbf-bugs.html","Compatibility bug in EV_BuildStairs" +"doom2_19","doom2.wad","hr.wad","IDGAMES/themes/hr/hr.zip","hr06-uv.lmp","IDGAMES/themes/hr/hruvlmps.zip","Yonatan Donner","More LOS optimization bugs","mbf-bugs.html","Compatibility bug in P_CheckSight" +"doom2_19","doom2.wad","hr.wad","IDGAMES/themes/hr/hr.zip","hr18-348.lmp","COMPETN/pwads/hr/speed/hr18-348.zip","Chris Ratcliff","spechit overflows",, +"doom2_19","doom2.wad","hr.wad","IDGAMES/themes/hr/hr.zip","hr22-uv.lmp","IDGAMES/themes/hr/hruvlmp2.zip","Anders Johnsen","lost soul bouncing compatibility","mbf-bugs.html","Bouncing lost souls" +"doom2_19","doom2.wad","punisher.wad","IDGAMES/levels/doom2/p-r/punishr.zip","punisher.lmp","IDGAMES/levels/doom2/p-r/punishr.zip","Dario Casali","lost soul bouncing compatibility","mbf-bugs.html","Bouncing lost souls" +"doom2_19","doom2.wad","requiem.wad","IDGAMES/levels/doom2/megawads/requiem.zip","rq22-318.lmp","COMPETN/pwads/requiem/speed/rq22-318.zip","Adam Williamson","REJECT padding for buggy levels",, +"boom202","doom2.wad","sprike.wad","IDGAMES/levels/doom2/Ports/s-u/sprike.zip","sprike.lmp","IDGAMES/levels/doom2/Ports/s-u/sprike.zip","Simon Varseszeghy","Boom 2.02 test",, +"mbf","plutonia.wad",,,"plnm7104.lmp","SDA/tas/tas_id.zip","(TAS) Istvan Pataki","MBF test",, +"mbf","doom2.wad",,,"30uv1617.lmp","SDA/tas/tas_id.zip","(TAS) Marijo Sedlic","MBF test",, +"prboom22x","doom2.wad","hr.wad","IDGAMES/themes/hr/hr.zip","hrhmp.lmp","n/a","(TAS) me","Prboom 2.2.x with loads & saves test",, +"","doom.wad","uac_dead.wad","IDGAMES/levels/doom/s-u/uac_dead.zip","uac_dead.lmp","IDGAMES/lmps/doom/ultimate/uac_dead.zip","","",, diff --git a/tests/lmpwatch.pl b/tests/lmpwatch.pl new file mode 100755 index 00000000..50d097f2 --- /dev/null +++ b/tests/lmpwatch.pl @@ -0,0 +1,182 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Getopt::Std; $Getopt::Std::STANDARD_HELP_VERSION = 1; + +sub VERSION_MESSAGE +{ + print <<"EOF"; +lmpwatch.pl: a clone of PrBoom-Plus's automated demo playback system. +EOF +} + +sub HELP_MESSAGE +{ + print <<"EOF"; +Usage: $0 [OPTIONS] DEMO_FILES... + +Options: + -d x name of doom executable (try "echo" to test the program) + -e x any extra options to pass to doom + -p x name of patterns file from lmpwatch.zip + -s x colon- or space-separated search path, may contain wildcards + -t test demo sync (not fully implemented, needs engine-side support) + -v print lots of debugging output + +EOF +} + +getopts('d:e:p:s:tv', \my %opts) or do { + HELP_MESSAGE(); + die "$0: bad options\n" +}; +if (!@ARGV) { + HELP_MESSAGE(); + die "$0: no demos?\n"; +} + +my $verbose = defined($opts{'v'}); + +# name of doom executable +my $DOOM = (defined $opts{'d'}) ? $opts{'d'} : "prboom"; +# any extra options to pass? +my $extra = (defined $opts{'e'}) ? $opts{'e'} : ""; + +# are we playing back demos or testing them for demosync? +my $testing = defined($opts{'t'}); +# "batch mode" if you're testing a demo (turns off the renderer) +my $demo_opts = $testing ? "-nodraw -nosound -timedemo" : "-playdemo"; + +# paths to search for files, these will be subject to filename globbing +my @paths = do { + # expand colon- or space-separated string into list + my @s = defined($opts{'s'}) ? split(/[: ]+/, $opts{'s'}) : (); + + # expand paths that contain wildcards + map { glob $_ } grep { defined $_ } (@s, $ENV{"DOOMWADDIR"}, + "/usr/local/share/games/doom", "/usr/share/games/doom"); +}; +print "DIRS: @paths\n\n" if $verbose; + +# name of the patterns file from lmpwatch.zip +my $patsfile = do { + my $p = (defined $opts{'p'}) ? $opts{'p'} : "patterns.txt"; + path_expand($p, @paths); +} or die "$0: lmpwatch.zip patterns file is needed to use this program\n"; +print "PATTERNS: $patsfile\n\n" if $verbose; +open my ($PATTERNS), $patsfile; + +while (@ARGV) { + my $demo = path_expand(shift @ARGV, @paths) or next; + my ($comment, $iwad, @files) = parse_patterns($PATTERNS, $demo); + print "DEMO: $demo\nIWAD: $iwad\nFILES: @files\n" if $verbose; + + # filename globbing and switch prepending for wad files + $iwad = path_expand($iwad, @paths) or next; # not strictly necessary + @files = map { path_expand($_, @paths) or next; } @files; + my @deh = grep { /\.(deh|bex)$/i } @files; + unshift @deh, "-deh" if @deh; + my @wad = grep { /\.wad$/i } @files; + unshift @wad, "-file" if @wad; # should be "-merge" for chocolate-doom? + + my $command = sprintf("%s -iwad %s %s %s %s %s %s", + $DOOM, $iwad, "@wad", "@deh", $demo_opts, $demo, $extra); + print "RUNNING: $command\n" if $verbose; + + if ($testing) { + my @finished = test_demo($command); + # unfortunately there is not yet an easy way to test if the + # list of maps the player exited is correct. lmpwatch.zip is + # only meant for demo playback, not testing. + # just print out the names of maps that were completed + print "FINISHED: " if $verbose; + print join(" ", (split(m!/!, $demo))[-1], @finished), "\n"; + } else { + system($command); + } + print "\n" if $verbose; +} + +close $PATTERNS; + +exit 0; + +# -------------------------------------------------------------------------- + +# play back a demo, return list of maps the player finishes +# requires that the engine prints "FINISHED: " when a map is beaten +sub test_demo +{ + my ($command) = @_; + my @finished; + + # run the game and capture its output + open my ($doom), "$command 2>&1 |"; + # make a list of maps the player managed to exit + # this needs engine-side support + while (<$doom>) { + push @finished, $1 if m/^FINISHED: (.*)$/ + } + close $doom; + + return @finished; +} + +# parse lmpwatch.zip's patterns file to determine wads required to play a demo +sub parse_patterns +{ + my ($PATTERNS, $demofile) = @_; + my ($mask, $comment, $pattern, $iwad, @files); + + my ($demo) = (split m!/!,$demofile)[-1]; + + seek $PATTERNS, 0, 0; # rewind file to start + while (<$PATTERNS>) { + chomp; + # read valid key/value pairs + my ($key, $value) = m/^(\w+)\s+\"(.+)\"/; + next if !defined($key) || !defined($value); + + # set the pattern mask + $mask = $value, next if $key eq 'demo_patterns_mask'; + + # ignore keys that don't start with the pattern mask + next if !$mask || $key !~ m/^$mask/; + + # decode the pattern + ($comment, $pattern, my $files) = split(m!/!, $value); + # the first file is always the iwad, apparently + ($iwad, @files) = split(m!\|!, $files); + next if !$iwad; # no iwad?? + + # stop at the first match (conflict avoidance) + last if $demo =~ m/$pattern/ + } + + return ($comment, $iwad, @files); +} + +# from the list supplied, find the first directory a given filename is in +sub path_expand +{ + my $file = shift; + + # if it's already a valid filename, skip the searching + return $file if -f $file; + + print " SEARCHING: $file\n" if $verbose; + for my $dir (@_) { + my $path = "$dir/$file"; + print " TRYING: $path\n" if $verbose; + if (-f $path) { + print " FOUND: $file => $path\n" if $verbose; + return $path; + } + } + warn "$0: cannot find $file\n"; + return undef; +} + +# vim:set sts=2 sw=2 ts=8 et: diff --git a/tests/runtests.py b/tests/runtests.py new file mode 100755 index 00000000..6792f98a --- /dev/null +++ b/tests/runtests.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python +import csv +import codecs +import os +import urllib2 +import shutil +import zipfile +import sys +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO +try: + import subprocess +except ImportError: + subprocess = None +from pprint import pprint + +url_lookup = { + 'COMPETN': 'ftp://competn.doom2.net:8002/pub/compet-n', + 'IDGAMES': 'http://www.gamers.org/pub/idgames', + 'SDA': 'ftp://competn.doom2.net:8002/pub/sda', +} + +basepath = None + +def iterDemoSpecs(specs): + #reader = csv.reader(codecs.open(specs,"r","utf-8")) + reader = csv.reader(open(specs,"r")) + headers = tuple(reader.next()) + for row in reader: + row = [x.strip() for x in row] + spec = dict(zip(headers, row)) + if len(spec): + yield spec + +def getFileFromZip(filename, filepath, package): + names = [x for x in package.namelist() if x.lower()==filename.lower()] + if len(names) == 0: + for name in package.namelist(): + if not name.lower().endswith('.zip'): + continue + data = StringIO(package.read(name)) + try: + subpackage = zipfile.ZipFile(data, 'r') + except zipfile.BadZipfile: + continue + if getFileFromZip(filename, filepath, subpackage): + return True + return False + dst = open(filepath, 'wb') + dst.write(package.read(names[0])) + dst.close() + package.close() + return True + +def getPathToFile(name, path): + if path == 'n/a': + if os.path.exists(os.path.abspath(name)): + return os.path.abspath(name) + return None + if path == '': + return '' + if path != '': + parts = path.split('/') + dirpath = os.path.abspath(os.path.join(*parts[:-1])) + filename = parts[-1] + filepath = os.path.abspath(os.path.join(dirpath, name)) + if os.path.exists(filepath): + return filepath + if not os.path.exists(path): + if not os.path.exists(dirpath): + os.makedirs(dirpath) + parts[0] = url_lookup.get(parts[0], parts[0]) + url = '/'.join(parts) + if not url.startswith('http://') \ + and not url.startswith('ftp://'): + raise ValueError, "unknown url '%s'" % url + print "Fetching %s" % url + try: + request = urllib2.urlopen(url) + except urllib2.HTTPError: + print "Error retriving the file." + return None + dst = open(path, 'wb') + shutil.copyfileobj(request, dst) + dst.close() + request.close() + if filename != name: + if filename.lower().endswith('.zip'): + package = zipfile.ZipFile(path, 'r') + if not getFileFromZip(name, filepath, package): + return None + return filepath + +def runtest(iwad, demo, demopath, pwad): + executable = '' + iwadpath = '' + use_pipe = False + if sys.platform == 'win32': + os.chdir(os.path.join(basepath, 'release')) + executable = 'prboom' + iwadpath = iwad + use_pipe = False + elif sys.platform == 'darwin': + os.chdir(os.path.join(basepath, 'src')) + executable = 'PrBoom.app/Contents/MacOS/PrBoom' + iwadpath = iwad + use_pipe = True + + options = [ + executable, + '-nodraw', + '-nosound', + '-nomouse', + '-nofullscreen', + '-width', '320', + '-height', '200', + ] + options.extend(('-iwad', iwadpath)) + if demopath is not None and demopath != '': + options.extend(('-fastdemo', demopath)) + else: + options.extend(('-fastdemo', demo)) + if pwad is not None and pwad != '': + options.extend(('-file', pwad)) + cmd = ' '.join(options) + print cmd + if sys.platform == 'win32': + p = subprocess.call(options) + try: + results = open(os.path.join(basepath, 'release', 'stderr.txt'),'rU').readlines() + results = [x.strip() for x in results if x.strip()] + if len(results): + print results[-1] + except IOError: + print "couldn't open stderr.txt" + else: + p = subprocess.Popen(options, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + (stdout, stderr) = p.communicate() + results = stderr.split('\n') + results = [x.strip() for x in results if x.strip()] + if len(results): + print results[-1] + +def getBasePath(): + curpath = os.path.join(os.getcwd(), __file__) + if not os.path.exists(curpath): + return + while 1: + if os.path.isfile(curpath): + curpath = os.path.split(curpath)[0] + if os.path.isdir(curpath): + contents = os.listdir(curpath) + if 'prboom.spec.in' in contents: + return curpath + oldpath = curpath + curpath = os.path.split(curpath)[0] + if oldpath == curpath: + return + +def run(): + global basepath + basepath = getBasePath() + if basepath is None: + raise ValueError, "Can't determine base path" + for demospec in iterDemoSpecs(os.path.join(basepath, 'tests', 'demo-testing.csv')): + os.chdir(os.path.join(basepath, 'tests')) + demoname, demopath = demospec['Demo'], demospec['Demo URL'] + demopath = getPathToFile(demoname, demopath) + pwadname, pwadpath = demospec['PWAD'], demospec['PWAD url'] + pwadpath = getPathToFile(pwadname, pwadpath) + iwad = demospec['IWAD'] + runtest(iwad, demoname, demopath, pwadpath) + +if __name__=='__main__': + run()