summaryrefslogtreecommitdiff
path: root/src/heretic
diff options
context:
space:
mode:
Diffstat (limited to 'src/heretic')
-rw-r--r--src/heretic/.gitignore6
-rw-r--r--src/heretic/Makefile.am70
-rw-r--r--src/heretic/am_data.h111
-rw-r--r--src/heretic/am_map.c1515
-rw-r--r--src/heretic/am_map.h119
-rw-r--r--src/heretic/ct_chat.c493
-rw-r--r--src/heretic/ct_chat.h46
-rw-r--r--src/heretic/d_main.c1075
-rw-r--r--src/heretic/d_net.c848
-rw-r--r--src/heretic/deh_ammo.c122
-rw-r--r--src/heretic/deh_frame.c344
-rw-r--r--src/heretic/deh_htext.c856
-rw-r--r--src/heretic/deh_htic.c186
-rw-r--r--src/heretic/deh_htic.h60
-rw-r--r--src/heretic/deh_sound.c118
-rw-r--r--src/heretic/deh_thing.c150
-rw-r--r--src/heretic/deh_weapon.c131
-rw-r--r--src/heretic/doomdata.h200
-rw-r--r--src/heretic/doomdef.h898
-rw-r--r--src/heretic/dstrings.h252
-rw-r--r--src/heretic/f_finale.c433
-rw-r--r--src/heretic/g_game.c1798
-rw-r--r--src/heretic/i_ibm.c1650
-rw-r--r--src/heretic/i_sound.c432
-rw-r--r--src/heretic/in_lude.c1077
-rw-r--r--src/heretic/info.c5608
-rw-r--r--src/heretic/info.h1586
-rw-r--r--src/heretic/m_random.c78
-rw-r--r--src/heretic/m_random.h42
-rw-r--r--src/heretic/mn_menu.c1652
-rw-r--r--src/heretic/p_action.h160
-rw-r--r--src/heretic/p_ceilng.c263
-rw-r--r--src/heretic/p_doors.c389
-rw-r--r--src/heretic/p_enemy.c2690
-rw-r--r--src/heretic/p_floor.c468
-rw-r--r--src/heretic/p_inter.c1493
-rw-r--r--src/heretic/p_lights.c282
-rw-r--r--src/heretic/p_local.h287
-rw-r--r--src/heretic/p_map.c1698
-rw-r--r--src/heretic/p_maputl.c784
-rw-r--r--src/heretic/p_mobj.c1633
-rw-r--r--src/heretic/p_plats.c266
-rw-r--r--src/heretic/p_pspr.c1908
-rw-r--r--src/heretic/p_setup.c660
-rw-r--r--src/heretic/p_sight.c363
-rw-r--r--src/heretic/p_spec.c1308
-rw-r--r--src/heretic/p_spec.h398
-rw-r--r--src/heretic/p_switch.c414
-rw-r--r--src/heretic/p_telept.c173
-rw-r--r--src/heretic/p_tick.c668
-rw-r--r--src/heretic/p_user.c1028
-rw-r--r--src/heretic/r_bsp.c484
-rw-r--r--src/heretic/r_data.c751
-rw-r--r--src/heretic/r_draw.c498
-rw-r--r--src/heretic/r_local.h484
-rw-r--r--src/heretic/r_main.c827
-rw-r--r--src/heretic/r_plane.c521
-rw-r--r--src/heretic/r_segs.c669
-rw-r--r--src/heretic/r_things.c1027
-rw-r--r--src/heretic/s_sound.c608
-rw-r--r--src/heretic/s_sound.h46
-rw-r--r--src/heretic/sb_bar.c1295
-rw-r--r--src/heretic/sounds.c257
-rw-r--r--src/heretic/sounds.h299
64 files changed, 47055 insertions, 0 deletions
diff --git a/src/heretic/.gitignore b/src/heretic/.gitignore
new file mode 100644
index 00000000..76092240
--- /dev/null
+++ b/src/heretic/.gitignore
@@ -0,0 +1,6 @@
+Makefile
+Makefile.in
+.deps
+tags
+TAGS
+
diff --git a/src/heretic/Makefile.am b/src/heretic/Makefile.am
new file mode 100644
index 00000000..e56ee806
--- /dev/null
+++ b/src/heretic/Makefile.am
@@ -0,0 +1,70 @@
+
+AM_CFLAGS=-I.. \
+ -I$(top_builddir)/textscreen \
+ @SDL_CFLAGS@ @SDLMIXER_CFLAGS@ @SDLNET_CFLAGS@
+
+noinst_LIBRARIES=libheretic.a
+
+SOURCE_FILES= \
+ am_data.h \
+am_map.c am_map.h \
+ct_chat.c ct_chat.h \
+d_main.c \
+d_net.c \
+ doomdata.h \
+ doomdef.h \
+ dstrings.h \
+f_finale.c \
+g_game.c \
+info.c info.h \
+in_lude.c \
+m_random.c m_random.h \
+mn_menu.c \
+ p_action.h \
+p_ceilng.c \
+p_doors.c \
+p_enemy.c \
+p_floor.c \
+p_inter.c \
+p_lights.c \
+p_local.h \
+p_map.c \
+p_maputl.c \
+p_mobj.c \
+p_plats.c \
+p_pspr.c \
+p_setup.c \
+p_sight.c \
+p_spec.c p_spec.h \
+p_switch.c \
+p_telept.c \
+p_tick.c \
+p_user.c \
+r_bsp.c \
+r_data.c \
+r_draw.c \
+ r_local.h \
+r_main.c \
+r_plane.c \
+r_segs.c \
+r_things.c \
+sb_bar.c \
+sounds.c sounds.h \
+s_sound.c s_sound.h
+
+EXTRA_DIST= \
+i_sound.c \
+i_ibm.c
+
+FEATURE_DEHACKED_SOURCE_FILES = \
+deh_ammo.c \
+deh_frame.c \
+deh_htext.c \
+deh_htic.c \
+deh_sound.c \
+deh_thing.c \
+deh_weapon.c
+
+libheretic_a_SOURCES=$(SOURCE_FILES) \
+ $(FEATURE_DEHACKED_SOURCE_FILES)
+
diff --git a/src/heretic/am_data.h b/src/heretic/am_data.h
new file mode 100644
index 00000000..342f06a9
--- /dev/null
+++ b/src/heretic/am_data.h
@@ -0,0 +1,111 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+// AM_data.h : The vector graphics for the automap
+
+#ifndef __AMDATA_H__
+#define __AMDATA_H__
+
+// a line drawing of the player pointing right, starting from the middle.
+
+#define R ((8*PLAYERRADIUS)/7)
+
+mline_t player_arrow[] = {
+ { { -R+R/4, 0 }, { 0, 0} }, // center line.
+ { { -R+R/4, R/8 }, { R, 0} }, // blade
+ { { -R+R/4, -R/8 }, { R, 0 } },
+ { { -R+R/4, -R/4 }, { -R+R/4, R/4 } }, // crosspiece
+ { { -R+R/8, -R/4 }, { -R+R/8, R/4 } },
+ { { -R+R/8, -R/4 }, { -R+R/4, -R/4} }, //crosspiece connectors
+ { { -R+R/8, R/4 }, { -R+R/4, R/4} },
+ { { -R-R/4, R/8 }, { -R-R/4, -R/8 } }, //pommel
+ { { -R-R/4, R/8 }, { -R+R/8, R/8 } },
+ { { -R-R/4, -R/8}, { -R+R/8, -R/8 } }
+ };
+
+mline_t keysquare[] = {
+ { { 0, 0 }, { R/4, -R/2 } },
+ { { R/4, -R/2 }, { R/2, -R/2 } },
+ { { R/2, -R/2 }, { R/2, R/2 } },
+ { { R/2, R/2 }, { R/4, R/2 } },
+ { { R/4, R/2 }, { 0, 0 } }, // handle part type thing
+ { { 0, 0 }, { -R, 0 } }, // stem
+ { { -R, 0 }, { -R, -R/2 } }, // end lockpick part
+ { { -3*R/4, 0 }, { -3*R/4, -R/4 } }
+ };
+
+/*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 NUMKEYSQUARELINES (sizeof(keysquare)/sizeof(mline_t))
+
+#define R ((8*PLAYERRADIUS)/7)
+mline_t cheat_player_arrow[] = {
+ { { -R+R/8, 0 }, { R, 0 } }, // -----
+ { { R, 0 }, { R-R/2, R/6 } }, // ----->
+ { { R, 0 }, { R-R/2, -R/6 } },
+ { { -R+R/8, 0 }, { -R-R/8, R/6 } }, // >----->
+ { { -R+R/8, 0 }, { -R-R/8, -R/6 } },
+ { { -R+3*R/8, 0 }, { -R+R/8, R/6 } }, // >>----->
+ { { -R+3*R/8, 0 }, { -R+R/8, -R/6 } },
+ { { -R/2, 0 }, { -R/2, -R/6 } }, // >>-d--->
+ { { -R/2, -R/6 }, { -R/2+R/6, -R/6 } },
+ { { -R/2+R/6, -R/6 }, { -R/2+R/6, R/4 } },
+ { { -R/6, 0 }, { -R/6, -R/6 } }, // >>-dd-->
+ { { -R/6, -R/6 }, { 0, -R/6 } },
+ { { 0, -R/6 }, { 0, R/4 } },
+ { { R/6, R/4 }, { R/6, -R/7 } }, // >>-ddt->
+ { { R/6, -R/7 }, { R/6+R/32, -R/7-R/32 } },
+ { { R/6+R/32, -R/7-R/32 }, { R/6+R/10, -R/7 } }
+ };
+#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))
+
+#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))
+
+#endif
diff --git a/src/heretic/am_map.c b/src/heretic/am_map.c
new file mode 100644
index 00000000..aae30cc9
--- /dev/null
+++ b/src/heretic/am_map.c
@@ -0,0 +1,1515 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// AM_map.c
+
+#include <stdio.h>
+
+#include "doomdef.h"
+#include "deh_str.h"
+#include "i_video.h"
+#include "m_controls.h"
+#include "p_local.h"
+#include "am_map.h"
+#include "am_data.h"
+
+#include "doomkeys.h"
+#include "v_video.h"
+
+vertex_t KeyPoints[NUMKEYS];
+
+#define NUMALIAS 3 // Number of antialiased lines.
+
+char *LevelNames[] = {
+ // EPISODE 1 - THE CITY OF THE DAMNED
+ "E1M1: THE DOCKS",
+ "E1M2: THE DUNGEONS",
+ "E1M3: THE GATEHOUSE",
+ "E1M4: THE GUARD TOWER",
+ "E1M5: THE CITADEL",
+ "E1M6: THE CATHEDRAL",
+ "E1M7: THE CRYPTS",
+ "E1M8: HELL'S MAW",
+ "E1M9: THE GRAVEYARD",
+ // EPISODE 2 - HELL'S MAW
+ "E2M1: THE CRATER",
+ "E2M2: THE LAVA PITS",
+ "E2M3: THE RIVER OF FIRE",
+ "E2M4: THE ICE GROTTO",
+ "E2M5: THE CATACOMBS",
+ "E2M6: THE LABYRINTH",
+ "E2M7: THE GREAT HALL",
+ "E2M8: THE PORTALS OF CHAOS",
+ "E2M9: THE GLACIER",
+ // EPISODE 3 - THE DOME OF D'SPARIL
+ "E3M1: THE STOREHOUSE",
+ "E3M2: THE CESSPOOL",
+ "E3M3: THE CONFLUENCE",
+ "E3M4: THE AZURE FORTRESS",
+ "E3M5: THE OPHIDIAN LAIR",
+ "E3M6: THE HALLS OF FEAR",
+ "E3M7: THE CHASM",
+ "E3M8: D'SPARIL'S KEEP",
+ "E3M9: THE AQUIFER",
+ // EPISODE 4: THE OSSUARY
+ "E4M1: CATAFALQUE",
+ "E4M2: BLOCKHOUSE",
+ "E4M3: AMBULATORY",
+ "E4M4: SEPULCHER",
+ "E4M5: GREAT STAIR",
+ "E4M6: HALLS OF THE APOSTATE",
+ "E4M7: RAMPARTS OF PERDITION",
+ "E4M8: SHATTERED BRIDGE",
+ "E4M9: MAUSOLEUM",
+ // EPISODE 5: THE STAGNANT DEMESNE
+ "E5M1: OCHRE CLIFFS",
+ "E5M2: RAPIDS",
+ "E5M3: QUAY",
+ "E5M4: COURTYARD",
+ "E5M5: HYDRATYR",
+ "E5M6: COLONNADE",
+ "E5M7: FOETID MANSE",
+ "E5M8: FIELD OF JUDGEMENT",
+ "E5M9: SKEIN OF D'SPARIL"
+};
+
+static int cheating = 0;
+static int grid = 0;
+
+static int leveljuststarted = 1; // kluge until AM_LevelInit() is called
+
+boolean automapactive = false;
+static int finit_width = SCREENWIDTH;
+static int finit_height = SCREENHEIGHT - 42;
+static int f_x, f_y; // location of window on screen
+static int f_w, f_h; // size of window on screen
+static int lightlev; // used for funky strobing effect
+static byte *fb; // pseudo-frame buffer
+static int amclock;
+
+static mpoint_t m_paninc; // how far the window pans each tic (map coords)
+static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords)
+static fixed_t ftom_zoommul; // how far the window zooms in each tic (fb coords)
+
+static fixed_t m_x, m_y; // LL x,y where the window is on the map (map coords)
+static fixed_t m_x2, m_y2; // UR x,y where the window is on the map (map coords)
+
+// width/height of window on map (map coords)
+static fixed_t m_w, m_h;
+static fixed_t min_x, min_y; // based on level size
+static fixed_t max_x, max_y; // based on level size
+static fixed_t max_w, max_h; // max_x-min_x, max_y-min_y
+static fixed_t min_w, min_h; // based on player size
+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
+static vertex_t oldplr;
+
+//static patch_t *marknums[10]; // numbers used for marking by the automap
+//static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are
+//static int markpointnum = 0; // next point to be assigned
+
+static int followplayer = 1; // specifies whether to follow the player around
+
+static char cheat_amap[] = { 'r', 'a', 'v', 'm', 'a', 'p' };
+
+static byte cheatcount = 0;
+
+extern boolean viewactive;
+
+static byte antialias[NUMALIAS][8] = {
+ {96, 97, 98, 99, 100, 101, 102, 103},
+ {110, 109, 108, 107, 106, 105, 104, 103},
+ {75, 76, 77, 78, 79, 80, 81, 103}
+};
+
+/*
+static byte *aliasmax[NUMALIAS] = {
+ &antialias[0][7], &antialias[1][7], &antialias[2][7]
+};*/
+
+static byte *maplump; // pointer to the raw data for the automap background.
+static short mapystart = 0; // y-value for the start of the map bitmap...used in the paralax stuff.
+static short mapxstart = 0; //x-value for the bitmap.
+
+//byte screens[][SCREENWIDTH*SCREENHEIGHT];
+//void V_MarkRect (int x, int y, int width, int height);
+
+// Functions
+
+void DrawWuLine(int X0, int Y0, int X1, int Y1, byte * BaseColor,
+ int NumLevels, unsigned short IntensityBits);
+
+// Calculates the slope and slope according to the x-axis of a line
+// segment in map coordinates (with the upright y-axis n' all) so
+// that it can be used with the brain-dead drawing stuff.
+
+// Ripped out for Heretic
+/*
+void AM_getIslope(mline_t *ml, islope_t *is)
+{
+ int dx, dy;
+
+ dy = ml->a.y - ml->b.y;
+ dx = ml->b.x - ml->a.x;
+ if (!dy) is->islp = (dx<0?-INT_MAX:INT_MAX);
+ else is->islp = FixedDiv(dx, dy);
+ if (!dx) is->slp = (dy<0?-INT_MAX:INT_MAX);
+ else is->slp = FixedDiv(dy, dx);
+}
+*/
+
+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;
+}
+
+void AM_saveScaleAndLoc(void)
+{
+ old_m_x = m_x;
+ old_m_y = m_y;
+ old_m_w = m_w;
+ old_m_h = m_h;
+}
+
+void AM_restoreScaleAndLoc(void)
+{
+
+ m_w = old_m_w;
+ m_h = old_m_h;
+ if (!followplayer)
+ {
+ m_x = old_m_x;
+ m_y = old_m_y;
+ }
+ else
+ {
+ m_x = plr->mo->x - m_w / 2;
+ m_y = plr->mo->y - m_h / 2;
+ }
+ m_x2 = m_x + m_w;
+ m_y2 = m_y + m_h;
+
+ // Change the scaling multipliers
+ scale_mtof = FixedDiv(f_w << FRACBITS, m_w);
+ scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
+}
+
+// adds a marker at the current location
+
+/*
+void AM_addMark(void)
+{
+ markpoints[markpointnum].x = m_x + m_w/2;
+ markpoints[markpointnum].y = m_y + m_h/2;
+ markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS;
+
+}
+*/
+void AM_findMinMaxBoundaries(void)
+{
+ int i;
+ fixed_t a, b;
+
+ min_x = min_y = INT_MAX;
+ max_x = max_y = -INT_MAX;
+ for (i = 0; i < numvertexes; i++)
+ {
+ if (vertexes[i].x < min_x)
+ min_x = vertexes[i].x;
+ else if (vertexes[i].x > 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 - min_x;
+ max_h = max_y - min_y;
+ min_w = 2 * PLAYERRADIUS;
+ min_h = 2 * PLAYERRADIUS;
+
+ a = FixedDiv(f_w << FRACBITS, max_w);
+ b = FixedDiv(f_h << FRACBITS, max_h);
+ min_scale_mtof = a < b ? a : b;
+
+ max_scale_mtof = FixedDiv(f_h << FRACBITS, 2 * PLAYERRADIUS);
+
+}
+
+void AM_changeWindowLoc(void)
+{
+ if (m_paninc.x || m_paninc.y)
+ {
+ followplayer = 0;
+ f_oldloc.x = INT_MAX;
+ }
+
+ m_x += m_paninc.x;
+ m_y += m_paninc.y;
+
+ if (m_x + m_w / 2 > max_x)
+ {
+ m_x = max_x - m_w / 2;
+ m_paninc.x = 0;
+ }
+ else if (m_x + m_w / 2 < min_x)
+ {
+ m_x = min_x - m_w / 2;
+ m_paninc.x = 0;
+ }
+ if (m_y + m_h / 2 > max_y)
+ {
+ m_y = max_y - m_h / 2;
+ m_paninc.y = 0;
+ }
+ else if (m_y + m_h / 2 < min_y)
+ {
+ m_y = min_y - m_h / 2;
+ m_paninc.y = 0;
+ }
+/*
+ mapxstart += MTOF(m_paninc.x+FRACUNIT/2);
+ mapystart -= MTOF(m_paninc.y+FRACUNIT/2);
+ if(mapxstart >= finit_width)
+ mapxstart -= finit_width;
+ if(mapxstart < 0)
+ mapxstart += finit_width;
+ if(mapystart >= finit_height)
+ mapystart -= finit_height;
+ if(mapystart < 0)
+ mapystart += finit_height;
+*/
+ m_x2 = m_x + m_w;
+ m_y2 = m_y + m_h;
+}
+
+void AM_initVariables(void)
+{
+ int pnum;
+ thinker_t *think;
+ mobj_t *mo;
+
+ //static event_t st_notify = { ev_keyup, AM_MSGENTERED };
+
+ automapactive = true;
+ fb = I_VideoBuffer;
+
+ f_oldloc.x = INT_MAX;
+ amclock = 0;
+ lightlev = 0;
+
+ 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; pnum < MAXPLAYERS; pnum++)
+ if (playeringame[pnum])
+ break;
+ plr = &players[pnum];
+ oldplr.x = plr->mo->x;
+ oldplr.y = plr->mo->y;
+ m_x = plr->mo->x - m_w / 2;
+ m_y = plr->mo->y - m_h / 2;
+ 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;
+
+ // load in the location of keys, if in baby mode
+
+ memset(KeyPoints, 0, sizeof(vertex_t) * 3);
+ if (gameskill == sk_baby)
+ {
+ for (think = thinkercap.next; think != &thinkercap;
+ think = think->next)
+ {
+ if (think->function != P_MobjThinker)
+ { //not a mobj
+ continue;
+ }
+ mo = (mobj_t *) think;
+ if (mo->type == MT_CKEY)
+ {
+ KeyPoints[0].x = mo->x;
+ KeyPoints[0].y = mo->y;
+ }
+ else if (mo->type == MT_AKYY)
+ {
+ KeyPoints[1].x = mo->x;
+ KeyPoints[1].y = mo->y;
+ }
+ else if (mo->type == MT_BKYY)
+ {
+ KeyPoints[2].x = mo->x;
+ KeyPoints[2].y = mo->y;
+ }
+ }
+ }
+
+ // inform the status bar of the change
+//c ST_Responder(&st_notify);
+}
+
+void AM_loadPics(void)
+{
+ //int i;
+ //char namebuf[9];
+/* for (i=0;i<10;i++)
+ {
+ sprintf(namebuf, "AMMNUM%d", i);
+ marknums[i] = W_CacheLumpName(namebuf, PU_STATIC);
+ }*/
+ maplump = W_CacheLumpName(DEH_String("AUTOPAGE"), PU_STATIC);
+}
+
+/*void AM_unloadPics(void)
+{
+ int i;
+ for (i=0;i<10;i++) Z_ChangeTag(marknums[i], PU_CACHE);
+
+}*/
+
+/*
+void AM_clearMarks(void)
+{
+ int i;
+ for (i=0;i<AM_NUMMARKPOINTS;i++) markpoints[i].x = -1; // means empty
+ markpointnum = 0;
+}
+*/
+
+// should be called at the start of every level
+// right now, i figure it out myself
+
+void AM_LevelInit(void)
+{
+ leveljuststarted = 0;
+
+ f_x = f_y = 0;
+ f_w = finit_width;
+ f_h = finit_height;
+ mapxstart = mapystart = 0;
+
+
+// AM_clearMarks();
+
+ 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);
+}
+
+static boolean stopped = true;
+
+void AM_Stop(void)
+{
+ //static event_t st_notify = { 0, ev_keyup, AM_MSGEXITED };
+
+// AM_unloadPics();
+ automapactive = false;
+// ST_Responder(&st_notify);
+ stopped = true;
+ BorderNeedRefresh = true;
+}
+
+void AM_Start(void)
+{
+ static int lastlevel = -1, lastepisode = -1;
+
+ if (!stopped)
+ AM_Stop();
+ stopped = false;
+ if (gamestate != GS_LEVEL)
+ {
+ return; // don't show automap if we aren't in a game!
+ }
+ if (lastlevel != gamemap || lastepisode != gameepisode)
+ {
+ AM_LevelInit();
+ lastlevel = gamemap;
+ lastepisode = gameepisode;
+ }
+ AM_initVariables();
+ AM_loadPics();
+}
+
+// set the window scale to the maximum size
+
+void AM_minOutWindowScale(void)
+{
+ scale_mtof = min_scale_mtof;
+ scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
+ AM_activateNewScale();
+}
+
+// set the window scale to the minimum size
+
+void AM_maxOutWindowScale(void)
+{
+ scale_mtof = max_scale_mtof;
+ scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
+ AM_activateNewScale();
+}
+
+boolean AM_Responder(event_t * ev)
+{
+ int rc;
+ int key;
+ static int cheatstate = 0;
+ static int bigstate = 0;
+
+ key = ev->data1;
+ rc = false;
+
+ if (!automapactive)
+ {
+
+ if (ev->type == ev_keydown && key == key_map_toggle
+ && gamestate == GS_LEVEL)
+ {
+ AM_Start();
+ viewactive = false;
+ // viewactive = true;
+ rc = true;
+ }
+ }
+ else if (ev->type == ev_keydown)
+ {
+ rc = true;
+
+ if (key == key_map_east) // pan right
+ {
+ if (!followplayer)
+ m_paninc.x = FTOM(F_PANINC);
+ else
+ rc = false;
+ }
+ else if (key == key_map_west) // pan left
+ {
+ if (!followplayer)
+ m_paninc.x = -FTOM(F_PANINC);
+ else
+ rc = false;
+ }
+ else if (key == key_map_north) // pan up
+ {
+ if (!followplayer)
+ m_paninc.y = FTOM(F_PANINC);
+ else
+ rc = false;
+ }
+ else if (key == key_map_south) // pan down
+ {
+ if (!followplayer)
+ m_paninc.y = -FTOM(F_PANINC);
+ else
+ rc = false;
+ }
+ else if (key == key_map_zoomout) // zoom out
+ {
+ mtof_zoommul = M_ZOOMOUT;
+ ftom_zoommul = M_ZOOMIN;
+ }
+ else if (key == key_map_zoomin) // zoom in
+ {
+ mtof_zoommul = M_ZOOMIN;
+ ftom_zoommul = M_ZOOMOUT;
+ }
+ else if (key == key_map_toggle) // toggle map (tab)
+ {
+ bigstate = 0;
+ viewactive = true;
+ AM_Stop();
+ }
+ else if (key == key_map_maxzoom)
+ {
+ bigstate = !bigstate;
+ if (bigstate)
+ {
+ AM_saveScaleAndLoc();
+ AM_minOutWindowScale();
+ }
+ else
+ AM_restoreScaleAndLoc();
+ }
+ else if (key == key_map_follow)
+ {
+ followplayer = !followplayer;
+ f_oldloc.x = INT_MAX;
+ P_SetMessage(plr,
+ followplayer ? AMSTR_FOLLOWON : AMSTR_FOLLOWOFF,
+ true);
+ }
+ /*
+ else if (key == key_map_grid)
+ {
+ grid = !grid;
+ plr->message = grid ? AMSTR_GRIDON : AMSTR_GRIDOFF;
+ }
+ else if (key == key_map_mark)
+ {
+ sprintf(buffer, "%s %d", AMSTR_MARKEDSPOT, markpointnum);
+ plr->message = buffer;
+ AM_addMark();
+ }
+ else if (key == key_map_clearmark)
+ {
+ AM_clearMarks();
+ plr->message = AMSTR_MARKSCLEARED;
+ }
+ */
+ else
+ {
+ cheatstate = 0;
+ rc = false;
+ }
+
+ if (cheat_amap[cheatcount] == ev->data1 && !netgame)
+ cheatcount++;
+ else
+ cheatcount = 0;
+ if (cheatcount == 6)
+ {
+ cheatcount = 0;
+ rc = false;
+ cheating = (cheating + 1) % 3;
+ }
+ }
+
+ else if (ev->type == ev_keyup)
+ {
+ rc = false;
+
+ if (key == key_map_east)
+ {
+ if (!followplayer)
+ m_paninc.x = 0;
+ }
+ else if (key == key_map_east)
+ {
+ if (!followplayer)
+ m_paninc.x = 0;
+ }
+ else if (key == key_map_north)
+ {
+ if (!followplayer)
+ m_paninc.y = 0;
+ }
+ else if (key == key_map_south)
+ {
+ if (!followplayer)
+ m_paninc.y = 0;
+ }
+ else if (key == key_map_zoomout || key == key_map_zoomin)
+ {
+ mtof_zoommul = FRACUNIT;
+ ftom_zoommul = FRACUNIT;
+ }
+ }
+
+ return rc;
+
+}
+
+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();
+}
+
+void AM_doFollowPlayer(void)
+{
+ if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y)
+ {
+// m_x = FTOM(MTOF(plr->mo->x - m_w/2));
+// m_y = FTOM(MTOF(plr->mo->y - m_h/2));
+// m_x = plr->mo->x - m_w/2;
+// m_y = plr->mo->y - m_h/2;
+ m_x = FTOM(MTOF(plr->mo->x)) - m_w / 2;
+ m_y = FTOM(MTOF(plr->mo->y)) - m_h / 2;
+ m_x2 = m_x + m_w;
+ m_y2 = m_y + m_h;
+
+ // do the parallax parchment scrolling.
+/*
+ dmapx = (MTOF(plr->mo->x)-MTOF(f_oldloc.x)); //fixed point
+ dmapy = (MTOF(f_oldloc.y)-MTOF(plr->mo->y));
+
+ if(f_oldloc.x == INT_MAX) //to eliminate an error when the user first
+ dmapx=0; //goes into the automap.
+ mapxstart += dmapx;
+ mapystart += dmapy;
+
+ while(mapxstart >= finit_width)
+ mapxstart -= finit_width;
+ while(mapxstart < 0)
+ mapxstart += finit_width;
+ while(mapystart >= finit_height)
+ mapystart -= finit_height;
+ while(mapystart < 0)
+ mapystart += finit_height;
+*/
+ f_oldloc.x = plr->mo->x;
+ f_oldloc.y = plr->mo->y;
+ }
+}
+
+// Ripped out for Heretic
+/*
+void AM_updateLightLev(void)
+{
+ static nexttic = 0;
+//static int litelevels[] = { 0, 3, 5, 6, 6, 7, 7, 7 };
+ static int litelevels[] = { 0, 4, 7, 10, 12, 14, 15, 15 };
+ static int litelevelscnt = 0;
+
+ // Change light level
+ if (amclock>nexttic)
+ {
+ lightlev = litelevels[litelevelscnt++];
+ if (litelevelscnt == sizeof(litelevels)/sizeof(int)) litelevelscnt = 0;
+ nexttic = amclock + 6 - (amclock % 6);
+ }
+}
+*/
+
+void AM_Ticker(void)
+{
+
+ if (!automapactive)
+ return;
+
+ amclock++;
+
+ if (followplayer)
+ 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();
+ // Update light level
+// AM_updateLightLev();
+
+}
+
+void AM_clearFB(int color)
+{
+ int i, j;
+ int dmapx;
+ int dmapy;
+
+ if (followplayer)
+ {
+ dmapx = (MTOF(plr->mo->x) - MTOF(oldplr.x)); //fixed point
+ dmapy = (MTOF(oldplr.y) - MTOF(plr->mo->y));
+
+ oldplr.x = plr->mo->x;
+ oldplr.y = plr->mo->y;
+// if(f_oldloc.x == INT_MAX) //to eliminate an error when the user first
+// dmapx=0; //goes into the automap.
+ mapxstart += dmapx >> 1;
+ mapystart += dmapy >> 1;
+
+ while (mapxstart >= finit_width)
+ mapxstart -= finit_width;
+ while (mapxstart < 0)
+ mapxstart += finit_width;
+ while (mapystart >= finit_height)
+ mapystart -= finit_height;
+ while (mapystart < 0)
+ mapystart += finit_height;
+ }
+ else
+ {
+ mapxstart += (MTOF(m_paninc.x) >> 1);
+ mapystart -= (MTOF(m_paninc.y) >> 1);
+ if (mapxstart >= finit_width)
+ mapxstart -= finit_width;
+ if (mapxstart < 0)
+ mapxstart += finit_width;
+ if (mapystart >= finit_height)
+ mapystart -= finit_height;
+ if (mapystart < 0)
+ mapystart += finit_height;
+ }
+
+ //blit the automap background to the screen.
+ j = mapystart * finit_width;
+ for (i = 0; i < finit_height; i++)
+ {
+ memcpy(I_VideoBuffer + i * finit_width, maplump + j + mapxstart,
+ finit_width - mapxstart);
+ memcpy(I_VideoBuffer + i * finit_width + finit_width - mapxstart,
+ maplump + j, mapxstart);
+ j += finit_width;
+ if (j >= finit_height * finit_width)
+ j = 0;
+ }
+
+// memcpy(I_VideoBuffer, maplump, finit_width*finit_height);
+// memset(fb, color, f_w*f_h);
+}
+
+// Based on Cohen-Sutherland clipping algorithm but with a slightly
+// faster reject and precalculated slopes. If I need the speed, will
+// hash algorithm to the common cases.
+
+boolean AM_clipMline(mline_t * ml, fline_t * fl)
+{
+ enum
+ { LEFT = 1, RIGHT = 2, BOTTOM = 4, TOP = 8 };
+ int outcode1 = 0, outcode2 = 0, outside;
+ fpoint_t tmp = { 0, 0 };
+ int dx, 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
+
+// Classic Bresenham w/ whatever optimizations I need for speed
+
+void AM_drawFline(fline_t * fl, int color)
+{
+
+ register int x, y, dx, dy, sx, sy, ax, ay, d;
+ static int fuck = 0;
+
+ switch (color)
+ {
+ case WALLCOLORS:
+ DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[0][0],
+ 8, 3);
+ break;
+ case FDWALLCOLORS:
+ DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[1][0],
+ 8, 3);
+ break;
+ case CDWALLCOLORS:
+ DrawWuLine(fl->a.x, fl->a.y, fl->b.x, fl->b.y, &antialias[2][0],
+ 8, 3);
+ break;
+ default:
+ {
+ // For debugging only
+ if (fl->a.x < 0 || fl->a.x >= f_w
+ || fl->a.y < 0 || fl->a.y >= f_h
+ || fl->b.x < 0 || fl->b.x >= f_w
+ || fl->b.y < 0 || fl->b.y >= f_h)
+ {
+ fprintf(stderr, "fuck %d \r", fuck++);
+ return;
+ }
+
+#define DOT(xx,yy,cc) fb[(yy)*f_w+(xx)]=(cc) //the MACRO!
+
+ dx = fl->b.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)
+ {
+ DOT(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)
+ {
+ DOT(x, y, color);
+ if (y == fl->b.y)
+ return;
+ if (d >= 0)
+ {
+ x += sx;
+ d -= ay;
+ }
+ y += sy;
+ d += ax;
+ }
+ }
+ }
+ }
+}
+
+/* Wu antialiased line drawer.
+ * (X0,Y0),(X1,Y1) = line to draw
+ * BaseColor = color # of first color in block used for antialiasing, the
+ * 100% intensity version of the drawing color
+ * NumLevels = size of color block, with BaseColor+NumLevels-1 being the
+ * 0% intensity version of the drawing color
+ * IntensityBits = log base 2 of NumLevels; the # of bits used to describe
+ * the intensity of the drawing color. 2**IntensityBits==NumLevels
+ */
+void PUTDOT(short xx, short yy, byte * cc, byte * cm)
+{
+ static int oldyy;
+ static int oldyyshifted;
+ byte *oldcc = cc;
+
+ if (xx < 32)
+ cc += 7 - (xx >> 2);
+ else if (xx > (finit_width - 32))
+ cc += 7 - ((finit_width - xx) >> 2);
+// if(cc==oldcc) //make sure that we don't double fade the corners.
+// {
+ if (yy < 32)
+ cc += 7 - (yy >> 2);
+ else if (yy > (finit_height - 32))
+ cc += 7 - ((finit_height - yy) >> 2);
+// }
+ if (cc > cm && cm != NULL)
+ {
+ cc = cm;
+ }
+ else if (cc > oldcc + 6) // don't let the color escape from the fade table...
+ {
+ cc = oldcc + 6;
+ }
+ if (yy == oldyy + 1)
+ {
+ oldyy++;
+ oldyyshifted += 320;
+ }
+ else if (yy == oldyy - 1)
+ {
+ oldyy--;
+ oldyyshifted -= 320;
+ }
+ else if (yy != oldyy)
+ {
+ oldyy = yy;
+ oldyyshifted = yy * 320;
+ }
+ fb[oldyyshifted + xx] = *(cc);
+// fb[(yy)*f_w+(xx)]=*(cc);
+}
+
+void DrawWuLine(int X0, int Y0, int X1, int Y1, byte * BaseColor,
+ int NumLevels, unsigned short IntensityBits)
+{
+ unsigned short IntensityShift, ErrorAdj, ErrorAcc;
+ unsigned short ErrorAccTemp, Weighting, WeightingComplementMask;
+ short DeltaX, DeltaY, Temp, XDir;
+
+ /* Make sure the line runs top to bottom */
+ if (Y0 > Y1)
+ {
+ Temp = Y0;
+ Y0 = Y1;
+ Y1 = Temp;
+ Temp = X0;
+ X0 = X1;
+ X1 = Temp;
+ }
+ /* Draw the initial pixel, which is always exactly intersected by
+ the line and so needs no weighting */
+ PUTDOT(X0, Y0, &BaseColor[0], NULL);
+
+ if ((DeltaX = X1 - X0) >= 0)
+ {
+ XDir = 1;
+ }
+ else
+ {
+ XDir = -1;
+ DeltaX = -DeltaX; /* make DeltaX positive */
+ }
+ /* Special-case horizontal, vertical, and diagonal lines, which
+ require no weighting because they go right through the center of
+ every pixel */
+ if ((DeltaY = Y1 - Y0) == 0)
+ {
+ /* Horizontal line */
+ while (DeltaX-- != 0)
+ {
+ X0 += XDir;
+ PUTDOT(X0, Y0, &BaseColor[0], NULL);
+ }
+ return;
+ }
+ if (DeltaX == 0)
+ {
+ /* Vertical line */
+ do
+ {
+ Y0++;
+ PUTDOT(X0, Y0, &BaseColor[0], NULL);
+ }
+ while (--DeltaY != 0);
+ return;
+ }
+ //diagonal line.
+ if (DeltaX == DeltaY)
+ {
+ do
+ {
+ X0 += XDir;
+ Y0++;
+ PUTDOT(X0, Y0, &BaseColor[0], NULL);
+ }
+ while (--DeltaY != 0);
+ return;
+ }
+ /* Line is not horizontal, diagonal, or vertical */
+ ErrorAcc = 0; /* initialize the line error accumulator to 0 */
+ /* # of bits by which to shift ErrorAcc to get intensity level */
+ IntensityShift = 16 - IntensityBits;
+ /* Mask used to flip all bits in an intensity weighting, producing the
+ result (1 - intensity weighting) */
+ WeightingComplementMask = NumLevels - 1;
+ /* Is this an X-major or Y-major line? */
+ if (DeltaY > DeltaX)
+ {
+ /* Y-major line; calculate 16-bit fixed-point fractional part of a
+ pixel that X advances each time Y advances 1 pixel, truncating the
+ result so that we won't overrun the endpoint along the X axis */
+ ErrorAdj = ((unsigned int) DeltaX << 16) / (unsigned int) DeltaY;
+ /* Draw all pixels other than the first and last */
+ while (--DeltaY)
+ {
+ ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
+ ErrorAcc += ErrorAdj; /* calculate error for next pixel */
+ if (ErrorAcc <= ErrorAccTemp)
+ {
+ /* The error accumulator turned over, so advance the X coord */
+ X0 += XDir;
+ }
+ Y0++; /* Y-major, so always advance Y */
+ /* The IntensityBits most significant bits of ErrorAcc give us the
+ intensity weighting for this pixel, and the complement of the
+ weighting for the paired pixel */
+ Weighting = ErrorAcc >> IntensityShift;
+ PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]);
+ PUTDOT(X0 + XDir, Y0,
+ &BaseColor[(Weighting ^ WeightingComplementMask)],
+ &BaseColor[7]);
+ }
+ /* Draw the final pixel, which is always exactly intersected by the line
+ and so needs no weighting */
+ PUTDOT(X1, Y1, &BaseColor[0], NULL);
+ return;
+ }
+ /* It's an X-major line; calculate 16-bit fixed-point fractional part of a
+ pixel that Y advances each time X advances 1 pixel, truncating the
+ result to avoid overrunning the endpoint along the X axis */
+ ErrorAdj = ((unsigned int) DeltaY << 16) / (unsigned int) DeltaX;
+ /* Draw all pixels other than the first and last */
+ while (--DeltaX)
+ {
+ ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
+ ErrorAcc += ErrorAdj; /* calculate error for next pixel */
+ if (ErrorAcc <= ErrorAccTemp)
+ {
+ /* The error accumulator turned over, so advance the Y coord */
+ Y0++;
+ }
+ X0 += XDir; /* X-major, so always advance X */
+ /* The IntensityBits most significant bits of ErrorAcc give us the
+ intensity weighting for this pixel, and the complement of the
+ weighting for the paired pixel */
+ Weighting = ErrorAcc >> IntensityShift;
+ PUTDOT(X0, Y0, &BaseColor[Weighting], &BaseColor[7]);
+ PUTDOT(X0, Y0 + 1,
+ &BaseColor[(Weighting ^ WeightingComplementMask)],
+ &BaseColor[7]);
+
+ }
+ /* Draw the final pixel, which is always exactly intersected by the line
+ and so needs no weighting */
+ PUTDOT(X1, Y1, &BaseColor[0], NULL);
+}
+
+void AM_drawMline(mline_t * ml, int color)
+{
+ static fline_t fl;
+
+ if (AM_clipMline(ml, &fl))
+ AM_drawFline(&fl, color); // draws it on frame buffer using fb coords
+
+}
+
+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 << FRACBITS))
+ start += (MAPBLOCKUNITS << FRACBITS)
+ - ((start - bmaporgx) % (MAPBLOCKUNITS << FRACBITS));
+ end = m_x + m_w;
+
+ // draw vertical gridlines
+ ml.a.y = m_y;
+ ml.b.y = m_y + m_h;
+ for (x = start; x < end; x += (MAPBLOCKUNITS << FRACBITS))
+ {
+ ml.a.x = x;
+ ml.b.x = x;
+ AM_drawMline(&ml, color);
+ }
+
+ // Figure out start of horizontal gridlines
+ start = m_y;
+ if ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS))
+ start += (MAPBLOCKUNITS << FRACBITS)
+ - ((start - bmaporgy) % (MAPBLOCKUNITS << FRACBITS));
+ end = m_y + m_h;
+
+ // draw horizontal gridlines
+ ml.a.x = m_x;
+ ml.b.x = m_x + m_w;
+ for (y = start; y < end; y += (MAPBLOCKUNITS << FRACBITS))
+ {
+ ml.a.y = y;
+ ml.b.y = y;
+ AM_drawMline(&ml, color);
+ }
+}
+
+void AM_drawWalls(void)
+{
+ int i;
+ static mline_t l;
+
+ for (i = 0; i < numlines; i++)
+ {
+ l.a.x = lines[i].v1->x;
+ l.a.y = lines[i].v1->y;
+ l.b.x = lines[i].v2->x;
+ l.b.y = lines[i].v2->y;
+ if (cheating || (lines[i].flags & ML_MAPPED))
+ {
+ if ((lines[i].flags & LINE_NEVERSEE) && !cheating)
+ continue;
+ if (!lines[i].backsector)
+ {
+ AM_drawMline(&l, WALLCOLORS + lightlev);
+ }
+ else
+ {
+ if (lines[i].special == 39)
+ { // teleporters
+ AM_drawMline(&l, WALLCOLORS + WALLRANGE / 2);
+ }
+ else if (lines[i].flags & ML_SECRET) // secret door
+ {
+ if (cheating)
+ AM_drawMline(&l, 0);
+ else
+ AM_drawMline(&l, WALLCOLORS + lightlev);
+ }
+ else if (lines[i].special > 25 && lines[i].special < 35)
+ {
+ switch (lines[i].special)
+ {
+ case 26:
+ case 32:
+ AM_drawMline(&l, BLUEKEY);
+ break;
+ case 27:
+ case 34:
+ AM_drawMline(&l, YELLOWKEY);
+ break;
+ case 28:
+ case 33:
+ AM_drawMline(&l, GREENKEY);
+ break;
+ default:
+ break;
+ }
+ }
+ else if (lines[i].backsector->floorheight
+ != lines[i].frontsector->floorheight)
+ {
+ AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change
+ }
+ else if (lines[i].backsector->ceilingheight
+ != lines[i].frontsector->ceilingheight)
+ {
+ AM_drawMline(&l, CDWALLCOLORS + lightlev); // ceiling level change
+ }
+ else if (cheating)
+ {
+ AM_drawMline(&l, TSWALLCOLORS + lightlev);
+ }
+ }
+ }
+ else if (plr->powers[pw_allmap])
+ {
+ if (!(lines[i].flags & LINE_NEVERSEE))
+ AM_drawMline(&l, GRAYS + 3);
+ }
+ }
+
+}
+
+void AM_rotate(fixed_t * x, fixed_t * y, angle_t a)
+{
+ fixed_t tmpx;
+
+ tmpx = FixedMul(*x, finecosine[a >> ANGLETOFINESHIFT])
+ - FixedMul(*y, finesine[a >> ANGLETOFINESHIFT]);
+ *y = FixedMul(*x, finesine[a >> ANGLETOFINESHIFT])
+ + FixedMul(*y, finecosine[a >> ANGLETOFINESHIFT]);
+ *x = tmpx;
+}
+
+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;
+
+ for (i = 0; i < lineguylines; i++)
+ {
+ l.a.x = lineguy[i].a.x;
+ l.a.y = lineguy[i].a.y;
+ if (scale)
+ {
+ l.a.x = FixedMul(scale, l.a.x);
+ l.a.y = FixedMul(scale, l.a.y);
+ }
+ if (angle)
+ AM_rotate(&l.a.x, &l.a.y, angle);
+ l.a.x += x;
+ l.a.y += y;
+
+ l.b.x = lineguy[i].b.x;
+ l.b.y = lineguy[i].b.y;
+ if (scale)
+ {
+ l.b.x = FixedMul(scale, l.b.x);
+ l.b.y = FixedMul(scale, l.b.y);
+ }
+ if (angle)
+ AM_rotate(&l.b.x, &l.b.y, angle);
+ l.b.x += x;
+ l.b.y += y;
+
+ AM_drawMline(&l, color);
+ }
+
+}
+
+void AM_drawPlayers(void)
+{
+
+ int i;
+ player_t *p;
+ static int their_colors[] = { GREENKEY, YELLOWKEY, BLOODRED, BLUEKEY };
+ int their_color = -1;
+ int color;
+
+ if (!netgame)
+ {
+ /*
+ if (cheating) AM_drawLineCharacter(cheat_player_arrow, NUMCHEATPLYRLINES, 0,
+ plr->mo->angle, WHITE, plr->mo->x, plr->mo->y);
+ *///cheat key player pointer is the same as non-cheat pointer..
+
+ AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, plr->mo->angle,
+ WHITE, plr->mo->x, plr->mo->y);
+ return;
+ }
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ their_color++;
+ p = &players[i];
+ if (deathmatch && !singledemo && p != plr)
+ {
+ continue;
+ }
+ if (!playeringame[i])
+ continue;
+ if (p->powers[pw_invisibility])
+ color = 102; // *close* to the automap color
+ else
+ color = their_colors[their_color];
+ AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, p->mo->angle,
+ color, p->mo->x, p->mo->y);
+ }
+}
+
+void AM_drawThings(int colors, int colorrange)
+{
+ int i;
+ mobj_t *t;
+
+ for (i = 0; i < numsectors; i++)
+ {
+ t = sectors[i].thinglist;
+ while (t)
+ {
+ AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES,
+ 16 << FRACBITS, t->angle, colors + lightlev,
+ t->x, t->y);
+ t = t->snext;
+ }
+ }
+}
+
+/*
+void AM_drawMarks(void)
+{
+ int i, fx, fy, w, h;
+
+ for (i=0;i<AM_NUMMARKPOINTS;i++)
+ {
+ if (markpoints[i].x != -1)
+ {
+ w = SHORT(marknums[i]->width);
+ h = SHORT(marknums[i]->height);
+ fx = CXMTOF(markpoints[i].x);
+ fy = CYMTOF(markpoints[i].y);
+ if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h)
+ V_DrawPatch(fx, fy, marknums[i]);
+ }
+ }
+}
+*/
+
+void AM_drawkeys(void)
+{
+ if (KeyPoints[0].x != 0 || KeyPoints[0].y != 0)
+ {
+ AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, YELLOWKEY,
+ KeyPoints[0].x, KeyPoints[0].y);
+ }
+ if (KeyPoints[1].x != 0 || KeyPoints[1].y != 0)
+ {
+ AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, GREENKEY,
+ KeyPoints[1].x, KeyPoints[1].y);
+ }
+ if (KeyPoints[2].x != 0 || KeyPoints[2].y != 0)
+ {
+ AM_drawLineCharacter(keysquare, NUMKEYSQUARELINES, 0, 0, BLUEKEY,
+ KeyPoints[2].x, KeyPoints[2].y);
+ }
+}
+
+void AM_drawCrosshair(int color)
+{
+ fb[(f_w * (f_h + 1)) / 2] = color; // single point for now
+}
+
+void AM_Drawer(void)
+{
+ char *level_name;
+ int numepisodes;
+
+ if (!automapactive)
+ return;
+
+ UpdateState |= I_FULLSCRN;
+ AM_clearFB(BACKGROUND);
+ if (grid)
+ AM_drawGrid(GRIDCOLORS);
+ AM_drawWalls();
+ AM_drawPlayers();
+ if (cheating == 2)
+ AM_drawThings(THINGCOLORS, THINGRANGE);
+// AM_drawCrosshair(XHAIRCOLORS);
+
+// AM_drawMarks();
+ if (gameskill == sk_baby)
+ {
+ AM_drawkeys();
+ }
+
+ if (gamemode == retail)
+ {
+ numepisodes = 5;
+ }
+ else
+ {
+ numepisodes = 3;
+ }
+
+ if (gameepisode <= numepisodes && gamemap < 10)
+ {
+ level_name = LevelNames[(gameepisode - 1) * 9 + gamemap - 1];
+ MN_DrTextA(DEH_String(level_name), 20, 145);
+ }
+// I_Update();
+// V_MarkRect(f_x, f_y, f_w, f_h);
+}
diff --git a/src/heretic/am_map.h b/src/heretic/am_map.h
new file mode 100644
index 00000000..cbd2935a
--- /dev/null
+++ b/src/heretic/am_map.h
@@ -0,0 +1,119 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 __AMMAP_H__
+#define __AMMAP_H__
+
+// For use if I do walls with outsides/insides
+#define REDS 12*8
+#define REDRANGE 1 //16
+#define BLUES (256-4*16+8)
+#define BLUERANGE 1 //8
+#define GREENS (33*8)
+#define GREENRANGE 1 //16
+#define GRAYS (5*8)
+#define GRAYSRANGE 1 //16
+#define BROWNS (14*8)
+#define BROWNRANGE 1 //16
+#define YELLOWS 10*8
+#define YELLOWRANGE 1
+#define BLACK 0
+#define WHITE 4*8
+#define PARCH 13*8-1
+#define BLOODRED 150
+#define BLUEKEY 197
+#define YELLOWKEY 144
+#define GREENKEY 220
+
+// Automap colors
+#define BACKGROUND PARCH
+#define YOURCOLORS WHITE
+#define YOURRANGE 0
+#define WALLCOLORS REDS
+#define WALLRANGE REDRANGE
+#define TSWALLCOLORS GRAYS
+#define TSWALLRANGE GRAYSRANGE
+#define FDWALLCOLORS BROWNS
+#define FDWALLRANGE BROWNRANGE
+#define CDWALLCOLORS YELLOWS
+#define CDWALLRANGE YELLOWRANGE
+#define THINGCOLORS GREENS
+#define THINGRANGE GREENRANGE
+#define SECRETWALLCOLORS WALLCOLORS
+#define SECRETWALLRANGE WALLRANGE
+#define GRIDCOLORS (GRAYS + GRAYSRANGE/2)
+#define GRIDRANGE 0
+#define XHAIRCOLORS GRAYS
+
+// drawing stuff
+#define FB 0
+
+#define AM_NUMMARKPOINTS 10
+
+#define AM_MSGHEADER (('a'<<24)+('m'<<16))
+#define AM_MSGENTERED (AM_MSGHEADER | ('e'<<8))
+#define AM_MSGEXITED (AM_MSGHEADER | ('x'<<8))
+
+#define INITSCALEMTOF (.2*FRACUNIT) // scale on entry
+// how much the automap moves window per tic in frame-buffer coordinates
+#define F_PANINC 4 // moves 140 pixels in 1 second
+// how much zoom-in per tic
+#define M_ZOOMIN ((int) (1.02*FRACUNIT)) // goes to 2x in 1 second
+// how much zoom-out per tic
+#define M_ZOOMOUT ((int) (FRACUNIT/1.02)) // pulls out to 0.5x in 1 second
+
+// translates between frame-buffer and map distances
+#define FTOM(x) FixedMul(((x)<<16),scale_ftom)
+#define MTOF(x) (FixedMul((x),scale_mtof)>>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)))
+
+// the following is crap
+#define LINE_NEVERSEE ML_DONTDRAW
+
+typedef struct
+{
+ int x, y;
+} fpoint_t;
+
+typedef struct
+{
+ fpoint_t a, b;
+} fline_t;
+
+typedef vertex_t mpoint_t;
+
+typedef struct
+{
+ mpoint_t a, b;
+} mline_t;
+
+typedef struct
+{
+ fixed_t slp, islp;
+} islope_t;
+
+// extern int f_x, f_y, f_w, f_h;
+
+#endif
diff --git a/src/heretic/ct_chat.c b/src/heretic/ct_chat.c
new file mode 100644
index 00000000..8059b74d
--- /dev/null
+++ b/src/heretic/ct_chat.c
@@ -0,0 +1,493 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Chat mode
+//
+
+#include <string.h>
+#include <ctype.h>
+#include "doomdef.h"
+#include "doomkeys.h"
+#include "deh_str.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+#define QUEUESIZE 128
+#define MESSAGESIZE 128
+#define MESSAGELEN 265
+
+#define CT_PLR_GREEN 1
+#define CT_PLR_YELLOW 2
+#define CT_PLR_RED 3
+#define CT_PLR_BLUE 4
+#define CT_PLR_ALL 5
+
+#define CT_KEY_GREEN 'g'
+#define CT_KEY_YELLOW 'y'
+#define CT_KEY_RED 'r'
+#define CT_KEY_BLUE 'b'
+#define CT_KEY_ALL 't'
+#define CT_ESCAPE 6
+
+// Public data
+
+void CT_Init(void);
+void CT_Drawer(void);
+boolean CT_Responder(event_t * ev);
+void CT_Ticker(void);
+char CT_dequeueChatChar(void);
+
+boolean chatmodeon;
+
+// Private data
+
+void CT_queueChatChar(char ch);
+void CT_ClearChatMessage(int player);
+void CT_AddChar(int player, char c);
+void CT_BackSpace(int player);
+
+int head;
+int tail;
+byte ChatQueue[QUEUESIZE];
+int chat_dest[MAXPLAYERS];
+char chat_msg[MAXPLAYERS][MESSAGESIZE];
+char plr_lastmsg[MAXPLAYERS][MESSAGESIZE + 9]; // add in the length of the pre-string
+int msgptr[MAXPLAYERS];
+int msglen[MAXPLAYERS];
+
+boolean cheated;
+
+static int FontABaseLump;
+
+char *CT_FromPlrText[MAXPLAYERS] = {
+ "GREEN: ",
+ "YELLOW: ",
+ "RED: ",
+ "BLUE: "
+};
+
+char *chat_macros[10];
+
+boolean altdown;
+boolean shiftdown;
+
+
+//===========================================================================
+//
+// CT_Init
+//
+// Initialize chat mode data
+//===========================================================================
+
+void CT_Init(void)
+{
+ int i;
+
+ head = 0; //initialize the queue index
+ tail = 0;
+ chatmodeon = false;
+ memset(ChatQueue, 0, QUEUESIZE);
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ chat_dest[i] = 0;
+ msgptr[i] = 0;
+ memset(plr_lastmsg[i], 0, MESSAGESIZE);
+ memset(chat_msg[i], 0, MESSAGESIZE);
+ }
+ FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1;
+ return;
+}
+
+//===========================================================================
+//
+// CT_Stop
+//
+//===========================================================================
+
+void CT_Stop(void)
+{
+ chatmodeon = false;
+ return;
+}
+
+//===========================================================================
+//
+// CT_Responder
+//
+//===========================================================================
+
+boolean CT_Responder(event_t * ev)
+{
+ char *macro;
+
+ int sendto;
+
+ if (!netgame)
+ {
+ return false;
+ }
+ if (ev->data1 == KEY_LALT || ev->data2 == KEY_RALT)
+ {
+ altdown = (ev->type == ev_keydown);
+ return false;
+ }
+ if (ev->data1 == KEY_RSHIFT)
+ {
+ shiftdown = (ev->type == ev_keydown);
+ return false;
+ }
+ if (ev->type != ev_keydown)
+ {
+ return false;
+ }
+ if (!chatmodeon)
+ {
+ sendto = 0;
+ if (ev->data1 == CT_KEY_ALL)
+ {
+ sendto = CT_PLR_ALL;
+ }
+ else if (ev->data1 == CT_KEY_GREEN)
+ {
+ sendto = CT_PLR_GREEN;
+ }
+ else if (ev->data1 == CT_KEY_YELLOW)
+ {
+ sendto = CT_PLR_YELLOW;
+ }
+ else if (ev->data1 == CT_KEY_RED)
+ {
+ sendto = CT_PLR_RED;
+ }
+ else if (ev->data1 == CT_KEY_BLUE)
+ {
+ sendto = CT_PLR_BLUE;
+ }
+ if (sendto == 0 || (sendto != CT_PLR_ALL && !playeringame[sendto - 1])
+ || sendto == consoleplayer + 1)
+ {
+ return false;
+ }
+ CT_queueChatChar(sendto);
+ chatmodeon = true;
+ return true;
+ }
+ else
+ {
+ if (altdown)
+ {
+ if (ev->data1 >= '0' && ev->data1 <= '9')
+ {
+ if (ev->data1 == '0')
+ { // macro 0 comes after macro 9
+ ev->data1 = '9' + 1;
+ }
+ macro = chat_macros[ev->data1 - '1'];
+ CT_queueChatChar(KEY_ENTER); //send old message
+ CT_queueChatChar(chat_dest[consoleplayer]); // chose the dest.
+ while (*macro)
+ {
+ CT_queueChatChar(toupper(*macro++));
+ }
+ CT_queueChatChar(KEY_ENTER); //send it off...
+ CT_Stop();
+ return true;
+ }
+ }
+ if (ev->data1 == KEY_ENTER)
+ {
+ CT_queueChatChar(KEY_ENTER);
+ CT_Stop();
+ return true;
+ }
+ else if (ev->data1 == KEY_ESCAPE)
+ {
+ CT_queueChatChar(CT_ESCAPE);
+ CT_Stop();
+ return true;
+ }
+ else if (ev->data1 >= 'a' && ev->data1 <= 'z')
+ {
+ CT_queueChatChar(ev->data1 - 32);
+ return true;
+ }
+ else if (shiftdown)
+ {
+ if (ev->data1 == '1')
+ {
+ CT_queueChatChar('!');
+ return true;
+ }
+ else if (ev->data1 == '/')
+ {
+ CT_queueChatChar('?');
+ return true;
+ }
+ }
+ else
+ {
+ if (ev->data1 == ' ' || ev->data1 == ',' || ev->data1 == '.'
+ || (ev->data1 >= '0' && ev->data1 <= '9') || ev->data1 == '\''
+ || ev->data1 == KEY_BACKSPACE || ev->data1 == '-'
+ || ev->data1 == '=')
+ {
+ CT_queueChatChar(ev->data1);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//===========================================================================
+//
+// CT_Ticker
+//
+//===========================================================================
+
+void CT_Ticker(void)
+{
+ int i;
+ int j;
+ char c;
+ int numplayers;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i])
+ {
+ continue;
+ }
+ if ((c = players[i].cmd.chatchar) != 0)
+ {
+ if (c <= 5)
+ {
+ chat_dest[i] = c;
+ continue;
+ }
+ else if (c == CT_ESCAPE)
+ {
+ CT_ClearChatMessage(i);
+ }
+ else if (c == KEY_ENTER)
+ {
+ numplayers = 0;
+ for (j = 0; j < MAXPLAYERS; j++)
+ {
+ numplayers += playeringame[j];
+ }
+ CT_AddChar(i, 0); // set the end of message character
+ if (numplayers > 2)
+ {
+ strncpy(plr_lastmsg[i], DEH_String(CT_FromPlrText[i]),
+ MESSAGESIZE + 9);
+ plr_lastmsg[i][MESSAGESIZE + 8] = '\0';
+ strcat(plr_lastmsg[i], chat_msg[i]);
+ }
+ else
+ {
+ strcpy(plr_lastmsg[i], chat_msg[i]);
+ }
+ if (i != consoleplayer && (chat_dest[i] == consoleplayer + 1
+ || chat_dest[i] == CT_PLR_ALL)
+ && *chat_msg[i])
+ {
+ P_SetMessage(&players[consoleplayer], plr_lastmsg[i],
+ true);
+ S_StartSound(NULL, sfx_chat);
+ }
+ else if (i == consoleplayer && (*chat_msg[i]))
+ {
+ if (numplayers > 1)
+ {
+ P_SetMessage(&players[consoleplayer],
+ DEH_String("-MESSAGE SENT-"), true);
+ S_StartSound(NULL, sfx_chat);
+ }
+ else
+ {
+ P_SetMessage(&players[consoleplayer],
+ DEH_String("THERE ARE NO OTHER PLAYERS IN THE GAME!"),
+ true);
+ S_StartSound(NULL, sfx_chat);
+ }
+ }
+ CT_ClearChatMessage(i);
+ }
+ else if (c == KEY_BACKSPACE)
+ {
+ CT_BackSpace(i);
+ }
+ else
+ {
+ CT_AddChar(i, c);
+ }
+ }
+ }
+ return;
+}
+
+//===========================================================================
+//
+// CT_Drawer
+//
+//===========================================================================
+
+void CT_Drawer(void)
+{
+ int i;
+ int x;
+ patch_t *patch;
+
+ if (chatmodeon)
+ {
+ x = 25;
+ for (i = 0; i < msgptr[consoleplayer]; i++)
+ {
+ if (chat_msg[consoleplayer][i] < 33)
+ {
+ x += 6;
+ }
+ else
+ {
+ patch = W_CacheLumpNum(FontABaseLump +
+ chat_msg[consoleplayer][i] - 33,
+ PU_CACHE);
+ V_DrawPatch(x, 10, patch);
+ x += patch->width;
+ }
+ }
+ V_DrawPatch(x, 10, W_CacheLumpName(DEH_String("FONTA59"), PU_CACHE));
+ BorderTopRefresh = true;
+ UpdateState |= I_MESSAGES;
+ }
+}
+
+//===========================================================================
+//
+// CT_queueChatChar
+//
+//===========================================================================
+
+void CT_queueChatChar(char ch)
+{
+ if (((tail + 1) & (QUEUESIZE - 1)) == head)
+ { // the queue is full
+ return;
+ }
+ ChatQueue[tail] = ch;
+ tail = (tail + 1) & (QUEUESIZE - 1);
+}
+
+//===========================================================================
+//
+// CT_dequeueChatChar
+//
+//===========================================================================
+
+char CT_dequeueChatChar(void)
+{
+ byte temp;
+
+ if (head == tail)
+ { // queue is empty
+ return 0;
+ }
+ temp = ChatQueue[head];
+ head = (head + 1) & (QUEUESIZE - 1);
+ return temp;
+}
+
+//===========================================================================
+//
+// CT_AddChar
+//
+//===========================================================================
+
+void CT_AddChar(int player, char c)
+{
+ patch_t *patch;
+
+ if (msgptr[player] + 1 >= MESSAGESIZE || msglen[player] >= MESSAGELEN)
+ { // full.
+ return;
+ }
+ chat_msg[player][msgptr[player]] = c;
+ msgptr[player]++;
+ if (c < 33)
+ {
+ msglen[player] += 6;
+ }
+ else
+ {
+ patch = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+ msglen[player] += patch->width;
+ }
+}
+
+//===========================================================================
+//
+// CT_BackSpace
+//
+// Backs up a space, when the user hits (obviously) backspace
+//===========================================================================
+
+void CT_BackSpace(int player)
+{
+ patch_t *patch;
+ char c;
+
+ if (msgptr[player] == 0)
+ { // message is already blank
+ return;
+ }
+ msgptr[player]--;
+ c = chat_msg[player][msgptr[player]];
+ if (c < 33)
+ {
+ msglen[player] -= 6;
+ }
+ else
+ {
+ patch = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+ msglen[player] -= patch->width;
+ }
+ chat_msg[player][msgptr[player]] = 0;
+}
+
+//===========================================================================
+//
+// CT_ClearChatMessage
+//
+// Clears out the data for the chat message, but the player's message
+// is still saved in plrmsg.
+//===========================================================================
+
+void CT_ClearChatMessage(int player)
+{
+ memset(chat_msg[player], 0, MESSAGESIZE);
+ msgptr[player] = 0;
+ msglen[player] = 0;
+}
diff --git a/src/heretic/ct_chat.h b/src/heretic/ct_chat.h
new file mode 100644
index 00000000..84544e56
--- /dev/null
+++ b/src/heretic/ct_chat.h
@@ -0,0 +1,46 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Chat mode stuff
+//
+
+#ifndef HERETIC_CT_CHAT_H
+#define HERETIC_CT_CHAT_H
+
+#define CT_PLR_GREEN 1
+#define CT_PLR_YELLOW 2
+#define CT_PLR_RED 3
+#define CT_PLR_BLUE 4
+#define CT_PLR_ALL 5
+
+#define CT_KEY_GREEN 'g'
+#define CT_KEY_YELLOW 'y'
+#define CT_KEY_RED 'r'
+#define CT_KEY_BLUE 'b'
+#define CT_KEY_ALL 't'
+
+extern char *chat_macros[10];
+
+#endif /* #ifndef HERETIC_CT_CHAT_H */
+
diff --git a/src/heretic/d_main.c b/src/heretic/d_main.c
new file mode 100644
index 00000000..169a86ea
--- /dev/null
+++ b/src/heretic/d_main.c
@@ -0,0 +1,1075 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// D_main.c
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "txt_main.h"
+#include "txt_io.h"
+
+#include "config.h"
+#include "ct_chat.h"
+#include "doomdef.h"
+#include "deh_main.h"
+#include "d_iwad.h"
+#include "i_endoom.h"
+#include "i_joystick.h"
+#include "i_sound.h"
+#include "i_system.h"
+#include "i_timer.h"
+#include "i_video.h"
+#include "m_argv.h"
+#include "m_config.h"
+#include "m_controls.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "w_main.h"
+#include "v_video.h"
+
+#define STARTUP_WINDOW_X 17
+#define STARTUP_WINDOW_Y 7
+
+GameMission_t gamemission = heretic;
+GameMode_t gamemode = indetermined;
+char *gamedescription = "unknown";
+
+boolean nomonsters; // checkparm of -nomonsters
+boolean respawnparm; // checkparm of -respawn
+boolean debugmode; // checkparm of -debug
+boolean ravpic; // checkparm of -ravpic
+boolean cdrom; // true if cd-rom mode active
+boolean singletics; // debug flag to cancel adaptiveness
+boolean noartiskip; // whether shift-enter skips an artifact
+
+skill_t startskill;
+int startepisode;
+int startmap;
+int UpdateState;
+static int graphical_startup = 1;
+static boolean using_graphical_startup;
+boolean autostart;
+extern boolean automapactive;
+
+boolean advancedemo;
+
+FILE *debugfile;
+
+static int show_endoom = 1;
+
+void D_CheckNetGame(void);
+void D_ProcessEvents(void);
+void G_BuildTiccmd(ticcmd_t * cmd);
+void D_DoAdvanceDemo(void);
+void D_PageDrawer(void);
+void D_AdvanceDemo(void);
+void F_Drawer(void);
+boolean F_Responder(event_t * ev);
+
+//---------------------------------------------------------------------------
+//
+// PROC D_ProcessEvents
+//
+// Send all the events of the given timestamp down the responder chain.
+//
+//---------------------------------------------------------------------------
+
+void D_ProcessEvents(void)
+{
+ event_t *ev;
+
+ while ((ev = D_PopEvent()) != NULL)
+ {
+ if (F_Responder(ev))
+ {
+ continue;
+ }
+ if (MN_Responder(ev))
+ {
+ continue;
+ }
+ G_Responder(ev);
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawMessage
+//
+//---------------------------------------------------------------------------
+
+void DrawMessage(void)
+{
+ player_t *player;
+
+ player = &players[consoleplayer];
+ if (player->messageTics <= 0 || !player->message)
+ { // No message
+ return;
+ }
+ MN_DrTextA(player->message, 160 - MN_TextAWidth(player->message) / 2, 1);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC D_Display
+//
+// Draw current display, possibly wiping it from the previous.
+//
+//---------------------------------------------------------------------------
+
+void R_ExecuteSetViewSize(void);
+
+extern boolean finalestage;
+
+void D_Display(void)
+{
+ extern boolean MenuActive;
+ extern boolean askforquit;
+
+ // Change the view size if needed
+ if (setsizeneeded)
+ {
+ R_ExecuteSetViewSize();
+ }
+
+//
+// do buffered drawing
+//
+ switch (gamestate)
+ {
+ case GS_LEVEL:
+ if (!gametic)
+ break;
+ if (automapactive)
+ AM_Drawer();
+ else
+ R_RenderPlayerView(&players[displayplayer]);
+ CT_Drawer();
+ UpdateState |= I_FULLVIEW;
+ SB_Drawer();
+ break;
+ case GS_INTERMISSION:
+ IN_Drawer();
+ break;
+ case GS_FINALE:
+ F_Drawer();
+ break;
+ case GS_DEMOSCREEN:
+ D_PageDrawer();
+ break;
+ }
+
+ if (paused && !MenuActive && !askforquit)
+ {
+ if (!netgame)
+ {
+ V_DrawPatch(160, viewwindowy + 5, W_CacheLumpName(DEH_String("PAUSED"),
+ PU_CACHE));
+ }
+ else
+ {
+ V_DrawPatch(160, 70, W_CacheLumpName(DEH_String("PAUSED"), PU_CACHE));
+ }
+ }
+ // Handle player messages
+ DrawMessage();
+
+ // Menu drawing
+ MN_Drawer();
+
+ // Send out any new accumulation
+ NetUpdate();
+
+ // Flush buffered stuff to screen
+ I_FinishUpdate();
+}
+
+//
+// D_GrabMouseCallback
+//
+// Called to determine whether to grab the mouse pointer
+//
+
+boolean D_GrabMouseCallback(void)
+{
+ // when menu is active or game is paused, release the mouse
+
+ if (MenuActive || paused)
+ return false;
+
+ // only grab mouse when playing levels (but not demos)
+
+ return (gamestate == GS_LEVEL) && !demoplayback;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC D_DoomLoop
+//
+//---------------------------------------------------------------------------
+
+void D_DoomLoop(void)
+{
+ if (M_CheckParm("-debugfile"))
+ {
+ char filename[20];
+ sprintf(filename, "debug%i.txt", consoleplayer);
+ debugfile = fopen(filename, "w");
+ }
+ I_SetWindowTitle(gamedescription);
+ I_InitGraphics();
+ I_SetGrabMouseCallback(D_GrabMouseCallback);
+
+ while (1)
+ {
+ // Frame syncronous IO operations
+ I_StartFrame();
+
+ // Process one or more tics
+ if (singletics)
+ {
+ I_StartTic();
+ D_ProcessEvents();
+ G_BuildTiccmd(&netcmds[consoleplayer][maketic % BACKUPTICS]);
+ if (advancedemo)
+ D_DoAdvanceDemo();
+ G_Ticker();
+ gametic++;
+ maketic++;
+ }
+ else
+ {
+ // Will run at least one tic
+ TryRunTics();
+ }
+
+ // Move positional sounds
+ S_UpdateSounds(players[consoleplayer].mo);
+ D_Display();
+ }
+}
+
+/*
+===============================================================================
+
+ DEMO LOOP
+
+===============================================================================
+*/
+
+int demosequence;
+int pagetic;
+char *pagename;
+
+
+/*
+================
+=
+= D_PageTicker
+=
+= Handles timing for warped projection
+=
+================
+*/
+
+void D_PageTicker(void)
+{
+ if (--pagetic < 0)
+ D_AdvanceDemo();
+}
+
+
+/*
+================
+=
+= D_PageDrawer
+=
+================
+*/
+
+extern boolean MenuActive;
+
+void D_PageDrawer(void)
+{
+ V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE));
+ if (demosequence == 1)
+ {
+ V_DrawPatch(4, 160, W_CacheLumpName(DEH_String("ADVISOR"), PU_CACHE));
+ }
+ UpdateState |= I_FULLSCRN;
+}
+
+/*
+=================
+=
+= D_AdvanceDemo
+=
+= Called after each demo or intro demosequence finishes
+=================
+*/
+
+void D_AdvanceDemo(void)
+{
+ advancedemo = true;
+}
+
+void D_DoAdvanceDemo(void)
+{
+ players[consoleplayer].playerstate = PST_LIVE; // don't reborn
+ advancedemo = false;
+ usergame = false; // can't save / end game here
+ paused = false;
+ gameaction = ga_nothing;
+ demosequence = (demosequence + 1) % 7;
+ switch (demosequence)
+ {
+ case 0:
+ pagetic = 210;
+ gamestate = GS_DEMOSCREEN;
+ pagename = DEH_String("TITLE");
+ S_StartSong(mus_titl, false);
+ break;
+ case 1:
+ pagetic = 140;
+ gamestate = GS_DEMOSCREEN;
+ pagename = DEH_String("TITLE");
+ break;
+ case 2:
+ BorderNeedRefresh = true;
+ UpdateState |= I_FULLSCRN;
+ G_DeferedPlayDemo(DEH_String("demo1"));
+ break;
+ case 3:
+ pagetic = 200;
+ gamestate = GS_DEMOSCREEN;
+ pagename = DEH_String("CREDIT");
+ break;
+ case 4:
+ BorderNeedRefresh = true;
+ UpdateState |= I_FULLSCRN;
+ G_DeferedPlayDemo(DEH_String("demo2"));
+ break;
+ case 5:
+ pagetic = 200;
+ gamestate = GS_DEMOSCREEN;
+ if (gamemode == shareware)
+ {
+ pagename = DEH_String("ORDER");
+ }
+ else
+ {
+ pagename = DEH_String("CREDIT");
+ }
+ break;
+ case 6:
+ BorderNeedRefresh = true;
+ UpdateState |= I_FULLSCRN;
+ G_DeferedPlayDemo(DEH_String("demo3"));
+ break;
+ }
+}
+
+
+/*
+=================
+=
+= D_StartTitle
+=
+=================
+*/
+
+void D_StartTitle(void)
+{
+ gameaction = ga_nothing;
+ demosequence = -1;
+ D_AdvanceDemo();
+}
+
+
+/*
+==============
+=
+= D_CheckRecordFrom
+=
+= -recordfrom <savegame num> <demoname>
+==============
+*/
+
+void D_CheckRecordFrom(void)
+{
+ int p;
+ char file[256];
+
+ p = M_CheckParm("-recordfrom");
+ if (!p || p > myargc - 2)
+ return;
+
+ if (cdrom)
+ {
+ sprintf(file, SAVEGAMENAMECD "%c.hsg", myargv[p + 1][0]);
+ }
+ else
+ {
+ sprintf(file, SAVEGAMENAME "%c.hsg", myargv[p + 1][0]);
+ }
+ G_LoadGame(file);
+ G_DoLoadGame(); // load the gameskill etc info from savegame
+
+ G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p + 2]);
+ D_DoomLoop(); // never returns
+}
+
+/*
+===============
+=
+= D_AddFile
+=
+===============
+*/
+
+// MAPDIR should be defined as the directory that holds development maps
+// for the -wart # # command
+
+#define MAPDIR "\\data\\"
+
+#define SHAREWAREWADNAME "heretic1.wad"
+
+char *iwadfile;
+
+char *basedefault = "heretic.cfg";
+
+void wadprintf(void)
+{
+ if (debugmode)
+ {
+ return;
+ }
+ // haleyjd FIXME: convert to textscreen code?
+#ifdef __WATCOMC__
+ _settextposition(23, 2);
+ _setbkcolor(1);
+ _settextcolor(0);
+ _outtext(exrnwads);
+ _settextposition(24, 2);
+ _outtext(exrnwads2);
+#endif
+}
+
+boolean D_AddFile(char *file)
+{
+ wad_file_t *handle;
+
+ printf(" adding %s\n", file);
+
+ handle = W_AddFile(file);
+
+ return handle != NULL;
+}
+
+//==========================================================
+//
+// Startup Thermo code
+//
+//==========================================================
+#define MSG_Y 9
+#define THERM_X 14
+#define THERM_Y 14
+
+int thermMax;
+int thermCurrent;
+char smsg[80]; // status bar line
+
+//
+// Heretic startup screen shit
+//
+
+static int startup_line = STARTUP_WINDOW_Y;
+
+void hprintf(char *string)
+{
+ if (using_graphical_startup)
+ {
+ TXT_BGColor(TXT_COLOR_CYAN, 0);
+ TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
+
+ TXT_GotoXY(STARTUP_WINDOW_X, startup_line);
+ ++startup_line;
+ TXT_Puts(string);
+
+ TXT_UpdateScreen();
+ }
+
+ // haleyjd: shouldn't be WATCOMC-only
+ if (debugmode)
+ puts(string);
+}
+
+void drawstatus(void)
+{
+ int i;
+
+ TXT_GotoXY(1, 24);
+ TXT_BGColor(TXT_COLOR_BLUE, 0);
+ TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
+
+ for (i=0; smsg[i] != '\0'; ++i)
+ {
+ TXT_PutChar(smsg[i]);
+ }
+}
+
+void status(char *string)
+{
+ if (using_graphical_startup)
+ {
+ strcat(smsg, string);
+ drawstatus();
+ }
+}
+
+void DrawThermo(void)
+{
+ static int last_progress = -1;
+ int progress;
+ int i;
+
+ if (!using_graphical_startup)
+ {
+ return;
+ }
+
+#if 0
+ progress = (98 * thermCurrent) / thermMax;
+ screen = (char *) 0xb8000 + (THERM_Y * 160 + THERM_X * 2);
+ for (i = 0; i < progress / 2; i++)
+ {
+ switch (i)
+ {
+ case 4:
+ case 9:
+ case 14:
+ case 19:
+ case 29:
+ case 34:
+ case 39:
+ case 44:
+ *screen++ = 0xb3;
+ *screen++ = (THERMCOLOR << 4) + 15;
+ break;
+ case 24:
+ *screen++ = 0xba;
+ *screen++ = (THERMCOLOR << 4) + 15;
+ break;
+ default:
+ *screen++ = 0xdb;
+ *screen++ = 0x40 + THERMCOLOR;
+ break;
+ }
+ }
+ if (progress & 1)
+ {
+ *screen++ = 0xdd;
+ *screen++ = 0x40 + THERMCOLOR;
+ }
+#else
+
+ // No progress? Don't update the screen.
+
+ progress = (50 * thermCurrent) / thermMax + 2;
+
+ if (last_progress == progress)
+ {
+ return;
+ }
+
+ last_progress = progress;
+
+ TXT_GotoXY(THERM_X, THERM_Y);
+
+ TXT_FGColor(TXT_COLOR_BRIGHT_GREEN);
+ TXT_BGColor(TXT_COLOR_GREEN, 0);
+
+ for (i = 0; i < progress; i++)
+ {
+ TXT_PutChar(0xdb);
+ }
+
+ TXT_UpdateScreen();
+#endif
+}
+
+void initStartup(void)
+{
+ byte *textScreen;
+ byte *loading;
+
+ if (!graphical_startup || debugmode)
+ {
+ using_graphical_startup = false;
+ return;
+ }
+
+ if (!TXT_Init())
+ {
+ using_graphical_startup = false;
+ return;
+ }
+
+ // Blit main screen
+ textScreen = TXT_GetScreenData();
+ loading = W_CacheLumpName(DEH_String("LOADING"), PU_CACHE);
+ memcpy(textScreen, loading, 4000);
+
+ // Print version string
+
+ TXT_BGColor(TXT_COLOR_RED, 0);
+ TXT_FGColor(TXT_COLOR_YELLOW);
+ TXT_GotoXY(46, 2);
+ TXT_Puts(HERETIC_VERSION_TEXT);
+
+ TXT_UpdateScreen();
+
+ using_graphical_startup = true;
+}
+
+static void finishStartup(void)
+{
+ if (using_graphical_startup)
+ {
+ TXT_Shutdown();
+ }
+}
+
+char tmsg[300];
+void tprintf(char *msg, int initflag)
+{
+ // haleyjd FIXME: convert to textscreen code?
+#ifdef __WATCOMC__
+ char temp[80];
+ int start;
+ int add;
+ int i;
+
+ if (initflag)
+ tmsg[0] = 0;
+ strcat(tmsg, msg);
+ blitStartup();
+ DrawThermo();
+ _setbkcolor(4);
+ _settextcolor(15);
+ for (add = start = i = 0; i <= strlen(tmsg); i++)
+ if ((tmsg[i] == '\n') || (!tmsg[i]))
+ {
+ memset(temp, 0, 80);
+ strncpy(temp, tmsg + start, i - start);
+ _settextposition(MSG_Y + add, 40 - strlen(temp) / 2);
+ _outtext(temp);
+ start = i + 1;
+ add++;
+ }
+ _settextposition(25, 1);
+ drawstatus();
+#else
+ printf("%s", msg);
+#endif
+}
+
+// haleyjd: moved up, removed WATCOMC code
+void CleanExit(void)
+{
+ DEH_printf("Exited from HERETIC.\n");
+ exit(1);
+}
+
+void CheckAbortStartup(void)
+{
+ // haleyjd: removed WATCOMC
+ // haleyjd FIXME: this should actually work in text mode too, but how to
+ // get input before SDL video init?
+ if(using_graphical_startup)
+ {
+ if(TXT_GetChar() == 27)
+ CleanExit();
+ }
+}
+
+void IncThermo(void)
+{
+ thermCurrent++;
+ DrawThermo();
+ CheckAbortStartup();
+}
+
+void InitThermo(int max)
+{
+ thermMax = max;
+ thermCurrent = 0;
+}
+
+//
+// Add configuration file variable bindings.
+//
+
+void D_BindVariables(void)
+{
+ extern int screenblocks;
+ extern int snd_Channels;
+ int i;
+
+ M_ApplyPlatformDefaults();
+
+ I_BindVideoVariables();
+ I_BindJoystickVariables();
+ I_BindSoundVariables();
+
+ M_BindBaseControls();
+ M_BindHereticControls();
+ M_BindWeaponControls();
+ M_BindChatControls(MAXPLAYERS);
+
+ M_BindMenuControls();
+ M_BindMapControls();
+
+ M_BindVariable("mouse_sensitivity", &mouseSensitivity);
+ M_BindVariable("sfx_volume", &snd_MaxVolume);
+ M_BindVariable("music_volume", &snd_MusicVolume);
+ M_BindVariable("screenblocks", &screenblocks);
+ M_BindVariable("snd_channels", &snd_Channels);
+ M_BindVariable("show_endoom", &show_endoom);
+ M_BindVariable("graphical_startup", &graphical_startup);
+
+ for (i=0; i<10; ++i)
+ {
+ char buf[12];
+
+ sprintf(buf, "chatmacro%i", i);
+ M_BindVariable(buf, &chat_macros[i]);
+ }
+}
+
+//
+// Called at exit to display the ENDOOM screen (ENDTEXT in Heretic)
+//
+
+static void D_Endoom(void)
+{
+ byte *endoom_data;
+
+ // Disable ENDOOM?
+
+ if (!show_endoom)
+ {
+ return;
+ }
+
+ endoom_data = W_CacheLumpName(DEH_String("ENDTEXT"), PU_STATIC);
+
+ I_Endoom(endoom_data);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC D_DoomMain
+//
+//---------------------------------------------------------------------------
+
+void D_DoomMain(void)
+{
+ int p;
+ char file[256];
+
+ I_PrintBanner(PACKAGE_STRING);
+
+ I_AtExit(D_Endoom, false);
+
+ M_FindResponseFile();
+ nomonsters = M_CheckParm("-nomonsters");
+ respawnparm = M_CheckParm("-respawn");
+ ravpic = M_CheckParm("-ravpic");
+ noartiskip = M_CheckParm("-noartiskip");
+ debugmode = M_CheckParm("-debug");
+ startskill = sk_medium;
+ startepisode = 1;
+ startmap = 1;
+ autostart = false;
+
+//
+// get skill / episode / map from parms
+//
+ if (M_CheckParm("-deathmatch"))
+ {
+ deathmatch = true;
+ }
+
+ p = M_CheckParm("-skill");
+ if (p && p < myargc - 1)
+ {
+ startskill = myargv[p + 1][0] - '1';
+ autostart = true;
+ }
+
+ p = M_CheckParm("-episode");
+ if (p && p < myargc - 1)
+ {
+ startepisode = myargv[p + 1][0] - '0';
+ startmap = 1;
+ autostart = true;
+ }
+
+ p = M_CheckParm("-warp");
+ if (p && p < myargc - 2)
+ {
+ startepisode = myargv[p + 1][0] - '0';
+ startmap = myargv[p + 2][0] - '0';
+ autostart = true;
+ }
+
+//
+// init subsystems
+//
+ DEH_printf("V_Init: allocate screens.\n");
+ V_Init();
+
+ // Check for -CDROM
+
+ cdrom = false;
+
+#ifdef _WIN32
+
+ //!
+ // @platform windows
+ // @vanilla
+ //
+ // Save configuration data and savegames in c:\heretic.cd,
+ // allowing play from CD.
+ //
+
+ if (M_CheckParm("-cdrom"))
+ {
+ cdrom = true;
+ }
+#endif
+
+ if (cdrom)
+ {
+ M_SetConfigDir(DEH_String("c:\\heretic.cd"));
+ }
+ else
+ {
+ M_SetConfigDir(NULL);
+ }
+
+ // Load defaults before initing other systems
+ DEH_printf("M_LoadDefaults: Load system defaults.\n");
+ D_BindVariables();
+ M_SetConfigFilenames("heretic.cfg", PROGRAM_PREFIX "heretic.cfg");
+ M_LoadDefaults();
+
+ I_AtExit(M_SaveDefaults, false);
+
+ DEH_printf("Z_Init: Init zone memory allocation daemon.\n");
+ Z_Init();
+
+#ifdef FEATURE_DEHACKED
+ printf("DEH_Init: Init Dehacked support.\n");
+ DEH_Init();
+#endif
+
+ DEH_printf("W_Init: Init WADfiles.\n");
+
+ iwadfile = D_FindIWAD(IWAD_MASK_HERETIC, &gamemission);
+
+ if (iwadfile == NULL)
+ {
+ I_Error("Game mode indeterminate. No IWAD was found. Try specifying\n"
+ "one with the '-iwad' command line parameter.");
+ }
+
+ D_AddFile(iwadfile);
+ W_ParseCommandLine();
+
+ p = M_CheckParm("-playdemo");
+ if (!p)
+ {
+ p = M_CheckParm("-timedemo");
+ }
+ if (p && p < myargc - 1)
+ {
+ DEH_snprintf(file, sizeof(file), "%s.lmp", myargv[p + 1]);
+ D_AddFile(file);
+ DEH_printf("Playing demo %s.lmp.\n", myargv[p + 1]);
+ }
+
+ if (W_CheckNumForName(DEH_String("E2M1")) == -1)
+ {
+ gamemode = shareware;
+ gamedescription = "Heretic (shareware)";
+ }
+ else if (W_CheckNumForName("EXTENDED") != -1)
+ {
+ // Presence of the EXTENDED lump indicates the retail version
+
+ gamemode = retail;
+ gamedescription = "Heretic: Shadow of the Serpent Riders";
+ }
+ else
+ {
+ gamemode = registered;
+ gamedescription = "Heretic (registered)";
+ }
+
+ I_PrintStartupBanner(gamedescription);
+
+ // haleyjd: removed WATCOMC
+ initStartup();
+
+ //
+ // Build status bar line!
+ //
+ smsg[0] = 0;
+ if (deathmatch)
+ status(DEH_String("DeathMatch..."));
+ if (nomonsters)
+ status(DEH_String("No Monsters..."));
+ if (respawnparm)
+ status(DEH_String("Respawning..."));
+ if (autostart)
+ {
+ char temp[64];
+ DEH_snprintf(temp, sizeof(temp),
+ "Warp to Episode %d, Map %d, Skill %d ",
+ startepisode, startmap, startskill + 1);
+ status(temp);
+ }
+ wadprintf(); // print the added wadfiles
+
+ tprintf(DEH_String("MN_Init: Init menu system.\n"), 1);
+ MN_Init();
+
+ CT_Init();
+
+ tprintf(DEH_String("R_Init: Init Heretic refresh daemon."), 1);
+ hprintf(DEH_String("Loading graphics"));
+ R_Init();
+ tprintf("\n", 0);
+
+ tprintf(DEH_String("P_Init: Init Playloop state.\n"), 1);
+ hprintf(DEH_String("Init game engine."));
+ P_Init();
+ IncThermo();
+
+ tprintf(DEH_String("I_Init: Setting up machine state.\n"), 1);
+ I_CheckIsScreensaver();
+ I_InitTimer();
+ I_InitJoystick();
+ IncThermo();
+
+ tprintf(DEH_String("S_Init: Setting up sound.\n"), 1);
+ S_Init();
+ //IO_StartupTimer();
+ S_Start();
+
+ tprintf(DEH_String("D_CheckNetGame: Checking network game status.\n"), 1);
+ hprintf(DEH_String("Checking network game status."));
+ D_CheckNetGame();
+ IncThermo();
+
+ // haleyjd: removed WATCOMC
+
+ tprintf(DEH_String("SB_Init: Loading patches.\n"), 1);
+ SB_Init();
+ IncThermo();
+
+//
+// start the apropriate game based on parms
+//
+
+ D_CheckRecordFrom();
+
+ p = M_CheckParm("-record");
+ if (p && p < myargc - 1)
+ {
+ G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p + 1]);
+ D_DoomLoop(); // Never returns
+ }
+
+ p = M_CheckParm("-playdemo");
+ if (p && p < myargc - 1)
+ {
+ singledemo = true; // Quit after one demo
+ G_DeferedPlayDemo(myargv[p + 1]);
+ D_DoomLoop(); // Never returns
+ }
+
+ p = M_CheckParm("-timedemo");
+ if (p && p < myargc - 1)
+ {
+ G_TimeDemo(myargv[p + 1]);
+ D_DoomLoop(); // Never returns
+ }
+
+ p = M_CheckParm("-loadgame");
+ if (p && p < myargc - 1)
+ {
+ if (cdrom)
+ {
+ sprintf(file, SAVEGAMENAMECD "%c.hsg", myargv[p + 1][0]);
+ }
+ else
+ {
+ sprintf(file, SAVEGAMENAME "%c.hsg", myargv[p + 1][0]);
+ }
+ G_LoadGame(file);
+ }
+
+ // Check valid episode and map
+ if (autostart || netgame)
+ {
+ if (!D_ValidEpisodeMap(gamemission, gamemode, startepisode, startmap))
+ {
+ startepisode = 1;
+ startmap = 1;
+ }
+ }
+
+ if (gameaction != ga_loadgame)
+ {
+ UpdateState |= I_FULLSCRN;
+ BorderNeedRefresh = true;
+ if (autostart || netgame)
+ {
+ G_InitNew(startskill, startepisode, startmap);
+ }
+ else
+ {
+ D_StartTitle();
+ }
+ }
+
+ finishStartup();
+
+ D_DoomLoop(); // Never returns
+}
diff --git a/src/heretic/d_net.c b/src/heretic/d_net.c
new file mode 100644
index 00000000..9258b7d7
--- /dev/null
+++ b/src/heretic/d_net.c
@@ -0,0 +1,848 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// d_net.c
+// This version has the fixed ticdup code
+
+#include <stdlib.h>
+
+#include "doomdef.h"
+#include "doomkeys.h"
+
+#include "i_system.h"
+#include "i_timer.h"
+#include "i_video.h"
+
+#define NCMD_EXIT 0x80000000
+#define NCMD_RETRANSMIT 0x40000000
+#define NCMD_SETUP 0x20000000
+#define NCMD_KILL 0x10000000 // kill game
+#define NCMD_CHECKSUM 0x0fffffff
+
+
+doomcom_t *doomcom;
+doomdata_t *netbuffer; // points inside doomcom
+
+
+/*
+==============================================================================
+
+ NETWORKING
+
+gametic is the tic about to (or currently being) run
+maketic is the tick that hasn't had control made for it yet
+nettics[] has the maketics for all players
+
+a gametic cannot be run until nettics[] > gametic for all players
+
+==============================================================================
+*/
+
+#define RESENDCOUNT 10
+#define PL_DRONE 0x80 // bit flag in doomdata->player
+
+ticcmd_t localcmds[BACKUPTICS];
+
+ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
+int nettics[MAXNETNODES];
+boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
+boolean remoteresend[MAXNETNODES]; // set when local needs tics
+int resendto[MAXNETNODES]; // set when remote needs tics
+int resendcount[MAXNETNODES];
+
+int nodeforplayer[MAXPLAYERS];
+
+int maketic;
+int lastnettic, skiptics;
+int ticdup;
+int maxsend; // BACKUPTICS/(2*ticdup)-1
+
+void D_ProcessEvents(void);
+void G_BuildTiccmd(ticcmd_t * cmd);
+void D_DoAdvanceDemo(void);
+
+boolean reboundpacket;
+doomdata_t reboundstore;
+
+
+int NetbufferSize(void)
+{
+ return (int) &(((doomdata_t *) 0)->cmds[netbuffer->numtics]);
+}
+
+unsigned NetbufferChecksum(void)
+{
+ unsigned c;
+ int i, l;
+
+ c = 0x1234567;
+
+#if defined(NeXT) || defined(NORMALUNIX)
+ return 0; // byte order problems
+#endif
+
+ l = (NetbufferSize() - (int) &(((doomdata_t *) 0)->retransmitfrom)) / 4;
+ for (i = 0; i < l; i++)
+ c += ((unsigned *) &netbuffer->retransmitfrom)[i] * (i + 1);
+
+ return c & NCMD_CHECKSUM;
+}
+
+int ExpandTics(int low)
+{
+ int delta;
+
+ delta = low - (maketic & 0xff);
+
+ if (delta >= -64 && delta <= 64)
+ return (maketic & ~0xff) + low;
+ if (delta > 64)
+ return (maketic & ~0xff) - 256 + low;
+ if (delta < -64)
+ return (maketic & ~0xff) + 256 + low;
+
+ I_Error("ExpandTics: strange value %i at maketic %i", low, maketic);
+ return 0;
+}
+
+
+//============================================================================
+
+
+/*
+==============
+=
+= HSendPacket
+=
+==============
+*/
+
+void HSendPacket(int node, int flags)
+{
+ netbuffer->checksum = NetbufferChecksum() | flags;
+
+ if (!node)
+ {
+ reboundstore = *netbuffer;
+ reboundpacket = true;
+ return;
+ }
+
+ if (demoplayback)
+ return;
+
+ if (!netgame)
+ I_Error("Tried to transmit to another node");
+
+ doomcom->command = CMD_SEND;
+ doomcom->remotenode = node;
+ doomcom->datalength = NetbufferSize();
+
+ if (debugfile)
+ {
+ int i;
+ int realretrans;
+ if (netbuffer->checksum & NCMD_RETRANSMIT)
+ realretrans = ExpandTics(netbuffer->retransmitfrom);
+ else
+ realretrans = -1;
+ fprintf(debugfile, "send (%i + %i, R %i) [%i] ",
+ ExpandTics(netbuffer->starttic), netbuffer->numtics,
+ realretrans, doomcom->datalength);
+ for (i = 0; i < doomcom->datalength; i++)
+ fprintf(debugfile, "%i ", ((byte *) netbuffer)[i]);
+ fprintf(debugfile, "\n");
+ }
+
+#ifdef I_NET
+ I_NetCmd();
+#endif
+}
+
+/*
+==============
+=
+= HGetPacket
+=
+= Returns false if no packet is waiting
+=
+==============
+*/
+
+boolean HGetPacket(void)
+{
+ if (reboundpacket)
+ {
+ *netbuffer = reboundstore;
+ doomcom->remotenode = 0;
+ reboundpacket = false;
+ return true;
+ }
+
+ if (!netgame)
+ return false;
+ if (demoplayback)
+ return false;
+
+ doomcom->command = CMD_GET;
+#ifdef I_NET
+ I_NetCmd();
+#endif
+ if (doomcom->remotenode == -1)
+ return false;
+
+ if (doomcom->datalength != NetbufferSize())
+ {
+ if (debugfile)
+ fprintf(debugfile, "bad packet length %i\n", doomcom->datalength);
+ return false;
+ }
+
+ if (NetbufferChecksum() != (netbuffer->checksum & NCMD_CHECKSUM))
+ {
+ if (debugfile)
+ fprintf(debugfile, "bad packet checksum\n");
+ return false;
+ }
+
+ if (debugfile)
+ {
+ int realretrans;
+ int i;
+
+ if (netbuffer->checksum & NCMD_SETUP)
+ fprintf(debugfile, "setup packet\n");
+ else
+ {
+ if (netbuffer->checksum & NCMD_RETRANSMIT)
+ realretrans = ExpandTics(netbuffer->retransmitfrom);
+ else
+ realretrans = -1;
+ fprintf(debugfile, "get %i = (%i + %i, R %i)[%i] ",
+ doomcom->remotenode, ExpandTics(netbuffer->starttic),
+ netbuffer->numtics, realretrans, doomcom->datalength);
+ for (i = 0; i < doomcom->datalength; i++)
+ fprintf(debugfile, "%i ", ((byte *) netbuffer)[i]);
+ fprintf(debugfile, "\n");
+ }
+ }
+ return true;
+}
+
+
+/*
+===================
+=
+= GetPackets
+=
+===================
+*/
+
+char exitmsg[80];
+
+void GetPackets(void)
+{
+ int netconsole;
+ int netnode;
+ ticcmd_t *src, *dest;
+ int realend;
+ int realstart;
+
+ while (HGetPacket())
+ {
+ if (netbuffer->checksum & NCMD_SETUP)
+ continue; // extra setup packet
+
+ netconsole = netbuffer->player & ~PL_DRONE;
+ netnode = doomcom->remotenode;
+ //
+ // to save bytes, only the low byte of tic numbers are sent
+ // Figure out what the rest of the bytes are
+ //
+ realstart = ExpandTics(netbuffer->starttic);
+ realend = (realstart + netbuffer->numtics);
+
+ //
+ // check for exiting the game
+ //
+ if (netbuffer->checksum & NCMD_EXIT)
+ {
+ if (!nodeingame[netnode])
+ continue;
+ nodeingame[netnode] = false;
+ playeringame[netconsole] = false;
+ strcpy(exitmsg, "PLAYER 1 LEFT THE GAME");
+ exitmsg[7] += netconsole;
+ players[consoleplayer].message = exitmsg;
+// if (demorecording)
+// G_CheckDemoStatus ();
+ continue;
+ }
+
+ //
+ // check for a remote game kill
+ //
+ if (netbuffer->checksum & NCMD_KILL)
+ I_Error("Killed by network driver");
+
+ nodeforplayer[netconsole] = netnode;
+
+ //
+ // check for retransmit request
+ //
+ if (resendcount[netnode] <= 0
+ && (netbuffer->checksum & NCMD_RETRANSMIT))
+ {
+ resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
+ if (debugfile)
+ fprintf(debugfile, "retransmit from %i\n", resendto[netnode]);
+ resendcount[netnode] = RESENDCOUNT;
+ }
+ else
+ resendcount[netnode]--;
+
+ //
+ // check for out of order / duplicated packet
+ //
+ if (realend == nettics[netnode])
+ continue;
+
+ if (realend < nettics[netnode])
+ {
+ if (debugfile)
+ fprintf(debugfile, "out of order packet (%i + %i)\n",
+ realstart, netbuffer->numtics);
+ continue;
+ }
+
+ //
+ // check for a missed packet
+ //
+ if (realstart > nettics[netnode])
+ {
+ // stop processing until the other system resends the missed tics
+ if (debugfile)
+ fprintf(debugfile, "missed tics from %i (%i - %i)\n", netnode,
+ realstart, nettics[netnode]);
+ remoteresend[netnode] = true;
+ continue;
+ }
+
+//
+// update command store from the packet
+//
+ {
+ int start;
+
+ remoteresend[netnode] = false;
+
+ start = nettics[netnode] - realstart;
+ src = &netbuffer->cmds[start];
+
+ while (nettics[netnode] < realend)
+ {
+ dest = &netcmds[netconsole][nettics[netnode] % BACKUPTICS];
+ nettics[netnode]++;
+ *dest = *src;
+ src++;
+ }
+ }
+ }
+
+}
+
+/*
+=============
+=
+= NetUpdate
+=
+= Builds ticcmds for console player
+= sends out a packet
+=============
+*/
+
+int gametime;
+
+void NetUpdate(void)
+{
+ int nowtime;
+ int newtics;
+ int i, j;
+ int realstart;
+ int gameticdiv;
+
+//
+// check time
+//
+ nowtime = I_GetTime() / ticdup;
+ newtics = nowtime - gametime;
+ gametime = nowtime;
+
+ if (newtics <= 0) // nothing new to update
+ goto listen;
+
+ if (skiptics <= newtics)
+ {
+ newtics -= skiptics;
+ skiptics = 0;
+ }
+ else
+ {
+ skiptics -= newtics;
+ newtics = 0;
+ }
+
+
+ netbuffer->player = consoleplayer;
+
+//
+// build new ticcmds for console player
+//
+ gameticdiv = gametic / ticdup;
+ for (i = 0; i < newtics; i++)
+ {
+ I_StartTic();
+ D_ProcessEvents();
+ if (maketic - gameticdiv >= BACKUPTICS / 2 - 1)
+ break; // can't hold any more
+//printf ("mk:%i ",maketic);
+ G_BuildTiccmd(&localcmds[maketic % BACKUPTICS]);
+ maketic++;
+ }
+
+
+ if (singletics)
+ return; // singletic update is syncronous
+
+//
+// send the packet to the other nodes
+//
+ for (i = 0; i < doomcom->numnodes; i++)
+ if (nodeingame[i])
+ {
+ netbuffer->starttic = realstart = resendto[i];
+ netbuffer->numtics = maketic - realstart;
+ if (netbuffer->numtics > BACKUPTICS)
+ I_Error("NetUpdate: netbuffer->numtics > BACKUPTICS");
+
+ resendto[i] = maketic - doomcom->extratics;
+
+ for (j = 0; j < netbuffer->numtics; j++)
+ netbuffer->cmds[j] = localcmds[(realstart + j) % BACKUPTICS];
+
+ if (remoteresend[i])
+ {
+ netbuffer->retransmitfrom = nettics[i];
+ HSendPacket(i, NCMD_RETRANSMIT);
+ }
+ else
+ {
+ netbuffer->retransmitfrom = 0;
+ HSendPacket(i, 0);
+ }
+ }
+
+//
+// listen for other packets
+//
+ listen:
+
+ GetPackets();
+}
+
+
+/*
+=====================
+=
+= CheckAbort
+=
+=====================
+*/
+
+void CheckAbort(void)
+{
+ event_t *ev;
+ int stoptic;
+
+ stoptic = I_GetTime() + 2;
+ while (I_GetTime() < stoptic)
+ I_StartTic();
+
+ I_StartTic();
+
+ while ((ev = D_PopEvent()) != NULL)
+ {
+ if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
+ I_Error("Network game synchronization aborted.");
+ }
+}
+
+/*
+=====================
+=
+= D_ArbitrateNetStart
+=
+=====================
+*/
+
+void D_ArbitrateNetStart(void)
+{
+ int i;
+ boolean gotinfo[MAXNETNODES];
+
+ autostart = true;
+ memset(gotinfo, 0, sizeof(gotinfo));
+
+ if (doomcom->consoleplayer)
+ { // listen for setup info from key player
+// mprintf ("listening for network start info...\n");
+ while (1)
+ {
+ CheckAbort();
+ if (!HGetPacket())
+ continue;
+ if (netbuffer->checksum & NCMD_SETUP)
+ {
+ if (netbuffer->player != HERETIC_VERSION)
+ I_Error
+ ("Different DOOM versions cannot play a net game!");
+ startskill = netbuffer->retransmitfrom & 15;
+ deathmatch = (netbuffer->retransmitfrom & 0xc0) >> 6;
+ nomonsters = (netbuffer->retransmitfrom & 0x20) > 0;
+ respawnparm = (netbuffer->retransmitfrom & 0x10) > 0;
+ //startmap = netbuffer->starttic & 0x3f;
+ //startepisode = netbuffer->starttic >> 6;
+ startmap = netbuffer->starttic & 15;
+ startepisode = netbuffer->starttic >> 4;
+ return;
+ }
+ }
+ }
+ else
+ { // key player, send the setup info
+// mprintf ("sending network start info...\n");
+ do
+ {
+ CheckAbort();
+ for (i = 0; i < doomcom->numnodes; i++)
+ {
+ netbuffer->retransmitfrom = startskill;
+ if (deathmatch)
+ netbuffer->retransmitfrom |= (deathmatch << 6);
+ if (nomonsters)
+ netbuffer->retransmitfrom |= 0x20;
+ if (respawnparm)
+ netbuffer->retransmitfrom |= 0x10;
+ //netbuffer->starttic = startepisode * 64 + startmap;
+ netbuffer->starttic = (startepisode << 4) + startmap;
+ netbuffer->player = HERETIC_VERSION;
+ netbuffer->numtics = 0;
+ HSendPacket(i, NCMD_SETUP);
+ }
+
+#if 1
+ for (i = 10; i && HGetPacket(); --i)
+ {
+ if ((netbuffer->player & 0x7f) < MAXNETNODES)
+ gotinfo[netbuffer->player & 0x7f] = true;
+ }
+#else
+ while (HGetPacket())
+ {
+ gotinfo[netbuffer->player & 0x7f] = true;
+ }
+#endif
+
+ for (i = 1; i < doomcom->numnodes; i++)
+ if (!gotinfo[i])
+ break;
+ }
+ while (i < doomcom->numnodes);
+ }
+}
+
+/*
+====================
+=
+= I_InitNetwork
+=
+====================
+*/
+
+static void I_InitNetwork(void)
+{
+ doomcom = malloc(sizeof(*doomcom));
+ memset(doomcom, 0, sizeof(*doomcom));
+ netgame = false;
+ doomcom->id = DOOMCOM_ID;
+ doomcom->numplayers = 1;
+ doomcom->numnodes = 1;
+ doomcom->deathmatch = false;
+ doomcom->consoleplayer = 0;
+ doomcom->ticdup = 1;
+ doomcom->extratics = 0;
+}
+
+/*
+===================
+=
+= D_CheckNetGame
+=
+= Works out player numbers among the net participants
+===================
+*/
+
+extern int viewangleoffset;
+
+void D_CheckNetGame(void)
+{
+ int i;
+
+ for (i = 0; i < MAXNETNODES; i++)
+ {
+ nodeingame[i] = false;
+ nettics[i] = 0;
+ remoteresend[i] = false; // set when local needs tics
+ resendto[i] = 0; // which tic to start sending
+ }
+
+// I_InitNetwork sets doomcom and netgame
+ I_InitNetwork();
+ if (doomcom->id != DOOMCOM_ID)
+ I_Error("Doomcom buffer invalid!");
+ netbuffer = &doomcom->data;
+ consoleplayer = displayplayer = doomcom->consoleplayer;
+ if (netgame)
+ D_ArbitrateNetStart();
+//printf ("startskill %i deathmatch: %i startmap: %i startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
+
+// read values out of doomcom
+ ticdup = doomcom->ticdup;
+ maxsend = BACKUPTICS / (2 * ticdup) - 1;
+ if (maxsend < 1)
+ maxsend = 1;
+
+ for (i = 0; i < doomcom->numplayers; i++)
+ playeringame[i] = true;
+ for (i = 0; i < doomcom->numnodes; i++)
+ nodeingame[i] = true;
+
+//printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
+
+}
+
+/*
+==================
+=
+= D_QuitNetGame
+=
+= Called before quitting to leave a net game without hanging the
+= other players
+=
+==================
+*/
+
+void D_QuitNetGame(void)
+{
+ int i, j;
+
+ if (debugfile)
+ fclose(debugfile);
+
+ if (!netgame || !usergame || consoleplayer == -1 || demoplayback)
+ return;
+
+// send a bunch of packets for security
+ netbuffer->player = consoleplayer;
+ netbuffer->numtics = 0;
+ for (i = 0; i < 4; i++)
+ {
+ for (j = 1; j < doomcom->numnodes; j++)
+ if (nodeingame[j])
+ HSendPacket(j, NCMD_EXIT);
+ I_WaitVBL(1);
+ }
+}
+
+
+
+/*
+===============
+=
+= TryRunTics
+=
+===============
+*/
+
+int frametics[4], frameon;
+int frameskip[4];
+int oldnettics;
+extern boolean advancedemo;
+
+void TryRunTics(void)
+{
+ int i;
+ int lowtic;
+ int entertic;
+ static int oldentertics;
+ int realtics, availabletics;
+ int counts;
+ int numplaying;
+
+//
+// get real tics
+//
+ entertic = I_GetTime() / ticdup;
+ realtics = entertic - oldentertics;
+ oldentertics = entertic;
+
+//
+// get available tics
+//
+ NetUpdate();
+
+ lowtic = INT_MAX;
+ numplaying = 0;
+ for (i = 0; i < doomcom->numnodes; i++)
+ if (nodeingame[i])
+ {
+ numplaying++;
+ if (nettics[i] < lowtic)
+ lowtic = nettics[i];
+ }
+ availabletics = lowtic - gametic / ticdup;
+
+
+//
+// decide how many tics to run
+//
+ if (realtics < availabletics - 1)
+ counts = realtics + 1;
+ else if (realtics < availabletics)
+ counts = realtics;
+ else
+ counts = availabletics;
+ if (counts < 1)
+ counts = 1;
+
+ frameon++;
+
+ if (debugfile)
+ fprintf(debugfile, "=======real: %i avail: %i game: %i\n", realtics,
+ availabletics, counts);
+
+ if (!demoplayback)
+ {
+ //=============================================================================
+ //
+ // ideally nettics[0] should be 1 - 3 tics above lowtic
+ // if we are consistantly slower, speed up time
+ //
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i])
+ break;
+ if (consoleplayer == i)
+ { // the key player does not adapt
+ }
+ else
+ {
+ if (nettics[0] <= nettics[nodeforplayer[i]])
+ {
+ gametime--;
+ // printf ("-");
+ }
+ frameskip[frameon & 3] = (oldnettics > nettics[nodeforplayer[i]]);
+ oldnettics = nettics[0];
+ if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
+ {
+ skiptics = 1;
+ // printf ("+");
+ }
+ }
+ //=============================================================================
+ } // demoplayback
+
+ //
+ // wait for new tics if needed
+ //
+ while (lowtic < gametic / ticdup + counts)
+ {
+
+ NetUpdate();
+ lowtic = INT_MAX;
+
+ for (i = 0; i < doomcom->numnodes; i++)
+ if (nodeingame[i] && nettics[i] < lowtic)
+ lowtic = nettics[i];
+
+ if (lowtic < gametic / ticdup)
+ I_Error("TryRunTics: lowtic < gametic");
+
+ // don't stay in here forever -- give the menu a chance to work
+ if (I_GetTime() / ticdup - entertic >= 20)
+ {
+ MN_Ticker();
+ return;
+ }
+
+ // Don't hog the CPU
+ I_Sleep(1);
+ }
+
+//
+// run the count * ticdup dics
+//
+ while (counts--)
+ {
+ for (i = 0; i < ticdup; i++)
+ {
+ if (gametic / ticdup > lowtic)
+ I_Error("gametic>lowtic");
+ if (advancedemo)
+ D_DoAdvanceDemo();
+ MN_Ticker();
+ G_Ticker();
+ gametic++;
+ //
+ // modify command for duplicated tics
+ //
+ if (i != ticdup - 1)
+ {
+ ticcmd_t *cmd;
+ int buf;
+ int j;
+
+ buf = (gametic / ticdup) % BACKUPTICS;
+ for (j = 0; j < MAXPLAYERS; j++)
+ {
+ cmd = &netcmds[j][buf];
+ cmd->chatchar = 0;
+ if (cmd->buttons & BT_SPECIAL)
+ cmd->buttons = 0;
+ }
+ }
+ }
+ NetUpdate(); // check for new console commands
+ }
+}
diff --git a/src/heretic/deh_ammo.c b/src/heretic/deh_ammo.c
new file mode 100644
index 00000000..fe86c757
--- /dev/null
+++ b/src/heretic/deh_ammo.c
@@ -0,0 +1,122 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Ammo" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doomdef.h"
+#include "doomtype.h"
+#include "deh_defs.h"
+#include "deh_io.h"
+#include "deh_main.h"
+#include "p_local.h"
+
+static void *DEH_AmmoStart(deh_context_t *context, char *line)
+{
+ int ammo_number = 0;
+
+ if (sscanf(line, "Ammo %i", &ammo_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ if (ammo_number < 0 || ammo_number >= NUMAMMO)
+ {
+ DEH_Warning(context, "Invalid ammo number: %i", ammo_number);
+ return NULL;
+ }
+
+ return &maxammo[ammo_number];
+}
+
+static void DEH_AmmoParseLine(deh_context_t *context, char *line, void *tag)
+{
+ char *variable_name, *value;
+ int ivalue;
+ int ammo_number;
+
+ if (tag == NULL)
+ return;
+
+ ammo_number = ((int *) tag) - maxammo;
+
+ // Parse the assignment
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ ivalue = atoi(value);
+
+ if (!strcasecmp(variable_name, "Per ammo"))
+ {
+ // Heretic doesn't have a "per clip" ammo array, instead
+ // it is per weapon. However, the weapon number lines
+ // up with the ammo number if we add one.
+
+ GetWeaponAmmo[ammo_number + 1] = ivalue;
+ }
+ else if (!strcasecmp(variable_name, "Max ammo"))
+ {
+ maxammo[ammo_number] = ivalue;
+ }
+ else
+ {
+ DEH_Warning(context, "Field named '%s' not found", variable_name);
+ }
+}
+
+static void DEH_AmmoMD5Hash(md5_context_t *context)
+{
+ int i;
+
+ for (i=0; i<NUMAMMO; ++i)
+ {
+ MD5_UpdateInt32(context, maxammo[i]);
+ }
+
+ for (i=0; i<NUMWEAPONS; ++i)
+ {
+ MD5_UpdateInt32(context, GetWeaponAmmo[i]);
+ }
+}
+
+deh_section_t deh_section_ammo =
+{
+ "Ammo",
+ NULL,
+ DEH_AmmoStart,
+ DEH_AmmoParseLine,
+ NULL,
+ DEH_AmmoMD5Hash,
+};
+
diff --git a/src/heretic/deh_frame.c b/src/heretic/deh_frame.c
new file mode 100644
index 00000000..8623ab0c
--- /dev/null
+++ b/src/heretic/deh_frame.c
@@ -0,0 +1,344 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Frame" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "doomtype.h"
+#include "info.h"
+
+#include "deh_defs.h"
+#include "deh_io.h"
+#include "deh_main.h"
+#include "deh_mapping.h"
+#include "deh_htic.h"
+
+#include "p_action.h"
+
+typedef struct
+{
+ int offsets[deh_hhe_num_versions];
+ void (*func)();
+} hhe_action_pointer_t;
+
+// Offsets of action pointers within the Heretic executables.
+// Different versions have different offsets.
+// (Seriously Greg, was this really necessary? What was wrong with the
+// "copying action pointer from another frame" technique used in dehacked?)
+
+// Offset Action function
+// v1.0 v1.2 v1.3
+
+static const hhe_action_pointer_t action_pointers[] =
+{
+ { { 77680, 80144, 80208 }, A_AccTeleGlitter },
+ { { 78608, 81104, 81168 }, A_AddPlayerCorpse },
+ { { 115808, 118000, 118240 }, A_AddPlayerRain },
+ { { 112272, 114480, 114720 }, A_BeakAttackPL1 },
+ { { 112448, 114656, 114896 }, A_BeakAttackPL2 },
+ { { 111856, 114176, 114416 }, A_BeakRaise },
+ { { 111568, 113888, 114128 }, A_BeakReady },
+ { { 74640, 77120, 77184 }, A_BeastAttack },
+ { { 70480, 72992, 73056 }, A_BeastPuff },
+ { { 73120, 75600, 75664 }, A_BlueSpark },
+ { { 115456, 117648, 117888 }, A_BoltSpark },
+ { { 77344, 79808, 79872 }, A_BossDeath },
+ { { 69328, 71856, 71920 }, A_Chase },
+ { { 0, 80976, 81040 }, A_CheckBurnGone },
+ { { 78480, 80944, 81008 }, A_CheckSkullDone },
+ { { 78448, 80912, 80976 }, A_CheckSkullFloor },
+ { { 71376, 73888, 73952 }, A_ChicAttack },
+ { { 71488, 74000, 74064 }, A_ChicChase },
+ { { 71456, 73968, 74032 }, A_ChicLook },
+ { { 71520, 74032, 74096 }, A_ChicPain },
+ { { 75792, 78208, 78272 }, A_ClinkAttack },
+ { { 108432, 110816, 111056 }, A_ContMobjSound },
+ { { 114752, 116944, 117184 }, A_DeathBallImpact },
+ { { 70016, 72528, 72592 }, A_DripBlood },
+ { { 77472, 79936, 80000 }, A_ESound },
+ { { 76784, 79248, 79312 }, A_Explode },
+ { { 69872, 72400, 72464 }, A_FaceTarget },
+ { { 71568, 74080, 74144 }, A_Feathers },
+ { { 112928, 115136, 115376 }, A_FireBlasterPL1 },
+ { { 113072, 115280, 115520 }, A_FireBlasterPL2 },
+ { { 115232, 117424, 117664 }, A_FireCrossbowPL1 },
+ { { 115312, 117504, 117744 }, A_FireCrossbowPL2 },
+ { { 113152, 115360, 115600 }, A_FireGoldWandPL1 },
+ { { 113296, 115504, 115744 }, A_FireGoldWandPL2 },
+ { { 113760, 115968, 116208 }, A_FireMacePL1 },
+ { { 114624, 116816, 117056 }, A_FireMacePL2 },
+ { { 116368, 118544, 118784 }, A_FirePhoenixPL1 },
+ { { 116736, 118896, 119136 }, A_FirePhoenixPL2 },
+ { { 115568, 117760, 118000 }, A_FireSkullRodPL1 },
+ { { 115648, 117840, 118080 }, A_FireSkullRodPL2 },
+ { { 117120, 119280, 119520 }, A_FlameEnd },
+ { { 78704, 81200, 81264 }, A_FlameSnd },
+ { { 117152, 119312, 119552 }, A_FloatPuff },
+ { { 78512, 81008, 81072 }, A_FreeTargMobj },
+ { { 117184, 119344, 119584 }, A_GauntletAttack },
+ { { 73232, 75712, 75776 }, A_GenWizard },
+ { { 75872, 78304, 78368 }, A_GhostOff },
+ { { 74752, 77232, 77296 }, A_HeadAttack },
+ { { 75488, 77984, 78048 }, A_HeadFireGrow },
+ { { 75328, 77824, 77888 }, A_HeadIceImpact },
+ { { 116336, 118512, 118752 }, A_HideInCeiling },
+ { { 78736, 81232, 81296 }, A_HideThing },
+ { { 70976, 73488, 73552 }, A_ImpDeath },
+ { { 70304, 72816, 72880 }, A_ImpExplode },
+ { { 70592, 73104, 73168 }, A_ImpMeAttack },
+ { { 70672, 73184, 73248 }, A_ImpMsAttack },
+ { { 70880, 73392, 73456 }, A_ImpMsAttack2 },
+ { { 71024, 73536, 73600 }, A_ImpXDeath1 },
+ { { 71072, 73584, 73648 }, A_ImpXDeath2 },
+ { { 77728, 80192, 80256 }, A_InitKeyGizmo },
+ { { 116720, 118880, 119120 }, A_InitPhoenixPL2 },
+ { { 70160, 72672, 72736 }, A_KnightAttack },
+ { { 117648, 119824, 120064 }, A_Light0 },
+ { { 69200, 71728, 71792 }, A_Look },
+ { { 111760, 114080, 114320 }, A_Lower },
+ { { 114032, 116224, 116464 }, A_MaceBallImpact },
+ { { 114192, 116384, 116624 }, A_MaceBallImpact2 },
+ { { 113904, 116112, 116352 }, A_MacePL1Check },
+ { { 77104, 79568, 79632 }, A_MakePod },
+ { { 73648, 76128, 76192 }, A_MinotaurAtk1 },
+ { { 74112, 76592, 76656 }, A_MinotaurAtk2 },
+ { { 74352, 76832, 76896 }, A_MinotaurAtk3 },
+ { { 74032, 76512, 76576 }, A_MinotaurCharge },
+ { { 73760, 76240, 76304 }, A_MinotaurDecide },
+ { { 74528, 77008, 77072 }, A_MntrFloorFire },
+ { { 71808, 74288, 74352 }, A_MummyAttack },
+ { { 71920, 74400, 74464 }, A_MummyAttack2 },
+ { { 72016, 74496, 74560 }, A_MummyFX1Seek },
+ { { 72048, 74528, 74592 }, A_MummySoul },
+ { { 76400, 78832, 78896 }, A_NoBlocking },
+ { { 69984, 72496, 72560 }, A_Pain },
+ { { 116496, 118656, 118896 }, A_PhoenixPuff },
+ { { 76896, 79360, 79424 }, A_PodPain },
+ { { 116272, 118448, 118688 }, A_RainImpact },
+ { { 111920, 114240, 114480 }, A_Raise },
+ { { 111696, 114016, 114256 }, A_ReFire },
+ { { 77056, 79520, 79584 }, A_RemovePod },
+ { { 116480, 0, 0 }, A_RemovedPhoenixFunc },
+ { { 81952, 84464, 84528 }, A_RestoreArtifact },
+ { { 82048, 84544, 84608 }, A_RestoreSpecialThing1 },
+ { { 82128, 84592, 84656 }, A_RestoreSpecialThing2 },
+ { { 76144, 78576, 78640 }, A_Scream },
+ { { 117104, 119264, 119504 }, A_ShutdownPhoenixPL2 },
+ { { 78288, 80752, 80816 }, A_SkullPop },
+ { { 115776, 117968, 118208 }, A_SkullRodPL2Seek },
+ { { 115984, 118176, 118416 }, A_SkullRodStorm },
+ { { 75632, 78048, 78112 }, A_SnakeAttack },
+ { { 75712, 78128, 78192 }, A_SnakeAttack2 },
+ { { 72144, 74624, 74688 }, A_Sor1Chase },
+ { { 72096, 74576, 74640 }, A_Sor1Pain },
+ { { 73392, 75872, 75936 }, A_Sor2DthInit },
+ { { 73424, 75904, 75968 }, A_Sor2DthLoop },
+ { { 73584, 76064, 76128 }, A_SorDBon },
+ { { 73552, 76032, 76096 }, A_SorDExp },
+ { { 73520, 76000, 76064 }, A_SorDSph },
+ { { 73488, 75968, 76032 }, A_SorRise },
+ { { 73616, 76096, 76160 }, A_SorSightSnd },
+ { { 73456, 75936, 76000 }, A_SorZap },
+ { { 72480, 74960, 75024 }, A_SorcererRise },
+ { { 115088, 117280, 117520 }, A_SpawnRippers },
+ { { 77520, 79984, 80048 }, A_SpawnTeleGlitter },
+ { { 77600, 80064, 80128 }, A_SpawnTeleGlitter2 },
+ { { 72192, 74672, 74736 }, A_Srcr1Attack },
+ { { 72896, 75376, 75440 }, A_Srcr2Attack },
+ { { 72816, 75296, 75360 }, A_Srcr2Decide },
+ { { 112640, 114848, 115088 }, A_StaffAttackPL1 },
+ { { 112784, 114992, 115232 }, A_StaffAttackPL2 },
+ { { 78752, 81248, 81312 }, A_UnHideThing },
+ { { 78080, 80544, 80608 }, A_VolcBallImpact },
+ { { 77856, 80320, 80384 }, A_VolcanoBlast },
+ { { 77824, 80288, 80352 }, A_VolcanoSet },
+ { { 111168, 113488, 113728 }, A_WeaponReady },
+ { { 75168, 77664, 77728 }, A_WhirlwindSeek },
+ { { 75888, 78320, 78384 }, A_WizAtk1 },
+ { { 75920, 78352, 78416 }, A_WizAtk2 },
+ { { 75952, 78384, 78448 }, A_WizAtk3 },
+};
+
+DEH_BEGIN_MAPPING(state_mapping, state_t)
+ DEH_MAPPING("Sprite number", sprite)
+ DEH_MAPPING("Sprite subnumber", frame)
+ DEH_MAPPING("Duration", tics)
+ DEH_MAPPING("Next frame", nextstate)
+ DEH_MAPPING("Unknown 1", misc1)
+ DEH_MAPPING("Unknown 2", misc2)
+DEH_END_MAPPING
+
+static void DEH_FrameInit(void)
+{
+ // Bit of a hack here:
+ DEH_HereticInit();
+}
+
+static void *DEH_FrameStart(deh_context_t *context, char *line)
+{
+ int frame_number = 0;
+ int mapped_frame_number;
+ state_t *state;
+
+ if (sscanf(line, "Frame %i", &frame_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ // Map the HHE frame number (which assumes a Heretic 1.0 state table)
+ // to the internal frame number (which is is the Heretic 1.3 state table):
+
+ mapped_frame_number = DEH_MapHereticFrameNumber(frame_number);
+
+ if (mapped_frame_number < 0 || mapped_frame_number >= DEH_HERETIC_NUMSTATES)
+ {
+ DEH_Warning(context, "Invalid frame number: %i", frame_number);
+ return NULL;
+ }
+
+ state = &states[mapped_frame_number];
+
+ return state;
+}
+
+static boolean GetActionPointerForOffset(int offset, void **result)
+{
+ int i;
+
+ // Special case.
+
+ if (offset == 0)
+ {
+ *result = NULL;
+ return true;
+ }
+
+ for (i=0; i<arrlen(action_pointers); ++i)
+ {
+ if (action_pointers[i].offsets[deh_hhe_version] == offset)
+ {
+ *result = action_pointers[i].func;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// If an invalid action pointer is specified, the patch may be for a
+// different version from the version we are currently set to. Try to
+// suggest a different version to use.
+
+static void SuggestOtherVersions(unsigned int offset)
+{
+ unsigned int i, v;
+
+ for (i=0; i<arrlen(action_pointers); ++i)
+ {
+ for (v=0; v<deh_hhe_num_versions; ++v)
+ {
+ if (action_pointers[i].offsets[v] == offset)
+ {
+ DEH_SuggestHereticVersion(v);
+ }
+ }
+ }
+}
+
+static void DEH_FrameParseLine(deh_context_t *context, char *line, void *tag)
+{
+ state_t *state;
+ char *variable_name, *value;
+ int ivalue;
+
+ if (tag == NULL)
+ return;
+
+ state = (state_t *) tag;
+
+ // Parse the assignment
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ // all values are integers
+
+ ivalue = atoi(value);
+
+ // Action pointer field is a special case:
+
+ if (!strcasecmp(variable_name, "Action pointer"))
+ {
+ void *func;
+
+ if (!GetActionPointerForOffset(ivalue, &func))
+ {
+ SuggestOtherVersions(ivalue);
+ DEH_Error(context, "Unknown action pointer: %i", ivalue);
+ return;
+ }
+
+ state->action = func;
+ }
+ else
+ {
+ // "Next frame" numbers need to undergo mapping.
+
+ if (!strcasecmp(variable_name, "Next frame"))
+ {
+ ivalue = DEH_MapHereticFrameNumber(ivalue);
+ }
+
+ DEH_SetMapping(context, &state_mapping, state, variable_name, ivalue);
+ }
+}
+
+static void DEH_FrameMD5Sum(md5_context_t *context)
+{
+ int i;
+
+ for (i=0; i<NUMSTATES; ++i)
+ {
+ DEH_StructMD5Sum(context, &state_mapping, &states[i]);
+ }
+}
+
+deh_section_t deh_section_frame =
+{
+ "Frame",
+ DEH_FrameInit,
+ DEH_FrameStart,
+ DEH_FrameParseLine,
+ NULL,
+ DEH_FrameMD5Sum,
+};
+
diff --git a/src/heretic/deh_htext.c b/src/heretic/deh_htext.c
new file mode 100644
index 00000000..5dfddac3
--- /dev/null
+++ b/src/heretic/deh_htext.c
@@ -0,0 +1,856 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005-2010 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses Text substitution sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <string.h>
+
+#include "doomtype.h"
+#include "dstrings.h"
+
+#include "z_zone.h"
+
+#include "deh_defs.h"
+#include "deh_io.h"
+#include "deh_htic.h"
+#include "deh_main.h"
+
+//
+// Ok, Greg, the action pointers thing was bad enough, but this really
+// takes the biscuit. Why does HHE's text replacement address strings
+// by offset??!! The dehacked way was much nicer, why change it?
+//
+
+typedef struct
+{
+ unsigned int offsets[deh_hhe_num_versions];
+ char *string;
+} hhe_string_t;
+
+// Offsets String
+// v1.0 v1.2 v1.3
+
+static const hhe_string_t strings[] =
+{
+ { { 228, 228, 228 }, "PLAYPAL" },
+ { { 1240, 1252, 1252 }, "E1M1: THE DOCKS" },
+ { { 1260, 1272, 1272 }, "E1M2: THE DUNGEONS" },
+ { { 1280, 1292, 1292 }, "E1M3: THE GATEHOUSE" },
+ { { 1304, 1316, 1316 }, "E1M4: THE GUARD TOWER" },
+ { { 1328, 1340, 1340 }, "E1M5: THE CITADEL" },
+ { { 1348, 1360, 1360 }, "E1M6: THE CATHEDRAL" },
+ { { 1372, 1384, 1384 }, "E1M7: THE CRYPTS" },
+ { { 1392, 1404, 1404 }, "E1M8: HELL'S MAW" },
+ { { 1412, 1424, 1424 }, "E1M9: THE GRAVEYARD" },
+ { { 1436, 1448, 1448 }, "E2M1: THE CRATER" },
+ { { 1456, 1468, 1468 }, "E2M2: THE LAVA PITS" },
+ { { 1480, 1492, 1492 }, "E2M3: THE RIVER OF FIRE" },
+ { { 1508, 1520, 1520 }, "E2M4: THE ICE GROTTO" },
+ { { 1532, 1544, 1544 }, "E2M5: THE CATACOMBS" },
+ { { 1556, 1568, 1568 }, "E2M6: THE LABYRINTH" },
+ { { 1580, 1592, 1592 }, "E2M7: THE GREAT HALL" },
+ { { 1604, 1616, 1616 }, "E2M8: THE PORTALS OF CHAOS" },
+ { { 1632, 1644, 1644 }, "E2M9: THE GLACIER" },
+ { { 1652, 1664, 1664 }, "E3M1: THE STOREHOUSE" },
+ { { 1676, 1688, 1688 }, "E3M2: THE CESSPOOL" },
+ { { 1696, 1708, 1708 }, "E3M3: THE CONFLUENCE" },
+ { { 1720, 1732, 1732 }, "E3M4: THE AZURE FORTRESS" },
+ { { 1748, 1760, 1760 }, "E3M5: THE OPHIDIAN LAIR" },
+ { { 1776, 1788, 1788 }, "E3M6: THE HALLS OF FEAR" },
+ { { 1804, 1816, 1816 }, "E3M7: THE CHASM" },
+ { { 1824, 1836, 1836 }, "E3M8: D'SPARIL'S KEEP" },
+ { { 1848, 1860, 1860 }, "E3M9: THE AQUIFER" },
+ { { 0, 1880, 1880 }, "E4M1: CATAFALQUE" },
+ { { 0, 1900, 1900 }, "E4M2: BLOCKHOUSE" },
+ { { 0, 1920, 1920 }, "E4M3: AMBULATORY" },
+ { { 0, 1940, 1940 }, "E4M4: SEPULCHER" },
+ { { 0, 1960, 1960 }, "E4M5: GREAT STAIR" },
+ { { 0, 1980, 1980 }, "E4M6: HALLS OF THE APOSTATE" },
+ { { 0, 2012, 2012 }, "E4M7: RAMPARTS OF PERDITION" },
+ { { 0, 2044, 2044 }, "E4M8: SHATTERED BRIDGE" },
+ { { 0, 2068, 2068 }, "E4M9: MAUSOLEUM" },
+ { { 0, 2088, 2088 }, "E5M1: OCHRE CLIFFS" },
+ { { 0, 2108, 2108 }, "E5M2: RAPIDS" },
+ { { 0, 2124, 2124 }, "E5M3: QUAY" },
+ { { 0, 2136, 2136 }, "E5M4: COURTYARD" },
+ { { 0, 2156, 2156 }, "E5M5: HYDRATYR" },
+ { { 0, 2172, 2172 }, "E5M6: COLONNADE" },
+ { { 0, 2192, 2192 }, "E5M7: FOETID MANSE" },
+ { { 0, 2212, 2212 }, "E5M8: FIELD OF JUDGEMENT" },
+ { { 0, 2240, 2240 }, "E5M9: SKEIN OF D'SPARIL" },
+ { { 1868, 2268, 2268 }, "AUTOPAGE" },
+ { { 1880, 2280, 2280 }, "FOLLOW MODE ON" },
+ { { 1896, 2296, 2296 }, "FOLLOW MODE OFF" },
+ { { 1924, 2324, 2324 }, "GREEN: " },
+ { { 1936, 2336, 2336 }, "YELLOW: " },
+ { { 1948, 2348, 2348 }, "RED: " },
+ { { 1956, 2356, 2356 }, "BLUE: " },
+ { { 1964, 2364, 2364 }, "FONTA_S" },
+ { { 1972, 2372, 2372 }, "-MESSAGE SENT-" },
+ { { 1988, 2388, 2388 }, "THERE ARE NO OTHER PLAYERS IN THE GAME!" },
+ { { 2028, 2428, 2428 }, "FONTA59" },
+ { { 2036, 2504, 2504 }, "PAUSED" },
+ { { 2072, 2540, 2540 }, "ADVISOR" },
+ { { 2080, 2548, 2548 }, "TITLE" },
+ { { 2088, 2556, 2556 }, "demo1" },
+ { { 2096, 2564, 2564 }, "CREDIT" },
+ { { 2104, 2572, 2572 }, "demo2" },
+ { { 2112, 2580, 2580 }, "ORDER" },
+ { { 2120, 2588, 2588 }, "demo3" },
+ { { 2304, 2696, 2696 }, "Exited from HERETIC.\n" },
+ { { 2412, 2800, 2800 }, "c:\\heretic.cd" },
+ { { 2528, 2916, 2916 }, "Playing demo %s.lmp.\n" },
+ { { 2592, 2980, 2980 }, "V_Init: allocate screens.\n" },
+ { { 2620, 3008, 3008 }, "M_LoadDefaults: Load system defaults.\n" },
+ { { 2660, 3048, 3048 }, "Z_Init: Init zone memory allocation daemon.\n" },
+ { { 2708, 3096, 3096 }, "W_Init: Init WADfiles.\n" },
+ { { 2732, 3120, 3120 }, "E2M1" },
+ { { 0, 3128, 3128 }, "EXTENDED" },
+ { { 2740, 3140, 3140 }, "LOADING" },
+ { { 2748, 3148, 3148 }, "DeathMatch..." },
+ { { 2764, 3164, 3164 }, "No Monsters..." },
+ { { 2780, 3180, 3180 }, "Respawning..." },
+ { { 2796, 3196, 3196 }, "Warp to Episode %d, Map %d, Skill %d " },
+ { { 2836, 3236, 3236 }, "MN_Init: Init menu system.\n" },
+ { { 2864, 3264, 3264 }, "R_Init: Init Heretic refresh daemon." },
+ { { 2904, 3304, 3304 }, "Loading graphics" },
+ { { 2924, 3324, 3324 }, "P_Init: Init Playloop state." },
+ { { 2956, 3356, 3356 }, "Init game engine." },
+ { { 2976, 3376, 3376 }, "I_Init: Setting up machine state.\n" },
+ { { 3012, 3412, 3412 }, "D_CheckNetGame: Checking network game status.\n" },
+ { { 3060, 3460, 3460 }, "Checking network game status." },
+ { { 3092, 3492, 3492 }, "SB_Init: Loading patches.\n" },
+ { { 0, 3752, 3752 }, "PLAYER 1 LEFT THE GAME" },
+ { { 3508, 3932, 3932 }, "Network game synchronization aborted." },
+ { { 0, 3972, 3972 }, "Different DOOM versions cannot play a net game!" },
+ { { 3908, 4132, 4132 }, "SKY1" },
+ { { 3916, 4140, 4140 }, "SKY2" },
+ { { 3924, 4148, 4148 }, "SKY3" },
+ { { 3736, 4196, 4196 }, "NET GAME" },
+ { { 3748, 4208, 4208 }, "SAVE GAME" },
+ { { 3760, 4220, 4220 }, "Only %i deathmatch spots, 4 required" },
+ { { 3800, 4260, 4260 }, "version %i" },
+ { { 3828, 4372, 4372 }, "c:\\heretic.cd\\hticsav%d.hsg" },
+ { { 3856, 4400, 4400 }, "hticsav%d.hsg" },
+ { { 3896, 4416, 4416 }, "GAME SAVED" },
+ { { 4016, 4456, 4456 }, E1TEXT },
+ { { 4536, 4976, 4976 }, E2TEXT },
+ { { 5068, 5508, 5508 }, E3TEXT },
+ { { 0, 6072, 6072 }, E4TEXT },
+ { { 0, 6780, 6780 }, E5TEXT },
+ { { 5632, 7468, 7468 }, "FLOOR25" },
+ { { 5640, 7476, 7476 }, "FLATHUH1" },
+ { { 5652, 7488, 7488 }, "FLTWAWA2" },
+ { { 0, 7500, 7500 }, "FLOOR28" },
+ { { 0, 7508, 7508 }, "FLOOR08" },
+ { { 5664, 7516, 7516 }, "FONTA_S" },
+ { { 5704, 7524, 7524 }, "PLAYPAL" },
+ { { 5672, 7532, 7532 }, "FINAL1" },
+ { { 5680, 7540, 7540 }, "FINAL2" },
+ { { 5688, 7548, 7548 }, "E2PAL" },
+ { { 5696, 7556, 7556 }, "E2END" },
+ { { 7884, 7564, 7564 }, "TITLE" },
+ { { 5712, 7572, 7572 }, "ORDER" },
+ { { 0, 7580, 7580 }, "CREDIT" },
+ { { 5720, 7588, 7588 }, "IMPX" },
+ { { 5728, 7596, 7596 }, "ACLO" },
+ { { 5736, 7604, 7604 }, "PTN1" },
+ { { 5744, 7612, 7612 }, "SHLD" },
+ { { 5752, 7620, 7620 }, "SHD2" },
+ { { 5760, 7628, 7628 }, "BAGH" },
+ { { 5768, 7636, 7636 }, "SPMP" },
+ { { 5776, 7644, 7644 }, "INVS" },
+ { { 5784, 7652, 7652 }, "PTN2" },
+ { { 5792, 7660, 7660 }, "SOAR" },
+ { { 5800, 7668, 7668 }, "INVU" },
+ { { 5808, 7676, 7676 }, "PWBK" },
+ { { 5816, 7684, 7684 }, "EGGC" },
+ { { 5824, 7692, 7692 }, "EGGM" },
+ { { 5832, 7700, 7700 }, "FX01" },
+ { { 5840, 7708, 7708 }, "SPHL" },
+ { { 5848, 7716, 7716 }, "TRCH" },
+ { { 5856, 7724, 7724 }, "FBMB" },
+ { { 5864, 7732, 7732 }, "XPL1" },
+ { { 5872, 7740, 7740 }, "ATLP" },
+ { { 5880, 7748, 7748 }, "PPOD" },
+ { { 5888, 7756, 7756 }, "AMG1" },
+ { { 5896, 7764, 7764 }, "SPSH" },
+ { { 5904, 7772, 7772 }, "LVAS" },
+ { { 5912, 7780, 7780 }, "SLDG" },
+ { { 5920, 7788, 7788 }, "SKH1" },
+ { { 5928, 7796, 7796 }, "SKH2" },
+ { { 5936, 7804, 7804 }, "SKH3" },
+ { { 5944, 7812, 7812 }, "SKH4" },
+ { { 5952, 7820, 7820 }, "CHDL" },
+ { { 5960, 7828, 7828 }, "SRTC" },
+ { { 5968, 7836, 7836 }, "SMPL" },
+ { { 5976, 7844, 7844 }, "STGS" },
+ { { 5984, 7852, 7852 }, "STGL" },
+ { { 5992, 7860, 7860 }, "STCS" },
+ { { 6000, 7868, 7868 }, "STCL" },
+ { { 6008, 7876, 7876 }, "KFR1" },
+ { { 6016, 7884, 7884 }, "BARL" },
+ { { 6024, 7892, 7892 }, "BRPL" },
+ { { 6032, 7900, 7900 }, "MOS1" },
+ { { 6040, 7908, 7908 }, "MOS2" },
+ { { 6048, 7916, 7916 }, "WTRH" },
+ { { 6056, 7924, 7924 }, "HCOR" },
+ { { 6064, 7932, 7932 }, "KGZ1" },
+ { { 6072, 7940, 7940 }, "KGZB" },
+ { { 6080, 7948, 7948 }, "KGZG" },
+ { { 6088, 7956, 7956 }, "KGZY" },
+ { { 6096, 7964, 7964 }, "VLCO" },
+ { { 6104, 7972, 7972 }, "VFBL" },
+ { { 6112, 7980, 7980 }, "VTFB" },
+ { { 6120, 7988, 7988 }, "SFFI" },
+ { { 6128, 7996, 7996 }, "TGLT" },
+ { { 6136, 8004, 8004 }, "TELE" },
+ { { 6144, 8012, 8012 }, "STFF" },
+ { { 6152, 8020, 8020 }, "PUF3" },
+ { { 6160, 8028, 8028 }, "PUF4" },
+ { { 6168, 8036, 8036 }, "BEAK" },
+ { { 6176, 8044, 8044 }, "WGNT" },
+ { { 6184, 8052, 8052 }, "GAUN" },
+ { { 6192, 8060, 8060 }, "PUF1" },
+ { { 6200, 8068, 8068 }, "WBLS" },
+ { { 6208, 8076, 8076 }, "BLSR" },
+ { { 6216, 8084, 8084 }, "FX18" },
+ { { 6224, 8092, 8092 }, "FX17" },
+ { { 6232, 8100, 8100 }, "WMCE" },
+ { { 6240, 8108, 8108 }, "MACE" },
+ { { 6248, 8116, 8116 }, "FX02" },
+ { { 6256, 8124, 8124 }, "WSKL" },
+ { { 6264, 8132, 8132 }, "HROD" },
+ { { 6272, 8140, 8140 }, "FX00" },
+ { { 6280, 8148, 8148 }, "FX20" },
+ { { 6288, 8156, 8156 }, "FX21" },
+ { { 6296, 8164, 8164 }, "FX22" },
+ { { 6304, 8172, 8172 }, "FX23" },
+ { { 6312, 8180, 8180 }, "GWND" },
+ { { 6320, 8188, 8188 }, "PUF2" },
+ { { 6328, 8196, 8196 }, "WPHX" },
+ { { 6336, 8204, 8204 }, "PHNX" },
+ { { 6344, 8212, 8212 }, "FX04" },
+ { { 6352, 8220, 8220 }, "FX08" },
+ { { 6360, 8228, 8228 }, "FX09" },
+ { { 6368, 8236, 8236 }, "WBOW" },
+ { { 6376, 8244, 8244 }, "CRBW" },
+ { { 6384, 8252, 8252 }, "FX03" },
+ { { 6392, 8260, 8260 }, "BLOD" },
+ { { 6400, 8268, 8268 }, "PLAY" },
+ { { 6408, 8276, 8276 }, "FDTH" },
+ { { 6416, 8284, 8284 }, "BSKL" },
+ { { 6424, 8292, 8292 }, "CHKN" },
+ { { 6432, 8300, 8300 }, "MUMM" },
+ { { 6440, 8308, 8308 }, "FX15" },
+ { { 6448, 8316, 8316 }, "BEAS" },
+ { { 6456, 8324, 8324 }, "FRB1" },
+ { { 6464, 8332, 8332 }, "SNKE" },
+ { { 6472, 8340, 8340 }, "SNFX" },
+ { { 6480, 8348, 8348 }, "HEAD" },
+ { { 6488, 8356, 8356 }, "FX05" },
+ { { 6496, 8364, 8364 }, "FX06" },
+ { { 6504, 8372, 8372 }, "FX07" },
+ { { 6512, 8380, 8380 }, "CLNK" },
+ { { 6520, 8388, 8388 }, "WZRD" },
+ { { 6528, 8396, 8396 }, "FX11" },
+ { { 6536, 8404, 8404 }, "FX10" },
+ { { 6544, 8412, 8412 }, "KNIG" },
+ { { 6552, 8420, 8420 }, "SPAX" },
+ { { 6560, 8428, 8428 }, "RAXE" },
+ { { 6568, 8436, 8436 }, "SRCR" },
+ { { 6576, 8444, 8444 }, "FX14" },
+ { { 6584, 8452, 8452 }, "SOR2" },
+ { { 6592, 8460, 8460 }, "SDTH" },
+ { { 6600, 8468, 8468 }, "FX16" },
+ { { 6608, 8476, 8476 }, "MNTR" },
+ { { 6616, 8484, 8484 }, "FX12" },
+ { { 6624, 8492, 8492 }, "FX13" },
+ { { 6632, 8500, 8500 }, "AKYY" },
+ { { 6640, 8508, 8508 }, "BKYY" },
+ { { 6648, 8516, 8516 }, "CKYY" },
+ { { 6656, 8524, 8524 }, "AMG2" },
+ { { 6664, 8532, 8532 }, "AMM1" },
+ { { 6672, 8540, 8540 }, "AMM2" },
+ { { 6680, 8548, 8548 }, "AMC1" },
+ { { 6688, 8556, 8556 }, "AMC2" },
+ { { 6696, 8564, 8564 }, "AMS1" },
+ { { 6704, 8572, 8572 }, "AMS2" },
+ { { 6712, 8580, 8580 }, "AMP1" },
+ { { 6720, 8588, 8588 }, "AMP2" },
+ { { 6728, 8596, 8596 }, "AMB1" },
+ { { 6736, 8604, 8604 }, "AMB2" },
+ { { 6744, 8612, 8612 }, "K" },
+ { { 6748, 8616, 8616 }, "I" },
+ { { 6752, 8620, 8620 }, "L" },
+ { { 6756, 8624, 8624 }, "E" },
+ { { 6760, 8628, 8628 }, "R" },
+ { { 6764, 8632, 8632 }, "S" },
+ { { 6768, 8636, 8636 }, "PLAYPAL" },
+ { { 6776, 8644, 8644 }, "MAPE1" },
+ { { 6784, 8652, 8652 }, "MAPE2" },
+ { { 6792, 8660, 8660 }, "MAPE3" },
+ { { 6800, 8668, 8668 }, "IN_X" },
+ { { 6808, 8676, 8676 }, "IN_YAH" },
+ { { 6816, 8684, 8684 }, "FONTB16" },
+ { { 6824, 8692, 8692 }, "FONTB_S" },
+ { { 6832, 8700, 8700 }, "FONTB13" },
+ { { 6840, 8708, 8708 }, "FONTB15" },
+ { { 6848, 8716, 8716 }, "FONTB05" },
+ { { 6856, 8724, 8724 }, "FACEA0" },
+ { { 6864, 8732, 8732 }, "FACEB0" },
+ { { 6940, 8808, 8808 }, "FLOOR16" },
+ { { 6948, 8816, 8816 }, "FINISHED" },
+ { { 6960, 8828, 8828 }, "NOW ENTERING:" },
+ { { 6976, 8844, 8844 }, "KILLS" },
+ { { 6984, 8852, 8852 }, "ITEMS" },
+ { { 6992, 8860, 8860 }, "SECRETS" },
+ { { 7000, 8868, 8868 }, "TIME" },
+ { { 7008, 8876, 8876 }, "BONUS" },
+ { { 7016, 8884, 8884 }, "SECRET" },
+ { { 7024, 8892, 8892 }, "TOTAL" },
+ { { 7032, 8900, 8900 }, "VICTIMS" },
+ { { 7040, 8908, 8908 }, ":" },
+ { { 7044, 8912, 8912 }, "NEW GAME" },
+ { { 7056, 8924, 8924 }, "OPTIONS" },
+ { { 7064, 8932, 8932 }, "GAME FILES" },
+ { { 7076, 8944, 8944 }, "INFO" },
+ { { 7084, 8952, 8952 }, "QUIT GAME" },
+ { { 7096, 8964, 8964 }, "CITY OF THE DAMNED" },
+ { { 7116, 8984, 8984 }, "HELL'S MAW" },
+ { { 7128, 8996, 8996 }, "THE DOME OF D'SPARIL" },
+ { { 0, 9020, 9020 }, "THE OSSUARY" },
+ { { 0, 9032, 9032 }, "THE STAGNANT DEMESNE" },
+ { { 7152, 9056, 9056 }, "LOAD GAME" },
+ { { 7164, 9068, 9068 }, "SAVE GAME" },
+ { { 7176, 9080, 9080 }, "THOU NEEDETH A WET-NURSE" },
+ { { 7204, 9108, 9108 }, "YELLOWBELLIES-R-US" },
+ { { 7224, 9128, 9128 }, "BRINGEST THEM ONETH" },
+ { { 7244, 9148, 9148 }, "THOU ART A SMITE-MEISTER" },
+ { { 7272, 9176, 9176 }, "BLACK PLAGUE POSSESSES THEE" },
+ { { 7300, 9204, 9204 }, "END GAME" },
+ { { 7312, 9216, 9216 }, "MESSAGES : " },
+ { { 7324, 9228, 9228 }, "MOUSE SENSITIVITY" },
+ { { 7344, 9248, 9248 }, "MORE..." },
+ { { 7352, 9256, 9256 }, "SCREEN SIZE" },
+ { { 7364, 9268, 9268 }, "SFX VOLUME" },
+ { { 7376, 9280, 9280 }, "MUSIC VOLUME" },
+ { { 7416, 9296, 9296 }, "ARE YOU SURE YOU WANT TO QUIT?" },
+ { { 7448, 9328, 9328 }, "ARE YOU SURE YOU WANT TO END THE GAME?" },
+ { { 7488, 9368, 9368 }, "DO YOU WANT TO QUICKSAVE THE GAME NAMED" },
+ { { 7528, 9408, 9408 }, "DO YOU WANT TO QUICKLOAD THE GAME NAMED" },
+ { { 7392, 9448, 9448 }, "M_SKL00" },
+ { { 7400, 9456, 9456 }, "FONTA_S" },
+ { { 7408, 9464, 9464 }, "FONTB_S" },
+ { { 7568, 9472, 9472 }, "?" },
+ { { 7572, 9476, 9476 }, "M_SLCTR1" },
+ { { 7584, 9488, 9488 }, "M_SLCTR2" },
+ { { 7596, 9500, 9500 }, "M_HTIC" },
+ { { 7604, 9508, 9508 }, "c:\\heretic.cd\\hticsav%d.hsg" },
+ { { 7632, 9536, 9536 }, "hticsav%d.hsg" },
+ { { 7652, 9556, 9556 }, "M_FSLOT" },
+ { { 7660, 9564, 9564 }, "ON" },
+ { { 7664, 9568, 9568 }, "OFF" },
+ { { 0, 9572, 9572 }, "YOU CAN'T START A NEW GAME IN NETPLAY!" },
+ { { 0, 9612, 9612 }, "YOU CAN'T LOAD A GAME IN NETPLAY!" },
+ { { 7668, 9648, 9648 }, "MESSAGES ON" },
+ { { 7680, 9660, 9660 }, "MESSAGES OFF" },
+ { { 7748, 9676, 9676 }, "ONLY AVAILABLE IN THE REGISTERED VERSION" },
+ { { 7792, 9720, 9720 }, "PLAYPAL" },
+ { { 7800, 9728, 9728 }, "QUICKSAVING...." },
+ { { 7816, 9744, 9744 }, "QUICKLOADING...." },
+ { { 7836, 9764, 9764 }, "CHOOSE A QUICKSAVE SLOT" },
+ { { 7860, 9788, 9788 }, "CHOOSE A QUICKLOAD SLOT" },
+ { { 0, 9812, 9812 }, "TITLE" },
+ { { 7892, 9820, 9820 }, "M_SLDLT" },
+ { { 7900, 9828, 9828 }, "M_SLDMD1" },
+ { { 7912, 9840, 9840 }, "M_SLDMD2" },
+ { { 7924, 9852, 9852 }, "M_SLDRT" },
+ { { 7932, 9860, 9860 }, "M_SLDKB" },
+ { { 9016, 10944, 10944 }, "SCREEN SHOT" },
+ { { 9028, 10956, 10956 }, "YOU NEED A BLUE KEY TO OPEN THIS DOOR" },
+ { { 9068, 10996, 10996 }, "YOU NEED A YELLOW KEY TO OPEN THIS DOOR" },
+ { { 9108, 11036, 11036 }, "YOU NEED A GREEN KEY TO OPEN THIS DOOR" },
+ { { 9244, 11172, 11172 }, "CRYSTAL VIAL" },
+ { { 9260, 11188, 11188 }, "SILVER SHIELD" },
+ { { 9276, 11204, 11204 }, "ENCHANTED SHIELD" },
+ { { 9296, 11224, 11224 }, "BAG OF HOLDING" },
+ { { 9312, 11240, 11240 }, "MAP SCROLL" },
+ { { 9324, 11252, 11252 }, "BLUE KEY" },
+ { { 9336, 11264, 11264 }, "YELLOW KEY" },
+ { { 9348, 11276, 11276 }, "GREEN KEY" },
+ { { 9360, 11288, 11288 }, "QUARTZ FLASK" },
+ { { 9376, 11304, 11304 }, "WINGS OF WRATH" },
+ { { 9392, 11320, 11320 }, "RING OF INVINCIBILITY" },
+ { { 9416, 11344, 11344 }, "TOME OF POWER" },
+ { { 9432, 11360, 11360 }, "SHADOWSPHERE" },
+ { { 9448, 11376, 11376 }, "MORPH OVUM" },
+ { { 9460, 11388, 11388 }, "MYSTIC URN" },
+ { { 9472, 11400, 11400 }, "TORCH" },
+ { { 9480, 11408, 11408 }, "TIME BOMB OF THE ANCIENTS" },
+ { { 9508, 11436, 11436 }, "CHAOS DEVICE" },
+ { { 9524, 11452, 11452 }, "WAND CRYSTAL" },
+ { { 9540, 11468, 11468 }, "CRYSTAL GEODE" },
+ { { 9556, 11484, 11484 }, "MACE SPHERES" },
+ { { 9572, 11500, 11500 }, "PILE OF MACE SPHERES" },
+ { { 9596, 11524, 11524 }, "ETHEREAL ARROWS" },
+ { { 9612, 11540, 11540 }, "QUIVER OF ETHEREAL ARROWS" },
+ { { 9640, 11568, 11568 }, "CLAW ORB" },
+ { { 9652, 11580, 11580 }, "ENERGY ORB" },
+ { { 9664, 11592, 11592 }, "LESSER RUNES" },
+ { { 9680, 11608, 11608 }, "GREATER RUNES" },
+ { { 9696, 11624, 11624 }, "FLAME ORB" },
+ { { 9708, 11636, 11636 }, "INFERNO ORB" },
+ { { 9720, 11648, 11648 }, "FIREMACE" },
+ { { 9732, 11660, 11660 }, "ETHEREAL CROSSBOW" },
+ { { 9752, 11680, 11680 }, "DRAGON CLAW" },
+ { { 9764, 11692, 11692 }, "HELLSTAFF" },
+ { { 9776, 11704, 11704 }, "PHOENIX ROD" },
+ { { 9788, 11716, 11716 }, "GAUNTLETS OF THE NECROMANCER" },
+ { { 10088, 12016, 12016 }, "FLTWAWA1" },
+ { { 10100, 12028, 12028 }, "FLTFLWW1" },
+ { { 10112, 12040, 12040 }, "FLTLAVA1" },
+ { { 10124, 12052, 12052 }, "FLATHUH1" },
+ { { 10136, 12064, 12064 }, "FLTSLUD1" },
+ { { 10148, 12076, 12076 }, "END" },
+ { { 10236, 12164, 12164 }, "texture2" },
+ { { 10444, 12372, 12372 }, "PLAYPAL" },
+ { { 10596, 12488, 12488 }, "PNAMES" },
+ { { 10604, 12496, 12496 }, "TEXTURE1" },
+ { { 10616, 12508, 12508 }, "TEXTURE2" },
+ { { 10628, 12520, 12520 }, "S_END" },
+ { { 10636, 12528, 12528 }, "S_START" },
+ { { 10728, 12620, 12620 }, "F_START" },
+ { { 10736, 12628, 12628 }, "F_END" },
+ { { 10744, 12636, 12636 }, "COLORMAP" },
+ { { 10756, 12648, 12648 }, "\nR_InitTextures " },
+ { { 10776, 12668, 12668 }, "R_InitFlats\n" },
+ { { 10792, 12684, 12684 }, "R_InitSpriteLumps " },
+ { { 10948, 12772, 12772 }, "TINTTAB" },
+ { { 10984, 12780, 12780 }, "FLOOR04" },
+ { { 10992, 12788, 12788 }, "FLAT513" },
+ { { 11000, 12796, 12796 }, "bordt" },
+ { { 11008, 12804, 12804 }, "bordb" },
+ { { 11016, 12812, 12812 }, "bordl" },
+ { { 11024, 12820, 12820 }, "bordr" },
+ { { 11032, 12828, 12828 }, "bordtl" },
+ { { 11040, 12836, 12836 }, "bordtr" },
+ { { 11048, 12844, 12844 }, "bordbr" },
+ { { 11056, 12852, 12852 }, "bordbl" },
+ { { 11064, 12860, 12860 }, "R_InitData " },
+ { { 11076, 12872, 12872 }, "R_InitPointToAngle\n" },
+ { { 11096, 12892, 12892 }, "R_InitTables " },
+ { { 11112, 12908, 12908 }, "R_InitPlanes\n" },
+ { { 11128, 12924, 12924 }, "R_InitLightTables " },
+ { { 11148, 12944, 12944 }, "R_InitSkyMap\n" },
+ { { 11164, 12960, 12960 }, "F_SKY1" },
+ { { 12120, 13484, 13484 }, "LTFACE" },
+ { { 12128, 13492, 13492 }, "RTFACE" },
+ { { 12136, 13500, 13500 }, "BARBACK" },
+ { { 12144, 13508, 13508 }, "INVBAR" },
+ { { 12152, 13516, 13516 }, "CHAIN" },
+ { { 12160, 13524, 13524 }, "STATBAR" },
+ { { 12168, 13532, 13532 }, "LIFEBAR" },
+ { { 12176, 13540, 13540 }, "LIFEGEM2" },
+ { { 12188, 13552, 13552 }, "LIFEGEM0" },
+ { { 12200, 13564, 13564 }, "LTFCTOP" },
+ { { 12208, 13572, 13572 }, "RTFCTOP" },
+ { { 12224, 13580, 13580 }, "SELECTBOX" },
+ { { 12236, 13592, 13592 }, "INVGEML1" },
+ { { 12248, 13604, 13604 }, "INVGEML2" },
+ { { 12260, 13616, 13616 }, "INVGEMR1" },
+ { { 12272, 13628, 13628 }, "INVGEMR2" },
+ { { 12284, 13640, 13640 }, "BLACKSQ" },
+ { { 12292, 13648, 13648 }, "ARMCLEAR" },
+ { { 12304, 13660, 13660 }, "CHAINBACK" },
+ { { 12316, 13672, 13672 }, "IN0" },
+ { { 12320, 13676, 13676 }, "NEGNUM" },
+ { { 12328, 13684, 13684 }, "FONTB16" },
+ { { 12336, 13692, 13692 }, "SMALLIN0" },
+ { { 12348, 13704, 13704 }, "PLAYPAL" },
+ { { 12356, 13712, 13712 }, "SPINBK0" },
+ { { 12364, 13720, 13720 }, "SPFLY0" },
+ { { 12372, 13728, 13728 }, "LAME" },
+ { { 12380, 13736, 13736 }, "*** SOUND DEBUG INFO ***" },
+ { { 12408, 13764, 13764 }, "NAME" },
+ { { 12416, 13772, 13772 }, "MO.T" },
+ { { 12424, 13780, 13780 }, "MO.X" },
+ { { 12432, 13788, 13788 }, "MO.Y" },
+ { { 12440, 13796, 13796 }, "ID" },
+ { { 12444, 13800, 13800 }, "PRI" },
+ { { 12448, 13804, 13804 }, "DIST" },
+ { { 12456, 13812, 13812 }, "------" },
+ { { 12464, 13820, 13820 }, "%s" },
+ { { 12468, 13824, 13824 }, "%d" },
+ { { 12472, 13828, 13828 }, "GOD1" },
+ { { 12480, 13836, 13836 }, "GOD2" },
+ { { 12488, 13844, 13844 }, "useartia" },
+ { { 12500, 13856, 13856 }, "ykeyicon" },
+ { { 12512, 13868, 13868 }, "gkeyicon" },
+ { { 12524, 13880, 13880 }, "bkeyicon" },
+ { { 12216, 13892, 13892 }, "ARTIBOX" },
+ { { 12536, 13900, 13900 }, "GOD MODE ON" },
+ { { 12548, 13912, 13912 }, "GOD MODE OFF" },
+ { { 12564, 13928, 13928 }, "NO CLIPPING ON" },
+ { { 12580, 13944, 13944 }, "NO CLIPPING OFF" },
+ { { 12596, 13960, 13960 }, "ALL WEAPONS" },
+ { { 12608, 13972, 13972 }, "POWER OFF" },
+ { { 12620, 13984, 13984 }, "POWER ON" },
+ { { 12632, 13996, 13996 }, "FULL HEALTH" },
+ { { 12644, 14008, 14008 }, "ALL KEYS" },
+ { { 12656, 14020, 14020 }, "SOUND DEBUG ON" },
+ { { 12672, 14036, 14036 }, "SOUND DEBUG OFF" },
+ { { 12688, 14052, 14052 }, "TICKER ON" },
+ { { 12700, 14064, 14064 }, "TICKER OFF" },
+ { { 12712, 14076, 14076 }, "CHOOSE AN ARTIFACT ( A - J )" },
+ { { 12744, 14108, 14108 }, "HOW MANY ( 1 - 9 )" },
+ { { 12764, 14128, 14128 }, "YOU GOT IT" },
+ { { 12776, 14140, 14140 }, "BAD INPUT" },
+ { { 12788, 14152, 14152 }, "LEVEL WARP" },
+ { { 12800, 14164, 14164 }, "CHICKEN OFF" },
+ { { 12812, 14176, 14176 }, "CHICKEN ON" },
+ { { 12824, 14188, 14188 }, "MASSACRE" },
+ { { 12836, 14200, 14200 }, "CHEATER - YOU DON'T DESERVE WEAPONS" },
+ { { 12872, 14236, 14236 }, "TRYING TO CHEAT, EH? NOW YOU DIE!" },
+};
+
+// String offsets that are valid but we don't support.
+
+static const int unsupported_strings_1_0[] =
+{
+ 0, 4, 64, 104, 160, 200, 220, 236,
+ 244, 252, 272, 288, 296, 316, 332, 372,
+ 436, 500, 504, 536, 544, 560, 576, 584,
+ 592, 612, 640, 664, 708, 712, 744, 764,
+ 808, 820, 828, 840, 876, 884, 908, 952,
+ 992, 1028, 1036, 1048, 1088, 1128, 1160, 1192,
+ 1212, 1912, 2044, 2056, 2068, 2128, 2140, 2168,
+ 2184, 2196, 2212, 2228, 2240, 2252, 2260, 2264,
+ 2284, 2292, 2296, 2300, 2328, 2340, 2352, 2364,
+ 2372, 2384, 2388, 2404, 2428, 2436, 2444, 2464,
+ 2496, 2508, 2520, 2552, 2564, 2572, 2584, 3120,
+ 3128, 3140, 3184, 3220, 3248, 3252, 3256, 3280,
+ 3304, 3320, 3352, 3380, 3400, 3432, 3464, 3548,
+ 3600, 3624, 3664, 3696, 3812, 3872, 3932, 3940,
+ 3976, 3996, 6872, 6896, 7648, 7696, 7940, 7964,
+ 7968, 7992, 8020, 8028, 8052, 8056, 8076, 8088,
+ 8104, 8116, 8128, 8136, 8148, 8164, 8180, 8192,
+ 8204, 8220, 8232, 8248, 8264, 8276, 8292, 8308,
+ 8320, 8328, 8340, 8352, 8364, 8376, 8392, 8408,
+ 8424, 8436, 8448, 8460, 8472, 8488, 8504, 8520,
+ 8536, 8548, 8560, 8572, 8584, 8596, 8608, 8612,
+ 8624, 8648, 8660, 8668, 8680, 8708, 8720, 8728,
+ 8740, 8752, 8764, 8788, 8800, 8812, 8824, 8848,
+ 8860, 8864, 8868, 8876, 8888, 8896, 8916, 8944,
+ 8948, 8960, 8964, 8968, 8980, 9148, 9172, 9212,
+ 9216, 9220, 9820, 9860, 9892, 9940, 9972, 10012,
+ 10036, 10040, 10052, 10080, 10152, 10192, 10248, 10284,
+ 10320, 10360, 10392, 10452, 10488, 10508, 10556, 10644,
+ 10684, 10812, 10844, 10880, 10912, 10956, 11172, 11200,
+ 11232, 11272, 11312, 11348, 11380, 11404, 11436, 11492,
+ 11548, 11616, 11684, 11748, 11792, 11840, 11896, 11936,
+ 11980, 12028, 12072, 12908, 12924, 12956, 12960, 12968,
+ 12976, 13020, 13048, 13076, 13104, 13136, 13168, 13196,
+ 13240, 13272, 13292, 13296, 13308, 13312, 13320, 13324,
+ 13364, 13408, 13460, 13492, 13516, 13560, 13612, 13664,
+ 13700, 13744, 13796, 13848, 13884, 13940, 13996, 14040,
+ 14084, 14140, 14148, 14156, 14164, 14184, 14192, 14204,
+ 14208, 14212, 14256, 14272, 14284, 14296, 14300, 14312,
+ 14320, 14324, 14348, 14356, 14360, 14372, 14380, 14392,
+ 14432, 14440, 14444, 14472, 14496, 14516, 14536, 14548,
+ 14560, 14572, 14580, 14588, 14596, 14604, 14612, 14620,
+ 14636, 14660, 14704, 14740, 14748, 14756, 14760, 14768,
+ -1,
+};
+
+static const int unsupported_strings_1_2[] =
+{
+ 0, 4, 64, 104, 160, 200, 220, 236,
+ 244, 252, 272, 288, 296, 316, 332, 372,
+ 436, 500, 504, 536, 544, 560, 576, 584,
+ 592, 612, 640, 664, 708, 712, 744, 756,
+ 776, 820, 832, 840, 852, 888, 896, 920,
+ 964, 1004, 1040, 1048, 1060, 1100, 1140, 1172,
+ 1204, 1224, 2312, 2436, 2448, 2464, 2480, 2492,
+ 2512, 2524, 2536, 2596, 2608, 2636, 2652, 2656,
+ 2676, 2684, 2688, 2720, 2732, 2744, 2752, 2764,
+ 2772, 2776, 2792, 2816, 2824, 2832, 2852, 2884,
+ 2896, 2908, 2940, 2952, 2960, 2972, 3520, 3528,
+ 3540, 3584, 3620, 3648, 3652, 3656, 3680, 3704,
+ 3720, 3776, 3804, 3824, 3856, 3888, 4020, 4044,
+ 4084, 4116, 4156, 4272, 4288, 4296, 4332, 4352,
+ 4428, 4432, 8740, 8764, 9552, 9868, 9888, 9900,
+ 9916, 9928, 9940, 9948, 9960, 9976, 9992, 10004,
+ 10016, 10032, 10044, 10060, 10076, 10088, 10104, 10120,
+ 10132, 10140, 10152, 10164, 10176, 10188, 10204, 10220,
+ 10236, 10248, 10260, 10272, 10284, 10300, 10316, 10332,
+ 10348, 10360, 10372, 10384, 10396, 10408, 10420, 10424,
+ 10436, 10460, 10472, 10480, 10492, 10520, 10532, 10540,
+ 10552, 10564, 10576, 10600, 10612, 10624, 10636, 10660,
+ 10672, 10676, 10700, 10704, 10728, 10756, 10764, 10788,
+ 10792, 10796, 10804, 10816, 10824, 10844, 10872, 10876,
+ 10888, 10892, 10896, 10908, 11076, 11100, 11140, 11144,
+ 11148, 11748, 11788, 11820, 11868, 11900, 11940, 11964,
+ 11968, 11980, 12008, 12080, 12120, 12176, 12212, 12248,
+ 12288, 12320, 12380, 12400, 12448, 12536, 12576, 12704,
+ 12736, 12968, 13000, 13024, 13080, 13136, 13204, 13272,
+ 13336, 13380, 13428, 14272, 14288, 14320, 14324, 14332,
+ 14340, 14384, 14412, 14440, 14468, 14500, 14532, 14560,
+ 14604, 14636, 14656, 14696, 14740, 14792, 14824, 14848,
+ 14892, 14944, 14996, 15032, 15076, 15128, 15180, 15216,
+ 15272, 15328, 15372, 15416, 15472, 15480, 15488, 15496,
+ 15516, 15524, 15536, 15540, 15544, 15588, 15604, 15616,
+ 15628, 15632, 15644, 15652, 15656, 15680, 15688, 15692,
+ 15704, 15712, 15724, 15764, 15772, 15776, 15804, 15828,
+ 15848, 15868, 15880, 15892, 15904, 15912, 15920, 15928,
+ 15936, -1,
+};
+
+static const int unsupported_strings_1_3[] =
+{
+ 0, 4, 64, 104, 160, 200, 220, 236,
+ 244, 252, 272, 288, 296, 316, 332, 372,
+ 436, 500, 504, 536, 544, 560, 576, 584,
+ 592, 612, 640, 664, 708, 712, 744, 756,
+ 776, 820, 832, 840, 852, 888, 896, 920,
+ 964, 1004, 1040, 1048, 1060, 1100, 1140, 1172,
+ 1204, 1224, 2312, 2436, 2448, 2464, 2480, 2492,
+ 2512, 2524, 2536, 2596, 2608, 2636, 2652, 2656,
+ 2676, 2684, 2688, 2720, 2732, 2744, 2752, 2764,
+ 2772, 2776, 2792, 2816, 2824, 2832, 2852, 2884,
+ 2896, 2908, 2940, 2952, 2960, 2972, 3520, 3528,
+ 3540, 3584, 3620, 3648, 3652, 3656, 3680, 3704,
+ 3720, 3776, 3804, 3824, 3856, 3888, 4020, 4044,
+ 4084, 4116, 4156, 4272, 4288, 4296, 4332, 4352,
+ 4428, 4432, 8740, 8764, 9552, 9868, 9888, 9900,
+ 9916, 9928, 9940, 9948, 9960, 9976, 9992, 10004,
+ 10016, 10032, 10044, 10060, 10076, 10088, 10104, 10120,
+ 10132, 10140, 10152, 10164, 10176, 10188, 10204, 10220,
+ 10236, 10248, 10260, 10272, 10284, 10300, 10316, 10332,
+ 10348, 10360, 10372, 10384, 10396, 10408, 10420, 10424,
+ 10436, 10460, 10472, 10480, 10492, 10520, 10532, 10540,
+ 10552, 10564, 10576, 10600, 10612, 10624, 10636, 10660,
+ 10672, 10676, 10700, 10704, 10728, 10756, 10764, 10788,
+ 10792, 10796, 10804, 10816, 10824, 10844, 10872, 10876,
+ 10888, 10892, 10896, 10908, 11076, 11100, 11140, 11144,
+ 11148, 11748, 11788, 11820, 11868, 11900, 11940, 11964,
+ 11968, 11980, 12008, 12080, 12120, 12176, 12212, 12248,
+ 12288, 12320, 12380, 12400, 12448, 12536, 12576, 12704,
+ 12736, 12968, 13000, 13024, 13080, 13136, 13204, 13272,
+ 13336, 13380, 13428, 14272, 14288, 14320, 14324, 14332,
+ 14340, 14384, 14412, 14440, 14468, 14500, 14532, 14560,
+ 14604, 14636, 14656, 14696, 14740, 14792, 14824, 14848,
+ 14892, 14944, 14996, 15032, 15076, 15128, 15180, 15216,
+ 15272, 15328, 15372, 15416, 15472, 15480, 15488, 15496,
+ 15516, 15524, 15536, 15540, 15544, 15588, 15604, 15616,
+ 15628, 15632, 15644, 15652, 15656, 15680, 15688, 15692,
+ 15704, 15712, 15724, 15764, 15772, 15776, 15804, 15828,
+ 15848, 15868, 15880, 15892, 15904, 15912, 15920, 15928,
+ 15936, -1,
+};
+
+static const int *unsupported_strings[] =
+{
+ unsupported_strings_1_0,
+ unsupported_strings_1_2,
+ unsupported_strings_1_3,
+};
+
+static boolean StringIsUnsupported(unsigned int offset)
+{
+ const int *string_list;
+ int i;
+
+ string_list = unsupported_strings[deh_hhe_version];
+
+ for (i=0; string_list[i] >= 0; ++i)
+ {
+ if ((unsigned int) string_list[i] == offset)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static boolean GetStringByOffset(unsigned int offset, char **result)
+{
+ int i;
+
+ for (i=0; i<arrlen(strings); ++i)
+ {
+ if (strings[i].offsets[deh_hhe_version] == offset)
+ {
+ *result = strings[i].string;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// Given a string length, find the maximum length of a
+// string that can replace it.
+
+static int MaxStringLength(int len)
+{
+ // Enough bytes for the string and the NUL terminator
+
+ len += 1;
+
+ // All strings in doom.exe are on 4-byte boundaries, so we may be able
+ // to support a slightly longer string.
+ // Extend up to the next 4-byte boundary
+
+ len += (4 - (len % 4)) % 4;
+
+ // Less one for the NUL terminator.
+
+ return len - 1;
+}
+
+// If a string offset does not match any string, it may be because
+// we are running in the wrong version mode, and the patch was generated
+// for a different Heretic version. Search the lookup tables to find
+// versiosn that match.
+
+static void SuggestOtherVersions(unsigned int offset)
+{
+ const int *string_list;
+ unsigned int i;
+ unsigned int v;
+
+ // Check main string table.
+
+ for (i=0; i<arrlen(strings); ++i)
+ {
+ for (v=0; v<deh_hhe_num_versions; ++v)
+ {
+ if (strings[i].offsets[v] == offset)
+ {
+ DEH_SuggestHereticVersion(v);
+ }
+ }
+ }
+
+ // Check unsupported string tables.
+
+ for (v=0; v<deh_hhe_num_versions; ++v)
+ {
+ string_list = unsupported_strings[v];
+
+ for (i=0; string_list[i] >= 0; ++i)
+ {
+ if (string_list[i] == offset)
+ {
+ DEH_SuggestHereticVersion(v);
+ }
+ }
+ }
+}
+
+static void *DEH_TextStart(deh_context_t *context, char *line)
+{
+ char *repl_text;
+ char *orig_text;
+ int orig_offset, repl_len;
+ int i;
+
+ if (sscanf(line, "Text %i %i", &orig_offset, &repl_len) != 2)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ repl_text = Z_Malloc(repl_len + 1, PU_STATIC, NULL);
+
+ // read in the "to" text
+
+ for (i=0; i<repl_len; ++i)
+ {
+ int c;
+
+ c = DEH_GetChar(context);
+
+ repl_text[i] = c;
+ }
+ repl_text[repl_len] = '\0';
+
+ // We don't support all strings, but at least recognise them:
+
+ if (StringIsUnsupported(orig_offset))
+ {
+ DEH_Warning(context, "Unsupported string replacement: %i", orig_offset);
+ }
+
+ // Find the string to replace:
+
+ else if (!GetStringByOffset(orig_offset, &orig_text))
+ {
+ SuggestOtherVersions(orig_offset);
+ DEH_Error(context, "Unknown string offset: %i", orig_offset);
+ }
+
+ // Only allow string replacements that are possible in Vanilla Doom.
+ // Chocolate Doom is unforgiving!
+
+ else if (!deh_allow_long_strings
+ && repl_len > MaxStringLength(strlen(orig_text)))
+ {
+ DEH_Error(context, "Replacement string is longer than the maximum "
+ "possible in heretic.exe");
+ }
+ else
+ {
+ // Success.
+
+ DEH_AddStringReplacement(orig_text, repl_text);
+
+ return NULL;
+ }
+
+ // Failure.
+
+ Z_Free(repl_text);
+
+ return NULL;
+}
+
+static void DEH_TextParseLine(deh_context_t *context, char *line, void *tag)
+{
+ // not used
+}
+
+deh_section_t deh_section_heretic_text =
+{
+ "Text",
+ NULL,
+ DEH_TextStart,
+ DEH_TextParseLine,
+ NULL,
+ NULL,
+};
+
diff --git a/src/heretic/deh_htic.c b/src/heretic/deh_htic.c
new file mode 100644
index 00000000..440fde96
--- /dev/null
+++ b/src/heretic/deh_htic.c
@@ -0,0 +1,186 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Top-level dehacked definitions for Heretic dehacked (HHE).
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "deh_defs.h"
+#include "deh_main.h"
+#include "deh_htic.h"
+#include "info.h"
+#include "m_argv.h"
+
+char *deh_signatures[] =
+{
+ "Patch File for HHE v1.0",
+ "Patch File for HHE v1.1",
+ NULL
+};
+
+static char *hhe_versions[] =
+{
+ "1.0", "1.2", "1.3"
+};
+
+// Version number for patches.
+
+deh_hhe_version_t deh_hhe_version = deh_hhe_1_0;
+
+// deh_ammo.c:
+extern deh_section_t deh_section_ammo;
+// deh_frame.c:
+extern deh_section_t deh_section_frame;
+// deh_ptr.c:
+extern deh_section_t deh_section_pointer;
+// deh_sound.c
+extern deh_section_t deh_section_sound;
+// deh_htext.c:
+extern deh_section_t deh_section_heretic_text;
+// deh_thing.c:
+extern deh_section_t deh_section_thing;
+// deh_weapon.c:
+extern deh_section_t deh_section_weapon;
+
+//
+// List of section types:
+//
+
+deh_section_t *deh_section_types[] =
+{
+ &deh_section_ammo,
+ &deh_section_frame,
+// &deh_section_pointer, TODO
+ &deh_section_sound,
+ &deh_section_heretic_text,
+ &deh_section_thing,
+ &deh_section_weapon,
+ NULL
+};
+
+static void SetHHEVersionByName(char *name)
+{
+ int i;
+
+ for (i=0; i<arrlen(hhe_versions); ++i)
+ {
+ if (!strcmp(hhe_versions[i], name))
+ {
+ deh_hhe_version = i;
+ return;
+ }
+ }
+
+ fprintf(stderr, "Unknown Heretic version: %s\n", name);
+ fprintf(stderr, "Valid versions:\n");
+
+ for (i=0; i<arrlen(hhe_versions); ++i)
+ {
+ fprintf(stderr, "\t%s\n", hhe_versions[i]);
+ }
+}
+
+// Initialize Heretic(HHE)-specific dehacked bits.
+
+void DEH_HereticInit(void)
+{
+ int i;
+
+ //!
+ // @arg <version>
+ //
+ // Select the Heretic version number that was used to generate the
+ // HHE patch to be loaded. Patches for each of the Vanilla
+ // Heretic versions (1.0, 1.2, 1.3) can be loaded, but the correct
+ // version number must be specified.
+
+ i = M_CheckParm("-hhever");
+
+ if (i > 0)
+ {
+ SetHHEVersionByName(myargv[i + 1]);
+ }
+
+ // For v1.0 patches, we must apply a slight change to the states[]
+ // table. The table was changed between 1.0 and 1.3 to add two extra
+ // frames to the player "burning death" animation.
+ //
+ // If we are using a v1.0 patch, we must change the table to cut
+ // these out again.
+
+ if (deh_hhe_version < deh_hhe_1_2)
+ {
+ states[S_PLAY_FDTH18].nextstate = S_NULL;
+ }
+}
+
+int DEH_MapHereticFrameNumber(int frame)
+{
+ if (deh_hhe_version < deh_hhe_1_2)
+ {
+ // Between Heretic 1.0 and 1.2, two new frames
+ // were added to the "states" table, to extend the "flame death"
+ // animation displayed when the player is killed by fire. Therefore,
+ // we must map Heretic 1.0 frame numbers to corresponding indexes
+ // for our state table.
+
+ if (frame >= S_PLAY_FDTH19)
+ {
+ frame = (frame - S_PLAY_FDTH19) + S_BLOODYSKULL1;
+ }
+ }
+ else
+ {
+ // After Heretic 1.2, three unused frames were removed from the
+ // states table, unused phoenix rod frames. Our state table includes
+ // these missing states for backwards compatibility. We must therefore
+ // adjust frame numbers for v1.2/v1.3 to corresponding indexes for
+ // our state table.
+
+ if (frame >= S_PHOENIXFXIX_1)
+ {
+ frame = (frame - S_PHOENIXFXIX_1) + S_PHOENIXPUFF1;
+ }
+ }
+
+ return frame;
+}
+
+void DEH_SuggestHereticVersion(deh_hhe_version_t version)
+{
+ fprintf(stderr,
+ "\n"
+ "This patch may be for version %s. You are currently running in\n"
+ "Heretic %s mode. For %s mode, this mode, add this to your command line:\n"
+ "\n"
+ "\t-hhever %s\n"
+ "\n",
+ hhe_versions[version],
+ hhe_versions[deh_hhe_version],
+ hhe_versions[version],
+ hhe_versions[version]);
+}
+
diff --git a/src/heretic/deh_htic.h b/src/heretic/deh_htic.h
new file mode 100644
index 00000000..7855da8c
--- /dev/null
+++ b/src/heretic/deh_htic.h
@@ -0,0 +1,60 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2010 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Common header for Heretic dehacked (HHE) support.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef DEH_HTIC_H
+#define DEH_HTIC_H
+
+#include "info.h"
+
+// HHE executable version. Loading HHE patches is (unfortunately)
+// dependent on the version of the Heretic executable used to make them.
+
+typedef enum
+{
+ deh_hhe_1_0,
+ deh_hhe_1_2,
+ deh_hhe_1_3,
+ deh_hhe_num_versions
+} deh_hhe_version_t;
+
+// HHE doesn't know about the last two states in the state table, so
+// these are considered invalid.
+
+#define DEH_HERETIC_NUMSTATES (NUMSTATES - 2)
+
+// It also doesn't know about the last two things in the mobjinfo table
+// (which correspond to the states above)
+
+#define DEH_HERETIC_NUMMOBJTYPES (NUMMOBJTYPES - 2)
+
+void DEH_HereticInit(void);
+int DEH_MapHereticFrameNumber(int frame);
+void DEH_SuggestHereticVersion(deh_hhe_version_t version);
+
+extern deh_hhe_version_t deh_hhe_version;
+
+#endif /* #ifndef DEH_HTIC_H */
+
diff --git a/src/heretic/deh_sound.c b/src/heretic/deh_sound.c
new file mode 100644
index 00000000..d1f266dd
--- /dev/null
+++ b/src/heretic/deh_sound.c
@@ -0,0 +1,118 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Sound" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "doomfeatures.h"
+#include "doomtype.h"
+#include "deh_defs.h"
+#include "deh_main.h"
+#include "deh_mapping.h"
+
+#include "doomdef.h"
+#include "i_sound.h"
+
+#include "sounds.h"
+
+DEH_BEGIN_MAPPING(sound_mapping, sfxinfo_t)
+ DEH_MAPPING_STRING("Name", name)
+ DEH_UNSUPPORTED_MAPPING("Special")
+ DEH_MAPPING("Value", priority)
+ DEH_MAPPING("Unknown 1", usefulness)
+ DEH_UNSUPPORTED_MAPPING("Unknown 2")
+ DEH_UNSUPPORTED_MAPPING("Unknown 3")
+ DEH_MAPPING("One/Two", numchannels)
+DEH_END_MAPPING
+
+static void *DEH_SoundStart(deh_context_t *context, char *line)
+{
+ int sound_number = 0;
+
+ if (sscanf(line, "Sound %i", &sound_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ if (sound_number < 0 || sound_number >= NUMSFX)
+ {
+ DEH_Warning(context, "Invalid sound number: %i", sound_number);
+ return NULL;
+ }
+
+ if (sound_number >= DEH_VANILLA_NUMSFX)
+ {
+ DEH_Warning(context, "Attempt to modify SFX %i. This will cause "
+ "problems in Vanilla dehacked.", sound_number);
+ }
+
+ return &S_sfx[sound_number];
+}
+
+static void DEH_SoundParseLine(deh_context_t *context, char *line, void *tag)
+{
+ sfxinfo_t *sfx;
+ char *variable_name, *value;
+
+ if (tag == NULL)
+ return;
+
+ sfx = (sfxinfo_t *) tag;
+
+ // Parse the assignment
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ // Set the field value:
+
+ if (!strcasecmp(variable_name, "Name"))
+ {
+ DEH_SetStringMapping(context, &sound_mapping, sfx,
+ variable_name, value);
+ }
+ else
+ {
+ DEH_SetMapping(context, &sound_mapping, sfx,
+ variable_name, atoi(value));
+ }
+}
+
+deh_section_t deh_section_sound =
+{
+ "Sound",
+ NULL,
+ DEH_SoundStart,
+ DEH_SoundParseLine,
+ NULL,
+ NULL,
+};
+
diff --git a/src/heretic/deh_thing.c b/src/heretic/deh_thing.c
new file mode 100644
index 00000000..ffededf2
--- /dev/null
+++ b/src/heretic/deh_thing.c
@@ -0,0 +1,150 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Thing" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "doomtype.h"
+#include "m_misc.h"
+
+#include "deh_defs.h"
+#include "deh_main.h"
+#include "deh_mapping.h"
+#include "deh_htic.h"
+
+#include "info.h"
+
+DEH_BEGIN_MAPPING(thing_mapping, mobjinfo_t)
+ DEH_MAPPING("ID #", doomednum)
+ DEH_MAPPING("Initial frame", spawnstate)
+ DEH_MAPPING("Hit points", spawnhealth)
+ DEH_MAPPING("First moving frame", seestate)
+ DEH_MAPPING("Alert sound", seesound)
+ DEH_MAPPING("Reaction time", reactiontime)
+ DEH_MAPPING("Attack sound", attacksound)
+ DEH_MAPPING("Injury frame", painstate)
+ DEH_MAPPING("Pain chance", painchance)
+ DEH_MAPPING("Pain sound", painsound)
+ DEH_MAPPING("Close attack frame", meleestate)
+ DEH_MAPPING("Far attack frame", missilestate)
+ DEH_MAPPING("Burning frame", crashstate)
+ DEH_MAPPING("Death frame", deathstate)
+ DEH_MAPPING("Exploding frame", xdeathstate)
+ DEH_MAPPING("Death sound", deathsound)
+ DEH_MAPPING("Speed", speed)
+ DEH_MAPPING("Width", radius)
+ DEH_MAPPING("Height", height)
+ DEH_MAPPING("Mass", mass)
+ DEH_MAPPING("Missile damage", damage)
+ DEH_MAPPING("Action sound", activesound)
+ DEH_MAPPING("Bits 1", flags)
+ DEH_MAPPING("Bits 2", flags2)
+DEH_END_MAPPING
+
+static void *DEH_ThingStart(deh_context_t *context, char *line)
+{
+ int thing_number = 0;
+ mobjinfo_t *mobj;
+
+ if (sscanf(line, "Thing %i", &thing_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ // HHE thing numbers are indexed from 1
+ --thing_number;
+
+ if (thing_number < 0 || thing_number >= DEH_HERETIC_NUMMOBJTYPES)
+ {
+ DEH_Warning(context, "Invalid thing number: %i", thing_number);
+ return NULL;
+ }
+
+ mobj = &mobjinfo[thing_number];
+
+ return mobj;
+}
+
+static void DEH_ThingParseLine(deh_context_t *context, char *line, void *tag)
+{
+ mobjinfo_t *mobj;
+ char *variable_name, *value;
+ int ivalue;
+
+ if (tag == NULL)
+ return;
+
+ mobj = (mobjinfo_t *) tag;
+
+ // Parse the assignment
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ // all values are integers
+
+ ivalue = atoi(value);
+
+ // If the value to be set is a frame, the frame number must
+ // undergo transformation from a Heretic 1.0 index to a
+ // Heretic 1.3 index.
+
+ if (M_StrCaseStr(variable_name, "frame") != NULL)
+ {
+ ivalue = DEH_MapHereticFrameNumber(ivalue);
+ }
+
+ // Set the field value
+
+ DEH_SetMapping(context, &thing_mapping, mobj, variable_name, ivalue);
+}
+
+static void DEH_ThingMD5Sum(md5_context_t *context)
+{
+ int i;
+
+ for (i=0; i<NUMMOBJTYPES; ++i)
+ {
+ DEH_StructMD5Sum(context, &thing_mapping, &mobjinfo[i]);
+ }
+}
+
+deh_section_t deh_section_thing =
+{
+ "Thing",
+ NULL,
+ DEH_ThingStart,
+ DEH_ThingParseLine,
+ NULL,
+ DEH_ThingMD5Sum,
+};
+
diff --git a/src/heretic/deh_weapon.c b/src/heretic/deh_weapon.c
new file mode 100644
index 00000000..28a90c68
--- /dev/null
+++ b/src/heretic/deh_weapon.c
@@ -0,0 +1,131 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 2005 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// Parses "Weapon" sections in dehacked files
+//
+//-----------------------------------------------------------------------------
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "doomtype.h"
+#include "m_misc.h"
+
+#include "doomdef.h"
+
+#include "deh_defs.h"
+#include "deh_main.h"
+#include "deh_mapping.h"
+#include "deh_htic.h"
+
+DEH_BEGIN_MAPPING(weapon_mapping, weaponinfo_t)
+ DEH_MAPPING("Ammo type", ammo)
+ DEH_MAPPING("Deselect frame", upstate)
+ DEH_MAPPING("Select frame", downstate)
+ DEH_MAPPING("Bobbing frame", readystate)
+ DEH_MAPPING("Shooting frame", atkstate)
+ DEH_MAPPING("Firing frame", holdatkstate)
+ DEH_MAPPING("Unknown frame", flashstate)
+DEH_END_MAPPING
+
+static void *DEH_WeaponStart(deh_context_t *context, char *line)
+{
+ int weapon_number = 0;
+
+ if (sscanf(line, "Weapon %i", &weapon_number) != 1)
+ {
+ DEH_Warning(context, "Parse error on section start");
+ return NULL;
+ }
+
+ if (weapon_number < 0 || weapon_number >= NUMWEAPONS * 2)
+ {
+ DEH_Warning(context, "Invalid weapon number: %i", weapon_number);
+ return NULL;
+ }
+
+ // Because of the tome of power, we have two levels of weapons:
+
+ if (weapon_number < NUMWEAPONS)
+ {
+ return &wpnlev1info[weapon_number];
+ }
+ else
+ {
+ return &wpnlev2info[weapon_number - NUMWEAPONS];
+ }
+}
+
+static void DEH_WeaponParseLine(deh_context_t *context, char *line, void *tag)
+{
+ char *variable_name, *value;
+ weaponinfo_t *weapon;
+ int ivalue;
+
+ if (tag == NULL)
+ return;
+
+ weapon = (weaponinfo_t *) tag;
+
+ if (!DEH_ParseAssignment(line, &variable_name, &value))
+ {
+ // Failed to parse
+
+ DEH_Warning(context, "Failed to parse assignment");
+ return;
+ }
+
+ ivalue = atoi(value);
+
+ // If this is a frame field, we need to map from Heretic 1.0 frame
+ // numbers to Heretic 1.3 frame numbers.
+
+ if (M_StrCaseStr(variable_name, "frame") != NULL)
+ {
+ ivalue = DEH_MapHereticFrameNumber(ivalue);
+ }
+
+ DEH_SetMapping(context, &weapon_mapping, weapon, variable_name, ivalue);
+}
+
+static void DEH_WeaponMD5Sum(md5_context_t *context)
+{
+ int i;
+
+ for (i=0; i<NUMWEAPONS ;++i)
+ {
+ DEH_StructMD5Sum(context, &weapon_mapping, &wpnlev1info[i]);
+ DEH_StructMD5Sum(context, &weapon_mapping, &wpnlev2info[i]);
+ }
+}
+
+deh_section_t deh_section_weapon =
+{
+ "Weapon",
+ NULL,
+ DEH_WeaponStart,
+ DEH_WeaponParseLine,
+ NULL,
+ DEH_WeaponMD5Sum,
+};
+
diff --git a/src/heretic/doomdata.h b/src/heretic/doomdata.h
new file mode 100644
index 00000000..ac84ec46
--- /dev/null
+++ b/src/heretic/doomdata.h
@@ -0,0 +1,200 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+// DoomData.h
+
+// all external data is defined here
+// most of the data is loaded into different structures at run time
+
+#ifndef __DOOMDATA__
+#define __DOOMDATA__
+
+#include "doomtype.h"
+
+/*
+===============================================================================
+
+ map level types
+
+===============================================================================
+*/
+
+// lump order in a map wad
+enum
+{
+ ML_LABEL,
+ ML_THINGS,
+ ML_LINEDEFS,
+ ML_SIDEDEFS,
+ ML_VERTEXES,
+ ML_SEGS,
+ ML_SSECTORS,
+ ML_NODES,
+ ML_SECTORS,
+ ML_REJECT,
+ ML_BLOCKMAP
+};
+
+
+typedef struct
+{
+ short x, y;
+} PACKEDATTR mapvertex_t;
+
+typedef struct
+{
+ short textureoffset;
+ short rowoffset;
+ char toptexture[8], bottomtexture[8], midtexture[8];
+ short sector; // on viewer's side
+} PACKEDATTR mapsidedef_t;
+
+typedef struct
+{
+ short v1, v2;
+ short flags;
+ short special, tag;
+ short sidenum[2]; // sidenum[1] will be -1 if one sided
+} PACKEDATTR maplinedef_t;
+
+#define ML_BLOCKING 1
+#define ML_BLOCKMONSTERS 2
+#define ML_TWOSIDED 4 // backside will not be present at all
+ // if not two sided
+
+// if a texture is pegged, the texture will have the end exposed to air held
+// constant at the top or bottom of the texture (stairs or pulled down things)
+// and will move with a height change of one of the neighbor sectors
+// Unpegged textures allways have the first row of the texture at the top
+// pixel of the line for both top and bottom textures (windows)
+#define ML_DONTPEGTOP 8
+#define ML_DONTPEGBOTTOM 16
+
+#define ML_SECRET 32 // don't map as two sided: IT'S A SECRET!
+#define ML_SOUNDBLOCK 64 // don't let sound cross two of these
+#define ML_DONTDRAW 128 // don't draw on the automap
+#define ML_MAPPED 256 // set if allready drawn in automap
+
+
+typedef struct
+{
+ short floorheight, ceilingheight;
+ char floorpic[8], ceilingpic[8];
+ short lightlevel;
+ short special, tag;
+} PACKEDATTR mapsector_t;
+
+typedef struct
+{
+ short numsegs;
+ short firstseg; // segs are stored sequentially
+} PACKEDATTR mapsubsector_t;
+
+typedef struct
+{
+ short v1, v2;
+ short angle;
+ short linedef, side;
+ short offset;
+} PACKEDATTR mapseg_t;
+
+#define NF_SUBSECTOR 0x8000
+typedef struct
+{
+ short x, y, dx, dy; // partition line
+ short bbox[2][4]; // bounding box for each child
+ unsigned short children[2]; // if NF_SUBSECTOR its a subsector
+} PACKEDATTR mapnode_t;
+
+typedef struct
+{
+ short x, y;
+ short angle;
+ short type;
+ short options;
+} PACKEDATTR mapthing_t;
+
+#define MTF_EASY 1
+#define MTF_NORMAL 2
+#define MTF_HARD 4
+#define MTF_AMBUSH 8
+
+/*
+===============================================================================
+
+ texture definition
+
+===============================================================================
+*/
+
+typedef struct
+{
+ short originx;
+ short originy;
+ short patch;
+ short stepdir;
+ short colormap;
+} PACKEDATTR mappatch_t;
+
+typedef struct
+{
+ char name[8];
+ boolean masked;
+ short width;
+ short height;
+ int obsolete;
+ short patchcount;
+ mappatch_t patches[1];
+} PACKEDATTR maptexture_t;
+
+
+/*
+===============================================================================
+
+ graphics
+
+===============================================================================
+*/
+
+// a pic is an unmasked block of pixels
+typedef struct
+{
+ byte width, height;
+ byte data;
+} pic_t;
+
+
+
+
+/*
+===============================================================================
+
+ status
+
+===============================================================================
+*/
+
+
+
+
+#endif // __DOOMDATA__
diff --git a/src/heretic/doomdef.h b/src/heretic/doomdef.h
new file mode 100644
index 00000000..aa1d0c0d
--- /dev/null
+++ b/src/heretic/doomdef.h
@@ -0,0 +1,898 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// DoomDef.h
+
+#ifndef __DOOMDEF__
+#define __DOOMDEF__
+#include <stdio.h>
+#include <string.h>
+//haleyjd: removed WATCOMC
+#include <limits.h>
+
+#define HERETIC_VERSION 130
+#define HERETIC_VERSION_TEXT "v1.3"
+
+// if rangecheck is undefined, most parameter validation debugging code
+// will not be compiled
+//#define RANGECHECK
+
+// all external data is defined here
+#include "doomdata.h"
+
+// all important printed strings
+#include "dstrings.h"
+
+// header generated by multigen utility
+#include "info.h"
+
+// WAD file access
+#include "w_wad.h"
+
+// fixed_t
+#include "m_fixed.h"
+
+// angle_t
+#include "tables.h"
+
+// events
+#include "d_event.h"
+
+// gamemode/mission
+#include "d_mode.h"
+
+// ticcmd_t
+#include "d_ticcmd.h"
+
+#define SAVEGAMENAME "hticsav"
+#define SAVEGAMENAMECD "c:\\heretic.cd\\hticsav"
+
+/*
+===============================================================================
+
+ GLOBAL TYPES
+
+===============================================================================
+*/
+
+#define NUMARTIFCTS 28
+#define MAXPLAYERS 4
+
+#define BT_ATTACK 1
+#define BT_USE 2
+#define BT_CHANGE 4 // if true, the next 3 bits hold weapon num
+#define BT_WEAPONMASK (8+16+32)
+#define BT_WEAPONSHIFT 3
+
+#define BT_SPECIAL 128 // game events, not really buttons
+#define BTS_SAVEMASK (4+8+16)
+#define BTS_SAVESHIFT 2
+#define BT_SPECIALMASK 3
+#define BTS_PAUSE 1 // pause the game
+#define BTS_SAVEGAME 2 // save the game at each console
+// savegame slot numbers occupy the second byte of buttons
+
+typedef enum
+{
+ GS_LEVEL,
+ GS_INTERMISSION,
+ GS_FINALE,
+ GS_DEMOSCREEN
+} gamestate_t;
+
+typedef enum
+{
+ ga_nothing,
+ ga_loadlevel,
+ ga_newgame,
+ ga_loadgame,
+ ga_savegame,
+ ga_playdemo,
+ ga_completed,
+ ga_victory,
+ ga_worlddone,
+ ga_screenshot
+} gameaction_t;
+
+typedef enum
+{
+ wipe_0,
+ wipe_1,
+ wipe_2,
+ wipe_3,
+ wipe_4,
+ NUMWIPES,
+ wipe_random
+} wipe_t;
+
+/*
+===============================================================================
+
+ MAPOBJ DATA
+
+===============================================================================
+*/
+
+// think_t is a function pointer to a routine to handle an actor
+typedef void (*think_t) ();
+
+typedef struct thinker_s
+{
+ struct thinker_s *prev, *next;
+ think_t function;
+} thinker_t;
+
+struct player_s;
+
+typedef struct mobj_s
+{
+ thinker_t thinker; // thinker links
+
+// info for drawing
+ fixed_t x, y, z;
+ struct mobj_s *snext, *sprev; // links in sector (if needed)
+ angle_t angle;
+ spritenum_t sprite; // used to find patch_t and flip value
+ int frame; // might be ord with FF_FULLBRIGHT
+
+// interaction info
+ struct mobj_s *bnext, *bprev; // links in blocks (if needed)
+ struct subsector_s *subsector;
+ fixed_t floorz, ceilingz; // closest together of contacted secs
+ fixed_t radius, height; // for movement checking
+ fixed_t momx, momy, momz; // momentums
+
+ int validcount; // if == validcount, already checked
+
+ mobjtype_t type;
+ mobjinfo_t *info; // &mobjinfo[mobj->type]
+ int tics; // state tic counter
+ state_t *state;
+ int damage; // For missiles
+ int flags;
+ int flags2; // Heretic flags
+ int special1; // Special info
+ int special2; // Special info
+ int health;
+ int movedir; // 0-7
+ int movecount; // when 0, select a new dir
+ struct mobj_s *target; // thing being chased/attacked (or NULL)
+ // also the originator for missiles
+ int reactiontime; // if non 0, don't attack yet
+ // used by player to freeze a bit after
+ // teleporting
+ int threshold; // if >0, the target will be chased
+ // no matter what (even if shot)
+ struct player_s *player; // only valid if type == MT_PLAYER
+ int lastlook; // player number last looked for
+
+ mapthing_t spawnpoint; // for nightmare respawn
+} mobj_t;
+
+// each sector has a degenmobj_t in it's center for sound origin purposes
+typedef struct
+{
+ thinker_t thinker; // not used for anything
+ fixed_t x, y, z;
+} degenmobj_t;
+
+//
+// frame flags
+//
+#define FF_FULLBRIGHT 0x8000 // flag in thing->frame
+#define FF_FRAMEMASK 0x7fff
+
+// --- mobj.flags ---
+
+#define MF_SPECIAL 1 // call P_SpecialThing when touched
+#define MF_SOLID 2
+#define MF_SHOOTABLE 4
+#define MF_NOSECTOR 8 // don't use the sector links
+ // (invisible but touchable)
+#define MF_NOBLOCKMAP 16 // don't use the blocklinks
+ // (inert but displayable)
+#define MF_AMBUSH 32
+#define MF_JUSTHIT 64 // try to attack right back
+#define MF_JUSTATTACKED 128 // take at least one step before attacking
+#define MF_SPAWNCEILING 256 // hang from ceiling instead of floor
+#define MF_NOGRAVITY 512 // don't apply gravity every tic
+
+// movement flags
+#define MF_DROPOFF 0x400 // allow jumps from high places
+#define MF_PICKUP 0x800 // for players to pick up items
+#define MF_NOCLIP 0x1000 // player cheat
+#define MF_SLIDE 0x2000 // keep info about sliding along walls
+#define MF_FLOAT 0x4000 // allow moves to any height, no gravity
+#define MF_TELEPORT 0x8000 // don't cross lines or look at heights
+#define MF_MISSILE 0x10000 // don't hit same species, explode on block
+
+#define MF_DROPPED 0x20000 // dropped by a demon, not level spawned
+#define MF_SHADOW 0x40000 // use translucent draw (shadow demons / invis)
+#define MF_NOBLOOD 0x80000 // don't bleed when shot (use puff)
+#define MF_CORPSE 0x100000 // don't stop moving halfway off a step
+#define MF_INFLOAT 0x200000 // floating to a height for a move, don't
+ // auto float to target's height
+
+#define MF_COUNTKILL 0x400000 // count towards intermission kill total
+#define MF_COUNTITEM 0x800000 // count towards intermission item total
+
+#define MF_SKULLFLY 0x1000000 // skull in flight
+#define MF_NOTDMATCH 0x2000000 // don't spawn in death match (key cards)
+
+#define MF_TRANSLATION 0xc000000 // if 0x4 0x8 or 0xc, use a translation
+#define MF_TRANSSHIFT 26 // table for player colormaps
+
+// --- mobj.flags2 ---
+
+#define MF2_LOGRAV 0x00000001 // alternate gravity setting
+#define MF2_WINDTHRUST 0x00000002 // gets pushed around by the wind
+ // specials
+#define MF2_FLOORBOUNCE 0x00000004 // bounces off the floor
+#define MF2_THRUGHOST 0x00000008 // missile will pass through ghosts
+#define MF2_FLY 0x00000010 // fly mode is active
+#define MF2_FOOTCLIP 0x00000020 // if feet are allowed to be clipped
+#define MF2_SPAWNFLOAT 0x00000040 // spawn random float z
+#define MF2_NOTELEPORT 0x00000080 // does not teleport
+#define MF2_RIP 0x00000100 // missile rips through solid
+ // targets
+#define MF2_PUSHABLE 0x00000200 // can be pushed by other moving
+ // mobjs
+#define MF2_SLIDE 0x00000400 // slides against walls
+#define MF2_ONMOBJ 0x00000800 // mobj is resting on top of another
+ // mobj
+#define MF2_PASSMOBJ 0x00001000 // Enable z block checking. If on,
+ // this flag will allow the mobj to
+ // pass over/under other mobjs.
+#define MF2_CANNOTPUSH 0x00002000 // cannot push other pushable mobjs
+#define MF2_FEETARECLIPPED 0x00004000 // a mobj's feet are now being cut
+#define MF2_BOSS 0x00008000 // mobj is a major boss
+#define MF2_FIREDAMAGE 0x00010000 // does fire damage
+#define MF2_NODMGTHRUST 0x00020000 // does not thrust target when
+ // damaging
+#define MF2_TELESTOMP 0x00040000 // mobj can stomp another
+#define MF2_FLOATBOB 0x00080000 // use float bobbing z movement
+#define MF2_DONTDRAW 0X00100000 // don't generate a vissprite
+
+//=============================================================================
+typedef enum
+{
+ PST_LIVE, // playing
+ PST_DEAD, // dead on the ground
+ PST_REBORN // ready to restart
+} playerstate_t;
+
+// psprites are scaled shapes 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, sy;
+} pspdef_t;
+
+typedef enum
+{
+ key_yellow,
+ key_green,
+ key_blue,
+ NUMKEYS
+} keytype_t;
+
+typedef enum
+{
+ wp_staff,
+ wp_goldwand,
+ wp_crossbow,
+ wp_blaster,
+ wp_skullrod,
+ wp_phoenixrod,
+ wp_mace,
+ wp_gauntlets,
+ wp_beak,
+ NUMWEAPONS,
+ wp_nochange
+} weapontype_t;
+
+#define AMMO_GWND_WIMPY 10
+#define AMMO_GWND_HEFTY 50
+#define AMMO_CBOW_WIMPY 5
+#define AMMO_CBOW_HEFTY 20
+#define AMMO_BLSR_WIMPY 10
+#define AMMO_BLSR_HEFTY 25
+#define AMMO_SKRD_WIMPY 20
+#define AMMO_SKRD_HEFTY 100
+#define AMMO_PHRD_WIMPY 1
+#define AMMO_PHRD_HEFTY 10
+#define AMMO_MACE_WIMPY 20
+#define AMMO_MACE_HEFTY 100
+
+typedef enum
+{
+ am_goldwand,
+ am_crossbow,
+ am_blaster,
+ am_skullrod,
+ am_phoenixrod,
+ am_mace,
+ NUMAMMO,
+ am_noammo // staff, gauntlets
+} ammotype_t;
+
+typedef struct
+{
+ ammotype_t ammo;
+ int upstate;
+ int downstate;
+ int readystate;
+ int atkstate;
+ int holdatkstate;
+ int flashstate;
+} weaponinfo_t;
+
+extern weaponinfo_t wpnlev1info[NUMWEAPONS];
+extern weaponinfo_t wpnlev2info[NUMWEAPONS];
+
+typedef enum
+{
+ arti_none,
+ arti_invulnerability,
+ arti_invisibility,
+ arti_health,
+ arti_superhealth,
+ arti_tomeofpower,
+ arti_torch,
+ arti_firebomb,
+ arti_egg,
+ arti_fly,
+ arti_teleport,
+ NUMARTIFACTS
+} artitype_t;
+
+typedef enum
+{
+ pw_None,
+ pw_invulnerability,
+ pw_invisibility,
+ pw_allmap,
+ pw_infrared,
+ pw_weaponlevel2,
+ pw_flight,
+ pw_shield,
+ pw_health2,
+ NUMPOWERS
+} powertype_t;
+
+#define INVULNTICS (30*35)
+#define INVISTICS (60*35)
+#define INFRATICS (120*35)
+#define IRONTICS (60*35)
+#define WPNLEV2TICS (40*35)
+#define FLIGHTTICS (60*35)
+
+#define CHICKENTICS (40*35)
+
+#define MESSAGETICS (4*35)
+#define BLINKTHRESHOLD (4*32)
+
+#define NUMINVENTORYSLOTS 14
+typedef struct
+{
+ int type;
+ int count;
+} inventory_t;
+
+/*
+================
+=
+= player_t
+=
+================
+*/
+
+typedef struct player_s
+{
+ mobj_t *mo;
+ playerstate_t playerstate;
+ ticcmd_t cmd;
+
+ fixed_t viewz; // focal origin above r.z
+ fixed_t viewheight; // base height above floor for viewz
+ fixed_t deltaviewheight; // squat speed
+ fixed_t bob; // bounded/scaled total momentum
+
+ int flyheight;
+ int lookdir;
+ boolean centering;
+ int health; // only used between levels, mo->health
+ // is used during levels
+ int armorpoints, armortype; // armor type is 0-2
+
+ inventory_t inventory[NUMINVENTORYSLOTS];
+ artitype_t readyArtifact;
+ int artifactCount;
+ int inventorySlotNum;
+ int powers[NUMPOWERS];
+ boolean keys[NUMKEYS];
+ boolean backpack;
+ signed int frags[MAXPLAYERS]; // kills of other players
+ weapontype_t readyweapon;
+ weapontype_t pendingweapon; // wp_nochange if not changing
+ boolean weaponowned[NUMWEAPONS];
+ int ammo[NUMAMMO];
+ int maxammo[NUMAMMO];
+ int attackdown, usedown; // true if button down last tic
+ int cheats; // bit flags
+
+ int refire; // refired shots are less accurate
+
+ int killcount, itemcount, secretcount; // for intermission
+ char *message; // hint messages
+ int messageTics; // counter for showing messages
+ int damagecount, bonuscount; // for screen flashing
+ int flamecount; // for flame thrower duration
+ mobj_t *attacker; // who did damage (NULL for floors)
+ int extralight; // so gun flashes light up areas
+ int fixedcolormap; // can be set to REDCOLORMAP, etc
+ int colormap; // 0-3 for which color to draw player
+ pspdef_t psprites[NUMPSPRITES]; // view sprites (gun, etc)
+ boolean didsecret; // true if secret level has been done
+ int chickenTics; // player is a chicken if > 0
+ int chickenPeck; // chicken peck countdown
+ mobj_t *rain1; // active rain maker 1
+ mobj_t *rain2; // active rain maker 2
+} player_t;
+
+#define CF_NOCLIP 1
+#define CF_GODMODE 2
+#define CF_NOMOMENTUM 4 // not really a cheat, just a debug aid
+
+
+#define BACKUPTICS 12 // CHANGED FROM 12 !?!?
+
+typedef struct
+{
+ unsigned checksum; // high bit is retransmit request
+ byte retransmitfrom; // only valid if NCMD_RETRANSMIT
+ byte starttic;
+ byte player, numtics;
+ ticcmd_t cmds[BACKUPTICS];
+} doomdata_t;
+
+typedef struct
+{
+ int id;
+ short intnum; // DOOM executes an int to execute commands
+
+// communication between DOOM and the driver
+ short command; // CMD_SEND or CMD_GET
+ short remotenode; // dest for send, set by get (-1 = no packet)
+ short datalength; // bytes in doomdata to be sent
+
+// info common to all nodes
+ short numnodes; // console is allways node 0
+ short ticdup; // 1 = no duplication, 2-5 = dup for slow nets
+ short extratics; // 1 = send a backup tic in every packet
+ short deathmatch; // 1 = deathmatch
+ short savegame; // -1 = new game, 0-5 = load savegame
+ short episode; // 1-3
+ short map; // 1-9
+ short skill; // 1-5
+
+// info specific to this node
+ short consoleplayer;
+ short numplayers;
+ short angleoffset; // 1 = left, 0 = center, -1 = right
+ short drone; // 1 = drone
+
+// packet data to be sent
+ doomdata_t data;
+} doomcom_t;
+
+#define DOOMCOM_ID 0x12345678l
+
+extern doomcom_t *doomcom;
+extern doomdata_t *netbuffer; // points inside doomcom
+
+#define MAXNETNODES 8 // max computers in a game
+
+#define CMD_SEND 1
+#define CMD_GET 2
+
+#define SBARHEIGHT 42 // status bar height at bottom of screen
+
+
+/*
+===============================================================================
+
+ GLOBAL VARIABLES
+
+===============================================================================
+*/
+
+#define TELEFOGHEIGHT (32*FRACUNIT)
+
+extern gameaction_t gameaction;
+
+extern boolean paused;
+
+extern GameMode_t gamemode;
+extern GameMission_t gamemission;
+
+extern boolean ExtendedWAD; // true if main WAD is the extended version
+
+extern boolean nomonsters; // checkparm of -nomonsters
+
+extern boolean respawnparm; // checkparm of -respawn
+
+extern boolean debugmode; // checkparm of -debug
+
+extern boolean usergame; // ok to save / end game
+
+extern boolean ravpic; // checkparm of -ravpic
+
+extern boolean altpal; // checkparm to use an alternate palette routine
+
+extern boolean cdrom; // true if cd-rom mode active ("-cdrom")
+
+extern boolean deathmatch; // only if started as net death
+
+extern boolean netgame; // only true if >1 player
+
+extern boolean playeringame[MAXPLAYERS];
+
+extern int consoleplayer; // player taking events and displaying
+
+extern int displayplayer;
+
+extern int viewangleoffset; // ANG90 = left side, ANG270 = right
+
+extern player_t players[MAXPLAYERS];
+
+extern boolean singletics; // debug flag to cancel adaptiveness
+
+extern boolean DebugSound; // debug flag for displaying sound info
+
+extern int maxammo[NUMAMMO];
+extern int GetWeaponAmmo[NUMWEAPONS];
+
+extern boolean demoplayback;
+extern int skytexture;
+
+extern gamestate_t gamestate;
+extern skill_t gameskill;
+extern boolean respawnmonsters;
+extern int gameepisode;
+extern int gamemap;
+extern int prevmap;
+extern int totalkills, totalitems, totalsecret; // for intermission
+extern int levelstarttic; // gametic at level start
+extern int leveltime; // tics in game play for par
+
+extern ticcmd_t netcmds[MAXPLAYERS][BACKUPTICS];
+extern int ticdup;
+
+#define MAXNETNODES 8
+extern ticcmd_t localcmds[BACKUPTICS];
+extern int gametic, maketic;
+extern int nettics[MAXNETNODES];
+
+#define SAVEGAMESIZE 0x30000
+#define SAVESTRINGSIZE 24
+extern byte *savebuffer;
+extern byte *save_p;
+
+extern mapthing_t *deathmatch_p;
+extern mapthing_t deathmatchstarts[10];
+extern mapthing_t playerstarts[MAXPLAYERS];
+
+extern int viewwindowx;
+extern int viewwindowy;
+extern int viewwidth;
+extern int scaledviewwidth;
+extern int viewheight;
+
+extern int mouseSensitivity;
+
+extern boolean precache; // if true, load all graphics at level load
+
+extern boolean singledemo; // quit after playing a demo from cmdline
+
+extern FILE *debugfile;
+extern int bodyqueslot;
+extern skill_t startskill;
+extern int startepisode;
+extern int startmap;
+extern boolean autostart;
+
+/*
+===============================================================================
+
+ GLOBAL FUNCTIONS
+
+===============================================================================
+*/
+
+#include "z_zone.h"
+
+//----------
+//BASE LEVEL
+//----------
+void D_DoomMain(void);
+void IncThermo(void);
+void InitThermo(int max);
+void tprintf(char *string, int initflag);
+// not a globally visible function, just included for source reference
+// calls all startup code
+// parses command line options
+// if not overrided, calls N_AdvanceDemo
+
+void D_DoomLoop(void);
+// not a globally visible function, just included for source reference
+// called by D_DoomMain, never exits
+// manages timing and IO
+// calls all ?_Responder, ?_Ticker, and ?_Drawer functions
+// calls I_GetTime, I_StartFrame, and I_StartTic
+
+void NetUpdate(void);
+// create any new ticcmds and broadcast to other players
+
+void D_QuitNetGame(void);
+// broadcasts special packets to other players to notify of game exit
+
+void TryRunTics(void);
+
+//---------
+//SYSTEM IO
+//---------
+
+byte *I_ZoneBase(int *size);
+// called by startup code to get the ammount of memory to malloc
+// for the zone management
+
+// asyncronous interrupt functions should maintain private ques that are
+// read by the syncronous functions to be converted into events
+
+// Copy buffer to video
+
+byte *I_AllocLow(int length);
+// allocates from low memory under dos, just mallocs under unix
+
+// haleyjd: was WATCOMC, preserved for historical interest.
+// This is similar to the -control structure in DOOM v1.4 and Strife.
+#if 0
+extern boolean useexterndriver;
+
+#define EBT_FIRE 1
+#define EBT_OPENDOOR 2
+#define EBT_SPEED 4
+#define EBT_STRAFE 8
+#define EBT_MAP 0x10
+#define EBT_INVENTORYLEFT 0x20
+#define EBT_INVENTORYRIGHT 0x40
+#define EBT_USEARTIFACT 0x80
+#define EBT_FLYDROP 0x100
+#define EBT_CENTERVIEW 0x200
+#define EBT_PAUSE 0x400
+#define EBT_WEAPONCYCLE 0x800
+
+typedef struct
+{
+ short vector; // Interrupt vector
+
+ signed char moveForward; // forward/backward (maxes at 50)
+ signed char moveSideways; // strafe (maxes at 24)
+ short angleTurn; // turning speed (640 [slow] 1280 [fast])
+ short angleHead; // head angle (+2080 [left] : 0 [center] : -2048 [right])
+ signed char pitch; // look up/down (-110 : +90)
+ signed char flyDirection; // flyheight (+1/-1)
+ unsigned short buttons; // EBT_* flags
+} externdata_t;
+#endif
+
+//----
+//GAME
+//----
+
+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);
+// can be called by the startup code or M_Responder
+// a normal game starts at map 1, but a warp test can start elsewhere
+
+void G_DeferedPlayDemo(char *demo);
+
+void G_LoadGame(char *name);
+// can be called by the startup code or M_Responder
+// calls P_SetupLevel or W_EnterWorld
+void G_DoLoadGame(void);
+
+void G_SaveGame(int slot, char *description);
+// called by M_Responder
+
+// Support routines for saving games
+void SV_Open(char *fileName);
+void SV_Close(char *fileName);
+void SV_Write(void *buffer, int size);
+void SV_WriteByte(byte val);
+void SV_WriteWord(unsigned short val);
+void SV_WriteLong(unsigned int val);
+
+void G_RecordDemo(skill_t skill, int numplayers, int episode, int map,
+ char *name);
+// only called by startup code
+
+void G_PlayDemo(char *name);
+void G_TimeDemo(char *name);
+
+void G_ExitLevel(void);
+void G_SecretExitLevel(void);
+
+void G_WorldDone(void);
+
+void G_Ticker(void);
+boolean G_Responder(event_t * ev);
+
+void G_ScreenShot(void);
+
+//-----
+//PLAY
+//-----
+
+void P_Ticker(void);
+// called by C_Ticker
+// can call G_PlayerExited
+// carries out all thinking of monsters and players
+
+void P_SetupLevel(int episode, int map, int playermask, skill_t skill);
+// called by W_Ticker
+
+void P_Init(void);
+// called by startup code
+
+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);
+// load / save game routines
+
+
+//-------
+//REFRESH
+//-------
+
+extern boolean setsizeneeded;
+
+extern boolean BorderNeedRefresh;
+extern boolean BorderTopRefresh;
+
+extern int UpdateState;
+// define the different areas for the dirty map
+#define I_NOUPDATE 0
+#define I_FULLVIEW 1
+#define I_STATBAR 2
+#define I_MESSAGES 4
+#define I_FULLSCRN 8
+
+void R_RenderPlayerView(player_t * player);
+// called by G_Drawer
+
+void R_Init(void);
+// called by startup code
+
+void R_DrawViewBorder(void);
+void R_DrawTopBorder(void);
+// if the view size is not full screen, draws a border around it
+
+void R_SetViewSize(int blocks, int detail);
+// called by M_Responder
+
+int R_FlatNumForName(char *name);
+
+int R_TextureNumForName(char *name);
+int R_CheckTextureNumForName(char *name);
+// called by P_Ticker for switches and animations
+// returns the texture number for the texture name
+
+
+//----
+//MISC
+//----
+// returns the position of the given parameter in the arg list (0 if not found)
+
+int M_DrawText(int x, int y, boolean direct, char *string);
+
+//----------------------
+// Interlude (IN_lude.c)
+//----------------------
+
+extern boolean intermission;
+
+void IN_Start(void);
+void IN_Ticker(void);
+void IN_Drawer(void);
+
+//----------------------
+// Chat mode (CT_chat.c)
+//----------------------
+
+void CT_Init(void);
+void CT_Drawer(void);
+boolean CT_Responder(event_t * ev);
+void CT_Ticker(void);
+char CT_dequeueChatChar(void);
+
+extern boolean chatmodeon;
+extern boolean ultimatemsg;
+
+//--------------------
+// Finale (F_finale.c)
+//--------------------
+
+void F_Drawer(void);
+void F_Ticker(void);
+void F_StartFinale(void);
+
+//----------------------
+// STATUS BAR (SB_bar.c)
+//----------------------
+
+extern int SB_state;
+void SB_Init(void);
+boolean SB_Responder(event_t * event);
+void SB_Ticker(void);
+void SB_Drawer(void);
+
+//-----------------
+// MENU (MN_menu.c)
+//-----------------
+
+extern boolean MenuActive;
+
+void MN_Init(void);
+void MN_ActivateMenu(void);
+void MN_DeactivateMenu(void);
+boolean MN_Responder(event_t * event);
+void MN_Ticker(void);
+void MN_Drawer(void);
+void MN_DrTextA(char *text, int x, int y);
+int MN_TextAWidth(char *text);
+void MN_DrTextB(char *text, int x, int y);
+int MN_TextBWidth(char *text);
+
+#include "sounds.h"
+
+#endif // __DOOMDEF__
diff --git a/src/heretic/dstrings.h b/src/heretic/dstrings.h
new file mode 100644
index 00000000..93900f9d
--- /dev/null
+++ b/src/heretic/dstrings.h
@@ -0,0 +1,252 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// DStrings.h
+
+//---------------------------------------------------------------------------
+//
+// P_inter.c
+//
+//---------------------------------------------------------------------------
+
+// Keys
+
+#define TXT_GOTBLUEKEY "BLUE KEY"
+#define TXT_GOTYELLOWKEY "YELLOW KEY"
+#define TXT_GOTGREENKEY "GREEN KEY"
+
+// Artifacts
+
+#define TXT_ARTIHEALTH "QUARTZ FLASK"
+#define TXT_ARTIFLY "WINGS OF WRATH"
+#define TXT_ARTIINVULNERABILITY "RING OF INVINCIBILITY"
+#define TXT_ARTITOMEOFPOWER "TOME OF POWER"
+#define TXT_ARTIINVISIBILITY "SHADOWSPHERE"
+#define TXT_ARTIEGG "MORPH OVUM"
+#define TXT_ARTISUPERHEALTH "MYSTIC URN"
+#define TXT_ARTITORCH "TORCH"
+#define TXT_ARTIFIREBOMB "TIME BOMB OF THE ANCIENTS"
+#define TXT_ARTITELEPORT "CHAOS DEVICE"
+
+// Items
+
+#define TXT_ITEMHEALTH "CRYSTAL VIAL"
+#define TXT_ITEMBAGOFHOLDING "BAG OF HOLDING"
+#define TXT_ITEMSHIELD1 "SILVER SHIELD"
+#define TXT_ITEMSHIELD2 "ENCHANTED SHIELD"
+#define TXT_ITEMSUPERMAP "MAP SCROLL"
+
+// Ammo
+
+#define TXT_AMMOGOLDWAND1 "WAND CRYSTAL"
+#define TXT_AMMOGOLDWAND2 "CRYSTAL GEODE"
+#define TXT_AMMOMACE1 "MACE SPHERES"
+#define TXT_AMMOMACE2 "PILE OF MACE SPHERES"
+#define TXT_AMMOCROSSBOW1 "ETHEREAL ARROWS"
+#define TXT_AMMOCROSSBOW2 "QUIVER OF ETHEREAL ARROWS"
+#define TXT_AMMOBLASTER1 "CLAW ORB"
+#define TXT_AMMOBLASTER2 "ENERGY ORB"
+#define TXT_AMMOSKULLROD1 "LESSER RUNES"
+#define TXT_AMMOSKULLROD2 "GREATER RUNES"
+#define TXT_AMMOPHOENIXROD1 "FLAME ORB"
+#define TXT_AMMOPHOENIXROD2 "INFERNO ORB"
+
+// Weapons
+
+#define TXT_WPNMACE "FIREMACE"
+#define TXT_WPNCROSSBOW "ETHEREAL CROSSBOW"
+#define TXT_WPNBLASTER "DRAGON CLAW"
+#define TXT_WPNSKULLROD "HELLSTAFF"
+#define TXT_WPNPHOENIXROD "PHOENIX ROD"
+#define TXT_WPNGAUNTLETS "GAUNTLETS OF THE NECROMANCER"
+
+//---------------------------------------------------------------------------
+//
+// SB_bar.c
+//
+//---------------------------------------------------------------------------
+
+#define TXT_CHEATGODON "GOD MODE ON"
+#define TXT_CHEATGODOFF "GOD MODE OFF"
+#define TXT_CHEATNOCLIPON "NO CLIPPING ON"
+#define TXT_CHEATNOCLIPOFF "NO CLIPPING OFF"
+#define TXT_CHEATWEAPONS "ALL WEAPONS"
+#define TXT_CHEATFLIGHTON "FLIGHT ON"
+#define TXT_CHEATFLIGHTOFF "FLIGHT OFF"
+#define TXT_CHEATPOWERON "POWER ON"
+#define TXT_CHEATPOWEROFF "POWER OFF"
+#define TXT_CHEATHEALTH "FULL HEALTH"
+#define TXT_CHEATKEYS "ALL KEYS"
+#define TXT_CHEATSOUNDON "SOUND DEBUG ON"
+#define TXT_CHEATSOUNDOFF "SOUND DEBUG OFF"
+#define TXT_CHEATTICKERON "TICKER ON"
+#define TXT_CHEATTICKEROFF "TICKER OFF"
+#define TXT_CHEATARTIFACTS1 "CHOOSE AN ARTIFACT ( A - J )"
+#define TXT_CHEATARTIFACTS2 "HOW MANY ( 1 - 9 )"
+#define TXT_CHEATARTIFACTS3 "YOU GOT IT"
+#define TXT_CHEATARTIFACTSFAIL "BAD INPUT"
+#define TXT_CHEATWARP "LEVEL WARP"
+#define TXT_CHEATSCREENSHOT "SCREENSHOT"
+#define TXT_CHEATCHICKENON "CHICKEN ON"
+#define TXT_CHEATCHICKENOFF "CHICKEN OFF"
+#define TXT_CHEATMASSACRE "MASSACRE"
+#define TXT_CHEATIDDQD "TRYING TO CHEAT, EH? NOW YOU DIE!"
+#define TXT_CHEATIDKFA "CHEATER - YOU DON'T DESERVE WEAPONS"
+
+//---------------------------------------------------------------------------
+//
+// P_doors.c
+//
+//---------------------------------------------------------------------------
+
+#define TXT_NEEDBLUEKEY "YOU NEED A BLUE KEY TO OPEN THIS DOOR"
+#define TXT_NEEDGREENKEY "YOU NEED A GREEN KEY TO OPEN THIS DOOR"
+#define TXT_NEEDYELLOWKEY "YOU NEED A YELLOW KEY TO OPEN THIS DOOR"
+
+//---------------------------------------------------------------------------
+//
+// G_game.c
+//
+//---------------------------------------------------------------------------
+
+#define TXT_GAMESAVED "GAME SAVED"
+
+//---------------------------------------------------------------------------
+//
+// 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"
+
+//---------------------------------------------------------------------------
+//
+// F_finale.c
+//
+//---------------------------------------------------------------------------
+
+#define E1TEXT "with the destruction of the iron\n"\
+ "liches and their minions, the last\n"\
+ "of the undead are cleared from this\n"\
+ "plane of existence.\n\n"\
+ "those creatures had to come from\n"\
+ "somewhere, though, and you have the\n"\
+ "sneaky suspicion that the fiery\n"\
+ "portal of hell's maw opens onto\n"\
+ "their home dimension.\n\n"\
+ "to make sure that more undead\n"\
+ "(or even worse things) don't come\n"\
+ "through, you'll have to seal hell's\n"\
+ "maw from the other side. of course\n"\
+ "this means you may get stuck in a\n"\
+ "very unfriendly world, but no one\n"\
+ "ever said being a Heretic was easy!"
+
+#define E2TEXT "the mighty maulotaurs have proved\n"\
+ "to be no match for you, and as\n"\
+ "their steaming corpses slide to the\n"\
+ "ground you feel a sense of grim\n"\
+ "satisfaction that they have been\n"\
+ "destroyed.\n\n"\
+ "the gateways which they guarded\n"\
+ "have opened, revealing what you\n"\
+ "hope is the way home. but as you\n"\
+ "step through, mocking laughter\n"\
+ "rings in your ears.\n\n"\
+ "was some other force controlling\n"\
+ "the maulotaurs? could there be even\n"\
+ "more horrific beings through this\n"\
+ "gate? the sweep of a crystal dome\n"\
+ "overhead where the sky should be is\n"\
+ "certainly not a good sign...."
+
+#define E3TEXT "the death of d'sparil has loosed\n"\
+ "the magical bonds holding his\n"\
+ "creatures on this plane, their\n"\
+ "dying screams overwhelming his own\n"\
+ "cries of agony.\n\n"\
+ "your oath of vengeance fulfilled,\n"\
+ "you enter the portal to your own\n"\
+ "world, mere moments before the dome\n"\
+ "shatters into a million pieces.\n\n"\
+ "but if d'sparil's power is broken\n"\
+ "forever, why don't you feel safe?\n"\
+ "was it that last shout just before\n"\
+ "his death, the one that sounded\n"\
+ "like a curse? or a summoning? you\n"\
+ "can't really be sure, but it might\n"\
+ "just have been a scream.\n\n"\
+ "then again, what about the other\n"\
+ "serpent riders?"
+
+#define E4TEXT "you thought you would return to your\n"\
+ "own world after d'sparil died, but\n"\
+ "his final act banished you to his\n"\
+ "own plane. here you entered the\n"\
+ "shattered remnants of lands\n"\
+ "conquered by d'sparil. you defeated\n"\
+ "the last guardians of these lands,\n"\
+ "but now you stand before the gates\n"\
+ "to d'sparil's stronghold. until this\n"\
+ "moment you had no doubts about your\n"\
+ "ability to face anything you might\n"\
+ "encounter, but beyond this portal\n"\
+ "lies the very heart of the evil\n"\
+ "which invaded your world. d'sparil\n"\
+ "might be dead, but the pit where he\n"\
+ "was spawned remains. now you must\n"\
+ "enter that pit in the hopes of\n"\
+ "finding a way out. and somewhere,\n"\
+ "in the darkest corner of d'sparil's\n"\
+ "demesne, his personal bodyguards\n"\
+ "await your arrival ..."
+
+#define E5TEXT "as the final maulotaur bellows his\n"\
+ "death-agony, you realize that you\n"\
+ "have never come so close to your own\n"\
+ "destruction. not even the fight with\n"\
+ "d'sparil and his disciples had been\n"\
+ "this desperate. grimly you stare at\n"\
+ "the gates which open before you,\n"\
+ "wondering if they lead home, or if\n"\
+ "they open onto some undreamed-of\n"\
+ "horror. you find yourself wondering\n"\
+ "if you have the strength to go on,\n"\
+ "if nothing but death and pain await\n"\
+ "you. but what else can you do, if\n"\
+ "the will to fight is gone? can you\n"\
+ "force yourself to continue in the\n"\
+ "face of such despair? do you have\n"\
+ "the courage? you find, in the end,\n"\
+ "that it is not within you to\n"\
+ "surrender without a fight. eyes\n"\
+ "wide, you go to meet your fate."
+
diff --git a/src/heretic/f_finale.c b/src/heretic/f_finale.c
new file mode 100644
index 00000000..4077bb82
--- /dev/null
+++ b/src/heretic/f_finale.c
@@ -0,0 +1,433 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+// F_finale.c
+
+#include <ctype.h>
+
+#include "doomdef.h"
+#include "deh_str.h"
+#include "i_swap.h"
+#include "i_video.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+int finalestage; // 0 = text, 1 = art screen
+int finalecount;
+
+#define TEXTSPEED 3
+#define TEXTWAIT 250
+
+char *finaletext;
+char *finaleflat;
+
+int FontABaseLump;
+
+extern boolean automapactive;
+extern boolean viewactive;
+
+extern void D_StartTitle(void);
+
+/*
+=======================
+=
+= F_StartFinale
+=
+=======================
+*/
+
+void F_StartFinale(void)
+{
+ gameaction = ga_nothing;
+ gamestate = GS_FINALE;
+ viewactive = false;
+ automapactive = false;
+ players[consoleplayer].messageTics = 1;
+ players[consoleplayer].message = NULL;
+
+ switch (gameepisode)
+ {
+ case 1:
+ finaleflat = DEH_String("FLOOR25");
+ finaletext = DEH_String(E1TEXT);
+ break;
+ case 2:
+ finaleflat = DEH_String("FLATHUH1");
+ finaletext = DEH_String(E2TEXT);
+ break;
+ case 3:
+ finaleflat = DEH_String("FLTWAWA2");
+ finaletext = DEH_String(E3TEXT);
+ break;
+ case 4:
+ finaleflat = DEH_String("FLOOR28");
+ finaletext = DEH_String(E4TEXT);
+ break;
+ case 5:
+ finaleflat = DEH_String("FLOOR08");
+ finaletext = DEH_String(E5TEXT);
+ break;
+ }
+
+ finalestage = 0;
+ finalecount = 0;
+ FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1;
+
+// S_ChangeMusic(mus_victor, true);
+ S_StartSong(mus_cptd, true);
+}
+
+
+
+boolean F_Responder(event_t * event)
+{
+ if (event->type != ev_keydown)
+ {
+ return false;
+ }
+ if (finalestage == 1 && gameepisode == 2)
+ { // we're showing the water pic, make any key kick to demo mode
+ finalestage++;
+ /*
+ memset((byte *) 0xa0000, 0, SCREENWIDTH * SCREENHEIGHT);
+ memset(I_VideoBuffer, 0, SCREENWIDTH * SCREENHEIGHT);
+ I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE));
+ */
+ return true;
+ }
+ return false;
+}
+
+
+/*
+=======================
+=
+= F_Ticker
+=
+=======================
+*/
+
+void F_Ticker(void)
+{
+ finalecount++;
+ if (!finalestage
+ && finalecount > strlen(finaletext) * TEXTSPEED + TEXTWAIT)
+ {
+ finalecount = 0;
+ if (!finalestage)
+ {
+ finalestage = 1;
+ }
+
+// wipegamestate = -1; // force a wipe
+/*
+ if (gameepisode == 3)
+ S_StartMusic (mus_bunny);
+*/
+ }
+}
+
+
+/*
+=======================
+=
+= F_TextWrite
+=
+=======================
+*/
+
+//#include "hu_stuff.h"
+//extern patch_t *hu_font[HU_FONTSIZE];
+
+void F_TextWrite(void)
+{
+ byte *src, *dest;
+ int x, y;
+ int count;
+ char *ch;
+ int c;
+ int cx, cy;
+ patch_t *w;
+
+//
+// erase the entire screen to a tiled background
+//
+ src = W_CacheLumpName(finaleflat, PU_CACHE);
+ dest = I_VideoBuffer;
+ for (y = 0; y < SCREENHEIGHT; y++)
+ {
+ for (x = 0; x < SCREENWIDTH / 64; x++)
+ {
+ memcpy(dest, src + ((y & 63) << 6), 64);
+ dest += 64;
+ }
+ if (SCREENWIDTH & 63)
+ {
+ memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63);
+ dest += (SCREENWIDTH & 63);
+ }
+ }
+
+// V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
+
+//
+// draw some of the text onto the screen
+//
+ cx = 20;
+ cy = 5;
+ ch = finaletext;
+
+ count = (finalecount - 10) / TEXTSPEED;
+ if (count < 0)
+ count = 0;
+ for (; count; count--)
+ {
+ c = *ch++;
+ if (!c)
+ break;
+ if (c == '\n')
+ {
+ cx = 20;
+ cy += 9;
+ continue;
+ }
+
+ c = toupper(c);
+ if (c < 33)
+ {
+ cx += 5;
+ continue;
+ }
+
+ w = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+ if (cx + w->width > SCREENWIDTH)
+ break;
+ V_DrawPatch(cx, cy, w);
+ cx += w->width;
+ }
+
+}
+
+
+void F_DrawPatchCol(int x, patch_t * patch, int col)
+{
+ column_t *column;
+ byte *source, *dest, *desttop;
+ int count;
+
+ column = (column_t *) ((byte *) patch + LONG(patch->columnofs[col]));
+ desttop = I_VideoBuffer + x;
+
+// step through the posts in a column
+
+ while (column->topdelta != 0xff)
+ {
+ source = (byte *) column + 3;
+ dest = desttop + column->topdelta * SCREENWIDTH;
+ count = column->length;
+
+ while (count--)
+ {
+ *dest = *source++;
+ dest += SCREENWIDTH;
+ }
+ column = (column_t *) ((byte *) column + column->length + 4);
+ }
+}
+
+/*
+==================
+=
+= F_DemonScroll
+=
+==================
+*/
+
+void F_DemonScroll(void)
+{
+ byte *p1, *p2;
+ static int yval = 0;
+ static int nextscroll = 0;
+
+ if (finalecount < nextscroll)
+ {
+ return;
+ }
+ p1 = W_CacheLumpName(DEH_String("FINAL1"), PU_LEVEL);
+ p2 = W_CacheLumpName(DEH_String("FINAL2"), PU_LEVEL);
+ if (finalecount < 70)
+ {
+ memcpy(I_VideoBuffer, p1, SCREENHEIGHT * SCREENWIDTH);
+ nextscroll = finalecount;
+ return;
+ }
+ if (yval < 64000)
+ {
+ memcpy(I_VideoBuffer, p2 + SCREENHEIGHT * SCREENWIDTH - yval, yval);
+ memcpy(I_VideoBuffer + yval, p1, SCREENHEIGHT * SCREENWIDTH - yval);
+ yval += SCREENWIDTH;
+ nextscroll = finalecount + 3;
+ }
+ else
+ { //else, we'll just sit here and wait, for now
+ memcpy(I_VideoBuffer, p2, SCREENWIDTH * SCREENHEIGHT);
+ }
+}
+
+/*
+==================
+=
+= F_DrawUnderwater
+=
+==================
+*/
+
+void F_DrawUnderwater(void)
+{
+ static boolean underwawa;
+ extern boolean MenuActive;
+ extern boolean askforquit;
+
+ switch (finalestage)
+ {
+ case 1:
+ if (!underwawa)
+ {
+ underwawa = true;
+ memset((byte *) 0xa0000, 0, SCREENWIDTH * SCREENHEIGHT);
+ I_SetPalette(W_CacheLumpName(DEH_String("E2PAL"), PU_CACHE));
+ V_DrawRawScreen(W_CacheLumpName(DEH_String("E2END"), PU_CACHE));
+ }
+ paused = false;
+ MenuActive = false;
+ askforquit = false;
+
+ break;
+ case 2:
+ V_DrawRawScreen(W_CacheLumpName(DEH_String("TITLE"), PU_CACHE));
+ //D_StartTitle(); // go to intro/demo mode.
+ }
+}
+
+
+#if 0
+/*
+==================
+=
+= F_BunnyScroll
+=
+==================
+*/
+
+void F_BunnyScroll(void)
+{
+ int scrolled, x;
+ patch_t *p1, *p2;
+ char name[10];
+ int stage;
+ static int laststage;
+
+ p1 = W_CacheLumpName("PFUB2", PU_LEVEL);
+ p2 = W_CacheLumpName("PFUB1", PU_LEVEL);
+
+ V_MarkRect(0, 0, SCREENWIDTH, SCREENHEIGHT);
+
+ scrolled = 320 - (finalecount - 230) / 2;
+ if (scrolled > 320)
+ scrolled = 320;
+ if (scrolled < 0)
+ scrolled = 0;
+
+ for (x = 0; x < SCREENWIDTH; x++)
+ {
+ if (x + scrolled < 320)
+ F_DrawPatchCol(x, p1, x + scrolled);
+ else
+ F_DrawPatchCol(x, p2, x + scrolled - 320);
+ }
+
+ if (finalecount < 1130)
+ return;
+ if (finalecount < 1180)
+ {
+ V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2, 0,
+ W_CacheLumpName("END0", PU_CACHE));
+ 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);
+ V_DrawPatch((SCREENWIDTH - 13 * 8) / 2, (SCREENHEIGHT - 8 * 8) / 2,
+ W_CacheLumpName(name, PU_CACHE));
+}
+#endif
+
+/*
+=======================
+=
+= F_Drawer
+=
+=======================
+*/
+
+void F_Drawer(void)
+{
+ UpdateState |= I_FULLSCRN;
+ if (!finalestage)
+ F_TextWrite();
+ else
+ {
+ switch (gameepisode)
+ {
+ case 1:
+ if (gamemode == shareware)
+ {
+ V_DrawRawScreen(W_CacheLumpName("ORDER", PU_CACHE));
+ }
+ else
+ {
+ V_DrawRawScreen(W_CacheLumpName("CREDIT", PU_CACHE));
+ }
+ break;
+ case 2:
+ F_DrawUnderwater();
+ break;
+ case 3:
+ F_DemonScroll();
+ break;
+ case 4: // Just show credits screen for extended episodes
+ case 5:
+ V_DrawRawScreen(W_CacheLumpName("CREDIT", PU_CACHE));
+ break;
+ }
+ }
+}
diff --git a/src/heretic/g_game.c b/src/heretic/g_game.c
new file mode 100644
index 00000000..b37f95e0
--- /dev/null
+++ b/src/heretic/g_game.c
@@ -0,0 +1,1798 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// G_game.c
+
+#include <stdio.h>
+#include <string.h>
+#include "doomdef.h"
+#include "doomkeys.h"
+#include "deh_str.h"
+#include "i_timer.h"
+#include "i_system.h"
+#include "m_controls.h"
+#include "m_misc.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+// Macros
+
+#define SVG_RAM 0
+#define SVG_FILE 1
+#define SAVE_GAME_TERMINATOR 0x1d
+#define AM_STARTKEY 9
+
+// Functions
+
+boolean G_CheckDemoStatus(void);
+void G_ReadDemoTiccmd(ticcmd_t * cmd);
+void G_WriteDemoTiccmd(ticcmd_t * cmd);
+void G_PlayerReborn(int player);
+void G_InitNew(skill_t skill, int episode, int map);
+
+void G_DoReborn(int playernum);
+
+void G_DoLoadLevel(void);
+void G_DoNewGame(void);
+void G_DoLoadGame(void);
+void G_DoPlayDemo(void);
+void G_DoCompleted(void);
+void G_DoVictory(void);
+void G_DoWorldDone(void);
+void G_DoSaveGame(void);
+
+void D_PageTicker(void);
+void D_AdvanceDemo(void);
+
+struct
+{
+ mobjtype_t type;
+ int speed[2];
+} MonsterMissileInfo[] = {
+ { MT_IMPBALL, { 10, 20 } },
+ { MT_MUMMYFX1, { 9, 18 } },
+ { MT_KNIGHTAXE, { 9, 18 } },
+ { MT_REDAXE, { 9, 18 } },
+ { MT_BEASTBALL, { 12, 20 } },
+ { MT_WIZFX1, { 18, 24 } },
+ { MT_SNAKEPRO_A, { 14, 20 } },
+ { MT_SNAKEPRO_B, { 14, 20 } },
+ { MT_HEADFX1, { 13, 20 } },
+ { MT_HEADFX3, { 10, 18 } },
+ { MT_MNTRFX1, { 20, 26 } },
+ { MT_MNTRFX2, { 14, 20 } },
+ { MT_SRCRFX1, { 20, 28 } },
+ { MT_SOR2FX1, { 20, 28 } },
+ { -1, { -1, -1 } } // Terminator
+};
+
+FILE *SaveGameFP;
+int SaveGameType;
+
+gameaction_t gameaction;
+gamestate_t gamestate;
+skill_t gameskill;
+boolean respawnmonsters;
+int gameepisode;
+int gamemap;
+int prevmap;
+
+boolean paused;
+boolean sendpause; // send a pause event next tic
+boolean sendsave; // send a save event next tic
+boolean usergame; // ok to save / end game
+
+boolean timingdemo; // if true, exit with report on completion
+int starttime; // for comparative timing purposes
+
+boolean viewactive;
+
+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 levelstarttic; // gametic at level start
+int totalkills, totalitems, totalsecret; // for intermission
+
+int mouseSensitivity;
+
+char demoname[32];
+boolean demorecording;
+boolean demoplayback;
+byte *demobuffer, *demo_p;
+boolean singledemo; // quit after playing a demo from cmdline
+
+boolean precache = true; // if true, load all graphics at start
+
+short consistancy[MAXPLAYERS][BACKUPTICS];
+
+byte *savebuffer, *save_p;
+
+
+//
+// controls (have defaults)
+//
+
+
+
+#define MAXPLMOVE 0x32
+
+fixed_t forwardmove[2] = { 0x19, 0x32 };
+fixed_t sidemove[2] = { 0x18, 0x28 };
+fixed_t angleturn[3] = { 640, 1280, 320 }; // + slow turn
+
+static int *weapon_keys[] =
+{
+ &key_weapon1,
+ &key_weapon2,
+ &key_weapon3,
+ &key_weapon4,
+ &key_weapon5,
+ &key_weapon6,
+ &key_weapon7
+};
+
+#define SLOWTURNTICS 6
+
+#define NUMKEYS 256
+boolean gamekeydown[NUMKEYS];
+int turnheld; // for accelerative turning
+int lookheld;
+
+
+boolean mousearray[4];
+boolean *mousebuttons = &mousearray[1];
+ // allow [-1]
+int mousex, mousey; // mouse values are used once
+int dclicktime, dclickstate, dclicks;
+int dclicktime2, dclickstate2, dclicks2;
+
+#define MAX_JOY_BUTTONS 20
+
+int joyxmove, joyymove; // joystick values are repeated
+boolean joyarray[MAX_JOY_BUTTONS + 1];
+boolean *joybuttons = &joyarray[1]; // allow [-1]
+
+int savegameslot;
+char savedescription[32];
+
+int inventoryTics;
+
+// haleyjd: removed WATCOMC
+
+//=============================================================================
+// Not used - ripped out for Heretic
+/*
+int G_CmdChecksum(ticcmd_t *cmd)
+{
+ int i;
+ int sum;
+
+ sum = 0;
+ for(i = 0; i < sizeof(*cmd)/4-1; i++)
+ {
+ sum += ((int *)cmd)[i];
+ }
+ return(sum);
+}
+*/
+
+/*
+====================
+=
+= 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
+====================
+*/
+
+extern boolean inventory;
+extern int curpos;
+extern int inv_ptr;
+
+boolean usearti = true;
+
+void G_BuildTiccmd(ticcmd_t * cmd)
+{
+ int i;
+ boolean strafe, bstrafe;
+ int speed, tspeed, lspeed;
+ int forward, side;
+ int look, arti;
+ int flyheight;
+
+ extern boolean noartiskip;
+
+ // haleyjd: removed externdriver crap
+
+ memset(cmd, 0, sizeof(*cmd));
+ //cmd->consistancy =
+ // consistancy[consoleplayer][(maketic*ticdup)%BACKUPTICS];
+ cmd->consistancy = consistancy[consoleplayer][maketic % BACKUPTICS];
+
+//printf ("cons: %i\n",cmd->consistancy);
+
+ strafe = gamekeydown[key_strafe] || mousebuttons[mousebstrafe]
+ || joybuttons[joybstrafe];
+ speed = joybspeed >= MAX_JOY_BUTTONS
+ || gamekeydown[key_speed]
+ || joybuttons[joybspeed];
+
+ // haleyjd: removed externdriver crap
+
+ forward = side = look = arti = flyheight = 0;
+
+//
+// use two stage accelerative turning on the keyboard and joystick
+//
+ if (joyxmove < 0 || joyxmove > 0
+ || gamekeydown[key_right] || gamekeydown[key_left])
+ turnheld += ticdup;
+ else
+ turnheld = 0;
+ if (turnheld < SLOWTURNTICS)
+ tspeed = 2; // slow turn
+ else
+ tspeed = speed;
+
+ if (gamekeydown[key_lookdown] || gamekeydown[key_lookup])
+ {
+ lookheld += ticdup;
+ }
+ else
+ {
+ lookheld = 0;
+ }
+ if (lookheld < SLOWTURNTICS)
+ {
+ lspeed = 1;
+ }
+ else
+ {
+ lspeed = 2;
+ }
+
+//
+// let movement keys cancel each other out
+//
+ if (strafe)
+ {
+ if (gamekeydown[key_right])
+ side += sidemove[speed];
+ if (gamekeydown[key_left])
+ side -= sidemove[speed];
+ if (joyxmove > 0)
+ side += sidemove[speed];
+ if (joyxmove < 0)
+ side -= sidemove[speed];
+ }
+ else
+ {
+ if (gamekeydown[key_right])
+ cmd->angleturn -= angleturn[tspeed];
+ if (gamekeydown[key_left])
+ cmd->angleturn += angleturn[tspeed];
+ if (joyxmove > 0)
+ cmd->angleturn -= angleturn[tspeed];
+ if (joyxmove < 0)
+ cmd->angleturn += angleturn[tspeed];
+ }
+
+ if (gamekeydown[key_up])
+ forward += forwardmove[speed];
+ if (gamekeydown[key_down])
+ forward -= forwardmove[speed];
+ if (joyymove < 0)
+ forward += forwardmove[speed];
+ if (joyymove > 0)
+ forward -= forwardmove[speed];
+ if (gamekeydown[key_straferight] || mousebuttons[mousebstraferight]
+ || joybuttons[joybstraferight])
+ side += sidemove[speed];
+ if (gamekeydown[key_strafeleft] || mousebuttons[mousebstrafeleft]
+ || joybuttons[joybstrafeleft])
+ side -= sidemove[speed];
+
+ // Look up/down/center keys
+ if (gamekeydown[key_lookup])
+ {
+ look = lspeed;
+ }
+ if (gamekeydown[key_lookdown])
+ {
+ look = -lspeed;
+ }
+ // haleyjd: removed externdriver crap
+ if (gamekeydown[key_lookcenter])
+ {
+ look = TOCENTER;
+ }
+
+ // haleyjd: removed externdriver crap
+
+ // Fly up/down/drop keys
+ if (gamekeydown[key_flyup])
+ {
+ flyheight = 5; // note that the actual flyheight will be twice this
+ }
+ if (gamekeydown[key_flydown])
+ {
+ flyheight = -5;
+ }
+ if (gamekeydown[key_flycenter])
+ {
+ flyheight = TOCENTER;
+ // haleyjd: removed externdriver crap
+ look = TOCENTER;
+ }
+
+ // Use artifact key
+ if (gamekeydown[key_useartifact])
+ {
+ if (gamekeydown[key_speed] && !noartiskip)
+ {
+ if (players[consoleplayer].inventory[inv_ptr].type != arti_none)
+ {
+ gamekeydown[key_useartifact] = false;
+ cmd->arti = 0xff; // skip artifact code
+ }
+ }
+ else
+ {
+ if (inventory)
+ {
+ players[consoleplayer].readyArtifact =
+ players[consoleplayer].inventory[inv_ptr].type;
+ inventory = false;
+ cmd->arti = 0;
+ usearti = false;
+ }
+ else if (usearti)
+ {
+ cmd->arti = players[consoleplayer].inventory[inv_ptr].type;
+ usearti = false;
+ }
+ }
+ }
+ if (gamekeydown[127] && !cmd->arti
+ && !players[consoleplayer].powers[pw_weaponlevel2])
+ {
+ gamekeydown[127] = false;
+ cmd->arti = arti_tomeofpower;
+ }
+
+//
+// buttons
+//
+ cmd->chatchar = CT_dequeueChatChar();
+
+ if (gamekeydown[key_fire] || mousebuttons[mousebfire]
+ || joybuttons[joybfire])
+ cmd->buttons |= BT_ATTACK;
+
+ if (gamekeydown[key_use] || joybuttons[joybuse] || mousebuttons[mousebuse])
+ {
+ cmd->buttons |= BT_USE;
+ dclicks = 0; // clear double clicks if hit use button
+ }
+
+ for (i=0; i<arrlen(weapon_keys); ++i)
+ {
+ int key = *weapon_keys[i];
+
+ if (gamekeydown[key])
+ {
+ cmd->buttons |= BT_CHANGE;
+ cmd->buttons |= i<<BT_WEAPONSHIFT;
+ break;
+ }
+ }
+
+
+//
+// mouse
+//
+ if (mousebuttons[mousebforward])
+ {
+ forward += forwardmove[speed];
+ }
+
+ if (mousebuttons[mousebbackward])
+ {
+ forward -= forwardmove[speed];
+ }
+
+ // Double click to use can be disabled
+
+ if (dclick_use)
+ {
+ //
+ // forward double click
+ //
+ if (mousebuttons[mousebforward] != dclickstate && dclicktime > 1)
+ {
+ dclickstate = mousebuttons[mousebforward];
+ if (dclickstate)
+ dclicks++;
+ if (dclicks == 2)
+ {
+ cmd->buttons |= BT_USE;
+ dclicks = 0;
+ }
+ else
+ dclicktime = 0;
+ }
+ else
+ {
+ dclicktime += ticdup;
+ if (dclicktime > 20)
+ {
+ dclicks = 0;
+ dclickstate = 0;
+ }
+ }
+
+ //
+ // strafe double click
+ //
+
+ bstrafe = mousebuttons[mousebstrafe] || joybuttons[joybstrafe];
+ if (bstrafe != dclickstate2 && dclicktime2 > 1)
+ {
+ dclickstate2 = bstrafe;
+ if (dclickstate2)
+ dclicks2++;
+ if (dclicks2 == 2)
+ {
+ cmd->buttons |= BT_USE;
+ dclicks2 = 0;
+ }
+ else
+ dclicktime2 = 0;
+ }
+ else
+ {
+ dclicktime2 += ticdup;
+ if (dclicktime2 > 20)
+ {
+ dclicks2 = 0;
+ dclickstate2 = 0;
+ }
+ }
+ }
+
+ if (strafe)
+ {
+ side += mousex * 2;
+ }
+ else
+ {
+ cmd->angleturn -= mousex * 0x8;
+ }
+
+ forward += mousey;
+ 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 += forward;
+ cmd->sidemove += side;
+ if (players[consoleplayer].playerstate == PST_LIVE)
+ {
+ if (look < 0)
+ {
+ look += 16;
+ }
+ cmd->lookfly = look;
+ }
+ if (flyheight < 0)
+ {
+ flyheight += 16;
+ }
+ cmd->lookfly |= flyheight << 4;
+
+//
+// special buttons
+//
+ if (sendpause)
+ {
+ sendpause = false;
+ cmd->buttons = BT_SPECIAL | BTS_PAUSE;
+ }
+
+ if (sendsave)
+ {
+ sendsave = false;
+ cmd->buttons =
+ BT_SPECIAL | BTS_SAVEGAME | (savegameslot << BTS_SAVESHIFT);
+ }
+
+}
+
+
+/*
+==============
+=
+= G_DoLoadLevel
+=
+==============
+*/
+
+void G_DoLoadLevel(void)
+{
+ int i;
+
+ levelstarttic = gametic; // for time calculation
+ gamestate = GS_LEVEL;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i] && players[i].playerstate == PST_DEAD)
+ players[i].playerstate = PST_REBORN;
+ memset(players[i].frags, 0, sizeof(players[i].frags));
+ }
+
+ P_SetupLevel(gameepisode, gamemap, 0, gameskill);
+ displayplayer = consoleplayer; // view the guy you are playing
+ starttime = I_GetTime();
+ gameaction = ga_nothing;
+ Z_CheckHeap();
+
+//
+// clear cmd building stuff
+//
+
+ memset(gamekeydown, 0, sizeof(gamekeydown));
+ joyxmove = joyymove = 0;
+ mousex = mousey = 0;
+ sendpause = sendsave = paused = false;
+ memset(mousebuttons, 0, sizeof(mousebuttons));
+ memset(joybuttons, 0, sizeof(joybuttons));
+}
+
+static void SetJoyButtons(unsigned int buttons_mask)
+{
+ int i;
+
+ for (i=0; i<MAX_JOY_BUTTONS; ++i)
+ {
+ joybuttons[i] = (buttons_mask & (1 << i)) != 0;
+ }
+}
+
+/*
+===============================================================================
+=
+= G_Responder
+=
+= get info needed to make ticcmd_ts for the players
+=
+===============================================================================
+*/
+
+boolean G_Responder(event_t * ev)
+{
+ player_t *plr;
+ extern boolean MenuActive;
+
+ plr = &players[consoleplayer];
+ if (ev->type == ev_keyup && ev->data1 == key_useartifact)
+ { // flag to denote that it's okay to use an artifact
+ if (!inventory)
+ {
+ plr->readyArtifact = plr->inventory[inv_ptr].type;
+ }
+ usearti = true;
+ }
+
+ // Check for spy mode player cycle
+ if (gamestate == GS_LEVEL && ev->type == ev_keydown
+ && ev->data1 == KEY_F12 && !deathmatch)
+ { // Cycle the display player
+ do
+ {
+ displayplayer++;
+ if (displayplayer == MAXPLAYERS)
+ {
+ displayplayer = 0;
+ }
+ }
+ while (!playeringame[displayplayer]
+ && displayplayer != consoleplayer);
+ return (true);
+ }
+
+ if (gamestate == GS_LEVEL)
+ {
+ if (CT_Responder(ev))
+ { // Chat ate the event
+ return (true);
+ }
+ if (SB_Responder(ev))
+ { // Status bar ate the event
+ return (true);
+ }
+ if (AM_Responder(ev))
+ { // Automap ate the event
+ return (true);
+ }
+ }
+
+ switch (ev->type)
+ {
+ case ev_keydown:
+ if (ev->data1 == key_invleft)
+ {
+ inventoryTics = 5 * 35;
+ if (!inventory)
+ {
+ inventory = true;
+ break;
+ }
+ inv_ptr--;
+ if (inv_ptr < 0)
+ {
+ inv_ptr = 0;
+ }
+ else
+ {
+ curpos--;
+ if (curpos < 0)
+ {
+ curpos = 0;
+ }
+ }
+ return (true);
+ }
+ if (ev->data1 == key_invright)
+ {
+ inventoryTics = 5 * 35;
+ if (!inventory)
+ {
+ inventory = true;
+ break;
+ }
+ inv_ptr++;
+ if (inv_ptr >= plr->inventorySlotNum)
+ {
+ inv_ptr--;
+ if (inv_ptr < 0)
+ inv_ptr = 0;
+ }
+ else
+ {
+ curpos++;
+ if (curpos > 6)
+ {
+ curpos = 6;
+ }
+ }
+ return (true);
+ }
+ if (ev->data1 == KEY_PAUSE && !MenuActive)
+ {
+ sendpause = true;
+ return (true);
+ }
+ if (ev->data1 < NUMKEYS)
+ {
+ gamekeydown[ev->data1] = true;
+ }
+ return (true); // eat key down events
+
+ case ev_keyup:
+ if (ev->data1 < NUMKEYS)
+ {
+ gamekeydown[ev->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;
+ mousex = ev->data2 * (mouseSensitivity + 5) / 10;
+ mousey = ev->data3 * (mouseSensitivity + 5) / 10;
+ return (true); // eat events
+
+ case ev_joystick:
+ SetJoyButtons(ev->data1);
+ joyxmove = ev->data2;
+ joyymove = ev->data3;
+ return (true); // eat events
+
+ default:
+ break;
+ }
+ return (false);
+}
+
+/*
+===============================================================================
+=
+= G_Ticker
+=
+===============================================================================
+*/
+
+void G_Ticker(void)
+{
+ int i, buf;
+ ticcmd_t *cmd = NULL;
+
+//
+// do player reborns if needed
+//
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i] && players[i].playerstate == PST_REBORN)
+ G_DoReborn(i);
+
+//
+// do things to change the game state
+//
+ while (gameaction != ga_nothing)
+ {
+ switch (gameaction)
+ {
+ case ga_loadlevel:
+ G_DoLoadLevel();
+ break;
+ case ga_newgame:
+ G_DoNewGame();
+ break;
+ case ga_loadgame:
+ G_DoLoadGame();
+ break;
+ case ga_savegame:
+ G_DoSaveGame();
+ break;
+ case ga_playdemo:
+ G_DoPlayDemo();
+ break;
+ case ga_screenshot:
+ V_ScreenShot("HTIC%02i.pcx");
+ gameaction = ga_nothing;
+ break;
+ case ga_completed:
+ G_DoCompleted();
+ break;
+ case ga_worlddone:
+ G_DoWorldDone();
+ break;
+ case ga_victory:
+ F_StartFinale();
+ break;
+ default:
+ break;
+ }
+ }
+
+
+//
+// get commands, check consistancy, and build new consistancy check
+//
+ //buf = gametic%BACKUPTICS;
+ buf = (gametic / ticdup) % BACKUPTICS;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i])
+ {
+ cmd = &players[i].cmd;
+
+ memcpy(cmd, &netcmds[i][buf], sizeof(ticcmd_t));
+
+ if (demoplayback)
+ G_ReadDemoTiccmd(cmd);
+ if (demorecording)
+ G_WriteDemoTiccmd(cmd);
+
+ if (netgame && !(gametic % ticdup))
+ {
+ if (gametic > BACKUPTICS
+ && consistancy[i][buf] != cmd->consistancy)
+ {
+ I_Error("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] = rndindex;
+ }
+ }
+
+//
+// check for special buttons
+//
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i])
+ {
+ if (players[i].cmd.buttons & BT_SPECIAL)
+ {
+ switch (players[i].cmd.buttons & BT_SPECIALMASK)
+ {
+ case BTS_PAUSE:
+ paused ^= 1;
+ if (paused)
+ {
+ S_PauseSound();
+ }
+ else
+ {
+ S_ResumeSound();
+ }
+ break;
+
+ case BTS_SAVEGAME:
+ if (!savedescription[0])
+ {
+ if (netgame)
+ {
+ strncpy(savedescription, DEH_String("NET GAME"),
+ sizeof(savedescription));
+ }
+ else
+ {
+ strncpy(savedescription, DEH_String("SAVE GAME"),
+ sizeof(savedescription));
+ }
+
+ savedescription[sizeof(savedescription) - 1] = '\0';
+ }
+ savegameslot =
+ (players[i].cmd.
+ buttons & BTS_SAVEMASK) >> BTS_SAVESHIFT;
+ gameaction = ga_savegame;
+ break;
+ }
+ }
+ }
+ // turn inventory off after a certain amount of time
+ if (inventory && !(--inventoryTics))
+ {
+ players[consoleplayer].readyArtifact =
+ players[consoleplayer].inventory[inv_ptr].type;
+ inventory = false;
+ cmd->arti = 0;
+ }
+//
+// do main actions
+//
+//
+// do main actions
+//
+ switch (gamestate)
+ {
+ case GS_LEVEL:
+ P_Ticker();
+ SB_Ticker();
+ AM_Ticker();
+ CT_Ticker();
+ break;
+ case GS_INTERMISSION:
+ IN_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_InitPlayer
+=
+= Called at the start
+= Called by the game initialization functions
+====================
+*/
+
+void G_InitPlayer(int player)
+{
+ player_t *p;
+
+// set up the saved info
+ p = &players[player];
+
+// clear everything else to defaults
+ G_PlayerReborn(player);
+
+}
+
+
+/*
+====================
+=
+= G_PlayerFinishLevel
+=
+= Can when a player completes a level
+====================
+*/
+extern int curpos;
+extern int inv_ptr;
+extern int playerkeys;
+
+void G_PlayerFinishLevel(int player)
+{
+ player_t *p;
+ int i;
+
+/* // BIG HACK
+ inv_ptr = 0;
+ curpos = 0;
+*/
+ // END HACK
+ p = &players[player];
+ for (i = 0; i < p->inventorySlotNum; i++)
+ {
+ p->inventory[i].count = 1;
+ }
+ p->artifactCount = p->inventorySlotNum;
+
+ if (!deathmatch)
+ {
+ for (i = 0; i < 16; i++)
+ {
+ P_PlayerUseArtifact(p, arti_fly);
+ }
+ }
+ memset(p->powers, 0, sizeof(p->powers));
+ memset(p->keys, 0, sizeof(p->keys));
+ playerkeys = 0;
+// memset(p->inventory, 0, sizeof(p->inventory));
+ if (p->chickenTics)
+ {
+ p->readyweapon = p->mo->special1; // Restore weapon
+ p->chickenTics = 0;
+ }
+ p->messageTics = 0;
+ p->lookdir = 0;
+ p->mo->flags &= ~MF_SHADOW; // Remove invisibility
+ p->extralight = 0; // Remove weapon flashes
+ p->fixedcolormap = 0; // Remove torch
+ p->damagecount = 0; // No palette changes
+ p->bonuscount = 0;
+ p->rain1 = NULL;
+ p->rain2 = NULL;
+ if (p == &players[consoleplayer])
+ {
+ SB_state = -1; // refresh the status bar
+ }
+}
+
+/*
+====================
+=
+= 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, itemcount, secretcount;
+ boolean secret;
+
+ secret = false;
+ memcpy(frags, players[player].frags, sizeof(frags));
+ killcount = players[player].killcount;
+ itemcount = players[player].itemcount;
+ secretcount = players[player].secretcount;
+
+ p = &players[player];
+ if (p->didsecret)
+ {
+ secret = true;
+ }
+ memset(p, 0, sizeof(*p));
+
+ 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 = MAXHEALTH;
+ p->readyweapon = p->pendingweapon = wp_goldwand;
+ p->weaponowned[wp_staff] = true;
+ p->weaponowned[wp_goldwand] = true;
+ p->messageTics = 0;
+ p->lookdir = 0;
+ p->ammo[am_goldwand] = 50;
+ for (i = 0; i < NUMAMMO; i++)
+ {
+ p->maxammo[i] = maxammo[i];
+ }
+ if (gamemap == 9 || secret)
+ {
+ p->didsecret = true;
+ }
+ if (p == &players[consoleplayer])
+ {
+ SB_state = -1; // refresh the status bar
+ inv_ptr = 0; // reset the inventory pointer
+ curpos = 0;
+ }
+}
+
+/*
+====================
+=
+= G_CheckSpot
+=
+= Returns false if the player cannot be respawned at the given mapthing_t spot
+= because something is occupying it
+====================
+*/
+
+void P_SpawnPlayer(mapthing_t * mthing);
+
+boolean G_CheckSpot(int playernum, mapthing_t * mthing)
+{
+ fixed_t x, y;
+ subsector_t *ss;
+ unsigned an;
+ mobj_t *mo;
+
+ x = mthing->x << FRACBITS;
+ y = mthing->y << FRACBITS;
+
+ players[playernum].mo->flags2 &= ~MF2_PASSMOBJ;
+ if (!P_CheckPosition(players[playernum].mo, x, y))
+ {
+ players[playernum].mo->flags2 |= MF2_PASSMOBJ;
+ return false;
+ }
+ players[playernum].mo->flags2 |= MF2_PASSMOBJ;
+
+// spawn a teleport fog
+ ss = R_PointInSubsector(x, y);
+ an = (ANG45 * (mthing->angle / 45)) >> ANGLETOFINESHIFT;
+
+ mo = P_SpawnMobj(x + 20 * finecosine[an], y + 20 * finesine[an],
+ ss->sector->floorheight + TELEFOGHEIGHT, 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 i, j;
+ int selections;
+
+ selections = deathmatch_p - deathmatchstarts;
+ if (selections < 4)
+ I_Error("Only %i deathmatch spots, 4 required", selections);
+
+ for (j = 0; j < 20; j++)
+ {
+ i = P_Random() % selections;
+ if (G_CheckSpot(playernum, &deathmatchstarts[i]))
+ {
+ deathmatchstarts[i].type = playernum + 1;
+ P_SpawnPlayer(&deathmatchstarts[i]);
+ return;
+ }
+ }
+
+// no good spot, so the player will probably get stuck
+ P_SpawnPlayer(&playerstarts[playernum]);
+}
+
+/*
+====================
+=
+= G_DoReborn
+=
+====================
+*/
+
+void G_DoReborn(int playernum)
+{
+ int i;
+
+ if (G_CheckDemoStatus())
+ return;
+ if (!netgame)
+ gameaction = ga_loadlevel; // reload the level from scratch
+ else
+ { // respawn at the start
+ players[playernum].mo->player = NULL; // dissasociate the corpse
+
+ // spawn at random spot if in death match
+ if (deathmatch)
+ {
+ G_DeathMatchSpawnPlayer(playernum);
+ return;
+ }
+
+ if (G_CheckSpot(playernum, &playerstarts[playernum]))
+ {
+ P_SpawnPlayer(&playerstarts[playernum]);
+ return;
+ }
+ // try to spawn at one of the other players spots
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (G_CheckSpot(playernum, &playerstarts[i]))
+ {
+ playerstarts[i].type = playernum + 1; // fake as other player
+ P_SpawnPlayer(&playerstarts[i]);
+ playerstarts[i].type = i + 1; // restore
+ return;
+ }
+ // he's going to be inside something. Too bad.
+ P_SpawnPlayer(&playerstarts[playernum]);
+ }
+}
+
+
+void G_ScreenShot(void)
+{
+ gameaction = ga_screenshot;
+}
+
+
+/*
+====================
+=
+= G_DoCompleted
+=
+====================
+*/
+
+boolean secretexit;
+
+void G_ExitLevel(void)
+{
+ secretexit = false;
+ gameaction = ga_completed;
+}
+
+void G_SecretExitLevel(void)
+{
+ secretexit = true;
+ gameaction = ga_completed;
+}
+
+void G_DoCompleted(void)
+{
+ int i;
+ static int afterSecret[5] = { 7, 5, 5, 5, 4 };
+
+ gameaction = ga_nothing;
+ if (G_CheckDemoStatus())
+ {
+ return;
+ }
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i])
+ {
+ G_PlayerFinishLevel(i);
+ }
+ }
+ prevmap = gamemap;
+ if (secretexit == true)
+ {
+ gamemap = 9;
+ }
+ else if (gamemap == 9)
+ { // Finished secret level
+ gamemap = afterSecret[gameepisode - 1];
+ }
+ else if (gamemap == 8)
+ {
+ gameaction = ga_victory;
+ return;
+ }
+ else
+ {
+ gamemap++;
+ }
+ gamestate = GS_INTERMISSION;
+ IN_Start();
+}
+
+//============================================================================
+//
+// G_WorldDone
+//
+//============================================================================
+
+void G_WorldDone(void)
+{
+ gameaction = ga_worlddone;
+}
+
+//============================================================================
+//
+// G_DoWorldDone
+//
+//============================================================================
+
+void G_DoWorldDone(void)
+{
+ gamestate = GS_LEVEL;
+ G_DoLoadLevel();
+ gameaction = ga_nothing;
+ viewactive = true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC G_LoadGame
+//
+// Can be called by the startup code or the menu task.
+//
+//---------------------------------------------------------------------------
+
+char savename[256];
+
+void G_LoadGame(char *name)
+{
+ strcpy(savename, name);
+ gameaction = ga_loadgame;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC G_DoLoadGame
+//
+// Called by G_Ticker based on gameaction.
+//
+//---------------------------------------------------------------------------
+
+#define VERSIONSIZE 16
+
+void G_DoLoadGame(void)
+{
+ int length;
+ int i;
+ int a, b, c;
+ char vcheck[VERSIONSIZE];
+
+ gameaction = ga_nothing;
+
+ length = M_ReadFile(savename, &savebuffer);
+ save_p = savebuffer + SAVESTRINGSIZE;
+ // Skip the description field
+ memset(vcheck, 0, sizeof(vcheck));
+
+ DEH_snprintf(vcheck, VERSIONSIZE, "version %i", HERETIC_VERSION);
+
+ if (strcmp((char *) save_p, vcheck) != 0)
+ { // Bad version
+ return;
+ }
+ save_p += VERSIONSIZE;
+ gameskill = *save_p++;
+ gameepisode = *save_p++;
+ gamemap = *save_p++;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ playeringame[i] = *save_p++;
+ }
+ // Load a base level
+ G_InitNew(gameskill, gameepisode, gamemap);
+
+ // Create leveltime
+ a = *save_p++;
+ b = *save_p++;
+ c = *save_p++;
+ leveltime = (a << 16) + (b << 8) + c;
+
+ // De-archive all the modifications
+ P_UnArchivePlayers();
+ P_UnArchiveWorld();
+ P_UnArchiveThinkers();
+ P_UnArchiveSpecials();
+
+ if (*save_p != SAVE_GAME_TERMINATOR)
+ { // Missing savegame termination marker
+ I_Error("Bad savegame");
+ }
+ Z_Free(savebuffer);
+}
+
+
+/*
+====================
+=
+= G_InitNew
+=
+= Can be called by the startup code or the menu task
+= consoleplayer, displayplayer, playeringame[] should be set
+====================
+*/
+
+skill_t d_skill;
+int d_episode;
+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;
+}
+
+void G_DoNewGame(void)
+{
+ G_InitNew(d_skill, d_episode, d_map);
+ gameaction = ga_nothing;
+}
+
+extern int skytexture;
+
+void G_InitNew(skill_t skill, int episode, int map)
+{
+ int i;
+ int speed;
+ static char *skyLumpNames[5] = {
+ "SKY1", "SKY2", "SKY3", "SKY1", "SKY3"
+ };
+
+ if (paused)
+ {
+ paused = false;
+ S_ResumeSound();
+ }
+ if (skill < sk_baby)
+ skill = sk_baby;
+ if (skill > sk_nightmare)
+ skill = sk_nightmare;
+ if (episode < 1)
+ episode = 1;
+ // Up to 9 episodes for testing
+ if (episode > 9)
+ episode = 9;
+ if (map < 1)
+ map = 1;
+ if (map > 9)
+ map = 9;
+ M_ClearRandom();
+ if (respawnparm)
+ {
+ respawnmonsters = true;
+ }
+ else
+ {
+ respawnmonsters = false;
+ }
+ // Set monster missile speeds
+ speed = skill == sk_nightmare;
+ for (i = 0; MonsterMissileInfo[i].type != -1; i++)
+ {
+ mobjinfo[MonsterMissileInfo[i].type].speed
+ = MonsterMissileInfo[i].speed[speed] << FRACBITS;
+ }
+ // Force players to be initialized upon first level load
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ players[i].playerstate = PST_REBORN;
+ players[i].didsecret = false;
+ }
+ // Set up a bunch of globals
+ usergame = true; // will be set false if a demo
+ paused = false;
+ demorecording = false;
+ demoplayback = false;
+ viewactive = true;
+ gameepisode = episode;
+ gamemap = map;
+ gameskill = skill;
+ viewactive = true;
+ BorderNeedRefresh = true;
+
+ // Set the sky map
+ if (episode > 5)
+ {
+ skytexture = R_TextureNumForName(DEH_String("SKY1"));
+ }
+ else
+ {
+ skytexture = R_TextureNumForName(DEH_String(skyLumpNames[episode - 1]));
+ }
+
+//
+// give one null ticcmd_t
+//
+#if 0
+ gametic = 0;
+ maketic = 1;
+ for (i = 0; i < MAXPLAYERS; i++)
+ nettics[i] = 1; // one null event for this gametic
+ memset(localcmds, 0, sizeof(localcmds));
+ memset(netcmds, 0, sizeof(netcmds));
+#endif
+ G_DoLoadLevel();
+}
+
+
+/*
+===============================================================================
+
+ DEMO RECORDING
+
+===============================================================================
+*/
+
+#define DEMOMARKER 0x80
+
+void G_ReadDemoTiccmd(ticcmd_t * cmd)
+{
+ if (*demo_p == DEMOMARKER)
+ { // end of demo data stream
+ G_CheckDemoStatus();
+ return;
+ }
+ cmd->forwardmove = ((signed char) *demo_p++);
+ cmd->sidemove = ((signed char) *demo_p++);
+ cmd->angleturn = ((unsigned char) *demo_p++) << 8;
+ cmd->buttons = (unsigned char) *demo_p++;
+ cmd->lookfly = (unsigned char) *demo_p++;
+ cmd->arti = (unsigned char) *demo_p++;
+}
+
+void G_WriteDemoTiccmd(ticcmd_t * cmd)
+{
+ if (gamekeydown['q']) // press q to end demo recording
+ G_CheckDemoStatus();
+ *demo_p++ = cmd->forwardmove;
+ *demo_p++ = cmd->sidemove;
+ *demo_p++ = cmd->angleturn >> 8;
+ *demo_p++ = cmd->buttons;
+ *demo_p++ = cmd->lookfly;
+ *demo_p++ = cmd->arti;
+ demo_p -= 6;
+ G_ReadDemoTiccmd(cmd); // make SURE it is exactly the same
+}
+
+
+
+/*
+===================
+=
+= G_RecordDemo
+=
+===================
+*/
+
+void G_RecordDemo(skill_t skill, int numplayers, int episode, int map,
+ char *name)
+{
+ int i;
+
+ G_InitNew(skill, episode, map);
+ usergame = false;
+ strcpy(demoname, name);
+ strcat(demoname, ".lmp");
+ demobuffer = demo_p = Z_Malloc(0x20000, PU_STATIC, NULL);
+ *demo_p++ = skill;
+ *demo_p++ = episode;
+ *demo_p++ = map;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ *demo_p++ = playeringame[i];
+
+ demorecording = true;
+}
+
+
+/*
+===================
+=
+= G_PlayDemo
+=
+===================
+*/
+
+char *defdemoname;
+
+void G_DeferedPlayDemo(char *name)
+{
+ defdemoname = name;
+ gameaction = ga_playdemo;
+}
+
+void G_DoPlayDemo(void)
+{
+ skill_t skill;
+ int i, episode, map;
+
+ gameaction = ga_nothing;
+ demobuffer = demo_p = W_CacheLumpName(defdemoname, PU_STATIC);
+ skill = *demo_p++;
+ episode = *demo_p++;
+ map = *demo_p++;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ playeringame[i] = *demo_p++;
+
+ precache = false; // don't spend a lot of time in loadlevel
+ G_InitNew(skill, episode, map);
+ precache = true;
+ usergame = false;
+ demoplayback = true;
+}
+
+
+/*
+===================
+=
+= G_TimeDemo
+=
+===================
+*/
+
+void G_TimeDemo(char *name)
+{
+ skill_t skill;
+ int episode, map;
+
+ demobuffer = demo_p = W_CacheLumpName(name, PU_STATIC);
+ skill = *demo_p++;
+ episode = *demo_p++;
+ map = *demo_p++;
+ G_InitNew(skill, episode, map);
+ usergame = false;
+ demoplayback = true;
+ timingdemo = true;
+ singletics = true;
+}
+
+
+/*
+===================
+=
+= G_CheckDemoStatus
+=
+= Called after a death or level completion to allow demos to be cleaned up
+= Returns true if a new demo loop action will take place
+===================
+*/
+
+boolean G_CheckDemoStatus(void)
+{
+ int endtime;
+
+ if (timingdemo)
+ {
+ endtime = I_GetTime();
+ I_Error("timed %i gametics in %i realtics", gametic,
+ endtime - starttime);
+ }
+
+ if (demoplayback)
+ {
+ if (singledemo)
+ I_Quit();
+
+ W_ReleaseLumpName(defdemoname);
+ demoplayback = false;
+ D_AdvanceDemo();
+ return true;
+ }
+
+ if (demorecording)
+ {
+ *demo_p++ = DEMOMARKER;
+ M_WriteFile(demoname, demobuffer, demo_p - demobuffer);
+ Z_Free(demobuffer);
+ demorecording = false;
+ I_Error("Demo %s recorded", demoname);
+ }
+
+ return false;
+}
+
+/**************************************************************************/
+/**************************************************************************/
+
+//==========================================================================
+//
+// G_SaveGame
+//
+// Called by the menu task. <description> is a 24 byte text string.
+//
+//==========================================================================
+
+void G_SaveGame(int slot, char *description)
+{
+ savegameslot = slot;
+ strcpy(savedescription, description);
+ sendsave = true;
+}
+
+//==========================================================================
+//
+// G_DoSaveGame
+//
+// Called by G_Ticker based on gameaction.
+//
+//==========================================================================
+
+void G_DoSaveGame(void)
+{
+ int i;
+ char name[100];
+ char verString[VERSIONSIZE];
+ char *description;
+
+ if (cdrom)
+ {
+ sprintf(name, SAVEGAMENAMECD "%d.hsg", savegameslot);
+ }
+ else
+ {
+ sprintf(name, SAVEGAMENAME "%d.hsg", savegameslot);
+ }
+ description = savedescription;
+
+ SV_Open(name);
+ SV_Write(description, SAVESTRINGSIZE);
+ memset(verString, 0, sizeof(verString));
+ DEH_snprintf(verString, VERSIONSIZE, "version %i", HERETIC_VERSION);
+ SV_Write(verString, VERSIONSIZE);
+ SV_WriteByte(gameskill);
+ SV_WriteByte(gameepisode);
+ SV_WriteByte(gamemap);
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ SV_WriteByte(playeringame[i]);
+ }
+ SV_WriteByte(leveltime >> 16);
+ SV_WriteByte(leveltime >> 8);
+ SV_WriteByte(leveltime);
+ P_ArchivePlayers();
+ P_ArchiveWorld();
+ P_ArchiveThinkers();
+ P_ArchiveSpecials();
+ SV_Close(name);
+
+ gameaction = ga_nothing;
+ savedescription[0] = 0;
+ P_SetMessage(&players[consoleplayer], DEH_String(TXT_GAMESAVED), true);
+}
+
+//==========================================================================
+//
+// SV_Open
+//
+//==========================================================================
+
+void SV_Open(char *fileName)
+{
+ SaveGameType = SVG_FILE;
+ SaveGameFP = fopen(fileName, "wb");
+}
+
+//==========================================================================
+//
+// SV_Close
+//
+//==========================================================================
+
+void SV_Close(char *fileName)
+{
+ int length;
+
+ SV_WriteByte(SAVE_GAME_TERMINATOR);
+ if (SaveGameType == SVG_RAM)
+ {
+ length = save_p - savebuffer;
+ if (length > SAVEGAMESIZE)
+ {
+ I_Error("Savegame buffer overrun");
+ }
+ M_WriteFile(fileName, savebuffer, length);
+ Z_Free(savebuffer);
+ }
+ else
+ { // SVG_FILE
+ fclose(SaveGameFP);
+ }
+}
+
+//==========================================================================
+//
+// SV_Write
+//
+//==========================================================================
+
+void SV_Write(void *buffer, int size)
+{
+ if (SaveGameType == SVG_RAM)
+ {
+ memcpy(save_p, buffer, size);
+ save_p += size;
+ }
+ else
+ { // SVG_FILE
+ fwrite(buffer, size, 1, SaveGameFP);
+ }
+}
+
+void SV_WriteByte(byte val)
+{
+ SV_Write(&val, sizeof(byte));
+}
+
+void SV_WriteWord(unsigned short val)
+{
+ SV_Write(&val, sizeof(unsigned short));
+}
+
+void SV_WriteLong(unsigned int val)
+{
+ SV_Write(&val, sizeof(int));
+}
diff --git a/src/heretic/i_ibm.c b/src/heretic/i_ibm.c
new file mode 100644
index 00000000..cf668680
--- /dev/null
+++ b/src/heretic/i_ibm.c
@@ -0,0 +1,1650 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// I_IBM.C
+
+#include <dos.h>
+#include <conio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <graph.h>
+#include "doomdef.h"
+#include "r_local.h"
+#include "dmx.h"
+#include "v_video.h"
+
+// Macros
+
+#define DPMI_INT 0x31
+//#define NOKBD
+//#define NOTIMER
+
+// Public Data
+
+int DisplayTicker = 0;
+
+// Code
+
+void main(int argc, char **argv)
+{
+ myargc = argc;
+ myargv = argv;
+ D_DoomMain();
+}
+
+void I_StartupNet(void);
+void I_ShutdownNet(void);
+void I_ReadExternDriver(void);
+
+typedef struct
+{
+ unsigned edi, esi, ebp, reserved, ebx, edx, ecx, eax;
+ unsigned short flags, es, ds, fs, gs, ip, cs, sp, ss;
+} dpmiregs_t;
+
+extern dpmiregs_t dpmiregs;
+
+void I_ReadMouse(void);
+void I_InitDiskFlash(void);
+
+extern int usemouse, usejoystick;
+
+extern void **lumpcache;
+
+/*
+=============================================================================
+
+ CONSTANTS
+
+=============================================================================
+*/
+
+#define SC_INDEX 0x3C4
+#define SC_RESET 0
+#define SC_CLOCK 1
+#define SC_MAPMASK 2
+#define SC_CHARMAP 3
+#define SC_MEMMODE 4
+
+#define CRTC_INDEX 0x3D4
+#define CRTC_H_TOTAL 0
+#define CRTC_H_DISPEND 1
+#define CRTC_H_BLANK 2
+#define CRTC_H_ENDBLANK 3
+#define CRTC_H_RETRACE 4
+#define CRTC_H_ENDRETRACE 5
+#define CRTC_V_TOTAL 6
+#define CRTC_OVERFLOW 7
+#define CRTC_ROWSCAN 8
+#define CRTC_MAXSCANLINE 9
+#define CRTC_CURSORSTART 10
+#define CRTC_CURSOREND 11
+#define CRTC_STARTHIGH 12
+#define CRTC_STARTLOW 13
+#define CRTC_CURSORHIGH 14
+#define CRTC_CURSORLOW 15
+#define CRTC_V_RETRACE 16
+#define CRTC_V_ENDRETRACE 17
+#define CRTC_V_DISPEND 18
+#define CRTC_OFFSET 19
+#define CRTC_UNDERLINE 20
+#define CRTC_V_BLANK 21
+#define CRTC_V_ENDBLANK 22
+#define CRTC_MODE 23
+#define CRTC_LINECOMPARE 24
+
+
+#define GC_INDEX 0x3CE
+#define GC_SETRESET 0
+#define GC_ENABLESETRESET 1
+#define GC_COLORCOMPARE 2
+#define GC_DATAROTATE 3
+#define GC_READMAP 4
+#define GC_MODE 5
+#define GC_MISCELLANEOUS 6
+#define GC_COLORDONTCARE 7
+#define GC_BITMASK 8
+
+#define ATR_INDEX 0x3c0
+#define ATR_MODE 16
+#define ATR_OVERSCAN 17
+#define ATR_COLORPLANEENABLE 18
+#define ATR_PELPAN 19
+#define ATR_COLORSELECT 20
+
+#define STATUS_REGISTER_1 0x3da
+
+#define PEL_WRITE_ADR 0x3c8
+#define PEL_READ_ADR 0x3c7
+#define PEL_DATA 0x3c9
+#define PEL_MASK 0x3c6
+
+boolean grmode;
+
+//==================================================
+//
+// joystick vars
+//
+//==================================================
+
+boolean joystickpresent;
+extern unsigned joystickx, joysticky;
+boolean I_ReadJoystick(void); // returns false if not connected
+
+
+//==================================================
+
+#define VBLCOUNTER 34000 // hardware tics to a frame
+
+
+#define TIMERINT 8
+#define KEYBOARDINT 9
+
+#define CRTCOFF (_inbyte(STATUS_REGISTER_1)&1)
+#define CLI _disable()
+#define STI _enable()
+
+#define _outbyte(x,y) (outp(x,y))
+#define _outhword(x,y) (outpw(x,y))
+
+#define _inbyte(x) (inp(x))
+#define _inhword(x) (inpw(x))
+
+#define MOUSEB1 1
+#define MOUSEB2 2
+#define MOUSEB3 4
+
+boolean mousepresent;
+//static int tsm_ID = -1; // tsm init flag
+
+//===============================
+
+int ticcount;
+
+// REGS stuff used for int calls
+union REGS regs;
+struct SREGS segregs;
+
+boolean novideo; // if true, stay in text mode for debugging
+
+#define KBDQUESIZE 32
+byte keyboardque[KBDQUESIZE];
+int kbdtail, kbdhead;
+
+#define KEY_LSHIFT 0xfe
+
+#define KEY_INS (0x80+0x52)
+#define KEY_DEL (0x80+0x53)
+#define KEY_PGUP (0x80+0x49)
+#define KEY_PGDN (0x80+0x51)
+#define KEY_HOME (0x80+0x47)
+#define KEY_END (0x80+0x4f)
+
+#define SC_RSHIFT 0x36
+#define SC_LSHIFT 0x2a
+
+byte scantokey[128] = {
+// 0 1 2 3 4 5 6 7
+// 8 9 A B C D E F
+ 0, 27, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '-', '=', KEY_BACKSPACE, 9, // 0
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '[', ']', 13, KEY_RCTRL, 'a', 's', // 1
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+ 39, '`', KEY_LSHIFT, 92, 'z', 'x', 'c', 'v', // 2
+ 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT, '*',
+ KEY_RALT, ' ', 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, // 3
+ KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, 0, 0, KEY_HOME,
+ KEY_UPARROW, KEY_PGUP, '-', KEY_LEFTARROW, '5', KEY_RIGHTARROW, '+', KEY_END, //4
+ KEY_DOWNARROW, KEY_PGDN, KEY_INS, KEY_DEL, 0, 0, 0, KEY_F11,
+ KEY_F12, 0, 0, 0, 0, 0, 0, 0, // 5
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, // 6
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 // 7
+};
+
+//==========================================================================
+
+//--------------------------------------------------------------------------
+//
+// FUNC I_GetTime
+//
+// Returns time in 1/35th second tics.
+//
+//--------------------------------------------------------------------------
+
+int I_GetTime(void)
+{
+#ifdef NOTIMER
+ ticcount++;
+#endif
+ return (ticcount);
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_ColorBorder
+//
+//--------------------------------------------------------------------------
+
+void I_ColorBorder(void)
+{
+ int i;
+
+ I_WaitVBL(1);
+ _outbyte(PEL_WRITE_ADR, 0);
+ for (i = 0; i < 3; i++)
+ {
+ _outbyte(PEL_DATA, 63);
+ }
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_UnColorBorder
+//
+//--------------------------------------------------------------------------
+
+void I_UnColorBorder(void)
+{
+ int i;
+
+ I_WaitVBL(1);
+ _outbyte(PEL_WRITE_ADR, 0);
+ for (i = 0; i < 3; i++)
+ {
+ _outbyte(PEL_DATA, 0);
+ }
+}
+
+/*
+============================================================================
+
+ USER INPUT
+
+============================================================================
+*/
+
+//--------------------------------------------------------------------------
+//
+// PROC I_WaitVBL
+//
+//--------------------------------------------------------------------------
+
+void I_WaitVBL(int vbls)
+{
+ int i;
+ int old;
+ int stat;
+
+ if (novideo)
+ {
+ return;
+ }
+ while (vbls--)
+ {
+ do
+ {
+ stat = inp(STATUS_REGISTER_1);
+ if (stat & 8)
+ {
+ break;
+ }
+ }
+ while (1);
+ do
+ {
+ stat = inp(STATUS_REGISTER_1);
+ if ((stat & 8) == 0)
+ {
+ break;
+ }
+ }
+ while (1);
+ }
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_SetPalette
+//
+// Palette source must use 8 bit RGB elements.
+//
+//--------------------------------------------------------------------------
+
+void I_SetPalette(byte * palette)
+{
+ int i;
+
+ if (novideo)
+ {
+ return;
+ }
+ I_WaitVBL(1);
+ _outbyte(PEL_WRITE_ADR, 0);
+ for (i = 0; i < 768; i++)
+ {
+ _outbyte(PEL_DATA, (gammatable[usegamma][*palette++]) >> 2);
+ }
+}
+
+/*
+============================================================================
+
+ GRAPHICS MODE
+
+============================================================================
+*/
+
+byte *pcscreen, *destscreen, *destview;
+
+
+/*
+==============
+=
+= I_Update
+=
+==============
+*/
+
+int UpdateState;
+extern int screenblocks;
+
+void I_Update(void)
+{
+ int i;
+ byte *dest;
+ int tics;
+ static int lasttic;
+
+//
+// blit screen to video
+//
+ if (DisplayTicker)
+ {
+ if (screenblocks > 9 || UpdateState & (I_FULLSCRN | I_MESSAGES))
+ {
+ dest = (byte *) screen;
+ }
+ else
+ {
+ dest = (byte *) pcscreen;
+ }
+ tics = ticcount - lasttic;
+ lasttic = ticcount;
+ if (tics > 20)
+ {
+ tics = 20;
+ }
+ for (i = 0; i < tics; i++)
+ {
+ *dest = 0xff;
+ dest += 2;
+ }
+ for (i = tics; i < 20; i++)
+ {
+ *dest = 0x00;
+ dest += 2;
+ }
+ }
+ if (UpdateState == I_NOUPDATE)
+ {
+ return;
+ }
+ if (UpdateState & I_FULLSCRN)
+ {
+ memcpy(pcscreen, screen, SCREENWIDTH * SCREENHEIGHT);
+ UpdateState = I_NOUPDATE; // clear out all draw types
+ }
+ if (UpdateState & I_FULLVIEW)
+ {
+ if (UpdateState & I_MESSAGES && screenblocks > 7)
+ {
+ for (i = 0; i <
+ (viewwindowy + viewheight) * SCREENWIDTH; i += SCREENWIDTH)
+ {
+ memcpy(pcscreen + i, screen + i, SCREENWIDTH);
+ }
+ UpdateState &= ~(I_FULLVIEW | I_MESSAGES);
+ }
+ else
+ {
+ for (i = viewwindowy * SCREENWIDTH + viewwindowx; i <
+ (viewwindowy + viewheight) * SCREENWIDTH; i += SCREENWIDTH)
+ {
+ memcpy(pcscreen + i, screen + i, viewwidth);
+ }
+ UpdateState &= ~I_FULLVIEW;
+ }
+ }
+ if (UpdateState & I_STATBAR)
+ {
+ memcpy(pcscreen + SCREENWIDTH * (SCREENHEIGHT - SBARHEIGHT),
+ screen + SCREENWIDTH * (SCREENHEIGHT - SBARHEIGHT),
+ SCREENWIDTH * SBARHEIGHT);
+ UpdateState &= ~I_STATBAR;
+ }
+ if (UpdateState & I_MESSAGES)
+ {
+ memcpy(pcscreen, screen, SCREENWIDTH * 28);
+ UpdateState &= ~I_MESSAGES;
+ }
+
+// memcpy(pcscreen, screen, SCREENHEIGHT*SCREENWIDTH);
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_InitGraphics
+//
+//--------------------------------------------------------------------------
+
+void I_InitGraphics(void)
+{
+ if (novideo)
+ {
+ return;
+ }
+ grmode = true;
+ regs.w.ax = 0x13;
+ int386(0x10, (const union REGS *) &regs, &regs);
+ pcscreen = destscreen = (byte *) 0xa0000;
+ I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE));
+ I_InitDiskFlash();
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_ShutdownGraphics
+//
+//--------------------------------------------------------------------------
+
+void I_ShutdownGraphics(void)
+{
+
+ if (*(byte *) 0x449 == 0x13) // don't reset mode if it didn't get set
+ {
+ regs.w.ax = 3;
+ int386(0x10, &regs, &regs); // back to text mode
+ }
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC I_ReadScreen
+//
+// Reads the screen currently displayed into a linear buffer.
+//
+//--------------------------------------------------------------------------
+
+void I_ReadScreen(byte * scr)
+{
+ memcpy(scr, screen, SCREENWIDTH * SCREENHEIGHT);
+}
+
+
+//===========================================================================
+
+/*
+===================
+=
+= I_StartTic
+=
+// called by D_DoomLoop
+// called before processing each tic in a frame
+// can call D_PostEvent
+// asyncronous interrupt functions should maintain private ques that are
+// read by the syncronous functions to be converted into events
+===================
+*/
+
+/*
+ OLD STARTTIC STUFF
+
+void I_StartTic (void)
+{
+ int k;
+ event_t ev;
+
+
+ I_ReadMouse ();
+
+//
+// keyboard events
+//
+ while (kbdtail < kbdhead)
+ {
+ k = keyboardque[kbdtail&(KBDQUESIZE-1)];
+
+// if (k==14)
+// I_Error ("exited");
+
+ kbdtail++;
+
+ // extended keyboard shift key bullshit
+ if ( (k&0x7f)==KEY_RSHIFT )
+ {
+ if ( keyboardque[(kbdtail-2)&(KBDQUESIZE-1)]==0xe0 )
+ continue;
+ k &= 0x80;
+ k |= KEY_RSHIFT;
+ }
+
+ if (k==0xe0)
+ continue; // special / pause keys
+ if (keyboardque[(kbdtail-2)&(KBDQUESIZE-1)] == 0xe1)
+ continue; // pause key bullshit
+
+ if (k==0xc5 && keyboardque[(kbdtail-2)&(KBDQUESIZE-1)] == 0x9d)
+ {
+ ev.type = ev_keydown;
+ ev.data1 = KEY_PAUSE;
+ D_PostEvent (&ev);
+ continue;
+ }
+
+ if (k&0x80)
+ ev.type = ev_keyup;
+ else
+ ev.type = ev_keydown;
+ k &= 0x7f;
+
+ ev.data1 = k;
+ //ev.data1 = scantokey[k];
+
+ D_PostEvent (&ev);
+ }
+}
+*/
+
+#define SC_UPARROW 0x48
+#define SC_DOWNARROW 0x50
+#define SC_LEFTARROW 0x4b
+#define SC_RIGHTARROW 0x4d
+
+void I_StartTic(void)
+{
+ int k;
+ event_t ev;
+
+
+ I_ReadMouse();
+
+//
+// keyboard events
+//
+ while (kbdtail < kbdhead)
+ {
+ k = keyboardque[kbdtail & (KBDQUESIZE - 1)];
+ kbdtail++;
+
+ // extended keyboard shift key bullshit
+ if ((k & 0x7f) == SC_LSHIFT || (k & 0x7f) == SC_RSHIFT)
+ {
+ if (keyboardque[(kbdtail - 2) & (KBDQUESIZE - 1)] == 0xe0)
+ continue;
+ k &= 0x80;
+ k |= SC_RSHIFT;
+ }
+
+ if (k == 0xe0)
+ continue; // special / pause keys
+ if (keyboardque[(kbdtail - 2) & (KBDQUESIZE - 1)] == 0xe1)
+ continue; // pause key bullshit
+
+ if (k == 0xc5
+ && keyboardque[(kbdtail - 2) & (KBDQUESIZE - 1)] == 0x9d)
+ {
+ ev.type = ev_keydown;
+ ev.data1 = KEY_PAUSE;
+ D_PostEvent(&ev);
+ continue;
+ }
+
+ if (k & 0x80)
+ ev.type = ev_keyup;
+ else
+ ev.type = ev_keydown;
+ k &= 0x7f;
+ switch (k)
+ {
+ case SC_UPARROW:
+ ev.data1 = KEY_UPARROW;
+ break;
+ case SC_DOWNARROW:
+ ev.data1 = KEY_DOWNARROW;
+ break;
+ case SC_LEFTARROW:
+ ev.data1 = KEY_LEFTARROW;
+ break;
+ case SC_RIGHTARROW:
+ ev.data1 = KEY_RIGHTARROW;
+ break;
+ default:
+ ev.data1 = scantokey[k];
+ break;
+ }
+ D_PostEvent(&ev);
+ }
+
+}
+
+
+void I_ReadKeys(void)
+{
+ int k;
+ event_t ev;
+
+
+ while (1)
+ {
+ while (kbdtail < kbdhead)
+ {
+ k = keyboardque[kbdtail & (KBDQUESIZE - 1)];
+ kbdtail++;
+ printf("0x%x\n", k);
+ if (k == 1)
+ I_Quit();
+ }
+ }
+}
+
+/*
+===============
+=
+= I_StartFrame
+=
+===============
+*/
+
+void I_StartFrame(void)
+{
+ I_JoystickEvents();
+ I_ReadExternDriver();
+}
+
+/*
+============================================================================
+
+ TIMER INTERRUPT
+
+============================================================================
+*/
+
+void I_ColorBlack(int r, int g, int b)
+{
+ _outbyte(PEL_WRITE_ADR, 0);
+ _outbyte(PEL_DATA, r);
+ _outbyte(PEL_DATA, g);
+ _outbyte(PEL_DATA, b);
+}
+
+
+/*
+================
+=
+= I_TimerISR
+=
+================
+*/
+
+int I_TimerISR(void)
+{
+ ticcount++;
+ return 0;
+}
+
+/*
+============================================================================
+
+ KEYBOARD
+
+============================================================================
+*/
+
+void (__interrupt __far * oldkeyboardisr) () = NULL;
+
+int lastpress;
+
+/*
+================
+=
+= I_KeyboardISR
+=
+================
+*/
+
+void __interrupt I_KeyboardISR(void)
+{
+// Get the scan code
+
+ keyboardque[kbdhead & (KBDQUESIZE - 1)] = lastpress = _inbyte(0x60);
+ kbdhead++;
+
+// acknowledge the interrupt
+
+ _outbyte(0x20, 0x20);
+}
+
+
+
+/*
+===============
+=
+= I_StartupKeyboard
+=
+===============
+*/
+
+void I_StartupKeyboard(void)
+{
+#ifndef NOKBD
+ oldkeyboardisr = _dos_getvect(KEYBOARDINT);
+ _dos_setvect(0x8000 | KEYBOARDINT, I_KeyboardISR);
+#endif
+
+//I_ReadKeys ();
+}
+
+
+void I_ShutdownKeyboard(void)
+{
+ if (oldkeyboardisr)
+ _dos_setvect(KEYBOARDINT, oldkeyboardisr);
+ *(short *) 0x41c = *(short *) 0x41a; // clear bios key buffer
+}
+
+
+
+/*
+============================================================================
+
+ MOUSE
+
+============================================================================
+*/
+
+
+int I_ResetMouse(void)
+{
+ regs.w.ax = 0; // reset
+ int386(0x33, &regs, &regs);
+ return regs.w.ax;
+}
+
+
+
+/*
+================
+=
+= StartupMouse
+=
+================
+*/
+
+void I_StartupCyberMan(void);
+
+void I_StartupMouse(void)
+{
+ int (far * function) ();
+
+ //
+ // General mouse detection
+ //
+ mousepresent = 0;
+ if (M_CheckParm("-nomouse") || !usemouse)
+ return;
+
+ if (I_ResetMouse() != 0xffff)
+ {
+ tprintf("Mouse: not present ", 0);
+ return;
+ }
+ tprintf("Mouse: detected ", 0);
+
+ mousepresent = 1;
+
+ I_StartupCyberMan();
+}
+
+
+/*
+================
+=
+= ShutdownMouse
+=
+================
+*/
+
+void I_ShutdownMouse(void)
+{
+ if (!mousepresent)
+ return;
+
+ I_ResetMouse();
+}
+
+
+/*
+================
+=
+= I_ReadMouse
+=
+================
+*/
+
+void I_ReadMouse(void)
+{
+ event_t ev;
+
+//
+// mouse events
+//
+ if (!mousepresent)
+ return;
+
+ ev.type = ev_mouse;
+
+ memset(&dpmiregs, 0, sizeof(dpmiregs));
+ dpmiregs.eax = 3; // read buttons / position
+ DPMIInt(0x33);
+ ev.data1 = dpmiregs.ebx;
+
+ dpmiregs.eax = 11; // read counters
+ DPMIInt(0x33);
+ ev.data2 = (short) dpmiregs.ecx;
+ ev.data3 = -(short) dpmiregs.edx;
+
+ D_PostEvent(&ev);
+}
+
+/*
+============================================================================
+
+ JOYSTICK
+
+============================================================================
+*/
+
+int joyxl, joyxh, joyyl, joyyh;
+
+boolean WaitJoyButton(void)
+{
+ int oldbuttons, buttons;
+
+ oldbuttons = 0;
+ do
+ {
+ I_WaitVBL(1);
+ buttons = ((inp(0x201) >> 4) & 1) ^ 1;
+ if (buttons != oldbuttons)
+ {
+ oldbuttons = buttons;
+ continue;
+ }
+
+ if ((lastpress & 0x7f) == 1)
+ {
+ joystickpresent = false;
+ return false;
+ }
+ }
+ while (!buttons);
+
+ do
+ {
+ I_WaitVBL(1);
+ buttons = ((inp(0x201) >> 4) & 1) ^ 1;
+ if (buttons != oldbuttons)
+ {
+ oldbuttons = buttons;
+ continue;
+ }
+
+ if ((lastpress & 0x7f) == 1)
+ {
+ joystickpresent = false;
+ return false;
+ }
+ }
+ while (buttons);
+
+ return true;
+}
+
+
+
+/*
+===============
+=
+= I_StartupJoystick
+=
+===============
+*/
+
+int basejoyx, basejoyy;
+
+void I_StartupJoystick(void)
+{
+ int buttons;
+ int count;
+ int centerx, centery;
+
+ joystickpresent = 0;
+ if (M_CheckParm("-nojoy") || !usejoystick)
+ return;
+
+ if (!I_ReadJoystick())
+ {
+ joystickpresent = false;
+ tprintf("joystick not found ", 0);
+ return;
+ }
+ printf("joystick found\n");
+ joystickpresent = true;
+
+ printf("CENTER the joystick and press button 1:");
+ if (!WaitJoyButton())
+ return;
+ I_ReadJoystick();
+ centerx = joystickx;
+ centery = joysticky;
+
+ printf
+ ("\nPush the joystick to the UPPER LEFT corner and press button 1:");
+ if (!WaitJoyButton())
+ return;
+ I_ReadJoystick();
+ joyxl = (centerx + joystickx) / 2;
+ joyyl = (centerx + joysticky) / 2;
+
+ printf
+ ("\nPush the joystick to the LOWER RIGHT corner and press button 1:");
+ if (!WaitJoyButton())
+ return;
+ I_ReadJoystick();
+ joyxh = (centerx + joystickx) / 2;
+ joyyh = (centery + joysticky) / 2;
+ printf("\n");
+}
+
+/*
+===============
+=
+= I_JoystickEvents
+=
+===============
+*/
+
+void I_JoystickEvents(void)
+{
+ event_t ev;
+
+//
+// joystick events
+//
+ if (!joystickpresent)
+ return;
+
+ I_ReadJoystick();
+ ev.type = ev_joystick;
+ ev.data1 = ((inp(0x201) >> 4) & 15) ^ 15;
+
+ if (joystickx < joyxl)
+ ev.data2 = -1;
+ else if (joystickx > joyxh)
+ ev.data2 = 1;
+ else
+ ev.data2 = 0;
+ if (joysticky < joyyl)
+ ev.data3 = -1;
+ else if (joysticky > joyyh)
+ ev.data3 = 1;
+ else
+ ev.data3 = 0;
+
+ D_PostEvent(&ev);
+}
+
+
+
+/*
+============================================================================
+
+ DPMI STUFF
+
+============================================================================
+*/
+
+#define REALSTACKSIZE 1024
+
+dpmiregs_t dpmiregs;
+
+unsigned realstackseg;
+
+void I_DivException(void);
+int I_SetDivException(void);
+
+void DPMIFarCall(void)
+{
+ segread(&segregs);
+ regs.w.ax = 0x301;
+ regs.w.bx = 0;
+ regs.w.cx = 0;
+ regs.x.edi = (unsigned) &dpmiregs;
+ segregs.es = segregs.ds;
+ int386x(DPMI_INT, &regs, &regs, &segregs);
+}
+
+
+void DPMIInt(int i)
+{
+ dpmiregs.ss = realstackseg;
+ dpmiregs.sp = REALSTACKSIZE - 4;
+
+ segread(&segregs);
+ regs.w.ax = 0x300;
+ regs.w.bx = i;
+ regs.w.cx = 0;
+ regs.x.edi = (unsigned) &dpmiregs;
+ segregs.es = segregs.ds;
+ int386x(DPMI_INT, &regs, &regs, &segregs);
+}
+
+
+/*
+==============
+=
+= I_StartupDPMI
+=
+==============
+*/
+
+void I_StartupDPMI(void)
+{
+ extern char __begtext;
+ extern char ___argc;
+ int n, d;
+
+//
+// allocate a decent stack for real mode ISRs
+//
+ realstackseg = (int) I_AllocLow(1024) >> 4;
+
+//
+// lock the entire program down
+//
+
+// _dpmi_lockregion (&__begtext, &___argc - &__begtext);
+
+
+//
+// catch divide by 0 exception
+//
+#if 0
+ segread(&segregs);
+ regs.w.ax = 0x0203; // DPMI set processor exception handler vector
+ regs.w.bx = 0; // int 0
+ regs.w.cx = segregs.cs;
+ regs.x.edx = (int) &I_DivException;
+ printf("%x : %x\n", regs.w.cx, regs.x.edx);
+ int386(DPMI_INT, &regs, &regs);
+#endif
+
+#if 0
+ n = I_SetDivException();
+ printf("return: %i\n", n);
+ n = 100;
+ d = 0;
+ printf("100 / 0 = %i\n", n / d);
+
+ exit(1);
+#endif
+}
+
+
+
+/*
+============================================================================
+
+ TIMER INTERRUPT
+
+============================================================================
+*/
+
+void (__interrupt __far * oldtimerisr) ();
+
+
+void IO_ColorBlack(int r, int g, int b)
+{
+ _outbyte(PEL_WRITE_ADR, 0);
+ _outbyte(PEL_DATA, r);
+ _outbyte(PEL_DATA, g);
+ _outbyte(PEL_DATA, b);
+}
+
+
+/*
+================
+=
+= IO_TimerISR
+=
+================
+*/
+
+//void __interrupt IO_TimerISR (void)
+
+void __interrupt __far IO_TimerISR(void)
+{
+ ticcount++;
+ _outbyte(0x20, 0x20); // Ack the interrupt
+}
+
+/*
+=====================
+=
+= IO_SetTimer0
+=
+= Sets system timer 0 to the specified speed
+=
+=====================
+*/
+
+void IO_SetTimer0(int speed)
+{
+ if (speed > 0 && speed < 150)
+ I_Error("INT_SetTimer0: %i is a bad value", speed);
+
+ _outbyte(0x43, 0x36); // Change timer 0
+ _outbyte(0x40, speed);
+ _outbyte(0x40, speed >> 8);
+}
+
+
+
+/*
+===============
+=
+= IO_StartupTimer
+=
+===============
+*/
+
+void IO_StartupTimer(void)
+{
+ oldtimerisr = _dos_getvect(TIMERINT);
+
+ _dos_setvect(0x8000 | TIMERINT, IO_TimerISR);
+ IO_SetTimer0(VBLCOUNTER);
+}
+
+void IO_ShutdownTimer(void)
+{
+ if (oldtimerisr)
+ {
+ IO_SetTimer0(0); // back to 18.4 ips
+ _dos_setvect(TIMERINT, oldtimerisr);
+ }
+}
+
+//===========================================================================
+
+
+/*
+===============
+=
+= I_Init
+=
+= hook interrupts and set graphics mode
+=
+===============
+*/
+
+void I_Init(void)
+{
+ extern void I_StartupTimer(void);
+
+ novideo = M_CheckParm("novideo");
+ tprintf("I_StartupDPMI", 1);
+ I_StartupDPMI();
+ tprintf("I_StartupMouse ", 1);
+ I_StartupMouse();
+// tprintf("I_StartupJoystick ",1);
+// I_StartupJoystick();
+// tprintf("I_StartupKeyboard ",1);
+// I_StartupKeyboard();
+}
+
+
+/*
+===============
+=
+= I_Shutdown
+=
+= return to default system state
+=
+===============
+*/
+
+void I_Shutdown(void)
+{
+ I_ShutdownGraphics();
+ IO_ShutdownTimer();
+ S_ShutDown();
+ I_ShutdownMouse();
+ I_ShutdownKeyboard();
+
+ IO_SetTimer0(0);
+}
+
+
+/*
+================
+=
+= I_Error
+=
+================
+*/
+
+void I_Error(char *error, ...)
+{
+ union REGS regs;
+
+ va_list argptr;
+
+ D_QuitNetGame();
+ I_Shutdown();
+ va_start(argptr, error);
+ regs.x.eax = 0x3;
+ int386(0x10, &regs, &regs);
+ vprintf(error, argptr);
+ va_end(argptr);
+ printf("\n");
+ exit(1);
+}
+
+//--------------------------------------------------------------------------
+//
+// I_Quit
+//
+// Shuts down net game, saves defaults, prints the exit text message,
+// goes to text mode, and exits.
+//
+//--------------------------------------------------------------------------
+
+void I_Quit(void)
+{
+ byte *scr;
+ char *lumpName;
+ int r;
+
+ D_QuitNetGame();
+ M_SaveDefaults();
+ scr = (byte *) W_CacheLumpName("ENDTEXT", PU_CACHE);
+ I_Shutdown();
+ memcpy((void *) 0xb8000, scr, 80 * 25 * 2);
+ regs.w.ax = 0x0200;
+ regs.h.bh = 0;
+ regs.h.dl = 0;
+ regs.h.dh = 23;
+ int386(0x10, (const union REGS *) &regs, &regs); // Set text pos
+ _settextposition(24, 1);
+ exit(0);
+}
+
+/*
+===============
+=
+= I_ZoneBase
+=
+===============
+*/
+
+byte *I_ZoneBase(int *size)
+{
+ int meminfo[32];
+ int heap;
+ int i;
+ int block;
+ byte *ptr;
+
+ memset(meminfo, 0, sizeof(meminfo));
+ segread(&segregs);
+ segregs.es = segregs.ds;
+ regs.w.ax = 0x500; // get memory info
+ regs.x.edi = (int) &meminfo;
+ int386x(0x31, &regs, &regs, &segregs);
+
+ heap = meminfo[0];
+ printf("DPMI memory: 0x%x, ", heap);
+
+ do
+ {
+ heap -= 0x10000; // leave 64k alone
+ if (heap > 0x800000)
+ heap = 0x800000;
+ ptr = malloc(heap);
+ }
+ while (!ptr);
+
+ printf("0x%x allocated for zone\n", heap);
+ if (heap < 0x180000)
+ I_Error("Insufficient DPMI memory!");
+#if 0
+ regs.w.ax = 0x501; // allocate linear block
+ regs.w.bx = heap >> 16;
+ regs.w.cx = heap & 0xffff;
+ int386(0x31, &regs, &regs);
+ if (regs.w.cflag)
+ I_Error("Couldn't allocate DPMI memory!");
+
+ block = (regs.w.si << 16) + regs.w.di;
+#endif
+
+ *size = heap;
+ return ptr;
+}
+
+/*
+=============================================================================
+
+ DISK ICON FLASHING
+
+=============================================================================
+*/
+
+void I_InitDiskFlash(void)
+{
+#if 0
+ void *pic;
+ byte *temp;
+
+ pic = W_CacheLumpName("STDISK", PU_CACHE);
+ temp = destscreen;
+ destscreen = (byte *) 0xac000;
+ V_DrawPatchDirect(SCREENWIDTH - 16, SCREENHEIGHT - 16, 0, pic);
+ destscreen = temp;
+#endif
+}
+
+// draw disk icon
+void I_BeginRead(void)
+{
+#if 0
+ byte *src, *dest;
+ int y;
+
+ if (!grmode)
+ return;
+
+// write through all planes
+ outp(SC_INDEX, SC_MAPMASK);
+ outp(SC_INDEX + 1, 15);
+// set write mode 1
+ outp(GC_INDEX, GC_MODE);
+ outp(GC_INDEX + 1, inp(GC_INDEX + 1) | 1);
+
+// copy to backup
+ src = currentscreen + 184 * 80 + 304 / 4;
+ dest = (byte *) 0xac000 + 184 * 80 + 288 / 4;
+ for (y = 0; y < 16; y++)
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = src[3];
+ src += 80;
+ dest += 80;
+ }
+
+// copy disk over
+ dest = currentscreen + 184 * 80 + 304 / 4;
+ src = (byte *) 0xac000 + 184 * 80 + 304 / 4;
+ for (y = 0; y < 16; y++)
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = src[3];
+ src += 80;
+ dest += 80;
+ }
+
+
+// set write mode 0
+ outp(GC_INDEX, GC_MODE);
+ outp(GC_INDEX + 1, inp(GC_INDEX + 1) & ~1);
+#endif
+}
+
+// erase disk icon
+void I_EndRead(void)
+{
+#if 0
+ byte *src, *dest;
+ int y;
+
+ if (!grmode)
+ return;
+
+// write through all planes
+ outp(SC_INDEX, SC_MAPMASK);
+ outp(SC_INDEX + 1, 15);
+// set write mode 1
+ outp(GC_INDEX, GC_MODE);
+ outp(GC_INDEX + 1, inp(GC_INDEX + 1) | 1);
+
+
+// copy disk over
+ dest = currentscreen + 184 * 80 + 304 / 4;
+ src = (byte *) 0xac000 + 184 * 80 + 288 / 4;
+ for (y = 0; y < 16; y++)
+ {
+ dest[0] = src[0];
+ dest[1] = src[1];
+ dest[2] = src[2];
+ dest[3] = src[3];
+ src += 80;
+ dest += 80;
+ }
+
+// set write mode 0
+ outp(GC_INDEX, GC_MODE);
+ outp(GC_INDEX + 1, inp(GC_INDEX + 1) & ~1);
+#endif
+}
+
+
+
+/*
+=============
+=
+= I_AllocLow
+=
+=============
+*/
+
+byte *I_AllocLow(int length)
+{
+ byte *mem;
+
+ // DPMI call 100h allocates DOS memory
+ segread(&segregs);
+ regs.w.ax = 0x0100; // DPMI allocate DOS memory
+ regs.w.bx = (length + 15) / 16;
+ int386(DPMI_INT, &regs, &regs);
+// segment = regs.w.ax;
+// selector = regs.w.dx;
+ if (regs.w.cflag != 0)
+ I_Error("I_AllocLow: DOS alloc of %i failed, %i free",
+ length, regs.w.bx * 16);
+
+
+ mem = (void *) ((regs.x.eax & 0xFFFF) << 4);
+
+ memset(mem, 0, length);
+ return mem;
+}
+
+/*
+============================================================================
+
+ NETWORKING
+
+============================================================================
+*/
+
+/* // FUCKED LINES
+typedef struct
+{
+ char priv[508];
+ } doomdata_t;
+*/// FUCKED LINES
+
+#define DOOMCOM_ID 0x12345678l
+
+/* // FUCKED LINES
+typedef struct
+{
+ long id;
+ short intnum; // DOOM executes an int to execute commands
+
+// communication between DOOM and the driver
+ short command; // CMD_SEND or CMD_GET
+ short remotenode; // dest for send, set by get (-1 = no packet)
+ short datalength; // bytes in doomdata to be sent
+
+// info common to all nodes
+ short numnodes; // console is allways node 0
+ short ticdup; // 1 = no duplication, 2-5 = dup for slow nets
+ short extratics; // 1 = send a backup tic in every packet
+ short deathmatch; // 1 = deathmatch
+ short savegame; // -1 = new game, 0-5 = load savegame
+ short episode; // 1-3
+ short map; // 1-9
+ short skill; // 1-5
+
+// info specific to this node
+ short consoleplayer;
+ short numplayers;
+ short angleoffset; // 1 = left, 0 = center, -1 = right
+ short drone; // 1 = drone
+
+// packet data to be sent
+ doomdata_t data;
+ } doomcom_t;
+*/// FUCKED LINES
+
+extern doomcom_t *doomcom;
+
+/*
+====================
+=
+= I_InitNetwork
+=
+====================
+*/
+
+void I_InitNetwork(void)
+{
+ int i;
+
+ i = M_CheckParm("-net");
+ if (!i)
+ {
+ //
+ // single player game
+ //
+ doomcom = malloc(sizeof(*doomcom));
+ memset(doomcom, 0, sizeof(*doomcom));
+ netgame = false;
+ doomcom->id = DOOMCOM_ID;
+ doomcom->numplayers = doomcom->numnodes = 1;
+ doomcom->deathmatch = false;
+ doomcom->consoleplayer = 0;
+ doomcom->ticdup = 1;
+ doomcom->extratics = 0;
+ return;
+ }
+
+ netgame = true;
+ doomcom = (doomcom_t *) atoi(myargv[i + 1]);
+//DEBUG
+ doomcom->skill = startskill;
+ doomcom->episode = startepisode;
+ doomcom->map = startmap;
+ doomcom->deathmatch = deathmatch;
+
+}
+
+void I_NetCmd(void)
+{
+ if (!netgame)
+ I_Error("I_NetCmd when not in netgame");
+ DPMIInt(doomcom->intnum);
+}
+
+int i_Vector;
+externdata_t *i_ExternData;
+boolean useexterndriver;
+
+//=========================================================================
+//
+// I_CheckExternDriver
+//
+// Checks to see if a vector, and an address for an external driver
+// have been passed.
+//=========================================================================
+
+void I_CheckExternDriver(void)
+{
+ int i;
+
+ if (!(i = M_CheckParm("-externdriver")))
+ {
+ return;
+ }
+ i_ExternData = (externdata_t *) atoi(myargv[i + 1]);
+ i_Vector = i_ExternData->vector;
+
+ useexterndriver = true;
+}
+
+//=========================================================================
+//
+// I_ReadExternDriver
+//
+// calls the external interrupt, which should then update i_ExternDriver
+//=========================================================================
+
+void I_ReadExternDriver(void)
+{
+ event_t ev;
+
+ if (useexterndriver)
+ {
+ DPMIInt(i_Vector);
+ }
+}
diff --git a/src/heretic/i_sound.c b/src/heretic/i_sound.c
new file mode 100644
index 00000000..d3f04767
--- /dev/null
+++ b/src/heretic/i_sound.c
@@ -0,0 +1,432 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// I_SOUND.C
+
+#include <stdio.h>
+#include "doomdef.h"
+#include "dmx.h"
+#include "sounds.h"
+#include "i_sound.h"
+#include "v_video.h"
+
+/*
+===============
+=
+= I_StartupTimer
+=
+===============
+*/
+
+int tsm_ID = -1;
+
+void I_StartupTimer(void)
+{
+#ifndef NOTIMER
+ extern int I_TimerISR(void);
+
+ tprintf("I_StartupTimer()\n", 0);
+ // installs master timer. Must be done before StartupTimer()!
+ TSM_Install(SND_TICRATE);
+ tsm_ID = TSM_NewService(I_TimerISR, 35, 255, 0); // max priority
+ if (tsm_ID == -1)
+ {
+ I_Error("Can't register 35 Hz timer w/ DMX library");
+ }
+#endif
+}
+
+void I_ShutdownTimer(void)
+{
+ TSM_DelService(tsm_ID);
+ TSM_Remove();
+}
+
+/*
+ *
+ * SOUND HEADER & DATA
+ *
+ *
+ */
+
+// sound information
+#if 0
+const char *dnames[] = { "None",
+ "PC_Speaker",
+ "Adlib",
+ "Sound_Blaster",
+ "ProAudio_Spectrum16",
+ "Gravis_Ultrasound",
+ "MPU",
+ "AWE32"
+};
+#endif
+
+const char snd_prefixen[] = { 'P', 'P', 'A', 'S', 'S', 'S', 'M',
+ 'M', 'M', 'S'
+};
+
+int snd_Channels;
+int snd_DesiredMusicDevice, snd_DesiredSfxDevice;
+int snd_MusicDevice, // current music card # (index to dmxCodes)
+ snd_SfxDevice, // current sfx card # (index to dmxCodes)
+ snd_MaxVolume, // maximum volume for sound
+ snd_MusicVolume; // maximum volume for music
+int dmxCodes[NUM_SCARDS]; // the dmx code for a given card
+
+int snd_SBport, snd_SBirq, snd_SBdma; // sound blaster variables
+int snd_Mport; // midi variables
+
+extern boolean snd_MusicAvail, // whether music is available
+ snd_SfxAvail; // whether sfx are available
+
+void I_PauseSong(int handle)
+{
+ MUS_PauseSong(handle);
+}
+
+void I_ResumeSong(int handle)
+{
+ MUS_ResumeSong(handle);
+}
+
+void I_SetMusicVolume(int volume)
+{
+ MUS_SetMasterVolume(volume * 8);
+// snd_MusicVolume = volume;
+}
+
+void I_SetSfxVolume(int volume)
+{
+ snd_MaxVolume = volume; // THROW AWAY?
+}
+
+/*
+ *
+ * SONG API
+ *
+ */
+
+int I_RegisterSong(void *data)
+{
+ int rc = MUS_RegisterSong(data);
+#ifdef SNDDEBUG
+ if (rc < 0)
+ printf("MUS_Reg() returned %d\n", rc);
+#endif
+ return rc;
+}
+
+void I_UnRegisterSong(int handle)
+{
+ int rc = MUS_UnregisterSong(handle);
+#ifdef SNDDEBUG
+ if (rc < 0)
+ printf("MUS_Unreg() returned %d\n", rc);
+#endif
+}
+
+int I_QrySongPlaying(int handle)
+{
+ int rc = MUS_QrySongPlaying(handle);
+#ifdef SNDDEBUG
+ if (rc < 0)
+ printf("MUS_QrySP() returned %d\n", rc);
+#endif
+ return rc;
+}
+
+// Stops a song. MUST be called before I_UnregisterSong().
+
+void I_StopSong(int handle)
+{
+ int rc;
+ rc = MUS_StopSong(handle);
+#ifdef SNDDEBUG
+ if (rc < 0)
+ printf("MUS_StopSong() returned %d\n", rc);
+#endif
+ // Fucking kluge pause
+ {
+ int s;
+ extern volatile int ticcount;
+ for (s = ticcount; ticcount - s < 10;);
+ }
+}
+
+void I_PlaySong(int handle, boolean looping)
+{
+ int rc;
+ rc = MUS_ChainSong(handle, looping ? handle : -1);
+#ifdef SNDDEBUG
+ if (rc < 0)
+ printf("MUS_ChainSong() returned %d\n", rc);
+#endif
+ rc = MUS_PlaySong(handle, snd_MusicVolume);
+#ifdef SNDDEBUG
+ if (rc < 0)
+ printf("MUS_PlaySong() returned %d\n", rc);
+#endif
+
+}
+
+/*
+ *
+ * SOUND FX API
+ *
+ */
+
+// Gets lump nums of the named sound. Returns pointer which will be
+// passed to I_StartSound() when you want to start an SFX. Must be
+// sure to pass this to UngetSoundEffect() so that they can be
+// freed!
+
+
+int I_GetSfxLumpNum(sfxinfo_t * sound)
+{
+ char namebuf[9];
+
+ if (sound->name == 0)
+ return 0;
+ if (sound->link)
+ sound = sound->link;
+// sprintf(namebuf, "d%c%s", snd_prefixen[snd_SfxDevice], sound->name);
+ return W_GetNumForName(sound->name);
+
+}
+
+int I_StartSound(int id, void *data, int vol, int sep, int pitch,
+ int priority)
+{
+/*
+ // hacks out certain PC sounds
+ if (snd_SfxDevice == PC
+ && (data == S_sfx[sfx_posact].data
+ || data == S_sfx[sfx_bgact].data
+ || data == S_sfx[sfx_dmact].data
+ || data == S_sfx[sfx_dmpain].data
+ || data == S_sfx[sfx_popain].data
+ || data == S_sfx[sfx_sawidl].data)) return -1;
+
+ else
+ */
+ return SFX_PlayPatch(data, pitch, sep, vol, 0, 0);
+
+}
+
+void I_StopSound(int handle)
+{
+// extern volatile long gDmaCount;
+// long waittocount;
+ SFX_StopPatch(handle);
+// waittocount = gDmaCount + 2;
+// while (gDmaCount < waittocount) ;
+}
+
+int I_SoundIsPlaying(int handle)
+{
+ return SFX_Playing(handle);
+}
+
+void I_UpdateSoundParams(int handle, int vol, int sep, int pitch)
+{
+ SFX_SetOrigin(handle, pitch, sep, vol);
+}
+
+/*
+ *
+ * SOUND STARTUP STUFF
+ *
+ *
+ */
+
+//
+// Why PC's Suck, Reason #8712
+//
+
+void I_sndArbitrateCards(void)
+{
+ char tmp[160];
+ boolean gus, adlib, pc, sb, midi;
+ int i, rc, mputype, p, opltype, wait, dmxlump;
+
+// snd_MaxVolume = 127;
+ //Damn you, Dave Taylor!
+
+ snd_MusicDevice = snd_DesiredMusicDevice;
+ snd_SfxDevice = snd_DesiredSfxDevice;
+
+ // check command-line parameters- overrides config file
+ //
+ if (M_CheckParm("-nosound"))
+ snd_MusicDevice = snd_SfxDevice = snd_none;
+ if (M_CheckParm("-nosfx"))
+ snd_SfxDevice = snd_none;
+ if (M_CheckParm("-nomusic"))
+ snd_MusicDevice = snd_none;
+
+ if (snd_MusicDevice > snd_MPU && snd_MusicDevice <= snd_MPU3)
+ snd_MusicDevice = snd_MPU;
+ if (snd_MusicDevice == snd_SB)
+ snd_MusicDevice = snd_Adlib;
+ if (snd_MusicDevice == snd_PAS)
+ snd_MusicDevice = snd_Adlib;
+
+ // figure out what i've got to initialize
+ //
+ gus = snd_MusicDevice == snd_GUS || snd_SfxDevice == snd_GUS;
+ sb = snd_SfxDevice == snd_SB || snd_MusicDevice == snd_SB;
+ adlib = snd_MusicDevice == snd_Adlib;
+ pc = snd_SfxDevice == snd_PC;
+ midi = snd_MusicDevice == snd_MPU;
+
+ // initialize whatever i've got
+ //
+ if (gus)
+ {
+ if (GF1_Detect())
+ tprintf("Dude. The GUS ain't responding.\n", 1);
+ else
+ {
+ dmxlump = W_GetNumForName("dmxgus");
+ GF1_SetMap(W_CacheLumpNum(dmxlump, PU_CACHE),
+ lumpinfo[dmxlump].size);
+ }
+
+ }
+ if (sb)
+ {
+ if (debugmode)
+ {
+ sprintf(tmp, "cfg p=0x%x, i=%d, d=%d\n",
+ snd_SBport, snd_SBirq, snd_SBdma);
+ tprintf(tmp, 0);
+ }
+ if (SB_Detect(&snd_SBport, &snd_SBirq, &snd_SBdma, 0))
+ {
+ sprintf(tmp, "SB isn't responding at p=0x%x, i=%d, d=%d\n",
+ snd_SBport, snd_SBirq, snd_SBdma);
+ tprintf(tmp, 0);
+ }
+ else
+ SB_SetCard(snd_SBport, snd_SBirq, snd_SBdma);
+
+ if (debugmode)
+ {
+ sprintf(tmp, "SB_Detect returned p=0x%x,i=%d,d=%d\n",
+ snd_SBport, snd_SBirq, snd_SBdma);
+ tprintf(tmp, 0);
+ }
+ }
+
+ if (adlib)
+ {
+ if (AL_Detect(&wait, 0))
+ tprintf("Dude. The Adlib isn't responding.\n", 1);
+ else
+ AL_SetCard(wait, W_CacheLumpName("genmidi", PU_STATIC));
+ }
+
+ if (midi)
+ {
+ if (debugmode)
+ {
+ sprintf(tmp, "cfg p=0x%x\n", snd_Mport);
+ tprintf(tmp, 0);
+ }
+
+ if (MPU_Detect(&snd_Mport, &i))
+ {
+ sprintf(tmp, "The MPU-401 isn't reponding @ p=0x%x.\n",
+ snd_Mport);
+ tprintf(tmp, 0);
+ }
+ else
+ MPU_SetCard(snd_Mport);
+ }
+
+}
+
+// inits all sound stuff
+
+void I_StartupSound(void)
+{
+ char tmp[80];
+ int rc, i;
+
+ if (debugmode)
+ tprintf("I_StartupSound: Hope you hear a pop.\n", 1);
+
+ // initialize dmxCodes[]
+ dmxCodes[0] = 0;
+ dmxCodes[snd_PC] = AHW_PC_SPEAKER;
+ dmxCodes[snd_Adlib] = AHW_ADLIB;
+ dmxCodes[snd_SB] = AHW_SOUND_BLASTER;
+ dmxCodes[snd_PAS] = AHW_MEDIA_VISION;
+ dmxCodes[snd_GUS] = AHW_ULTRA_SOUND;
+ dmxCodes[snd_MPU] = AHW_MPU_401;
+ dmxCodes[snd_AWE] = AHW_AWE32;
+
+ // inits sound library timer stuff
+ I_StartupTimer();
+
+ // pick the sound cards i'm going to use
+ //
+ I_sndArbitrateCards();
+
+ if (debugmode)
+ {
+ sprintf(tmp, " Music device #%d & dmxCode=%d", snd_MusicDevice,
+ dmxCodes[snd_MusicDevice]);
+ tprintf(tmp, 0);
+ sprintf(tmp, " Sfx device #%d & dmxCode=%d\n", snd_SfxDevice,
+ dmxCodes[snd_SfxDevice]);
+ tprintf(tmp, 0);
+ }
+
+ // inits DMX sound library
+ tprintf(" calling DMX_Init", 0);
+ rc = DMX_Init(SND_TICRATE, SND_MAXSONGS, dmxCodes[snd_MusicDevice],
+ dmxCodes[snd_SfxDevice]);
+
+ if (debugmode)
+ {
+ sprintf(tmp, " DMX_Init() returned %d", rc);
+ tprintf(tmp, 0);
+ }
+
+}
+
+// shuts down all sound stuff
+
+void I_ShutdownSound(void)
+{
+ DMX_DeInit();
+ I_ShutdownTimer();
+}
+
+void I_SetChannels(int channels)
+{
+ WAV_PlayMode(channels, SND_SAMPLERATE);
+}
diff --git a/src/heretic/in_lude.c b/src/heretic/in_lude.c
new file mode 100644
index 00000000..3c382814
--- /dev/null
+++ b/src/heretic/in_lude.c
@@ -0,0 +1,1077 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+/*
+========================
+=
+= IN_lude.c
+=
+========================
+*/
+
+#include "doomdef.h"
+#include "deh_str.h"
+#include "s_sound.h"
+#include "i_system.h"
+#include "i_video.h"
+#include "v_video.h"
+
+typedef enum
+{
+ SINGLE,
+ COOPERATIVE,
+ DEATHMATCH
+} gametype_t;
+
+// Public functions
+
+void IN_Start(void);
+void IN_Ticker(void);
+void IN_Drawer(void);
+
+boolean intermission;
+
+// Private functions
+
+void IN_WaitStop(void);
+void IN_Stop(void);
+void IN_LoadPics(void);
+void IN_UnloadPics(void);
+void IN_CheckForSkip(void);
+void IN_InitStats(void);
+void IN_InitDeathmatchStats(void);
+void IN_InitNetgameStats(void);
+void IN_DrawOldLevel(void);
+void IN_DrawYAH(void);
+void IN_DrawStatBack(void);
+void IN_DrawSingleStats(void);
+void IN_DrawCoopStats(void);
+void IN_DrawDMStats(void);
+void IN_DrawNumber(int val, int x, int y, int digits);
+void IN_DrawTime(int x, int y, int h, int m, int s);
+void IN_DrTextB(char *text, int x, int y);
+
+static boolean skipintermission;
+static int interstate = 0;
+static int intertime = -1;
+static int oldintertime = 0;
+static gametype_t gametype;
+
+static int cnt;
+
+static int hours;
+static int minutes;
+static int seconds;
+
+static int slaughterboy; // in DM, the player with the most kills
+
+static int killPercent[MAXPLAYERS];
+static int bonusPercent[MAXPLAYERS];
+static int secretPercent[MAXPLAYERS];
+
+static patch_t *patchINTERPIC;
+static patch_t *patchBEENTHERE;
+static patch_t *patchGOINGTHERE;
+static patch_t *FontBNumbers[10];
+static patch_t *FontBNegative;
+static patch_t *FontBSlash;
+static patch_t *FontBPercent;
+
+static int FontBLump;
+static int FontBLumpBase;
+static int patchFaceOkayBase;
+static int patchFaceDeadBase;
+
+static signed int totalFrags[MAXPLAYERS];
+static fixed_t dSlideX[MAXPLAYERS];
+static fixed_t dSlideY[MAXPLAYERS];
+
+static char *KillersText[] = { "K", "I", "L", "L", "E", "R", "S" };
+
+extern char *LevelNames[];
+
+typedef struct
+{
+ int x;
+ int y;
+} yahpt_t;
+
+static yahpt_t YAHspot[3][9] = {
+ {
+ {172, 78},
+ {86, 90},
+ {73, 66},
+ {159, 95},
+ {148, 126},
+ {132, 54},
+ {131, 74},
+ {208, 138},
+ {52, 101}
+ },
+ {
+ {218, 57},
+ {137, 81},
+ {155, 124},
+ {171, 68},
+ {250, 86},
+ {136, 98},
+ {203, 90},
+ {220, 140},
+ {279, 106}
+ },
+ {
+ {86, 99},
+ {124, 103},
+ {154, 79},
+ {202, 83},
+ {178, 59},
+ {142, 58},
+ {219, 66},
+ {247, 57},
+ {107, 80}
+ }
+};
+
+//========================================================================
+//
+// IN_Start
+//
+//========================================================================
+
+extern void AM_Stop(void);
+
+void IN_Start(void)
+{
+ I_SetPalette(W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE));
+ IN_LoadPics();
+ IN_InitStats();
+ intermission = true;
+ interstate = -1;
+ skipintermission = false;
+ intertime = 0;
+ oldintertime = 0;
+ AM_Stop();
+ S_StartSong(mus_intr, true);
+}
+
+//========================================================================
+//
+// IN_WaitStop
+//
+//========================================================================
+
+void IN_WaitStop(void)
+{
+ if (!--cnt)
+ {
+ IN_Stop();
+ G_WorldDone();
+ }
+}
+
+//========================================================================
+//
+// IN_Stop
+//
+//========================================================================
+
+void IN_Stop(void)
+{
+ intermission = false;
+ IN_UnloadPics();
+ SB_state = -1;
+ BorderNeedRefresh = true;
+}
+
+//========================================================================
+//
+// IN_InitStats
+//
+// Initializes the stats for single player mode
+//========================================================================
+
+void IN_InitStats(void)
+{
+ int i;
+ int j;
+ signed int slaughterfrags;
+ int posnum;
+ int slaughtercount;
+ int playercount;
+ int count;
+
+ if (!netgame)
+ {
+ gametype = SINGLE;
+ count = leveltime / 35;
+ hours = count / 3600;
+ count -= hours * 3600;
+ minutes = count / 60;
+ count -= minutes * 60;
+ seconds = count;
+ }
+ else if (netgame && !deathmatch)
+ {
+ gametype = COOPERATIVE;
+ memset(killPercent, 0, MAXPLAYERS * sizeof(int));
+ memset(bonusPercent, 0, MAXPLAYERS * sizeof(int));
+ memset(secretPercent, 0, MAXPLAYERS * sizeof(int));
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i])
+ {
+ if (totalkills)
+ {
+ killPercent[i] = players[i].killcount * 100 / totalkills;
+ }
+ if (totalitems)
+ {
+ bonusPercent[i] = players[i].itemcount * 100 / totalitems;
+ }
+ if (totalsecret)
+ {
+ secretPercent[i] =
+ players[i].secretcount * 100 / totalsecret;
+ }
+ }
+ }
+ }
+ else
+ {
+ gametype = DEATHMATCH;
+ slaughterboy = 0;
+ slaughterfrags = -9999;
+ posnum = 0;
+ playercount = 0;
+ slaughtercount = 0;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ totalFrags[i] = 0;
+ if (playeringame[i])
+ {
+ playercount++;
+ for (j = 0; j < MAXPLAYERS; j++)
+ {
+ if (playeringame[j])
+ {
+ totalFrags[i] += players[i].frags[j];
+ }
+ }
+ dSlideX[i] = (43 * posnum * FRACUNIT) / 20;
+ dSlideY[i] = (36 * posnum * FRACUNIT) / 20;
+ posnum++;
+ }
+ if (totalFrags[i] > slaughterfrags)
+ {
+ slaughterboy = 1 << i;
+ slaughterfrags = totalFrags[i];
+ slaughtercount = 1;
+ }
+ else if (totalFrags[i] == slaughterfrags)
+ {
+ slaughterboy |= 1 << i;
+ slaughtercount++;
+ }
+ }
+ if (playercount == slaughtercount)
+ { // don't do the slaughter stuff if everyone is equal
+ slaughterboy = 0;
+ }
+ }
+}
+
+static void IN_LoadUnloadPics(void (*callback)(char *lumpname,
+ int lumpnum,
+ patch_t **ptr))
+{
+ int i;
+
+ switch (gameepisode)
+ {
+ case 1:
+ callback(DEH_String("MAPE1"), 0, &patchINTERPIC);
+ break;
+ case 2:
+ callback(DEH_String("MAPE2"), 0, &patchINTERPIC);
+ break;
+ case 3:
+ callback(DEH_String("MAPE3"), 0, &patchINTERPIC);
+ break;
+ default:
+ break;
+ }
+
+ callback(DEH_String("IN_X"), 0, &patchBEENTHERE);
+ callback(DEH_String("IN_YAH"), 0, &patchGOINGTHERE);
+ callback(DEH_String("FONTB13"), 0, &FontBNegative);
+
+ callback(DEH_String("FONTB15"), 0, &FontBSlash);
+ callback(DEH_String("FONTB05"), 0, &FontBPercent);
+
+ FontBLumpBase = W_GetNumForName(DEH_String("FONTB16"));
+
+ for (i = 0; i < 10; i++)
+ {
+ callback(NULL, FontBLumpBase + i, &FontBNumbers[i]);
+ }
+}
+
+//========================================================================
+//
+// IN_LoadPics
+//
+//========================================================================
+
+static void LoadLumpCallback(char *lumpname, int lumpnum, patch_t **ptr)
+{
+ if (lumpname != NULL)
+ {
+ lumpnum = W_GetNumForName(lumpname);
+ }
+
+ // Cache the lump
+
+ *ptr = W_CacheLumpNum(lumpnum, PU_STATIC);
+}
+
+void IN_LoadPics(void)
+{
+ FontBLump = W_GetNumForName(DEH_String("FONTB_S")) + 1;
+ patchFaceOkayBase = W_GetNumForName(DEH_String("FACEA0"));
+ patchFaceDeadBase = W_GetNumForName(DEH_String("FACEB0"));
+
+ IN_LoadUnloadPics(LoadLumpCallback);
+}
+
+//========================================================================
+//
+// IN_UnloadPics
+//
+//========================================================================
+
+static void UnloadLumpCallback(char *lumpname, int lumpnum, patch_t **ptr)
+{
+ if (lumpname != NULL)
+ {
+ W_ReleaseLumpName(lumpname);
+ }
+ else
+ {
+ W_ReleaseLumpNum(lumpnum);
+ }
+}
+
+void IN_UnloadPics(void)
+{
+ IN_LoadUnloadPics(UnloadLumpCallback);
+}
+
+//========================================================================
+//
+// IN_Ticker
+//
+//========================================================================
+
+void IN_Ticker(void)
+{
+ if (!intermission)
+ {
+ return;
+ }
+ if (interstate == 3)
+ {
+ IN_WaitStop();
+ return;
+ }
+ IN_CheckForSkip();
+ intertime++;
+ if (oldintertime < intertime)
+ {
+ interstate++;
+ if (gameepisode > 3 && interstate >= 1)
+ { // Extended Wad levels: skip directly to the next level
+ interstate = 3;
+ }
+ switch (interstate)
+ {
+ case 0:
+ oldintertime = intertime + 300;
+ if (gameepisode > 3)
+ {
+ oldintertime = intertime + 1200;
+ }
+ break;
+ case 1:
+ oldintertime = intertime + 200;
+ break;
+ case 2:
+ oldintertime = INT_MAX;
+ break;
+ case 3:
+ cnt = 10;
+ break;
+ default:
+ break;
+ }
+ }
+ if (skipintermission)
+ {
+ if (interstate == 0 && intertime < 150)
+ {
+ intertime = 150;
+ skipintermission = false;
+ return;
+ }
+ else if (interstate < 2 && gameepisode < 4)
+ {
+ interstate = 2;
+ skipintermission = false;
+ S_StartSound(NULL, sfx_dorcls);
+ return;
+ }
+ interstate = 3;
+ cnt = 10;
+ skipintermission = false;
+ S_StartSound(NULL, sfx_dorcls);
+ }
+}
+
+//========================================================================
+//
+// IN_CheckForSkip
+//
+// Check to see if any player hit a key
+//========================================================================
+
+void IN_CheckForSkip(void)
+{
+ int i;
+ player_t *player;
+
+ for (i = 0, player = players; i < MAXPLAYERS; i++, player++)
+ {
+ if (playeringame[i])
+ {
+ if (player->cmd.buttons & BT_ATTACK)
+ {
+ if (!player->attackdown)
+ {
+ skipintermission = 1;
+ }
+ player->attackdown = true;
+ }
+ else
+ {
+ player->attackdown = false;
+ }
+ if (player->cmd.buttons & BT_USE)
+ {
+ if (!player->usedown)
+ {
+ skipintermission = 1;
+ }
+ player->usedown = true;
+ }
+ else
+ {
+ player->usedown = false;
+ }
+ }
+ }
+}
+
+//========================================================================
+//
+// IN_Drawer
+//
+//========================================================================
+
+void IN_Drawer(void)
+{
+ static int oldinterstate;
+
+ if (!intermission)
+ {
+ return;
+ }
+ if (interstate == 3)
+ {
+ return;
+ }
+ UpdateState |= I_FULLSCRN;
+ if (oldinterstate != 2 && interstate == 2)
+ {
+ S_StartSound(NULL, sfx_pstop);
+ }
+ oldinterstate = interstate;
+ switch (interstate)
+ {
+ case 0: // draw stats
+ IN_DrawStatBack();
+ switch (gametype)
+ {
+ case SINGLE:
+ IN_DrawSingleStats();
+ break;
+ case COOPERATIVE:
+ IN_DrawCoopStats();
+ break;
+ case DEATHMATCH:
+ IN_DrawDMStats();
+ break;
+ }
+ break;
+ case 1: // leaving old level
+ if (gameepisode < 4)
+ {
+ V_DrawPatch(0, 0, patchINTERPIC);
+ IN_DrawOldLevel();
+ }
+ break;
+ case 2: // going to the next level
+ if (gameepisode < 4)
+ {
+ V_DrawPatch(0, 0, patchINTERPIC);
+ IN_DrawYAH();
+ }
+ break;
+ case 3: // waiting before going to the next level
+ if (gameepisode < 4)
+ {
+ V_DrawPatch(0, 0, patchINTERPIC);
+ }
+ break;
+ default:
+ I_Error("IN_lude: Intermission state out of range.\n");
+ break;
+ }
+}
+
+//========================================================================
+//
+// IN_DrawStatBack
+//
+//========================================================================
+
+void IN_DrawStatBack(void)
+{
+ int x;
+ int y;
+
+ byte *src;
+ byte *dest;
+
+ src = W_CacheLumpName(DEH_String("FLOOR16"), PU_CACHE);
+ dest = I_VideoBuffer;
+
+ for (y = 0; y < SCREENHEIGHT; y++)
+ {
+ for (x = 0; x < SCREENWIDTH / 64; x++)
+ {
+ memcpy(dest, src + ((y & 63) << 6), 64);
+ dest += 64;
+ }
+ if (SCREENWIDTH & 63)
+ {
+ memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63);
+ dest += (SCREENWIDTH & 63);
+ }
+ }
+}
+
+//========================================================================
+//
+// IN_DrawOldLevel
+//
+//========================================================================
+
+void IN_DrawOldLevel(void)
+{
+ int i;
+ int x;
+
+ x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] +
+ 7) / 2;
+ IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7, x, 3);
+ x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2;
+ MN_DrTextA(DEH_String("FINISHED"), x, 25);
+
+ if (prevmap == 9)
+ {
+ for (i = 0; i < gamemap - 1; i++)
+ {
+ V_DrawPatch(YAHspot[gameepisode - 1][i].x,
+ YAHspot[gameepisode - 1][i].y, patchBEENTHERE);
+ }
+ if (!(intertime & 16))
+ {
+ V_DrawPatch(YAHspot[gameepisode - 1][8].x,
+ YAHspot[gameepisode - 1][8].y, patchBEENTHERE);
+ }
+ }
+ else
+ {
+ for (i = 0; i < prevmap - 1; i++)
+ {
+ V_DrawPatch(YAHspot[gameepisode - 1][i].x,
+ YAHspot[gameepisode - 1][i].y, patchBEENTHERE);
+ }
+ if (players[consoleplayer].didsecret)
+ {
+ V_DrawPatch(YAHspot[gameepisode - 1][8].x,
+ YAHspot[gameepisode - 1][8].y, patchBEENTHERE);
+ }
+ if (!(intertime & 16))
+ {
+ V_DrawPatch(YAHspot[gameepisode - 1][prevmap - 1].x,
+ YAHspot[gameepisode - 1][prevmap - 1].y,
+ patchBEENTHERE);
+ }
+ }
+}
+
+//========================================================================
+//
+// IN_DrawYAH
+//
+//========================================================================
+
+void IN_DrawYAH(void)
+{
+ int i;
+ int x;
+
+ x = 160 - MN_TextAWidth(DEH_String("NOW ENTERING:")) / 2;
+ MN_DrTextA(DEH_String("NOW ENTERING:"), x, 10);
+ x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] +
+ 7) / 2;
+ IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] + 7, x, 20);
+
+ if (prevmap == 9)
+ {
+ prevmap = gamemap - 1;
+ }
+ for (i = 0; i < prevmap; i++)
+ {
+ V_DrawPatch(YAHspot[gameepisode - 1][i].x,
+ YAHspot[gameepisode - 1][i].y, patchBEENTHERE);
+ }
+ if (players[consoleplayer].didsecret)
+ {
+ V_DrawPatch(YAHspot[gameepisode - 1][8].x,
+ YAHspot[gameepisode - 1][8].y, patchBEENTHERE);
+ }
+ if (!(intertime & 16) || interstate == 3)
+ { // draw the destination 'X'
+ V_DrawPatch(YAHspot[gameepisode - 1][gamemap - 1].x,
+ YAHspot[gameepisode - 1][gamemap - 1].y, patchGOINGTHERE);
+ }
+}
+
+//========================================================================
+//
+// IN_DrawSingleStats
+//
+//========================================================================
+
+void IN_DrawSingleStats(void)
+{
+ int x;
+ static int sounds;
+
+ IN_DrTextB(DEH_String("KILLS"), 50, 65);
+ IN_DrTextB(DEH_String("ITEMS"), 50, 90);
+ IN_DrTextB(DEH_String("SECRETS"), 50, 115);
+
+ x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] +
+ 7) / 2;
+ IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7, x, 3);
+ x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2;
+ MN_DrTextA(DEH_String("FINISHED"), x, 25);
+
+ if (intertime < 30)
+ {
+ sounds = 0;
+ return;
+ }
+ if (sounds < 1 && intertime >= 30)
+ {
+ S_StartSound(NULL, sfx_dorcls);
+ sounds++;
+ }
+ IN_DrawNumber(players[consoleplayer].killcount, 200, 65, 3);
+ V_DrawShadowedPatch(237, 65, FontBSlash);
+ IN_DrawNumber(totalkills, 248, 65, 3);
+ if (intertime < 60)
+ {
+ return;
+ }
+ if (sounds < 2 && intertime >= 60)
+ {
+ S_StartSound(NULL, sfx_dorcls);
+ sounds++;
+ }
+ IN_DrawNumber(players[consoleplayer].itemcount, 200, 90, 3);
+ V_DrawShadowedPatch(237, 90, FontBSlash);
+ IN_DrawNumber(totalitems, 248, 90, 3);
+ if (intertime < 90)
+ {
+ return;
+ }
+ if (sounds < 3 && intertime >= 90)
+ {
+ S_StartSound(NULL, sfx_dorcls);
+ sounds++;
+ }
+ IN_DrawNumber(players[consoleplayer].secretcount, 200, 115, 3);
+ V_DrawShadowedPatch(237, 115, FontBSlash);
+ IN_DrawNumber(totalsecret, 248, 115, 3);
+ if (intertime < 150)
+ {
+ return;
+ }
+ if (sounds < 4 && intertime >= 150)
+ {
+ S_StartSound(NULL, sfx_dorcls);
+ sounds++;
+ }
+
+ if (gamemode != retail || gameepisode <= 3)
+ {
+ IN_DrTextB(DEH_String("TIME"), 85, 160);
+ IN_DrawTime(155, 160, hours, minutes, seconds);
+ }
+ else
+ {
+ x = 160 - MN_TextAWidth(DEH_String("NOW ENTERING:")) / 2;
+ MN_DrTextA(DEH_String("NOW ENTERING:"), x, 160);
+ x = 160 -
+ MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] +
+ 7) / 2;
+ IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + gamemap - 1] + 7, x,
+ 170);
+ skipintermission = false;
+ }
+}
+
+//========================================================================
+//
+// IN_DrawCoopStats
+//
+//========================================================================
+
+void IN_DrawCoopStats(void)
+{
+ int i;
+ int x;
+ int ypos;
+
+ static int sounds;
+
+ IN_DrTextB(DEH_String("KILLS"), 95, 35);
+ IN_DrTextB(DEH_String("BONUS"), 155, 35);
+ IN_DrTextB(DEH_String("SECRET"), 232, 35);
+ x = 160 - MN_TextBWidth(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] +
+ 7) / 2;
+ IN_DrTextB(LevelNames[(gameepisode - 1) * 9 + prevmap - 1] + 7, x, 3);
+ x = 160 - MN_TextAWidth(DEH_String("FINISHED")) / 2;
+ MN_DrTextA(DEH_String("FINISHED"), x, 25);
+
+ ypos = 50;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i])
+ {
+ V_DrawShadowedPatch(25, ypos,
+ W_CacheLumpNum(patchFaceOkayBase + i,
+ PU_CACHE));
+ if (intertime < 40)
+ {
+ sounds = 0;
+ ypos += 37;
+ continue;
+ }
+ else if (intertime >= 40 && sounds < 1)
+ {
+ S_StartSound(NULL, sfx_dorcls);
+ sounds++;
+ }
+ IN_DrawNumber(killPercent[i], 85, ypos + 10, 3);
+ V_DrawShadowedPatch(121, ypos + 10, FontBPercent);
+ IN_DrawNumber(bonusPercent[i], 160, ypos + 10, 3);
+ V_DrawShadowedPatch(196, ypos + 10, FontBPercent);
+ IN_DrawNumber(secretPercent[i], 237, ypos + 10, 3);
+ V_DrawShadowedPatch(273, ypos + 10, FontBPercent);
+ ypos += 37;
+ }
+ }
+}
+
+//========================================================================
+//
+// IN_DrawDMStats
+//
+//========================================================================
+
+void IN_DrawDMStats(void)
+{
+ int i;
+ int j;
+ int ypos;
+ int xpos;
+ int kpos;
+
+ static int sounds;
+
+ xpos = 90;
+ ypos = 55;
+
+ IN_DrTextB(DEH_String("TOTAL"), 265, 30);
+ MN_DrTextA(DEH_String("VICTIMS"), 140, 8);
+ for (i = 0; i < 7; i++)
+ {
+ MN_DrTextA(DEH_String(KillersText[i]), 10, 80 + 9 * i);
+ }
+ if (intertime < 20)
+ {
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i])
+ {
+ V_DrawShadowedPatch(40,
+ ((ypos << FRACBITS) +
+ dSlideY[i] * intertime) >> FRACBITS,
+ W_CacheLumpNum(patchFaceOkayBase + i,
+ PU_CACHE));
+ V_DrawShadowedPatch(((xpos << FRACBITS) +
+ dSlideX[i] * intertime) >> FRACBITS, 18,
+ W_CacheLumpNum(patchFaceDeadBase + i,
+ PU_CACHE));
+ }
+ }
+ sounds = 0;
+ return;
+ }
+ if (intertime >= 20 && sounds < 1)
+ {
+ S_StartSound(NULL, sfx_dorcls);
+ sounds++;
+ }
+ if (intertime >= 100 && slaughterboy && sounds < 2)
+ {
+ S_StartSound(NULL, sfx_wpnup);
+ sounds++;
+ }
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i])
+ {
+ if (intertime < 100 || i == consoleplayer)
+ {
+ V_DrawShadowedPatch(40, ypos,
+ W_CacheLumpNum(patchFaceOkayBase + i,
+ PU_CACHE));
+ V_DrawShadowedPatch(xpos, 18,
+ W_CacheLumpNum(patchFaceDeadBase + i,
+ PU_CACHE));
+ }
+ else
+ {
+ V_DrawTLPatch(40, ypos,
+ W_CacheLumpNum(patchFaceOkayBase + i,
+ PU_CACHE));
+ V_DrawTLPatch(xpos, 18,
+ W_CacheLumpNum(patchFaceDeadBase + i,
+ PU_CACHE));
+ }
+ kpos = 86;
+ for (j = 0; j < MAXPLAYERS; j++)
+ {
+ if (playeringame[j])
+ {
+ IN_DrawNumber(players[i].frags[j], kpos, ypos + 10, 3);
+ kpos += 43;
+ }
+ }
+ if (slaughterboy & (1 << i))
+ {
+ if (!(intertime & 16))
+ {
+ IN_DrawNumber(totalFrags[i], 263, ypos + 10, 3);
+ }
+ }
+ else
+ {
+ IN_DrawNumber(totalFrags[i], 263, ypos + 10, 3);
+ }
+ ypos += 36;
+ xpos += 43;
+ }
+ }
+}
+
+//========================================================================
+//
+// IN_DrawTime
+//
+//========================================================================
+
+void IN_DrawTime(int x, int y, int h, int m, int s)
+{
+ if (h)
+ {
+ IN_DrawNumber(h, x, y, 2);
+ IN_DrTextB(DEH_String(":"), x + 26, y);
+ }
+ x += 34;
+ if (m || h)
+ {
+ IN_DrawNumber(m, x, y, 2);
+ }
+ x += 34;
+ if (s)
+ {
+ IN_DrTextB(DEH_String(":"), x - 8, y);
+ IN_DrawNumber(s, x, y, 2);
+ }
+}
+
+//========================================================================
+//
+// IN_DrawNumber
+//
+//========================================================================
+
+void IN_DrawNumber(int val, int x, int y, int digits)
+{
+ patch_t *patch;
+ int xpos;
+ int oldval;
+ int realdigits;
+ boolean neg;
+
+ oldval = val;
+ xpos = x;
+ neg = false;
+ realdigits = 1;
+
+ if (val < 0)
+ { //...this should reflect negative frags
+ val = -val;
+ neg = true;
+ if (val > 99)
+ {
+ val = 99;
+ }
+ }
+ if (val > 9)
+ {
+ realdigits++;
+ if (digits < realdigits)
+ {
+ realdigits = digits;
+ val = 9;
+ }
+ }
+ if (val > 99)
+ {
+ realdigits++;
+ if (digits < realdigits)
+ {
+ realdigits = digits;
+ val = 99;
+ }
+ }
+ if (val > 999)
+ {
+ realdigits++;
+ if (digits < realdigits)
+ {
+ realdigits = digits;
+ val = 999;
+ }
+ }
+ if (digits == 4)
+ {
+ patch = FontBNumbers[val / 1000];
+ V_DrawShadowedPatch(xpos + 6 - patch->width / 2 - 12, y, patch);
+ }
+ if (digits > 2)
+ {
+ if (realdigits > 2)
+ {
+ patch = FontBNumbers[val / 100];
+ V_DrawShadowedPatch(xpos + 6 - patch->width / 2, y, patch);
+ }
+ xpos += 12;
+ }
+ val = val % 100;
+ if (digits > 1)
+ {
+ if (val > 9)
+ {
+ patch = FontBNumbers[val / 10];
+ V_DrawShadowedPatch(xpos + 6 - patch->width / 2, y, patch);
+ }
+ else if (digits == 2 || oldval > 99)
+ {
+ V_DrawShadowedPatch(xpos, y, FontBNumbers[0]);
+ }
+ xpos += 12;
+ }
+ val = val % 10;
+ patch = FontBNumbers[val];
+ V_DrawShadowedPatch(xpos + 6 - patch->width / 2, y, patch);
+ if (neg)
+ {
+ patch = FontBNegative;
+ V_DrawShadowedPatch(xpos + 6 - patch->width / 2 - 12 * (realdigits),
+ y, patch);
+ }
+}
+
+//========================================================================
+//
+// IN_DrTextB
+//
+//========================================================================
+
+void IN_DrTextB(char *text, int x, int y)
+{
+ char c;
+ patch_t *p;
+
+ while ((c = *text++) != 0)
+ {
+ if (c < 33)
+ {
+ x += 8;
+ }
+ else
+ {
+ p = W_CacheLumpNum(FontBLump + c - 33, PU_CACHE);
+ V_DrawShadowedPatch(x, y, p);
+ x += p->width - 1;
+ }
+ }
+}
diff --git a/src/heretic/info.c b/src/heretic/info.c
new file mode 100644
index 00000000..b6dd921f
--- /dev/null
+++ b/src/heretic/info.c
@@ -0,0 +1,5608 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 "doomdef.h"
+#include "p_action.h"
+
+char *sprnames[] = {
+ "IMPX","ACLO","PTN1","SHLD","SHD2","BAGH","SPMP","INVS","PTN2","SOAR",
+ "INVU","PWBK","EGGC","EGGM","FX01","SPHL","TRCH","FBMB","XPL1","ATLP",
+ "PPOD","AMG1","SPSH","LVAS","SLDG","SKH1","SKH2","SKH3","SKH4","CHDL",
+ "SRTC","SMPL","STGS","STGL","STCS","STCL","KFR1","BARL","BRPL","MOS1",
+ "MOS2","WTRH","HCOR","KGZ1","KGZB","KGZG","KGZY","VLCO","VFBL","VTFB",
+ "SFFI","TGLT","TELE","STFF","PUF3","PUF4","BEAK","WGNT","GAUN","PUF1",
+ "WBLS","BLSR","FX18","FX17","WMCE","MACE","FX02","WSKL","HROD","FX00",
+ "FX20","FX21","FX22","FX23","GWND","PUF2","WPHX","PHNX","FX04","FX08",
+ "FX09","WBOW","CRBW","FX03","BLOD","PLAY","FDTH","BSKL","CHKN","MUMM",
+ "FX15","BEAS","FRB1","SNKE","SNFX","HEAD","FX05","FX06","FX07","CLNK",
+ "WZRD","FX11","FX10","KNIG","SPAX","RAXE","SRCR","FX14","SOR2","SDTH",
+ "FX16","MNTR","FX12","FX13","AKYY","BKYY","CKYY","AMG2","AMM1","AMM2",
+ "AMC1","AMC2","AMS1","AMS2","AMP1","AMP2","AMB1","AMB2",
+ NULL
+};
+
+state_t states[NUMSTATES] = {
+ {SPR_IMPX, 0, -1, NULL, S_NULL, 0, 0}, // S_NULL
+ {SPR_ACLO, 4, 1050, A_FreeTargMobj, S_NULL, 0, 0}, // S_FREETARGMOBJ
+ {SPR_PTN1, 0, 3, NULL, S_ITEM_PTN1_2, 0, 0}, // S_ITEM_PTN1_1
+ {SPR_PTN1, 1, 3, NULL, S_ITEM_PTN1_3, 0, 0}, // S_ITEM_PTN1_2
+ {SPR_PTN1, 2, 3, NULL, S_ITEM_PTN1_1, 0, 0}, // S_ITEM_PTN1_3
+ {SPR_SHLD, 0, -1, NULL, S_NULL, 0, 0}, // S_ITEM_SHLD1
+ {SPR_SHD2, 0, -1, NULL, S_NULL, 0, 0}, // S_ITEM_SHD2_1
+ {SPR_BAGH, 0, -1, NULL, S_NULL, 0, 0}, // S_ITEM_BAGH1
+ {SPR_SPMP, 0, -1, NULL, S_NULL, 0, 0}, // S_ITEM_SPMP1
+ {SPR_ACLO, 4, 1400, NULL, S_HIDESPECIAL2, 0, 0}, // S_HIDESPECIAL1
+ {SPR_ACLO, 0, 4, A_RestoreSpecialThing1, S_HIDESPECIAL3, 0, 0}, // S_HIDESPECIAL2
+ {SPR_ACLO, 1, 4, NULL, S_HIDESPECIAL4, 0, 0}, // S_HIDESPECIAL3
+ {SPR_ACLO, 0, 4, NULL, S_HIDESPECIAL5, 0, 0}, // S_HIDESPECIAL4
+ {SPR_ACLO, 1, 4, NULL, S_HIDESPECIAL6, 0, 0}, // S_HIDESPECIAL5
+ {SPR_ACLO, 2, 4, NULL, S_HIDESPECIAL7, 0, 0}, // S_HIDESPECIAL6
+ {SPR_ACLO, 1, 4, NULL, S_HIDESPECIAL8, 0, 0}, // S_HIDESPECIAL7
+ {SPR_ACLO, 2, 4, NULL, S_HIDESPECIAL9, 0, 0}, // S_HIDESPECIAL8
+ {SPR_ACLO, 3, 4, NULL, S_HIDESPECIAL10, 0, 0}, // S_HIDESPECIAL9
+ {SPR_ACLO, 2, 4, NULL, S_HIDESPECIAL11, 0, 0}, // S_HIDESPECIAL10
+ {SPR_ACLO, 3, 4, A_RestoreSpecialThing2, S_NULL, 0, 0}, // S_HIDESPECIAL11
+ {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI2, 0, 0}, // S_DORMANTARTI1
+ {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI3, 0, 0}, // S_DORMANTARTI2
+ {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI4, 0, 0}, // S_DORMANTARTI3
+ {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI5, 0, 0}, // S_DORMANTARTI4
+ {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI6, 0, 0}, // S_DORMANTARTI5
+ {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI7, 0, 0}, // S_DORMANTARTI6
+ {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI8, 0, 0}, // S_DORMANTARTI7
+ {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI9, 0, 0}, // S_DORMANTARTI8
+ {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI10, 0, 0}, // S_DORMANTARTI9
+ {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI11, 0, 0}, // S_DORMANTARTI10
+ {SPR_ACLO, 0, 1400, A_HideThing, S_DORMANTARTI12, 0, 0}, // S_DORMANTARTI11
+ {SPR_ACLO, 0, 3, A_UnHideThing, S_DORMANTARTI13, 0, 0}, // S_DORMANTARTI12
+ {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI14, 0, 0}, // S_DORMANTARTI13
+ {SPR_ACLO, 0, 3, NULL, S_DORMANTARTI15, 0, 0}, // S_DORMANTARTI14
+ {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI16, 0, 0}, // S_DORMANTARTI15
+ {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI17, 0, 0}, // S_DORMANTARTI16
+ {SPR_ACLO, 1, 3, NULL, S_DORMANTARTI18, 0, 0}, // S_DORMANTARTI17
+ {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI19, 0, 0}, // S_DORMANTARTI18
+ {SPR_ACLO, 3, 3, NULL, S_DORMANTARTI20, 0, 0}, // S_DORMANTARTI19
+ {SPR_ACLO, 2, 3, NULL, S_DORMANTARTI21, 0, 0}, // S_DORMANTARTI20
+ {SPR_ACLO, 3, 3, A_RestoreArtifact, S_NULL, 0, 0}, // S_DORMANTARTI21
+ {SPR_ACLO, 3, 3, NULL, S_DEADARTI2, 0, 0}, // S_DEADARTI1
+ {SPR_ACLO, 2, 3, NULL, S_DEADARTI3, 0, 0}, // S_DEADARTI2
+ {SPR_ACLO, 3, 3, NULL, S_DEADARTI4, 0, 0}, // S_DEADARTI3
+ {SPR_ACLO, 2, 3, NULL, S_DEADARTI5, 0, 0}, // S_DEADARTI4
+ {SPR_ACLO, 1, 3, NULL, S_DEADARTI6, 0, 0}, // S_DEADARTI5
+ {SPR_ACLO, 2, 3, NULL, S_DEADARTI7, 0, 0}, // S_DEADARTI6
+ {SPR_ACLO, 1, 3, NULL, S_DEADARTI8, 0, 0}, // S_DEADARTI7
+ {SPR_ACLO, 0, 3, NULL, S_DEADARTI9, 0, 0}, // S_DEADARTI8
+ {SPR_ACLO, 1, 3, NULL, S_DEADARTI10, 0, 0}, // S_DEADARTI9
+ {SPR_ACLO, 0, 3, NULL, S_NULL, 0, 0}, // S_DEADARTI10
+ {SPR_INVS, 32768, 350, NULL, S_ARTI_INVS1, 0, 0}, // S_ARTI_INVS1
+ {SPR_PTN2, 0, 4, NULL, S_ARTI_PTN2_2, 0, 0}, // S_ARTI_PTN2_1
+ {SPR_PTN2, 1, 4, NULL, S_ARTI_PTN2_3, 0, 0}, // S_ARTI_PTN2_2
+ {SPR_PTN2, 2, 4, NULL, S_ARTI_PTN2_1, 0, 0}, // S_ARTI_PTN2_3
+ {SPR_SOAR, 0, 5, NULL, S_ARTI_SOAR2, 0, 0}, // S_ARTI_SOAR1
+ {SPR_SOAR, 1, 5, NULL, S_ARTI_SOAR3, 0, 0}, // S_ARTI_SOAR2
+ {SPR_SOAR, 2, 5, NULL, S_ARTI_SOAR4, 0, 0}, // S_ARTI_SOAR3
+ {SPR_SOAR, 1, 5, NULL, S_ARTI_SOAR1, 0, 0}, // S_ARTI_SOAR4
+ {SPR_INVU, 0, 3, NULL, S_ARTI_INVU2, 0, 0}, // S_ARTI_INVU1
+ {SPR_INVU, 1, 3, NULL, S_ARTI_INVU3, 0, 0}, // S_ARTI_INVU2
+ {SPR_INVU, 2, 3, NULL, S_ARTI_INVU4, 0, 0}, // S_ARTI_INVU3
+ {SPR_INVU, 3, 3, NULL, S_ARTI_INVU1, 0, 0}, // S_ARTI_INVU4
+ {SPR_PWBK, 0, 350, NULL, S_ARTI_PWBK1, 0, 0}, // S_ARTI_PWBK1
+ {SPR_EGGC, 0, 6, NULL, S_ARTI_EGGC2, 0, 0}, // S_ARTI_EGGC1
+ {SPR_EGGC, 1, 6, NULL, S_ARTI_EGGC3, 0, 0}, // S_ARTI_EGGC2
+ {SPR_EGGC, 2, 6, NULL, S_ARTI_EGGC4, 0, 0}, // S_ARTI_EGGC3
+ {SPR_EGGC, 1, 6, NULL, S_ARTI_EGGC1, 0, 0}, // S_ARTI_EGGC4
+ {SPR_EGGM, 0, 4, NULL, S_EGGFX2, 0, 0}, // S_EGGFX1
+ {SPR_EGGM, 1, 4, NULL, S_EGGFX3, 0, 0}, // S_EGGFX2
+ {SPR_EGGM, 2, 4, NULL, S_EGGFX4, 0, 0}, // S_EGGFX3
+ {SPR_EGGM, 3, 4, NULL, S_EGGFX5, 0, 0}, // S_EGGFX4
+ {SPR_EGGM, 4, 4, NULL, S_EGGFX1, 0, 0}, // S_EGGFX5
+ {SPR_FX01, 32772, 3, NULL, S_EGGFXI1_2, 0, 0}, // S_EGGFXI1_1
+ {SPR_FX01, 32773, 3, NULL, S_EGGFXI1_3, 0, 0}, // S_EGGFXI1_2
+ {SPR_FX01, 32774, 3, NULL, S_EGGFXI1_4, 0, 0}, // S_EGGFXI1_3
+ {SPR_FX01, 32775, 3, NULL, S_NULL, 0, 0}, // S_EGGFXI1_4
+ {SPR_SPHL, 0, 350, NULL, S_ARTI_SPHL1, 0, 0}, // S_ARTI_SPHL1
+ {SPR_TRCH, 32768, 3, NULL, S_ARTI_TRCH2, 0, 0}, // S_ARTI_TRCH1
+ {SPR_TRCH, 32769, 3, NULL, S_ARTI_TRCH3, 0, 0}, // S_ARTI_TRCH2
+ {SPR_TRCH, 32770, 3, NULL, S_ARTI_TRCH1, 0, 0}, // S_ARTI_TRCH3
+ {SPR_FBMB, 4, 350, NULL, S_ARTI_FBMB1, 0, 0}, // S_ARTI_FBMB1
+ {SPR_FBMB, 0, 10, NULL, S_FIREBOMB2, 0, 0}, // S_FIREBOMB1
+ {SPR_FBMB, 1, 10, NULL, S_FIREBOMB3, 0, 0}, // S_FIREBOMB2
+ {SPR_FBMB, 2, 10, NULL, S_FIREBOMB4, 0, 0}, // S_FIREBOMB3
+ {SPR_FBMB, 3, 10, NULL, S_FIREBOMB5, 0, 0}, // S_FIREBOMB4
+ {SPR_FBMB, 4, 6, A_Scream, S_FIREBOMB6, 0, 0}, // S_FIREBOMB5
+ {SPR_XPL1, 32768, 4, A_Explode, S_FIREBOMB7, 0, 0}, // S_FIREBOMB6
+ {SPR_XPL1, 32769, 4, NULL, S_FIREBOMB8, 0, 0}, // S_FIREBOMB7
+ {SPR_XPL1, 32770, 4, NULL, S_FIREBOMB9, 0, 0}, // S_FIREBOMB8
+ {SPR_XPL1, 32771, 4, NULL, S_FIREBOMB10, 0, 0}, // S_FIREBOMB9
+ {SPR_XPL1, 32772, 4, NULL, S_FIREBOMB11, 0, 0}, // S_FIREBOMB10
+ {SPR_XPL1, 32773, 4, NULL, S_NULL, 0, 0}, // S_FIREBOMB11
+ {SPR_ATLP, 0, 4, NULL, S_ARTI_ATLP2, 0, 0}, // S_ARTI_ATLP1
+ {SPR_ATLP, 1, 4, NULL, S_ARTI_ATLP3, 0, 0}, // S_ARTI_ATLP2
+ {SPR_ATLP, 2, 4, NULL, S_ARTI_ATLP4, 0, 0}, // S_ARTI_ATLP3
+ {SPR_ATLP, 1, 4, NULL, S_ARTI_ATLP1, 0, 0}, // S_ARTI_ATLP4
+ {SPR_PPOD, 0, 10, NULL, S_POD_WAIT1, 0, 0}, // S_POD_WAIT1
+ {SPR_PPOD, 1, 14, A_PodPain, S_POD_WAIT1, 0, 0}, // S_POD_PAIN1
+ {SPR_PPOD, 32770, 5, A_RemovePod, S_POD_DIE2, 0, 0}, // S_POD_DIE1
+ {SPR_PPOD, 32771, 5, A_Scream, S_POD_DIE3, 0, 0}, // S_POD_DIE2
+ {SPR_PPOD, 32772, 5, A_Explode, S_POD_DIE4, 0, 0}, // S_POD_DIE3
+ {SPR_PPOD, 32773, 10, NULL, S_FREETARGMOBJ, 0, 0}, // S_POD_DIE4
+ {SPR_PPOD, 8, 3, NULL, S_POD_GROW2, 0, 0}, // S_POD_GROW1
+ {SPR_PPOD, 9, 3, NULL, S_POD_GROW3, 0, 0}, // S_POD_GROW2
+ {SPR_PPOD, 10, 3, NULL, S_POD_GROW4, 0, 0}, // S_POD_GROW3
+ {SPR_PPOD, 11, 3, NULL, S_POD_GROW5, 0, 0}, // S_POD_GROW4
+ {SPR_PPOD, 12, 3, NULL, S_POD_GROW6, 0, 0}, // S_POD_GROW5
+ {SPR_PPOD, 13, 3, NULL, S_POD_GROW7, 0, 0}, // S_POD_GROW6
+ {SPR_PPOD, 14, 3, NULL, S_POD_GROW8, 0, 0}, // S_POD_GROW7
+ {SPR_PPOD, 15, 3, NULL, S_POD_WAIT1, 0, 0}, // S_POD_GROW8
+ {SPR_PPOD, 6, 8, NULL, S_PODGOO2, 0, 0}, // S_PODGOO1
+ {SPR_PPOD, 7, 8, NULL, S_PODGOO1, 0, 0}, // S_PODGOO2
+ {SPR_PPOD, 6, 10, NULL, S_NULL, 0, 0}, // S_PODGOOX
+ {SPR_AMG1, 0, 35, A_MakePod, S_PODGENERATOR, 0, 0}, // S_PODGENERATOR
+ {SPR_SPSH, 0, 8, NULL, S_SPLASH2, 0, 0}, // S_SPLASH1
+ {SPR_SPSH, 1, 8, NULL, S_SPLASH3, 0, 0}, // S_SPLASH2
+ {SPR_SPSH, 2, 8, NULL, S_SPLASH4, 0, 0}, // S_SPLASH3
+ {SPR_SPSH, 3, 16, NULL, S_NULL, 0, 0}, // S_SPLASH4
+ {SPR_SPSH, 3, 10, NULL, S_NULL, 0, 0}, // S_SPLASHX
+ {SPR_SPSH, 4, 5, NULL, S_SPLASHBASE2, 0, 0}, // S_SPLASHBASE1
+ {SPR_SPSH, 5, 5, NULL, S_SPLASHBASE3, 0, 0}, // S_SPLASHBASE2
+ {SPR_SPSH, 6, 5, NULL, S_SPLASHBASE4, 0, 0}, // S_SPLASHBASE3
+ {SPR_SPSH, 7, 5, NULL, S_SPLASHBASE5, 0, 0}, // S_SPLASHBASE4
+ {SPR_SPSH, 8, 5, NULL, S_SPLASHBASE6, 0, 0}, // S_SPLASHBASE5
+ {SPR_SPSH, 9, 5, NULL, S_SPLASHBASE7, 0, 0}, // S_SPLASHBASE6
+ {SPR_SPSH, 10, 5, NULL, S_NULL, 0, 0}, // S_SPLASHBASE7
+ {SPR_LVAS, 32768, 5, NULL, S_LAVASPLASH2, 0, 0}, // S_LAVASPLASH1
+ {SPR_LVAS, 32769, 5, NULL, S_LAVASPLASH3, 0, 0}, // S_LAVASPLASH2
+ {SPR_LVAS, 32770, 5, NULL, S_LAVASPLASH4, 0, 0}, // S_LAVASPLASH3
+ {SPR_LVAS, 32771, 5, NULL, S_LAVASPLASH5, 0, 0}, // S_LAVASPLASH4
+ {SPR_LVAS, 32772, 5, NULL, S_LAVASPLASH6, 0, 0}, // S_LAVASPLASH5
+ {SPR_LVAS, 32773, 5, NULL, S_NULL, 0, 0}, // S_LAVASPLASH6
+ {SPR_LVAS, 32774, 5, NULL, S_LAVASMOKE2, 0, 0}, // S_LAVASMOKE1
+ {SPR_LVAS, 32775, 5, NULL, S_LAVASMOKE3, 0, 0}, // S_LAVASMOKE2
+ {SPR_LVAS, 32776, 5, NULL, S_LAVASMOKE4, 0, 0}, // S_LAVASMOKE3
+ {SPR_LVAS, 32777, 5, NULL, S_LAVASMOKE5, 0, 0}, // S_LAVASMOKE4
+ {SPR_LVAS, 32778, 5, NULL, S_NULL, 0, 0}, // S_LAVASMOKE5
+ {SPR_SLDG, 0, 8, NULL, S_SLUDGECHUNK2, 0, 0}, // S_SLUDGECHUNK1
+ {SPR_SLDG, 1, 8, NULL, S_SLUDGECHUNK3, 0, 0}, // S_SLUDGECHUNK2
+ {SPR_SLDG, 2, 8, NULL, S_SLUDGECHUNK4, 0, 0}, // S_SLUDGECHUNK3
+ {SPR_SLDG, 3, 8, NULL, S_NULL, 0, 0}, // S_SLUDGECHUNK4
+ {SPR_SLDG, 3, 6, NULL, S_NULL, 0, 0}, // S_SLUDGECHUNKX
+ {SPR_SLDG, 4, 5, NULL, S_SLUDGESPLASH2, 0, 0}, // S_SLUDGESPLASH1
+ {SPR_SLDG, 5, 5, NULL, S_SLUDGESPLASH3, 0, 0}, // S_SLUDGESPLASH2
+ {SPR_SLDG, 6, 5, NULL, S_SLUDGESPLASH4, 0, 0}, // S_SLUDGESPLASH3
+ {SPR_SLDG, 7, 5, NULL, S_NULL, 0, 0}, // S_SLUDGESPLASH4
+ {SPR_SKH1, 0, -1, NULL, S_NULL, 0, 0}, // S_SKULLHANG70_1
+ {SPR_SKH2, 0, -1, NULL, S_NULL, 0, 0}, // S_SKULLHANG60_1
+ {SPR_SKH3, 0, -1, NULL, S_NULL, 0, 0}, // S_SKULLHANG45_1
+ {SPR_SKH4, 0, -1, NULL, S_NULL, 0, 0}, // S_SKULLHANG35_1
+ {SPR_CHDL, 0, 4, NULL, S_CHANDELIER2, 0, 0}, // S_CHANDELIER1
+ {SPR_CHDL, 1, 4, NULL, S_CHANDELIER3, 0, 0}, // S_CHANDELIER2
+ {SPR_CHDL, 2, 4, NULL, S_CHANDELIER1, 0, 0}, // S_CHANDELIER3
+ {SPR_SRTC, 0, 4, NULL, S_SERPTORCH2, 0, 0}, // S_SERPTORCH1
+ {SPR_SRTC, 1, 4, NULL, S_SERPTORCH3, 0, 0}, // S_SERPTORCH2
+ {SPR_SRTC, 2, 4, NULL, S_SERPTORCH1, 0, 0}, // S_SERPTORCH3
+ {SPR_SMPL, 0, -1, NULL, S_NULL, 0, 0}, // S_SMALLPILLAR
+ {SPR_STGS, 0, -1, NULL, S_NULL, 0, 0}, // S_STALAGMITESMALL
+ {SPR_STGL, 0, -1, NULL, S_NULL, 0, 0}, // S_STALAGMITELARGE
+ {SPR_STCS, 0, -1, NULL, S_NULL, 0, 0}, // S_STALACTITESMALL
+ {SPR_STCL, 0, -1, NULL, S_NULL, 0, 0}, // S_STALACTITELARGE
+ {SPR_KFR1, 32768, 3, NULL, S_FIREBRAZIER2, 0, 0}, // S_FIREBRAZIER1
+ {SPR_KFR1, 32769, 3, NULL, S_FIREBRAZIER3, 0, 0}, // S_FIREBRAZIER2
+ {SPR_KFR1, 32770, 3, NULL, S_FIREBRAZIER4, 0, 0}, // S_FIREBRAZIER3
+ {SPR_KFR1, 32771, 3, NULL, S_FIREBRAZIER5, 0, 0}, // S_FIREBRAZIER4
+ {SPR_KFR1, 32772, 3, NULL, S_FIREBRAZIER6, 0, 0}, // S_FIREBRAZIER5
+ {SPR_KFR1, 32773, 3, NULL, S_FIREBRAZIER7, 0, 0}, // S_FIREBRAZIER6
+ {SPR_KFR1, 32774, 3, NULL, S_FIREBRAZIER8, 0, 0}, // S_FIREBRAZIER7
+ {SPR_KFR1, 32775, 3, NULL, S_FIREBRAZIER1, 0, 0}, // S_FIREBRAZIER8
+ {SPR_BARL, 0, -1, NULL, S_NULL, 0, 0}, // S_BARREL
+ {SPR_BRPL, 0, -1, NULL, S_NULL, 0, 0}, // S_BRPILLAR
+ {SPR_MOS1, 0, -1, NULL, S_NULL, 0, 0}, // S_MOSS1
+ {SPR_MOS2, 0, -1, NULL, S_NULL, 0, 0}, // S_MOSS2
+ {SPR_WTRH, 32768, 6, NULL, S_WALLTORCH2, 0, 0}, // S_WALLTORCH1
+ {SPR_WTRH, 32769, 6, NULL, S_WALLTORCH3, 0, 0}, // S_WALLTORCH2
+ {SPR_WTRH, 32770, 6, NULL, S_WALLTORCH1, 0, 0}, // S_WALLTORCH3
+ {SPR_HCOR, 0, -1, NULL, S_NULL, 0, 0}, // S_HANGINGCORPSE
+ {SPR_KGZ1, 0, 1, NULL, S_KEYGIZMO2, 0, 0}, // S_KEYGIZMO1
+ {SPR_KGZ1, 0, 1, A_InitKeyGizmo, S_KEYGIZMO3, 0, 0}, // S_KEYGIZMO2
+ {SPR_KGZ1, 0, -1, NULL, S_NULL, 0, 0}, // S_KEYGIZMO3
+ {SPR_KGZB, 0, 1, NULL, S_KGZ_START, 0, 0}, // S_KGZ_START
+ {SPR_KGZB, 32768, -1, NULL, S_NULL, 0, 0}, // S_KGZ_BLUEFLOAT1
+ {SPR_KGZG, 32768, -1, NULL, S_NULL, 0, 0}, // S_KGZ_GREENFLOAT1
+ {SPR_KGZY, 32768, -1, NULL, S_NULL, 0, 0}, // S_KGZ_YELLOWFLOAT1
+ {SPR_VLCO, 0, 350, NULL, S_VOLCANO2, 0, 0}, // S_VOLCANO1
+ {SPR_VLCO, 0, 35, A_VolcanoSet, S_VOLCANO3, 0, 0}, // S_VOLCANO2
+ {SPR_VLCO, 1, 3, NULL, S_VOLCANO4, 0, 0}, // S_VOLCANO3
+ {SPR_VLCO, 2, 3, NULL, S_VOLCANO5, 0, 0}, // S_VOLCANO4
+ {SPR_VLCO, 3, 3, NULL, S_VOLCANO6, 0, 0}, // S_VOLCANO5
+ {SPR_VLCO, 1, 3, NULL, S_VOLCANO7, 0, 0}, // S_VOLCANO6
+ {SPR_VLCO, 2, 3, NULL, S_VOLCANO8, 0, 0}, // S_VOLCANO7
+ {SPR_VLCO, 3, 3, NULL, S_VOLCANO9, 0, 0}, // S_VOLCANO8
+ {SPR_VLCO, 4, 10, A_VolcanoBlast, S_VOLCANO2, 0, 0}, // S_VOLCANO9
+ {SPR_VFBL, 0, 4, A_BeastPuff, S_VOLCANOBALL2, 0, 0}, // S_VOLCANOBALL1
+ {SPR_VFBL, 1, 4, A_BeastPuff, S_VOLCANOBALL1, 0, 0}, // S_VOLCANOBALL2
+ {SPR_XPL1, 0, 4, A_VolcBallImpact, S_VOLCANOBALLX2, 0, 0}, // S_VOLCANOBALLX1
+ {SPR_XPL1, 1, 4, NULL, S_VOLCANOBALLX3, 0, 0}, // S_VOLCANOBALLX2
+ {SPR_XPL1, 2, 4, NULL, S_VOLCANOBALLX4, 0, 0}, // S_VOLCANOBALLX3
+ {SPR_XPL1, 3, 4, NULL, S_VOLCANOBALLX5, 0, 0}, // S_VOLCANOBALLX4
+ {SPR_XPL1, 4, 4, NULL, S_VOLCANOBALLX6, 0, 0}, // S_VOLCANOBALLX5
+ {SPR_XPL1, 5, 4, NULL, S_NULL, 0, 0}, // S_VOLCANOBALLX6
+ {SPR_VTFB, 0, 4, NULL, S_VOLCANOTBALL2, 0, 0}, // S_VOLCANOTBALL1
+ {SPR_VTFB, 1, 4, NULL, S_VOLCANOTBALL1, 0, 0}, // S_VOLCANOTBALL2
+ {SPR_SFFI, 2, 4, NULL, S_VOLCANOTBALLX2, 0, 0}, // S_VOLCANOTBALLX1
+ {SPR_SFFI, 1, 4, NULL, S_VOLCANOTBALLX3, 0, 0}, // S_VOLCANOTBALLX2
+ {SPR_SFFI, 0, 4, NULL, S_VOLCANOTBALLX4, 0, 0}, // S_VOLCANOTBALLX3
+ {SPR_SFFI, 1, 4, NULL, S_VOLCANOTBALLX5, 0, 0}, // S_VOLCANOTBALLX4
+ {SPR_SFFI, 2, 4, NULL, S_VOLCANOTBALLX6, 0, 0}, // S_VOLCANOTBALLX5
+ {SPR_SFFI, 3, 4, NULL, S_VOLCANOTBALLX7, 0, 0}, // S_VOLCANOTBALLX6
+ {SPR_SFFI, 4, 4, NULL, S_NULL, 0, 0}, // S_VOLCANOTBALLX7
+ {SPR_TGLT, 0, 8, A_SpawnTeleGlitter, S_TELEGLITGEN1, 0, 0}, // S_TELEGLITGEN1
+ {SPR_TGLT, 5, 8, A_SpawnTeleGlitter2, S_TELEGLITGEN2, 0, 0}, // S_TELEGLITGEN2
+ {SPR_TGLT, 32768, 2, NULL, S_TELEGLITTER1_2, 0, 0}, // S_TELEGLITTER1_1
+ {SPR_TGLT, 32769, 2, A_AccTeleGlitter, S_TELEGLITTER1_3, 0, 0}, // S_TELEGLITTER1_2
+ {SPR_TGLT, 32770, 2, NULL, S_TELEGLITTER1_4, 0, 0}, // S_TELEGLITTER1_3
+ {SPR_TGLT, 32771, 2, A_AccTeleGlitter, S_TELEGLITTER1_5, 0, 0}, // S_TELEGLITTER1_4
+ {SPR_TGLT, 32772, 2, NULL, S_TELEGLITTER1_1, 0, 0}, // S_TELEGLITTER1_5
+ {SPR_TGLT, 32773, 2, NULL, S_TELEGLITTER2_2, 0, 0}, // S_TELEGLITTER2_1
+ {SPR_TGLT, 32774, 2, A_AccTeleGlitter, S_TELEGLITTER2_3, 0, 0}, // S_TELEGLITTER2_2
+ {SPR_TGLT, 32775, 2, NULL, S_TELEGLITTER2_4, 0, 0}, // S_TELEGLITTER2_3
+ {SPR_TGLT, 32776, 2, A_AccTeleGlitter, S_TELEGLITTER2_5, 0, 0}, // S_TELEGLITTER2_4
+ {SPR_TGLT, 32777, 2, NULL, S_TELEGLITTER2_1, 0, 0}, // S_TELEGLITTER2_5
+ {SPR_TELE, 32768, 6, NULL, S_TFOG2, 0, 0}, // S_TFOG1
+ {SPR_TELE, 32769, 6, NULL, S_TFOG3, 0, 0}, // S_TFOG2
+ {SPR_TELE, 32770, 6, NULL, S_TFOG4, 0, 0}, // S_TFOG3
+ {SPR_TELE, 32771, 6, NULL, S_TFOG5, 0, 0}, // S_TFOG4
+ {SPR_TELE, 32772, 6, NULL, S_TFOG6, 0, 0}, // S_TFOG5
+ {SPR_TELE, 32773, 6, NULL, S_TFOG7, 0, 0}, // S_TFOG6
+ {SPR_TELE, 32774, 6, NULL, S_TFOG8, 0, 0}, // S_TFOG7
+ {SPR_TELE, 32775, 6, NULL, S_TFOG9, 0, 0}, // S_TFOG8
+ {SPR_TELE, 32774, 6, NULL, S_TFOG10, 0, 0}, // S_TFOG9
+ {SPR_TELE, 32773, 6, NULL, S_TFOG11, 0, 0}, // S_TFOG10
+ {SPR_TELE, 32772, 6, NULL, S_TFOG12, 0, 0}, // S_TFOG11
+ {SPR_TELE, 32771, 6, NULL, S_TFOG13, 0, 0}, // S_TFOG12
+ {SPR_TELE, 32770, 6, NULL, S_NULL, 0, 0}, // S_TFOG13
+ {SPR_STFF, 0, 0, A_Light0, S_NULL, 0, 0}, // S_LIGHTDONE
+ {SPR_STFF, 0, 1, A_WeaponReady, S_STAFFREADY, 0, 0}, // S_STAFFREADY
+ {SPR_STFF, 0, 1, A_Lower, S_STAFFDOWN, 0, 0}, // S_STAFFDOWN
+ {SPR_STFF, 0, 1, A_Raise, S_STAFFUP, 0, 0}, // S_STAFFUP
+ {SPR_STFF, 3, 4, A_WeaponReady, S_STAFFREADY2_2, 0, 0}, // S_STAFFREADY2_1
+ {SPR_STFF, 4, 4, A_WeaponReady, S_STAFFREADY2_3, 0, 0}, // S_STAFFREADY2_2
+ {SPR_STFF, 5, 4, A_WeaponReady, S_STAFFREADY2_1, 0, 0}, // S_STAFFREADY2_3
+ {SPR_STFF, 3, 1, A_Lower, S_STAFFDOWN2, 0, 0}, // S_STAFFDOWN2
+ {SPR_STFF, 3, 1, A_Raise, S_STAFFUP2, 0, 0}, // S_STAFFUP2
+ {SPR_STFF, 1, 6, NULL, S_STAFFATK1_2, 0, 0}, // S_STAFFATK1_1
+ {SPR_STFF, 2, 8, A_StaffAttackPL1, S_STAFFATK1_3, 0, 0}, // S_STAFFATK1_2
+ {SPR_STFF, 1, 8, A_ReFire, S_STAFFREADY, 0, 0}, // S_STAFFATK1_3
+ {SPR_STFF, 6, 6, NULL, S_STAFFATK2_2, 0, 0}, // S_STAFFATK2_1
+ {SPR_STFF, 7, 8, A_StaffAttackPL2, S_STAFFATK2_3, 0, 0}, // S_STAFFATK2_2
+ {SPR_STFF, 6, 8, A_ReFire, S_STAFFREADY2_1, 0, 0}, // S_STAFFATK2_3
+ {SPR_PUF3, 32768, 4, NULL, S_STAFFPUFF2, 0, 0}, // S_STAFFPUFF1
+ {SPR_PUF3, 1, 4, NULL, S_STAFFPUFF3, 0, 0}, // S_STAFFPUFF2
+ {SPR_PUF3, 2, 4, NULL, S_STAFFPUFF4, 0, 0}, // S_STAFFPUFF3
+ {SPR_PUF3, 3, 4, NULL, S_NULL, 0, 0}, // S_STAFFPUFF4
+ {SPR_PUF4, 32768, 4, NULL, S_STAFFPUFF2_2, 0, 0}, // S_STAFFPUFF2_1
+ {SPR_PUF4, 32769, 4, NULL, S_STAFFPUFF2_3, 0, 0}, // S_STAFFPUFF2_2
+ {SPR_PUF4, 32770, 4, NULL, S_STAFFPUFF2_4, 0, 0}, // S_STAFFPUFF2_3
+ {SPR_PUF4, 32771, 4, NULL, S_STAFFPUFF2_5, 0, 0}, // S_STAFFPUFF2_4
+ {SPR_PUF4, 32772, 4, NULL, S_STAFFPUFF2_6, 0, 0}, // S_STAFFPUFF2_5
+ {SPR_PUF4, 32773, 4, NULL, S_NULL, 0, 0}, // S_STAFFPUFF2_6
+ {SPR_BEAK, 0, 1, A_BeakReady, S_BEAKREADY, 0, 0}, // S_BEAKREADY
+ {SPR_BEAK, 0, 1, A_Lower, S_BEAKDOWN, 0, 0}, // S_BEAKDOWN
+ {SPR_BEAK, 0, 1, A_BeakRaise, S_BEAKUP, 0, 0}, // S_BEAKUP
+ {SPR_BEAK, 0, 18, A_BeakAttackPL1, S_BEAKREADY, 0, 0}, // S_BEAKATK1_1
+ {SPR_BEAK, 0, 12, A_BeakAttackPL2, S_BEAKREADY, 0, 0}, // S_BEAKATK2_1
+ {SPR_WGNT, 0, -1, NULL, S_NULL, 0, 0}, // S_WGNT
+ {SPR_GAUN, 0, 1, A_WeaponReady, S_GAUNTLETREADY, 0, 0}, // S_GAUNTLETREADY
+ {SPR_GAUN, 0, 1, A_Lower, S_GAUNTLETDOWN, 0, 0}, // S_GAUNTLETDOWN
+ {SPR_GAUN, 0, 1, A_Raise, S_GAUNTLETUP, 0, 0}, // S_GAUNTLETUP
+ {SPR_GAUN, 6, 4, A_WeaponReady, S_GAUNTLETREADY2_2, 0, 0}, // S_GAUNTLETREADY2_1
+ {SPR_GAUN, 7, 4, A_WeaponReady, S_GAUNTLETREADY2_3, 0, 0}, // S_GAUNTLETREADY2_2
+ {SPR_GAUN, 8, 4, A_WeaponReady, S_GAUNTLETREADY2_1, 0, 0}, // S_GAUNTLETREADY2_3
+ {SPR_GAUN, 6, 1, A_Lower, S_GAUNTLETDOWN2, 0, 0}, // S_GAUNTLETDOWN2
+ {SPR_GAUN, 6, 1, A_Raise, S_GAUNTLETUP2, 0, 0}, // S_GAUNTLETUP2
+ {SPR_GAUN, 1, 4, NULL, S_GAUNTLETATK1_2, 0, 0}, // S_GAUNTLETATK1_1
+ {SPR_GAUN, 2, 4, NULL, S_GAUNTLETATK1_3, 0, 0}, // S_GAUNTLETATK1_2
+ {SPR_GAUN, 32771, 4, A_GauntletAttack, S_GAUNTLETATK1_4, 0, 0}, // S_GAUNTLETATK1_3
+ {SPR_GAUN, 32772, 4, A_GauntletAttack, S_GAUNTLETATK1_5, 0, 0}, // S_GAUNTLETATK1_4
+ {SPR_GAUN, 32773, 4, A_GauntletAttack, S_GAUNTLETATK1_6, 0, 0}, // S_GAUNTLETATK1_5
+ {SPR_GAUN, 2, 4, A_ReFire, S_GAUNTLETATK1_7, 0, 0}, // S_GAUNTLETATK1_6
+ {SPR_GAUN, 1, 4, A_Light0, S_GAUNTLETREADY, 0, 0}, // S_GAUNTLETATK1_7
+ {SPR_GAUN, 9, 4, NULL, S_GAUNTLETATK2_2, 0, 0}, // S_GAUNTLETATK2_1
+ {SPR_GAUN, 10, 4, NULL, S_GAUNTLETATK2_3, 0, 0}, // S_GAUNTLETATK2_2
+ {SPR_GAUN, 32779, 4, A_GauntletAttack, S_GAUNTLETATK2_4, 0, 0}, // S_GAUNTLETATK2_3
+ {SPR_GAUN, 32780, 4, A_GauntletAttack, S_GAUNTLETATK2_5, 0, 0}, // S_GAUNTLETATK2_4
+ {SPR_GAUN, 32781, 4, A_GauntletAttack, S_GAUNTLETATK2_6, 0, 0}, // S_GAUNTLETATK2_5
+ {SPR_GAUN, 10, 4, A_ReFire, S_GAUNTLETATK2_7, 0, 0}, // S_GAUNTLETATK2_6
+ {SPR_GAUN, 9, 4, A_Light0, S_GAUNTLETREADY2_1, 0, 0}, // S_GAUNTLETATK2_7
+ {SPR_PUF1, 32768, 4, NULL, S_GAUNTLETPUFF1_2, 0, 0}, // S_GAUNTLETPUFF1_1
+ {SPR_PUF1, 32769, 4, NULL, S_GAUNTLETPUFF1_3, 0, 0}, // S_GAUNTLETPUFF1_2
+ {SPR_PUF1, 32770, 4, NULL, S_GAUNTLETPUFF1_4, 0, 0}, // S_GAUNTLETPUFF1_3
+ {SPR_PUF1, 32771, 4, NULL, S_NULL, 0, 0}, // S_GAUNTLETPUFF1_4
+ {SPR_PUF1, 32772, 4, NULL, S_GAUNTLETPUFF2_2, 0, 0}, // S_GAUNTLETPUFF2_1
+ {SPR_PUF1, 32773, 4, NULL, S_GAUNTLETPUFF2_3, 0, 0}, // S_GAUNTLETPUFF2_2
+ {SPR_PUF1, 32774, 4, NULL, S_GAUNTLETPUFF2_4, 0, 0}, // S_GAUNTLETPUFF2_3
+ {SPR_PUF1, 32775, 4, NULL, S_NULL, 0, 0}, // S_GAUNTLETPUFF2_4
+ {SPR_WBLS, 0, -1, NULL, S_NULL, 0, 0}, // S_BLSR
+ {SPR_BLSR, 0, 1, A_WeaponReady, S_BLASTERREADY, 0, 0}, // S_BLASTERREADY
+ {SPR_BLSR, 0, 1, A_Lower, S_BLASTERDOWN, 0, 0}, // S_BLASTERDOWN
+ {SPR_BLSR, 0, 1, A_Raise, S_BLASTERUP, 0, 0}, // S_BLASTERUP
+ {SPR_BLSR, 1, 3, NULL, S_BLASTERATK1_2, 0, 0}, // S_BLASTERATK1_1
+ {SPR_BLSR, 2, 3, NULL, S_BLASTERATK1_3, 0, 0}, // S_BLASTERATK1_2
+ {SPR_BLSR, 3, 2, A_FireBlasterPL1, S_BLASTERATK1_4, 0, 0}, // S_BLASTERATK1_3
+ {SPR_BLSR, 2, 2, NULL, S_BLASTERATK1_5, 0, 0}, // S_BLASTERATK1_4
+ {SPR_BLSR, 1, 2, NULL, S_BLASTERATK1_6, 0, 0}, // S_BLASTERATK1_5
+ {SPR_BLSR, 0, 0, A_ReFire, S_BLASTERREADY, 0, 0}, // S_BLASTERATK1_6
+ {SPR_BLSR, 1, 0, NULL, S_BLASTERATK2_2, 0, 0}, // S_BLASTERATK2_1
+ {SPR_BLSR, 2, 0, NULL, S_BLASTERATK2_3, 0, 0}, // S_BLASTERATK2_2
+ {SPR_BLSR, 3, 3, A_FireBlasterPL2, S_BLASTERATK2_4, 0, 0}, // S_BLASTERATK2_3
+ {SPR_BLSR, 2, 4, NULL, S_BLASTERATK2_5, 0, 0}, // S_BLASTERATK2_4
+ {SPR_BLSR, 1, 4, NULL, S_BLASTERATK2_6, 0, 0}, // S_BLASTERATK2_5
+ {SPR_BLSR, 0, 0, A_ReFire, S_BLASTERREADY, 0, 0}, // S_BLASTERATK2_6
+ {SPR_ACLO, 4, 200, NULL, S_BLASTERFX1_1, 0, 0}, // S_BLASTERFX1_1
+ {SPR_FX18, 32768, 3, A_SpawnRippers, S_BLASTERFXI1_2, 0, 0}, // S_BLASTERFXI1_1
+ {SPR_FX18, 32769, 3, NULL, S_BLASTERFXI1_3, 0, 0}, // S_BLASTERFXI1_2
+ {SPR_FX18, 32770, 4, NULL, S_BLASTERFXI1_4, 0, 0}, // S_BLASTERFXI1_3
+ {SPR_FX18, 32771, 4, NULL, S_BLASTERFXI1_5, 0, 0}, // S_BLASTERFXI1_4
+ {SPR_FX18, 32772, 4, NULL, S_BLASTERFXI1_6, 0, 0}, // S_BLASTERFXI1_5
+ {SPR_FX18, 32773, 4, NULL, S_BLASTERFXI1_7, 0, 0}, // S_BLASTERFXI1_6
+ {SPR_FX18, 32774, 4, NULL, S_NULL, 0, 0}, // S_BLASTERFXI1_7
+ {SPR_FX18, 7, 4, NULL, S_BLASTERSMOKE2, 0, 0}, // S_BLASTERSMOKE1
+ {SPR_FX18, 8, 4, NULL, S_BLASTERSMOKE3, 0, 0}, // S_BLASTERSMOKE2
+ {SPR_FX18, 9, 4, NULL, S_BLASTERSMOKE4, 0, 0}, // S_BLASTERSMOKE3
+ {SPR_FX18, 10, 4, NULL, S_BLASTERSMOKE5, 0, 0}, // S_BLASTERSMOKE4
+ {SPR_FX18, 11, 4, NULL, S_NULL, 0, 0}, // S_BLASTERSMOKE5
+ {SPR_FX18, 12, 4, NULL, S_RIPPER2, 0, 0}, // S_RIPPER1
+ {SPR_FX18, 13, 5, NULL, S_RIPPER1, 0, 0}, // S_RIPPER2
+ {SPR_FX18, 32782, 4, NULL, S_RIPPERX2, 0, 0}, // S_RIPPERX1
+ {SPR_FX18, 32783, 4, NULL, S_RIPPERX3, 0, 0}, // S_RIPPERX2
+ {SPR_FX18, 32784, 4, NULL, S_RIPPERX4, 0, 0}, // S_RIPPERX3
+ {SPR_FX18, 32785, 4, NULL, S_RIPPERX5, 0, 0}, // S_RIPPERX4
+ {SPR_FX18, 32786, 4, NULL, S_NULL, 0, 0}, // S_RIPPERX5
+ {SPR_FX17, 32768, 4, NULL, S_BLASTERPUFF1_2, 0, 0}, // S_BLASTERPUFF1_1
+ {SPR_FX17, 32769, 4, NULL, S_BLASTERPUFF1_3, 0, 0}, // S_BLASTERPUFF1_2
+ {SPR_FX17, 32770, 4, NULL, S_BLASTERPUFF1_4, 0, 0}, // S_BLASTERPUFF1_3
+ {SPR_FX17, 32771, 4, NULL, S_BLASTERPUFF1_5, 0, 0}, // S_BLASTERPUFF1_4
+ {SPR_FX17, 32772, 4, NULL, S_NULL, 0, 0}, // S_BLASTERPUFF1_5
+ {SPR_FX17, 32773, 3, NULL, S_BLASTERPUFF2_2, 0, 0}, // S_BLASTERPUFF2_1
+ {SPR_FX17, 32774, 3, NULL, S_BLASTERPUFF2_3, 0, 0}, // S_BLASTERPUFF2_2
+ {SPR_FX17, 32775, 4, NULL, S_BLASTERPUFF2_4, 0, 0}, // S_BLASTERPUFF2_3
+ {SPR_FX17, 32776, 4, NULL, S_BLASTERPUFF2_5, 0, 0}, // S_BLASTERPUFF2_4
+ {SPR_FX17, 32777, 4, NULL, S_BLASTERPUFF2_6, 0, 0}, // S_BLASTERPUFF2_5
+ {SPR_FX17, 32778, 4, NULL, S_BLASTERPUFF2_7, 0, 0}, // S_BLASTERPUFF2_6
+ {SPR_FX17, 32779, 4, NULL, S_NULL, 0, 0}, // S_BLASTERPUFF2_7
+ {SPR_WMCE, 0, -1, NULL, S_NULL, 0, 0}, // S_WMCE
+ {SPR_MACE, 0, 1, A_WeaponReady, S_MACEREADY, 0, 0}, // S_MACEREADY
+ {SPR_MACE, 0, 1, A_Lower, S_MACEDOWN, 0, 0}, // S_MACEDOWN
+ {SPR_MACE, 0, 1, A_Raise, S_MACEUP, 0, 0}, // S_MACEUP
+ {SPR_MACE, 1, 4, NULL, S_MACEATK1_2, 0, 0}, // S_MACEATK1_1
+ {SPR_MACE, 2, 3, A_FireMacePL1, S_MACEATK1_3, 0, 0}, // S_MACEATK1_2
+ {SPR_MACE, 3, 3, A_FireMacePL1, S_MACEATK1_4, 0, 0}, // S_MACEATK1_3
+ {SPR_MACE, 4, 3, A_FireMacePL1, S_MACEATK1_5, 0, 0}, // S_MACEATK1_4
+ {SPR_MACE, 5, 3, A_FireMacePL1, S_MACEATK1_6, 0, 0}, // S_MACEATK1_5
+ {SPR_MACE, 2, 4, A_ReFire, S_MACEATK1_7, 0, 0}, // S_MACEATK1_6
+ {SPR_MACE, 3, 4, NULL, S_MACEATK1_8, 0, 0}, // S_MACEATK1_7
+ {SPR_MACE, 4, 4, NULL, S_MACEATK1_9, 0, 0}, // S_MACEATK1_8
+ {SPR_MACE, 5, 4, NULL, S_MACEATK1_10, 0, 0}, // S_MACEATK1_9
+ {SPR_MACE, 1, 4, NULL, S_MACEREADY, 0, 0}, // S_MACEATK1_10
+ {SPR_MACE, 1, 4, NULL, S_MACEATK2_2, 0, 0}, // S_MACEATK2_1
+ {SPR_MACE, 3, 4, A_FireMacePL2, S_MACEATK2_3, 0, 0}, // S_MACEATK2_2
+ {SPR_MACE, 1, 4, NULL, S_MACEATK2_4, 0, 0}, // S_MACEATK2_3
+ {SPR_MACE, 0, 8, A_ReFire, S_MACEREADY, 0, 0}, // S_MACEATK2_4
+ {SPR_FX02, 0, 4, A_MacePL1Check, S_MACEFX1_2, 0, 0}, // S_MACEFX1_1
+ {SPR_FX02, 1, 4, A_MacePL1Check, S_MACEFX1_1, 0, 0}, // S_MACEFX1_2
+ {SPR_FX02, 32773, 4, A_MaceBallImpact, S_MACEFXI1_2, 0, 0}, // S_MACEFXI1_1
+ {SPR_FX02, 32774, 4, NULL, S_MACEFXI1_3, 0, 0}, // S_MACEFXI1_2
+ {SPR_FX02, 32775, 4, NULL, S_MACEFXI1_4, 0, 0}, // S_MACEFXI1_3
+ {SPR_FX02, 32776, 4, NULL, S_MACEFXI1_5, 0, 0}, // S_MACEFXI1_4
+ {SPR_FX02, 32777, 4, NULL, S_NULL, 0, 0}, // S_MACEFXI1_5
+ {SPR_FX02, 2, 4, NULL, S_MACEFX2_2, 0, 0}, // S_MACEFX2_1
+ {SPR_FX02, 3, 4, NULL, S_MACEFX2_1, 0, 0}, // S_MACEFX2_2
+ {SPR_FX02, 32773, 4, A_MaceBallImpact2, S_MACEFXI1_2, 0, 0}, // S_MACEFXI2_1
+ {SPR_FX02, 0, 4, NULL, S_MACEFX3_2, 0, 0}, // S_MACEFX3_1
+ {SPR_FX02, 1, 4, NULL, S_MACEFX3_1, 0, 0}, // S_MACEFX3_2
+ {SPR_FX02, 4, 99, NULL, S_MACEFX4_1, 0, 0}, // S_MACEFX4_1
+ {SPR_FX02, 32770, 4, A_DeathBallImpact, S_MACEFXI1_2, 0, 0}, // S_MACEFXI4_1
+ {SPR_WSKL, 0, -1, NULL, S_NULL, 0, 0}, // S_WSKL
+ {SPR_HROD, 0, 1, A_WeaponReady, S_HORNRODREADY, 0, 0}, // S_HORNRODREADY
+ {SPR_HROD, 0, 1, A_Lower, S_HORNRODDOWN, 0, 0}, // S_HORNRODDOWN
+ {SPR_HROD, 0, 1, A_Raise, S_HORNRODUP, 0, 0}, // S_HORNRODUP
+ {SPR_HROD, 0, 4, A_FireSkullRodPL1, S_HORNRODATK1_2, 0, 0}, // S_HORNRODATK1_1
+ {SPR_HROD, 1, 4, A_FireSkullRodPL1, S_HORNRODATK1_3, 0, 0}, // S_HORNRODATK1_2
+ {SPR_HROD, 1, 0, A_ReFire, S_HORNRODREADY, 0, 0}, // S_HORNRODATK1_3
+ {SPR_HROD, 2, 2, NULL, S_HORNRODATK2_2, 0, 0}, // S_HORNRODATK2_1
+ {SPR_HROD, 3, 3, NULL, S_HORNRODATK2_3, 0, 0}, // S_HORNRODATK2_2
+ {SPR_HROD, 4, 2, NULL, S_HORNRODATK2_4, 0, 0}, // S_HORNRODATK2_3
+ {SPR_HROD, 5, 3, NULL, S_HORNRODATK2_5, 0, 0}, // S_HORNRODATK2_4
+ {SPR_HROD, 6, 4, A_FireSkullRodPL2, S_HORNRODATK2_6, 0, 0}, // S_HORNRODATK2_5
+ {SPR_HROD, 5, 2, NULL, S_HORNRODATK2_7, 0, 0}, // S_HORNRODATK2_6
+ {SPR_HROD, 4, 3, NULL, S_HORNRODATK2_8, 0, 0}, // S_HORNRODATK2_7
+ {SPR_HROD, 3, 2, NULL, S_HORNRODATK2_9, 0, 0}, // S_HORNRODATK2_8
+ {SPR_HROD, 2, 2, A_ReFire, S_HORNRODREADY, 0, 0}, // S_HORNRODATK2_9
+ {SPR_FX00, 32768, 6, NULL, S_HRODFX1_2, 0, 0}, // S_HRODFX1_1
+ {SPR_FX00, 32769, 6, NULL, S_HRODFX1_1, 0, 0}, // S_HRODFX1_2
+ {SPR_FX00, 32775, 5, NULL, S_HRODFXI1_2, 0, 0}, // S_HRODFXI1_1
+ {SPR_FX00, 32776, 5, NULL, S_HRODFXI1_3, 0, 0}, // S_HRODFXI1_2
+ {SPR_FX00, 32777, 4, NULL, S_HRODFXI1_4, 0, 0}, // S_HRODFXI1_3
+ {SPR_FX00, 32778, 4, NULL, S_HRODFXI1_5, 0, 0}, // S_HRODFXI1_4
+ {SPR_FX00, 32779, 3, NULL, S_HRODFXI1_6, 0, 0}, // S_HRODFXI1_5
+ {SPR_FX00, 32780, 3, NULL, S_NULL, 0, 0}, // S_HRODFXI1_6
+ {SPR_FX00, 32770, 3, NULL, S_HRODFX2_2, 0, 0}, // S_HRODFX2_1
+ {SPR_FX00, 32771, 3, A_SkullRodPL2Seek, S_HRODFX2_3, 0, 0}, // S_HRODFX2_2
+ {SPR_FX00, 32772, 3, NULL, S_HRODFX2_4, 0, 0}, // S_HRODFX2_3
+ {SPR_FX00, 32773, 3, A_SkullRodPL2Seek, S_HRODFX2_1, 0, 0}, // S_HRODFX2_4
+ {SPR_FX00, 32775, 5, A_AddPlayerRain, S_HRODFXI2_2, 0, 0}, // S_HRODFXI2_1
+ {SPR_FX00, 32776, 5, NULL, S_HRODFXI2_3, 0, 0}, // S_HRODFXI2_2
+ {SPR_FX00, 32777, 4, NULL, S_HRODFXI2_4, 0, 0}, // S_HRODFXI2_3
+ {SPR_FX00, 32778, 3, NULL, S_HRODFXI2_5, 0, 0}, // S_HRODFXI2_4
+ {SPR_FX00, 32779, 3, NULL, S_HRODFXI2_6, 0, 0}, // S_HRODFXI2_5
+ {SPR_FX00, 32780, 3, NULL, S_HRODFXI2_7, 0, 0}, // S_HRODFXI2_6
+ {SPR_FX00, 6, 1, A_HideInCeiling, S_HRODFXI2_8, 0, 0}, // S_HRODFXI2_7
+ {SPR_FX00, 6, 1, A_SkullRodStorm, S_HRODFXI2_8, 0, 0}, // S_HRODFXI2_8
+ {SPR_FX20, 32768, -1, NULL, S_NULL, 0, 0}, // S_RAINPLR1_1
+ {SPR_FX21, 32768, -1, NULL, S_NULL, 0, 0}, // S_RAINPLR2_1
+ {SPR_FX22, 32768, -1, NULL, S_NULL, 0, 0}, // S_RAINPLR3_1
+ {SPR_FX23, 32768, -1, NULL, S_NULL, 0, 0}, // S_RAINPLR4_1
+ {SPR_FX20, 32769, 4, A_RainImpact, S_RAINPLR1X_2, 0, 0}, // S_RAINPLR1X_1
+ {SPR_FX20, 32770, 4, NULL, S_RAINPLR1X_3, 0, 0}, // S_RAINPLR1X_2
+ {SPR_FX20, 32771, 4, NULL, S_RAINPLR1X_4, 0, 0}, // S_RAINPLR1X_3
+ {SPR_FX20, 32772, 4, NULL, S_RAINPLR1X_5, 0, 0}, // S_RAINPLR1X_4
+ {SPR_FX20, 32773, 4, NULL, S_NULL, 0, 0}, // S_RAINPLR1X_5
+ {SPR_FX21, 32769, 4, A_RainImpact, S_RAINPLR2X_2, 0, 0}, // S_RAINPLR2X_1
+ {SPR_FX21, 32770, 4, NULL, S_RAINPLR2X_3, 0, 0}, // S_RAINPLR2X_2
+ {SPR_FX21, 32771, 4, NULL, S_RAINPLR2X_4, 0, 0}, // S_RAINPLR2X_3
+ {SPR_FX21, 32772, 4, NULL, S_RAINPLR2X_5, 0, 0}, // S_RAINPLR2X_4
+ {SPR_FX21, 32773, 4, NULL, S_NULL, 0, 0}, // S_RAINPLR2X_5
+ {SPR_FX22, 32769, 4, A_RainImpact, S_RAINPLR3X_2, 0, 0}, // S_RAINPLR3X_1
+ {SPR_FX22, 32770, 4, NULL, S_RAINPLR3X_3, 0, 0}, // S_RAINPLR3X_2
+ {SPR_FX22, 32771, 4, NULL, S_RAINPLR3X_4, 0, 0}, // S_RAINPLR3X_3
+ {SPR_FX22, 32772, 4, NULL, S_RAINPLR3X_5, 0, 0}, // S_RAINPLR3X_4
+ {SPR_FX22, 32773, 4, NULL, S_NULL, 0, 0}, // S_RAINPLR3X_5
+ {SPR_FX23, 32769, 4, A_RainImpact, S_RAINPLR4X_2, 0, 0}, // S_RAINPLR4X_1
+ {SPR_FX23, 32770, 4, NULL, S_RAINPLR4X_3, 0, 0}, // S_RAINPLR4X_2
+ {SPR_FX23, 32771, 4, NULL, S_RAINPLR4X_4, 0, 0}, // S_RAINPLR4X_3
+ {SPR_FX23, 32772, 4, NULL, S_RAINPLR4X_5, 0, 0}, // S_RAINPLR4X_4
+ {SPR_FX23, 32773, 4, NULL, S_NULL, 0, 0}, // S_RAINPLR4X_5
+ {SPR_FX20, 32774, 4, NULL, S_RAINAIRXPLR1_2, 0, 0}, // S_RAINAIRXPLR1_1
+ {SPR_FX21, 32774, 4, NULL, S_RAINAIRXPLR2_2, 0, 0}, // S_RAINAIRXPLR2_1
+ {SPR_FX22, 32774, 4, NULL, S_RAINAIRXPLR3_2, 0, 0}, // S_RAINAIRXPLR3_1
+ {SPR_FX23, 32774, 4, NULL, S_RAINAIRXPLR4_2, 0, 0}, // S_RAINAIRXPLR4_1
+ {SPR_FX20, 32775, 4, NULL, S_RAINAIRXPLR1_3, 0, 0}, // S_RAINAIRXPLR1_2
+ {SPR_FX21, 32775, 4, NULL, S_RAINAIRXPLR2_3, 0, 0}, // S_RAINAIRXPLR2_2
+ {SPR_FX22, 32775, 4, NULL, S_RAINAIRXPLR3_3, 0, 0}, // S_RAINAIRXPLR3_2
+ {SPR_FX23, 32775, 4, NULL, S_RAINAIRXPLR4_3, 0, 0}, // S_RAINAIRXPLR4_2
+ {SPR_FX20, 32776, 4, NULL, S_NULL, 0, 0}, // S_RAINAIRXPLR1_3
+ {SPR_FX21, 32776, 4, NULL, S_NULL, 0, 0}, // S_RAINAIRXPLR2_3
+ {SPR_FX22, 32776, 4, NULL, S_NULL, 0, 0}, // S_RAINAIRXPLR3_3
+ {SPR_FX23, 32776, 4, NULL, S_NULL, 0, 0}, // S_RAINAIRXPLR4_3
+ {SPR_GWND, 0, 1, A_WeaponReady, S_GOLDWANDREADY, 0, 0}, // S_GOLDWANDREADY
+ {SPR_GWND, 0, 1, A_Lower, S_GOLDWANDDOWN, 0, 0}, // S_GOLDWANDDOWN
+ {SPR_GWND, 0, 1, A_Raise, S_GOLDWANDUP, 0, 0}, // S_GOLDWANDUP
+ {SPR_GWND, 1, 3, NULL, S_GOLDWANDATK1_2, 0, 0}, // S_GOLDWANDATK1_1
+ {SPR_GWND, 2, 5, A_FireGoldWandPL1, S_GOLDWANDATK1_3, 0, 0}, // S_GOLDWANDATK1_2
+ {SPR_GWND, 3, 3, NULL, S_GOLDWANDATK1_4, 0, 0}, // S_GOLDWANDATK1_3
+ {SPR_GWND, 3, 0, A_ReFire, S_GOLDWANDREADY, 0, 0}, // S_GOLDWANDATK1_4
+ {SPR_GWND, 1, 3, NULL, S_GOLDWANDATK2_2, 0, 0}, // S_GOLDWANDATK2_1
+ {SPR_GWND, 2, 4, A_FireGoldWandPL2, S_GOLDWANDATK2_3, 0, 0}, // S_GOLDWANDATK2_2
+ {SPR_GWND, 3, 3, NULL, S_GOLDWANDATK2_4, 0, 0}, // S_GOLDWANDATK2_3
+ {SPR_GWND, 3, 0, A_ReFire, S_GOLDWANDREADY, 0, 0}, // S_GOLDWANDATK2_4
+ {SPR_FX01, 32768, 6, NULL, S_GWANDFX1_2, 0, 0}, // S_GWANDFX1_1
+ {SPR_FX01, 32769, 6, NULL, S_GWANDFX1_1, 0, 0}, // S_GWANDFX1_2
+ {SPR_FX01, 32772, 3, NULL, S_GWANDFXI1_2, 0, 0}, // S_GWANDFXI1_1
+ {SPR_FX01, 32773, 3, NULL, S_GWANDFXI1_3, 0, 0}, // S_GWANDFXI1_2
+ {SPR_FX01, 32774, 3, NULL, S_GWANDFXI1_4, 0, 0}, // S_GWANDFXI1_3
+ {SPR_FX01, 32775, 3, NULL, S_NULL, 0, 0}, // S_GWANDFXI1_4
+ {SPR_FX01, 32770, 6, NULL, S_GWANDFX2_2, 0, 0}, // S_GWANDFX2_1
+ {SPR_FX01, 32771, 6, NULL, S_GWANDFX2_1, 0, 0}, // S_GWANDFX2_2
+ {SPR_PUF2, 32768, 3, NULL, S_GWANDPUFF1_2, 0, 0}, // S_GWANDPUFF1_1
+ {SPR_PUF2, 32769, 3, NULL, S_GWANDPUFF1_3, 0, 0}, // S_GWANDPUFF1_2
+ {SPR_PUF2, 32770, 3, NULL, S_GWANDPUFF1_4, 0, 0}, // S_GWANDPUFF1_3
+ {SPR_PUF2, 32771, 3, NULL, S_GWANDPUFF1_5, 0, 0}, // S_GWANDPUFF1_4
+ {SPR_PUF2, 32772, 3, NULL, S_NULL, 0, 0}, // S_GWANDPUFF1_5
+ {SPR_WPHX, 0, -1, NULL, S_NULL, 0, 0}, // S_WPHX
+ {SPR_PHNX, 0, 1, A_WeaponReady, S_PHOENIXREADY, 0, 0}, // S_PHOENIXREADY
+ {SPR_PHNX, 0, 1, A_Lower, S_PHOENIXDOWN, 0, 0}, // S_PHOENIXDOWN
+ {SPR_PHNX, 0, 1, A_Raise, S_PHOENIXUP, 0, 0}, // S_PHOENIXUP
+ {SPR_PHNX, 1, 5, NULL, S_PHOENIXATK1_2, 0, 0}, // S_PHOENIXATK1_1
+ {SPR_PHNX, 2, 7, A_FirePhoenixPL1, S_PHOENIXATK1_3, 0, 0}, // S_PHOENIXATK1_2
+ {SPR_PHNX, 3, 4, NULL, S_PHOENIXATK1_4, 0, 0}, // S_PHOENIXATK1_3
+ {SPR_PHNX, 1, 4, NULL, S_PHOENIXATK1_5, 0, 0}, // S_PHOENIXATK1_4
+ {SPR_PHNX, 1, 0, A_ReFire, S_PHOENIXREADY, 0, 0}, // S_PHOENIXATK1_5
+ {SPR_PHNX, 1, 3, A_InitPhoenixPL2, S_PHOENIXATK2_2, 0, 0}, // S_PHOENIXATK2_1
+ {SPR_PHNX, 32770, 1, A_FirePhoenixPL2, S_PHOENIXATK2_3, 0, 0}, // S_PHOENIXATK2_2
+ {SPR_PHNX, 1, 4, A_ReFire, S_PHOENIXATK2_4, 0, 0}, // S_PHOENIXATK2_3
+ {SPR_PHNX, 1, 4, A_ShutdownPhoenixPL2, S_PHOENIXREADY, 0, 0}, // S_PHOENIXATK2_4
+ {SPR_FX04, 32768, 4, A_PhoenixPuff, S_PHOENIXFX1_1, 0, 0}, // S_PHOENIXFX1_1
+ {SPR_FX08, 32768, 6, A_Explode, S_PHOENIXFXI1_2, 0, 0}, // S_PHOENIXFXI1_1
+ {SPR_FX08, 32769, 5, NULL, S_PHOENIXFXI1_3, 0, 0}, // S_PHOENIXFXI1_2
+ {SPR_FX08, 32770, 5, NULL, S_PHOENIXFXI1_4, 0, 0}, // S_PHOENIXFXI1_3
+ {SPR_FX08, 32771, 4, NULL, S_PHOENIXFXI1_5, 0, 0}, // S_PHOENIXFXI1_4
+ {SPR_FX08, 32772, 4, NULL, S_PHOENIXFXI1_6, 0, 0}, // S_PHOENIXFXI1_5
+ {SPR_FX08, 32773, 4, NULL, S_PHOENIXFXI1_7, 0, 0}, // S_PHOENIXFXI1_6
+ {SPR_FX08, 32774, 4, NULL, S_PHOENIXFXI1_8, 0, 0}, // S_PHOENIXFXI1_7
+ {SPR_FX08, 32775, 4, NULL, S_NULL, 0, 0}, // S_PHOENIXFXI1_8
+ {SPR_FX08, 32776, 8, NULL, S_PHOENIXFXIX_1, 0, 0 }, // S_PHOENIXFXIX_1
+ {SPR_FX08, 32777, 8, A_RemovedPhoenixFunc, S_PHOENIXFXIX_2, 0, 0 }, // S_PHOENIXFXIX_2
+ {SPR_FX08, 32778, 8, NULL, S_NULL, 0, 0 }, // S_PHOENIXFXIX_3
+ {SPR_FX04, 1, 4, NULL, S_PHOENIXPUFF2, 0, 0}, // S_PHOENIXPUFF1
+ {SPR_FX04, 2, 4, NULL, S_PHOENIXPUFF3, 0, 0}, // S_PHOENIXPUFF2
+ {SPR_FX04, 3, 4, NULL, S_PHOENIXPUFF4, 0, 0}, // S_PHOENIXPUFF3
+ {SPR_FX04, 4, 4, NULL, S_PHOENIXPUFF5, 0, 0}, // S_PHOENIXPUFF4
+ {SPR_FX04, 5, 4, NULL, S_NULL, 0, 0}, // S_PHOENIXPUFF5
+ {SPR_FX09, 32768, 2, NULL, S_PHOENIXFX2_2, 0, 0}, // S_PHOENIXFX2_1
+ {SPR_FX09, 32769, 2, NULL, S_PHOENIXFX2_3, 0, 0}, // S_PHOENIXFX2_2
+ {SPR_FX09, 32768, 2, NULL, S_PHOENIXFX2_4, 0, 0}, // S_PHOENIXFX2_3
+ {SPR_FX09, 32769, 2, NULL, S_PHOENIXFX2_5, 0, 0}, // S_PHOENIXFX2_4
+ {SPR_FX09, 32768, 2, NULL, S_PHOENIXFX2_6, 0, 0}, // S_PHOENIXFX2_5
+ {SPR_FX09, 32769, 2, A_FlameEnd, S_PHOENIXFX2_7, 0, 0}, // S_PHOENIXFX2_6
+ {SPR_FX09, 32770, 2, NULL, S_PHOENIXFX2_8, 0, 0}, // S_PHOENIXFX2_7
+ {SPR_FX09, 32771, 2, NULL, S_PHOENIXFX2_9, 0, 0}, // S_PHOENIXFX2_8
+ {SPR_FX09, 32772, 2, NULL, S_PHOENIXFX2_10, 0, 0}, // S_PHOENIXFX2_9
+ {SPR_FX09, 32773, 2, NULL, S_NULL, 0, 0}, // S_PHOENIXFX2_10
+ {SPR_FX09, 32774, 3, NULL, S_PHOENIXFXI2_2, 0, 0}, // S_PHOENIXFXI2_1
+ {SPR_FX09, 32775, 3, A_FloatPuff, S_PHOENIXFXI2_3, 0, 0}, // S_PHOENIXFXI2_2
+ {SPR_FX09, 32776, 4, NULL, S_PHOENIXFXI2_4, 0, 0}, // S_PHOENIXFXI2_3
+ {SPR_FX09, 32777, 5, NULL, S_PHOENIXFXI2_5, 0, 0}, // S_PHOENIXFXI2_4
+ {SPR_FX09, 32778, 5, NULL, S_NULL, 0, 0}, // S_PHOENIXFXI2_5
+ {SPR_WBOW, 0, -1, NULL, S_NULL, 0, 0}, // S_WBOW
+ {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW2, 0, 0}, // S_CRBOW1
+ {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW3, 0, 0}, // S_CRBOW2
+ {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW4, 0, 0}, // S_CRBOW3
+ {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW5, 0, 0}, // S_CRBOW4
+ {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW6, 0, 0}, // S_CRBOW5
+ {SPR_CRBW, 0, 1, A_WeaponReady, S_CRBOW7, 0, 0}, // S_CRBOW6
+ {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW8, 0, 0}, // S_CRBOW7
+ {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW9, 0, 0}, // S_CRBOW8
+ {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW10, 0, 0}, // S_CRBOW9
+ {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW11, 0, 0}, // S_CRBOW10
+ {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW12, 0, 0}, // S_CRBOW11
+ {SPR_CRBW, 1, 1, A_WeaponReady, S_CRBOW13, 0, 0}, // S_CRBOW12
+ {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW14, 0, 0}, // S_CRBOW13
+ {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW15, 0, 0}, // S_CRBOW14
+ {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW16, 0, 0}, // S_CRBOW15
+ {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW17, 0, 0}, // S_CRBOW16
+ {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW18, 0, 0}, // S_CRBOW17
+ {SPR_CRBW, 2, 1, A_WeaponReady, S_CRBOW1, 0, 0}, // S_CRBOW18
+ {SPR_CRBW, 0, 1, A_Lower, S_CRBOWDOWN, 0, 0}, // S_CRBOWDOWN
+ {SPR_CRBW, 0, 1, A_Raise, S_CRBOWUP, 0, 0}, // S_CRBOWUP
+ {SPR_CRBW, 3, 6, A_FireCrossbowPL1, S_CRBOWATK1_2, 0, 0}, // S_CRBOWATK1_1
+ {SPR_CRBW, 4, 3, NULL, S_CRBOWATK1_3, 0, 0}, // S_CRBOWATK1_2
+ {SPR_CRBW, 5, 3, NULL, S_CRBOWATK1_4, 0, 0}, // S_CRBOWATK1_3
+ {SPR_CRBW, 6, 3, NULL, S_CRBOWATK1_5, 0, 0}, // S_CRBOWATK1_4
+ {SPR_CRBW, 7, 3, NULL, S_CRBOWATK1_6, 0, 0}, // S_CRBOWATK1_5
+ {SPR_CRBW, 0, 4, NULL, S_CRBOWATK1_7, 0, 0}, // S_CRBOWATK1_6
+ {SPR_CRBW, 1, 4, NULL, S_CRBOWATK1_8, 0, 0}, // S_CRBOWATK1_7
+ {SPR_CRBW, 2, 5, A_ReFire, S_CRBOW1, 0, 0}, // S_CRBOWATK1_8
+ {SPR_CRBW, 3, 5, A_FireCrossbowPL2, S_CRBOWATK2_2, 0, 0}, // S_CRBOWATK2_1
+ {SPR_CRBW, 4, 3, NULL, S_CRBOWATK2_3, 0, 0}, // S_CRBOWATK2_2
+ {SPR_CRBW, 5, 2, NULL, S_CRBOWATK2_4, 0, 0}, // S_CRBOWATK2_3
+ {SPR_CRBW, 6, 3, NULL, S_CRBOWATK2_5, 0, 0}, // S_CRBOWATK2_4
+ {SPR_CRBW, 7, 2, NULL, S_CRBOWATK2_6, 0, 0}, // S_CRBOWATK2_5
+ {SPR_CRBW, 0, 3, NULL, S_CRBOWATK2_7, 0, 0}, // S_CRBOWATK2_6
+ {SPR_CRBW, 1, 3, NULL, S_CRBOWATK2_8, 0, 0}, // S_CRBOWATK2_7
+ {SPR_CRBW, 2, 4, A_ReFire, S_CRBOW1, 0, 0}, // S_CRBOWATK2_8
+ {SPR_FX03, 32769, 1, NULL, S_CRBOWFX1, 0, 0}, // S_CRBOWFX1
+ {SPR_FX03, 32775, 8, NULL, S_CRBOWFXI1_2, 0, 0}, // S_CRBOWFXI1_1
+ {SPR_FX03, 32776, 8, NULL, S_CRBOWFXI1_3, 0, 0}, // S_CRBOWFXI1_2
+ {SPR_FX03, 32777, 8, NULL, S_NULL, 0, 0}, // S_CRBOWFXI1_3
+ {SPR_FX03, 32769, 1, A_BoltSpark, S_CRBOWFX2, 0, 0}, // S_CRBOWFX2
+ {SPR_FX03, 32768, 1, NULL, S_CRBOWFX3, 0, 0}, // S_CRBOWFX3
+ {SPR_FX03, 32770, 8, NULL, S_CRBOWFXI3_2, 0, 0}, // S_CRBOWFXI3_1
+ {SPR_FX03, 32771, 8, NULL, S_CRBOWFXI3_3, 0, 0}, // S_CRBOWFXI3_2
+ {SPR_FX03, 32772, 8, NULL, S_NULL, 0, 0}, // S_CRBOWFXI3_3
+ {SPR_FX03, 32773, 8, NULL, S_CRBOWFX4_2, 0, 0}, // S_CRBOWFX4_1
+ {SPR_FX03, 32774, 8, NULL, S_NULL, 0, 0}, // S_CRBOWFX4_2
+ {SPR_BLOD, 2, 8, NULL, S_BLOOD2, 0, 0}, // S_BLOOD1
+ {SPR_BLOD, 1, 8, NULL, S_BLOOD3, 0, 0}, // S_BLOOD2
+ {SPR_BLOD, 0, 8, NULL, S_NULL, 0, 0}, // S_BLOOD3
+ {SPR_BLOD, 2, 8, NULL, S_BLOODSPLATTER2, 0, 0}, // S_BLOODSPLATTER1
+ {SPR_BLOD, 1, 8, NULL, S_BLOODSPLATTER3, 0, 0}, // S_BLOODSPLATTER2
+ {SPR_BLOD, 0, 8, NULL, S_NULL, 0, 0}, // S_BLOODSPLATTER3
+ {SPR_BLOD, 0, 6, NULL, S_NULL, 0, 0}, // S_BLOODSPLATTERX
+ {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, 6, NULL, S_PLAY_DIE2, 0, 0}, // S_PLAY_DIE1
+ {SPR_PLAY, 8, 6, A_Scream, S_PLAY_DIE3, 0, 0}, // S_PLAY_DIE2
+ {SPR_PLAY, 9, 6, NULL, S_PLAY_DIE4, 0, 0}, // S_PLAY_DIE3
+ {SPR_PLAY, 10, 6, NULL, S_PLAY_DIE5, 0, 0}, // S_PLAY_DIE4
+ {SPR_PLAY, 11, 6, A_NoBlocking, S_PLAY_DIE6, 0, 0}, // S_PLAY_DIE5
+ {SPR_PLAY, 12, 6, NULL, S_PLAY_DIE7, 0, 0}, // S_PLAY_DIE6
+ {SPR_PLAY, 13, 6, NULL, S_PLAY_DIE8, 0, 0}, // S_PLAY_DIE7
+ {SPR_PLAY, 14, 6, NULL, S_PLAY_DIE9, 0, 0}, // S_PLAY_DIE8
+ {SPR_PLAY, 15, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_PLAY_DIE9
+ {SPR_PLAY, 16, 5, A_Scream, S_PLAY_XDIE2, 0, 0}, // S_PLAY_XDIE1
+ {SPR_PLAY, 17, 5, A_SkullPop, S_PLAY_XDIE3, 0, 0}, // S_PLAY_XDIE2
+ {SPR_PLAY, 18, 5, A_NoBlocking, S_PLAY_XDIE4, 0, 0}, // S_PLAY_XDIE3
+ {SPR_PLAY, 19, 5, NULL, S_PLAY_XDIE5, 0, 0}, // S_PLAY_XDIE4
+ {SPR_PLAY, 20, 5, NULL, S_PLAY_XDIE6, 0, 0}, // S_PLAY_XDIE5
+ {SPR_PLAY, 21, 5, NULL, S_PLAY_XDIE7, 0, 0}, // S_PLAY_XDIE6
+ {SPR_PLAY, 22, 5, NULL, S_PLAY_XDIE8, 0, 0}, // S_PLAY_XDIE7
+ {SPR_PLAY, 23, 5, NULL, S_PLAY_XDIE9, 0, 0}, // S_PLAY_XDIE8
+ {SPR_PLAY, 24, -1, A_AddPlayerCorpse, S_NULL, 0, 0}, // S_PLAY_XDIE9
+ {SPR_FDTH, 32768, 5, A_FlameSnd, S_PLAY_FDTH2, 0, 0}, // S_PLAY_FDTH1
+ {SPR_FDTH, 32769, 4, NULL, S_PLAY_FDTH3, 0, 0}, // S_PLAY_FDTH2
+ {SPR_FDTH, 32770, 5, NULL, S_PLAY_FDTH4, 0, 0}, // S_PLAY_FDTH3
+ {SPR_FDTH, 32771, 4, A_Scream, S_PLAY_FDTH5, 0, 0}, // S_PLAY_FDTH4
+ {SPR_FDTH, 32772, 5, NULL, S_PLAY_FDTH6, 0, 0}, // S_PLAY_FDTH5
+ {SPR_FDTH, 32773, 4, NULL, S_PLAY_FDTH7, 0, 0}, // S_PLAY_FDTH6
+ {SPR_FDTH, 32774, 5, A_FlameSnd, S_PLAY_FDTH8, 0, 0}, // S_PLAY_FDTH7
+ {SPR_FDTH, 32775, 4, NULL, S_PLAY_FDTH9, 0, 0}, // S_PLAY_FDTH8
+ {SPR_FDTH, 32776, 5, NULL, S_PLAY_FDTH10, 0, 0}, // S_PLAY_FDTH9
+ {SPR_FDTH, 32777, 4, NULL, S_PLAY_FDTH11, 0, 0}, // S_PLAY_FDTH10
+ {SPR_FDTH, 32778, 5, NULL, S_PLAY_FDTH12, 0, 0}, // S_PLAY_FDTH11
+ {SPR_FDTH, 32779, 4, NULL, S_PLAY_FDTH13, 0, 0}, // S_PLAY_FDTH12
+ {SPR_FDTH, 32780, 5, NULL, S_PLAY_FDTH14, 0, 0}, // S_PLAY_FDTH13
+ {SPR_FDTH, 32781, 4, NULL, S_PLAY_FDTH15, 0, 0}, // S_PLAY_FDTH14
+ {SPR_FDTH, 32782, 5, A_NoBlocking, S_PLAY_FDTH16, 0, 0}, // S_PLAY_FDTH15
+ {SPR_FDTH, 32783, 4, NULL, S_PLAY_FDTH17, 0, 0}, // S_PLAY_FDTH16
+ {SPR_FDTH, 32784, 5, NULL, S_PLAY_FDTH18, 0, 0}, // S_PLAY_FDTH17
+ {SPR_FDTH, 32785, 4, NULL, S_PLAY_FDTH19, 0, 0}, // S_PLAY_FDTH18
+ {SPR_ACLO, 4, 35, A_CheckBurnGone, S_PLAY_FDTH19, 0, 0}, // S_PLAY_FDTH19
+ {SPR_ACLO, 4, 8, NULL, S_NULL, 0, 0}, // S_PLAY_FDTH20
+ {SPR_BSKL, 0, 5, A_CheckSkullFloor, S_BLOODYSKULL2, 0, 0}, // S_BLOODYSKULL1
+ {SPR_BSKL, 1, 5, A_CheckSkullFloor, S_BLOODYSKULL3, 0, 0}, // S_BLOODYSKULL2
+ {SPR_BSKL, 2, 5, A_CheckSkullFloor, S_BLOODYSKULL4, 0, 0}, // S_BLOODYSKULL3
+ {SPR_BSKL, 3, 5, A_CheckSkullFloor, S_BLOODYSKULL5, 0, 0}, // S_BLOODYSKULL4
+ {SPR_BSKL, 4, 5, A_CheckSkullFloor, S_BLOODYSKULL1, 0, 0}, // S_BLOODYSKULL5
+ {SPR_BSKL, 5, 16, A_CheckSkullDone, S_BLOODYSKULLX1, 0, 0}, // S_BLOODYSKULLX1
+ {SPR_BSKL, 5, 1050, NULL, S_NULL, 0, 0}, // S_BLOODYSKULLX2
+ {SPR_CHKN, 0, -1, NULL, S_NULL, 0, 0}, // S_CHICPLAY
+ {SPR_CHKN, 0, 3, NULL, S_CHICPLAY_RUN2, 0, 0}, // S_CHICPLAY_RUN1
+ {SPR_CHKN, 1, 3, NULL, S_CHICPLAY_RUN3, 0, 0}, // S_CHICPLAY_RUN2
+ {SPR_CHKN, 0, 3, NULL, S_CHICPLAY_RUN4, 0, 0}, // S_CHICPLAY_RUN3
+ {SPR_CHKN, 1, 3, NULL, S_CHICPLAY_RUN1, 0, 0}, // S_CHICPLAY_RUN4
+ {SPR_CHKN, 2, 12, NULL, S_CHICPLAY, 0, 0}, // S_CHICPLAY_ATK1
+ {SPR_CHKN, 3, 4, A_Feathers, S_CHICPLAY_PAIN2, 0, 0}, // S_CHICPLAY_PAIN
+ {SPR_CHKN, 2, 4, A_Pain, S_CHICPLAY, 0, 0}, // S_CHICPLAY_PAIN2
+ {SPR_CHKN, 0, 10, A_ChicLook, S_CHICKEN_LOOK2, 0, 0}, // S_CHICKEN_LOOK1
+ {SPR_CHKN, 1, 10, A_ChicLook, S_CHICKEN_LOOK1, 0, 0}, // S_CHICKEN_LOOK2
+ {SPR_CHKN, 0, 3, A_ChicChase, S_CHICKEN_WALK2, 0, 0}, // S_CHICKEN_WALK1
+ {SPR_CHKN, 1, 3, A_ChicChase, S_CHICKEN_WALK1, 0, 0}, // S_CHICKEN_WALK2
+ {SPR_CHKN, 3, 5, A_Feathers, S_CHICKEN_PAIN2, 0, 0}, // S_CHICKEN_PAIN1
+ {SPR_CHKN, 2, 5, A_ChicPain, S_CHICKEN_WALK1, 0, 0}, // S_CHICKEN_PAIN2
+ {SPR_CHKN, 0, 8, A_FaceTarget, S_CHICKEN_ATK2, 0, 0}, // S_CHICKEN_ATK1
+ {SPR_CHKN, 2, 10, A_ChicAttack, S_CHICKEN_WALK1, 0, 0}, // S_CHICKEN_ATK2
+ {SPR_CHKN, 4, 6, A_Scream, S_CHICKEN_DIE2, 0, 0}, // S_CHICKEN_DIE1
+ {SPR_CHKN, 5, 6, A_Feathers, S_CHICKEN_DIE3, 0, 0}, // S_CHICKEN_DIE2
+ {SPR_CHKN, 6, 6, NULL, S_CHICKEN_DIE4, 0, 0}, // S_CHICKEN_DIE3
+ {SPR_CHKN, 7, 6, A_NoBlocking, S_CHICKEN_DIE5, 0, 0}, // S_CHICKEN_DIE4
+ {SPR_CHKN, 8, 6, NULL, S_CHICKEN_DIE6, 0, 0}, // S_CHICKEN_DIE5
+ {SPR_CHKN, 9, 6, NULL, S_CHICKEN_DIE7, 0, 0}, // S_CHICKEN_DIE6
+ {SPR_CHKN, 10, 6, NULL, S_CHICKEN_DIE8, 0, 0}, // S_CHICKEN_DIE7
+ {SPR_CHKN, 11, -1, NULL, S_NULL, 0, 0}, // S_CHICKEN_DIE8
+ {SPR_CHKN, 12, 3, NULL, S_FEATHER2, 0, 0}, // S_FEATHER1
+ {SPR_CHKN, 13, 3, NULL, S_FEATHER3, 0, 0}, // S_FEATHER2
+ {SPR_CHKN, 14, 3, NULL, S_FEATHER4, 0, 0}, // S_FEATHER3
+ {SPR_CHKN, 15, 3, NULL, S_FEATHER5, 0, 0}, // S_FEATHER4
+ {SPR_CHKN, 16, 3, NULL, S_FEATHER6, 0, 0}, // S_FEATHER5
+ {SPR_CHKN, 15, 3, NULL, S_FEATHER7, 0, 0}, // S_FEATHER6
+ {SPR_CHKN, 14, 3, NULL, S_FEATHER8, 0, 0}, // S_FEATHER7
+ {SPR_CHKN, 13, 3, NULL, S_FEATHER1, 0, 0}, // S_FEATHER8
+ {SPR_CHKN, 13, 6, NULL, S_NULL, 0, 0}, // S_FEATHERX
+ {SPR_MUMM, 0, 10, A_Look, S_MUMMY_LOOK2, 0, 0}, // S_MUMMY_LOOK1
+ {SPR_MUMM, 1, 10, A_Look, S_MUMMY_LOOK1, 0, 0}, // S_MUMMY_LOOK2
+ {SPR_MUMM, 0, 4, A_Chase, S_MUMMY_WALK2, 0, 0}, // S_MUMMY_WALK1
+ {SPR_MUMM, 1, 4, A_Chase, S_MUMMY_WALK3, 0, 0}, // S_MUMMY_WALK2
+ {SPR_MUMM, 2, 4, A_Chase, S_MUMMY_WALK4, 0, 0}, // S_MUMMY_WALK3
+ {SPR_MUMM, 3, 4, A_Chase, S_MUMMY_WALK1, 0, 0}, // S_MUMMY_WALK4
+ {SPR_MUMM, 4, 6, A_FaceTarget, S_MUMMY_ATK2, 0, 0}, // S_MUMMY_ATK1
+ {SPR_MUMM, 5, 6, A_MummyAttack, S_MUMMY_ATK3, 0, 0}, // S_MUMMY_ATK2
+ {SPR_MUMM, 6, 6, A_FaceTarget, S_MUMMY_WALK1, 0, 0}, // S_MUMMY_ATK3
+ {SPR_MUMM, 23, 5, A_FaceTarget, S_MUMMYL_ATK2, 0, 0}, // S_MUMMYL_ATK1
+ {SPR_MUMM, 32792, 5, A_FaceTarget, S_MUMMYL_ATK3, 0, 0}, // S_MUMMYL_ATK2
+ {SPR_MUMM, 23, 5, A_FaceTarget, S_MUMMYL_ATK4, 0, 0}, // S_MUMMYL_ATK3
+ {SPR_MUMM, 32792, 5, A_FaceTarget, S_MUMMYL_ATK5, 0, 0}, // S_MUMMYL_ATK4
+ {SPR_MUMM, 23, 5, A_FaceTarget, S_MUMMYL_ATK6, 0, 0}, // S_MUMMYL_ATK5
+ {SPR_MUMM, 32792, 15, A_MummyAttack2, S_MUMMY_WALK1, 0, 0}, // S_MUMMYL_ATK6
+ {SPR_MUMM, 7, 4, NULL, S_MUMMY_PAIN2, 0, 0}, // S_MUMMY_PAIN1
+ {SPR_MUMM, 7, 4, A_Pain, S_MUMMY_WALK1, 0, 0}, // S_MUMMY_PAIN2
+ {SPR_MUMM, 8, 5, NULL, S_MUMMY_DIE2, 0, 0}, // S_MUMMY_DIE1
+ {SPR_MUMM, 9, 5, A_Scream, S_MUMMY_DIE3, 0, 0}, // S_MUMMY_DIE2
+ {SPR_MUMM, 10, 5, A_MummySoul, S_MUMMY_DIE4, 0, 0}, // S_MUMMY_DIE3
+ {SPR_MUMM, 11, 5, NULL, S_MUMMY_DIE5, 0, 0}, // S_MUMMY_DIE4
+ {SPR_MUMM, 12, 5, A_NoBlocking, S_MUMMY_DIE6, 0, 0}, // S_MUMMY_DIE5
+ {SPR_MUMM, 13, 5, NULL, S_MUMMY_DIE7, 0, 0}, // S_MUMMY_DIE6
+ {SPR_MUMM, 14, 5, NULL, S_MUMMY_DIE8, 0, 0}, // S_MUMMY_DIE7
+ {SPR_MUMM, 15, -1, NULL, S_NULL, 0, 0}, // S_MUMMY_DIE8
+ {SPR_MUMM, 16, 5, NULL, S_MUMMY_SOUL2, 0, 0}, // S_MUMMY_SOUL1
+ {SPR_MUMM, 17, 5, NULL, S_MUMMY_SOUL3, 0, 0}, // S_MUMMY_SOUL2
+ {SPR_MUMM, 18, 5, NULL, S_MUMMY_SOUL4, 0, 0}, // S_MUMMY_SOUL3
+ {SPR_MUMM, 19, 9, NULL, S_MUMMY_SOUL5, 0, 0}, // S_MUMMY_SOUL4
+ {SPR_MUMM, 20, 5, NULL, S_MUMMY_SOUL6, 0, 0}, // S_MUMMY_SOUL5
+ {SPR_MUMM, 21, 5, NULL, S_MUMMY_SOUL7, 0, 0}, // S_MUMMY_SOUL6
+ {SPR_MUMM, 22, 5, NULL, S_NULL, 0, 0}, // S_MUMMY_SOUL7
+ {SPR_FX15, 32768, 5, A_ContMobjSound, S_MUMMYFX1_2, 0, 0}, // S_MUMMYFX1_1
+ {SPR_FX15, 32769, 5, A_MummyFX1Seek, S_MUMMYFX1_3, 0, 0}, // S_MUMMYFX1_2
+ {SPR_FX15, 32770, 5, NULL, S_MUMMYFX1_4, 0, 0}, // S_MUMMYFX1_3
+ {SPR_FX15, 32769, 5, A_MummyFX1Seek, S_MUMMYFX1_1, 0, 0}, // S_MUMMYFX1_4
+ {SPR_FX15, 32771, 5, NULL, S_MUMMYFXI1_2, 0, 0}, // S_MUMMYFXI1_1
+ {SPR_FX15, 32772, 5, NULL, S_MUMMYFXI1_3, 0, 0}, // S_MUMMYFXI1_2
+ {SPR_FX15, 32773, 5, NULL, S_MUMMYFXI1_4, 0, 0}, // S_MUMMYFXI1_3
+ {SPR_FX15, 32774, 5, NULL, S_NULL, 0, 0}, // S_MUMMYFXI1_4
+ {SPR_BEAS, 0, 10, A_Look, S_BEAST_LOOK2, 0, 0}, // S_BEAST_LOOK1
+ {SPR_BEAS, 1, 10, A_Look, S_BEAST_LOOK1, 0, 0}, // S_BEAST_LOOK2
+ {SPR_BEAS, 0, 3, A_Chase, S_BEAST_WALK2, 0, 0}, // S_BEAST_WALK1
+ {SPR_BEAS, 1, 3, A_Chase, S_BEAST_WALK3, 0, 0}, // S_BEAST_WALK2
+ {SPR_BEAS, 2, 3, A_Chase, S_BEAST_WALK4, 0, 0}, // S_BEAST_WALK3
+ {SPR_BEAS, 3, 3, A_Chase, S_BEAST_WALK5, 0, 0}, // S_BEAST_WALK4
+ {SPR_BEAS, 4, 3, A_Chase, S_BEAST_WALK6, 0, 0}, // S_BEAST_WALK5
+ {SPR_BEAS, 5, 3, A_Chase, S_BEAST_WALK1, 0, 0}, // S_BEAST_WALK6
+ {SPR_BEAS, 7, 10, A_FaceTarget, S_BEAST_ATK2, 0, 0}, // S_BEAST_ATK1
+ {SPR_BEAS, 8, 10, A_BeastAttack, S_BEAST_WALK1, 0, 0}, // S_BEAST_ATK2
+ {SPR_BEAS, 6, 3, NULL, S_BEAST_PAIN2, 0, 0}, // S_BEAST_PAIN1
+ {SPR_BEAS, 6, 3, A_Pain, S_BEAST_WALK1, 0, 0}, // S_BEAST_PAIN2
+ {SPR_BEAS, 17, 6, NULL, S_BEAST_DIE2, 0, 0}, // S_BEAST_DIE1
+ {SPR_BEAS, 18, 6, A_Scream, S_BEAST_DIE3, 0, 0}, // S_BEAST_DIE2
+ {SPR_BEAS, 19, 6, NULL, S_BEAST_DIE4, 0, 0}, // S_BEAST_DIE3
+ {SPR_BEAS, 20, 6, NULL, S_BEAST_DIE5, 0, 0}, // S_BEAST_DIE4
+ {SPR_BEAS, 21, 6, NULL, S_BEAST_DIE6, 0, 0}, // S_BEAST_DIE5
+ {SPR_BEAS, 22, 6, A_NoBlocking, S_BEAST_DIE7, 0, 0}, // S_BEAST_DIE6
+ {SPR_BEAS, 23, 6, NULL, S_BEAST_DIE8, 0, 0}, // S_BEAST_DIE7
+ {SPR_BEAS, 24, 6, NULL, S_BEAST_DIE9, 0, 0}, // S_BEAST_DIE8
+ {SPR_BEAS, 25, -1, NULL, S_NULL, 0, 0}, // S_BEAST_DIE9
+ {SPR_BEAS, 9, 5, NULL, S_BEAST_XDIE2, 0, 0}, // S_BEAST_XDIE1
+ {SPR_BEAS, 10, 6, A_Scream, S_BEAST_XDIE3, 0, 0}, // S_BEAST_XDIE2
+ {SPR_BEAS, 11, 5, NULL, S_BEAST_XDIE4, 0, 0}, // S_BEAST_XDIE3
+ {SPR_BEAS, 12, 6, NULL, S_BEAST_XDIE5, 0, 0}, // S_BEAST_XDIE4
+ {SPR_BEAS, 13, 5, NULL, S_BEAST_XDIE6, 0, 0}, // S_BEAST_XDIE5
+ {SPR_BEAS, 14, 6, A_NoBlocking, S_BEAST_XDIE7, 0, 0}, // S_BEAST_XDIE6
+ {SPR_BEAS, 15, 5, NULL, S_BEAST_XDIE8, 0, 0}, // S_BEAST_XDIE7
+ {SPR_BEAS, 16, -1, NULL, S_NULL, 0, 0}, // S_BEAST_XDIE8
+ {SPR_FRB1, 0, 2, A_BeastPuff, S_BEASTBALL2, 0, 0}, // S_BEASTBALL1
+ {SPR_FRB1, 0, 2, A_BeastPuff, S_BEASTBALL3, 0, 0}, // S_BEASTBALL2
+ {SPR_FRB1, 1, 2, A_BeastPuff, S_BEASTBALL4, 0, 0}, // S_BEASTBALL3
+ {SPR_FRB1, 1, 2, A_BeastPuff, S_BEASTBALL5, 0, 0}, // S_BEASTBALL4
+ {SPR_FRB1, 2, 2, A_BeastPuff, S_BEASTBALL6, 0, 0}, // S_BEASTBALL5
+ {SPR_FRB1, 2, 2, A_BeastPuff, S_BEASTBALL1, 0, 0}, // S_BEASTBALL6
+ {SPR_FRB1, 3, 4, NULL, S_BEASTBALLX2, 0, 0}, // S_BEASTBALLX1
+ {SPR_FRB1, 4, 4, NULL, S_BEASTBALLX3, 0, 0}, // S_BEASTBALLX2
+ {SPR_FRB1, 5, 4, NULL, S_BEASTBALLX4, 0, 0}, // S_BEASTBALLX3
+ {SPR_FRB1, 6, 4, NULL, S_BEASTBALLX5, 0, 0}, // S_BEASTBALLX4
+ {SPR_FRB1, 7, 4, NULL, S_NULL, 0, 0}, // S_BEASTBALLX5
+ {SPR_FRB1, 0, 4, NULL, S_BURNBALL2, 0, 0}, // S_BURNBALL1
+ {SPR_FRB1, 1, 4, NULL, S_BURNBALL3, 0, 0}, // S_BURNBALL2
+ {SPR_FRB1, 2, 4, NULL, S_BURNBALL4, 0, 0}, // S_BURNBALL3
+ {SPR_FRB1, 3, 4, NULL, S_BURNBALL5, 0, 0}, // S_BURNBALL4
+ {SPR_FRB1, 4, 4, NULL, S_BURNBALL6, 0, 0}, // S_BURNBALL5
+ {SPR_FRB1, 5, 4, NULL, S_BURNBALL7, 0, 0}, // S_BURNBALL6
+ {SPR_FRB1, 6, 4, NULL, S_BURNBALL8, 0, 0}, // S_BURNBALL7
+ {SPR_FRB1, 7, 4, NULL, S_NULL, 0, 0}, // S_BURNBALL8
+ {SPR_FRB1, 32768, 4, NULL, S_BURNBALLFB2, 0, 0}, // S_BURNBALLFB1
+ {SPR_FRB1, 32769, 4, NULL, S_BURNBALLFB3, 0, 0}, // S_BURNBALLFB2
+ {SPR_FRB1, 32770, 4, NULL, S_BURNBALLFB4, 0, 0}, // S_BURNBALLFB3
+ {SPR_FRB1, 32771, 4, NULL, S_BURNBALLFB5, 0, 0}, // S_BURNBALLFB4
+ {SPR_FRB1, 32772, 4, NULL, S_BURNBALLFB6, 0, 0}, // S_BURNBALLFB5
+ {SPR_FRB1, 32773, 4, NULL, S_BURNBALLFB7, 0, 0}, // S_BURNBALLFB6
+ {SPR_FRB1, 32774, 4, NULL, S_BURNBALLFB8, 0, 0}, // S_BURNBALLFB7
+ {SPR_FRB1, 32775, 4, NULL, S_NULL, 0, 0}, // S_BURNBALLFB8
+ {SPR_FRB1, 3, 4, NULL, S_PUFFY2, 0, 0}, // S_PUFFY1
+ {SPR_FRB1, 4, 4, NULL, S_PUFFY3, 0, 0}, // S_PUFFY2
+ {SPR_FRB1, 5, 4, NULL, S_PUFFY4, 0, 0}, // S_PUFFY3
+ {SPR_FRB1, 6, 4, NULL, S_PUFFY5, 0, 0}, // S_PUFFY4
+ {SPR_FRB1, 7, 4, NULL, S_NULL, 0, 0}, // S_PUFFY5
+ {SPR_SNKE, 0, 10, A_Look, S_SNAKE_LOOK2, 0, 0}, // S_SNAKE_LOOK1
+ {SPR_SNKE, 1, 10, A_Look, S_SNAKE_LOOK1, 0, 0}, // S_SNAKE_LOOK2
+ {SPR_SNKE, 0, 4, A_Chase, S_SNAKE_WALK2, 0, 0}, // S_SNAKE_WALK1
+ {SPR_SNKE, 1, 4, A_Chase, S_SNAKE_WALK3, 0, 0}, // S_SNAKE_WALK2
+ {SPR_SNKE, 2, 4, A_Chase, S_SNAKE_WALK4, 0, 0}, // S_SNAKE_WALK3
+ {SPR_SNKE, 3, 4, A_Chase, S_SNAKE_WALK1, 0, 0}, // S_SNAKE_WALK4
+ {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK2, 0, 0}, // S_SNAKE_ATK1
+ {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK3, 0, 0}, // S_SNAKE_ATK2
+ {SPR_SNKE, 5, 4, A_SnakeAttack, S_SNAKE_ATK4, 0, 0}, // S_SNAKE_ATK3
+ {SPR_SNKE, 5, 4, A_SnakeAttack, S_SNAKE_ATK5, 0, 0}, // S_SNAKE_ATK4
+ {SPR_SNKE, 5, 4, A_SnakeAttack, S_SNAKE_ATK6, 0, 0}, // S_SNAKE_ATK5
+ {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK7, 0, 0}, // S_SNAKE_ATK6
+ {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK8, 0, 0}, // S_SNAKE_ATK7
+ {SPR_SNKE, 5, 5, A_FaceTarget, S_SNAKE_ATK9, 0, 0}, // S_SNAKE_ATK8
+ {SPR_SNKE, 5, 4, A_SnakeAttack2, S_SNAKE_WALK1, 0, 0}, // S_SNAKE_ATK9
+ {SPR_SNKE, 4, 3, NULL, S_SNAKE_PAIN2, 0, 0}, // S_SNAKE_PAIN1
+ {SPR_SNKE, 4, 3, A_Pain, S_SNAKE_WALK1, 0, 0}, // S_SNAKE_PAIN2
+ {SPR_SNKE, 6, 5, NULL, S_SNAKE_DIE2, 0, 0}, // S_SNAKE_DIE1
+ {SPR_SNKE, 7, 5, A_Scream, S_SNAKE_DIE3, 0, 0}, // S_SNAKE_DIE2
+ {SPR_SNKE, 8, 5, NULL, S_SNAKE_DIE4, 0, 0}, // S_SNAKE_DIE3
+ {SPR_SNKE, 9, 5, NULL, S_SNAKE_DIE5, 0, 0}, // S_SNAKE_DIE4
+ {SPR_SNKE, 10, 5, NULL, S_SNAKE_DIE6, 0, 0}, // S_SNAKE_DIE5
+ {SPR_SNKE, 11, 5, NULL, S_SNAKE_DIE7, 0, 0}, // S_SNAKE_DIE6
+ {SPR_SNKE, 12, 5, A_NoBlocking, S_SNAKE_DIE8, 0, 0}, // S_SNAKE_DIE7
+ {SPR_SNKE, 13, 5, NULL, S_SNAKE_DIE9, 0, 0}, // S_SNAKE_DIE8
+ {SPR_SNKE, 14, 5, NULL, S_SNAKE_DIE10, 0, 0}, // S_SNAKE_DIE9
+ {SPR_SNKE, 15, -1, NULL, S_NULL, 0, 0}, // S_SNAKE_DIE10
+ {SPR_SNFX, 32768, 5, NULL, S_SNAKEPRO_A2, 0, 0}, // S_SNAKEPRO_A1
+ {SPR_SNFX, 32769, 5, NULL, S_SNAKEPRO_A3, 0, 0}, // S_SNAKEPRO_A2
+ {SPR_SNFX, 32770, 5, NULL, S_SNAKEPRO_A4, 0, 0}, // S_SNAKEPRO_A3
+ {SPR_SNFX, 32771, 5, NULL, S_SNAKEPRO_A1, 0, 0}, // S_SNAKEPRO_A4
+ {SPR_SNFX, 32772, 5, NULL, S_SNAKEPRO_AX2, 0, 0}, // S_SNAKEPRO_AX1
+ {SPR_SNFX, 32773, 5, NULL, S_SNAKEPRO_AX3, 0, 0}, // S_SNAKEPRO_AX2
+ {SPR_SNFX, 32774, 4, NULL, S_SNAKEPRO_AX4, 0, 0}, // S_SNAKEPRO_AX3
+ {SPR_SNFX, 32775, 3, NULL, S_SNAKEPRO_AX5, 0, 0}, // S_SNAKEPRO_AX4
+ {SPR_SNFX, 32776, 3, NULL, S_NULL, 0, 0}, // S_SNAKEPRO_AX5
+ {SPR_SNFX, 32777, 6, NULL, S_SNAKEPRO_B2, 0, 0}, // S_SNAKEPRO_B1
+ {SPR_SNFX, 32778, 6, NULL, S_SNAKEPRO_B1, 0, 0}, // S_SNAKEPRO_B2
+ {SPR_SNFX, 32779, 5, NULL, S_SNAKEPRO_BX2, 0, 0}, // S_SNAKEPRO_BX1
+ {SPR_SNFX, 32780, 5, NULL, S_SNAKEPRO_BX3, 0, 0}, // S_SNAKEPRO_BX2
+ {SPR_SNFX, 32781, 4, NULL, S_SNAKEPRO_BX4, 0, 0}, // S_SNAKEPRO_BX3
+ {SPR_SNFX, 32782, 3, NULL, S_NULL, 0, 0}, // S_SNAKEPRO_BX4
+ {SPR_HEAD, 0, 10, A_Look, S_HEAD_LOOK, 0, 0}, // S_HEAD_LOOK
+ {SPR_HEAD, 0, 4, A_Chase, S_HEAD_FLOAT, 0, 0}, // S_HEAD_FLOAT
+ {SPR_HEAD, 0, 5, A_FaceTarget, S_HEAD_ATK2, 0, 0}, // S_HEAD_ATK1
+ {SPR_HEAD, 1, 20, A_HeadAttack, S_HEAD_FLOAT, 0, 0}, // S_HEAD_ATK2
+ {SPR_HEAD, 0, 4, NULL, S_HEAD_PAIN2, 0, 0}, // S_HEAD_PAIN1
+ {SPR_HEAD, 0, 4, A_Pain, S_HEAD_FLOAT, 0, 0}, // S_HEAD_PAIN2
+ {SPR_HEAD, 2, 7, NULL, S_HEAD_DIE2, 0, 0}, // S_HEAD_DIE1
+ {SPR_HEAD, 3, 7, A_Scream, S_HEAD_DIE3, 0, 0}, // S_HEAD_DIE2
+ {SPR_HEAD, 4, 7, NULL, S_HEAD_DIE4, 0, 0}, // S_HEAD_DIE3
+ {SPR_HEAD, 5, 7, NULL, S_HEAD_DIE5, 0, 0}, // S_HEAD_DIE4
+ {SPR_HEAD, 6, 7, A_NoBlocking, S_HEAD_DIE6, 0, 0}, // S_HEAD_DIE5
+ {SPR_HEAD, 7, 7, NULL, S_HEAD_DIE7, 0, 0}, // S_HEAD_DIE6
+ {SPR_HEAD, 8, -1, A_BossDeath, S_NULL, 0, 0}, // S_HEAD_DIE7
+ {SPR_FX05, 0, 6, NULL, S_HEADFX1_2, 0, 0}, // S_HEADFX1_1
+ {SPR_FX05, 1, 6, NULL, S_HEADFX1_3, 0, 0}, // S_HEADFX1_2
+ {SPR_FX05, 2, 6, NULL, S_HEADFX1_1, 0, 0}, // S_HEADFX1_3
+ {SPR_FX05, 3, 5, A_HeadIceImpact, S_HEADFXI1_2, 0, 0}, // S_HEADFXI1_1
+ {SPR_FX05, 4, 5, NULL, S_HEADFXI1_3, 0, 0}, // S_HEADFXI1_2
+ {SPR_FX05, 5, 5, NULL, S_HEADFXI1_4, 0, 0}, // S_HEADFXI1_3
+ {SPR_FX05, 6, 5, NULL, S_NULL, 0, 0}, // S_HEADFXI1_4
+ {SPR_FX05, 7, 6, NULL, S_HEADFX2_2, 0, 0}, // S_HEADFX2_1
+ {SPR_FX05, 8, 6, NULL, S_HEADFX2_3, 0, 0}, // S_HEADFX2_2
+ {SPR_FX05, 9, 6, NULL, S_HEADFX2_1, 0, 0}, // S_HEADFX2_3
+ {SPR_FX05, 3, 5, NULL, S_HEADFXI2_2, 0, 0}, // S_HEADFXI2_1
+ {SPR_FX05, 4, 5, NULL, S_HEADFXI2_3, 0, 0}, // S_HEADFXI2_2
+ {SPR_FX05, 5, 5, NULL, S_HEADFXI2_4, 0, 0}, // S_HEADFXI2_3
+ {SPR_FX05, 6, 5, NULL, S_NULL, 0, 0}, // S_HEADFXI2_4
+ {SPR_FX06, 0, 4, A_HeadFireGrow, S_HEADFX3_2, 0, 0}, // S_HEADFX3_1
+ {SPR_FX06, 1, 4, A_HeadFireGrow, S_HEADFX3_3, 0, 0}, // S_HEADFX3_2
+ {SPR_FX06, 2, 4, A_HeadFireGrow, S_HEADFX3_1, 0, 0}, // S_HEADFX3_3
+ {SPR_FX06, 0, 5, NULL, S_HEADFX3_5, 0, 0}, // S_HEADFX3_4
+ {SPR_FX06, 1, 5, NULL, S_HEADFX3_6, 0, 0}, // S_HEADFX3_5
+ {SPR_FX06, 2, 5, NULL, S_HEADFX3_4, 0, 0}, // S_HEADFX3_6
+ {SPR_FX06, 3, 5, NULL, S_HEADFXI3_2, 0, 0}, // S_HEADFXI3_1
+ {SPR_FX06, 4, 5, NULL, S_HEADFXI3_3, 0, 0}, // S_HEADFXI3_2
+ {SPR_FX06, 5, 5, NULL, S_HEADFXI3_4, 0, 0}, // S_HEADFXI3_3
+ {SPR_FX06, 6, 5, NULL, S_NULL, 0, 0}, // S_HEADFXI3_4
+ {SPR_FX07, 3, 3, NULL, S_HEADFX4_2, 0, 0}, // S_HEADFX4_1
+ {SPR_FX07, 4, 3, NULL, S_HEADFX4_3, 0, 0}, // S_HEADFX4_2
+ {SPR_FX07, 5, 3, NULL, S_HEADFX4_4, 0, 0}, // S_HEADFX4_3
+ {SPR_FX07, 6, 3, NULL, S_HEADFX4_5, 0, 0}, // S_HEADFX4_4
+ {SPR_FX07, 0, 3, A_WhirlwindSeek, S_HEADFX4_6, 0, 0}, // S_HEADFX4_5
+ {SPR_FX07, 1, 3, A_WhirlwindSeek, S_HEADFX4_7, 0, 0}, // S_HEADFX4_6
+ {SPR_FX07, 2, 3, A_WhirlwindSeek, S_HEADFX4_5, 0, 0}, // S_HEADFX4_7
+ {SPR_FX07, 6, 4, NULL, S_HEADFXI4_2, 0, 0}, // S_HEADFXI4_1
+ {SPR_FX07, 5, 4, NULL, S_HEADFXI4_3, 0, 0}, // S_HEADFXI4_2
+ {SPR_FX07, 4, 4, NULL, S_HEADFXI4_4, 0, 0}, // S_HEADFXI4_3
+ {SPR_FX07, 3, 4, NULL, S_NULL, 0, 0}, // S_HEADFXI4_4
+ {SPR_CLNK, 0, 10, A_Look, S_CLINK_LOOK2, 0, 0}, // S_CLINK_LOOK1
+ {SPR_CLNK, 1, 10, A_Look, S_CLINK_LOOK1, 0, 0}, // S_CLINK_LOOK2
+ {SPR_CLNK, 0, 3, A_Chase, S_CLINK_WALK2, 0, 0}, // S_CLINK_WALK1
+ {SPR_CLNK, 1, 3, A_Chase, S_CLINK_WALK3, 0, 0}, // S_CLINK_WALK2
+ {SPR_CLNK, 2, 3, A_Chase, S_CLINK_WALK4, 0, 0}, // S_CLINK_WALK3
+ {SPR_CLNK, 3, 3, A_Chase, S_CLINK_WALK1, 0, 0}, // S_CLINK_WALK4
+ {SPR_CLNK, 4, 5, A_FaceTarget, S_CLINK_ATK2, 0, 0}, // S_CLINK_ATK1
+ {SPR_CLNK, 5, 4, A_FaceTarget, S_CLINK_ATK3, 0, 0}, // S_CLINK_ATK2
+ {SPR_CLNK, 6, 7, A_ClinkAttack, S_CLINK_WALK1, 0, 0}, // S_CLINK_ATK3
+ {SPR_CLNK, 7, 3, NULL, S_CLINK_PAIN2, 0, 0}, // S_CLINK_PAIN1
+ {SPR_CLNK, 7, 3, A_Pain, S_CLINK_WALK1, 0, 0}, // S_CLINK_PAIN2
+ {SPR_CLNK, 8, 6, NULL, S_CLINK_DIE2, 0, 0}, // S_CLINK_DIE1
+ {SPR_CLNK, 9, 6, NULL, S_CLINK_DIE3, 0, 0}, // S_CLINK_DIE2
+ {SPR_CLNK, 10, 5, A_Scream, S_CLINK_DIE4, 0, 0}, // S_CLINK_DIE3
+ {SPR_CLNK, 11, 5, A_NoBlocking, S_CLINK_DIE5, 0, 0}, // S_CLINK_DIE4
+ {SPR_CLNK, 12, 5, NULL, S_CLINK_DIE6, 0, 0}, // S_CLINK_DIE5
+ {SPR_CLNK, 13, 5, NULL, S_CLINK_DIE7, 0, 0}, // S_CLINK_DIE6
+ {SPR_CLNK, 14, -1, NULL, S_NULL, 0, 0}, // S_CLINK_DIE7
+ {SPR_WZRD, 0, 10, A_Look, S_WIZARD_LOOK2, 0, 0}, // S_WIZARD_LOOK1
+ {SPR_WZRD, 1, 10, A_Look, S_WIZARD_LOOK1, 0, 0}, // S_WIZARD_LOOK2
+ {SPR_WZRD, 0, 3, A_Chase, S_WIZARD_WALK2, 0, 0}, // S_WIZARD_WALK1
+ {SPR_WZRD, 0, 4, A_Chase, S_WIZARD_WALK3, 0, 0}, // S_WIZARD_WALK2
+ {SPR_WZRD, 0, 3, A_Chase, S_WIZARD_WALK4, 0, 0}, // S_WIZARD_WALK3
+ {SPR_WZRD, 0, 4, A_Chase, S_WIZARD_WALK5, 0, 0}, // S_WIZARD_WALK4
+ {SPR_WZRD, 1, 3, A_Chase, S_WIZARD_WALK6, 0, 0}, // S_WIZARD_WALK5
+ {SPR_WZRD, 1, 4, A_Chase, S_WIZARD_WALK7, 0, 0}, // S_WIZARD_WALK6
+ {SPR_WZRD, 1, 3, A_Chase, S_WIZARD_WALK8, 0, 0}, // S_WIZARD_WALK7
+ {SPR_WZRD, 1, 4, A_Chase, S_WIZARD_WALK1, 0, 0}, // S_WIZARD_WALK8
+ {SPR_WZRD, 2, 4, A_WizAtk1, S_WIZARD_ATK2, 0, 0}, // S_WIZARD_ATK1
+ {SPR_WZRD, 2, 4, A_WizAtk2, S_WIZARD_ATK3, 0, 0}, // S_WIZARD_ATK2
+ {SPR_WZRD, 2, 4, A_WizAtk1, S_WIZARD_ATK4, 0, 0}, // S_WIZARD_ATK3
+ {SPR_WZRD, 2, 4, A_WizAtk2, S_WIZARD_ATK5, 0, 0}, // S_WIZARD_ATK4
+ {SPR_WZRD, 2, 4, A_WizAtk1, S_WIZARD_ATK6, 0, 0}, // S_WIZARD_ATK5
+ {SPR_WZRD, 2, 4, A_WizAtk2, S_WIZARD_ATK7, 0, 0}, // S_WIZARD_ATK6
+ {SPR_WZRD, 2, 4, A_WizAtk1, S_WIZARD_ATK8, 0, 0}, // S_WIZARD_ATK7
+ {SPR_WZRD, 2, 4, A_WizAtk2, S_WIZARD_ATK9, 0, 0}, // S_WIZARD_ATK8
+ {SPR_WZRD, 3, 12, A_WizAtk3, S_WIZARD_WALK1, 0, 0}, // S_WIZARD_ATK9
+ {SPR_WZRD, 4, 3, A_GhostOff, S_WIZARD_PAIN2, 0, 0}, // S_WIZARD_PAIN1
+ {SPR_WZRD, 4, 3, A_Pain, S_WIZARD_WALK1, 0, 0}, // S_WIZARD_PAIN2
+ {SPR_WZRD, 5, 6, A_GhostOff, S_WIZARD_DIE2, 0, 0}, // S_WIZARD_DIE1
+ {SPR_WZRD, 6, 6, A_Scream, S_WIZARD_DIE3, 0, 0}, // S_WIZARD_DIE2
+ {SPR_WZRD, 7, 6, NULL, S_WIZARD_DIE4, 0, 0}, // S_WIZARD_DIE3
+ {SPR_WZRD, 8, 6, NULL, S_WIZARD_DIE5, 0, 0}, // S_WIZARD_DIE4
+ {SPR_WZRD, 9, 6, A_NoBlocking, S_WIZARD_DIE6, 0, 0}, // S_WIZARD_DIE5
+ {SPR_WZRD, 10, 6, NULL, S_WIZARD_DIE7, 0, 0}, // S_WIZARD_DIE6
+ {SPR_WZRD, 11, 6, NULL, S_WIZARD_DIE8, 0, 0}, // S_WIZARD_DIE7
+ {SPR_WZRD, 12, -1, NULL, S_NULL, 0, 0}, // S_WIZARD_DIE8
+ {SPR_FX11, 32768, 6, NULL, S_WIZFX1_2, 0, 0}, // S_WIZFX1_1
+ {SPR_FX11, 32769, 6, NULL, S_WIZFX1_1, 0, 0}, // S_WIZFX1_2
+ {SPR_FX11, 32770, 5, NULL, S_WIZFXI1_2, 0, 0}, // S_WIZFXI1_1
+ {SPR_FX11, 32771, 5, NULL, S_WIZFXI1_3, 0, 0}, // S_WIZFXI1_2
+ {SPR_FX11, 32772, 5, NULL, S_WIZFXI1_4, 0, 0}, // S_WIZFXI1_3
+ {SPR_FX11, 32773, 5, NULL, S_WIZFXI1_5, 0, 0}, // S_WIZFXI1_4
+ {SPR_FX11, 32774, 5, NULL, S_NULL, 0, 0}, // S_WIZFXI1_5
+ {SPR_IMPX, 0, 10, A_Look, S_IMP_LOOK2, 0, 0}, // S_IMP_LOOK1
+ {SPR_IMPX, 1, 10, A_Look, S_IMP_LOOK3, 0, 0}, // S_IMP_LOOK2
+ {SPR_IMPX, 2, 10, A_Look, S_IMP_LOOK4, 0, 0}, // S_IMP_LOOK3
+ {SPR_IMPX, 1, 10, A_Look, S_IMP_LOOK1, 0, 0}, // S_IMP_LOOK4
+ {SPR_IMPX, 0, 3, A_Chase, S_IMP_FLY2, 0, 0}, // S_IMP_FLY1
+ {SPR_IMPX, 0, 3, A_Chase, S_IMP_FLY3, 0, 0}, // S_IMP_FLY2
+ {SPR_IMPX, 1, 3, A_Chase, S_IMP_FLY4, 0, 0}, // S_IMP_FLY3
+ {SPR_IMPX, 1, 3, A_Chase, S_IMP_FLY5, 0, 0}, // S_IMP_FLY4
+ {SPR_IMPX, 2, 3, A_Chase, S_IMP_FLY6, 0, 0}, // S_IMP_FLY5
+ {SPR_IMPX, 2, 3, A_Chase, S_IMP_FLY7, 0, 0}, // S_IMP_FLY6
+ {SPR_IMPX, 1, 3, A_Chase, S_IMP_FLY8, 0, 0}, // S_IMP_FLY7
+ {SPR_IMPX, 1, 3, A_Chase, S_IMP_FLY1, 0, 0}, // S_IMP_FLY8
+ {SPR_IMPX, 3, 6, A_FaceTarget, S_IMP_MEATK2, 0, 0}, // S_IMP_MEATK1
+ {SPR_IMPX, 4, 6, A_FaceTarget, S_IMP_MEATK3, 0, 0}, // S_IMP_MEATK2
+ {SPR_IMPX, 5, 6, A_ImpMeAttack, S_IMP_FLY1, 0, 0}, // S_IMP_MEATK3
+ {SPR_IMPX, 0, 10, A_FaceTarget, S_IMP_MSATK1_2, 0, 0}, // S_IMP_MSATK1_1
+ {SPR_IMPX, 1, 6, A_ImpMsAttack, S_IMP_MSATK1_3, 0, 0}, // S_IMP_MSATK1_2
+ {SPR_IMPX, 2, 6, NULL, S_IMP_MSATK1_4, 0, 0}, // S_IMP_MSATK1_3
+ {SPR_IMPX, 1, 6, NULL, S_IMP_MSATK1_5, 0, 0}, // S_IMP_MSATK1_4
+ {SPR_IMPX, 0, 6, NULL, S_IMP_MSATK1_6, 0, 0}, // S_IMP_MSATK1_5
+ {SPR_IMPX, 1, 6, NULL, S_IMP_MSATK1_3, 0, 0}, // S_IMP_MSATK1_6
+ {SPR_IMPX, 3, 6, A_FaceTarget, S_IMP_MSATK2_2, 0, 0}, // S_IMP_MSATK2_1
+ {SPR_IMPX, 4, 6, A_FaceTarget, S_IMP_MSATK2_3, 0, 0}, // S_IMP_MSATK2_2
+ {SPR_IMPX, 5, 6, A_ImpMsAttack2, S_IMP_FLY1, 0, 0}, // S_IMP_MSATK2_3
+ {SPR_IMPX, 6, 3, NULL, S_IMP_PAIN2, 0, 0}, // S_IMP_PAIN1
+ {SPR_IMPX, 6, 3, A_Pain, S_IMP_FLY1, 0, 0}, // S_IMP_PAIN2
+ {SPR_IMPX, 6, 4, A_ImpDeath, S_IMP_DIE2, 0, 0}, // S_IMP_DIE1
+ {SPR_IMPX, 7, 5, NULL, S_IMP_DIE2, 0, 0}, // S_IMP_DIE2
+ {SPR_IMPX, 18, 5, A_ImpXDeath1, S_IMP_XDIE2, 0, 0}, // S_IMP_XDIE1
+ {SPR_IMPX, 19, 5, NULL, S_IMP_XDIE3, 0, 0}, // S_IMP_XDIE2
+ {SPR_IMPX, 20, 5, NULL, S_IMP_XDIE4, 0, 0}, // S_IMP_XDIE3
+ {SPR_IMPX, 21, 5, A_ImpXDeath2, S_IMP_XDIE5, 0, 0}, // S_IMP_XDIE4
+ {SPR_IMPX, 22, 5, NULL, S_IMP_XDIE5, 0, 0}, // S_IMP_XDIE5
+ {SPR_IMPX, 8, 7, A_ImpExplode, S_IMP_CRASH2, 0, 0}, // S_IMP_CRASH1
+ {SPR_IMPX, 9, 7, A_Scream, S_IMP_CRASH3, 0, 0}, // S_IMP_CRASH2
+ {SPR_IMPX, 10, 7, NULL, S_IMP_CRASH4, 0, 0}, // S_IMP_CRASH3
+ {SPR_IMPX, 11, -1, NULL, S_NULL, 0, 0}, // S_IMP_CRASH4
+ {SPR_IMPX, 23, 7, NULL, S_IMP_XCRASH2, 0, 0}, // S_IMP_XCRASH1
+ {SPR_IMPX, 24, 7, NULL, S_IMP_XCRASH3, 0, 0}, // S_IMP_XCRASH2
+ {SPR_IMPX, 25, -1, NULL, S_NULL, 0, 0}, // S_IMP_XCRASH3
+ {SPR_IMPX, 12, 5, NULL, S_IMP_CHUNKA2, 0, 0}, // S_IMP_CHUNKA1
+ {SPR_IMPX, 13, 700, NULL, S_IMP_CHUNKA3, 0, 0}, // S_IMP_CHUNKA2
+ {SPR_IMPX, 14, 700, NULL, S_NULL, 0, 0}, // S_IMP_CHUNKA3
+ {SPR_IMPX, 15, 5, NULL, S_IMP_CHUNKB2, 0, 0}, // S_IMP_CHUNKB1
+ {SPR_IMPX, 16, 700, NULL, S_IMP_CHUNKB3, 0, 0}, // S_IMP_CHUNKB2
+ {SPR_IMPX, 17, 700, NULL, S_NULL, 0, 0}, // S_IMP_CHUNKB3
+ {SPR_FX10, 32768, 6, NULL, S_IMPFX2, 0, 0}, // S_IMPFX1
+ {SPR_FX10, 32769, 6, NULL, S_IMPFX3, 0, 0}, // S_IMPFX2
+ {SPR_FX10, 32770, 6, NULL, S_IMPFX1, 0, 0}, // S_IMPFX3
+ {SPR_FX10, 32771, 5, NULL, S_IMPFXI2, 0, 0}, // S_IMPFXI1
+ {SPR_FX10, 32772, 5, NULL, S_IMPFXI3, 0, 0}, // S_IMPFXI2
+ {SPR_FX10, 32773, 5, NULL, S_IMPFXI4, 0, 0}, // S_IMPFXI3
+ {SPR_FX10, 32774, 5, NULL, S_NULL, 0, 0}, // S_IMPFXI4
+ {SPR_KNIG, 0, 10, A_Look, S_KNIGHT_STND2, 0, 0}, // S_KNIGHT_STND1
+ {SPR_KNIG, 1, 10, A_Look, S_KNIGHT_STND1, 0, 0}, // S_KNIGHT_STND2
+ {SPR_KNIG, 0, 4, A_Chase, S_KNIGHT_WALK2, 0, 0}, // S_KNIGHT_WALK1
+ {SPR_KNIG, 1, 4, A_Chase, S_KNIGHT_WALK3, 0, 0}, // S_KNIGHT_WALK2
+ {SPR_KNIG, 2, 4, A_Chase, S_KNIGHT_WALK4, 0, 0}, // S_KNIGHT_WALK3
+ {SPR_KNIG, 3, 4, A_Chase, S_KNIGHT_WALK1, 0, 0}, // S_KNIGHT_WALK4
+ {SPR_KNIG, 4, 10, A_FaceTarget, S_KNIGHT_ATK2, 0, 0}, // S_KNIGHT_ATK1
+ {SPR_KNIG, 5, 8, A_FaceTarget, S_KNIGHT_ATK3, 0, 0}, // S_KNIGHT_ATK2
+ {SPR_KNIG, 6, 8, A_KnightAttack, S_KNIGHT_ATK4, 0, 0}, // S_KNIGHT_ATK3
+ {SPR_KNIG, 4, 10, A_FaceTarget, S_KNIGHT_ATK5, 0, 0}, // S_KNIGHT_ATK4
+ {SPR_KNIG, 5, 8, A_FaceTarget, S_KNIGHT_ATK6, 0, 0}, // S_KNIGHT_ATK5
+ {SPR_KNIG, 6, 8, A_KnightAttack, S_KNIGHT_WALK1, 0, 0}, // S_KNIGHT_ATK6
+ {SPR_KNIG, 7, 3, NULL, S_KNIGHT_PAIN2, 0, 0}, // S_KNIGHT_PAIN1
+ {SPR_KNIG, 7, 3, A_Pain, S_KNIGHT_WALK1, 0, 0}, // S_KNIGHT_PAIN2
+ {SPR_KNIG, 8, 6, NULL, S_KNIGHT_DIE2, 0, 0}, // S_KNIGHT_DIE1
+ {SPR_KNIG, 9, 6, A_Scream, S_KNIGHT_DIE3, 0, 0}, // S_KNIGHT_DIE2
+ {SPR_KNIG, 10, 6, NULL, S_KNIGHT_DIE4, 0, 0}, // S_KNIGHT_DIE3
+ {SPR_KNIG, 11, 6, A_NoBlocking, S_KNIGHT_DIE5, 0, 0}, // S_KNIGHT_DIE4
+ {SPR_KNIG, 12, 6, NULL, S_KNIGHT_DIE6, 0, 0}, // S_KNIGHT_DIE5
+ {SPR_KNIG, 13, 6, NULL, S_KNIGHT_DIE7, 0, 0}, // S_KNIGHT_DIE6
+ {SPR_KNIG, 14, -1, NULL, S_NULL, 0, 0}, // S_KNIGHT_DIE7
+ {SPR_SPAX, 32768, 3, A_ContMobjSound, S_SPINAXE2, 0, 0}, // S_SPINAXE1
+ {SPR_SPAX, 32769, 3, NULL, S_SPINAXE3, 0, 0}, // S_SPINAXE2
+ {SPR_SPAX, 32770, 3, NULL, S_SPINAXE1, 0, 0}, // S_SPINAXE3
+ {SPR_SPAX, 32771, 6, NULL, S_SPINAXEX2, 0, 0}, // S_SPINAXEX1
+ {SPR_SPAX, 32772, 6, NULL, S_SPINAXEX3, 0, 0}, // S_SPINAXEX2
+ {SPR_SPAX, 32773, 6, NULL, S_NULL, 0, 0}, // S_SPINAXEX3
+ {SPR_RAXE, 32768, 5, A_DripBlood, S_REDAXE2, 0, 0}, // S_REDAXE1
+ {SPR_RAXE, 32769, 5, A_DripBlood, S_REDAXE1, 0, 0}, // S_REDAXE2
+ {SPR_RAXE, 32770, 6, NULL, S_REDAXEX2, 0, 0}, // S_REDAXEX1
+ {SPR_RAXE, 32771, 6, NULL, S_REDAXEX3, 0, 0}, // S_REDAXEX2
+ {SPR_RAXE, 32772, 6, NULL, S_NULL, 0, 0}, // S_REDAXEX3
+ {SPR_SRCR, 0, 10, A_Look, S_SRCR1_LOOK2, 0, 0}, // S_SRCR1_LOOK1
+ {SPR_SRCR, 1, 10, A_Look, S_SRCR1_LOOK1, 0, 0}, // S_SRCR1_LOOK2
+ {SPR_SRCR, 0, 5, A_Sor1Chase, S_SRCR1_WALK2, 0, 0}, // S_SRCR1_WALK1
+ {SPR_SRCR, 1, 5, A_Sor1Chase, S_SRCR1_WALK3, 0, 0}, // S_SRCR1_WALK2
+ {SPR_SRCR, 2, 5, A_Sor1Chase, S_SRCR1_WALK4, 0, 0}, // S_SRCR1_WALK3
+ {SPR_SRCR, 3, 5, A_Sor1Chase, S_SRCR1_WALK1, 0, 0}, // S_SRCR1_WALK4
+ {SPR_SRCR, 16, 6, A_Sor1Pain, S_SRCR1_WALK1, 0, 0}, // S_SRCR1_PAIN1
+ {SPR_SRCR, 16, 7, A_FaceTarget, S_SRCR1_ATK2, 0, 0}, // S_SRCR1_ATK1
+ {SPR_SRCR, 17, 6, A_FaceTarget, S_SRCR1_ATK3, 0, 0}, // S_SRCR1_ATK2
+ {SPR_SRCR, 18, 10, A_Srcr1Attack, S_SRCR1_WALK1, 0, 0}, // S_SRCR1_ATK3
+ {SPR_SRCR, 18, 10, A_FaceTarget, S_SRCR1_ATK5, 0, 0}, // S_SRCR1_ATK4
+ {SPR_SRCR, 16, 7, A_FaceTarget, S_SRCR1_ATK6, 0, 0}, // S_SRCR1_ATK5
+ {SPR_SRCR, 17, 6, A_FaceTarget, S_SRCR1_ATK7, 0, 0}, // S_SRCR1_ATK6
+ {SPR_SRCR, 18, 10, A_Srcr1Attack, S_SRCR1_WALK1, 0, 0}, // S_SRCR1_ATK7
+ {SPR_SRCR, 4, 7, NULL, S_SRCR1_DIE2, 0, 0}, // S_SRCR1_DIE1
+ {SPR_SRCR, 5, 7, A_Scream, S_SRCR1_DIE3, 0, 0}, // S_SRCR1_DIE2
+ {SPR_SRCR, 6, 7, NULL, S_SRCR1_DIE4, 0, 0}, // S_SRCR1_DIE3
+ {SPR_SRCR, 7, 6, NULL, S_SRCR1_DIE5, 0, 0}, // S_SRCR1_DIE4
+ {SPR_SRCR, 8, 6, NULL, S_SRCR1_DIE6, 0, 0}, // S_SRCR1_DIE5
+ {SPR_SRCR, 9, 6, NULL, S_SRCR1_DIE7, 0, 0}, // S_SRCR1_DIE6
+ {SPR_SRCR, 10, 6, NULL, S_SRCR1_DIE8, 0, 0}, // S_SRCR1_DIE7
+ {SPR_SRCR, 11, 25, A_SorZap, S_SRCR1_DIE9, 0, 0}, // S_SRCR1_DIE8
+ {SPR_SRCR, 12, 5, NULL, S_SRCR1_DIE10, 0, 0}, // S_SRCR1_DIE9
+ {SPR_SRCR, 13, 5, NULL, S_SRCR1_DIE11, 0, 0}, // S_SRCR1_DIE10
+ {SPR_SRCR, 14, 4, NULL, S_SRCR1_DIE12, 0, 0}, // S_SRCR1_DIE11
+ {SPR_SRCR, 11, 20, A_SorZap, S_SRCR1_DIE13, 0, 0}, // S_SRCR1_DIE12
+ {SPR_SRCR, 12, 5, NULL, S_SRCR1_DIE14, 0, 0}, // S_SRCR1_DIE13
+ {SPR_SRCR, 13, 5, NULL, S_SRCR1_DIE15, 0, 0}, // S_SRCR1_DIE14
+ {SPR_SRCR, 14, 4, NULL, S_SRCR1_DIE16, 0, 0}, // S_SRCR1_DIE15
+ {SPR_SRCR, 11, 12, NULL, S_SRCR1_DIE17, 0, 0}, // S_SRCR1_DIE16
+ {SPR_SRCR, 15, -1, A_SorcererRise, S_NULL, 0, 0}, // S_SRCR1_DIE17
+ {SPR_FX14, 32768, 6, NULL, S_SRCRFX1_2, 0, 0}, // S_SRCRFX1_1
+ {SPR_FX14, 32769, 6, NULL, S_SRCRFX1_3, 0, 0}, // S_SRCRFX1_2
+ {SPR_FX14, 32770, 6, NULL, S_SRCRFX1_1, 0, 0}, // S_SRCRFX1_3
+ {SPR_FX14, 32771, 5, NULL, S_SRCRFXI1_2, 0, 0}, // S_SRCRFXI1_1
+ {SPR_FX14, 32772, 5, NULL, S_SRCRFXI1_3, 0, 0}, // S_SRCRFXI1_2
+ {SPR_FX14, 32773, 5, NULL, S_SRCRFXI1_4, 0, 0}, // S_SRCRFXI1_3
+ {SPR_FX14, 32774, 5, NULL, S_SRCRFXI1_5, 0, 0}, // S_SRCRFXI1_4
+ {SPR_FX14, 32775, 5, NULL, S_NULL, 0, 0}, // S_SRCRFXI1_5
+ {SPR_SOR2, 0, 4, NULL, S_SOR2_RISE2, 0, 0}, // S_SOR2_RISE1
+ {SPR_SOR2, 1, 4, NULL, S_SOR2_RISE3, 0, 0}, // S_SOR2_RISE2
+ {SPR_SOR2, 2, 4, A_SorRise, S_SOR2_RISE4, 0, 0}, // S_SOR2_RISE3
+ {SPR_SOR2, 3, 4, NULL, S_SOR2_RISE5, 0, 0}, // S_SOR2_RISE4
+ {SPR_SOR2, 4, 4, NULL, S_SOR2_RISE6, 0, 0}, // S_SOR2_RISE5
+ {SPR_SOR2, 5, 4, NULL, S_SOR2_RISE7, 0, 0}, // S_SOR2_RISE6
+ {SPR_SOR2, 6, 12, A_SorSightSnd, S_SOR2_WALK1, 0, 0}, // S_SOR2_RISE7
+ {SPR_SOR2, 12, 10, A_Look, S_SOR2_LOOK2, 0, 0}, // S_SOR2_LOOK1
+ {SPR_SOR2, 13, 10, A_Look, S_SOR2_LOOK1, 0, 0}, // S_SOR2_LOOK2
+ {SPR_SOR2, 12, 4, A_Chase, S_SOR2_WALK2, 0, 0}, // S_SOR2_WALK1
+ {SPR_SOR2, 13, 4, A_Chase, S_SOR2_WALK3, 0, 0}, // S_SOR2_WALK2
+ {SPR_SOR2, 14, 4, A_Chase, S_SOR2_WALK4, 0, 0}, // S_SOR2_WALK3
+ {SPR_SOR2, 15, 4, A_Chase, S_SOR2_WALK1, 0, 0}, // S_SOR2_WALK4
+ {SPR_SOR2, 16, 3, NULL, S_SOR2_PAIN2, 0, 0}, // S_SOR2_PAIN1
+ {SPR_SOR2, 16, 6, A_Pain, S_SOR2_WALK1, 0, 0}, // S_SOR2_PAIN2
+ {SPR_SOR2, 17, 9, A_Srcr2Decide, S_SOR2_ATK2, 0, 0}, // S_SOR2_ATK1
+ {SPR_SOR2, 18, 9, A_FaceTarget, S_SOR2_ATK3, 0, 0}, // S_SOR2_ATK2
+ {SPR_SOR2, 19, 20, A_Srcr2Attack, S_SOR2_WALK1, 0, 0}, // S_SOR2_ATK3
+ {SPR_SOR2, 11, 6, NULL, S_SOR2_TELE2, 0, 0}, // S_SOR2_TELE1
+ {SPR_SOR2, 10, 6, NULL, S_SOR2_TELE3, 0, 0}, // S_SOR2_TELE2
+ {SPR_SOR2, 9, 6, NULL, S_SOR2_TELE4, 0, 0}, // S_SOR2_TELE3
+ {SPR_SOR2, 8, 6, NULL, S_SOR2_TELE5, 0, 0}, // S_SOR2_TELE4
+ {SPR_SOR2, 7, 6, NULL, S_SOR2_TELE6, 0, 0}, // S_SOR2_TELE5
+ {SPR_SOR2, 6, 6, NULL, S_SOR2_WALK1, 0, 0}, // S_SOR2_TELE6
+ {SPR_SDTH, 0, 8, A_Sor2DthInit, S_SOR2_DIE2, 0, 0}, // S_SOR2_DIE1
+ {SPR_SDTH, 1, 8, NULL, S_SOR2_DIE3, 0, 0}, // S_SOR2_DIE2
+ {SPR_SDTH, 2, 8, A_SorDSph, S_SOR2_DIE4, 0, 0}, // S_SOR2_DIE3
+ {SPR_SDTH, 3, 7, NULL, S_SOR2_DIE5, 0, 0}, // S_SOR2_DIE4
+ {SPR_SDTH, 4, 7, NULL, S_SOR2_DIE6, 0, 0}, // S_SOR2_DIE5
+ {SPR_SDTH, 5, 7, A_Sor2DthLoop, S_SOR2_DIE7, 0, 0}, // S_SOR2_DIE6
+ {SPR_SDTH, 6, 6, A_SorDExp, S_SOR2_DIE8, 0, 0}, // S_SOR2_DIE7
+ {SPR_SDTH, 7, 6, NULL, S_SOR2_DIE9, 0, 0}, // S_SOR2_DIE8
+ {SPR_SDTH, 8, 18, NULL, S_SOR2_DIE10, 0, 0}, // S_SOR2_DIE9
+ {SPR_SDTH, 9, 6, A_NoBlocking, S_SOR2_DIE11, 0, 0}, // S_SOR2_DIE10
+ {SPR_SDTH, 10, 6, A_SorDBon, S_SOR2_DIE12, 0, 0}, // S_SOR2_DIE11
+ {SPR_SDTH, 11, 6, NULL, S_SOR2_DIE13, 0, 0}, // S_SOR2_DIE12
+ {SPR_SDTH, 12, 6, NULL, S_SOR2_DIE14, 0, 0}, // S_SOR2_DIE13
+ {SPR_SDTH, 13, 6, NULL, S_SOR2_DIE15, 0, 0}, // S_SOR2_DIE14
+ {SPR_SDTH, 14, -1, A_BossDeath, S_NULL, 0, 0}, // S_SOR2_DIE15
+ {SPR_FX16, 32768, 3, A_BlueSpark, S_SOR2FX1_2, 0, 0}, // S_SOR2FX1_1
+ {SPR_FX16, 32769, 3, A_BlueSpark, S_SOR2FX1_3, 0, 0}, // S_SOR2FX1_2
+ {SPR_FX16, 32770, 3, A_BlueSpark, S_SOR2FX1_1, 0, 0}, // S_SOR2FX1_3
+ {SPR_FX16, 32774, 5, A_Explode, S_SOR2FXI1_2, 0, 0}, // S_SOR2FXI1_1
+ {SPR_FX16, 32775, 5, NULL, S_SOR2FXI1_3, 0, 0}, // S_SOR2FXI1_2
+ {SPR_FX16, 32776, 5, NULL, S_SOR2FXI1_4, 0, 0}, // S_SOR2FXI1_3
+ {SPR_FX16, 32777, 5, NULL, S_SOR2FXI1_5, 0, 0}, // S_SOR2FXI1_4
+ {SPR_FX16, 32778, 5, NULL, S_SOR2FXI1_6, 0, 0}, // S_SOR2FXI1_5
+ {SPR_FX16, 32779, 5, NULL, S_NULL, 0, 0}, // S_SOR2FXI1_6
+ {SPR_FX16, 32771, 12, NULL, S_SOR2FXSPARK2, 0, 0}, // S_SOR2FXSPARK1
+ {SPR_FX16, 32772, 12, NULL, S_SOR2FXSPARK3, 0, 0}, // S_SOR2FXSPARK2
+ {SPR_FX16, 32773, 12, NULL, S_NULL, 0, 0}, // S_SOR2FXSPARK3
+ {SPR_FX11, 32768, 35, NULL, S_SOR2FX2_2, 0, 0}, // S_SOR2FX2_1
+ {SPR_FX11, 32768, 5, A_GenWizard, S_SOR2FX2_3, 0, 0}, // S_SOR2FX2_2
+ {SPR_FX11, 32769, 5, NULL, S_SOR2FX2_2, 0, 0}, // S_SOR2FX2_3
+ {SPR_FX11, 32770, 5, NULL, S_SOR2FXI2_2, 0, 0}, // S_SOR2FXI2_1
+ {SPR_FX11, 32771, 5, NULL, S_SOR2FXI2_3, 0, 0}, // S_SOR2FXI2_2
+ {SPR_FX11, 32772, 5, NULL, S_SOR2FXI2_4, 0, 0}, // S_SOR2FXI2_3
+ {SPR_FX11, 32773, 5, NULL, S_SOR2FXI2_5, 0, 0}, // S_SOR2FXI2_4
+ {SPR_FX11, 32774, 5, NULL, S_NULL, 0, 0}, // S_SOR2FXI2_5
+ {SPR_SOR2, 6, 8, NULL, S_SOR2TELEFADE2, 0, 0}, // S_SOR2TELEFADE1
+ {SPR_SOR2, 7, 6, NULL, S_SOR2TELEFADE3, 0, 0}, // S_SOR2TELEFADE2
+ {SPR_SOR2, 8, 6, NULL, S_SOR2TELEFADE4, 0, 0}, // S_SOR2TELEFADE3
+ {SPR_SOR2, 9, 6, NULL, S_SOR2TELEFADE5, 0, 0}, // S_SOR2TELEFADE4
+ {SPR_SOR2, 10, 6, NULL, S_SOR2TELEFADE6, 0, 0}, // S_SOR2TELEFADE5
+ {SPR_SOR2, 11, 6, NULL, S_NULL, 0, 0}, // S_SOR2TELEFADE6
+ {SPR_MNTR, 0, 10, A_Look, S_MNTR_LOOK2, 0, 0}, // S_MNTR_LOOK1
+ {SPR_MNTR, 1, 10, A_Look, S_MNTR_LOOK1, 0, 0}, // S_MNTR_LOOK2
+ {SPR_MNTR, 0, 5, A_Chase, S_MNTR_WALK2, 0, 0}, // S_MNTR_WALK1
+ {SPR_MNTR, 1, 5, A_Chase, S_MNTR_WALK3, 0, 0}, // S_MNTR_WALK2
+ {SPR_MNTR, 2, 5, A_Chase, S_MNTR_WALK4, 0, 0}, // S_MNTR_WALK3
+ {SPR_MNTR, 3, 5, A_Chase, S_MNTR_WALK1, 0, 0}, // S_MNTR_WALK4
+ {SPR_MNTR, 21, 10, A_FaceTarget, S_MNTR_ATK1_2, 0, 0}, // S_MNTR_ATK1_1
+ {SPR_MNTR, 22, 7, A_FaceTarget, S_MNTR_ATK1_3, 0, 0}, // S_MNTR_ATK1_2
+ {SPR_MNTR, 23, 12, A_MinotaurAtk1, S_MNTR_WALK1, 0, 0}, // S_MNTR_ATK1_3
+ {SPR_MNTR, 21, 10, A_MinotaurDecide, S_MNTR_ATK2_2, 0, 0}, // S_MNTR_ATK2_1
+ {SPR_MNTR, 24, 4, A_FaceTarget, S_MNTR_ATK2_3, 0, 0}, // S_MNTR_ATK2_2
+ {SPR_MNTR, 25, 9, A_MinotaurAtk2, S_MNTR_WALK1, 0, 0}, // S_MNTR_ATK2_3
+ {SPR_MNTR, 21, 10, A_FaceTarget, S_MNTR_ATK3_2, 0, 0}, // S_MNTR_ATK3_1
+ {SPR_MNTR, 22, 7, A_FaceTarget, S_MNTR_ATK3_3, 0, 0}, // S_MNTR_ATK3_2
+ {SPR_MNTR, 23, 12, A_MinotaurAtk3, S_MNTR_WALK1, 0, 0}, // S_MNTR_ATK3_3
+ {SPR_MNTR, 23, 12, NULL, S_MNTR_ATK3_1, 0, 0}, // S_MNTR_ATK3_4
+ {SPR_MNTR, 20, 2, A_MinotaurCharge, S_MNTR_ATK4_1, 0, 0}, // S_MNTR_ATK4_1
+ {SPR_MNTR, 4, 3, NULL, S_MNTR_PAIN2, 0, 0}, // S_MNTR_PAIN1
+ {SPR_MNTR, 4, 6, A_Pain, S_MNTR_WALK1, 0, 0}, // S_MNTR_PAIN2
+ {SPR_MNTR, 5, 6, NULL, S_MNTR_DIE2, 0, 0}, // S_MNTR_DIE1
+ {SPR_MNTR, 6, 5, NULL, S_MNTR_DIE3, 0, 0}, // S_MNTR_DIE2
+ {SPR_MNTR, 7, 6, A_Scream, S_MNTR_DIE4, 0, 0}, // S_MNTR_DIE3
+ {SPR_MNTR, 8, 5, NULL, S_MNTR_DIE5, 0, 0}, // S_MNTR_DIE4
+ {SPR_MNTR, 9, 6, NULL, S_MNTR_DIE6, 0, 0}, // S_MNTR_DIE5
+ {SPR_MNTR, 10, 5, NULL, S_MNTR_DIE7, 0, 0}, // S_MNTR_DIE6
+ {SPR_MNTR, 11, 6, NULL, S_MNTR_DIE8, 0, 0}, // S_MNTR_DIE7
+ {SPR_MNTR, 12, 5, A_NoBlocking, S_MNTR_DIE9, 0, 0}, // S_MNTR_DIE8
+ {SPR_MNTR, 13, 6, NULL, S_MNTR_DIE10, 0, 0}, // S_MNTR_DIE9
+ {SPR_MNTR, 14, 5, NULL, S_MNTR_DIE11, 0, 0}, // S_MNTR_DIE10
+ {SPR_MNTR, 15, 6, NULL, S_MNTR_DIE12, 0, 0}, // S_MNTR_DIE11
+ {SPR_MNTR, 16, 5, NULL, S_MNTR_DIE13, 0, 0}, // S_MNTR_DIE12
+ {SPR_MNTR, 17, 6, NULL, S_MNTR_DIE14, 0, 0}, // S_MNTR_DIE13
+ {SPR_MNTR, 18, 5, NULL, S_MNTR_DIE15, 0, 0}, // S_MNTR_DIE14
+ {SPR_MNTR, 19, -1, A_BossDeath, S_NULL, 0, 0}, // S_MNTR_DIE15
+ {SPR_FX12, 32768, 6, NULL, S_MNTRFX1_2, 0, 0}, // S_MNTRFX1_1
+ {SPR_FX12, 32769, 6, NULL, S_MNTRFX1_1, 0, 0}, // S_MNTRFX1_2
+ {SPR_FX12, 32770, 5, NULL, S_MNTRFXI1_2, 0, 0}, // S_MNTRFXI1_1
+ {SPR_FX12, 32771, 5, NULL, S_MNTRFXI1_3, 0, 0}, // S_MNTRFXI1_2
+ {SPR_FX12, 32772, 5, NULL, S_MNTRFXI1_4, 0, 0}, // S_MNTRFXI1_3
+ {SPR_FX12, 32773, 5, NULL, S_MNTRFXI1_5, 0, 0}, // S_MNTRFXI1_4
+ {SPR_FX12, 32774, 5, NULL, S_MNTRFXI1_6, 0, 0}, // S_MNTRFXI1_5
+ {SPR_FX12, 32775, 5, NULL, S_NULL, 0, 0}, // S_MNTRFXI1_6
+ {SPR_FX13, 0, 2, A_MntrFloorFire, S_MNTRFX2_1, 0, 0}, // S_MNTRFX2_1
+ {SPR_FX13, 32776, 4, A_Explode, S_MNTRFXI2_2, 0, 0}, // S_MNTRFXI2_1
+ {SPR_FX13, 32777, 4, NULL, S_MNTRFXI2_3, 0, 0}, // S_MNTRFXI2_2
+ {SPR_FX13, 32778, 4, NULL, S_MNTRFXI2_4, 0, 0}, // S_MNTRFXI2_3
+ {SPR_FX13, 32779, 4, NULL, S_MNTRFXI2_5, 0, 0}, // S_MNTRFXI2_4
+ {SPR_FX13, 32780, 4, NULL, S_NULL, 0, 0}, // S_MNTRFXI2_5
+ {SPR_FX13, 32771, 4, NULL, S_MNTRFX3_2, 0, 0}, // S_MNTRFX3_1
+ {SPR_FX13, 32770, 4, NULL, S_MNTRFX3_3, 0, 0}, // S_MNTRFX3_2
+ {SPR_FX13, 32769, 5, NULL, S_MNTRFX3_4, 0, 0}, // S_MNTRFX3_3
+ {SPR_FX13, 32770, 5, NULL, S_MNTRFX3_5, 0, 0}, // S_MNTRFX3_4
+ {SPR_FX13, 32771, 5, NULL, S_MNTRFX3_6, 0, 0}, // S_MNTRFX3_5
+ {SPR_FX13, 32772, 5, NULL, S_MNTRFX3_7, 0, 0}, // S_MNTRFX3_6
+ {SPR_FX13, 32773, 4, NULL, S_MNTRFX3_8, 0, 0}, // S_MNTRFX3_7
+ {SPR_FX13, 32774, 4, NULL, S_MNTRFX3_9, 0, 0}, // S_MNTRFX3_8
+ {SPR_FX13, 32775, 4, NULL, S_NULL, 0, 0}, // S_MNTRFX3_9
+ {SPR_AKYY, 32768, 3, NULL, S_AKYY2, 0, 0}, // S_AKYY1
+ {SPR_AKYY, 32769, 3, NULL, S_AKYY3, 0, 0}, // S_AKYY2
+ {SPR_AKYY, 32770, 3, NULL, S_AKYY4, 0, 0}, // S_AKYY3
+ {SPR_AKYY, 32771, 3, NULL, S_AKYY5, 0, 0}, // S_AKYY4
+ {SPR_AKYY, 32772, 3, NULL, S_AKYY6, 0, 0}, // S_AKYY5
+ {SPR_AKYY, 32773, 3, NULL, S_AKYY7, 0, 0}, // S_AKYY6
+ {SPR_AKYY, 32774, 3, NULL, S_AKYY8, 0, 0}, // S_AKYY7
+ {SPR_AKYY, 32775, 3, NULL, S_AKYY9, 0, 0}, // S_AKYY8
+ {SPR_AKYY, 32776, 3, NULL, S_AKYY10, 0, 0}, // S_AKYY9
+ {SPR_AKYY, 32777, 3, NULL, S_AKYY1, 0, 0}, // S_AKYY10
+ {SPR_BKYY, 32768, 3, NULL, S_BKYY2, 0, 0}, // S_BKYY1
+ {SPR_BKYY, 32769, 3, NULL, S_BKYY3, 0, 0}, // S_BKYY2
+ {SPR_BKYY, 32770, 3, NULL, S_BKYY4, 0, 0}, // S_BKYY3
+ {SPR_BKYY, 32771, 3, NULL, S_BKYY5, 0, 0}, // S_BKYY4
+ {SPR_BKYY, 32772, 3, NULL, S_BKYY6, 0, 0}, // S_BKYY5
+ {SPR_BKYY, 32773, 3, NULL, S_BKYY7, 0, 0}, // S_BKYY6
+ {SPR_BKYY, 32774, 3, NULL, S_BKYY8, 0, 0}, // S_BKYY7
+ {SPR_BKYY, 32775, 3, NULL, S_BKYY9, 0, 0}, // S_BKYY8
+ {SPR_BKYY, 32776, 3, NULL, S_BKYY10, 0, 0}, // S_BKYY9
+ {SPR_BKYY, 32777, 3, NULL, S_BKYY1, 0, 0}, // S_BKYY10
+ {SPR_CKYY, 32768, 3, NULL, S_CKYY2, 0, 0}, // S_CKYY1
+ {SPR_CKYY, 32769, 3, NULL, S_CKYY3, 0, 0}, // S_CKYY2
+ {SPR_CKYY, 32770, 3, NULL, S_CKYY4, 0, 0}, // S_CKYY3
+ {SPR_CKYY, 32771, 3, NULL, S_CKYY5, 0, 0}, // S_CKYY4
+ {SPR_CKYY, 32772, 3, NULL, S_CKYY6, 0, 0}, // S_CKYY5
+ {SPR_CKYY, 32773, 3, NULL, S_CKYY7, 0, 0}, // S_CKYY6
+ {SPR_CKYY, 32774, 3, NULL, S_CKYY8, 0, 0}, // S_CKYY7
+ {SPR_CKYY, 32775, 3, NULL, S_CKYY9, 0, 0}, // S_CKYY8
+ {SPR_CKYY, 32776, 3, NULL, S_CKYY1, 0, 0}, // S_CKYY9
+ {SPR_AMG1, 0, -1, NULL, S_NULL, 0, 0}, // S_AMG1
+ {SPR_AMG2, 0, 4, NULL, S_AMG2_2, 0, 0}, // S_AMG2_1
+ {SPR_AMG2, 1, 4, NULL, S_AMG2_3, 0, 0}, // S_AMG2_2
+ {SPR_AMG2, 2, 4, NULL, S_AMG2_1, 0, 0}, // S_AMG2_3
+ {SPR_AMM1, 0, -1, NULL, S_NULL, 0, 0}, // S_AMM1
+ {SPR_AMM2, 0, -1, NULL, S_NULL, 0, 0}, // S_AMM2
+ {SPR_AMC1, 0, -1, NULL, S_NULL, 0, 0}, // S_AMC1
+ {SPR_AMC2, 0, 5, NULL, S_AMC2_2, 0, 0}, // S_AMC2_1
+ {SPR_AMC2, 1, 5, NULL, S_AMC2_3, 0, 0}, // S_AMC2_2
+ {SPR_AMC2, 2, 5, NULL, S_AMC2_1, 0, 0}, // S_AMC2_3
+ {SPR_AMS1, 0, 5, NULL, S_AMS1_2, 0, 0}, // S_AMS1_1
+ {SPR_AMS1, 1, 5, NULL, S_AMS1_1, 0, 0}, // S_AMS1_2
+ {SPR_AMS2, 0, 5, NULL, S_AMS2_2, 0, 0}, // S_AMS2_1
+ {SPR_AMS2, 1, 5, NULL, S_AMS2_1, 0, 0}, // S_AMS2_2
+ {SPR_AMP1, 0, 4, NULL, S_AMP1_2, 0, 0}, // S_AMP1_1
+ {SPR_AMP1, 1, 4, NULL, S_AMP1_3, 0, 0}, // S_AMP1_2
+ {SPR_AMP1, 2, 4, NULL, S_AMP1_1, 0, 0}, // S_AMP1_3
+ {SPR_AMP2, 0, 4, NULL, S_AMP2_2, 0, 0}, // S_AMP2_1
+ {SPR_AMP2, 1, 4, NULL, S_AMP2_3, 0, 0}, // S_AMP2_2
+ {SPR_AMP2, 2, 4, NULL, S_AMP2_1, 0, 0}, // S_AMP2_3
+ {SPR_AMB1, 0, 4, NULL, S_AMB1_2, 0, 0}, // S_AMB1_1
+ {SPR_AMB1, 1, 4, NULL, S_AMB1_3, 0, 0}, // S_AMB1_2
+ {SPR_AMB1, 2, 4, NULL, S_AMB1_1, 0, 0}, // S_AMB1_3
+ {SPR_AMB2, 0, 4, NULL, S_AMB2_2, 0, 0}, // S_AMB2_1
+ {SPR_AMB2, 1, 4, NULL, S_AMB2_3, 0, 0}, // S_AMB2_2
+ {SPR_AMB2, 2, 4, NULL, S_AMB2_1, 0, 0}, // S_AMB2_3
+ {SPR_AMG1, 0, 100, A_ESound, S_SND_WIND, 0, 0}, // S_SND_WIND
+ {SPR_AMG1, 0, 85, A_ESound, S_SND_WATERFALL, 0, 0} // S_SND_WATERFALL
+};
+
+
+mobjinfo_t mobjinfo[NUMMOBJTYPES] = {
+
+ { // MT_MISC0
+ 81, // doomednum
+ S_ITEM_PTN1_1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_ITEMSHIELD1
+ 85, // doomednum
+ S_ITEM_SHLD1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_ITEMSHIELD2
+ 31, // doomednum
+ S_ITEM_SHD2_1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_MISC1
+ 8, // doomednum
+ S_ITEM_BAGH1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_MISC2
+ 35, // doomednum
+ S_ITEM_SPMP1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_ARTIINVISIBILITY
+ 75, // doomednum
+ S_ARTI_INVS1, // 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, // crashstate
+ 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_SHADOW | MF_COUNTITEM, // flags
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_MISC3
+ 82, // doomednum
+ S_ARTI_PTN2_1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_ARTIFLY
+ 83, // doomednum
+ S_ARTI_SOAR1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_ARTIINVULNERABILITY
+ 84, // doomednum
+ S_ARTI_INVU1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_ARTITOMEOFPOWER
+ 86, // doomednum
+ S_ARTI_PWBK1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_ARTIEGG
+ 30, // doomednum
+ S_ARTI_EGGC1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_EGGFX
+ -1, // doomednum
+ S_EGGFX1, // 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_NULL, // crashstate
+ S_EGGFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 18 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 1, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_ARTISUPERHEAL
+ 32, // doomednum
+ S_ARTI_SPHL1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_MISC4
+ 33, // doomednum
+ S_ARTI_TRCH1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_MISC5
+ 34, // doomednum
+ S_ARTI_FBMB1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_FIREBOMB
+ -1, // doomednum
+ S_FIREBOMB1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_phohit, // deathsound
+ 0, // speed
+ 20 * FRACUNIT, // radius
+ 16 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOGRAVITY | MF_SHADOW, // flags
+ 0 // flags2
+ },
+
+ { // MT_ARTITELEPORT
+ 36, // doomednum
+ S_ARTI_ATLP1, // 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, // crashstate
+ 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
+ MF2_FLOATBOB // flags2
+ },
+
+ { // MT_POD
+ 2035, // doomednum
+ S_POD_WAIT1, // spawnstate
+ 45, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_POD_PAIN1, // painstate
+ 255, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ S_POD_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_podexp, // deathsound
+ 0, // speed
+ 16 * FRACUNIT, // radius
+ 54 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID | MF_NOBLOOD | MF_SHOOTABLE | MF_DROPOFF, // flags
+ MF2_WINDTHRUST | MF2_PUSHABLE | MF2_SLIDE | MF2_PASSMOBJ | MF2_TELESTOMP // flags2
+ },
+
+ { // MT_PODGOO
+ -1, // doomednum
+ S_PODGOO1, // 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, // crashstate
+ S_PODGOOX, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 2 * FRACUNIT, // radius
+ 4 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH // flags2
+ },
+
+ { // MT_PODGENERATOR
+ 43, // doomednum
+ S_PODGENERATOR, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_SPLASH
+ -1, // doomednum
+ S_SPLASH1, // 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, // crashstate
+ S_SPLASHX, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 2 * FRACUNIT, // radius
+ 4 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH // flags2
+ },
+
+ { // MT_SPLASHBASE
+ -1, // doomednum
+ S_SPLASHBASE1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_LAVASPLASH
+ -1, // doomednum
+ S_LAVASPLASH1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_LAVASMOKE
+ -1, // doomednum
+ S_LAVASMOKE1, // 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, // crashstate
+ 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_SHADOW, // flags
+ 0 // flags2
+ },
+
+ { // MT_SLUDGECHUNK
+ -1, // doomednum
+ S_SLUDGECHUNK1, // 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, // crashstate
+ S_SLUDGECHUNKX, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 2 * FRACUNIT, // radius
+ 4 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH // flags2
+ },
+
+ { // MT_SLUDGESPLASH
+ -1, // doomednum
+ S_SLUDGESPLASH1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_SKULLHANG70
+ 17, // doomednum
+ S_SKULLHANG70_1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 20 * FRACUNIT, // radius
+ 70 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_SKULLHANG60
+ 24, // doomednum
+ S_SKULLHANG60_1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 20 * FRACUNIT, // radius
+ 60 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_SKULLHANG45
+ 25, // doomednum
+ S_SKULLHANG45_1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 20 * FRACUNIT, // radius
+ 45 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_SKULLHANG35
+ 26, // doomednum
+ S_SKULLHANG35_1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 20 * FRACUNIT, // radius
+ 35 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_CHANDELIER
+ 28, // doomednum
+ S_CHANDELIER1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 20 * FRACUNIT, // radius
+ 60 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_SERPTORCH
+ 27, // doomednum
+ S_SERPTORCH1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 12 * FRACUNIT, // radius
+ 54 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_SMALLPILLAR
+ 29, // doomednum
+ S_SMALLPILLAR, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 16 * FRACUNIT, // radius
+ 34 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_STALAGMITESMALL
+ 37, // doomednum
+ S_STALAGMITESMALL, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 8 * FRACUNIT, // radius
+ 32 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_STALAGMITELARGE
+ 38, // doomednum
+ S_STALAGMITELARGE, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 12 * FRACUNIT, // radius
+ 64 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_STALACTITESMALL
+ 39, // doomednum
+ S_STALACTITESMALL, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 8 * FRACUNIT, // radius
+ 36 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_STALACTITELARGE
+ 40, // doomednum
+ S_STALACTITELARGE, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 12 * FRACUNIT, // radius
+ 68 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_MISC6
+ 76, // doomednum
+ S_FIREBRAZIER1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 16 * FRACUNIT, // radius
+ 44 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_BARREL
+ 44, // doomednum
+ S_BARREL, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 12 * FRACUNIT, // radius
+ 32 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_MISC7
+ 47, // doomednum
+ S_BRPILLAR, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 14 * FRACUNIT, // radius
+ 128 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_MISC8
+ 48, // doomednum
+ S_MOSS1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 20 * FRACUNIT, // radius
+ 23 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_MISC9
+ 49, // doomednum
+ S_MOSS2, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 20 * FRACUNIT, // radius
+ 27 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_MISC10
+ 50, // doomednum
+ S_WALLTORCH1, // 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, // crashstate
+ 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_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_MISC11
+ 51, // doomednum
+ S_HANGINGCORPSE, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 8 * FRACUNIT, // radius
+ 104 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID | MF_SPAWNCEILING | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_KEYGIZMOBLUE
+ 94, // doomednum
+ S_KEYGIZMO1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 16 * FRACUNIT, // radius
+ 50 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_KEYGIZMOGREEN
+ 95, // doomednum
+ S_KEYGIZMO1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 16 * FRACUNIT, // radius
+ 50 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_KEYGIZMOYELLOW
+ 96, // doomednum
+ S_KEYGIZMO1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 16 * FRACUNIT, // radius
+ 50 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_KEYGIZMOFLOAT
+ -1, // doomednum
+ S_KGZ_START, // 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, // crashstate
+ 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 | MF_NOGRAVITY, // flags
+ 0 // flags2
+ },
+
+ { // MT_MISC12
+ 87, // doomednum
+ S_VOLCANO1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 12 * FRACUNIT, // radius
+ 20 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID, // flags
+ 0 // flags2
+ },
+
+ { // MT_VOLCANOBLAST
+ -1, // doomednum
+ S_VOLCANOBALL1, // 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, // crashstate
+ S_VOLCANOBALLX1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_volhit, // deathsound
+ 2 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_LOGRAV | MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2
+ },
+
+ { // MT_VOLCANOTBLAST
+ -1, // doomednum
+ S_VOLCANOTBALL1, // 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, // crashstate
+ S_VOLCANOTBALLX1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 2 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 1, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_LOGRAV | MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2
+ },
+
+ { // MT_TELEGLITGEN
+ 74, // doomednum
+ S_TELEGLITGEN1, // 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, // crashstate
+ 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_NOSECTOR, // flags
+ 0 // flags2
+ },
+
+ { // MT_TELEGLITGEN2
+ 52, // doomednum
+ S_TELEGLITGEN2, // 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, // crashstate
+ 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_NOSECTOR, // flags
+ 0 // flags2
+ },
+
+ { // MT_TELEGLITTER
+ -1, // doomednum
+ S_TELEGLITTER1_1, // 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, // crashstate
+ 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_MISSILE, // flags
+ 0 // flags2
+ },
+
+ { // MT_TELEGLITTER2
+ -1, // doomednum
+ S_TELEGLITTER2_1, // 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, // crashstate
+ 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_MISSILE, // flags
+ 0 // flags2
+ },
+
+ { // MT_TFOG
+ -1, // doomednum
+ S_TFOG1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_STAFFPUFF
+ -1, // doomednum
+ S_STAFFPUFF1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_stfhit, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_STAFFPUFF2
+ -1, // doomednum
+ S_STAFFPUFF2_1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_stfpow, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_BEAKPUFF
+ -1, // doomednum
+ S_STAFFPUFF1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_chicatk, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_MISC13
+ 2005, // doomednum
+ S_WGNT, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_GAUNTLETPUFF1
+ -1, // doomednum
+ S_GAUNTLETPUFF1_1, // 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, // crashstate
+ 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_SHADOW, // flags
+ 0 // flags2
+ },
+
+ { // MT_GAUNTLETPUFF2
+ -1, // doomednum
+ S_GAUNTLETPUFF2_1, // 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, // crashstate
+ 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_SHADOW, // flags
+ 0 // flags2
+ },
+
+ { // MT_MISC14
+ 53, // doomednum
+ S_BLSR, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_BLASTERFX1
+ -1, // doomednum
+ S_BLASTERFX1_1, // 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, // crashstate
+ S_BLASTERFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_blshit, // deathsound
+ 184 * FRACUNIT, // speed
+ 12 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_BLASTERSMOKE
+ -1, // doomednum
+ S_BLASTERSMOKE1, // 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, // crashstate
+ 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_SHADOW, // flags
+ MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2
+ },
+
+ { // MT_RIPPER
+ -1, // doomednum
+ S_RIPPER1, // 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_NULL, // crashstate
+ S_RIPPERX1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_hrnhit, // deathsound
+ 14 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 1, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT | MF2_RIP // flags2
+ },
+
+ { // MT_BLASTERPUFF1
+ -1, // doomednum
+ S_BLASTERPUFF1_1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_BLASTERPUFF2
+ -1, // doomednum
+ S_BLASTERPUFF2_1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_WMACE
+ 2002, // doomednum
+ S_WMCE, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_MACEFX1
+ -1, // doomednum
+ S_MACEFX1_1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_lobsht, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ S_MACEFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 20 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_FLOORBOUNCE | MF2_THRUGHOST | MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_MACEFX2
+ -1, // doomednum
+ S_MACEFX2_1, // 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_NULL, // crashstate
+ S_MACEFXI2_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 10 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 6, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_LOGRAV | MF2_FLOORBOUNCE | MF2_THRUGHOST | MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_MACEFX3
+ -1, // doomednum
+ S_MACEFX3_1, // 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_NULL, // crashstate
+ S_MACEFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 7 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 4, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_LOGRAV | MF2_FLOORBOUNCE | MF2_THRUGHOST | MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_MACEFX4
+ -1, // doomednum
+ S_MACEFX4_1, // 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_NULL, // crashstate
+ S_MACEFXI4_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 7 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 18, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_LOGRAV | MF2_FLOORBOUNCE | MF2_THRUGHOST | MF2_TELESTOMP // flags2
+ },
+
+ { // MT_WSKULLROD
+ 2004, // doomednum
+ S_WSKL, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_HORNRODFX1
+ -1, // doomednum
+ S_HRODFX1_1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_hrnsht, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ S_HRODFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_hrnhit, // deathsound
+ 22 * FRACUNIT, // speed
+ 12 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 3, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_WINDTHRUST | MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_HORNRODFX2
+ -1, // doomednum
+ S_HRODFX2_1, // spawnstate
+ 4 * 35, // spawnhealth
+ S_NULL, // seestate
+ sfx_hrnsht, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ S_HRODFXI2_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_ramphit, // deathsound
+ 22 * FRACUNIT, // speed
+ 12 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 10, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_RAINPLR1
+ -1, // doomednum
+ S_RAINPLR1_1, // 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_NULL, // crashstate
+ S_RAINPLR1X_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 12 * FRACUNIT, // speed
+ 5 * FRACUNIT, // radius
+ 12 * FRACUNIT, // height
+ 100, // mass
+ 5, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_RAINPLR2
+ -1, // doomednum
+ S_RAINPLR2_1, // 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_NULL, // crashstate
+ S_RAINPLR2X_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 12 * FRACUNIT, // speed
+ 5 * FRACUNIT, // radius
+ 12 * FRACUNIT, // height
+ 100, // mass
+ 5, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_RAINPLR3
+ -1, // doomednum
+ S_RAINPLR3_1, // 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_NULL, // crashstate
+ S_RAINPLR3X_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 12 * FRACUNIT, // speed
+ 5 * FRACUNIT, // radius
+ 12 * FRACUNIT, // height
+ 100, // mass
+ 5, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_RAINPLR4
+ -1, // doomednum
+ S_RAINPLR4_1, // 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_NULL, // crashstate
+ S_RAINPLR4X_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 12 * FRACUNIT, // speed
+ 5 * FRACUNIT, // radius
+ 12 * FRACUNIT, // height
+ 100, // mass
+ 5, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_GOLDWANDFX1
+ -1, // doomednum
+ S_GWANDFX1_1, // 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_NULL, // crashstate
+ S_GWANDFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_gldhit, // deathsound
+ 22 * FRACUNIT, // speed
+ 10 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_GOLDWANDFX2
+ -1, // doomednum
+ S_GWANDFX2_1, // 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_NULL, // crashstate
+ S_GWANDFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 18 * FRACUNIT, // speed
+ 10 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 1, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_GOLDWANDPUFF1
+ -1, // doomednum
+ S_GWANDPUFF1_1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_GOLDWANDPUFF2
+ -1, // doomednum
+ S_GWANDFXI1_1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_WPHOENIXROD
+ 2003, // doomednum
+ S_WPHX, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_PHOENIXFX1
+ -1, // doomednum
+ S_PHOENIXFX1_1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_phosht, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ S_PHOENIXFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_phohit, // 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
+ MF2_THRUGHOST | MF2_NOTELEPORT // flags2
+ },
+
+ // The following thing is present in the mobjinfo table from Heretic 1.0,
+ // but not in Heretic 1.3 (ie. it was removed). It has been re-inserted
+ // here to support HHE patches.
+
+ { // MT_PHOENIXFX_REMOVED
+ -1, // doomednum
+ S_PHOENIXFXIX_1, // 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, // crashstate
+ S_PHOENIXFXIX_3, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 2 * FRACUNIT, // radius
+ 4 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_PHOENIXPUFF
+ -1, // doomednum
+ S_PHOENIXPUFF1, // 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, // crashstate
+ 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_SHADOW, // flags
+ MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2
+ },
+
+ { // MT_PHOENIXFX2
+ -1, // doomednum
+ S_PHOENIXFX2_1, // 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_NULL, // crashstate
+ S_PHOENIXFXI2_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 10 * FRACUNIT, // speed
+ 6 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2
+ },
+
+ { // MT_MISC15
+ 2001, // doomednum
+ S_WBOW, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_CRBOWFX1
+ -1, // doomednum
+ S_CRBOWFX1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_bowsht, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ S_CRBOWFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_hrnhit, // deathsound
+ 30 * FRACUNIT, // speed
+ 11 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 10, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_CRBOWFX2
+ -1, // doomednum
+ S_CRBOWFX2, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_bowsht, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // crashstate
+ S_CRBOWFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_hrnhit, // deathsound
+ 32 * FRACUNIT, // speed
+ 11 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 6, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_CRBOWFX3
+ -1, // doomednum
+ S_CRBOWFX3, // 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_NULL, // crashstate
+ S_CRBOWFXI3_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_hrnhit, // deathsound
+ 20 * FRACUNIT, // speed
+ 11 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_WINDTHRUST | MF2_THRUGHOST | MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_CRBOWFX4
+ -1, // doomednum
+ S_CRBOWFX4_1, // 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, // crashstate
+ 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
+ MF2_LOGRAV // flags2
+ },
+
+ { // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_BLOODSPLATTER
+ -1, // doomednum
+ S_BLOODSPLATTER1, // 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, // crashstate
+ S_BLOODSPLATTERX, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 2 * FRACUNIT, // radius
+ 4 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2
+ },
+
+ { // 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_plrpai, // painsound
+ S_NULL, // meleestate
+ S_PLAY_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_PLAY_DIE1, // deathstate
+ S_PLAY_XDIE1, // xdeathstate
+ sfx_plrdth, // 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
+ MF2_WINDTHRUST | MF2_FOOTCLIP | MF2_SLIDE | MF2_PASSMOBJ | MF2_TELESTOMP // flags2
+ },
+
+ { // MT_BLOODYSKULL
+ -1, // doomednum
+ S_BLOODYSKULL1, // 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, // crashstate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 4 * FRACUNIT, // radius
+ 4 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_DROPOFF, // flags
+ MF2_LOGRAV | MF2_CANNOTPUSH // flags2
+ },
+
+ { // MT_CHICPLAYER
+ -1, // doomednum
+ S_CHICPLAY, // spawnstate
+ 100, // spawnhealth
+ S_CHICPLAY_RUN1, // seestate
+ sfx_None, // seesound
+ 0, // reactiontime
+ sfx_None, // attacksound
+ S_CHICPLAY_PAIN, // painstate
+ 255, // painchance
+ sfx_chicpai, // painsound
+ S_NULL, // meleestate
+ S_CHICPLAY_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_CHICKEN_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_chicdth, // deathsound
+ 0, // speed
+ 16 * FRACUNIT, // radius
+ 24 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_DROPOFF | MF_NOTDMATCH, // flags
+ MF2_WINDTHRUST | MF2_SLIDE | MF2_PASSMOBJ | MF2_FOOTCLIP | MF2_LOGRAV | MF2_TELESTOMP // flags2
+ },
+
+ { // MT_CHICKEN
+ -1, // doomednum
+ S_CHICKEN_LOOK1, // spawnstate
+ 10, // spawnhealth
+ S_CHICKEN_WALK1, // seestate
+ sfx_chicpai, // seesound
+ 8, // reactiontime
+ sfx_chicatk, // attacksound
+ S_CHICKEN_PAIN1, // painstate
+ 200, // painchance
+ sfx_chicpai, // painsound
+ S_CHICKEN_ATK1, // meleestate
+ 0, // missilestate
+ S_NULL, // crashstate
+ S_CHICKEN_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_chicdth, // deathsound
+ 4, // speed
+ 9 * FRACUNIT, // radius
+ 22 * FRACUNIT, // height
+ 40, // mass
+ 0, // damage
+ sfx_chicact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_DROPOFF, // flags
+ MF2_WINDTHRUST | MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_FEATHER
+ -1, // doomednum
+ S_FEATHER1, // 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, // crashstate
+ S_FEATHERX, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 2 * FRACUNIT, // radius
+ 4 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF, // flags
+ MF2_NOTELEPORT | MF2_LOGRAV | MF2_CANNOTPUSH | MF2_WINDTHRUST // flags2
+ },
+
+ { // MT_MUMMY
+ 68, // doomednum
+ S_MUMMY_LOOK1, // spawnstate
+ 80, // spawnhealth
+ S_MUMMY_WALK1, // seestate
+ sfx_mumsit, // seesound
+ 8, // reactiontime
+ sfx_mumat1, // attacksound
+ S_MUMMY_PAIN1, // painstate
+ 128, // painchance
+ sfx_mumpai, // painsound
+ S_MUMMY_ATK1, // meleestate
+ 0, // missilestate
+ S_NULL, // crashstate
+ S_MUMMY_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_mumdth, // deathsound
+ 12, // speed
+ 22 * FRACUNIT, // radius
+ 62 * FRACUNIT, // height
+ 75, // mass
+ 0, // damage
+ sfx_mumact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_MUMMYLEADER
+ 45, // doomednum
+ S_MUMMY_LOOK1, // spawnstate
+ 100, // spawnhealth
+ S_MUMMY_WALK1, // seestate
+ sfx_mumsit, // seesound
+ 8, // reactiontime
+ sfx_mumat1, // attacksound
+ S_MUMMY_PAIN1, // painstate
+ 64, // painchance
+ sfx_mumpai, // painsound
+ S_MUMMY_ATK1, // meleestate
+ S_MUMMYL_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_MUMMY_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_mumdth, // deathsound
+ 12, // speed
+ 22 * FRACUNIT, // radius
+ 62 * FRACUNIT, // height
+ 75, // mass
+ 0, // damage
+ sfx_mumact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_MUMMYGHOST
+ 69, // doomednum
+ S_MUMMY_LOOK1, // spawnstate
+ 80, // spawnhealth
+ S_MUMMY_WALK1, // seestate
+ sfx_mumsit, // seesound
+ 8, // reactiontime
+ sfx_mumat1, // attacksound
+ S_MUMMY_PAIN1, // painstate
+ 128, // painchance
+ sfx_mumpai, // painsound
+ S_MUMMY_ATK1, // meleestate
+ 0, // missilestate
+ S_NULL, // crashstate
+ S_MUMMY_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_mumdth, // deathsound
+ 12, // speed
+ 22 * FRACUNIT, // radius
+ 62 * FRACUNIT, // height
+ 75, // mass
+ 0, // damage
+ sfx_mumact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_SHADOW, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_MUMMYLEADERGHOST
+ 46, // doomednum
+ S_MUMMY_LOOK1, // spawnstate
+ 100, // spawnhealth
+ S_MUMMY_WALK1, // seestate
+ sfx_mumsit, // seesound
+ 8, // reactiontime
+ sfx_mumat1, // attacksound
+ S_MUMMY_PAIN1, // painstate
+ 64, // painchance
+ sfx_mumpai, // painsound
+ S_MUMMY_ATK1, // meleestate
+ S_MUMMYL_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_MUMMY_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_mumdth, // deathsound
+ 12, // speed
+ 22 * FRACUNIT, // radius
+ 62 * FRACUNIT, // height
+ 75, // mass
+ 0, // damage
+ sfx_mumact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_SHADOW, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_MUMMYSOUL
+ -1, // doomednum
+ S_MUMMY_SOUL1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_MUMMYFX1
+ -1, // doomednum
+ S_MUMMYFX1_1, // 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_NULL, // crashstate
+ S_MUMMYFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 9 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 14 * FRACUNIT, // height
+ 100, // mass
+ 4, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_BEAST
+ 70, // doomednum
+ S_BEAST_LOOK1, // spawnstate
+ 220, // spawnhealth
+ S_BEAST_WALK1, // seestate
+ sfx_bstsit, // seesound
+ 8, // reactiontime
+ sfx_bstatk, // attacksound
+ S_BEAST_PAIN1, // painstate
+ 100, // painchance
+ sfx_bstpai, // painsound
+ 0, // meleestate
+ S_BEAST_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_BEAST_DIE1, // deathstate
+ S_BEAST_XDIE1, // xdeathstate
+ sfx_bstdth, // deathsound
+ 14, // speed
+ 32 * FRACUNIT, // radius
+ 74 * FRACUNIT, // height
+ 200, // mass
+ 0, // damage
+ sfx_bstact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_BEASTBALL
+ -1, // doomednum
+ S_BEASTBALL1, // 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_NULL, // crashstate
+ S_BEASTBALLX1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 12 * FRACUNIT, // speed
+ 9 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 4, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_WINDTHRUST | MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_BURNBALL
+ -1, // doomednum
+ S_BURNBALL1, // 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_NULL, // crashstate
+ S_BEASTBALLX1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 10 * FRACUNIT, // speed
+ 6 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_BURNBALLFB
+ -1, // doomednum
+ S_BURNBALLFB1, // 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_NULL, // crashstate
+ S_BEASTBALLX1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 10 * FRACUNIT, // speed
+ 6 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_PUFFY
+ -1, // doomednum
+ S_PUFFY1, // 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_NULL, // crashstate
+ S_PUFFY1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 10 * FRACUNIT, // speed
+ 6 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_NOGRAVITY | MF_MISSILE, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_SNAKE
+ 92, // doomednum
+ S_SNAKE_LOOK1, // spawnstate
+ 280, // spawnhealth
+ S_SNAKE_WALK1, // seestate
+ sfx_snksit, // seesound
+ 8, // reactiontime
+ sfx_snkatk, // attacksound
+ S_SNAKE_PAIN1, // painstate
+ 48, // painchance
+ sfx_snkpai, // painsound
+ 0, // meleestate
+ S_SNAKE_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_SNAKE_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_snkdth, // deathsound
+ 10, // speed
+ 22 * FRACUNIT, // radius
+ 70 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_snkact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_SNAKEPRO_A
+ -1, // doomednum
+ S_SNAKEPRO_A1, // 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_NULL, // crashstate
+ S_SNAKEPRO_AX1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 14 * FRACUNIT, // speed
+ 12 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 1, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_WINDTHRUST | MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_SNAKEPRO_B
+ -1, // doomednum
+ S_SNAKEPRO_B1, // 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_NULL, // crashstate
+ S_SNAKEPRO_BX1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 14 * FRACUNIT, // speed
+ 12 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 3, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_HEAD
+ 6, // doomednum
+ S_HEAD_LOOK, // spawnstate
+ 700, // spawnhealth
+ S_HEAD_FLOAT, // seestate
+ sfx_hedsit, // seesound
+ 8, // reactiontime
+ sfx_hedat1, // attacksound
+ S_HEAD_PAIN1, // painstate
+ 32, // painchance
+ sfx_hedpai, // painsound
+ 0, // meleestate
+ S_HEAD_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_HEAD_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_heddth, // deathsound
+ 6, // speed
+ 40 * FRACUNIT, // radius
+ 72 * FRACUNIT, // height
+ 325, // mass
+ 0, // damage
+ sfx_hedact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_NOBLOOD, // flags
+ MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_HEADFX1
+ -1, // doomednum
+ S_HEADFX1_1, // 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_NULL, // crashstate
+ S_HEADFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 13 * FRACUNIT, // speed
+ 12 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 1, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT | MF2_THRUGHOST // flags2
+ },
+
+ { // MT_HEADFX2
+ -1, // doomednum
+ S_HEADFX2_1, // 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_NULL, // crashstate
+ S_HEADFXI2_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 8 * FRACUNIT, // speed
+ 12 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 3, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_HEADFX3
+ -1, // doomednum
+ S_HEADFX3_1, // 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_NULL, // crashstate
+ S_HEADFXI3_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 10 * FRACUNIT, // speed
+ 14 * FRACUNIT, // radius
+ 12 * FRACUNIT, // height
+ 100, // mass
+ 5, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_WINDTHRUST | MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_WHIRLWIND
+ -1, // doomednum
+ S_HEADFX4_1, // 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_NULL, // crashstate
+ S_HEADFXI4_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 10 * FRACUNIT, // speed
+ 16 * FRACUNIT, // radius
+ 74 * FRACUNIT, // height
+ 100, // mass
+ 1, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY | MF_SHADOW, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_CLINK
+ 90, // doomednum
+ S_CLINK_LOOK1, // spawnstate
+ 150, // spawnhealth
+ S_CLINK_WALK1, // seestate
+ sfx_clksit, // seesound
+ 8, // reactiontime
+ sfx_clkatk, // attacksound
+ S_CLINK_PAIN1, // painstate
+ 32, // painchance
+ sfx_clkpai, // painsound
+ S_CLINK_ATK1, // meleestate
+ 0, // missilestate
+ S_NULL, // crashstate
+ S_CLINK_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_clkdth, // deathsound
+ 14, // speed
+ 20 * FRACUNIT, // radius
+ 64 * FRACUNIT, // height
+ 75, // mass
+ 0, // damage
+ sfx_clkact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_NOBLOOD, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_WIZARD
+ 15, // doomednum
+ S_WIZARD_LOOK1, // spawnstate
+ 180, // spawnhealth
+ S_WIZARD_WALK1, // seestate
+ sfx_wizsit, // seesound
+ 8, // reactiontime
+ sfx_wizatk, // attacksound
+ S_WIZARD_PAIN1, // painstate
+ 64, // painchance
+ sfx_wizpai, // painsound
+ 0, // meleestate
+ S_WIZARD_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_WIZARD_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_wizdth, // deathsound
+ 12, // speed
+ 16 * FRACUNIT, // radius
+ 68 * FRACUNIT, // height
+ 100, // mass
+ 0, // damage
+ sfx_wizact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_FLOAT | MF_NOGRAVITY, // flags
+ MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_WIZFX1
+ -1, // doomednum
+ S_WIZFX1_1, // 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_NULL, // crashstate
+ S_WIZFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 18 * FRACUNIT, // speed
+ 10 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 3, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_IMP
+ 66, // doomednum
+ S_IMP_LOOK1, // spawnstate
+ 40, // spawnhealth
+ S_IMP_FLY1, // seestate
+ sfx_impsit, // seesound
+ 8, // reactiontime
+ sfx_impat1, // attacksound
+ S_IMP_PAIN1, // painstate
+ 200, // painchance
+ sfx_imppai, // painsound
+ S_IMP_MEATK1, // meleestate
+ S_IMP_MSATK1_1, // missilestate
+ S_IMP_CRASH1, // crashstate
+ S_IMP_DIE1, // deathstate
+ S_IMP_XDIE1, // xdeathstate
+ sfx_impdth, // deathsound
+ 10, // speed
+ 16 * FRACUNIT, // radius
+ 36 * FRACUNIT, // height
+ 50, // mass
+ 0, // damage
+ sfx_impact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY | MF_COUNTKILL, // flags
+ MF2_SPAWNFLOAT | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_IMPLEADER
+ 5, // doomednum
+ S_IMP_LOOK1, // spawnstate
+ 80, // spawnhealth
+ S_IMP_FLY1, // seestate
+ sfx_impsit, // seesound
+ 8, // reactiontime
+ sfx_impat2, // attacksound
+ S_IMP_PAIN1, // painstate
+ 200, // painchance
+ sfx_imppai, // painsound
+ 0, // meleestate
+ S_IMP_MSATK2_1, // missilestate
+ S_IMP_CRASH1, // crashstate
+ S_IMP_DIE1, // deathstate
+ S_IMP_XDIE1, // xdeathstate
+ sfx_impdth, // deathsound
+ 10, // speed
+ 16 * FRACUNIT, // radius
+ 36 * FRACUNIT, // height
+ 50, // mass
+ 0, // damage
+ sfx_impact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_FLOAT | MF_NOGRAVITY | MF_COUNTKILL, // flags
+ MF2_SPAWNFLOAT | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_IMPCHUNK1
+ -1, // doomednum
+ S_IMP_CHUNKA1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_IMPCHUNK2
+ -1, // doomednum
+ S_IMP_CHUNKB1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_IMPBALL
+ -1, // doomednum
+ S_IMPFX1, // 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_NULL, // crashstate
+ S_IMPFXI1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 10 * FRACUNIT, // speed
+ 8 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 1, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_WINDTHRUST | MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_KNIGHT
+ 64, // doomednum
+ S_KNIGHT_STND1, // spawnstate
+ 200, // spawnhealth
+ S_KNIGHT_WALK1, // seestate
+ sfx_kgtsit, // seesound
+ 8, // reactiontime
+ sfx_kgtatk, // attacksound
+ S_KNIGHT_PAIN1, // painstate
+ 100, // painchance
+ sfx_kgtpai, // painsound
+ S_KNIGHT_ATK1, // meleestate
+ S_KNIGHT_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_KNIGHT_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_kgtdth, // deathsound
+ 12, // speed
+ 24 * FRACUNIT, // radius
+ 78 * FRACUNIT, // height
+ 150, // mass
+ 0, // damage
+ sfx_kgtact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_KNIGHTGHOST
+ 65, // doomednum
+ S_KNIGHT_STND1, // spawnstate
+ 200, // spawnhealth
+ S_KNIGHT_WALK1, // seestate
+ sfx_kgtsit, // seesound
+ 8, // reactiontime
+ sfx_kgtatk, // attacksound
+ S_KNIGHT_PAIN1, // painstate
+ 100, // painchance
+ sfx_kgtpai, // painsound
+ S_KNIGHT_ATK1, // meleestate
+ S_KNIGHT_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_KNIGHT_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_kgtdth, // deathsound
+ 12, // speed
+ 24 * FRACUNIT, // radius
+ 78 * FRACUNIT, // height
+ 150, // mass
+ 0, // damage
+ sfx_kgtact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_SHADOW, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ // flags2
+ },
+
+ { // MT_KNIGHTAXE
+ -1, // doomednum
+ S_SPINAXE1, // 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_NULL, // crashstate
+ S_SPINAXEX1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_hrnhit, // deathsound
+ 9 * FRACUNIT, // speed
+ 10 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 2, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_WINDTHRUST | MF2_NOTELEPORT | MF2_THRUGHOST // flags2
+ },
+
+ { // MT_REDAXE
+ -1, // doomednum
+ S_REDAXE1, // 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_NULL, // crashstate
+ S_REDAXEX1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_hrnhit, // deathsound
+ 9 * FRACUNIT, // speed
+ 10 * FRACUNIT, // radius
+ 8 * FRACUNIT, // height
+ 100, // mass
+ 7, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT | MF2_THRUGHOST // flags2
+ },
+
+ { // MT_SORCERER1
+ 7, // doomednum
+ S_SRCR1_LOOK1, // spawnstate
+ 2000, // spawnhealth
+ S_SRCR1_WALK1, // seestate
+ sfx_sbtsit, // seesound
+ 8, // reactiontime
+ sfx_sbtatk, // attacksound
+ S_SRCR1_PAIN1, // painstate
+ 56, // painchance
+ sfx_sbtpai, // painsound
+ 0, // meleestate
+ S_SRCR1_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_SRCR1_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_sbtdth, // deathsound
+ 16, // speed
+ 28 * FRACUNIT, // radius
+ 100 * FRACUNIT, // height
+ 800, // mass
+ 0, // damage
+ sfx_sbtact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ | MF2_BOSS // flags2
+ },
+
+ { // MT_SRCRFX1
+ -1, // doomednum
+ S_SRCRFX1_1, // 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_NULL, // crashstate
+ S_SRCRFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 20 * FRACUNIT, // speed
+ 10 * FRACUNIT, // radius
+ 10 * FRACUNIT, // height
+ 100, // mass
+ 10, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2
+ },
+
+ { // MT_SORCERER2
+ -1, // doomednum
+ S_SOR2_LOOK1, // spawnstate
+ 3500, // spawnhealth
+ S_SOR2_WALK1, // seestate
+ sfx_sorsit, // seesound
+ 8, // reactiontime
+ sfx_soratk, // attacksound
+ S_SOR2_PAIN1, // painstate
+ 32, // painchance
+ sfx_sorpai, // painsound
+ 0, // meleestate
+ S_SOR2_ATK1, // missilestate
+ S_NULL, // crashstate
+ S_SOR2_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 14, // speed
+ 16 * FRACUNIT, // radius
+ 70 * FRACUNIT, // height
+ 300, // mass
+ 0, // damage
+ sfx_soract, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_DROPOFF, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ | MF2_BOSS // flags2
+ },
+
+ { // MT_SOR2FX1
+ -1, // doomednum
+ S_SOR2FX1_1, // 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_NULL, // crashstate
+ S_SOR2FXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 20 * FRACUNIT, // speed
+ 10 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 1, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_SOR2FXSPARK
+ -1, // doomednum
+ S_SOR2FXSPARK1, // 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, // crashstate
+ 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
+ MF2_NOTELEPORT | MF2_CANNOTPUSH // flags2
+ },
+
+ { // MT_SOR2FX2
+ -1, // doomednum
+ S_SOR2FX2_1, // 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_NULL, // crashstate
+ S_SOR2FXI2_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 6 * FRACUNIT, // speed
+ 10 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 10, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT // flags2
+ },
+
+ { // MT_SOR2TELEFADE
+ -1, // doomednum
+ S_SOR2TELEFADE1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_MINOTAUR
+ 9, // doomednum
+ S_MNTR_LOOK1, // spawnstate
+ 3000, // spawnhealth
+ S_MNTR_WALK1, // seestate
+ sfx_minsit, // seesound
+ 8, // reactiontime
+ sfx_minat1, // attacksound
+ S_MNTR_PAIN1, // painstate
+ 25, // painchance
+ sfx_minpai, // painsound
+ S_MNTR_ATK1_1, // meleestate
+ S_MNTR_ATK2_1, // missilestate
+ S_NULL, // crashstate
+ S_MNTR_DIE1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_mindth, // deathsound
+ 16, // speed
+ 28 * FRACUNIT, // radius
+ 100 * FRACUNIT, // height
+ 800, // mass
+ 7, // damage
+ sfx_minact, // activesound
+ MF_SOLID | MF_SHOOTABLE | MF_COUNTKILL | MF_DROPOFF, // flags
+ MF2_FOOTCLIP | MF2_PASSMOBJ | MF2_BOSS // flags2
+ },
+
+ { // MT_MNTRFX1
+ -1, // doomednum
+ S_MNTRFX1_1, // 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_NULL, // crashstate
+ S_MNTRFXI1_1, // deathstate
+ S_NULL, // xdeathstate
+ 0, // deathsound
+ 20 * FRACUNIT, // speed
+ 10 * FRACUNIT, // radius
+ 6 * FRACUNIT, // height
+ 100, // mass
+ 3, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2
+ },
+
+ { // MT_MNTRFX2
+ -1, // doomednum
+ S_MNTRFX2_1, // 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_NULL, // crashstate
+ S_MNTRFXI2_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_phohit, // deathsound
+ 14 * FRACUNIT, // speed
+ 5 * FRACUNIT, // radius
+ 12 * FRACUNIT, // height
+ 100, // mass
+ 4, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2
+ },
+
+ { // MT_MNTRFX3
+ -1, // doomednum
+ S_MNTRFX3_1, // 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_NULL, // crashstate
+ S_MNTRFXI2_1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_phohit, // deathsound
+ 0, // speed
+ 8 * FRACUNIT, // radius
+ 16 * FRACUNIT, // height
+ 100, // mass
+ 4, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP | MF_MISSILE | MF_DROPOFF | MF_NOGRAVITY, // flags
+ MF2_NOTELEPORT | MF2_FIREDAMAGE // flags2
+ },
+
+ { // MT_AKYY
+ 73, // doomednum
+ S_AKYY1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_BKYY
+ 79, // doomednum
+ S_BKYY1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_CKEY
+ 80, // doomednum
+ S_CKYY1, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMGWNDWIMPY
+ 10, // doomednum
+ S_AMG1, // spawnstate
+ AMMO_GWND_WIMPY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMGWNDHEFTY
+ 12, // doomednum
+ S_AMG2_1, // spawnstate
+ AMMO_GWND_HEFTY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMMACEWIMPY
+ 13, // doomednum
+ S_AMM1, // spawnstate
+ AMMO_MACE_WIMPY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMMACEHEFTY
+ 16, // doomednum
+ S_AMM2, // spawnstate
+ AMMO_MACE_HEFTY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMCBOWWIMPY
+ 18, // doomednum
+ S_AMC1, // spawnstate
+ AMMO_CBOW_WIMPY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMCBOWHEFTY
+ 19, // doomednum
+ S_AMC2_1, // spawnstate
+ AMMO_CBOW_HEFTY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMSKRDWIMPY
+ 20, // doomednum
+ S_AMS1_1, // spawnstate
+ AMMO_SKRD_WIMPY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMSKRDHEFTY
+ 21, // doomednum
+ S_AMS2_1, // spawnstate
+ AMMO_SKRD_HEFTY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMPHRDWIMPY
+ 22, // doomednum
+ S_AMP1_1, // spawnstate
+ AMMO_PHRD_WIMPY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMPHRDHEFTY
+ 23, // doomednum
+ S_AMP2_1, // spawnstate
+ AMMO_PHRD_HEFTY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMBLSRWIMPY
+ 54, // doomednum
+ S_AMB1_1, // spawnstate
+ AMMO_BLSR_WIMPY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_AMBLSRHEFTY
+ 55, // doomednum
+ S_AMB2_1, // spawnstate
+ AMMO_BLSR_HEFTY, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_SOUNDWIND
+ 42, // doomednum
+ S_SND_WIND, // 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, // crashstate
+ 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
+ 0 // flags2
+ },
+
+ { // MT_SOUNDWATERFALL
+ 41, // doomednum
+ S_SND_WATERFALL, // 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, // crashstate
+ 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
+ 0 // flags2
+ }
+};
diff --git a/src/heretic/info.h b/src/heretic/info.h
new file mode 100644
index 00000000..236826cb
--- /dev/null
+++ b/src/heretic/info.h
@@ -0,0 +1,1586 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 HERETIC_INFO_H
+#define HERETIC_INFO_H
+
+typedef enum
+{
+ SPR_IMPX,
+ SPR_ACLO,
+ SPR_PTN1,
+ SPR_SHLD,
+ SPR_SHD2,
+ SPR_BAGH,
+ SPR_SPMP,
+ SPR_INVS,
+ SPR_PTN2,
+ SPR_SOAR,
+ SPR_INVU,
+ SPR_PWBK,
+ SPR_EGGC,
+ SPR_EGGM,
+ SPR_FX01,
+ SPR_SPHL,
+ SPR_TRCH,
+ SPR_FBMB,
+ SPR_XPL1,
+ SPR_ATLP,
+ SPR_PPOD,
+ SPR_AMG1,
+ SPR_SPSH,
+ SPR_LVAS,
+ SPR_SLDG,
+ SPR_SKH1,
+ SPR_SKH2,
+ SPR_SKH3,
+ SPR_SKH4,
+ SPR_CHDL,
+ SPR_SRTC,
+ SPR_SMPL,
+ SPR_STGS,
+ SPR_STGL,
+ SPR_STCS,
+ SPR_STCL,
+ SPR_KFR1,
+ SPR_BARL,
+ SPR_BRPL,
+ SPR_MOS1,
+ SPR_MOS2,
+ SPR_WTRH,
+ SPR_HCOR,
+ SPR_KGZ1,
+ SPR_KGZB,
+ SPR_KGZG,
+ SPR_KGZY,
+ SPR_VLCO,
+ SPR_VFBL,
+ SPR_VTFB,
+ SPR_SFFI,
+ SPR_TGLT,
+ SPR_TELE,
+ SPR_STFF,
+ SPR_PUF3,
+ SPR_PUF4,
+ SPR_BEAK,
+ SPR_WGNT,
+ SPR_GAUN,
+ SPR_PUF1,
+ SPR_WBLS,
+ SPR_BLSR,
+ SPR_FX18,
+ SPR_FX17,
+ SPR_WMCE,
+ SPR_MACE,
+ SPR_FX02,
+ SPR_WSKL,
+ SPR_HROD,
+ SPR_FX00,
+ SPR_FX20,
+ SPR_FX21,
+ SPR_FX22,
+ SPR_FX23,
+ SPR_GWND,
+ SPR_PUF2,
+ SPR_WPHX,
+ SPR_PHNX,
+ SPR_FX04,
+ SPR_FX08,
+ SPR_FX09,
+ SPR_WBOW,
+ SPR_CRBW,
+ SPR_FX03,
+ SPR_BLOD,
+ SPR_PLAY,
+ SPR_FDTH,
+ SPR_BSKL,
+ SPR_CHKN,
+ SPR_MUMM,
+ SPR_FX15,
+ SPR_BEAS,
+ SPR_FRB1,
+ SPR_SNKE,
+ SPR_SNFX,
+ SPR_HEAD,
+ SPR_FX05,
+ SPR_FX06,
+ SPR_FX07,
+ SPR_CLNK,
+ SPR_WZRD,
+ SPR_FX11,
+ SPR_FX10,
+ SPR_KNIG,
+ SPR_SPAX,
+ SPR_RAXE,
+ SPR_SRCR,
+ SPR_FX14,
+ SPR_SOR2,
+ SPR_SDTH,
+ SPR_FX16,
+ SPR_MNTR,
+ SPR_FX12,
+ SPR_FX13,
+ SPR_AKYY,
+ SPR_BKYY,
+ SPR_CKYY,
+ SPR_AMG2,
+ SPR_AMM1,
+ SPR_AMM2,
+ SPR_AMC1,
+ SPR_AMC2,
+ SPR_AMS1,
+ SPR_AMS2,
+ SPR_AMP1,
+ SPR_AMP2,
+ SPR_AMB1,
+ SPR_AMB2,
+ NUMSPRITES
+} spritenum_t;
+
+typedef enum
+{
+ S_NULL,
+ S_FREETARGMOBJ,
+ S_ITEM_PTN1_1,
+ S_ITEM_PTN1_2,
+ S_ITEM_PTN1_3,
+ S_ITEM_SHLD1,
+ S_ITEM_SHD2_1,
+ S_ITEM_BAGH1,
+ S_ITEM_SPMP1,
+ S_HIDESPECIAL1,
+ S_HIDESPECIAL2,
+ S_HIDESPECIAL3,
+ S_HIDESPECIAL4,
+ S_HIDESPECIAL5,
+ S_HIDESPECIAL6,
+ S_HIDESPECIAL7,
+ S_HIDESPECIAL8,
+ S_HIDESPECIAL9,
+ S_HIDESPECIAL10,
+ S_HIDESPECIAL11,
+ S_DORMANTARTI1,
+ S_DORMANTARTI2,
+ S_DORMANTARTI3,
+ S_DORMANTARTI4,
+ S_DORMANTARTI5,
+ S_DORMANTARTI6,
+ S_DORMANTARTI7,
+ S_DORMANTARTI8,
+ S_DORMANTARTI9,
+ S_DORMANTARTI10,
+ S_DORMANTARTI11,
+ S_DORMANTARTI12,
+ S_DORMANTARTI13,
+ S_DORMANTARTI14,
+ S_DORMANTARTI15,
+ S_DORMANTARTI16,
+ S_DORMANTARTI17,
+ S_DORMANTARTI18,
+ S_DORMANTARTI19,
+ S_DORMANTARTI20,
+ S_DORMANTARTI21,
+ S_DEADARTI1,
+ S_DEADARTI2,
+ S_DEADARTI3,
+ S_DEADARTI4,
+ S_DEADARTI5,
+ S_DEADARTI6,
+ S_DEADARTI7,
+ S_DEADARTI8,
+ S_DEADARTI9,
+ S_DEADARTI10,
+ S_ARTI_INVS1,
+ S_ARTI_PTN2_1,
+ S_ARTI_PTN2_2,
+ S_ARTI_PTN2_3,
+ S_ARTI_SOAR1,
+ S_ARTI_SOAR2,
+ S_ARTI_SOAR3,
+ S_ARTI_SOAR4,
+ S_ARTI_INVU1,
+ S_ARTI_INVU2,
+ S_ARTI_INVU3,
+ S_ARTI_INVU4,
+ S_ARTI_PWBK1,
+ S_ARTI_EGGC1,
+ S_ARTI_EGGC2,
+ S_ARTI_EGGC3,
+ S_ARTI_EGGC4,
+ S_EGGFX1,
+ S_EGGFX2,
+ S_EGGFX3,
+ S_EGGFX4,
+ S_EGGFX5,
+ S_EGGFXI1_1,
+ S_EGGFXI1_2,
+ S_EGGFXI1_3,
+ S_EGGFXI1_4,
+ S_ARTI_SPHL1,
+ S_ARTI_TRCH1,
+ S_ARTI_TRCH2,
+ S_ARTI_TRCH3,
+ S_ARTI_FBMB1,
+ S_FIREBOMB1,
+ S_FIREBOMB2,
+ S_FIREBOMB3,
+ S_FIREBOMB4,
+ S_FIREBOMB5,
+ S_FIREBOMB6,
+ S_FIREBOMB7,
+ S_FIREBOMB8,
+ S_FIREBOMB9,
+ S_FIREBOMB10,
+ S_FIREBOMB11,
+ S_ARTI_ATLP1,
+ S_ARTI_ATLP2,
+ S_ARTI_ATLP3,
+ S_ARTI_ATLP4,
+ S_POD_WAIT1,
+ S_POD_PAIN1,
+ S_POD_DIE1,
+ S_POD_DIE2,
+ S_POD_DIE3,
+ S_POD_DIE4,
+ S_POD_GROW1,
+ S_POD_GROW2,
+ S_POD_GROW3,
+ S_POD_GROW4,
+ S_POD_GROW5,
+ S_POD_GROW6,
+ S_POD_GROW7,
+ S_POD_GROW8,
+ S_PODGOO1,
+ S_PODGOO2,
+ S_PODGOOX,
+ S_PODGENERATOR,
+ S_SPLASH1,
+ S_SPLASH2,
+ S_SPLASH3,
+ S_SPLASH4,
+ S_SPLASHX,
+ S_SPLASHBASE1,
+ S_SPLASHBASE2,
+ S_SPLASHBASE3,
+ S_SPLASHBASE4,
+ S_SPLASHBASE5,
+ S_SPLASHBASE6,
+ S_SPLASHBASE7,
+ S_LAVASPLASH1,
+ S_LAVASPLASH2,
+ S_LAVASPLASH3,
+ S_LAVASPLASH4,
+ S_LAVASPLASH5,
+ S_LAVASPLASH6,
+ S_LAVASMOKE1,
+ S_LAVASMOKE2,
+ S_LAVASMOKE3,
+ S_LAVASMOKE4,
+ S_LAVASMOKE5,
+ S_SLUDGECHUNK1,
+ S_SLUDGECHUNK2,
+ S_SLUDGECHUNK3,
+ S_SLUDGECHUNK4,
+ S_SLUDGECHUNKX,
+ S_SLUDGESPLASH1,
+ S_SLUDGESPLASH2,
+ S_SLUDGESPLASH3,
+ S_SLUDGESPLASH4,
+ S_SKULLHANG70_1,
+ S_SKULLHANG60_1,
+ S_SKULLHANG45_1,
+ S_SKULLHANG35_1,
+ S_CHANDELIER1,
+ S_CHANDELIER2,
+ S_CHANDELIER3,
+ S_SERPTORCH1,
+ S_SERPTORCH2,
+ S_SERPTORCH3,
+ S_SMALLPILLAR,
+ S_STALAGMITESMALL,
+ S_STALAGMITELARGE,
+ S_STALACTITESMALL,
+ S_STALACTITELARGE,
+ S_FIREBRAZIER1,
+ S_FIREBRAZIER2,
+ S_FIREBRAZIER3,
+ S_FIREBRAZIER4,
+ S_FIREBRAZIER5,
+ S_FIREBRAZIER6,
+ S_FIREBRAZIER7,
+ S_FIREBRAZIER8,
+ S_BARREL,
+ S_BRPILLAR,
+ S_MOSS1,
+ S_MOSS2,
+ S_WALLTORCH1,
+ S_WALLTORCH2,
+ S_WALLTORCH3,
+ S_HANGINGCORPSE,
+ S_KEYGIZMO1,
+ S_KEYGIZMO2,
+ S_KEYGIZMO3,
+ S_KGZ_START,
+ S_KGZ_BLUEFLOAT1,
+ S_KGZ_GREENFLOAT1,
+ S_KGZ_YELLOWFLOAT1,
+ S_VOLCANO1,
+ S_VOLCANO2,
+ S_VOLCANO3,
+ S_VOLCANO4,
+ S_VOLCANO5,
+ S_VOLCANO6,
+ S_VOLCANO7,
+ S_VOLCANO8,
+ S_VOLCANO9,
+ S_VOLCANOBALL1,
+ S_VOLCANOBALL2,
+ S_VOLCANOBALLX1,
+ S_VOLCANOBALLX2,
+ S_VOLCANOBALLX3,
+ S_VOLCANOBALLX4,
+ S_VOLCANOBALLX5,
+ S_VOLCANOBALLX6,
+ S_VOLCANOTBALL1,
+ S_VOLCANOTBALL2,
+ S_VOLCANOTBALLX1,
+ S_VOLCANOTBALLX2,
+ S_VOLCANOTBALLX3,
+ S_VOLCANOTBALLX4,
+ S_VOLCANOTBALLX5,
+ S_VOLCANOTBALLX6,
+ S_VOLCANOTBALLX7,
+ S_TELEGLITGEN1,
+ S_TELEGLITGEN2,
+ S_TELEGLITTER1_1,
+ S_TELEGLITTER1_2,
+ S_TELEGLITTER1_3,
+ S_TELEGLITTER1_4,
+ S_TELEGLITTER1_5,
+ S_TELEGLITTER2_1,
+ S_TELEGLITTER2_2,
+ S_TELEGLITTER2_3,
+ S_TELEGLITTER2_4,
+ S_TELEGLITTER2_5,
+ S_TFOG1,
+ S_TFOG2,
+ S_TFOG3,
+ S_TFOG4,
+ S_TFOG5,
+ S_TFOG6,
+ S_TFOG7,
+ S_TFOG8,
+ S_TFOG9,
+ S_TFOG10,
+ S_TFOG11,
+ S_TFOG12,
+ S_TFOG13,
+ S_LIGHTDONE,
+ S_STAFFREADY,
+ S_STAFFDOWN,
+ S_STAFFUP,
+ S_STAFFREADY2_1,
+ S_STAFFREADY2_2,
+ S_STAFFREADY2_3,
+ S_STAFFDOWN2,
+ S_STAFFUP2,
+ S_STAFFATK1_1,
+ S_STAFFATK1_2,
+ S_STAFFATK1_3,
+ S_STAFFATK2_1,
+ S_STAFFATK2_2,
+ S_STAFFATK2_3,
+ S_STAFFPUFF1,
+ S_STAFFPUFF2,
+ S_STAFFPUFF3,
+ S_STAFFPUFF4,
+ S_STAFFPUFF2_1,
+ S_STAFFPUFF2_2,
+ S_STAFFPUFF2_3,
+ S_STAFFPUFF2_4,
+ S_STAFFPUFF2_5,
+ S_STAFFPUFF2_6,
+ S_BEAKREADY,
+ S_BEAKDOWN,
+ S_BEAKUP,
+ S_BEAKATK1_1,
+ S_BEAKATK2_1,
+ S_WGNT,
+ S_GAUNTLETREADY,
+ S_GAUNTLETDOWN,
+ S_GAUNTLETUP,
+ S_GAUNTLETREADY2_1,
+ S_GAUNTLETREADY2_2,
+ S_GAUNTLETREADY2_3,
+ S_GAUNTLETDOWN2,
+ S_GAUNTLETUP2,
+ S_GAUNTLETATK1_1,
+ S_GAUNTLETATK1_2,
+ S_GAUNTLETATK1_3,
+ S_GAUNTLETATK1_4,
+ S_GAUNTLETATK1_5,
+ S_GAUNTLETATK1_6,
+ S_GAUNTLETATK1_7,
+ S_GAUNTLETATK2_1,
+ S_GAUNTLETATK2_2,
+ S_GAUNTLETATK2_3,
+ S_GAUNTLETATK2_4,
+ S_GAUNTLETATK2_5,
+ S_GAUNTLETATK2_6,
+ S_GAUNTLETATK2_7,
+ S_GAUNTLETPUFF1_1,
+ S_GAUNTLETPUFF1_2,
+ S_GAUNTLETPUFF1_3,
+ S_GAUNTLETPUFF1_4,
+ S_GAUNTLETPUFF2_1,
+ S_GAUNTLETPUFF2_2,
+ S_GAUNTLETPUFF2_3,
+ S_GAUNTLETPUFF2_4,
+ S_BLSR,
+ S_BLASTERREADY,
+ S_BLASTERDOWN,
+ S_BLASTERUP,
+ S_BLASTERATK1_1,
+ S_BLASTERATK1_2,
+ S_BLASTERATK1_3,
+ S_BLASTERATK1_4,
+ S_BLASTERATK1_5,
+ S_BLASTERATK1_6,
+ S_BLASTERATK2_1,
+ S_BLASTERATK2_2,
+ S_BLASTERATK2_3,
+ S_BLASTERATK2_4,
+ S_BLASTERATK2_5,
+ S_BLASTERATK2_6,
+ S_BLASTERFX1_1,
+ S_BLASTERFXI1_1,
+ S_BLASTERFXI1_2,
+ S_BLASTERFXI1_3,
+ S_BLASTERFXI1_4,
+ S_BLASTERFXI1_5,
+ S_BLASTERFXI1_6,
+ S_BLASTERFXI1_7,
+ S_BLASTERSMOKE1,
+ S_BLASTERSMOKE2,
+ S_BLASTERSMOKE3,
+ S_BLASTERSMOKE4,
+ S_BLASTERSMOKE5,
+ S_RIPPER1,
+ S_RIPPER2,
+ S_RIPPERX1,
+ S_RIPPERX2,
+ S_RIPPERX3,
+ S_RIPPERX4,
+ S_RIPPERX5,
+ S_BLASTERPUFF1_1,
+ S_BLASTERPUFF1_2,
+ S_BLASTERPUFF1_3,
+ S_BLASTERPUFF1_4,
+ S_BLASTERPUFF1_5,
+ S_BLASTERPUFF2_1,
+ S_BLASTERPUFF2_2,
+ S_BLASTERPUFF2_3,
+ S_BLASTERPUFF2_4,
+ S_BLASTERPUFF2_5,
+ S_BLASTERPUFF2_6,
+ S_BLASTERPUFF2_7,
+ S_WMCE,
+ S_MACEREADY,
+ S_MACEDOWN,
+ S_MACEUP,
+ S_MACEATK1_1,
+ S_MACEATK1_2,
+ S_MACEATK1_3,
+ S_MACEATK1_4,
+ S_MACEATK1_5,
+ S_MACEATK1_6,
+ S_MACEATK1_7,
+ S_MACEATK1_8,
+ S_MACEATK1_9,
+ S_MACEATK1_10,
+ S_MACEATK2_1,
+ S_MACEATK2_2,
+ S_MACEATK2_3,
+ S_MACEATK2_4,
+ S_MACEFX1_1,
+ S_MACEFX1_2,
+ S_MACEFXI1_1,
+ S_MACEFXI1_2,
+ S_MACEFXI1_3,
+ S_MACEFXI1_4,
+ S_MACEFXI1_5,
+ S_MACEFX2_1,
+ S_MACEFX2_2,
+ S_MACEFXI2_1,
+ S_MACEFX3_1,
+ S_MACEFX3_2,
+ S_MACEFX4_1,
+ S_MACEFXI4_1,
+ S_WSKL,
+ S_HORNRODREADY,
+ S_HORNRODDOWN,
+ S_HORNRODUP,
+ S_HORNRODATK1_1,
+ S_HORNRODATK1_2,
+ S_HORNRODATK1_3,
+ S_HORNRODATK2_1,
+ S_HORNRODATK2_2,
+ S_HORNRODATK2_3,
+ S_HORNRODATK2_4,
+ S_HORNRODATK2_5,
+ S_HORNRODATK2_6,
+ S_HORNRODATK2_7,
+ S_HORNRODATK2_8,
+ S_HORNRODATK2_9,
+ S_HRODFX1_1,
+ S_HRODFX1_2,
+ S_HRODFXI1_1,
+ S_HRODFXI1_2,
+ S_HRODFXI1_3,
+ S_HRODFXI1_4,
+ S_HRODFXI1_5,
+ S_HRODFXI1_6,
+ S_HRODFX2_1,
+ S_HRODFX2_2,
+ S_HRODFX2_3,
+ S_HRODFX2_4,
+ S_HRODFXI2_1,
+ S_HRODFXI2_2,
+ S_HRODFXI2_3,
+ S_HRODFXI2_4,
+ S_HRODFXI2_5,
+ S_HRODFXI2_6,
+ S_HRODFXI2_7,
+ S_HRODFXI2_8,
+ S_RAINPLR1_1,
+ S_RAINPLR2_1,
+ S_RAINPLR3_1,
+ S_RAINPLR4_1,
+ S_RAINPLR1X_1,
+ S_RAINPLR1X_2,
+ S_RAINPLR1X_3,
+ S_RAINPLR1X_4,
+ S_RAINPLR1X_5,
+ S_RAINPLR2X_1,
+ S_RAINPLR2X_2,
+ S_RAINPLR2X_3,
+ S_RAINPLR2X_4,
+ S_RAINPLR2X_5,
+ S_RAINPLR3X_1,
+ S_RAINPLR3X_2,
+ S_RAINPLR3X_3,
+ S_RAINPLR3X_4,
+ S_RAINPLR3X_5,
+ S_RAINPLR4X_1,
+ S_RAINPLR4X_2,
+ S_RAINPLR4X_3,
+ S_RAINPLR4X_4,
+ S_RAINPLR4X_5,
+ S_RAINAIRXPLR1_1,
+ S_RAINAIRXPLR2_1,
+ S_RAINAIRXPLR3_1,
+ S_RAINAIRXPLR4_1,
+ S_RAINAIRXPLR1_2,
+ S_RAINAIRXPLR2_2,
+ S_RAINAIRXPLR3_2,
+ S_RAINAIRXPLR4_2,
+ S_RAINAIRXPLR1_3,
+ S_RAINAIRXPLR2_3,
+ S_RAINAIRXPLR3_3,
+ S_RAINAIRXPLR4_3,
+ S_GOLDWANDREADY,
+ S_GOLDWANDDOWN,
+ S_GOLDWANDUP,
+ S_GOLDWANDATK1_1,
+ S_GOLDWANDATK1_2,
+ S_GOLDWANDATK1_3,
+ S_GOLDWANDATK1_4,
+ S_GOLDWANDATK2_1,
+ S_GOLDWANDATK2_2,
+ S_GOLDWANDATK2_3,
+ S_GOLDWANDATK2_4,
+ S_GWANDFX1_1,
+ S_GWANDFX1_2,
+ S_GWANDFXI1_1,
+ S_GWANDFXI1_2,
+ S_GWANDFXI1_3,
+ S_GWANDFXI1_4,
+ S_GWANDFX2_1,
+ S_GWANDFX2_2,
+ S_GWANDPUFF1_1,
+ S_GWANDPUFF1_2,
+ S_GWANDPUFF1_3,
+ S_GWANDPUFF1_4,
+ S_GWANDPUFF1_5,
+ S_WPHX,
+ S_PHOENIXREADY,
+ S_PHOENIXDOWN,
+ S_PHOENIXUP,
+ S_PHOENIXATK1_1,
+ S_PHOENIXATK1_2,
+ S_PHOENIXATK1_3,
+ S_PHOENIXATK1_4,
+ S_PHOENIXATK1_5,
+ S_PHOENIXATK2_1,
+ S_PHOENIXATK2_2,
+ S_PHOENIXATK2_3,
+ S_PHOENIXATK2_4,
+ S_PHOENIXFX1_1,
+ S_PHOENIXFXI1_1,
+ S_PHOENIXFXI1_2,
+ S_PHOENIXFXI1_3,
+ S_PHOENIXFXI1_4,
+ S_PHOENIXFXI1_5,
+ S_PHOENIXFXI1_6,
+ S_PHOENIXFXI1_7,
+ S_PHOENIXFXI1_8,
+ S_PHOENIXFXIX_1, // [ States in Heretic 1.0 that were removed
+ S_PHOENIXFXIX_2,
+ S_PHOENIXFXIX_3, // ]
+ S_PHOENIXPUFF1,
+ S_PHOENIXPUFF2,
+ S_PHOENIXPUFF3,
+ S_PHOENIXPUFF4,
+ S_PHOENIXPUFF5,
+ S_PHOENIXFX2_1,
+ S_PHOENIXFX2_2,
+ S_PHOENIXFX2_3,
+ S_PHOENIXFX2_4,
+ S_PHOENIXFX2_5,
+ S_PHOENIXFX2_6,
+ S_PHOENIXFX2_7,
+ S_PHOENIXFX2_8,
+ S_PHOENIXFX2_9,
+ S_PHOENIXFX2_10,
+ S_PHOENIXFXI2_1,
+ S_PHOENIXFXI2_2,
+ S_PHOENIXFXI2_3,
+ S_PHOENIXFXI2_4,
+ S_PHOENIXFXI2_5,
+ S_WBOW,
+ S_CRBOW1,
+ S_CRBOW2,
+ S_CRBOW3,
+ S_CRBOW4,
+ S_CRBOW5,
+ S_CRBOW6,
+ S_CRBOW7,
+ S_CRBOW8,
+ S_CRBOW9,
+ S_CRBOW10,
+ S_CRBOW11,
+ S_CRBOW12,
+ S_CRBOW13,
+ S_CRBOW14,
+ S_CRBOW15,
+ S_CRBOW16,
+ S_CRBOW17,
+ S_CRBOW18,
+ S_CRBOWDOWN,
+ S_CRBOWUP,
+ S_CRBOWATK1_1,
+ S_CRBOWATK1_2,
+ S_CRBOWATK1_3,
+ S_CRBOWATK1_4,
+ S_CRBOWATK1_5,
+ S_CRBOWATK1_6,
+ S_CRBOWATK1_7,
+ S_CRBOWATK1_8,
+ S_CRBOWATK2_1,
+ S_CRBOWATK2_2,
+ S_CRBOWATK2_3,
+ S_CRBOWATK2_4,
+ S_CRBOWATK2_5,
+ S_CRBOWATK2_6,
+ S_CRBOWATK2_7,
+ S_CRBOWATK2_8,
+ S_CRBOWFX1,
+ S_CRBOWFXI1_1,
+ S_CRBOWFXI1_2,
+ S_CRBOWFXI1_3,
+ S_CRBOWFX2,
+ S_CRBOWFX3,
+ S_CRBOWFXI3_1,
+ S_CRBOWFXI3_2,
+ S_CRBOWFXI3_3,
+ S_CRBOWFX4_1,
+ S_CRBOWFX4_2,
+ S_BLOOD1,
+ S_BLOOD2,
+ S_BLOOD3,
+ S_BLOODSPLATTER1,
+ S_BLOODSPLATTER2,
+ S_BLOODSPLATTER3,
+ S_BLOODSPLATTERX,
+ 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_DIE8,
+ S_PLAY_DIE9,
+ 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_PLAY_FDTH1,
+ S_PLAY_FDTH2,
+ S_PLAY_FDTH3,
+ S_PLAY_FDTH4,
+ S_PLAY_FDTH5,
+ S_PLAY_FDTH6,
+ S_PLAY_FDTH7,
+ S_PLAY_FDTH8,
+ S_PLAY_FDTH9,
+ S_PLAY_FDTH10,
+ S_PLAY_FDTH11,
+ S_PLAY_FDTH12,
+ S_PLAY_FDTH13,
+ S_PLAY_FDTH14,
+ S_PLAY_FDTH15,
+ S_PLAY_FDTH16,
+ S_PLAY_FDTH17,
+ S_PLAY_FDTH18,
+ S_PLAY_FDTH19, // < These two frames were not present in the Heretic
+ S_PLAY_FDTH20, // < 1.0 executable (fire death animation was extended)
+ S_BLOODYSKULL1,
+ S_BLOODYSKULL2,
+ S_BLOODYSKULL3,
+ S_BLOODYSKULL4,
+ S_BLOODYSKULL5,
+ S_BLOODYSKULLX1,
+ S_BLOODYSKULLX2,
+ S_CHICPLAY,
+ S_CHICPLAY_RUN1,
+ S_CHICPLAY_RUN2,
+ S_CHICPLAY_RUN3,
+ S_CHICPLAY_RUN4,
+ S_CHICPLAY_ATK1,
+ S_CHICPLAY_PAIN,
+ S_CHICPLAY_PAIN2,
+ S_CHICKEN_LOOK1,
+ S_CHICKEN_LOOK2,
+ S_CHICKEN_WALK1,
+ S_CHICKEN_WALK2,
+ S_CHICKEN_PAIN1,
+ S_CHICKEN_PAIN2,
+ S_CHICKEN_ATK1,
+ S_CHICKEN_ATK2,
+ S_CHICKEN_DIE1,
+ S_CHICKEN_DIE2,
+ S_CHICKEN_DIE3,
+ S_CHICKEN_DIE4,
+ S_CHICKEN_DIE5,
+ S_CHICKEN_DIE6,
+ S_CHICKEN_DIE7,
+ S_CHICKEN_DIE8,
+ S_FEATHER1,
+ S_FEATHER2,
+ S_FEATHER3,
+ S_FEATHER4,
+ S_FEATHER5,
+ S_FEATHER6,
+ S_FEATHER7,
+ S_FEATHER8,
+ S_FEATHERX,
+ S_MUMMY_LOOK1,
+ S_MUMMY_LOOK2,
+ S_MUMMY_WALK1,
+ S_MUMMY_WALK2,
+ S_MUMMY_WALK3,
+ S_MUMMY_WALK4,
+ S_MUMMY_ATK1,
+ S_MUMMY_ATK2,
+ S_MUMMY_ATK3,
+ S_MUMMYL_ATK1,
+ S_MUMMYL_ATK2,
+ S_MUMMYL_ATK3,
+ S_MUMMYL_ATK4,
+ S_MUMMYL_ATK5,
+ S_MUMMYL_ATK6,
+ S_MUMMY_PAIN1,
+ S_MUMMY_PAIN2,
+ S_MUMMY_DIE1,
+ S_MUMMY_DIE2,
+ S_MUMMY_DIE3,
+ S_MUMMY_DIE4,
+ S_MUMMY_DIE5,
+ S_MUMMY_DIE6,
+ S_MUMMY_DIE7,
+ S_MUMMY_DIE8,
+ S_MUMMY_SOUL1,
+ S_MUMMY_SOUL2,
+ S_MUMMY_SOUL3,
+ S_MUMMY_SOUL4,
+ S_MUMMY_SOUL5,
+ S_MUMMY_SOUL6,
+ S_MUMMY_SOUL7,
+ S_MUMMYFX1_1,
+ S_MUMMYFX1_2,
+ S_MUMMYFX1_3,
+ S_MUMMYFX1_4,
+ S_MUMMYFXI1_1,
+ S_MUMMYFXI1_2,
+ S_MUMMYFXI1_3,
+ S_MUMMYFXI1_4,
+ S_BEAST_LOOK1,
+ S_BEAST_LOOK2,
+ S_BEAST_WALK1,
+ S_BEAST_WALK2,
+ S_BEAST_WALK3,
+ S_BEAST_WALK4,
+ S_BEAST_WALK5,
+ S_BEAST_WALK6,
+ S_BEAST_ATK1,
+ S_BEAST_ATK2,
+ S_BEAST_PAIN1,
+ S_BEAST_PAIN2,
+ S_BEAST_DIE1,
+ S_BEAST_DIE2,
+ S_BEAST_DIE3,
+ S_BEAST_DIE4,
+ S_BEAST_DIE5,
+ S_BEAST_DIE6,
+ S_BEAST_DIE7,
+ S_BEAST_DIE8,
+ S_BEAST_DIE9,
+ S_BEAST_XDIE1,
+ S_BEAST_XDIE2,
+ S_BEAST_XDIE3,
+ S_BEAST_XDIE4,
+ S_BEAST_XDIE5,
+ S_BEAST_XDIE6,
+ S_BEAST_XDIE7,
+ S_BEAST_XDIE8,
+ S_BEASTBALL1,
+ S_BEASTBALL2,
+ S_BEASTBALL3,
+ S_BEASTBALL4,
+ S_BEASTBALL5,
+ S_BEASTBALL6,
+ S_BEASTBALLX1,
+ S_BEASTBALLX2,
+ S_BEASTBALLX3,
+ S_BEASTBALLX4,
+ S_BEASTBALLX5,
+ S_BURNBALL1,
+ S_BURNBALL2,
+ S_BURNBALL3,
+ S_BURNBALL4,
+ S_BURNBALL5,
+ S_BURNBALL6,
+ S_BURNBALL7,
+ S_BURNBALL8,
+ S_BURNBALLFB1,
+ S_BURNBALLFB2,
+ S_BURNBALLFB3,
+ S_BURNBALLFB4,
+ S_BURNBALLFB5,
+ S_BURNBALLFB6,
+ S_BURNBALLFB7,
+ S_BURNBALLFB8,
+ S_PUFFY1,
+ S_PUFFY2,
+ S_PUFFY3,
+ S_PUFFY4,
+ S_PUFFY5,
+ S_SNAKE_LOOK1,
+ S_SNAKE_LOOK2,
+ S_SNAKE_WALK1,
+ S_SNAKE_WALK2,
+ S_SNAKE_WALK3,
+ S_SNAKE_WALK4,
+ S_SNAKE_ATK1,
+ S_SNAKE_ATK2,
+ S_SNAKE_ATK3,
+ S_SNAKE_ATK4,
+ S_SNAKE_ATK5,
+ S_SNAKE_ATK6,
+ S_SNAKE_ATK7,
+ S_SNAKE_ATK8,
+ S_SNAKE_ATK9,
+ S_SNAKE_PAIN1,
+ S_SNAKE_PAIN2,
+ S_SNAKE_DIE1,
+ S_SNAKE_DIE2,
+ S_SNAKE_DIE3,
+ S_SNAKE_DIE4,
+ S_SNAKE_DIE5,
+ S_SNAKE_DIE6,
+ S_SNAKE_DIE7,
+ S_SNAKE_DIE8,
+ S_SNAKE_DIE9,
+ S_SNAKE_DIE10,
+ S_SNAKEPRO_A1,
+ S_SNAKEPRO_A2,
+ S_SNAKEPRO_A3,
+ S_SNAKEPRO_A4,
+ S_SNAKEPRO_AX1,
+ S_SNAKEPRO_AX2,
+ S_SNAKEPRO_AX3,
+ S_SNAKEPRO_AX4,
+ S_SNAKEPRO_AX5,
+ S_SNAKEPRO_B1,
+ S_SNAKEPRO_B2,
+ S_SNAKEPRO_BX1,
+ S_SNAKEPRO_BX2,
+ S_SNAKEPRO_BX3,
+ S_SNAKEPRO_BX4,
+ S_HEAD_LOOK,
+ S_HEAD_FLOAT,
+ S_HEAD_ATK1,
+ S_HEAD_ATK2,
+ S_HEAD_PAIN1,
+ S_HEAD_PAIN2,
+ S_HEAD_DIE1,
+ S_HEAD_DIE2,
+ S_HEAD_DIE3,
+ S_HEAD_DIE4,
+ S_HEAD_DIE5,
+ S_HEAD_DIE6,
+ S_HEAD_DIE7,
+ S_HEADFX1_1,
+ S_HEADFX1_2,
+ S_HEADFX1_3,
+ S_HEADFXI1_1,
+ S_HEADFXI1_2,
+ S_HEADFXI1_3,
+ S_HEADFXI1_4,
+ S_HEADFX2_1,
+ S_HEADFX2_2,
+ S_HEADFX2_3,
+ S_HEADFXI2_1,
+ S_HEADFXI2_2,
+ S_HEADFXI2_3,
+ S_HEADFXI2_4,
+ S_HEADFX3_1,
+ S_HEADFX3_2,
+ S_HEADFX3_3,
+ S_HEADFX3_4,
+ S_HEADFX3_5,
+ S_HEADFX3_6,
+ S_HEADFXI3_1,
+ S_HEADFXI3_2,
+ S_HEADFXI3_3,
+ S_HEADFXI3_4,
+ S_HEADFX4_1,
+ S_HEADFX4_2,
+ S_HEADFX4_3,
+ S_HEADFX4_4,
+ S_HEADFX4_5,
+ S_HEADFX4_6,
+ S_HEADFX4_7,
+ S_HEADFXI4_1,
+ S_HEADFXI4_2,
+ S_HEADFXI4_3,
+ S_HEADFXI4_4,
+ S_CLINK_LOOK1,
+ S_CLINK_LOOK2,
+ S_CLINK_WALK1,
+ S_CLINK_WALK2,
+ S_CLINK_WALK3,
+ S_CLINK_WALK4,
+ S_CLINK_ATK1,
+ S_CLINK_ATK2,
+ S_CLINK_ATK3,
+ S_CLINK_PAIN1,
+ S_CLINK_PAIN2,
+ S_CLINK_DIE1,
+ S_CLINK_DIE2,
+ S_CLINK_DIE3,
+ S_CLINK_DIE4,
+ S_CLINK_DIE5,
+ S_CLINK_DIE6,
+ S_CLINK_DIE7,
+ S_WIZARD_LOOK1,
+ S_WIZARD_LOOK2,
+ S_WIZARD_WALK1,
+ S_WIZARD_WALK2,
+ S_WIZARD_WALK3,
+ S_WIZARD_WALK4,
+ S_WIZARD_WALK5,
+ S_WIZARD_WALK6,
+ S_WIZARD_WALK7,
+ S_WIZARD_WALK8,
+ S_WIZARD_ATK1,
+ S_WIZARD_ATK2,
+ S_WIZARD_ATK3,
+ S_WIZARD_ATK4,
+ S_WIZARD_ATK5,
+ S_WIZARD_ATK6,
+ S_WIZARD_ATK7,
+ S_WIZARD_ATK8,
+ S_WIZARD_ATK9,
+ S_WIZARD_PAIN1,
+ S_WIZARD_PAIN2,
+ S_WIZARD_DIE1,
+ S_WIZARD_DIE2,
+ S_WIZARD_DIE3,
+ S_WIZARD_DIE4,
+ S_WIZARD_DIE5,
+ S_WIZARD_DIE6,
+ S_WIZARD_DIE7,
+ S_WIZARD_DIE8,
+ S_WIZFX1_1,
+ S_WIZFX1_2,
+ S_WIZFXI1_1,
+ S_WIZFXI1_2,
+ S_WIZFXI1_3,
+ S_WIZFXI1_4,
+ S_WIZFXI1_5,
+ S_IMP_LOOK1,
+ S_IMP_LOOK2,
+ S_IMP_LOOK3,
+ S_IMP_LOOK4,
+ S_IMP_FLY1,
+ S_IMP_FLY2,
+ S_IMP_FLY3,
+ S_IMP_FLY4,
+ S_IMP_FLY5,
+ S_IMP_FLY6,
+ S_IMP_FLY7,
+ S_IMP_FLY8,
+ S_IMP_MEATK1,
+ S_IMP_MEATK2,
+ S_IMP_MEATK3,
+ S_IMP_MSATK1_1,
+ S_IMP_MSATK1_2,
+ S_IMP_MSATK1_3,
+ S_IMP_MSATK1_4,
+ S_IMP_MSATK1_5,
+ S_IMP_MSATK1_6,
+ S_IMP_MSATK2_1,
+ S_IMP_MSATK2_2,
+ S_IMP_MSATK2_3,
+ S_IMP_PAIN1,
+ S_IMP_PAIN2,
+ S_IMP_DIE1,
+ S_IMP_DIE2,
+ S_IMP_XDIE1,
+ S_IMP_XDIE2,
+ S_IMP_XDIE3,
+ S_IMP_XDIE4,
+ S_IMP_XDIE5,
+ S_IMP_CRASH1,
+ S_IMP_CRASH2,
+ S_IMP_CRASH3,
+ S_IMP_CRASH4,
+ S_IMP_XCRASH1,
+ S_IMP_XCRASH2,
+ S_IMP_XCRASH3,
+ S_IMP_CHUNKA1,
+ S_IMP_CHUNKA2,
+ S_IMP_CHUNKA3,
+ S_IMP_CHUNKB1,
+ S_IMP_CHUNKB2,
+ S_IMP_CHUNKB3,
+ S_IMPFX1,
+ S_IMPFX2,
+ S_IMPFX3,
+ S_IMPFXI1,
+ S_IMPFXI2,
+ S_IMPFXI3,
+ S_IMPFXI4,
+ S_KNIGHT_STND1,
+ S_KNIGHT_STND2,
+ S_KNIGHT_WALK1,
+ S_KNIGHT_WALK2,
+ S_KNIGHT_WALK3,
+ S_KNIGHT_WALK4,
+ S_KNIGHT_ATK1,
+ S_KNIGHT_ATK2,
+ S_KNIGHT_ATK3,
+ S_KNIGHT_ATK4,
+ S_KNIGHT_ATK5,
+ S_KNIGHT_ATK6,
+ S_KNIGHT_PAIN1,
+ S_KNIGHT_PAIN2,
+ S_KNIGHT_DIE1,
+ S_KNIGHT_DIE2,
+ S_KNIGHT_DIE3,
+ S_KNIGHT_DIE4,
+ S_KNIGHT_DIE5,
+ S_KNIGHT_DIE6,
+ S_KNIGHT_DIE7,
+ S_SPINAXE1,
+ S_SPINAXE2,
+ S_SPINAXE3,
+ S_SPINAXEX1,
+ S_SPINAXEX2,
+ S_SPINAXEX3,
+ S_REDAXE1,
+ S_REDAXE2,
+ S_REDAXEX1,
+ S_REDAXEX2,
+ S_REDAXEX3,
+ S_SRCR1_LOOK1,
+ S_SRCR1_LOOK2,
+ S_SRCR1_WALK1,
+ S_SRCR1_WALK2,
+ S_SRCR1_WALK3,
+ S_SRCR1_WALK4,
+ S_SRCR1_PAIN1,
+ S_SRCR1_ATK1,
+ S_SRCR1_ATK2,
+ S_SRCR1_ATK3,
+ S_SRCR1_ATK4,
+ S_SRCR1_ATK5,
+ S_SRCR1_ATK6,
+ S_SRCR1_ATK7,
+ S_SRCR1_DIE1,
+ S_SRCR1_DIE2,
+ S_SRCR1_DIE3,
+ S_SRCR1_DIE4,
+ S_SRCR1_DIE5,
+ S_SRCR1_DIE6,
+ S_SRCR1_DIE7,
+ S_SRCR1_DIE8,
+ S_SRCR1_DIE9,
+ S_SRCR1_DIE10,
+ S_SRCR1_DIE11,
+ S_SRCR1_DIE12,
+ S_SRCR1_DIE13,
+ S_SRCR1_DIE14,
+ S_SRCR1_DIE15,
+ S_SRCR1_DIE16,
+ S_SRCR1_DIE17,
+ S_SRCRFX1_1,
+ S_SRCRFX1_2,
+ S_SRCRFX1_3,
+ S_SRCRFXI1_1,
+ S_SRCRFXI1_2,
+ S_SRCRFXI1_3,
+ S_SRCRFXI1_4,
+ S_SRCRFXI1_5,
+ S_SOR2_RISE1,
+ S_SOR2_RISE2,
+ S_SOR2_RISE3,
+ S_SOR2_RISE4,
+ S_SOR2_RISE5,
+ S_SOR2_RISE6,
+ S_SOR2_RISE7,
+ S_SOR2_LOOK1,
+ S_SOR2_LOOK2,
+ S_SOR2_WALK1,
+ S_SOR2_WALK2,
+ S_SOR2_WALK3,
+ S_SOR2_WALK4,
+ S_SOR2_PAIN1,
+ S_SOR2_PAIN2,
+ S_SOR2_ATK1,
+ S_SOR2_ATK2,
+ S_SOR2_ATK3,
+ S_SOR2_TELE1,
+ S_SOR2_TELE2,
+ S_SOR2_TELE3,
+ S_SOR2_TELE4,
+ S_SOR2_TELE5,
+ S_SOR2_TELE6,
+ S_SOR2_DIE1,
+ S_SOR2_DIE2,
+ S_SOR2_DIE3,
+ S_SOR2_DIE4,
+ S_SOR2_DIE5,
+ S_SOR2_DIE6,
+ S_SOR2_DIE7,
+ S_SOR2_DIE8,
+ S_SOR2_DIE9,
+ S_SOR2_DIE10,
+ S_SOR2_DIE11,
+ S_SOR2_DIE12,
+ S_SOR2_DIE13,
+ S_SOR2_DIE14,
+ S_SOR2_DIE15,
+ S_SOR2FX1_1,
+ S_SOR2FX1_2,
+ S_SOR2FX1_3,
+ S_SOR2FXI1_1,
+ S_SOR2FXI1_2,
+ S_SOR2FXI1_3,
+ S_SOR2FXI1_4,
+ S_SOR2FXI1_5,
+ S_SOR2FXI1_6,
+ S_SOR2FXSPARK1,
+ S_SOR2FXSPARK2,
+ S_SOR2FXSPARK3,
+ S_SOR2FX2_1,
+ S_SOR2FX2_2,
+ S_SOR2FX2_3,
+ S_SOR2FXI2_1,
+ S_SOR2FXI2_2,
+ S_SOR2FXI2_3,
+ S_SOR2FXI2_4,
+ S_SOR2FXI2_5,
+ S_SOR2TELEFADE1,
+ S_SOR2TELEFADE2,
+ S_SOR2TELEFADE3,
+ S_SOR2TELEFADE4,
+ S_SOR2TELEFADE5,
+ S_SOR2TELEFADE6,
+ S_MNTR_LOOK1,
+ S_MNTR_LOOK2,
+ S_MNTR_WALK1,
+ S_MNTR_WALK2,
+ S_MNTR_WALK3,
+ S_MNTR_WALK4,
+ S_MNTR_ATK1_1,
+ S_MNTR_ATK1_2,
+ S_MNTR_ATK1_3,
+ S_MNTR_ATK2_1,
+ S_MNTR_ATK2_2,
+ S_MNTR_ATK2_3,
+ S_MNTR_ATK3_1,
+ S_MNTR_ATK3_2,
+ S_MNTR_ATK3_3,
+ S_MNTR_ATK3_4,
+ S_MNTR_ATK4_1,
+ S_MNTR_PAIN1,
+ S_MNTR_PAIN2,
+ S_MNTR_DIE1,
+ S_MNTR_DIE2,
+ S_MNTR_DIE3,
+ S_MNTR_DIE4,
+ S_MNTR_DIE5,
+ S_MNTR_DIE6,
+ S_MNTR_DIE7,
+ S_MNTR_DIE8,
+ S_MNTR_DIE9,
+ S_MNTR_DIE10,
+ S_MNTR_DIE11,
+ S_MNTR_DIE12,
+ S_MNTR_DIE13,
+ S_MNTR_DIE14,
+ S_MNTR_DIE15,
+ S_MNTRFX1_1,
+ S_MNTRFX1_2,
+ S_MNTRFXI1_1,
+ S_MNTRFXI1_2,
+ S_MNTRFXI1_3,
+ S_MNTRFXI1_4,
+ S_MNTRFXI1_5,
+ S_MNTRFXI1_6,
+ S_MNTRFX2_1,
+ S_MNTRFXI2_1,
+ S_MNTRFXI2_2,
+ S_MNTRFXI2_3,
+ S_MNTRFXI2_4,
+ S_MNTRFXI2_5,
+ S_MNTRFX3_1,
+ S_MNTRFX3_2,
+ S_MNTRFX3_3,
+ S_MNTRFX3_4,
+ S_MNTRFX3_5,
+ S_MNTRFX3_6,
+ S_MNTRFX3_7,
+ S_MNTRFX3_8,
+ S_MNTRFX3_9,
+ S_AKYY1,
+ S_AKYY2,
+ S_AKYY3,
+ S_AKYY4,
+ S_AKYY5,
+ S_AKYY6,
+ S_AKYY7,
+ S_AKYY8,
+ S_AKYY9,
+ S_AKYY10,
+ S_BKYY1,
+ S_BKYY2,
+ S_BKYY3,
+ S_BKYY4,
+ S_BKYY5,
+ S_BKYY6,
+ S_BKYY7,
+ S_BKYY8,
+ S_BKYY9,
+ S_BKYY10,
+ S_CKYY1,
+ S_CKYY2,
+ S_CKYY3,
+ S_CKYY4,
+ S_CKYY5,
+ S_CKYY6,
+ S_CKYY7,
+ S_CKYY8,
+ S_CKYY9,
+ S_AMG1,
+ S_AMG2_1,
+ S_AMG2_2,
+ S_AMG2_3,
+ S_AMM1,
+ S_AMM2,
+ S_AMC1,
+ S_AMC2_1,
+ S_AMC2_2,
+ S_AMC2_3,
+ S_AMS1_1,
+ S_AMS1_2,
+ S_AMS2_1,
+ S_AMS2_2,
+ S_AMP1_1,
+ S_AMP1_2,
+ S_AMP1_3,
+ S_AMP2_1,
+ S_AMP2_2,
+ S_AMP2_3,
+ S_AMB1_1,
+ S_AMB1_2,
+ S_AMB1_3,
+ S_AMB2_1,
+ S_AMB2_2,
+ S_AMB2_3,
+ S_SND_WIND,
+ S_SND_WATERFALL,
+ NUMSTATES
+} statenum_t;
+
+typedef struct
+{
+ spritenum_t sprite;
+ int frame;
+ int tics;
+ void (*action) ();
+ statenum_t nextstate;
+ int misc1, misc2;
+} state_t;
+
+extern state_t states[NUMSTATES];
+extern char *sprnames[];
+
+
+
+typedef enum
+{
+ MT_MISC0,
+ MT_ITEMSHIELD1,
+ MT_ITEMSHIELD2,
+ MT_MISC1,
+ MT_MISC2,
+ MT_ARTIINVISIBILITY,
+ MT_MISC3,
+ MT_ARTIFLY,
+ MT_ARTIINVULNERABILITY,
+ MT_ARTITOMEOFPOWER,
+ MT_ARTIEGG,
+ MT_EGGFX,
+ MT_ARTISUPERHEAL,
+ MT_MISC4,
+ MT_MISC5,
+ MT_FIREBOMB,
+ MT_ARTITELEPORT,
+ MT_POD,
+ MT_PODGOO,
+ MT_PODGENERATOR,
+ MT_SPLASH,
+ MT_SPLASHBASE,
+ MT_LAVASPLASH,
+ MT_LAVASMOKE,
+ MT_SLUDGECHUNK,
+ MT_SLUDGESPLASH,
+ MT_SKULLHANG70,
+ MT_SKULLHANG60,
+ MT_SKULLHANG45,
+ MT_SKULLHANG35,
+ MT_CHANDELIER,
+ MT_SERPTORCH,
+ MT_SMALLPILLAR,
+ MT_STALAGMITESMALL,
+ MT_STALAGMITELARGE,
+ MT_STALACTITESMALL,
+ MT_STALACTITELARGE,
+ MT_MISC6,
+ MT_BARREL,
+ MT_MISC7,
+ MT_MISC8,
+ MT_MISC9,
+ MT_MISC10,
+ MT_MISC11,
+ MT_KEYGIZMOBLUE,
+ MT_KEYGIZMOGREEN,
+ MT_KEYGIZMOYELLOW,
+ MT_KEYGIZMOFLOAT,
+ MT_MISC12,
+ MT_VOLCANOBLAST,
+ MT_VOLCANOTBLAST,
+ MT_TELEGLITGEN,
+ MT_TELEGLITGEN2,
+ MT_TELEGLITTER,
+ MT_TELEGLITTER2,
+ MT_TFOG,
+ MT_TELEPORTMAN,
+ MT_STAFFPUFF,
+ MT_STAFFPUFF2,
+ MT_BEAKPUFF,
+ MT_MISC13,
+ MT_GAUNTLETPUFF1,
+ MT_GAUNTLETPUFF2,
+ MT_MISC14,
+ MT_BLASTERFX1,
+ MT_BLASTERSMOKE,
+ MT_RIPPER,
+ MT_BLASTERPUFF1,
+ MT_BLASTERPUFF2,
+ MT_WMACE,
+ MT_MACEFX1,
+ MT_MACEFX2,
+ MT_MACEFX3,
+ MT_MACEFX4,
+ MT_WSKULLROD,
+ MT_HORNRODFX1,
+ MT_HORNRODFX2,
+ MT_RAINPLR1,
+ MT_RAINPLR2,
+ MT_RAINPLR3,
+ MT_RAINPLR4,
+ MT_GOLDWANDFX1,
+ MT_GOLDWANDFX2,
+ MT_GOLDWANDPUFF1,
+ MT_GOLDWANDPUFF2,
+ MT_WPHOENIXROD,
+ MT_PHOENIXFX1,
+ MT_PHOENIXFX_REMOVED, // In Heretic 1.0, but removed.
+ MT_PHOENIXPUFF,
+ MT_PHOENIXFX2,
+ MT_MISC15,
+ MT_CRBOWFX1,
+ MT_CRBOWFX2,
+ MT_CRBOWFX3,
+ MT_CRBOWFX4,
+ MT_BLOOD,
+ MT_BLOODSPLATTER,
+ MT_PLAYER,
+ MT_BLOODYSKULL,
+ MT_CHICPLAYER,
+ MT_CHICKEN,
+ MT_FEATHER,
+ MT_MUMMY,
+ MT_MUMMYLEADER,
+ MT_MUMMYGHOST,
+ MT_MUMMYLEADERGHOST,
+ MT_MUMMYSOUL,
+ MT_MUMMYFX1,
+ MT_BEAST,
+ MT_BEASTBALL,
+ MT_BURNBALL,
+ MT_BURNBALLFB,
+ MT_PUFFY,
+ MT_SNAKE,
+ MT_SNAKEPRO_A,
+ MT_SNAKEPRO_B,
+ MT_HEAD,
+ MT_HEADFX1,
+ MT_HEADFX2,
+ MT_HEADFX3,
+ MT_WHIRLWIND,
+ MT_CLINK,
+ MT_WIZARD,
+ MT_WIZFX1,
+ MT_IMP,
+ MT_IMPLEADER,
+ MT_IMPCHUNK1,
+ MT_IMPCHUNK2,
+ MT_IMPBALL,
+ MT_KNIGHT,
+ MT_KNIGHTGHOST,
+ MT_KNIGHTAXE,
+ MT_REDAXE,
+ MT_SORCERER1,
+ MT_SRCRFX1,
+ MT_SORCERER2,
+ MT_SOR2FX1,
+ MT_SOR2FXSPARK,
+ MT_SOR2FX2,
+ MT_SOR2TELEFADE,
+ MT_MINOTAUR,
+ MT_MNTRFX1,
+ MT_MNTRFX2,
+ MT_MNTRFX3,
+ MT_AKYY,
+ MT_BKYY,
+ MT_CKEY,
+ MT_AMGWNDWIMPY,
+ MT_AMGWNDHEFTY,
+ MT_AMMACEWIMPY,
+ MT_AMMACEHEFTY,
+ MT_AMCBOWWIMPY,
+ MT_AMCBOWHEFTY,
+ MT_AMSKRDWIMPY,
+ MT_AMSKRDHEFTY,
+ MT_AMPHRDWIMPY,
+ MT_AMPHRDHEFTY,
+ MT_AMBLSRWIMPY,
+ MT_AMBLSRHEFTY,
+ MT_SOUNDWIND,
+ MT_SOUNDWATERFALL,
+ NUMMOBJTYPES
+} mobjtype_t;
+
+typedef struct
+{
+ int doomednum;
+ int spawnstate;
+ int spawnhealth;
+ int seestate;
+ int seesound;
+ int reactiontime;
+ int attacksound;
+ int painstate;
+ int painchance;
+ int painsound;
+ int meleestate;
+ int missilestate;
+ int crashstate;
+ int deathstate;
+ int xdeathstate;
+ int deathsound;
+ int speed;
+ int radius;
+ int height;
+ int mass;
+ int damage;
+ int activesound;
+ int flags;
+ int flags2;
+} mobjinfo_t;
+
+extern mobjinfo_t mobjinfo[NUMMOBJTYPES];
+
+#endif /* #ifndef HERETIC_INFO_H */
+
diff --git a/src/heretic/m_random.c b/src/heretic/m_random.c
new file mode 100644
index 00000000..383d906c
--- /dev/null
+++ b/src/heretic/m_random.c
@@ -0,0 +1,78 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 "m_random.h"
+
+/*
+===============
+=
+= M_Random
+=
+= Returns a 0-255 number
+=
+===============
+*/
+
+const unsigned int rndtable[256] = {
+ 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
+};
+
+int rndindex = 0;
+int prndindex = 0;
+
+int P_Random(void)
+{
+ prndindex = (prndindex + 1) & 0xff;
+ return rndtable[prndindex];
+}
+
+int M_Random(void)
+{
+ rndindex = (rndindex + 1) & 0xff;
+ return rndtable[rndindex];
+}
+
+void M_ClearRandom(void)
+{
+ rndindex = prndindex = 0;
+}
+
diff --git a/src/heretic/m_random.h b/src/heretic/m_random.h
new file mode 100644
index 00000000..b1d2ca7d
--- /dev/null
+++ b/src/heretic/m_random.h
@@ -0,0 +1,42 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 HERETIC_M_RANDOM_H
+#define HERETIC_M_RANDOM_H
+
+// Most damage defined using HITDICE
+#define HITDICE(a) ((1+(P_Random()&7))*a)
+
+int M_Random(void);
+// returns a number from 0 to 255
+int P_Random(void);
+// as M_Random, but used only by the play simulation
+
+void M_ClearRandom(void);
+// fix randoms for demos
+
+extern int rndindex;
+
+#endif // HERETIC_M_RANDOM_H
+
diff --git a/src/heretic/mn_menu.c b/src/heretic/mn_menu.c
new file mode 100644
index 00000000..955e0124
--- /dev/null
+++ b/src/heretic/mn_menu.c
@@ -0,0 +1,1652 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// MN_menu.c
+
+#include <ctype.h>
+
+#include "deh_str.h"
+#include "doomdef.h"
+#include "doomkeys.h"
+#include "i_system.h"
+#include "i_swap.h"
+#include "m_controls.h"
+#include "p_local.h"
+#include "r_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+// Macros
+
+#define LEFT_DIR 0
+#define RIGHT_DIR 1
+#define ITEM_HEIGHT 20
+#define SELECTOR_XOFFSET (-28)
+#define SELECTOR_YOFFSET (-1)
+#define SLOTTEXTLEN 16
+#define ASCII_CURSOR '['
+
+// Types
+
+typedef enum
+{
+ ITT_EMPTY,
+ ITT_EFUNC,
+ ITT_LRFUNC,
+ ITT_SETMENU,
+ ITT_INERT
+} ItemType_t;
+
+typedef enum
+{
+ MENU_MAIN,
+ MENU_EPISODE,
+ MENU_SKILL,
+ MENU_OPTIONS,
+ MENU_OPTIONS2,
+ MENU_FILES,
+ MENU_LOAD,
+ MENU_SAVE,
+ MENU_NONE
+} MenuType_t;
+
+typedef struct
+{
+ ItemType_t type;
+ char *text;
+ boolean(*func) (int option);
+ int option;
+ MenuType_t menu;
+} MenuItem_t;
+
+typedef struct
+{
+ int x;
+ int y;
+ void (*drawFunc) (void);
+ int itemCount;
+ MenuItem_t *items;
+ int oldItPos;
+ MenuType_t prevMenu;
+} Menu_t;
+
+// Private Functions
+
+static void InitFonts(void);
+static void SetMenu(MenuType_t menu);
+static boolean SCNetCheck(int option);
+static boolean SCQuitGame(int option);
+static boolean SCEpisode(int option);
+static boolean SCSkill(int option);
+static boolean SCMouseSensi(int option);
+static boolean SCSfxVolume(int option);
+static boolean SCMusicVolume(int option);
+static boolean SCScreenSize(int option);
+static boolean SCLoadGame(int option);
+static boolean SCSaveGame(int option);
+static boolean SCMessages(int option);
+static boolean SCEndGame(int option);
+static boolean SCInfo(int option);
+static void DrawMainMenu(void);
+static void DrawEpisodeMenu(void);
+static void DrawSkillMenu(void);
+static void DrawOptionsMenu(void);
+static void DrawOptions2Menu(void);
+static void DrawFileSlots(Menu_t * menu);
+static void DrawFilesMenu(void);
+static void MN_DrawInfo(void);
+static void DrawLoadMenu(void);
+static void DrawSaveMenu(void);
+static void DrawSlider(Menu_t * menu, int item, int width, int slot);
+void MN_LoadSlotText(void);
+
+// External Data
+
+extern int usegamma;
+extern int detailLevel;
+extern int screenblocks;
+
+// Public Data
+
+boolean MenuActive;
+int InfoType;
+boolean messageson;
+
+// Private Data
+
+static int FontABaseLump;
+static int FontBBaseLump;
+static int SkullBaseLump;
+static Menu_t *CurrentMenu;
+static int CurrentItPos;
+static int MenuEpisode;
+static int MenuTime;
+static boolean soundchanged;
+
+boolean askforquit;
+boolean typeofask;
+static boolean FileMenuKeySteal;
+static boolean slottextloaded;
+static char SlotText[6][SLOTTEXTLEN + 2];
+static char oldSlotText[SLOTTEXTLEN + 2];
+static int SlotStatus[6];
+static int slotptr;
+static int currentSlot;
+static int quicksave;
+static int quickload;
+
+static MenuItem_t MainItems[] = {
+ {ITT_EFUNC, "NEW GAME", SCNetCheck, 1, MENU_EPISODE},
+ {ITT_SETMENU, "OPTIONS", NULL, 0, MENU_OPTIONS},
+ {ITT_SETMENU, "GAME FILES", NULL, 0, MENU_FILES},
+ {ITT_EFUNC, "INFO", SCInfo, 0, MENU_NONE},
+ {ITT_EFUNC, "QUIT GAME", SCQuitGame, 0, MENU_NONE}
+};
+
+static Menu_t MainMenu = {
+ 110, 56,
+ DrawMainMenu,
+ 5, MainItems,
+ 0,
+ MENU_NONE
+};
+
+static MenuItem_t EpisodeItems[] = {
+ {ITT_EFUNC, "CITY OF THE DAMNED", SCEpisode, 1, MENU_NONE},
+ {ITT_EFUNC, "HELL'S MAW", SCEpisode, 2, MENU_NONE},
+ {ITT_EFUNC, "THE DOME OF D'SPARIL", SCEpisode, 3, MENU_NONE},
+ {ITT_EFUNC, "THE OSSUARY", SCEpisode, 4, MENU_NONE},
+ {ITT_EFUNC, "THE STAGNANT DEMESNE", SCEpisode, 5, MENU_NONE}
+};
+
+static Menu_t EpisodeMenu = {
+ 80, 50,
+ DrawEpisodeMenu,
+ 3, EpisodeItems,
+ 0,
+ MENU_MAIN
+};
+
+static MenuItem_t FilesItems[] = {
+ {ITT_EFUNC, "LOAD GAME", SCNetCheck, 2, MENU_LOAD},
+ {ITT_SETMENU, "SAVE GAME", NULL, 0, MENU_SAVE}
+};
+
+static Menu_t FilesMenu = {
+ 110, 60,
+ DrawFilesMenu,
+ 2, FilesItems,
+ 0,
+ MENU_MAIN
+};
+
+static MenuItem_t LoadItems[] = {
+ {ITT_EFUNC, NULL, SCLoadGame, 0, MENU_NONE},
+ {ITT_EFUNC, NULL, SCLoadGame, 1, MENU_NONE},
+ {ITT_EFUNC, NULL, SCLoadGame, 2, MENU_NONE},
+ {ITT_EFUNC, NULL, SCLoadGame, 3, MENU_NONE},
+ {ITT_EFUNC, NULL, SCLoadGame, 4, MENU_NONE},
+ {ITT_EFUNC, NULL, SCLoadGame, 5, MENU_NONE}
+};
+
+static Menu_t LoadMenu = {
+ 70, 30,
+ DrawLoadMenu,
+ 6, LoadItems,
+ 0,
+ MENU_FILES
+};
+
+static MenuItem_t SaveItems[] = {
+ {ITT_EFUNC, NULL, SCSaveGame, 0, MENU_NONE},
+ {ITT_EFUNC, NULL, SCSaveGame, 1, MENU_NONE},
+ {ITT_EFUNC, NULL, SCSaveGame, 2, MENU_NONE},
+ {ITT_EFUNC, NULL, SCSaveGame, 3, MENU_NONE},
+ {ITT_EFUNC, NULL, SCSaveGame, 4, MENU_NONE},
+ {ITT_EFUNC, NULL, SCSaveGame, 5, MENU_NONE}
+};
+
+static Menu_t SaveMenu = {
+ 70, 30,
+ DrawSaveMenu,
+ 6, SaveItems,
+ 0,
+ MENU_FILES
+};
+
+static MenuItem_t SkillItems[] = {
+ {ITT_EFUNC, "THOU NEEDETH A WET-NURSE", SCSkill, sk_baby, MENU_NONE},
+ {ITT_EFUNC, "YELLOWBELLIES-R-US", SCSkill, sk_easy, MENU_NONE},
+ {ITT_EFUNC, "BRINGEST THEM ONETH", SCSkill, sk_medium, MENU_NONE},
+ {ITT_EFUNC, "THOU ART A SMITE-MEISTER", SCSkill, sk_hard, MENU_NONE},
+ {ITT_EFUNC, "BLACK PLAGUE POSSESSES THEE",
+ SCSkill, sk_nightmare, MENU_NONE}
+};
+
+static Menu_t SkillMenu = {
+ 38, 30,
+ DrawSkillMenu,
+ 5, SkillItems,
+ 2,
+ MENU_EPISODE
+};
+
+static MenuItem_t OptionsItems[] = {
+ {ITT_EFUNC, "END GAME", SCEndGame, 0, MENU_NONE},
+ {ITT_EFUNC, "MESSAGES : ", SCMessages, 0, MENU_NONE},
+ {ITT_LRFUNC, "MOUSE SENSITIVITY", SCMouseSensi, 0, MENU_NONE},
+ {ITT_EMPTY, NULL, NULL, 0, MENU_NONE},
+ {ITT_SETMENU, "MORE...", NULL, 0, MENU_OPTIONS2}
+};
+
+static Menu_t OptionsMenu = {
+ 88, 30,
+ DrawOptionsMenu,
+ 5, OptionsItems,
+ 0,
+ MENU_MAIN
+};
+
+static MenuItem_t Options2Items[] = {
+ {ITT_LRFUNC, "SCREEN SIZE", SCScreenSize, 0, MENU_NONE},
+ {ITT_EMPTY, NULL, NULL, 0, MENU_NONE},
+ {ITT_LRFUNC, "SFX VOLUME", SCSfxVolume, 0, MENU_NONE},
+ {ITT_EMPTY, NULL, NULL, 0, MENU_NONE},
+ {ITT_LRFUNC, "MUSIC VOLUME", SCMusicVolume, 0, MENU_NONE},
+ {ITT_EMPTY, NULL, NULL, 0, MENU_NONE}
+};
+
+static Menu_t Options2Menu = {
+ 90, 20,
+ DrawOptions2Menu,
+ 6, Options2Items,
+ 0,
+ MENU_OPTIONS
+};
+
+static Menu_t *Menus[] = {
+ &MainMenu,
+ &EpisodeMenu,
+ &SkillMenu,
+ &OptionsMenu,
+ &Options2Menu,
+ &FilesMenu,
+ &LoadMenu,
+ &SaveMenu
+};
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_Init
+//
+//---------------------------------------------------------------------------
+
+void MN_Init(void)
+{
+ InitFonts();
+ MenuActive = false;
+ messageson = true;
+ SkullBaseLump = W_GetNumForName(DEH_String("M_SKL00"));
+
+ if (gamemode == retail)
+ { // Add episodes 4 and 5 to the menu
+ EpisodeMenu.itemCount = 5;
+ EpisodeMenu.y -= ITEM_HEIGHT;
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC InitFonts
+//
+//---------------------------------------------------------------------------
+
+static void InitFonts(void)
+{
+ FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1;
+ FontBBaseLump = W_GetNumForName(DEH_String("FONTB_S")) + 1;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_DrTextA
+//
+// Draw text using font A.
+//
+//---------------------------------------------------------------------------
+
+void MN_DrTextA(char *text, int x, int y)
+{
+ char c;
+ patch_t *p;
+
+ while ((c = *text++) != 0)
+ {
+ if (c < 33)
+ {
+ x += 5;
+ }
+ else
+ {
+ p = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+ V_DrawPatch(x, y, p);
+ x += SHORT(p->width) - 1;
+ }
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC MN_TextAWidth
+//
+// Returns the pixel width of a string using font A.
+//
+//---------------------------------------------------------------------------
+
+int MN_TextAWidth(char *text)
+{
+ char c;
+ int width;
+ patch_t *p;
+
+ width = 0;
+ while ((c = *text++) != 0)
+ {
+ if (c < 33)
+ {
+ width += 5;
+ }
+ else
+ {
+ p = W_CacheLumpNum(FontABaseLump + c - 33, PU_CACHE);
+ width += SHORT(p->width) - 1;
+ }
+ }
+ return (width);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_DrTextB
+//
+// Draw text using font B.
+//
+//---------------------------------------------------------------------------
+
+void MN_DrTextB(char *text, int x, int y)
+{
+ char c;
+ patch_t *p;
+
+ while ((c = *text++) != 0)
+ {
+ if (c < 33)
+ {
+ x += 8;
+ }
+ else
+ {
+ p = W_CacheLumpNum(FontBBaseLump + c - 33, PU_CACHE);
+ V_DrawPatch(x, y, p);
+ x += SHORT(p->width) - 1;
+ }
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC MN_TextBWidth
+//
+// Returns the pixel width of a string using font B.
+//
+//---------------------------------------------------------------------------
+
+int MN_TextBWidth(char *text)
+{
+ char c;
+ int width;
+ patch_t *p;
+
+ width = 0;
+ while ((c = *text++) != 0)
+ {
+ if (c < 33)
+ {
+ width += 5;
+ }
+ else
+ {
+ p = W_CacheLumpNum(FontBBaseLump + c - 33, PU_CACHE);
+ width += SHORT(p->width) - 1;
+ }
+ }
+ return (width);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_Ticker
+//
+//---------------------------------------------------------------------------
+
+void MN_Ticker(void)
+{
+ if (MenuActive == false)
+ {
+ return;
+ }
+ MenuTime++;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_Drawer
+//
+//---------------------------------------------------------------------------
+
+char *QuitEndMsg[] = {
+ "ARE YOU SURE YOU WANT TO QUIT?",
+ "ARE YOU SURE YOU WANT TO END THE GAME?",
+ "DO YOU WANT TO QUICKSAVE THE GAME NAMED",
+ "DO YOU WANT TO QUICKLOAD THE GAME NAMED"
+};
+
+void MN_Drawer(void)
+{
+ int i;
+ int x;
+ int y;
+ MenuItem_t *item;
+ char *message;
+ char *selName;
+
+ if (MenuActive == false)
+ {
+ if (askforquit)
+ {
+ message = DEH_String(QuitEndMsg[typeofask - 1]);
+
+ MN_DrTextA(message, 160 - MN_TextAWidth(message) / 2, 80);
+ if (typeofask == 3)
+ {
+ MN_DrTextA(SlotText[quicksave - 1], 160 -
+ MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90);
+ MN_DrTextA(DEH_String("?"), 160 +
+ MN_TextAWidth(SlotText[quicksave - 1]) / 2, 90);
+ }
+ if (typeofask == 4)
+ {
+ MN_DrTextA(SlotText[quickload - 1], 160 -
+ MN_TextAWidth(SlotText[quickload - 1]) / 2, 90);
+ MN_DrTextA(DEH_String("?"), 160 +
+ MN_TextAWidth(SlotText[quickload - 1]) / 2, 90);
+ }
+ UpdateState |= I_FULLSCRN;
+ }
+ return;
+ }
+ else
+ {
+ UpdateState |= I_FULLSCRN;
+ if (InfoType)
+ {
+ MN_DrawInfo();
+ return;
+ }
+ if (screenblocks < 10)
+ {
+ BorderNeedRefresh = true;
+ }
+ if (CurrentMenu->drawFunc != NULL)
+ {
+ CurrentMenu->drawFunc();
+ }
+ x = CurrentMenu->x;
+ y = CurrentMenu->y;
+ item = CurrentMenu->items;
+ for (i = 0; i < CurrentMenu->itemCount; i++)
+ {
+ if (item->type != ITT_EMPTY && item->text)
+ {
+ MN_DrTextB(DEH_String(item->text), x, y);
+ }
+ y += ITEM_HEIGHT;
+ item++;
+ }
+ y = CurrentMenu->y + (CurrentItPos * ITEM_HEIGHT) + SELECTOR_YOFFSET;
+ selName = DEH_String(MenuTime & 16 ? "M_SLCTR1" : "M_SLCTR2");
+ V_DrawPatch(x + SELECTOR_XOFFSET, y,
+ W_CacheLumpName(selName, PU_CACHE));
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawMainMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawMainMenu(void)
+{
+ int frame;
+
+ frame = (MenuTime / 3) % 18;
+ V_DrawPatch(88, 0, W_CacheLumpName(DEH_String("M_HTIC"), PU_CACHE));
+ V_DrawPatch(40, 10, W_CacheLumpNum(SkullBaseLump + (17 - frame),
+ PU_CACHE));
+ V_DrawPatch(232, 10, W_CacheLumpNum(SkullBaseLump + frame, PU_CACHE));
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawEpisodeMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawEpisodeMenu(void)
+{
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawSkillMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawSkillMenu(void)
+{
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawFilesMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawFilesMenu(void)
+{
+// clear out the quicksave/quickload stuff
+ quicksave = 0;
+ quickload = 0;
+ players[consoleplayer].message = NULL;
+ players[consoleplayer].messageTics = 1;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawLoadMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawLoadMenu(void)
+{
+ char *title;
+
+ title = DEH_String("LOAD GAME");
+
+ MN_DrTextB(title, 160 - MN_TextBWidth(title) / 2, 10);
+ if (!slottextloaded)
+ {
+ MN_LoadSlotText();
+ }
+ DrawFileSlots(&LoadMenu);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawSaveMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawSaveMenu(void)
+{
+ char *title;
+
+ title = DEH_String("SAVE GAME");
+
+ MN_DrTextB(title, 160 - MN_TextBWidth(title) / 2, 10);
+ if (!slottextloaded)
+ {
+ MN_LoadSlotText();
+ }
+ DrawFileSlots(&SaveMenu);
+}
+
+//===========================================================================
+//
+// MN_LoadSlotText
+//
+// Loads in the text message for each slot
+//===========================================================================
+
+void MN_LoadSlotText(void)
+{
+ FILE *fp;
+ int count;
+ int i;
+ char name[256];
+
+ for (i = 0; i < 6; i++)
+ {
+ if (cdrom)
+ {
+ sprintf(name, SAVEGAMENAMECD "%d.hsg", i);
+ }
+ else
+ {
+ sprintf(name, SAVEGAMENAME "%d.hsg", i);
+ }
+ fp = fopen(name, "rb+");
+ if (!fp)
+ {
+ SlotText[i][0] = 0; // empty the string
+ SlotStatus[i] = 0;
+ continue;
+ }
+ count = fread(&SlotText[i], SLOTTEXTLEN, 1, fp);
+ fclose(fp);
+ SlotStatus[i] = 1;
+ }
+ slottextloaded = true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawFileSlots
+//
+//---------------------------------------------------------------------------
+
+static void DrawFileSlots(Menu_t * menu)
+{
+ int i;
+ int x;
+ int y;
+
+ x = menu->x;
+ y = menu->y;
+ for (i = 0; i < 6; i++)
+ {
+ V_DrawPatch(x, y, W_CacheLumpName(DEH_String("M_FSLOT"), PU_CACHE));
+ if (SlotStatus[i])
+ {
+ MN_DrTextA(SlotText[i], x + 5, y + 5);
+ }
+ y += ITEM_HEIGHT;
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawOptionsMenu
+//
+//---------------------------------------------------------------------------
+
+static void DrawOptionsMenu(void)
+{
+ if (messageson)
+ {
+ MN_DrTextB(DEH_String("ON"), 196, 50);
+ }
+ else
+ {
+ MN_DrTextB(DEH_String("OFF"), 196, 50);
+ }
+ DrawSlider(&OptionsMenu, 3, 10, mouseSensitivity);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawOptions2Menu
+//
+//---------------------------------------------------------------------------
+
+static void DrawOptions2Menu(void)
+{
+ DrawSlider(&Options2Menu, 1, 9, screenblocks - 3);
+ DrawSlider(&Options2Menu, 3, 16, snd_MaxVolume);
+ DrawSlider(&Options2Menu, 5, 16, snd_MusicVolume);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCNetCheck
+//
+//---------------------------------------------------------------------------
+
+static boolean SCNetCheck(int option)
+{
+ if (!netgame)
+ { // okay to go into the menu
+ return true;
+ }
+ switch (option)
+ {
+ case 1:
+ P_SetMessage(&players[consoleplayer],
+ "YOU CAN'T START A NEW GAME IN NETPLAY!", true);
+ break;
+ case 2:
+ P_SetMessage(&players[consoleplayer],
+ "YOU CAN'T LOAD A GAME IN NETPLAY!", true);
+ break;
+ default:
+ break;
+ }
+ MenuActive = false;
+ return false;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCQuitGame
+//
+//---------------------------------------------------------------------------
+
+static boolean SCQuitGame(int option)
+{
+ MenuActive = false;
+ askforquit = true;
+ typeofask = 1; //quit game
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCEndGame
+//
+//---------------------------------------------------------------------------
+
+static boolean SCEndGame(int option)
+{
+ if (demoplayback || netgame)
+ {
+ return false;
+ }
+ MenuActive = false;
+ askforquit = true;
+ typeofask = 2; //endgame
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCMessages
+//
+//---------------------------------------------------------------------------
+
+static boolean SCMessages(int option)
+{
+ messageson ^= 1;
+ if (messageson)
+ {
+ P_SetMessage(&players[consoleplayer], DEH_String("MESSAGES ON"), true);
+ }
+ else
+ {
+ P_SetMessage(&players[consoleplayer], DEH_String("MESSAGES OFF"), true);
+ }
+ S_StartSound(NULL, sfx_chat);
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCLoadGame
+//
+//---------------------------------------------------------------------------
+
+static boolean SCLoadGame(int option)
+{
+ char name[256];
+
+ if (!SlotStatus[option])
+ { // slot's empty...don't try and load
+ return false;
+ }
+ if (cdrom)
+ {
+ sprintf(name, SAVEGAMENAMECD "%d.hsg", option);
+ }
+ else
+ {
+ sprintf(name, SAVEGAMENAME "%d.hsg", option);
+ }
+ G_LoadGame(name);
+ MN_DeactivateMenu();
+ BorderNeedRefresh = true;
+ if (quickload == -1)
+ {
+ quickload = option + 1;
+ players[consoleplayer].message = NULL;
+ players[consoleplayer].messageTics = 1;
+ }
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCSaveGame
+//
+//---------------------------------------------------------------------------
+
+static boolean SCSaveGame(int option)
+{
+ char *ptr;
+
+ if (!FileMenuKeySteal)
+ {
+ FileMenuKeySteal = true;
+ strcpy(oldSlotText, SlotText[option]);
+ ptr = SlotText[option];
+ while (*ptr)
+ {
+ ptr++;
+ }
+ *ptr = '[';
+ *(ptr + 1) = 0;
+ SlotStatus[option]++;
+ currentSlot = option;
+ slotptr = ptr - SlotText[option];
+ return false;
+ }
+ else
+ {
+ G_SaveGame(option, SlotText[option]);
+ FileMenuKeySteal = false;
+ MN_DeactivateMenu();
+ }
+ BorderNeedRefresh = true;
+ if (quicksave == -1)
+ {
+ quicksave = option + 1;
+ players[consoleplayer].message = NULL;
+ players[consoleplayer].messageTics = 1;
+ }
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCEpisode
+//
+//---------------------------------------------------------------------------
+
+static boolean SCEpisode(int option)
+{
+ if (gamemode == shareware && option > 1)
+ {
+ P_SetMessage(&players[consoleplayer],
+ "ONLY AVAILABLE IN THE REGISTERED VERSION", true);
+ }
+ else
+ {
+ MenuEpisode = option;
+ SetMenu(MENU_SKILL);
+ }
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCSkill
+//
+//---------------------------------------------------------------------------
+
+static boolean SCSkill(int option)
+{
+ G_DeferedInitNew(option, MenuEpisode, 1);
+ MN_DeactivateMenu();
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCMouseSensi
+//
+//---------------------------------------------------------------------------
+
+static boolean SCMouseSensi(int option)
+{
+ if (option == RIGHT_DIR)
+ {
+ if (mouseSensitivity < 9)
+ {
+ mouseSensitivity++;
+ }
+ }
+ else if (mouseSensitivity)
+ {
+ mouseSensitivity--;
+ }
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCSfxVolume
+//
+//---------------------------------------------------------------------------
+
+static boolean SCSfxVolume(int option)
+{
+ if (option == RIGHT_DIR)
+ {
+ if (snd_MaxVolume < 15)
+ {
+ snd_MaxVolume++;
+ }
+ }
+ else if (snd_MaxVolume)
+ {
+ snd_MaxVolume--;
+ }
+ S_SetMaxVolume(false); // don't recalc the sound curve, yet
+ soundchanged = true; // we'll set it when we leave the menu
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCMusicVolume
+//
+//---------------------------------------------------------------------------
+
+static boolean SCMusicVolume(int option)
+{
+ if (option == RIGHT_DIR)
+ {
+ if (snd_MusicVolume < 15)
+ {
+ snd_MusicVolume++;
+ }
+ }
+ else if (snd_MusicVolume)
+ {
+ snd_MusicVolume--;
+ }
+ S_SetMusicVolume();
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCScreenSize
+//
+//---------------------------------------------------------------------------
+
+static boolean SCScreenSize(int option)
+{
+ if (option == RIGHT_DIR)
+ {
+ if (screenblocks < 11)
+ {
+ screenblocks++;
+ }
+ }
+ else if (screenblocks > 3)
+ {
+ screenblocks--;
+ }
+ R_SetViewSize(screenblocks, detailLevel);
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SCInfo
+//
+//---------------------------------------------------------------------------
+
+static boolean SCInfo(int option)
+{
+ InfoType = 1;
+ S_StartSound(NULL, sfx_dorcls);
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ return true;
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC MN_Responder
+//
+//---------------------------------------------------------------------------
+
+boolean MN_Responder(event_t * event)
+{
+ int charTyped;
+ int key;
+ int i;
+ MenuItem_t *item;
+ extern boolean automapactive;
+ static boolean shiftdown;
+ extern void D_StartTitle(void);
+ extern void G_CheckDemoStatus(void);
+ char *textBuffer;
+
+ // "close" button pressed on window?
+ if (event->type == ev_quit)
+ {
+ SCQuitGame(0);
+ S_StartSound(NULL, sfx_chat);
+ return true;
+ }
+
+ if (event->data1 == KEY_RSHIFT)
+ {
+ shiftdown = (event->type == ev_keydown);
+ }
+ if (event->type != ev_keydown)
+ {
+ return (false);
+ }
+ key = event->data1;
+ charTyped = event->data2;
+
+ if (InfoType)
+ {
+ if (gamemode == shareware)
+ {
+ InfoType = (InfoType + 1) % 5;
+ }
+ else
+ {
+ InfoType = (InfoType + 1) % 4;
+ }
+ if (key == KEY_ESCAPE)
+ {
+ InfoType = 0;
+ }
+ if (!InfoType)
+ {
+ paused = false;
+ MN_DeactivateMenu();
+ SB_state = -1; //refresh the statbar
+ BorderNeedRefresh = true;
+ }
+ S_StartSound(NULL, sfx_dorcls);
+ return (true); //make the info screen eat the keypress
+ }
+
+ if (ravpic && key == KEY_F1)
+ {
+ G_ScreenShot();
+ return (true);
+ }
+
+ if (askforquit)
+ {
+ if (key == key_menu_confirm)
+ {
+ switch (typeofask)
+ {
+ case 1:
+ G_CheckDemoStatus();
+ I_Quit();
+ return false;
+
+ case 2:
+ players[consoleplayer].messageTics = 0;
+ //set the msg to be cleared
+ players[consoleplayer].message = NULL;
+ paused = false;
+ I_SetPalette(W_CacheLumpName
+ ("PLAYPAL", PU_CACHE));
+ D_StartTitle(); // go to intro/demo mode.
+ break;
+
+ case 3:
+ P_SetMessage(&players[consoleplayer],
+ "QUICKSAVING....", false);
+ FileMenuKeySteal = true;
+ SCSaveGame(quicksave - 1);
+ BorderNeedRefresh = true;
+ break;
+
+ case 4:
+ P_SetMessage(&players[consoleplayer],
+ "QUICKLOADING....", false);
+ SCLoadGame(quickload - 1);
+ BorderNeedRefresh = true;
+ break;
+
+ default:
+ break;
+ }
+
+ askforquit = false;
+ typeofask = 0;
+
+ return true;
+ }
+ else if (key == key_menu_abort || key == KEY_ESCAPE)
+ {
+ players[consoleplayer].messageTics = 1; //set the msg to be cleared
+ askforquit = false;
+ typeofask = 0;
+ paused = false;
+ UpdateState |= I_FULLSCRN;
+ BorderNeedRefresh = true;
+ return true;
+ }
+
+ return false; // don't let the keys filter thru
+ }
+
+ if (!MenuActive && !chatmodeon)
+ {
+ if (key == key_menu_decscreen)
+ {
+ if (automapactive)
+ { // Don't screen size in automap
+ return (false);
+ }
+ SCScreenSize(LEFT_DIR);
+ S_StartSound(NULL, sfx_keyup);
+ BorderNeedRefresh = true;
+ UpdateState |= I_FULLSCRN;
+ return (true);
+ }
+ else if (key == key_menu_incscreen)
+ {
+ if (automapactive)
+ { // Don't screen size in automap
+ return (false);
+ }
+ SCScreenSize(RIGHT_DIR);
+ S_StartSound(NULL, sfx_keyup);
+ BorderNeedRefresh = true;
+ UpdateState |= I_FULLSCRN;
+ return (true);
+ }
+ else if (key == key_menu_help) // F1
+ {
+ SCInfo(0); // start up info screens
+ MenuActive = true;
+ return (true);
+ }
+ else if (key == key_menu_save) // F2 (save game)
+ {
+ if (gamestate == GS_LEVEL && !demoplayback)
+ {
+ MenuActive = true;
+ FileMenuKeySteal = false;
+ MenuTime = 0;
+ CurrentMenu = &SaveMenu;
+ CurrentItPos = CurrentMenu->oldItPos;
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ S_StartSound(NULL, sfx_dorcls);
+ slottextloaded = false; //reload the slot text, when needed
+ }
+ return true;
+ }
+ else if (key == key_menu_load) // F3 (load game)
+ {
+ if (SCNetCheck(2))
+ {
+ MenuActive = true;
+ FileMenuKeySteal = false;
+ MenuTime = 0;
+ CurrentMenu = &LoadMenu;
+ CurrentItPos = CurrentMenu->oldItPos;
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ S_StartSound(NULL, sfx_dorcls);
+ slottextloaded = false; //reload the slot text, when needed
+ }
+ return true;
+ }
+ else if (key == key_menu_volume) // F4 (volume)
+ {
+ MenuActive = true;
+ FileMenuKeySteal = false;
+ MenuTime = 0;
+ CurrentMenu = &Options2Menu;
+ CurrentItPos = CurrentMenu->oldItPos;
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ S_StartSound(NULL, sfx_dorcls);
+ slottextloaded = false; //reload the slot text, when needed
+ return true;
+ }
+ else if (key == key_menu_detail) // F5 (detail)
+ {
+ // F5 isn't used in Heretic. (detail level)
+ return true;
+ }
+ else if (key == key_menu_qsave) // F6 (quicksave)
+ {
+ if (gamestate == GS_LEVEL && !demoplayback)
+ {
+ if (!quicksave || quicksave == -1)
+ {
+ MenuActive = true;
+ FileMenuKeySteal = false;
+ MenuTime = 0;
+ CurrentMenu = &SaveMenu;
+ CurrentItPos = CurrentMenu->oldItPos;
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ S_StartSound(NULL, sfx_dorcls);
+ slottextloaded = false; //reload the slot text, when needed
+ quicksave = -1;
+ P_SetMessage(&players[consoleplayer],
+ "CHOOSE A QUICKSAVE SLOT", true);
+ }
+ else
+ {
+ askforquit = true;
+ typeofask = 3;
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ S_StartSound(NULL, sfx_chat);
+ }
+ }
+ return true;
+ }
+ else if (key == key_menu_endgame) // F7 (end game)
+ {
+ if (gamestate == GS_LEVEL && !demoplayback)
+ {
+ S_StartSound(NULL, sfx_chat);
+ SCEndGame(0);
+ }
+ return true;
+ }
+ else if (key == key_menu_messages) // F8 (toggle messages)
+ {
+ SCMessages(0);
+ return true;
+ }
+ else if (key == key_menu_qload) // F9 (quickload)
+ {
+ if (!quickload || quickload == -1)
+ {
+ MenuActive = true;
+ FileMenuKeySteal = false;
+ MenuTime = 0;
+ CurrentMenu = &LoadMenu;
+ CurrentItPos = CurrentMenu->oldItPos;
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ S_StartSound(NULL, sfx_dorcls);
+ slottextloaded = false; //reload the slot text, when needed
+ quickload = -1;
+ P_SetMessage(&players[consoleplayer],
+ "CHOOSE A QUICKLOAD SLOT", true);
+ }
+ else
+ {
+ askforquit = true;
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ typeofask = 4;
+ S_StartSound(NULL, sfx_chat);
+ }
+ return true;
+ }
+ else if (key == key_menu_quit) // F10 (quit)
+ {
+ if (gamestate == GS_LEVEL)
+ {
+ SCQuitGame(0);
+ S_StartSound(NULL, sfx_chat);
+ }
+ return true;
+ }
+ else if (key == key_menu_gamma) // F11 (gamma correction)
+ {
+ usegamma++;
+ if (usegamma > 4)
+ {
+ usegamma = 0;
+ }
+ I_SetPalette((byte *) W_CacheLumpName("PLAYPAL", PU_CACHE));
+ return true;
+ }
+
+ }
+
+ if (!MenuActive)
+ {
+ if (key == key_menu_activate || gamestate == GS_DEMOSCREEN || demoplayback)
+ {
+ MN_ActivateMenu();
+ return (true);
+ }
+ return (false);
+ }
+ if (!FileMenuKeySteal)
+ {
+ item = &CurrentMenu->items[CurrentItPos];
+
+ if (key == key_menu_down) // Next menu item
+ {
+ do
+ {
+ if (CurrentItPos + 1 > CurrentMenu->itemCount - 1)
+ {
+ CurrentItPos = 0;
+ }
+ else
+ {
+ CurrentItPos++;
+ }
+ }
+ while (CurrentMenu->items[CurrentItPos].type == ITT_EMPTY);
+ S_StartSound(NULL, sfx_switch);
+ return (true);
+ }
+ else if (key == key_menu_up) // Previous menu item
+ {
+ do
+ {
+ if (CurrentItPos == 0)
+ {
+ CurrentItPos = CurrentMenu->itemCount - 1;
+ }
+ else
+ {
+ CurrentItPos--;
+ }
+ }
+ while (CurrentMenu->items[CurrentItPos].type == ITT_EMPTY);
+ S_StartSound(NULL, sfx_switch);
+ return (true);
+ }
+ else if (key == key_menu_left) // Slider left
+ {
+ if (item->type == ITT_LRFUNC && item->func != NULL)
+ {
+ item->func(LEFT_DIR);
+ S_StartSound(NULL, sfx_keyup);
+ }
+ return (true);
+ }
+ else if (key == key_menu_right) // Slider right
+ {
+ if (item->type == ITT_LRFUNC && item->func != NULL)
+ {
+ item->func(RIGHT_DIR);
+ S_StartSound(NULL, sfx_keyup);
+ }
+ return (true);
+ }
+ else if (key == key_menu_forward) // Activate item (enter)
+ {
+ if (item->type == ITT_SETMENU)
+ {
+ SetMenu(item->menu);
+ }
+ else if (item->func != NULL)
+ {
+ CurrentMenu->oldItPos = CurrentItPos;
+ if (item->type == ITT_LRFUNC)
+ {
+ item->func(RIGHT_DIR);
+ }
+ else if (item->type == ITT_EFUNC)
+ {
+ if (item->func(item->option))
+ {
+ if (item->menu != MENU_NONE)
+ {
+ SetMenu(item->menu);
+ }
+ }
+ }
+ }
+ S_StartSound(NULL, sfx_dorcls);
+ return (true);
+ }
+ else if (key == key_menu_activate) // Toggle menu
+ {
+ MN_DeactivateMenu();
+ return (true);
+ }
+ else if (key == key_menu_back) // Go back to previous menu
+ {
+ S_StartSound(NULL, sfx_switch);
+ if (CurrentMenu->prevMenu == MENU_NONE)
+ {
+ MN_DeactivateMenu();
+ }
+ else
+ {
+ SetMenu(CurrentMenu->prevMenu);
+ }
+ return (true);
+ }
+ else if (charTyped != 0)
+ {
+ // Jump to menu item based on first letter:
+
+ for (i = 0; i < CurrentMenu->itemCount; i++)
+ {
+ if (CurrentMenu->items[i].text)
+ {
+ if (toupper(charTyped)
+ == toupper(DEH_String(CurrentMenu->items[i].text)[0]))
+ {
+ CurrentItPos = i;
+ return (true);
+ }
+ }
+ }
+ }
+
+ return (false);
+ }
+ else
+ { // Editing file names
+ textBuffer = &SlotText[currentSlot][slotptr];
+ if (key == KEY_BACKSPACE)
+ {
+ if (slotptr)
+ {
+ *textBuffer-- = 0;
+ *textBuffer = ASCII_CURSOR;
+ slotptr--;
+ }
+ return (true);
+ }
+ if (key == KEY_ESCAPE)
+ {
+ memset(SlotText[currentSlot], 0, SLOTTEXTLEN + 2);
+ strcpy(SlotText[currentSlot], oldSlotText);
+ SlotStatus[currentSlot]--;
+ MN_DeactivateMenu();
+ return (true);
+ }
+ if (key == KEY_ENTER)
+ {
+ SlotText[currentSlot][slotptr] = 0; // clear the cursor
+ item = &CurrentMenu->items[CurrentItPos];
+ CurrentMenu->oldItPos = CurrentItPos;
+ if (item->type == ITT_EFUNC)
+ {
+ item->func(item->option);
+ if (item->menu != MENU_NONE)
+ {
+ SetMenu(item->menu);
+ }
+ }
+ return (true);
+ }
+ if (slotptr < SLOTTEXTLEN && key != KEY_BACKSPACE)
+ {
+ if (isalpha(charTyped))
+ {
+ *textBuffer++ = toupper(charTyped);
+ *textBuffer = ASCII_CURSOR;
+ slotptr++;
+ return (true);
+ }
+ if (isdigit(charTyped) || charTyped == ' '
+ || charTyped == ',' || charTyped == '.' || charTyped == '-'
+ || charTyped == '!')
+ {
+ *textBuffer++ = charTyped;
+ *textBuffer = ASCII_CURSOR;
+ slotptr++;
+ return (true);
+ }
+ }
+ return (true);
+ }
+ return (false);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_ActivateMenu
+//
+//---------------------------------------------------------------------------
+
+void MN_ActivateMenu(void)
+{
+ if (MenuActive)
+ {
+ return;
+ }
+ if (paused)
+ {
+ S_ResumeSound();
+ }
+ MenuActive = true;
+ FileMenuKeySteal = false;
+ MenuTime = 0;
+ CurrentMenu = &MainMenu;
+ CurrentItPos = CurrentMenu->oldItPos;
+ if (!netgame && !demoplayback)
+ {
+ paused = true;
+ }
+ S_StartSound(NULL, sfx_dorcls);
+ slottextloaded = false; //reload the slot text, when needed
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_DeactivateMenu
+//
+//---------------------------------------------------------------------------
+
+void MN_DeactivateMenu(void)
+{
+ CurrentMenu->oldItPos = CurrentItPos;
+ MenuActive = false;
+ if (!netgame)
+ {
+ paused = false;
+ }
+ S_StartSound(NULL, sfx_dorcls);
+ if (soundchanged)
+ {
+ S_SetMaxVolume(true); //recalc the sound curve
+ soundchanged = false;
+ }
+ players[consoleplayer].message = NULL;
+ players[consoleplayer].messageTics = 1;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC MN_DrawInfo
+//
+//---------------------------------------------------------------------------
+
+void MN_DrawInfo(void)
+{
+ I_SetPalette(W_CacheLumpName("PLAYPAL", PU_CACHE));
+ V_DrawRawScreen(W_CacheLumpNum(W_GetNumForName("TITLE") + InfoType,
+ PU_CACHE));
+// V_DrawPatch(0, 0, W_CacheLumpNum(W_GetNumForName("TITLE")+InfoType,
+// PU_CACHE));
+}
+
+
+//---------------------------------------------------------------------------
+//
+// PROC SetMenu
+//
+//---------------------------------------------------------------------------
+
+static void SetMenu(MenuType_t menu)
+{
+ CurrentMenu->oldItPos = CurrentItPos;
+ CurrentMenu = Menus[menu];
+ CurrentItPos = CurrentMenu->oldItPos;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawSlider
+//
+//---------------------------------------------------------------------------
+
+static void DrawSlider(Menu_t * menu, int item, int width, int slot)
+{
+ int x;
+ int y;
+ int x2;
+ int count;
+
+ x = menu->x + 24;
+ y = menu->y + 2 + (item * ITEM_HEIGHT);
+ V_DrawPatch(x - 32, y, W_CacheLumpName(DEH_String("M_SLDLT"), PU_CACHE));
+ for (x2 = x, count = width; count--; x2 += 8)
+ {
+ V_DrawPatch(x2, y, W_CacheLumpName(DEH_String(count & 1 ? "M_SLDMD1"
+ : "M_SLDMD2"), PU_CACHE));
+ }
+ V_DrawPatch(x2, y, W_CacheLumpName(DEH_String("M_SLDRT"), PU_CACHE));
+ V_DrawPatch(x + 4 + slot * 8, y + 7,
+ W_CacheLumpName(DEH_String("M_SLDKB"), PU_CACHE));
+}
diff --git a/src/heretic/p_action.h b/src/heretic/p_action.h
new file mode 100644
index 00000000..8d8e383c
--- /dev/null
+++ b/src/heretic/p_action.h
@@ -0,0 +1,160 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+//
+// External definitions for action pointer functions.
+//
+//-----------------------------------------------------------------------------
+
+#ifndef HERETIC_P_ACTION_H
+#define HERETIC_P_ACTION_H
+
+void A_FreeTargMobj();
+void A_RestoreSpecialThing1();
+void A_RestoreSpecialThing2();
+void A_HideThing();
+void A_UnHideThing();
+void A_RestoreArtifact();
+void A_Scream();
+void A_Explode();
+void A_PodPain();
+void A_RemovePod();
+void A_MakePod();
+void A_InitKeyGizmo();
+void A_VolcanoSet();
+void A_VolcanoBlast();
+void A_BeastPuff();
+void A_VolcBallImpact();
+void A_SpawnTeleGlitter();
+void A_SpawnTeleGlitter2();
+void A_AccTeleGlitter();
+void A_Light0();
+void A_WeaponReady();
+void A_Lower();
+void A_Raise();
+void A_StaffAttackPL1();
+void A_ReFire();
+void A_StaffAttackPL2();
+void A_BeakReady();
+void A_BeakRaise();
+void A_BeakAttackPL1();
+void A_BeakAttackPL2();
+void A_GauntletAttack();
+void A_FireBlasterPL1();
+void A_FireBlasterPL2();
+void A_SpawnRippers();
+void A_FireMacePL1();
+void A_FireMacePL2();
+void A_MacePL1Check();
+void A_MaceBallImpact();
+void A_MaceBallImpact2();
+void A_DeathBallImpact();
+void A_FireSkullRodPL1();
+void A_FireSkullRodPL2();
+void A_SkullRodPL2Seek();
+void A_AddPlayerRain();
+void A_HideInCeiling();
+void A_SkullRodStorm();
+void A_RainImpact();
+void A_FireGoldWandPL1();
+void A_FireGoldWandPL2();
+void A_FirePhoenixPL1();
+void A_InitPhoenixPL2();
+void A_FirePhoenixPL2();
+void A_ShutdownPhoenixPL2();
+void A_PhoenixPuff();
+void A_RemovedPhoenixFunc();
+void A_FlameEnd();
+void A_FloatPuff();
+void A_FireCrossbowPL1();
+void A_FireCrossbowPL2();
+void A_BoltSpark();
+void A_Pain();
+void A_NoBlocking();
+void A_AddPlayerCorpse();
+void A_SkullPop();
+void A_FlameSnd();
+void A_CheckBurnGone();
+void A_CheckSkullFloor();
+void A_CheckSkullDone();
+void A_Feathers();
+void A_ChicLook();
+void A_ChicChase();
+void A_ChicPain();
+void A_FaceTarget();
+void A_ChicAttack();
+void A_Look();
+void A_Chase();
+void A_MummyAttack();
+void A_MummyAttack2();
+void A_MummySoul();
+void A_ContMobjSound();
+void A_MummyFX1Seek();
+void A_BeastAttack();
+void A_SnakeAttack();
+void A_SnakeAttack2();
+void A_HeadAttack();
+void A_BossDeath();
+void A_HeadIceImpact();
+void A_HeadFireGrow();
+void A_WhirlwindSeek();
+void A_ClinkAttack();
+void A_WizAtk1();
+void A_WizAtk2();
+void A_WizAtk3();
+void A_GhostOff();
+void A_ImpMeAttack();
+void A_ImpMsAttack();
+void A_ImpMsAttack2();
+void A_ImpDeath();
+void A_ImpXDeath1();
+void A_ImpXDeath2();
+void A_ImpExplode();
+void A_KnightAttack();
+void A_DripBlood();
+void A_Sor1Chase();
+void A_Sor1Pain();
+void A_Srcr1Attack();
+void A_SorZap();
+void A_SorcererRise();
+void A_SorRise();
+void A_SorSightSnd();
+void A_Srcr2Decide();
+void A_Srcr2Attack();
+void A_Sor2DthInit();
+void A_SorDSph();
+void A_Sor2DthLoop();
+void A_SorDExp();
+void A_SorDBon();
+void A_BlueSpark();
+void A_GenWizard();
+void A_MinotaurAtk1();
+void A_MinotaurDecide();
+void A_MinotaurAtk2();
+void A_MinotaurAtk3();
+void A_MinotaurCharge();
+void A_MntrFloorFire();
+void A_ESound();
+
+#endif /* #ifndef HERETIC_P_ACTION_H */
+
diff --git a/src/heretic/p_ceilng.c b/src/heretic/p_ceilng.c
new file mode 100644
index 00000000..bf7ef676
--- /dev/null
+++ b/src/heretic/p_ceilng.c
@@ -0,0 +1,263 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 "doomdef.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+//==================================================================
+//==================================================================
+//
+// CEILINGS
+//
+//==================================================================
+//==================================================================
+
+ceiling_t *activeceilings[MAXCEILINGS];
+
+//==================================================================
+//
+// T_MoveCeiling
+//
+//==================================================================
+void T_MoveCeiling(ceiling_t * ceiling)
+{
+ result_e res;
+
+ switch (ceiling->direction)
+ {
+ case 0: // IN STASIS
+ break;
+ case 1: // UP
+ res = T_MovePlane(ceiling->sector, ceiling->speed,
+ ceiling->topheight, false, 1,
+ ceiling->direction);
+ if (!(leveltime & 7))
+ S_StartSound(&ceiling->sector->soundorg,
+ sfx_dormov);
+ if (res == pastdest)
+ switch (ceiling->type)
+ {
+ case raiseToHighest:
+ P_RemoveActiveCeiling(ceiling);
+ break;
+ case fastCrushAndRaise:
+ case crushAndRaise:
+ ceiling->direction = -1;
+ break;
+ default:
+ break;
+ }
+ break;
+ case -1: // DOWN
+ res = T_MovePlane(ceiling->sector, ceiling->speed,
+ ceiling->bottomheight, ceiling->crush, 1,
+ ceiling->direction);
+ if (!(leveltime & 7))
+ S_StartSound(&ceiling->sector->soundorg,
+ sfx_dormov);
+ if (res == pastdest)
+ switch (ceiling->type)
+ {
+ case crushAndRaise:
+ ceiling->speed = CEILSPEED;
+ case fastCrushAndRaise:
+ ceiling->direction = 1;
+ break;
+ case lowerAndCrush:
+ case lowerToFloor:
+ P_RemoveActiveCeiling(ceiling);
+ break;
+ default:
+ break;
+ }
+ else if (res == crushed)
+ switch (ceiling->type)
+ {
+ case crushAndRaise:
+ case lowerAndCrush:
+ ceiling->speed = CEILSPEED / 8;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+}
+
+//==================================================================
+//
+// EV_DoCeiling
+// Move a ceiling up/down and all around!
+//
+//==================================================================
+int EV_DoCeiling(line_t * line, ceiling_e type)
+{
+ int secnum, rtn;
+ sector_t *sec;
+ ceiling_t *ceiling;
+
+ secnum = -1;
+ rtn = 0;
+
+ //
+ // Reactivate in-stasis ceilings...for certain types.
+ //
+ switch (type)
+ {
+ case fastCrushAndRaise:
+ case crushAndRaise:
+ P_ActivateInStasisCeiling(line);
+ default:
+ break;
+ }
+
+ while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
+ {
+ sec = &sectors[secnum];
+ if (sec->specialdata)
+ continue;
+
+ //
+ // new door thinker
+ //
+ rtn = 1;
+ ceiling = Z_Malloc(sizeof(*ceiling), PU_LEVSPEC, 0);
+ P_AddThinker(&ceiling->thinker);
+ sec->specialdata = ceiling;
+ ceiling->thinker.function = T_MoveCeiling;
+ ceiling->sector = sec;
+ ceiling->crush = false;
+ 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 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;
+ }
+
+ ceiling->tag = sec->tag;
+ ceiling->type = type;
+ P_AddActiveCeiling(ceiling);
+ }
+ return rtn;
+}
+
+//==================================================================
+//
+// Add an active ceiling
+//
+//==================================================================
+void P_AddActiveCeiling(ceiling_t * c)
+{
+ int i;
+ for (i = 0; i < MAXCEILINGS; i++)
+ if (activeceilings[i] == NULL)
+ {
+ activeceilings[i] = c;
+ return;
+ }
+}
+
+//==================================================================
+//
+// Remove a ceiling's thinker
+//
+//==================================================================
+void P_RemoveActiveCeiling(ceiling_t * c)
+{
+ int i;
+
+ for (i = 0; i < MAXCEILINGS; i++)
+ if (activeceilings[i] == c)
+ {
+ activeceilings[i]->sector->specialdata = NULL;
+ P_RemoveThinker(&activeceilings[i]->thinker);
+ activeceilings[i] = NULL;
+ break;
+ }
+}
+
+//==================================================================
+//
+// Restart a ceiling that's in-stasis
+//
+//==================================================================
+void P_ActivateInStasisCeiling(line_t * line)
+{
+ int i;
+
+ for (i = 0; i < MAXCEILINGS; i++)
+ if (activeceilings[i] && (activeceilings[i]->tag == line->tag) &&
+ (activeceilings[i]->direction == 0))
+ {
+ activeceilings[i]->direction = activeceilings[i]->olddirection;
+ activeceilings[i]->thinker.function = T_MoveCeiling;
+ }
+}
+
+//==================================================================
+//
+// EV_CeilingCrushStop
+// Stop a ceiling from crushing!
+//
+//==================================================================
+int EV_CeilingCrushStop(line_t * line)
+{
+ int i;
+ int rtn;
+
+ rtn = 0;
+ for (i = 0; i < MAXCEILINGS; i++)
+ if (activeceilings[i] && (activeceilings[i]->tag == line->tag) &&
+ (activeceilings[i]->direction != 0))
+ {
+ activeceilings[i]->olddirection = activeceilings[i]->direction;
+ activeceilings[i]->thinker.function = NULL;
+ activeceilings[i]->direction = 0; // in-stasis
+ rtn = 1;
+ }
+
+ return rtn;
+}
diff --git a/src/heretic/p_doors.c b/src/heretic/p_doors.c
new file mode 100644
index 00000000..56e37119
--- /dev/null
+++ b/src/heretic/p_doors.c
@@ -0,0 +1,389 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_doors.c
+
+#include "doomdef.h"
+#include "deh_str.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+//==================================================================
+//==================================================================
+//
+// VERTICAL DOORS
+//
+//==================================================================
+//==================================================================
+
+//==================================================================
+//
+// T_VerticalDoor
+//
+//==================================================================
+void T_VerticalDoor(vldoor_t * door)
+{
+ result_e res;
+
+ switch (door->direction)
+ {
+ case 0: // WAITING
+ if (!--door->topcountdown)
+ switch (door->type)
+ {
+ case normal:
+ door->direction = -1; // time to go back down
+ S_StartSound(&door->sector->soundorg, sfx_doropn);
+ break;
+ case close30ThenOpen:
+ door->direction = 1;
+ S_StartSound(&door->sector->soundorg, sfx_doropn);
+ break;
+ default:
+ break;
+ }
+ break;
+ case 2: // INITIAL WAIT
+ if (!--door->topcountdown)
+ {
+ switch (door->type)
+ {
+ case raiseIn5Mins:
+ door->direction = 1;
+ door->type = normal;
+ S_StartSound(&door->sector->soundorg, sfx_doropn);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case -1: // DOWN
+ res = T_MovePlane(door->sector, door->speed,
+ door->sector->floorheight, false, 1,
+ door->direction);
+ if (res == pastdest)
+ {
+ switch (door->type)
+ {
+ case normal:
+ case close:
+ door->sector->specialdata = NULL;
+ P_RemoveThinker(&door->thinker); // unlink and free
+ S_StartSound(&door->sector->soundorg, sfx_dorcls);
+ break;
+ case close30ThenOpen:
+ door->direction = 0;
+ door->topcountdown = 35 * 30;
+ break;
+ default:
+ break;
+ }
+ }
+ else if (res == crushed)
+ {
+ switch (door->type)
+ {
+ case close: // DON'T GO BACK UP!
+ break;
+ default:
+ door->direction = 1;
+ S_StartSound(&door->sector->soundorg, sfx_doropn);
+ break;
+ }
+ }
+ break;
+ case 1: // UP
+ res = T_MovePlane(door->sector, door->speed,
+ door->topheight, false, 1, door->direction);
+ if (res == pastdest)
+ {
+ switch (door->type)
+ {
+ case normal:
+ door->direction = 0; // wait at top
+ door->topcountdown = door->topwait;
+ break;
+ case close30ThenOpen:
+ case open:
+ door->sector->specialdata = NULL;
+ P_RemoveThinker(&door->thinker); // unlink and free
+ S_StopSound(&door->sector->soundorg);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// EV_DoDoor
+//
+// Move a door up/down
+//
+//----------------------------------------------------------------------------
+
+int EV_DoDoor(line_t * line, vldoor_e type, fixed_t speed)
+{
+ int secnum;
+ int retcode;
+ sector_t *sec;
+ vldoor_t *door;
+
+ secnum = -1;
+ retcode = 0;
+ while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
+ {
+ sec = &sectors[secnum];
+ if (sec->specialdata)
+ {
+ continue;
+ }
+ // Add new door thinker
+ retcode = 1;
+ door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0);
+ P_AddThinker(&door->thinker);
+ sec->specialdata = door;
+ door->thinker.function = T_VerticalDoor;
+ door->sector = sec;
+ switch (type)
+ {
+ case close:
+ door->topheight = P_FindLowestCeilingSurrounding(sec);
+ door->topheight -= 4 * FRACUNIT;
+ door->direction = -1;
+ S_StartSound(&door->sector->soundorg, sfx_doropn);
+ break;
+ case close30ThenOpen:
+ door->topheight = sec->ceilingheight;
+ door->direction = -1;
+ S_StartSound(&door->sector->soundorg, sfx_doropn);
+ break;
+ case normal:
+ case open:
+ door->direction = 1;
+ door->topheight = P_FindLowestCeilingSurrounding(sec);
+ door->topheight -= 4 * FRACUNIT;
+ if (door->topheight != sec->ceilingheight)
+ {
+ S_StartSound(&door->sector->soundorg,
+ sfx_doropn);
+ }
+ break;
+ default:
+ break;
+ }
+ door->type = type;
+ door->speed = speed;
+ door->topwait = VDOORWAIT;
+ }
+ return (retcode);
+}
+
+//==================================================================
+//
+// EV_VerticalDoor : open a door manually, no tag value
+//
+//==================================================================
+void EV_VerticalDoor(line_t * line, mobj_t * thing)
+{
+ player_t *player;
+ int secnum;
+ sector_t *sec;
+ vldoor_t *door;
+ int side;
+
+ side = 0; // only front sides can be used
+//
+// Check for locks
+//
+ player = thing->player;
+ switch (line->special)
+ {
+ case 26: // Blue Lock
+ case 32:
+ if (!player)
+ {
+ return;
+ }
+ if (!player->keys[key_blue])
+ {
+ P_SetMessage(player, DEH_String(TXT_NEEDBLUEKEY), false);
+ S_StartSound(NULL, sfx_plroof);
+ return;
+ }
+ break;
+ case 27: // Yellow Lock
+ case 34:
+ if (!player)
+ {
+ return;
+ }
+ if (!player->keys[key_yellow])
+ {
+ P_SetMessage(player, DEH_String(TXT_NEEDYELLOWKEY), false);
+ S_StartSound(NULL, sfx_plroof);
+ return;
+ }
+ break;
+ case 28: // Green Lock
+ case 33:
+ if (!player)
+ {
+ return;
+ }
+ if (!player->keys[key_green])
+ {
+ P_SetMessage(player, DEH_String(TXT_NEEDGREENKEY), false);
+ S_StartSound(NULL, sfx_plroof);
+ return;
+ }
+ break;
+ }
+
+ // if the sector has an active thinker, use it
+ sec = sides[line->sidenum[side ^ 1]].sector;
+ secnum = sec - sectors;
+ if (sec->specialdata)
+ {
+ door = sec->specialdata;
+ switch (line->special)
+ {
+ case 1: // ONLY FOR "RAISE" DOORS, NOT "OPEN"s
+ case 26:
+ case 27:
+ case 28:
+ if (door->direction == -1)
+ {
+ door->direction = 1; // go back up
+ }
+ else
+ {
+ if (!thing->player)
+ { // Monsters don't close doors
+ return;
+ }
+ door->direction = -1; // start going down immediately
+ }
+ return;
+ }
+ }
+
+ // for proper sound
+ switch (line->special)
+ {
+ case 1: // NORMAL DOOR SOUND
+ case 31:
+ S_StartSound(&sec->soundorg, sfx_doropn);
+ //S_StartSound(&sec->soundorg, sfx_dormov);
+ break;
+ default: // LOCKED DOOR SOUND
+ S_StartSound(&sec->soundorg, sfx_doropn);
+ //S_StartSound(&sec->soundorg, sfx_dormov);
+ break;
+ }
+
+ //
+ // new door thinker
+ //
+ door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0);
+ P_AddThinker(&door->thinker);
+ sec->specialdata = door;
+ door->thinker.function = T_VerticalDoor;
+ door->sector = sec;
+ door->direction = 1;
+ 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;
+ }
+ door->speed = VDOORSPEED;
+ door->topwait = VDOORWAIT;
+
+ //
+ // find the top and bottom of the movement range
+ //
+ door->topheight = P_FindLowestCeilingSurrounding(sec);
+ door->topheight -= 4 * FRACUNIT;
+}
+
+//==================================================================
+//
+// Spawn a door that closes after 30 seconds
+//
+//==================================================================
+void P_SpawnDoorCloseIn30(sector_t * sec)
+{
+ vldoor_t *door;
+
+ door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0);
+ P_AddThinker(&door->thinker);
+ sec->specialdata = door;
+ sec->special = 0;
+ door->thinker.function = T_VerticalDoor;
+ door->sector = sec;
+ door->direction = 0;
+ door->type = normal;
+ door->speed = VDOORSPEED;
+ door->topcountdown = 30 * 35;
+}
+
+//==================================================================
+//
+// Spawn a door that opens after 5 minutes
+//
+//==================================================================
+void P_SpawnDoorRaiseIn5Mins(sector_t * sec, int secnum)
+{
+ vldoor_t *door;
+
+ door = Z_Malloc(sizeof(*door), PU_LEVSPEC, 0);
+ P_AddThinker(&door->thinker);
+ sec->specialdata = door;
+ 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;
+}
diff --git a/src/heretic/p_enemy.c b/src/heretic/p_enemy.c
new file mode 100644
index 00000000..6ad8cff0
--- /dev/null
+++ b/src/heretic/p_enemy.c
@@ -0,0 +1,2690 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_enemy.c
+
+#include <stdlib.h>
+#include "doomdef.h"
+#include "i_system.h"
+#include "i_timer.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+// Macros
+
+#define MAX_BOSS_SPOTS 8
+
+// Types
+
+typedef struct
+{
+ fixed_t x;
+ fixed_t y;
+ angle_t angle;
+} BossSpot_t;
+
+// Private Data
+
+static int BossSpotCount;
+static BossSpot_t BossSpots[MAX_BOSS_SPOTS];
+
+//----------------------------------------------------------------------------
+//
+// PROC P_InitMonsters
+//
+// Called at level load.
+//
+//----------------------------------------------------------------------------
+
+void P_InitMonsters(void)
+{
+ BossSpotCount = 0;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_AddBossSpot
+//
+//----------------------------------------------------------------------------
+
+void P_AddBossSpot(fixed_t x, fixed_t y, angle_t angle)
+{
+ if (BossSpotCount == MAX_BOSS_SPOTS)
+ {
+ I_Error("Too many boss spots.");
+ }
+ BossSpots[BossSpotCount].x = x;
+ BossSpots[BossSpotCount].y = y;
+ BossSpots[BossSpotCount].angle = angle;
+ BossSpotCount++;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_RecursiveSound
+//
+//----------------------------------------------------------------------------
+
+mobj_t *soundtarget;
+
+void P_RecursiveSound(sector_t * sec, int soundblocks)
+{
+ int i;
+ line_t *check;
+ sector_t *other;
+
+ // Wake up all monsters in this sector
+ if (sec->validcount == validcount
+ && sec->soundtraversed <= soundblocks + 1)
+ { // Already flooded
+ return;
+ }
+ sec->validcount = validcount;
+ sec->soundtraversed = soundblocks + 1;
+ sec->soundtarget = soundtarget;
+ for (i = 0; i < sec->linecount; i++)
+ {
+ check = sec->lines[i];
+ if (!(check->flags & ML_TWOSIDED))
+ {
+ continue;
+ }
+ P_LineOpening(check);
+ if (openrange <= 0)
+ { // Closed door
+ continue;
+ }
+ if (sides[check->sidenum[0]].sector == sec)
+ {
+ other = sides[check->sidenum[1]].sector;
+ }
+ else
+ {
+ other = sides[check->sidenum[0]].sector;
+ }
+ if (check->flags & ML_SOUNDBLOCK)
+ {
+ if (!soundblocks)
+ {
+ P_RecursiveSound(other, 1);
+ }
+ }
+ else
+ {
+ P_RecursiveSound(other, soundblocks);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC 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 * emmiter)
+{
+ soundtarget = target;
+ validcount++;
+ P_RecursiveSound(emmiter->subsector->sector, 0);
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_CheckMeleeRange
+//
+//----------------------------------------------------------------------------
+
+boolean P_CheckMeleeRange(mobj_t * actor)
+{
+ mobj_t *mo;
+ fixed_t dist;
+
+ if (!actor->target)
+ {
+ return (false);
+ }
+ mo = actor->target;
+ dist = P_AproxDistance(mo->x - actor->x, mo->y - actor->y);
+ if (dist >= MELEERANGE)
+ {
+ return (false);
+ }
+ if (!P_CheckSight(actor, mo))
+ {
+ return (false);
+ }
+ if (mo->z > actor->z + actor->height)
+ { // Target is higher than the attacker
+ return (false);
+ }
+ else if (actor->z > mo->z + mo->height)
+ { // Attacker is higher
+ return (false);
+ }
+ return (true);
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_CheckMissileRange
+//
+//----------------------------------------------------------------------------
+
+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;
+ return (true);
+ }
+ if (actor->reactiontime)
+ { // Don't attack yet
+ return (false);
+ }
+ dist = (P_AproxDistance(actor->x - actor->target->x,
+ actor->y - actor->target->y) >> FRACBITS) - 64;
+ if (!actor->info->meleestate)
+ { // No melee attack, so fire more frequently
+ dist -= 128;
+ }
+ if (actor->type == MT_IMP)
+ { // Imp's fly attack from far away
+ dist >>= 1;
+ }
+ if (dist > 200)
+ {
+ dist = 200;
+ }
+ if (P_Random() < dist)
+ {
+ return (false);
+ }
+ return (true);
+}
+
+/*
+================
+=
+= P_Move
+=
+= Move in the current direction
+= returns false if the move is blocked
+================
+*/
+
+fixed_t xspeed[8] =
+ { FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000, 0, 47000 };
+fixed_t yspeed[8] =
+ { 0, 47000, FRACUNIT, 47000, 0, -47000, -FRACUNIT, -47000 };
+
+#define MAXSPECIALCROSS 8
+extern line_t *spechit[MAXSPECIALCROSS];
+extern int numspechit;
+
+boolean P_Move(mobj_t * actor)
+{
+ fixed_t tryx, tryy;
+ line_t *ld;
+ boolean good;
+
+ if (actor->movedir == DI_NODIR)
+ {
+ return (false);
+ }
+ tryx = actor->x + actor->info->speed * xspeed[actor->movedir];
+ tryy = actor->y + actor->info->speed * yspeed[actor->movedir];
+ if (!P_TryMove(actor, tryx, tryy))
+ { // open any specials
+ if (actor->flags & MF_FLOAT && floatok)
+ { // must adjust height
+ if (actor->z < tmfloorz)
+ {
+ actor->z += FLOATSPEED;
+ }
+ else
+ {
+ actor->z -= FLOATSPEED;
+ }
+ actor->flags |= MF_INFLOAT;
+ return (true);
+ }
+ if (!numspechit)
+ {
+ return false;
+ }
+ actor->movedir = DI_NODIR;
+ good = false;
+ while (numspechit--)
+ {
+ ld = spechit[numspechit];
+ // if the special isn't a door that can be opened, return false
+ if (P_UseSpecialLine(actor, ld))
+ {
+ good = true;
+ }
+ }
+ return (good);
+ }
+ else
+ {
+ actor->flags &= ~MF_INFLOAT;
+ }
+ if (!(actor->flags & MF_FLOAT))
+ {
+ if (actor->z > actor->floorz)
+ {
+ P_HitFloor(actor);
+ }
+ actor->z = actor->floorz;
+ }
+ return (true);
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_TryWalk
+//
+// Attempts to move actor in its current (ob->moveangle) direction.
+// If blocked by either a wall or an actor returns FALSE.
+// If move is either clear of block only by a door, returns TRUE and sets.
+// If a door is in the way, an OpenDoor call is made to start it opening.
+//
+//----------------------------------------------------------------------------
+
+boolean P_TryWalk(mobj_t * actor)
+{
+ if (!P_Move(actor))
+ {
+ return (false);
+ }
+ actor->movecount = P_Random() & 15;
+ return (true);
+}
+
+/*
+================
+=
+= P_NewChaseDir
+=
+================
+*/
+
+dirtype_t opposite[] =
+ { DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST, DI_EAST, DI_NORTHEAST,
+ DI_NORTH, DI_NORTHWEST, DI_NODIR
+};
+
+dirtype_t diags[] =
+ { DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST };
+
+void P_NewChaseDir(mobj_t * actor)
+{
+ fixed_t deltax, deltay;
+ dirtype_t d[3];
+ dirtype_t tdir, olddir, turnaround;
+
+ if (!actor->target)
+ I_Error("P_NewChaseDir: called with no target");
+
+ olddir = actor->movedir;
+ turnaround = opposite[olddir];
+
+ deltax = actor->target->x - actor->x;
+ deltay = actor->target->y - actor->y;
+ if (deltax > 10 * FRACUNIT)
+ d[1] = DI_EAST;
+ else if (deltax < -10 * FRACUNIT)
+ d[1] = DI_WEST;
+ else
+ d[1] = DI_NODIR;
+ if (deltay < -10 * FRACUNIT)
+ d[2] = DI_SOUTH;
+ else if (deltay > 10 * FRACUNIT)
+ d[2] = DI_NORTH;
+ else
+ d[2] = DI_NODIR;
+
+// try direct route
+ if (d[1] != DI_NODIR && d[2] != DI_NODIR)
+ {
+ actor->movedir = diags[((deltay < 0) << 1) + (deltax > 0)];
+ if (actor->movedir != turnaround && P_TryWalk(actor))
+ return;
+ }
+
+// try other directions
+ if (P_Random() > 200 || abs(deltay) > abs(deltax))
+ {
+ tdir = d[1];
+ d[1] = d[2];
+ d[2] = tdir;
+ }
+
+ if (d[1] == turnaround)
+ d[1] = DI_NODIR;
+ if (d[2] == turnaround)
+ d[2] = DI_NODIR;
+
+ if (d[1] != DI_NODIR)
+ {
+ actor->movedir = d[1];
+ if (P_TryWalk(actor))
+ return; /*either moved forward or attacked */
+ }
+
+ if (d[2] != DI_NODIR)
+ {
+ actor->movedir = d[2];
+ if (P_TryWalk(actor))
+ return;
+ }
+
+/* there is no direct path to the player, so pick another direction */
+
+ if (olddir != DI_NODIR)
+ {
+ actor->movedir = olddir;
+ if (P_TryWalk(actor))
+ return;
+ }
+
+ if (P_Random() & 1) /*randomly determine direction of search */
+ {
+ for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
+ {
+ if (tdir != turnaround)
+ {
+ actor->movedir = tdir;
+ if (P_TryWalk(actor))
+ return;
+ }
+ }
+ }
+ else
+ {
+ for (tdir = DI_SOUTHEAST; tdir != DI_EAST-1; tdir--)
+ {
+ if (tdir != turnaround)
+ {
+ actor->movedir = tdir;
+ if (P_TryWalk(actor))
+ return;
+ }
+ }
+ }
+
+ if (turnaround != DI_NODIR)
+ {
+ actor->movedir = turnaround;
+ if (P_TryWalk(actor))
+ return;
+ }
+
+ actor->movedir = DI_NODIR; // can't move
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_LookForMonsters
+//
+//---------------------------------------------------------------------------
+
+#define MONS_LOOK_RANGE (20*64*FRACUNIT)
+#define MONS_LOOK_LIMIT 64
+
+boolean P_LookForMonsters(mobj_t * actor)
+{
+ int count;
+ mobj_t *mo;
+ thinker_t *think;
+
+ if (!P_CheckSight(players[0].mo, actor))
+ { // Player can't see monster
+ return (false);
+ }
+ count = 0;
+ for (think = thinkercap.next; think != &thinkercap; think = think->next)
+ {
+ if (think->function != P_MobjThinker)
+ { // Not a mobj thinker
+ continue;
+ }
+ mo = (mobj_t *) think;
+ if (!(mo->flags & MF_COUNTKILL) || (mo == actor) || (mo->health <= 0))
+ { // Not a valid monster
+ continue;
+ }
+ if (P_AproxDistance(actor->x - mo->x, actor->y - mo->y)
+ > MONS_LOOK_RANGE)
+ { // Out of range
+ continue;
+ }
+ if (P_Random() < 16)
+ { // Skip
+ continue;
+ }
+ if (count++ > MONS_LOOK_LIMIT)
+ { // Stop searching
+ return (false);
+ }
+ if (!P_CheckSight(actor, mo))
+ { // Out of sight
+ continue;
+ }
+ // Found a target monster
+ actor->target = mo;
+ return (true);
+ }
+ return (false);
+}
+
+/*
+================
+=
+= P_LookForPlayers
+=
+= If allaround is false, only look 180 degrees in front
+= returns true if a player is targeted
+================
+*/
+
+boolean P_LookForPlayers(mobj_t * actor, boolean allaround)
+{
+ int c;
+ int stop;
+ player_t *player;
+ sector_t *sector;
+ angle_t an;
+ fixed_t dist;
+
+ if (!netgame && players[0].health <= 0)
+ { // Single player game and player is dead, look for monsters
+ return (P_LookForMonsters(actor));
+ }
+ sector = actor->subsector->sector;
+ c = 0;
+ stop = (actor->lastlook - 1) & 3;
+ for (;; actor->lastlook = (actor->lastlook + 1) & 3)
+ {
+ if (!playeringame[actor->lastlook])
+ continue;
+
+ if (c++ == 2 || actor->lastlook == stop)
+ return false; // done looking
+
+ player = &players[actor->lastlook];
+ if (player->health <= 0)
+ continue; // dead
+ if (!P_CheckSight(actor, player->mo))
+ continue; // out of sight
+
+ if (!allaround)
+ {
+ an = R_PointToAngle2(actor->x, actor->y,
+ player->mo->x, player->mo->y) - actor->angle;
+ if (an > ANG90 && an < ANG270)
+ {
+ dist = P_AproxDistance(player->mo->x - actor->x,
+ player->mo->y - actor->y);
+ // if real close, react anyway
+ if (dist > MELEERANGE)
+ continue; // behind back
+ }
+ }
+ if (player->mo->flags & MF_SHADOW)
+ { // Player is invisible
+ if ((P_AproxDistance(player->mo->x - actor->x,
+ player->mo->y - actor->y) > 2 * MELEERANGE)
+ && P_AproxDistance(player->mo->momx, player->mo->momy)
+ < 5 * FRACUNIT)
+ { // Player is sneaking - can't detect
+ return (false);
+ }
+ if (P_Random() < 225)
+ { // Player isn't sneaking, but still didn't detect
+ return (false);
+ }
+ }
+ actor->target = player->mo;
+ return (true);
+ }
+ return (false);
+}
+
+/*
+===============================================================================
+
+ ACTION ROUTINES
+
+===============================================================================
+*/
+
+/*
+==============
+=
+= A_Look
+=
+= Stay in state until a player is sighted
+=
+==============
+*/
+
+void A_Look(mobj_t * actor)
+{
+ mobj_t *targ;
+
+ actor->threshold = 0; // any shot will wake up
+ targ = actor->subsector->sector->soundtarget;
+ if (targ && (targ->flags & MF_SHOOTABLE))
+ {
+ actor->target = targ;
+ if (actor->flags & MF_AMBUSH)
+ {
+ if (P_CheckSight(actor, actor->target))
+ goto seeyou;
+ }
+ else
+ goto seeyou;
+ }
+
+
+ if (!P_LookForPlayers(actor, false))
+ return;
+
+// go into chase state
+ seeyou:
+ if (actor->info->seesound)
+ {
+ int sound;
+
+/*
+ switch (actor->info->seesound)
+ {
+ case sfx_posit1:
+ case sfx_posit2:
+ case sfx_posit3:
+ sound = sfx_posit1+P_Random()%3;
+ break;
+ case sfx_bgsit1:
+ case sfx_bgsit2:
+ sound = sfx_bgsit1+P_Random()%2;
+ break;
+ default:
+ sound = actor->info->seesound;
+ break;
+ }
+*/
+ sound = actor->info->seesound;
+ if (actor->flags2 & MF2_BOSS)
+ { // Full volume
+ S_StartSound(NULL, sound);
+ }
+ else
+ {
+ S_StartSound(actor, sound);
+ }
+ }
+ P_SetMobjState(actor, actor->info->seestate);
+}
+
+
+/*
+==============
+=
+= A_Chase
+=
+= Actor has a melee attack, so it tries to close as fast as possible
+=
+==============
+*/
+
+void A_Chase(mobj_t * actor)
+{
+ int delta;
+
+ if (actor->reactiontime)
+ {
+ actor->reactiontime--;
+ }
+
+ // Modify target threshold
+ if (actor->threshold)
+ {
+ actor->threshold--;
+ }
+
+ if (gameskill == sk_nightmare)
+ { // Monsters move faster in nightmare mode
+ actor->tics -= actor->tics / 2;
+ if (actor->tics < 3)
+ {
+ actor->tics = 3;
+ }
+ }
+
+//
+// turn towards movement direction if not there yet
+//
+ if (actor->movedir < 8)
+ {
+ actor->angle &= (7 << 29);
+ delta = actor->angle - (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))
+ { // look for a new target
+ if (P_LookForPlayers(actor, true))
+ { // got a new target
+ return;
+ }
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+//
+// don't attack twice in a row
+//
+ if (actor->flags & MF_JUSTATTACKED)
+ {
+ actor->flags &= ~MF_JUSTATTACKED;
+ if (gameskill != sk_nightmare)
+ 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);
+ return;
+ }
+
+//
+// check for missile attack
+//
+ if (actor->info->missilestate)
+ {
+ if (gameskill < sk_nightmare && actor->movecount)
+ goto nomissile;
+ if (!P_CheckMissileRange(actor))
+ goto nomissile;
+ P_SetMobjState(actor, actor->info->missilestate);
+ actor->flags |= MF_JUSTATTACKED;
+ return;
+ }
+ nomissile:
+
+//
+// possibly choose another target
+//
+ if (netgame && !actor->threshold && !P_CheckSight(actor, actor->target))
+ {
+ if (P_LookForPlayers(actor, true))
+ return; // got a new target
+ }
+
+//
+// chase towards player
+//
+ if (--actor->movecount < 0 || !P_Move(actor))
+ {
+ P_NewChaseDir(actor);
+ }
+
+//
+// make active sound
+//
+ if (actor->info->activesound && P_Random() < 3)
+ {
+ if (actor->type == MT_WIZARD && P_Random() < 128)
+ {
+ S_StartSound(actor, actor->info->seesound);
+ }
+ else if (actor->type == MT_SORCERER2)
+ {
+ S_StartSound(NULL, actor->info->activesound);
+ }
+ else
+ {
+ S_StartSound(actor, actor->info->activesound);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC 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)
+ { // Target is a ghost
+ actor->angle += (P_Random() - P_Random()) << 21;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Pain
+//
+//----------------------------------------------------------------------------
+
+void A_Pain(mobj_t * actor)
+{
+ if (actor->info->painsound)
+ {
+ S_StartSound(actor, actor->info->painsound);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_DripBlood
+//
+//----------------------------------------------------------------------------
+
+void A_DripBlood(mobj_t * actor)
+{
+ mobj_t *mo;
+
+ mo = P_SpawnMobj(actor->x + ((P_Random() - P_Random()) << 11),
+ actor->y + ((P_Random() - P_Random()) << 11), actor->z,
+ MT_BLOOD);
+ mo->momx = (P_Random() - P_Random()) << 10;
+ mo->momy = (P_Random() - P_Random()) << 10;
+ mo->flags2 |= MF2_LOGRAV;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_KnightAttack
+//
+//----------------------------------------------------------------------------
+
+void A_KnightAttack(mobj_t * actor)
+{
+ if (!actor->target)
+ {
+ return;
+ }
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(3));
+ S_StartSound(actor, sfx_kgtat2);
+ return;
+ }
+ // Throw axe
+ S_StartSound(actor, actor->info->attacksound);
+ if (actor->type == MT_KNIGHTGHOST || P_Random() < 40)
+ { // Red axe
+ P_SpawnMissile(actor, actor->target, MT_REDAXE);
+ return;
+ }
+ // Green axe
+ P_SpawnMissile(actor, actor->target, MT_KNIGHTAXE);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ImpExplode
+//
+//----------------------------------------------------------------------------
+
+void A_ImpExplode(mobj_t * actor)
+{
+ mobj_t *mo;
+
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_IMPCHUNK1);
+ mo->momx = (P_Random() - P_Random()) << 10;
+ mo->momy = (P_Random() - P_Random()) << 10;
+ mo->momz = 9 * FRACUNIT;
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_IMPCHUNK2);
+ mo->momx = (P_Random() - P_Random()) << 10;
+ mo->momy = (P_Random() - P_Random()) << 10;
+ mo->momz = 9 * FRACUNIT;
+ if (actor->special1 == 666)
+ { // Extreme death crash
+ P_SetMobjState(actor, S_IMP_XCRASH1);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_BeastPuff
+//
+//----------------------------------------------------------------------------
+
+void A_BeastPuff(mobj_t * actor)
+{
+ if (P_Random() > 64)
+ {
+ P_SpawnMobj(actor->x + ((P_Random() - P_Random()) << 10),
+ actor->y + ((P_Random() - P_Random()) << 10),
+ actor->z + ((P_Random() - P_Random()) << 10), MT_PUFFY);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ImpMeAttack
+//
+//----------------------------------------------------------------------------
+
+void A_ImpMeAttack(mobj_t * actor)
+{
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(actor, actor->info->attacksound);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, 5 + (P_Random() & 7));
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ImpMsAttack
+//
+//----------------------------------------------------------------------------
+
+void A_ImpMsAttack(mobj_t * actor)
+{
+ mobj_t *dest;
+ angle_t an;
+ int dist;
+
+ if (!actor->target || P_Random() > 64)
+ {
+ P_SetMobjState(actor, actor->info->seestate);
+ return;
+ }
+ dest = actor->target;
+ actor->flags |= MF_SKULLFLY;
+ S_StartSound(actor, actor->info->attacksound);
+ A_FaceTarget(actor);
+ an = actor->angle >> ANGLETOFINESHIFT;
+ actor->momx = FixedMul(12 * FRACUNIT, finecosine[an]);
+ actor->momy = FixedMul(12 * FRACUNIT, finesine[an]);
+ dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
+ dist = dist / (12 * FRACUNIT);
+ if (dist < 1)
+ {
+ dist = 1;
+ }
+ actor->momz = (dest->z + (dest->height >> 1) - actor->z) / dist;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ImpMsAttack2
+//
+// Fireball attack of the imp leader.
+//
+//----------------------------------------------------------------------------
+
+void A_ImpMsAttack2(mobj_t * actor)
+{
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(actor, actor->info->attacksound);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, 5 + (P_Random() & 7));
+ return;
+ }
+ P_SpawnMissile(actor, actor->target, MT_IMPBALL);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ImpDeath
+//
+//----------------------------------------------------------------------------
+
+void A_ImpDeath(mobj_t * actor)
+{
+ actor->flags &= ~MF_SOLID;
+ actor->flags2 |= MF2_FOOTCLIP;
+ if (actor->z <= actor->floorz)
+ {
+ P_SetMobjState(actor, S_IMP_CRASH1);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ImpXDeath1
+//
+//----------------------------------------------------------------------------
+
+void A_ImpXDeath1(mobj_t * actor)
+{
+ actor->flags &= ~MF_SOLID;
+ actor->flags |= MF_NOGRAVITY;
+ actor->flags2 |= MF2_FOOTCLIP;
+ actor->special1 = 666; // Flag the crash routine
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ImpXDeath2
+//
+//----------------------------------------------------------------------------
+
+void A_ImpXDeath2(mobj_t * actor)
+{
+ actor->flags &= ~MF_NOGRAVITY;
+ if (actor->z <= actor->floorz)
+ {
+ P_SetMobjState(actor, S_IMP_CRASH1);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_UpdateChicken
+//
+// Returns true if the chicken morphs.
+//
+//----------------------------------------------------------------------------
+
+boolean P_UpdateChicken(mobj_t * actor, int tics)
+{
+ mobj_t *fog;
+ fixed_t x;
+ fixed_t y;
+ fixed_t z;
+ mobjtype_t moType;
+ mobj_t *mo;
+ mobj_t oldChicken;
+
+ actor->special1 -= tics;
+ if (actor->special1 > 0)
+ {
+ return (false);
+ }
+ moType = actor->special2;
+ x = actor->x;
+ y = actor->y;
+ z = actor->z;
+ oldChicken = *actor;
+ P_SetMobjState(actor, S_FREETARGMOBJ);
+ mo = P_SpawnMobj(x, y, z, moType);
+ if (P_TestMobjLocation(mo) == false)
+ { // Didn't fit
+ P_RemoveMobj(mo);
+ mo = P_SpawnMobj(x, y, z, MT_CHICKEN);
+ mo->angle = oldChicken.angle;
+ mo->flags = oldChicken.flags;
+ mo->health = oldChicken.health;
+ mo->target = oldChicken.target;
+ mo->special1 = 5 * 35; // Next try in 5 seconds
+ mo->special2 = moType;
+ return (false);
+ }
+ mo->angle = oldChicken.angle;
+ mo->target = oldChicken.target;
+ fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG);
+ S_StartSound(fog, sfx_telept);
+ return (true);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ChicAttack
+//
+//----------------------------------------------------------------------------
+
+void A_ChicAttack(mobj_t * actor)
+{
+ if (P_UpdateChicken(actor, 18))
+ {
+ return;
+ }
+ if (!actor->target)
+ {
+ return;
+ }
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, 1 + (P_Random() & 1));
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ChicLook
+//
+//----------------------------------------------------------------------------
+
+void A_ChicLook(mobj_t * actor)
+{
+ if (P_UpdateChicken(actor, 10))
+ {
+ return;
+ }
+ A_Look(actor);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ChicChase
+//
+//----------------------------------------------------------------------------
+
+void A_ChicChase(mobj_t * actor)
+{
+ if (P_UpdateChicken(actor, 3))
+ {
+ return;
+ }
+ A_Chase(actor);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ChicPain
+//
+//----------------------------------------------------------------------------
+
+void A_ChicPain(mobj_t * actor)
+{
+ if (P_UpdateChicken(actor, 10))
+ {
+ return;
+ }
+ S_StartSound(actor, actor->info->painsound);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Feathers
+//
+//----------------------------------------------------------------------------
+
+void A_Feathers(mobj_t * actor)
+{
+ int i;
+ int count;
+ mobj_t *mo;
+
+ if (actor->health > 0)
+ { // Pain
+ count = P_Random() < 32 ? 2 : 1;
+ }
+ else
+ { // Death
+ count = 5 + (P_Random() & 3);
+ }
+ for (i = 0; i < count; i++)
+ {
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z + 20 * FRACUNIT,
+ MT_FEATHER);
+ mo->target = actor;
+ mo->momx = (P_Random() - P_Random()) << 8;
+ mo->momy = (P_Random() - P_Random()) << 8;
+ mo->momz = FRACUNIT + (P_Random() << 9);
+ P_SetMobjState(mo, S_FEATHER1 + (P_Random() & 7));
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MummyAttack
+//
+//----------------------------------------------------------------------------
+
+void A_MummyAttack(mobj_t * actor)
+{
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(actor, actor->info->attacksound);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(2));
+ S_StartSound(actor, sfx_mumat2);
+ return;
+ }
+ S_StartSound(actor, sfx_mumat1);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MummyAttack2
+//
+// Mummy leader missile attack.
+//
+//----------------------------------------------------------------------------
+
+void A_MummyAttack2(mobj_t * actor)
+{
+ mobj_t *mo;
+
+ if (!actor->target)
+ {
+ return;
+ }
+ //S_StartSound(actor, actor->info->attacksound);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(2));
+ return;
+ }
+ mo = P_SpawnMissile(actor, actor->target, MT_MUMMYFX1);
+ //mo = P_SpawnMissile(actor, actor->target, MT_EGGFX);
+ if (mo != NULL)
+ {
+ mo->special1 = (int) actor->target;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MummyFX1Seek
+//
+//----------------------------------------------------------------------------
+
+void A_MummyFX1Seek(mobj_t * actor)
+{
+ P_SeekerMissile(actor, ANG1_X * 10, ANG1_X * 20);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MummySoul
+//
+//----------------------------------------------------------------------------
+
+void A_MummySoul(mobj_t * mummy)
+{
+ mobj_t *mo;
+
+ mo = P_SpawnMobj(mummy->x, mummy->y, mummy->z + 10 * FRACUNIT,
+ MT_MUMMYSOUL);
+ mo->momz = FRACUNIT;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Sor1Pain
+//
+//----------------------------------------------------------------------------
+
+void A_Sor1Pain(mobj_t * actor)
+{
+ actor->special1 = 20; // Number of steps to walk fast
+ A_Pain(actor);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Sor1Chase
+//
+//----------------------------------------------------------------------------
+
+void A_Sor1Chase(mobj_t * actor)
+{
+ if (actor->special1)
+ {
+ actor->special1--;
+ actor->tics -= 3;
+ }
+ A_Chase(actor);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Srcr1Attack
+//
+// Sorcerer demon attack.
+//
+//----------------------------------------------------------------------------
+
+void A_Srcr1Attack(mobj_t * actor)
+{
+ mobj_t *mo;
+ fixed_t momz;
+ angle_t angle;
+
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(actor, actor->info->attacksound);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(8));
+ return;
+ }
+ if (actor->health > (actor->info->spawnhealth / 3) * 2)
+ { // Spit one fireball
+ P_SpawnMissile(actor, actor->target, MT_SRCRFX1);
+ }
+ else
+ { // Spit three fireballs
+ mo = P_SpawnMissile(actor, actor->target, MT_SRCRFX1);
+ if (mo)
+ {
+ momz = mo->momz;
+ angle = mo->angle;
+ P_SpawnMissileAngle(actor, MT_SRCRFX1, angle - ANG1_X * 3, momz);
+ P_SpawnMissileAngle(actor, MT_SRCRFX1, angle + ANG1_X * 3, momz);
+ }
+ if (actor->health < actor->info->spawnhealth / 3)
+ { // Maybe attack again
+ if (actor->special1)
+ { // Just attacked, so don't attack again
+ actor->special1 = 0;
+ }
+ else
+ { // Set state to attack again
+ actor->special1 = 1;
+ P_SetMobjState(actor, S_SRCR1_ATK4);
+ }
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_SorcererRise
+//
+//----------------------------------------------------------------------------
+
+void A_SorcererRise(mobj_t * actor)
+{
+ mobj_t *mo;
+
+ actor->flags &= ~MF_SOLID;
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SORCERER2);
+ P_SetMobjState(mo, S_SOR2_RISE1);
+ mo->angle = actor->angle;
+ mo->target = actor->target;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_DSparilTeleport
+//
+//----------------------------------------------------------------------------
+
+void P_DSparilTeleport(mobj_t * actor)
+{
+ int i;
+ fixed_t x;
+ fixed_t y;
+ fixed_t prevX;
+ fixed_t prevY;
+ fixed_t prevZ;
+ mobj_t *mo;
+
+ if (!BossSpotCount)
+ { // No spots
+ return;
+ }
+ i = P_Random();
+ do
+ {
+ i++;
+ x = BossSpots[i % BossSpotCount].x;
+ y = BossSpots[i % BossSpotCount].y;
+ }
+ while (P_AproxDistance(actor->x - x, actor->y - y) < 128 * FRACUNIT);
+ prevX = actor->x;
+ prevY = actor->y;
+ prevZ = actor->z;
+ if (P_TeleportMove(actor, x, y))
+ {
+ mo = P_SpawnMobj(prevX, prevY, prevZ, MT_SOR2TELEFADE);
+ S_StartSound(mo, sfx_telept);
+ P_SetMobjState(actor, S_SOR2_TELE1);
+ S_StartSound(actor, sfx_telept);
+ actor->z = actor->floorz;
+ actor->angle = BossSpots[i % BossSpotCount].angle;
+ actor->momx = actor->momy = actor->momz = 0;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Srcr2Decide
+//
+//----------------------------------------------------------------------------
+
+void A_Srcr2Decide(mobj_t * actor)
+{
+ static int chance[] = {
+ 192, 120, 120, 120, 64, 64, 32, 16, 0
+ };
+
+ if (!BossSpotCount)
+ { // No spots
+ return;
+ }
+ if (P_Random() < chance[actor->health / (actor->info->spawnhealth / 8)])
+ {
+ P_DSparilTeleport(actor);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Srcr2Attack
+//
+//----------------------------------------------------------------------------
+
+void A_Srcr2Attack(mobj_t * actor)
+{
+ int chance;
+
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(NULL, actor->info->attacksound);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(20));
+ return;
+ }
+ chance = actor->health < actor->info->spawnhealth / 2 ? 96 : 48;
+ if (P_Random() < chance)
+ { // Wizard spawners
+ P_SpawnMissileAngle(actor, MT_SOR2FX2,
+ actor->angle - ANG45, FRACUNIT / 2);
+ P_SpawnMissileAngle(actor, MT_SOR2FX2,
+ actor->angle + ANG45, FRACUNIT / 2);
+ }
+ else
+ { // Blue bolt
+ P_SpawnMissile(actor, actor->target, MT_SOR2FX1);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_BlueSpark
+//
+//----------------------------------------------------------------------------
+
+void A_BlueSpark(mobj_t * actor)
+{
+ int i;
+ mobj_t *mo;
+
+ for (i = 0; i < 2; i++)
+ {
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SOR2FXSPARK);
+ mo->momx = (P_Random() - P_Random()) << 9;
+ mo->momy = (P_Random() - P_Random()) << 9;
+ mo->momz = FRACUNIT + (P_Random() << 8);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_GenWizard
+//
+//----------------------------------------------------------------------------
+
+void A_GenWizard(mobj_t * actor)
+{
+ mobj_t *mo;
+ mobj_t *fog;
+
+ mo = P_SpawnMobj(actor->x, actor->y,
+ actor->z - mobjinfo[MT_WIZARD].height / 2, MT_WIZARD);
+ if (P_TestMobjLocation(mo) == false)
+ { // Didn't fit
+ P_RemoveMobj(mo);
+ return;
+ }
+ actor->momx = actor->momy = actor->momz = 0;
+ P_SetMobjState(actor, mobjinfo[actor->type].deathstate);
+ actor->flags &= ~MF_MISSILE;
+ fog = P_SpawnMobj(actor->x, actor->y, actor->z, MT_TFOG);
+ S_StartSound(fog, sfx_telept);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Sor2DthInit
+//
+//----------------------------------------------------------------------------
+
+void A_Sor2DthInit(mobj_t * actor)
+{
+ actor->special1 = 7; // Animation loop counter
+ P_Massacre(); // Kill monsters early
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Sor2DthLoop
+//
+//----------------------------------------------------------------------------
+
+void A_Sor2DthLoop(mobj_t * actor)
+{
+ if (--actor->special1)
+ { // Need to loop
+ P_SetMobjState(actor, S_SOR2_DIE4);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// D'Sparil Sound Routines
+//
+//----------------------------------------------------------------------------
+
+void A_SorZap(mobj_t * actor)
+{
+ S_StartSound(NULL, sfx_sorzap);
+}
+
+void A_SorRise(mobj_t * actor)
+{
+ S_StartSound(NULL, sfx_sorrise);
+}
+
+void A_SorDSph(mobj_t * actor)
+{
+ S_StartSound(NULL, sfx_sordsph);
+}
+
+void A_SorDExp(mobj_t * actor)
+{
+ S_StartSound(NULL, sfx_sordexp);
+}
+
+void A_SorDBon(mobj_t * actor)
+{
+ S_StartSound(NULL, sfx_sordbon);
+}
+
+void A_SorSightSnd(mobj_t * actor)
+{
+ S_StartSound(NULL, sfx_sorsit);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MinotaurAtk1
+//
+// Melee attack.
+//
+//----------------------------------------------------------------------------
+
+void A_MinotaurAtk1(mobj_t * actor)
+{
+ player_t *player;
+
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(actor, sfx_stfpow);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(4));
+ if ((player = actor->target->player) != NULL)
+ { // Squish the player
+ player->deltaviewheight = -16 * FRACUNIT;
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MinotaurDecide
+//
+// Choose a missile attack.
+//
+//----------------------------------------------------------------------------
+
+#define MNTR_CHARGE_SPEED (13*FRACUNIT)
+
+void A_MinotaurDecide(mobj_t * actor)
+{
+ angle_t angle;
+ mobj_t *target;
+ int dist;
+
+ target = actor->target;
+ if (!target)
+ {
+ return;
+ }
+ S_StartSound(actor, sfx_minsit);
+ dist = P_AproxDistance(actor->x - target->x, actor->y - target->y);
+ if (target->z + target->height > actor->z
+ && target->z + target->height < actor->z + actor->height
+ && dist < 8 * 64 * FRACUNIT
+ && dist > 1 * 64 * FRACUNIT && P_Random() < 150)
+ { // Charge attack
+ // Don't call the state function right away
+ P_SetMobjStateNF(actor, S_MNTR_ATK4_1);
+ actor->flags |= MF_SKULLFLY;
+ A_FaceTarget(actor);
+ angle = actor->angle >> ANGLETOFINESHIFT;
+ actor->momx = FixedMul(MNTR_CHARGE_SPEED, finecosine[angle]);
+ actor->momy = FixedMul(MNTR_CHARGE_SPEED, finesine[angle]);
+ actor->special1 = 35 / 2; // Charge duration
+ }
+ else if (target->z == target->floorz
+ && dist < 9 * 64 * FRACUNIT && P_Random() < 220)
+ { // Floor fire attack
+ P_SetMobjState(actor, S_MNTR_ATK3_1);
+ actor->special2 = 0;
+ }
+ else
+ { // Swing attack
+ A_FaceTarget(actor);
+ // Don't need to call P_SetMobjState because the current state
+ // falls through to the swing attack
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MinotaurCharge
+//
+//----------------------------------------------------------------------------
+
+void A_MinotaurCharge(mobj_t * actor)
+{
+ mobj_t *puff;
+
+ if (actor->special1)
+ {
+ puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF);
+ puff->momz = 2 * FRACUNIT;
+ actor->special1--;
+ }
+ else
+ {
+ actor->flags &= ~MF_SKULLFLY;
+ P_SetMobjState(actor, actor->info->seestate);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MinotaurAtk2
+//
+// Swing attack.
+//
+//----------------------------------------------------------------------------
+
+void A_MinotaurAtk2(mobj_t * actor)
+{
+ mobj_t *mo;
+ angle_t angle;
+ fixed_t momz;
+
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(actor, sfx_minat2);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(5));
+ return;
+ }
+ mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX1);
+ if (mo)
+ {
+ S_StartSound(mo, sfx_minat2);
+ momz = mo->momz;
+ angle = mo->angle;
+ P_SpawnMissileAngle(actor, MT_MNTRFX1, angle - (ANG45 / 8), momz);
+ P_SpawnMissileAngle(actor, MT_MNTRFX1, angle + (ANG45 / 8), momz);
+ P_SpawnMissileAngle(actor, MT_MNTRFX1, angle - (ANG45 / 16), momz);
+ P_SpawnMissileAngle(actor, MT_MNTRFX1, angle + (ANG45 / 16), momz);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MinotaurAtk3
+//
+// Floor fire attack.
+//
+//----------------------------------------------------------------------------
+
+void A_MinotaurAtk3(mobj_t * actor)
+{
+ mobj_t *mo;
+ player_t *player;
+
+ if (!actor->target)
+ {
+ return;
+ }
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(5));
+ if ((player = actor->target->player) != NULL)
+ { // Squish the player
+ player->deltaviewheight = -16 * FRACUNIT;
+ }
+ }
+ else
+ {
+ mo = P_SpawnMissile(actor, actor->target, MT_MNTRFX2);
+ if (mo != NULL)
+ {
+ S_StartSound(mo, sfx_minat1);
+ }
+ }
+ if (P_Random() < 192 && actor->special2 == 0)
+ {
+ P_SetMobjState(actor, S_MNTR_ATK3_4);
+ actor->special2 = 1;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MntrFloorFire
+//
+//----------------------------------------------------------------------------
+
+void A_MntrFloorFire(mobj_t * actor)
+{
+ mobj_t *mo;
+
+ actor->z = actor->floorz;
+ mo = P_SpawnMobj(actor->x + ((P_Random() - P_Random()) << 10),
+ actor->y + ((P_Random() - P_Random()) << 10), ONFLOORZ,
+ MT_MNTRFX3);
+ mo->target = actor->target;
+ mo->momx = 1; // Force block checking
+ P_CheckMissileSpawn(mo);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_BeastAttack
+//
+//----------------------------------------------------------------------------
+
+void A_BeastAttack(mobj_t * actor)
+{
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(actor, actor->info->attacksound);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(3));
+ return;
+ }
+ P_SpawnMissile(actor, actor->target, MT_BEASTBALL);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_HeadAttack
+//
+//----------------------------------------------------------------------------
+
+void A_HeadAttack(mobj_t * actor)
+{
+ int i;
+ mobj_t *fire;
+ mobj_t *baseFire;
+ mobj_t *mo;
+ mobj_t *target;
+ int randAttack;
+ static int atkResolve1[] = { 50, 150 };
+ static int atkResolve2[] = { 150, 200 };
+ int dist;
+
+ // Ice ball (close 20% : far 60%)
+ // Fire column (close 40% : far 20%)
+ // Whirlwind (close 40% : far 20%)
+ // Distance threshold = 8 cells
+
+ target = actor->target;
+ if (target == NULL)
+ {
+ return;
+ }
+ A_FaceTarget(actor);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(target, actor, actor, HITDICE(6));
+ return;
+ }
+ dist = P_AproxDistance(actor->x - target->x, actor->y - target->y)
+ > 8 * 64 * FRACUNIT;
+ randAttack = P_Random();
+ if (randAttack < atkResolve1[dist])
+ { // Ice ball
+ P_SpawnMissile(actor, target, MT_HEADFX1);
+ S_StartSound(actor, sfx_hedat2);
+ }
+ else if (randAttack < atkResolve2[dist])
+ { // Fire column
+ baseFire = P_SpawnMissile(actor, target, MT_HEADFX3);
+ if (baseFire != NULL)
+ {
+ P_SetMobjState(baseFire, S_HEADFX3_4); // Don't grow
+ for (i = 0; i < 5; i++)
+ {
+ fire = P_SpawnMobj(baseFire->x, baseFire->y,
+ baseFire->z, MT_HEADFX3);
+ if (i == 0)
+ {
+ S_StartSound(actor, sfx_hedat1);
+ }
+ fire->target = baseFire->target;
+ fire->angle = baseFire->angle;
+ fire->momx = baseFire->momx;
+ fire->momy = baseFire->momy;
+ fire->momz = baseFire->momz;
+ fire->damage = 0;
+ fire->health = (i + 1) * 2;
+ P_CheckMissileSpawn(fire);
+ }
+ }
+ }
+ else
+ { // Whirlwind
+ mo = P_SpawnMissile(actor, target, MT_WHIRLWIND);
+ if (mo != NULL)
+ {
+ mo->z -= 32 * FRACUNIT;
+ mo->special1 = (int) target;
+ mo->special2 = 50; // Timer for active sound
+ mo->health = 20 * TICRATE; // Duration
+ S_StartSound(actor, sfx_hedat3);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_WhirlwindSeek
+//
+//----------------------------------------------------------------------------
+
+void A_WhirlwindSeek(mobj_t * actor)
+{
+ actor->health -= 3;
+ if (actor->health < 0)
+ {
+ actor->momx = actor->momy = actor->momz = 0;
+ P_SetMobjState(actor, mobjinfo[actor->type].deathstate);
+ actor->flags &= ~MF_MISSILE;
+ return;
+ }
+ if ((actor->special2 -= 3) < 0)
+ {
+ actor->special2 = 58 + (P_Random() & 31);
+ S_StartSound(actor, sfx_hedat3);
+ }
+ if (actor->special1
+ && (((mobj_t *) (actor->special1))->flags & MF_SHADOW))
+ {
+ return;
+ }
+ P_SeekerMissile(actor, ANG1_X * 10, ANG1_X * 30);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_HeadIceImpact
+//
+//----------------------------------------------------------------------------
+
+void A_HeadIceImpact(mobj_t * ice)
+{
+ int i;
+ angle_t angle;
+ mobj_t *shard;
+
+ for (i = 0; i < 8; i++)
+ {
+ shard = P_SpawnMobj(ice->x, ice->y, ice->z, MT_HEADFX2);
+ angle = i * ANG45;
+ shard->target = ice->target;
+ shard->angle = angle;
+ angle >>= ANGLETOFINESHIFT;
+ shard->momx = FixedMul(shard->info->speed, finecosine[angle]);
+ shard->momy = FixedMul(shard->info->speed, finesine[angle]);
+ shard->momz = (fixed_t)(-.6 * FRACUNIT);
+ P_CheckMissileSpawn(shard);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_HeadFireGrow
+//
+//----------------------------------------------------------------------------
+
+void A_HeadFireGrow(mobj_t * fire)
+{
+ fire->health--;
+ fire->z += 9 * FRACUNIT;
+ if (fire->health == 0)
+ {
+ fire->damage = fire->info->damage;
+ P_SetMobjState(fire, S_HEADFX3_4);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_SnakeAttack
+//
+//----------------------------------------------------------------------------
+
+void A_SnakeAttack(mobj_t * actor)
+{
+ if (!actor->target)
+ {
+ P_SetMobjState(actor, S_SNAKE_WALK1);
+ return;
+ }
+ S_StartSound(actor, actor->info->attacksound);
+ A_FaceTarget(actor);
+ P_SpawnMissile(actor, actor->target, MT_SNAKEPRO_A);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_SnakeAttack2
+//
+//----------------------------------------------------------------------------
+
+void A_SnakeAttack2(mobj_t * actor)
+{
+ if (!actor->target)
+ {
+ P_SetMobjState(actor, S_SNAKE_WALK1);
+ return;
+ }
+ S_StartSound(actor, actor->info->attacksound);
+ A_FaceTarget(actor);
+ P_SpawnMissile(actor, actor->target, MT_SNAKEPRO_B);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ClinkAttack
+//
+//----------------------------------------------------------------------------
+
+void A_ClinkAttack(mobj_t * actor)
+{
+ int damage;
+
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(actor, actor->info->attacksound);
+ if (P_CheckMeleeRange(actor))
+ {
+ damage = ((P_Random() % 7) + 3);
+ P_DamageMobj(actor->target, actor, actor, damage);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_GhostOff
+//
+//----------------------------------------------------------------------------
+
+void A_GhostOff(mobj_t * actor)
+{
+ actor->flags &= ~MF_SHADOW;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_WizAtk1
+//
+//----------------------------------------------------------------------------
+
+void A_WizAtk1(mobj_t * actor)
+{
+ A_FaceTarget(actor);
+ actor->flags &= ~MF_SHADOW;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_WizAtk2
+//
+//----------------------------------------------------------------------------
+
+void A_WizAtk2(mobj_t * actor)
+{
+ A_FaceTarget(actor);
+ actor->flags |= MF_SHADOW;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_WizAtk3
+//
+//----------------------------------------------------------------------------
+
+void A_WizAtk3(mobj_t * actor)
+{
+ mobj_t *mo;
+ angle_t angle;
+ fixed_t momz;
+
+ actor->flags &= ~MF_SHADOW;
+ if (!actor->target)
+ {
+ return;
+ }
+ S_StartSound(actor, actor->info->attacksound);
+ if (P_CheckMeleeRange(actor))
+ {
+ P_DamageMobj(actor->target, actor, actor, HITDICE(4));
+ return;
+ }
+ mo = P_SpawnMissile(actor, actor->target, MT_WIZFX1);
+ if (mo)
+ {
+ momz = mo->momz;
+ angle = mo->angle;
+ P_SpawnMissileAngle(actor, MT_WIZFX1, angle - (ANG45 / 8), momz);
+ P_SpawnMissileAngle(actor, MT_WIZFX1, angle + (ANG45 / 8), momz);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Scream
+//
+//----------------------------------------------------------------------------
+
+void A_Scream(mobj_t * actor)
+{
+ switch (actor->type)
+ {
+ case MT_CHICPLAYER:
+ case MT_SORCERER1:
+ case MT_MINOTAUR:
+ // Make boss death sounds full volume
+ S_StartSound(NULL, actor->info->deathsound);
+ break;
+ case MT_PLAYER:
+ // Handle the different player death screams
+ if (actor->special1 < 10)
+ { // Wimpy death sound
+ S_StartSound(actor, sfx_plrwdth);
+ }
+ else if (actor->health > -50)
+ { // Normal death sound
+ S_StartSound(actor, actor->info->deathsound);
+ }
+ else if (actor->health > -100)
+ { // Crazy death sound
+ S_StartSound(actor, sfx_plrcdth);
+ }
+ else
+ { // Extreme death sound
+ S_StartSound(actor, sfx_gibdth);
+ }
+ break;
+ default:
+ S_StartSound(actor, actor->info->deathsound);
+ break;
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_DropItem
+//
+//---------------------------------------------------------------------------
+
+void P_DropItem(mobj_t * source, mobjtype_t type, int special, int chance)
+{
+ mobj_t *mo;
+
+ if (P_Random() > chance)
+ {
+ return;
+ }
+ mo = P_SpawnMobj(source->x, source->y,
+ source->z + (source->height >> 1), type);
+ mo->momx = (P_Random() - P_Random()) << 8;
+ mo->momy = (P_Random() - P_Random()) << 8;
+ mo->momz = FRACUNIT * 5 + (P_Random() << 10);
+ mo->flags |= MF_DROPPED;
+ mo->health = special;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_NoBlocking
+//
+//----------------------------------------------------------------------------
+
+void A_NoBlocking(mobj_t * actor)
+{
+ actor->flags &= ~MF_SOLID;
+ // Check for monsters dropping things
+ switch (actor->type)
+ {
+ case MT_MUMMY:
+ case MT_MUMMYLEADER:
+ case MT_MUMMYGHOST:
+ case MT_MUMMYLEADERGHOST:
+ P_DropItem(actor, MT_AMGWNDWIMPY, 3, 84);
+ break;
+ case MT_KNIGHT:
+ case MT_KNIGHTGHOST:
+ P_DropItem(actor, MT_AMCBOWWIMPY, 5, 84);
+ break;
+ case MT_WIZARD:
+ P_DropItem(actor, MT_AMBLSRWIMPY, 10, 84);
+ P_DropItem(actor, MT_ARTITOMEOFPOWER, 0, 4);
+ break;
+ case MT_HEAD:
+ P_DropItem(actor, MT_AMBLSRWIMPY, 10, 84);
+ P_DropItem(actor, MT_ARTIEGG, 0, 51);
+ break;
+ case MT_BEAST:
+ P_DropItem(actor, MT_AMCBOWWIMPY, 10, 84);
+ break;
+ case MT_CLINK:
+ P_DropItem(actor, MT_AMSKRDWIMPY, 20, 84);
+ break;
+ case MT_SNAKE:
+ P_DropItem(actor, MT_AMPHRDWIMPY, 5, 84);
+ break;
+ case MT_MINOTAUR:
+ P_DropItem(actor, MT_ARTISUPERHEAL, 0, 51);
+ P_DropItem(actor, MT_AMPHRDWIMPY, 10, 84);
+ break;
+ default:
+ break;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_Explode
+//
+// Handles a bunch of exploding things.
+//
+//----------------------------------------------------------------------------
+
+void A_Explode(mobj_t * actor)
+{
+ int damage;
+
+ damage = 128;
+ switch (actor->type)
+ {
+ case MT_FIREBOMB: // Time Bombs
+ actor->z += 32 * FRACUNIT;
+ actor->flags &= ~MF_SHADOW;
+ break;
+ case MT_MNTRFX2: // Minotaur floor fire
+ damage = 24;
+ break;
+ case MT_SOR2FX1: // D'Sparil missile
+ damage = 80 + (P_Random() & 31);
+ break;
+ default:
+ break;
+ }
+ P_RadiusAttack(actor, actor->target, damage);
+ P_HitFloor(actor);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_PodPain
+//
+//----------------------------------------------------------------------------
+
+void A_PodPain(mobj_t * actor)
+{
+ int i;
+ int count;
+ int chance;
+ mobj_t *goo;
+
+ chance = P_Random();
+ if (chance < 128)
+ {
+ return;
+ }
+ count = chance > 240 ? 2 : 1;
+ for (i = 0; i < count; i++)
+ {
+ goo = P_SpawnMobj(actor->x, actor->y,
+ actor->z + 48 * FRACUNIT, MT_PODGOO);
+ goo->target = actor;
+ goo->momx = (P_Random() - P_Random()) << 9;
+ goo->momy = (P_Random() - P_Random()) << 9;
+ goo->momz = FRACUNIT / 2 + (P_Random() << 9);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_RemovePod
+//
+//----------------------------------------------------------------------------
+
+void A_RemovePod(mobj_t * actor)
+{
+ mobj_t *mo;
+
+ if (actor->special2)
+ {
+ mo = (mobj_t *) actor->special2;
+ if (mo->special1 > 0)
+ {
+ mo->special1--;
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MakePod
+//
+//----------------------------------------------------------------------------
+
+#define MAX_GEN_PODS 16
+
+void A_MakePod(mobj_t * actor)
+{
+ mobj_t *mo;
+ fixed_t x;
+ fixed_t y;
+ fixed_t z;
+
+ if (actor->special1 == MAX_GEN_PODS)
+ { // Too many generated pods
+ return;
+ }
+ x = actor->x;
+ y = actor->y;
+ z = actor->z;
+ mo = P_SpawnMobj(x, y, ONFLOORZ, MT_POD);
+ if (P_CheckPosition(mo, x, y) == false)
+ { // Didn't fit
+ P_RemoveMobj(mo);
+ return;
+ }
+ P_SetMobjState(mo, S_POD_GROW1);
+ P_ThrustMobj(mo, P_Random() << 24, (fixed_t) (4.5 * FRACUNIT));
+ S_StartSound(mo, sfx_newpod);
+ actor->special1++; // Increment generated pod count
+ mo->special2 = (int) actor; // Link the generator to the pod
+ return;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_Massacre
+//
+// Kills all monsters.
+//
+//----------------------------------------------------------------------------
+
+void P_Massacre(void)
+{
+ mobj_t *mo;
+ thinker_t *think;
+
+ for (think = thinkercap.next; think != &thinkercap; think = think->next)
+ {
+ if (think->function != P_MobjThinker)
+ { // Not a mobj thinker
+ continue;
+ }
+ mo = (mobj_t *) think;
+ if ((mo->flags & MF_COUNTKILL) && (mo->health > 0))
+ {
+ P_DamageMobj(mo, NULL, NULL, 10000);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_BossDeath
+//
+// Trigger special effects if all bosses are dead.
+//
+//----------------------------------------------------------------------------
+
+void A_BossDeath(mobj_t * actor)
+{
+ mobj_t *mo;
+ thinker_t *think;
+ line_t dummyLine;
+ static mobjtype_t bossType[6] = {
+ MT_HEAD,
+ MT_MINOTAUR,
+ MT_SORCERER2,
+ MT_HEAD,
+ MT_MINOTAUR,
+ -1
+ };
+
+ if (gamemap != 8)
+ { // Not a boss level
+ return;
+ }
+ if (actor->type != bossType[gameepisode - 1])
+ { // Not considered a boss in this episode
+ return;
+ }
+ // Make sure all bosses are dead
+ for (think = thinkercap.next; think != &thinkercap; think = think->next)
+ {
+ if (think->function != P_MobjThinker)
+ { // Not a mobj thinker
+ continue;
+ }
+ mo = (mobj_t *) think;
+ if ((mo != actor) && (mo->type == actor->type) && (mo->health > 0))
+ { // Found a living boss
+ return;
+ }
+ }
+ if (gameepisode > 1)
+ { // Kill any remaining monsters
+ P_Massacre();
+ }
+ dummyLine.tag = 666;
+ EV_DoFloor(&dummyLine, lowerFloor);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ESound
+//
+//----------------------------------------------------------------------------
+
+void A_ESound(mobj_t * mo)
+{
+ int sound = sfx_None;
+
+ switch (mo->type)
+ {
+ case MT_SOUNDWATERFALL:
+ sound = sfx_waterfl;
+ break;
+ case MT_SOUNDWIND:
+ sound = sfx_wind;
+ break;
+ default:
+ break;
+ }
+ S_StartSound(mo, sound);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_SpawnTeleGlitter
+//
+//----------------------------------------------------------------------------
+
+void A_SpawnTeleGlitter(mobj_t * actor)
+{
+ mobj_t *mo;
+
+ mo = P_SpawnMobj(actor->x + ((P_Random() & 31) - 16) * FRACUNIT,
+ actor->y + ((P_Random() & 31) - 16) * FRACUNIT,
+ actor->subsector->sector->floorheight, MT_TELEGLITTER);
+ mo->momz = FRACUNIT / 4;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_SpawnTeleGlitter2
+//
+//----------------------------------------------------------------------------
+
+void A_SpawnTeleGlitter2(mobj_t * actor)
+{
+ mobj_t *mo;
+
+ mo = P_SpawnMobj(actor->x + ((P_Random() & 31) - 16) * FRACUNIT,
+ actor->y + ((P_Random() & 31) - 16) * FRACUNIT,
+ actor->subsector->sector->floorheight, MT_TELEGLITTER2);
+ mo->momz = FRACUNIT / 4;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_AccTeleGlitter
+//
+//----------------------------------------------------------------------------
+
+void A_AccTeleGlitter(mobj_t * actor)
+{
+ if (++actor->health > 35)
+ {
+ actor->momz += actor->momz / 2;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_InitKeyGizmo
+//
+//----------------------------------------------------------------------------
+
+void A_InitKeyGizmo(mobj_t * gizmo)
+{
+ mobj_t *mo;
+ statenum_t state = S_NULL;
+
+ switch (gizmo->type)
+ {
+ case MT_KEYGIZMOBLUE:
+ state = S_KGZ_BLUEFLOAT1;
+ break;
+ case MT_KEYGIZMOGREEN:
+ state = S_KGZ_GREENFLOAT1;
+ break;
+ case MT_KEYGIZMOYELLOW:
+ state = S_KGZ_YELLOWFLOAT1;
+ break;
+ default:
+ break;
+ }
+ mo = P_SpawnMobj(gizmo->x, gizmo->y, gizmo->z + 60 * FRACUNIT,
+ MT_KEYGIZMOFLOAT);
+ P_SetMobjState(mo, state);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_VolcanoSet
+//
+//----------------------------------------------------------------------------
+
+void A_VolcanoSet(mobj_t * volcano)
+{
+ volcano->tics = 105 + (P_Random() & 127);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_VolcanoBlast
+//
+//----------------------------------------------------------------------------
+
+void A_VolcanoBlast(mobj_t * volcano)
+{
+ int i;
+ int count;
+ mobj_t *blast;
+ angle_t angle;
+
+ count = 1 + (P_Random() % 3);
+ for (i = 0; i < count; i++)
+ {
+ blast = P_SpawnMobj(volcano->x, volcano->y, volcano->z + 44 * FRACUNIT, MT_VOLCANOBLAST); // MT_VOLCANOBLAST
+ blast->target = volcano;
+ angle = P_Random() << 24;
+ blast->angle = angle;
+ angle >>= ANGLETOFINESHIFT;
+ blast->momx = FixedMul(1 * FRACUNIT, finecosine[angle]);
+ blast->momy = FixedMul(1 * FRACUNIT, finesine[angle]);
+ blast->momz = (fixed_t)(2.5 * FRACUNIT) + (P_Random() << 10);
+ S_StartSound(blast, sfx_volsht);
+ P_CheckMissileSpawn(blast);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_VolcBallImpact
+//
+//----------------------------------------------------------------------------
+
+void A_VolcBallImpact(mobj_t * ball)
+{
+ int i;
+ mobj_t *tiny;
+ angle_t angle;
+
+ if (ball->z <= ball->floorz)
+ {
+ ball->flags |= MF_NOGRAVITY;
+ ball->flags2 &= ~MF2_LOGRAV;
+ ball->z += 28 * FRACUNIT;
+ //ball->momz = 3*FRACUNIT;
+ }
+ P_RadiusAttack(ball, ball->target, 25);
+ for (i = 0; i < 4; i++)
+ {
+ tiny = P_SpawnMobj(ball->x, ball->y, ball->z, MT_VOLCANOTBLAST);
+ tiny->target = ball;
+ angle = i * ANG90;
+ tiny->angle = angle;
+ angle >>= ANGLETOFINESHIFT;
+ tiny->momx = FixedMul((fixed_t)(FRACUNIT * .7), finecosine[angle]);
+ tiny->momy = FixedMul((fixed_t)(FRACUNIT * .7), finesine[angle]);
+ tiny->momz = FRACUNIT + (P_Random() << 9);
+ P_CheckMissileSpawn(tiny);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_SkullPop
+//
+//----------------------------------------------------------------------------
+
+void A_SkullPop(mobj_t * actor)
+{
+ mobj_t *mo;
+ player_t *player;
+
+ actor->flags &= ~MF_SOLID;
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z + 48 * FRACUNIT,
+ MT_BLOODYSKULL);
+ //mo->target = actor;
+ mo->momx = (P_Random() - P_Random()) << 9;
+ mo->momy = (P_Random() - P_Random()) << 9;
+ mo->momz = FRACUNIT * 2 + (P_Random() << 6);
+ // Attach player mobj to bloody skull
+ player = actor->player;
+ actor->player = NULL;
+ mo->player = player;
+ mo->health = actor->health;
+ mo->angle = actor->angle;
+ player->mo = mo;
+ player->lookdir = 0;
+ player->damagecount = 32;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_CheckSkullFloor
+//
+//----------------------------------------------------------------------------
+
+void A_CheckSkullFloor(mobj_t * actor)
+{
+ if (actor->z <= actor->floorz)
+ {
+ P_SetMobjState(actor, S_BLOODYSKULLX1);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_CheckSkullDone
+//
+//----------------------------------------------------------------------------
+
+void A_CheckSkullDone(mobj_t * actor)
+{
+ if (actor->special2 == 666)
+ {
+ P_SetMobjState(actor, S_BLOODYSKULLX2);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_CheckBurnGone
+//
+//----------------------------------------------------------------------------
+
+void A_CheckBurnGone(mobj_t * actor)
+{
+ if (actor->special2 == 666)
+ {
+ P_SetMobjState(actor, S_PLAY_FDTH20);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FreeTargMobj
+//
+//----------------------------------------------------------------------------
+
+void A_FreeTargMobj(mobj_t * mo)
+{
+ mo->momx = mo->momy = mo->momz = 0;
+ mo->z = mo->ceilingz + 4 * FRACUNIT;
+ mo->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY | MF_SOLID);
+ mo->flags |= MF_CORPSE | MF_DROPOFF | MF_NOGRAVITY;
+ mo->flags2 &= ~(MF2_PASSMOBJ | MF2_LOGRAV);
+ mo->player = NULL;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_AddPlayerCorpse
+//
+//----------------------------------------------------------------------------
+
+#define BODYQUESIZE 32
+mobj_t *bodyque[BODYQUESIZE];
+int bodyqueslot;
+
+void A_AddPlayerCorpse(mobj_t * actor)
+{
+ if (bodyqueslot >= BODYQUESIZE)
+ { // Too many player corpses - remove an old one
+ P_RemoveMobj(bodyque[bodyqueslot % BODYQUESIZE]);
+ }
+ bodyque[bodyqueslot % BODYQUESIZE] = actor;
+ bodyqueslot++;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FlameSnd
+//
+//----------------------------------------------------------------------------
+
+void A_FlameSnd(mobj_t * actor)
+{
+ S_StartSound(actor, sfx_hedat1); // Burn sound
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_HideThing
+//
+//----------------------------------------------------------------------------
+
+void A_HideThing(mobj_t * actor)
+{
+ //P_UnsetThingPosition(actor);
+ actor->flags2 |= MF2_DONTDRAW;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_UnHideThing
+//
+//----------------------------------------------------------------------------
+
+void A_UnHideThing(mobj_t * actor)
+{
+ //P_SetThingPosition(actor);
+ actor->flags2 &= ~MF2_DONTDRAW;
+}
diff --git a/src/heretic/p_floor.c b/src/heretic/p_floor.c
new file mode 100644
index 00000000..24c7ffa2
--- /dev/null
+++ b/src/heretic/p_floor.c
@@ -0,0 +1,468 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 "doomdef.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+//==================================================================
+//==================================================================
+//
+// FLOORS
+//
+//==================================================================
+//==================================================================
+
+
+
+//==================================================================
+//
+// Move a plane (floor or ceiling) and check for crushing
+//
+//==================================================================
+result_e T_MovePlane(sector_t * sector, fixed_t speed,
+ fixed_t dest, boolean crush, int floorOrCeiling,
+ int direction)
+{
+ boolean flag;
+ fixed_t lastpos;
+
+ switch (floorOrCeiling)
+ {
+ case 0: // FLOOR
+ switch (direction)
+ {
+ case -1: // DOWN
+ if (sector->floorheight - speed < dest)
+ {
+ lastpos = sector->floorheight;
+ sector->floorheight = dest;
+ flag = P_ChangeSector(sector, crush);
+ if (flag == true)
+ {
+ sector->floorheight = lastpos;
+ P_ChangeSector(sector, crush);
+ //return crushed;
+ }
+ return pastdest;
+ }
+ else
+ {
+ lastpos = sector->floorheight;
+ sector->floorheight -= speed;
+ flag = P_ChangeSector(sector, crush);
+ if (flag == true)
+ {
+ sector->floorheight = lastpos;
+ P_ChangeSector(sector, crush);
+ return crushed;
+ }
+ }
+ break;
+
+ case 1: // UP
+ if (sector->floorheight + speed > dest)
+ {
+ lastpos = sector->floorheight;
+ sector->floorheight = dest;
+ flag = P_ChangeSector(sector, crush);
+ if (flag == true)
+ {
+ sector->floorheight = lastpos;
+ P_ChangeSector(sector, crush);
+ //return crushed;
+ }
+ return pastdest;
+ }
+ else // COULD GET CRUSHED
+ {
+ lastpos = sector->floorheight;
+ sector->floorheight += speed;
+ flag = P_ChangeSector(sector, crush);
+ if (flag == true)
+ {
+ if (crush == true)
+ return crushed;
+ sector->floorheight = lastpos;
+ P_ChangeSector(sector, crush);
+ return crushed;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 1: // CEILING
+ switch (direction)
+ {
+ case -1: // DOWN
+ if (sector->ceilingheight - speed < dest)
+ {
+ lastpos = sector->ceilingheight;
+ sector->ceilingheight = dest;
+ flag = P_ChangeSector(sector, crush);
+ if (flag == true)
+ {
+ sector->ceilingheight = lastpos;
+ P_ChangeSector(sector, crush);
+ //return crushed;
+ }
+ return pastdest;
+ }
+ else // COULD GET CRUSHED
+ {
+ lastpos = sector->ceilingheight;
+ sector->ceilingheight -= speed;
+ flag = P_ChangeSector(sector, crush);
+ if (flag == true)
+ {
+ if (crush == true)
+ return crushed;
+ sector->ceilingheight = lastpos;
+ P_ChangeSector(sector, crush);
+ return crushed;
+ }
+ }
+ break;
+
+ case 1: // UP
+ if (sector->ceilingheight + speed > dest)
+ {
+ lastpos = sector->ceilingheight;
+ sector->ceilingheight = dest;
+ flag = P_ChangeSector(sector, crush);
+ if (flag == true)
+ {
+ sector->ceilingheight = lastpos;
+ P_ChangeSector(sector, crush);
+ //return crushed;
+ }
+ return pastdest;
+ }
+ else
+ {
+ lastpos = sector->ceilingheight;
+ sector->ceilingheight += speed;
+ flag = P_ChangeSector(sector, crush);
+#if 0
+ if (flag == true)
+ {
+ sector->ceilingheight = lastpos;
+ P_ChangeSector(sector, crush);
+ return crushed;
+ }
+#endif
+ }
+ break;
+ }
+ break;
+
+ }
+ return ok;
+}
+
+//==================================================================
+//
+// MOVE A FLOOR TO IT'S DESTINATION (UP OR DOWN)
+//
+//==================================================================
+void T_MoveFloor(floormove_t * floor)
+{
+ result_e res;
+
+ res = T_MovePlane(floor->sector, floor->speed,
+ floor->floordestheight, floor->crush, 0,
+ floor->direction);
+ if (!(leveltime & 7))
+ {
+ S_StartSound(&floor->sector->soundorg, sfx_dormov);
+ }
+
+ if (res == pastdest)
+ {
+ floor->sector->specialdata = NULL;
+ if (floor->type == raiseBuildStep)
+ {
+ S_StartSound(&floor->sector->soundorg, sfx_pstop);
+ }
+ if (floor->direction == 1)
+ switch (floor->type)
+ {
+ case donutRaise:
+ floor->sector->special = floor->newspecial;
+ floor->sector->floorpic = floor->texture;
+ default:
+ break;
+ }
+ else if (floor->direction == -1)
+ switch (floor->type)
+ {
+ case lowerAndChange:
+ floor->sector->special = floor->newspecial;
+ floor->sector->floorpic = floor->texture;
+ default:
+ break;
+ }
+ P_RemoveThinker(&floor->thinker);
+ }
+
+}
+
+//==================================================================
+//
+// HANDLE FLOOR TYPES
+//
+//==================================================================
+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;
+ while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
+ {
+ sec = &sectors[secnum];
+
+ // ALREADY MOVING? IF SO, KEEP GOING...
+ if (sec->specialdata)
+ continue;
+
+ //
+ // new floor thinker
+ //
+ rtn = 1;
+ floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0);
+ P_AddThinker(&floor->thinker);
+ sec->specialdata = floor;
+ floor->thinker.function = T_MoveFloor;
+ floor->type = floortype;
+ floor->crush = false;
+ switch (floortype)
+ {
+ case lowerFloor:
+ floor->direction = -1;
+ floor->sector = sec;
+ floor->speed = FLOORSPEED;
+ floor->floordestheight = P_FindHighestFloorSurrounding(sec);
+ break;
+ case lowerFloorToLowest:
+ floor->direction = -1;
+ floor->sector = sec;
+ floor->speed = FLOORSPEED;
+ floor->floordestheight = P_FindLowestFloorSurrounding(sec);
+ break;
+ case turboLower:
+ floor->direction = -1;
+ floor->sector = sec;
+ floor->speed = FLOORSPEED * 4;
+ floor->floordestheight = (8 * FRACUNIT) +
+ P_FindHighestFloorSurrounding(sec);
+ 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 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;
+ 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;
+ break;
+ case raiseToTexture:
+ {
+ int minsize = INT_MAX;
+ side_t *side;
+
+ floor->direction = 1;
+ floor->sector = sec;
+ floor->speed = FLOORSPEED;
+ for (i = 0; i < sec->linecount; i++)
+ if (twoSided(secnum, i))
+ {
+ side = getSide(secnum, i, 0);
+ if (side->bottomtexture >= 0)
+ if (textureheight[side->bottomtexture] <
+ minsize)
+ minsize =
+ textureheight[side->bottomtexture];
+ side = getSide(secnum, i, 1);
+ if (side->bottomtexture >= 0)
+ if (textureheight[side->bottomtexture] <
+ minsize)
+ minsize =
+ textureheight[side->bottomtexture];
+ }
+ floor->floordestheight = floor->sector->floorheight +
+ minsize;
+ }
+ break;
+ case lowerAndChange:
+ floor->direction = -1;
+ floor->sector = sec;
+ floor->speed = FLOORSPEED;
+ floor->floordestheight = P_FindLowestFloorSurrounding(sec);
+ floor->texture = sec->floorpic;
+ for (i = 0; i < sec->linecount; i++)
+ if (twoSided(secnum, i))
+ {
+ if (getSide(secnum, i, 0)->sector - sectors == secnum)
+ {
+ sec = getSector(secnum, i, 1);
+ floor->texture = sec->floorpic;
+ floor->newspecial = sec->special;
+ break;
+ }
+ else
+ {
+ sec = getSector(secnum, i, 0);
+ floor->texture = sec->floorpic;
+ floor->newspecial = sec->special;
+ break;
+ }
+ }
+ default:
+ break;
+ }
+ }
+ return rtn;
+}
+
+//==================================================================
+//
+// BUILD A STAIRCASE!
+//
+//==================================================================
+int EV_BuildStairs(line_t * line, fixed_t stepDelta)
+{
+ int secnum;
+ int height;
+ int i;
+ int newsecnum;
+ int texture;
+ int ok;
+ int rtn;
+ sector_t *sec, *tsec;
+ floormove_t *floor;
+
+ secnum = -1;
+ rtn = 0;
+ while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
+ {
+ sec = &sectors[secnum];
+
+ // ALREADY MOVING? IF SO, KEEP GOING...
+ if (sec->specialdata)
+ continue;
+
+ //
+ // new floor thinker
+ //
+ rtn = 1;
+ height = sec->floorheight + stepDelta;
+ floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0);
+ P_AddThinker(&floor->thinker);
+ sec->specialdata = floor;
+ floor->thinker.function = T_MoveFloor;
+ floor->type = raiseBuildStep;
+ floor->direction = 1;
+ floor->sector = sec;
+ floor->speed = FLOORSPEED;
+ floor->floordestheight = height;
+
+ texture = sec->floorpic;
+
+ //
+ // 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])->flags & ML_TWOSIDED))
+ continue;
+
+ tsec = (sec->lines[i])->frontsector;
+ newsecnum = tsec - sectors;
+ if (secnum != newsecnum)
+ continue;
+ tsec = (sec->lines[i])->backsector;
+ newsecnum = tsec - sectors;
+ if (tsec->floorpic != texture)
+ continue;
+
+ height += stepDelta;
+ if (tsec->specialdata)
+ continue;
+
+ sec = tsec;
+ secnum = newsecnum;
+ floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0);
+ P_AddThinker(&floor->thinker);
+ sec->specialdata = floor;
+ floor->thinker.function = T_MoveFloor;
+ floor->type = raiseBuildStep;
+ floor->direction = 1;
+ floor->sector = sec;
+ floor->speed = FLOORSPEED;
+ floor->floordestheight = height;
+ ok = 1;
+ break;
+ }
+ }
+ while (ok);
+ }
+ return (rtn);
+}
diff --git a/src/heretic/p_inter.c b/src/heretic/p_inter.c
new file mode 100644
index 00000000..afdb37f2
--- /dev/null
+++ b/src/heretic/p_inter.c
@@ -0,0 +1,1493 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_inter.c
+
+#include "doomdef.h"
+#include "deh_str.h"
+#include "i_system.h"
+#include "i_timer.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "s_sound.h"
+
+#define BONUSADD 6
+
+int WeaponValue[] = {
+ 1, // staff
+ 3, // goldwand
+ 4, // crossbow
+ 5, // blaster
+ 6, // skullrod
+ 7, // phoenixrod
+ 8, // mace
+ 2, // gauntlets
+ 0 // beak
+};
+
+int maxammo[NUMAMMO] = {
+ 100, // gold wand
+ 50, // crossbow
+ 200, // blaster
+ 200, // skull rod
+ 20, // phoenix rod
+ 150 // mace
+};
+
+int GetWeaponAmmo[NUMWEAPONS] = {
+ 0, // staff
+ 25, // gold wand
+ 10, // crossbow
+ 30, // blaster
+ 50, // skull rod
+ 2, // phoenix rod
+ 50, // mace
+ 0, // gauntlets
+ 0 // beak
+};
+
+static weapontype_t GetAmmoChange[] = {
+ wp_goldwand,
+ wp_crossbow,
+ wp_blaster,
+ wp_skullrod,
+ wp_phoenixrod,
+ wp_mace
+};
+
+/*
+static boolean GetAmmoChangePL1[NUMWEAPONS][NUMAMMO] =
+{
+ // staff
+ {wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace},
+ // gold wand
+ {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace},
+ // crossbow
+ {-1, -1, wp_blaster, wp_skullrod, -1, -1},
+ // blaster
+ {-1, -1, -1, -1, -1, -1},
+ // skull rod
+ {-1, -1, -1, -1, -1, -1},
+ // phoenix rod
+ {-1, -1, -1, -1, -1, -1},
+ // mace
+ {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1},
+ // gauntlets
+ {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, wp_mace}
+};
+*/
+
+/*
+static boolean GetAmmoChangePL2[NUMWEAPONS][NUMAMMO] =
+{
+ // staff
+ {wp_goldwand, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod,
+ wp_mace},
+ // gold wand
+ {-1, wp_crossbow, wp_blaster, wp_skullrod, wp_phoenixrod, wp_mace},
+ // crossbow
+ {-1, -1, wp_blaster, wp_skullrod, wp_phoenixrod, -1},
+ // blaster
+ {-1, -1, -1, wp_skullrod, wp_phoenixrod, -1},
+ // skull rod
+ {-1, -1, -1, -1, -1, -1},
+ // phoenix rod
+ {-1, -1, -1, -1, -1, -1},
+ // mace
+ {-1, wp_crossbow, wp_blaster, wp_skullrod, -1, -1},
+ // gauntlets
+ {-1, -1, -1, wp_skullrod, wp_phoenixrod, wp_mace}
+};
+*/
+
+//--------------------------------------------------------------------------
+//
+// PROC P_SetMessage
+//
+//--------------------------------------------------------------------------
+
+boolean ultimatemsg;
+
+void P_SetMessage(player_t * player, char *message, boolean ultmsg)
+{
+ extern boolean messageson;
+
+ if ((ultimatemsg || !messageson) && !ultmsg)
+ {
+ return;
+ }
+ player->message = message;
+ player->messageTics = MESSAGETICS;
+ BorderTopRefresh = true;
+ if (ultmsg)
+ {
+ ultimatemsg = true;
+ }
+}
+
+//--------------------------------------------------------------------------
+//
+// FUNC P_GiveAmmo
+//
+// Returns true if the player accepted the ammo, false if it was
+// refused (player has maxammo[ammo]).
+//
+//--------------------------------------------------------------------------
+
+boolean P_GiveAmmo(player_t * player, ammotype_t ammo, int count)
+{
+ int prevAmmo;
+ //weapontype_t changeWeapon;
+
+ if (ammo == am_noammo)
+ {
+ return (false);
+ }
+ if (ammo < 0 || ammo > NUMAMMO)
+ {
+ I_Error("P_GiveAmmo: bad type %i", ammo);
+ }
+ if (player->ammo[ammo] == player->maxammo[ammo])
+ {
+ return (false);
+ }
+ if (gameskill == sk_baby || gameskill == sk_nightmare)
+ { // extra ammo in baby mode and nightmare mode
+ count += count >> 1;
+ }
+ prevAmmo = player->ammo[ammo];
+
+ player->ammo[ammo] += count;
+ if (player->ammo[ammo] > player->maxammo[ammo])
+ {
+ player->ammo[ammo] = player->maxammo[ammo];
+ }
+ if (prevAmmo)
+ {
+ // Don't attempt to change weapons if the player already had
+ // ammo of the type just given
+ return (true);
+ }
+ if (player->readyweapon == wp_staff
+ || player->readyweapon == wp_gauntlets)
+ {
+ if (player->weaponowned[GetAmmoChange[ammo]])
+ {
+ player->pendingweapon = GetAmmoChange[ammo];
+ }
+ }
+/*
+ if(player->powers[pw_weaponlevel2])
+ {
+ changeWeapon = GetAmmoChangePL2[player->readyweapon][ammo];
+ }
+ else
+ {
+ changeWeapon = GetAmmoChangePL1[player->readyweapon][ammo];
+ }
+ if(changeWeapon != -1)
+ {
+ if(player->weaponowned[changeWeapon])
+ {
+ player->pendingweapon = changeWeapon;
+ }
+ }
+*/
+ return (true);
+}
+
+//--------------------------------------------------------------------------
+//
+// FUNC P_GiveWeapon
+//
+// Returns true if the weapon or its ammo was accepted.
+//
+//--------------------------------------------------------------------------
+
+boolean P_GiveWeapon(player_t * player, weapontype_t weapon)
+{
+ boolean gaveAmmo;
+ boolean gaveWeapon;
+
+ if (netgame && !deathmatch)
+ { // Cooperative net-game
+ if (player->weaponowned[weapon])
+ {
+ return (false);
+ }
+ player->bonuscount += BONUSADD;
+ player->weaponowned[weapon] = true;
+ P_GiveAmmo(player, wpnlev1info[weapon].ammo, GetWeaponAmmo[weapon]);
+ player->pendingweapon = weapon;
+ if (player == &players[consoleplayer])
+ {
+ S_StartSound(NULL, sfx_wpnup);
+ }
+ return (false);
+ }
+ gaveAmmo = P_GiveAmmo(player, wpnlev1info[weapon].ammo,
+ GetWeaponAmmo[weapon]);
+ if (player->weaponowned[weapon])
+ {
+ gaveWeapon = false;
+ }
+ else
+ {
+ gaveWeapon = true;
+ player->weaponowned[weapon] = true;
+ if (WeaponValue[weapon] > WeaponValue[player->readyweapon])
+ { // Only switch to more powerful weapons
+ player->pendingweapon = weapon;
+ }
+ }
+ return (gaveWeapon || gaveAmmo);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_GiveBody
+//
+// Returns false if the body isn't needed at all.
+//
+//---------------------------------------------------------------------------
+
+boolean P_GiveBody(player_t * player, int num)
+{
+ int max;
+
+ max = MAXHEALTH;
+ if (player->chickenTics)
+ {
+ max = MAXCHICKENHEALTH;
+ }
+ if (player->health >= max)
+ {
+ return (false);
+ }
+ player->health += num;
+ if (player->health > max)
+ {
+ player->health = max;
+ }
+ player->mo->health = player->health;
+ return (true);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_GiveArmor
+//
+// Returns false if the armor is worse than the current armor.
+//
+//---------------------------------------------------------------------------
+
+boolean P_GiveArmor(player_t * player, int armortype)
+{
+ int hits;
+
+ hits = armortype * 100;
+ if (player->armorpoints >= hits)
+ {
+ return (false);
+ }
+ player->armortype = armortype;
+ player->armorpoints = hits;
+ return (true);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_GiveKey
+//
+//---------------------------------------------------------------------------
+
+void P_GiveKey(player_t * player, keytype_t key)
+{
+ extern int playerkeys;
+ extern vertex_t KeyPoints[];
+
+ if (player->keys[key])
+ {
+ return;
+ }
+ if (player == &players[consoleplayer])
+ {
+ playerkeys |= 1 << key;
+ KeyPoints[key].x = 0;
+ KeyPoints[key].y = 0;
+ }
+ player->bonuscount = BONUSADD;
+ player->keys[key] = true;
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_GivePower
+//
+// Returns true if power accepted.
+//
+//---------------------------------------------------------------------------
+
+boolean P_GivePower(player_t * player, powertype_t power)
+{
+ if (power == pw_invulnerability)
+ {
+ if (player->powers[power] > BLINKTHRESHOLD)
+ { // Already have it
+ return (false);
+ }
+ player->powers[power] = INVULNTICS;
+ return (true);
+ }
+ if (power == pw_weaponlevel2)
+ {
+ if (player->powers[power] > BLINKTHRESHOLD)
+ { // Already have it
+ return (false);
+ }
+ player->powers[power] = WPNLEV2TICS;
+ return (true);
+ }
+ if (power == pw_invisibility)
+ {
+ if (player->powers[power] > BLINKTHRESHOLD)
+ { // Already have it
+ return (false);
+ }
+ player->powers[power] = INVISTICS;
+ player->mo->flags |= MF_SHADOW;
+ return (true);
+ }
+ if (power == pw_flight)
+ {
+ if (player->powers[power] > BLINKTHRESHOLD)
+ { // Already have it
+ return (false);
+ }
+ player->powers[power] = FLIGHTTICS;
+ player->mo->flags2 |= MF2_FLY;
+ player->mo->flags |= MF_NOGRAVITY;
+ if (player->mo->z <= player->mo->floorz)
+ {
+ player->flyheight = 10; // thrust the player in the air a bit
+ }
+ return (true);
+ }
+ if (power == pw_infrared)
+ {
+ if (player->powers[power] > BLINKTHRESHOLD)
+ { // Already have it
+ return (false);
+ }
+ player->powers[power] = INFRATICS;
+ return (true);
+ }
+/*
+ if(power == pw_ironfeet)
+ {
+ player->powers[power] = IRONTICS;
+ return(true);
+ }
+ if(power == pw_strength)
+ {
+ P_GiveBody(player, 100);
+ player->powers[power] = 1;
+ return(true);
+ }
+*/
+ if (player->powers[power])
+ {
+ return (false); // already got it
+ }
+ player->powers[power] = 1;
+ return (true);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_GiveArtifact
+//
+// Returns true if artifact accepted.
+//
+//---------------------------------------------------------------------------
+
+boolean P_GiveArtifact(player_t * player, artitype_t arti, mobj_t * mo)
+{
+ int i;
+
+ i = 0;
+ while (player->inventory[i].type != arti && i < player->inventorySlotNum)
+ {
+ i++;
+ }
+ if (i == player->inventorySlotNum)
+ {
+ player->inventory[i].count = 1;
+ player->inventory[i].type = arti;
+ player->inventorySlotNum++;
+ }
+ else
+ {
+ if (player->inventory[i].count >= 16)
+ { // Player already has 16 of this item
+ return (false);
+ }
+ player->inventory[i].count++;
+ }
+ if (player->artifactCount == 0)
+ {
+ player->readyArtifact = arti;
+ }
+ player->artifactCount++;
+ if (mo && (mo->flags & MF_COUNTITEM))
+ {
+ player->itemcount++;
+ }
+ return (true);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_SetDormantArtifact
+//
+// Removes the MF_SPECIAL flag, and initiates the artifact pickup
+// animation.
+//
+//---------------------------------------------------------------------------
+
+void P_SetDormantArtifact(mobj_t * arti)
+{
+ arti->flags &= ~MF_SPECIAL;
+ if (deathmatch && (arti->type != MT_ARTIINVULNERABILITY)
+ && (arti->type != MT_ARTIINVISIBILITY))
+ {
+ P_SetMobjState(arti, S_DORMANTARTI1);
+ }
+ else
+ { // Don't respawn
+ P_SetMobjState(arti, S_DEADARTI1);
+ }
+ S_StartSound(arti, sfx_artiup);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_RestoreArtifact
+//
+//---------------------------------------------------------------------------
+
+void A_RestoreArtifact(mobj_t * arti)
+{
+ arti->flags |= MF_SPECIAL;
+ P_SetMobjState(arti, arti->info->spawnstate);
+ S_StartSound(arti, sfx_respawn);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_HideSpecialThing
+//
+//----------------------------------------------------------------------------
+
+void P_HideSpecialThing(mobj_t * thing)
+{
+ thing->flags &= ~MF_SPECIAL;
+ thing->flags2 |= MF2_DONTDRAW;
+ P_SetMobjState(thing, S_HIDESPECIAL1);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_RestoreSpecialThing1
+//
+// Make a special thing visible again.
+//
+//---------------------------------------------------------------------------
+
+void A_RestoreSpecialThing1(mobj_t * thing)
+{
+ if (thing->type == MT_WMACE)
+ { // Do random mace placement
+ P_RepositionMace(thing);
+ }
+ thing->flags2 &= ~MF2_DONTDRAW;
+ S_StartSound(thing, sfx_respawn);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_RestoreSpecialThing2
+//
+//---------------------------------------------------------------------------
+
+void A_RestoreSpecialThing2(mobj_t * thing)
+{
+ thing->flags |= MF_SPECIAL;
+ P_SetMobjState(thing, thing->info->spawnstate);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_TouchSpecialThing
+//
+//---------------------------------------------------------------------------
+
+void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher)
+{
+ int i;
+ player_t *player;
+ fixed_t delta;
+ int sound;
+ boolean respawn;
+
+ delta = special->z - toucher->z;
+ if (delta > toucher->height || delta < -32 * FRACUNIT)
+ { // Out of reach
+ return;
+ }
+ if (toucher->health <= 0)
+ { // Toucher is dead
+ return;
+ }
+ sound = sfx_itemup;
+ player = toucher->player;
+ respawn = true;
+ switch (special->sprite)
+ {
+ // Items
+ case SPR_PTN1: // Item_HealingPotion
+ if (!P_GiveBody(player, 10))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_ITEMHEALTH), false);
+ break;
+ case SPR_SHLD: // Item_Shield1
+ if (!P_GiveArmor(player, 1))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_ITEMSHIELD1), false);
+ break;
+ case SPR_SHD2: // Item_Shield2
+ if (!P_GiveArmor(player, 2))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_ITEMSHIELD2), false);
+ break;
+ case SPR_BAGH: // Item_BagOfHolding
+ if (!player->backpack)
+ {
+ for (i = 0; i < NUMAMMO; i++)
+ {
+ player->maxammo[i] *= 2;
+ }
+ player->backpack = true;
+ }
+ P_GiveAmmo(player, am_goldwand, AMMO_GWND_WIMPY);
+ P_GiveAmmo(player, am_blaster, AMMO_BLSR_WIMPY);
+ P_GiveAmmo(player, am_crossbow, AMMO_CBOW_WIMPY);
+ P_GiveAmmo(player, am_skullrod, AMMO_SKRD_WIMPY);
+ P_GiveAmmo(player, am_phoenixrod, AMMO_PHRD_WIMPY);
+ P_SetMessage(player, DEH_String(TXT_ITEMBAGOFHOLDING), false);
+ break;
+ case SPR_SPMP: // Item_SuperMap
+ if (!P_GivePower(player, pw_allmap))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_ITEMSUPERMAP), false);
+ break;
+
+ // Keys
+ case SPR_BKYY: // Key_Blue
+ if (!player->keys[key_blue])
+ {
+ P_SetMessage(player, DEH_String(TXT_GOTBLUEKEY), false);
+ }
+ P_GiveKey(player, key_blue);
+ sound = sfx_keyup;
+ if (!netgame)
+ {
+ break;
+ }
+ return;
+ case SPR_CKYY: // Key_Yellow
+ if (!player->keys[key_yellow])
+ {
+ P_SetMessage(player, DEH_String(TXT_GOTYELLOWKEY), false);
+ }
+ sound = sfx_keyup;
+ P_GiveKey(player, key_yellow);
+ if (!netgame)
+ {
+ break;
+ }
+ return;
+ case SPR_AKYY: // Key_Green
+ if (!player->keys[key_green])
+ {
+ P_SetMessage(player, DEH_String(TXT_GOTGREENKEY), false);
+ }
+ sound = sfx_keyup;
+ P_GiveKey(player, key_green);
+ if (!netgame)
+ {
+ break;
+ }
+ return;
+
+ // Artifacts
+ case SPR_PTN2: // Arti_HealingPotion
+ if (P_GiveArtifact(player, arti_health, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTIHEALTH), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+ case SPR_SOAR: // Arti_Fly
+ if (P_GiveArtifact(player, arti_fly, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTIFLY), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+ case SPR_INVU: // Arti_Invulnerability
+ if (P_GiveArtifact(player, arti_invulnerability, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTIINVULNERABILITY), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+ case SPR_PWBK: // Arti_TomeOfPower
+ if (P_GiveArtifact(player, arti_tomeofpower, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTITOMEOFPOWER), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+ case SPR_INVS: // Arti_Invisibility
+ if (P_GiveArtifact(player, arti_invisibility, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTIINVISIBILITY), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+ case SPR_EGGC: // Arti_Egg
+ if (P_GiveArtifact(player, arti_egg, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTIEGG), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+ case SPR_SPHL: // Arti_SuperHealth
+ if (P_GiveArtifact(player, arti_superhealth, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTISUPERHEALTH), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+ case SPR_TRCH: // Arti_Torch
+ if (P_GiveArtifact(player, arti_torch, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTITORCH), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+ case SPR_FBMB: // Arti_FireBomb
+ if (P_GiveArtifact(player, arti_firebomb, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTIFIREBOMB), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+ case SPR_ATLP: // Arti_Teleport
+ if (P_GiveArtifact(player, arti_teleport, special))
+ {
+ P_SetMessage(player, DEH_String(TXT_ARTITELEPORT), false);
+ P_SetDormantArtifact(special);
+ }
+ return;
+
+ // Ammo
+ case SPR_AMG1: // Ammo_GoldWandWimpy
+ if (!P_GiveAmmo(player, am_goldwand, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND1), false);
+ break;
+ case SPR_AMG2: // Ammo_GoldWandHefty
+ if (!P_GiveAmmo(player, am_goldwand, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOGOLDWAND2), false);
+ break;
+ case SPR_AMM1: // Ammo_MaceWimpy
+ if (!P_GiveAmmo(player, am_mace, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOMACE1), false);
+ break;
+ case SPR_AMM2: // Ammo_MaceHefty
+ if (!P_GiveAmmo(player, am_mace, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOMACE2), false);
+ break;
+ case SPR_AMC1: // Ammo_CrossbowWimpy
+ if (!P_GiveAmmo(player, am_crossbow, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW1), false);
+ break;
+ case SPR_AMC2: // Ammo_CrossbowHefty
+ if (!P_GiveAmmo(player, am_crossbow, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOCROSSBOW2), false);
+ break;
+ case SPR_AMB1: // Ammo_BlasterWimpy
+ if (!P_GiveAmmo(player, am_blaster, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOBLASTER1), false);
+ break;
+ case SPR_AMB2: // Ammo_BlasterHefty
+ if (!P_GiveAmmo(player, am_blaster, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOBLASTER2), false);
+ break;
+ case SPR_AMS1: // Ammo_SkullRodWimpy
+ if (!P_GiveAmmo(player, am_skullrod, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD1), false);
+ break;
+ case SPR_AMS2: // Ammo_SkullRodHefty
+ if (!P_GiveAmmo(player, am_skullrod, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOSKULLROD2), false);
+ break;
+ case SPR_AMP1: // Ammo_PhoenixRodWimpy
+ if (!P_GiveAmmo(player, am_phoenixrod, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD1), false);
+ break;
+ case SPR_AMP2: // Ammo_PhoenixRodHefty
+ if (!P_GiveAmmo(player, am_phoenixrod, special->health))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_AMMOPHOENIXROD2), false);
+ break;
+
+ // Weapons
+ case SPR_WMCE: // Weapon_Mace
+ if (!P_GiveWeapon(player, wp_mace))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_WPNMACE), false);
+ sound = sfx_wpnup;
+ break;
+ case SPR_WBOW: // Weapon_Crossbow
+ if (!P_GiveWeapon(player, wp_crossbow))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_WPNCROSSBOW), false);
+ sound = sfx_wpnup;
+ break;
+ case SPR_WBLS: // Weapon_Blaster
+ if (!P_GiveWeapon(player, wp_blaster))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_WPNBLASTER), false);
+ sound = sfx_wpnup;
+ break;
+ case SPR_WSKL: // Weapon_SkullRod
+ if (!P_GiveWeapon(player, wp_skullrod))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_WPNSKULLROD), false);
+ sound = sfx_wpnup;
+ break;
+ case SPR_WPHX: // Weapon_PhoenixRod
+ if (!P_GiveWeapon(player, wp_phoenixrod))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_WPNPHOENIXROD), false);
+ sound = sfx_wpnup;
+ break;
+ case SPR_WGNT: // Weapon_Gauntlets
+ if (!P_GiveWeapon(player, wp_gauntlets))
+ {
+ return;
+ }
+ P_SetMessage(player, DEH_String(TXT_WPNGAUNTLETS), false);
+ sound = sfx_wpnup;
+ break;
+ default:
+ I_Error("P_SpecialThing: Unknown gettable thing");
+ }
+ if (special->flags & MF_COUNTITEM)
+ {
+ player->itemcount++;
+ }
+ if (deathmatch && respawn && !(special->flags & MF_DROPPED))
+ {
+ P_HideSpecialThing(special);
+ }
+ else
+ {
+ P_RemoveMobj(special);
+ }
+ player->bonuscount += BONUSADD;
+ if (player == &players[consoleplayer])
+ {
+ S_StartSound(NULL, sound);
+ SB_PaletteFlash();
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_KillMobj
+//
+//---------------------------------------------------------------------------
+
+void P_KillMobj(mobj_t * source, mobj_t * target)
+{
+ target->flags &= ~(MF_SHOOTABLE | MF_FLOAT | MF_SKULLFLY | MF_NOGRAVITY);
+ target->flags |= MF_CORPSE | MF_DROPOFF;
+ target->flags2 &= ~MF2_PASSMOBJ;
+ target->height >>= 2;
+ if (source && source->player)
+ {
+ if (target->flags & MF_COUNTKILL)
+ { // Count for intermission
+ source->player->killcount++;
+ }
+ if (target->player)
+ { // Frag stuff
+ if (target == source)
+ { // Self-frag
+ target->player->frags[target->player - players]--;
+ }
+ else
+ {
+ source->player->frags[target->player - players]++;
+ if (source->player == &players[consoleplayer])
+ {
+ S_StartSound(NULL, sfx_gfrag);
+ }
+ if (source->player->chickenTics)
+ { // Make a super chicken
+ P_GivePower(source->player, pw_weaponlevel2);
+ }
+ }
+ }
+ }
+ else if (!netgame && (target->flags & MF_COUNTKILL))
+ { // Count all monster deaths
+ players[0].killcount++;
+ }
+ if (target->player)
+ {
+ if (!source)
+ { // Self-frag
+ target->player->frags[target->player - players]--;
+ }
+ target->flags &= ~MF_SOLID;
+ target->flags2 &= ~MF2_FLY;
+ target->player->powers[pw_flight] = 0;
+ target->player->powers[pw_weaponlevel2] = 0;
+ target->player->playerstate = PST_DEAD;
+ P_DropWeapon(target->player);
+ if (target->flags2 & MF2_FIREDAMAGE)
+ { // Player flame death
+ P_SetMobjState(target, S_PLAY_FDTH1);
+ //S_StartSound(target, sfx_hedat1); // Burn sound
+ return;
+ }
+ }
+ if (target->health < -(target->info->spawnhealth >> 1)
+ && target->info->xdeathstate)
+ { // Extreme death
+ P_SetMobjState(target, target->info->xdeathstate);
+ }
+ else
+ { // Normal death
+ P_SetMobjState(target, target->info->deathstate);
+ }
+ target->tics -= P_Random() & 3;
+// I_StartSound(&actor->r, actor->info->deathsound);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_MinotaurSlam
+//
+//---------------------------------------------------------------------------
+
+void P_MinotaurSlam(mobj_t * source, mobj_t * target)
+{
+ angle_t angle;
+ fixed_t thrust;
+
+ angle = R_PointToAngle2(source->x, source->y, target->x, target->y);
+ angle >>= ANGLETOFINESHIFT;
+ thrust = 16 * FRACUNIT + (P_Random() << 10);
+ target->momx += FixedMul(thrust, finecosine[angle]);
+ target->momy += FixedMul(thrust, finesine[angle]);
+ P_DamageMobj(target, NULL, NULL, HITDICE(6));
+ if (target->player)
+ {
+ target->reactiontime = 14 + (P_Random() & 7);
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_TouchWhirlwind
+//
+//---------------------------------------------------------------------------
+
+void P_TouchWhirlwind(mobj_t * target)
+{
+ int randVal;
+
+ target->angle += (P_Random() - P_Random()) << 20;
+ target->momx += (P_Random() - P_Random()) << 10;
+ target->momy += (P_Random() - P_Random()) << 10;
+ if (leveltime & 16 && !(target->flags2 & MF2_BOSS))
+ {
+ randVal = P_Random();
+ if (randVal > 160)
+ {
+ randVal = 160;
+ }
+ target->momz += randVal << 10;
+ if (target->momz > 12 * FRACUNIT)
+ {
+ target->momz = 12 * FRACUNIT;
+ }
+ }
+ if (!(leveltime & 7))
+ {
+ P_DamageMobj(target, NULL, NULL, 3);
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_ChickenMorphPlayer
+//
+// Returns true if the player gets turned into a chicken.
+//
+//---------------------------------------------------------------------------
+
+boolean P_ChickenMorphPlayer(player_t * player)
+{
+ mobj_t *pmo;
+ mobj_t *fog;
+ mobj_t *chicken;
+ fixed_t x;
+ fixed_t y;
+ fixed_t z;
+ angle_t angle;
+ int oldFlags2;
+
+ if (player->chickenTics)
+ {
+ if ((player->chickenTics < CHICKENTICS - TICRATE)
+ && !player->powers[pw_weaponlevel2])
+ { // Make a super chicken
+ P_GivePower(player, pw_weaponlevel2);
+ }
+ return (false);
+ }
+ if (player->powers[pw_invulnerability])
+ { // Immune when invulnerable
+ return (false);
+ }
+ pmo = player->mo;
+ x = pmo->x;
+ y = pmo->y;
+ z = pmo->z;
+ angle = pmo->angle;
+ oldFlags2 = pmo->flags2;
+ P_SetMobjState(pmo, S_FREETARGMOBJ);
+ fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG);
+ S_StartSound(fog, sfx_telept);
+ chicken = P_SpawnMobj(x, y, z, MT_CHICPLAYER);
+ chicken->special1 = player->readyweapon;
+ chicken->angle = angle;
+ chicken->player = player;
+ player->health = chicken->health = MAXCHICKENHEALTH;
+ player->mo = chicken;
+ player->armorpoints = player->armortype = 0;
+ player->powers[pw_invisibility] = 0;
+ player->powers[pw_weaponlevel2] = 0;
+ if (oldFlags2 & MF2_FLY)
+ {
+ chicken->flags2 |= MF2_FLY;
+ }
+ player->chickenTics = CHICKENTICS;
+ P_ActivateBeak(player);
+ return (true);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_ChickenMorph
+//
+//---------------------------------------------------------------------------
+
+boolean P_ChickenMorph(mobj_t * actor)
+{
+ mobj_t *fog;
+ mobj_t *chicken;
+ mobj_t *target;
+ mobjtype_t moType;
+ fixed_t x;
+ fixed_t y;
+ fixed_t z;
+ angle_t angle;
+ int ghost;
+
+ if (actor->player)
+ {
+ return (false);
+ }
+ moType = actor->type;
+ switch (moType)
+ {
+ case MT_POD:
+ case MT_CHICKEN:
+ case MT_HEAD:
+ case MT_MINOTAUR:
+ case MT_SORCERER1:
+ case MT_SORCERER2:
+ return (false);
+ default:
+ break;
+ }
+ x = actor->x;
+ y = actor->y;
+ z = actor->z;
+ angle = actor->angle;
+ ghost = actor->flags & MF_SHADOW;
+ target = actor->target;
+ P_SetMobjState(actor, S_FREETARGMOBJ);
+ fog = P_SpawnMobj(x, y, z + TELEFOGHEIGHT, MT_TFOG);
+ S_StartSound(fog, sfx_telept);
+ chicken = P_SpawnMobj(x, y, z, MT_CHICKEN);
+ chicken->special2 = moType;
+ chicken->special1 = CHICKENTICS + P_Random();
+ chicken->flags |= ghost;
+ chicken->target = target;
+ chicken->angle = angle;
+ return (true);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_AutoUseChaosDevice
+//
+//---------------------------------------------------------------------------
+
+boolean P_AutoUseChaosDevice(player_t * player)
+{
+ int i;
+
+ for (i = 0; i < player->inventorySlotNum; i++)
+ {
+ if (player->inventory[i].type == arti_teleport)
+ {
+ P_PlayerUseArtifact(player, arti_teleport);
+ player->health = player->mo->health = (player->health + 1) / 2;
+ return (true);
+ }
+ }
+ return (false);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_AutoUseHealth
+//
+//---------------------------------------------------------------------------
+
+void P_AutoUseHealth(player_t * player, int saveHealth)
+{
+ int i;
+ int count;
+ int normalCount;
+ int normalSlot;
+ int superCount;
+ int superSlot;
+
+ normalCount = 0;
+ superCount = 0;
+ normalSlot = 0;
+ superSlot = 0;
+
+ for (i = 0; i < player->inventorySlotNum; i++)
+ {
+ if (player->inventory[i].type == arti_health)
+ {
+ normalSlot = i;
+ normalCount = player->inventory[i].count;
+ }
+ else if (player->inventory[i].type == arti_superhealth)
+ {
+ superSlot = i;
+ superCount = player->inventory[i].count;
+ }
+ }
+ if ((gameskill == sk_baby) && (normalCount * 25 >= saveHealth))
+ { // Use quartz flasks
+ count = (saveHealth + 24) / 25;
+ for (i = 0; i < count; i++)
+ {
+ player->health += 25;
+ P_PlayerRemoveArtifact(player, normalSlot);
+ }
+ }
+ else if (superCount * 100 >= saveHealth)
+ { // Use mystic urns
+ count = (saveHealth + 99) / 100;
+ for (i = 0; i < count; i++)
+ {
+ player->health += 100;
+ P_PlayerRemoveArtifact(player, superSlot);
+ }
+ }
+ else if ((gameskill == sk_baby)
+ && (superCount * 100 + normalCount * 25 >= saveHealth))
+ { // Use mystic urns and quartz flasks
+ count = (saveHealth + 24) / 25;
+ saveHealth -= count * 25;
+ for (i = 0; i < count; i++)
+ {
+ player->health += 25;
+ P_PlayerRemoveArtifact(player, normalSlot);
+ }
+ count = (saveHealth + 99) / 100;
+ for (i = 0; i < count; i++)
+ {
+ player->health += 100;
+ P_PlayerRemoveArtifact(player, normalSlot);
+ }
+ }
+ player->mo->health = player->health;
+}
+
+/*
+=================
+=
+= 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 barrel explosions and other environmental stuff
+==================
+*/
+
+void P_DamageMobj
+ (mobj_t * target, mobj_t * inflictor, mobj_t * source, int damage)
+{
+ unsigned ang;
+ int saved;
+ player_t *player;
+ fixed_t thrust;
+ int temp;
+
+ if (!(target->flags & MF_SHOOTABLE))
+ {
+ // Shouldn't happen
+ return;
+ }
+ if (target->health <= 0)
+ {
+ return;
+ }
+ if (target->flags & MF_SKULLFLY)
+ {
+ if (target->type == MT_MINOTAUR)
+ { // Minotaur is invulnerable during charge attack
+ return;
+ }
+ target->momx = target->momy = target->momz = 0;
+ }
+ player = target->player;
+ if (player && gameskill == sk_baby)
+ {
+ // Take half damage in trainer mode
+ damage >>= 1;
+ }
+ // Special damage types
+ if (inflictor)
+ {
+ switch (inflictor->type)
+ {
+ case MT_EGGFX:
+ if (player)
+ {
+ P_ChickenMorphPlayer(player);
+ }
+ else
+ {
+ P_ChickenMorph(target);
+ }
+ return; // Always return
+ case MT_WHIRLWIND:
+ P_TouchWhirlwind(target);
+ return;
+ case MT_MINOTAUR:
+ if (inflictor->flags & MF_SKULLFLY)
+ { // Slam only when in charge mode
+ P_MinotaurSlam(inflictor, target);
+ return;
+ }
+ break;
+ case MT_MACEFX4: // Death ball
+ if ((target->flags2 & MF2_BOSS) || target->type == MT_HEAD)
+ { // Don't allow cheap boss kills
+ break;
+ }
+ else if (target->player)
+ { // Player specific checks
+ if (target->player->powers[pw_invulnerability])
+ { // Can't hurt invulnerable players
+ break;
+ }
+ if (P_AutoUseChaosDevice(target->player))
+ { // Player was saved using chaos device
+ return;
+ }
+ }
+ damage = 10000; // Something's gonna die
+ break;
+ case MT_PHOENIXFX2: // Flame thrower
+ if (target->player && P_Random() < 128)
+ { // Freeze player for a bit
+ target->reactiontime += 4;
+ }
+ break;
+ case MT_RAINPLR1: // Rain missiles
+ case MT_RAINPLR2:
+ case MT_RAINPLR3:
+ case MT_RAINPLR4:
+ if (target->flags2 & MF2_BOSS)
+ { // Decrease damage for bosses
+ damage = (P_Random() & 7) + 1;
+ }
+ break;
+ case MT_HORNRODFX2:
+ case MT_PHOENIXFX1:
+ if (target->type == MT_SORCERER2 && P_Random() < 96)
+ { // D'Sparil teleports away
+ P_DSparilTeleport(target);
+ return;
+ }
+ break;
+ case MT_BLASTERFX1:
+ case MT_RIPPER:
+ if (target->type == MT_HEAD)
+ { // Less damage to Ironlich bosses
+ damage = P_Random() & 1;
+ if (!damage)
+ {
+ return;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ // Push the target unless source is using the gauntlets
+ if (inflictor && (!source || !source->player
+ || source->player->readyweapon != wp_gauntlets)
+ && !(inflictor->flags2 & MF2_NODMGTHRUST))
+ {
+ ang = R_PointToAngle2(inflictor->x, inflictor->y,
+ target->x, target->y);
+ //thrust = damage*(FRACUNIT>>3)*100/target->info->mass;
+ thrust = damage * (FRACUNIT >> 3) * 150 / target->info->mass;
+ // make fall forwards sometimes
+ if ((damage < 40) && (damage > target->health)
+ && (target->z - inflictor->z > 64 * FRACUNIT) && (P_Random() & 1))
+ {
+ ang += ANG180;
+ thrust *= 4;
+ }
+ ang >>= ANGLETOFINESHIFT;
+ if (source && source->player && (source == inflictor)
+ && source->player->powers[pw_weaponlevel2]
+ && source->player->readyweapon == wp_staff)
+ {
+ // Staff power level 2
+ target->momx += FixedMul(10 * FRACUNIT, finecosine[ang]);
+ target->momy += FixedMul(10 * FRACUNIT, finesine[ang]);
+ if (!(target->flags & MF_NOGRAVITY))
+ {
+ target->momz += 5 * FRACUNIT;
+ }
+ }
+ else
+ {
+ target->momx += FixedMul(thrust, finecosine[ang]);
+ target->momy += FixedMul(thrust, finesine[ang]);
+ }
+ }
+
+ //
+ // player specific
+ //
+ if (player)
+ {
+ // end of game hell hack
+ //if(target->subsector->sector->special == 11
+ // && damage >= target->health)
+ //{
+ // damage = target->health - 1;
+ //}
+
+ if (damage < 1000 && ((player->cheats & CF_GODMODE)
+ || player->powers[pw_invulnerability]))
+ {
+ return;
+ }
+ if (player->armortype)
+ {
+ if (player->armortype == 1)
+ {
+ saved = damage >> 1;
+ }
+ else
+ {
+ saved = (damage >> 1) + (damage >> 2);
+ }
+ if (player->armorpoints <= saved)
+ {
+ // armor is used up
+ saved = player->armorpoints;
+ player->armortype = 0;
+ }
+ player->armorpoints -= saved;
+ damage -= saved;
+ }
+ if (damage >= player->health
+ && ((gameskill == sk_baby) || deathmatch) && !player->chickenTics)
+ { // Try to use some inventory health
+ P_AutoUseHealth(player, damage - player->health + 1);
+ }
+ 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...
+ }
+ temp = damage < 100 ? damage : 100;
+ if (player == &players[consoleplayer])
+ {
+ I_Tactile(40, 10, 40 + temp * 2);
+ SB_PaletteFlash();
+ }
+ }
+
+ //
+ // do the damage
+ //
+ target->health -= damage;
+ if (target->health <= 0)
+ { // Death
+ target->special1 = damage;
+ if (target->type == MT_POD && source && source->type != MT_POD)
+ { // Make sure players get frags for chain-reaction kills
+ target->target = source;
+ }
+ if (player && inflictor && !player->chickenTics)
+ { // Check for flame death
+ if ((inflictor->flags2 & MF2_FIREDAMAGE)
+ || ((inflictor->type == MT_PHOENIXFX1)
+ && (target->health > -50) && (damage > 25)))
+ {
+ target->flags2 |= MF2_FIREDAMAGE;
+ }
+ }
+ P_KillMobj(source, target);
+ return;
+ }
+ if ((P_Random() < target->info->painchance)
+ && !(target->flags & MF_SKULLFLY))
+ {
+ target->flags |= MF_JUSTHIT; // fight back!
+ P_SetMobjState(target, target->info->painstate);
+ }
+ target->reactiontime = 0; // we're awake now...
+ if (!target->threshold && source && !(source->flags2 & MF2_BOSS)
+ && !(target->type == MT_SORCERER2 && source->type == MT_WIZARD))
+ {
+ // Target actor is not intent on another actor,
+ // so make him chase after source
+ target->target = source;
+ target->threshold = BASETHRESHOLD;
+ if (target->state == &states[target->info->spawnstate]
+ && target->info->seestate != S_NULL)
+ {
+ P_SetMobjState(target, target->info->seestate);
+ }
+ }
+}
diff --git a/src/heretic/p_lights.c b/src/heretic/p_lights.c
new file mode 100644
index 00000000..5d97a55e
--- /dev/null
+++ b/src/heretic/p_lights.c
@@ -0,0 +1,282 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 "doomdef.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "v_video.h"
+
+//==================================================================
+//==================================================================
+//
+// BROKEN LIGHT FLASHING
+//
+//==================================================================
+//==================================================================
+
+//==================================================================
+//
+// T_LightFlash
+//
+// After the map has been loaded, scan each sector for specials
+// that spawn thinkers
+//
+//==================================================================
+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() & flash->mintime) + 1;
+ }
+ else
+ {
+ flash->sector->lightlevel = flash->maxlight;
+ flash->count = (P_Random() & flash->maxtime) + 1;
+ }
+
+}
+
+
+//==================================================================
+//
+// P_SpawnLightFlash
+//
+// After the map has been loaded, scan each sector for specials that spawn thinkers
+//
+//==================================================================
+void P_SpawnLightFlash(sector_t * sector)
+{
+ lightflash_t *flash;
+
+ sector->special = 0; // nothing special about it during gameplay
+
+ flash = Z_Malloc(sizeof(*flash), PU_LEVSPEC, 0);
+ 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() & flash->maxtime) + 1;
+}
+
+//==================================================================
+//
+// STROBE LIGHT FLASHING
+//
+//==================================================================
+
+//==================================================================
+//
+// T_StrobeFlash
+//
+// After the map has been loaded, scan each sector for specials that spawn thinkers
+//
+//==================================================================
+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;
+ }
+
+}
+
+//==================================================================
+//
+// P_SpawnLightFlash
+//
+// After the map has been loaded, scan each sector for specials that spawn thinkers
+//
+//==================================================================
+void P_SpawnStrobeFlash(sector_t * sector, int fastOrSlow, int inSync)
+{
+ strobe_t *flash;
+
+ flash = Z_Malloc(sizeof(*flash), PU_LEVSPEC, 0);
+ 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;
+ sector->special = 0; // nothing special about it during gameplay
+
+ if (!inSync)
+ flash->count = (P_Random() & 7) + 1;
+ else
+ flash->count = 1;
+}
+
+//==================================================================
+//
+// Start strobing lights (usually from a trigger)
+//
+//==================================================================
+void EV_StartLightStrobing(line_t * line)
+{
+ int secnum;
+ sector_t *sec;
+
+ secnum = -1;
+ while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
+ {
+ sec = &sectors[secnum];
+ if (sec->specialdata)
+ continue;
+
+ P_SpawnStrobeFlash(sec, SLOWDARK, 0);
+ }
+}
+
+//==================================================================
+//
+// TURN LINE'S TAG LIGHTS OFF
+//
+//==================================================================
+void EV_TurnTagLightsOff(line_t * line)
+{
+ int i;
+ int j;
+ int min;
+ sector_t *sector;
+ sector_t *tsec;
+ line_t *templine;
+
+ sector = sectors;
+ for (j = 0; j < numsectors; j++, sector++)
+ if (sector->tag == line->tag)
+ {
+ min = sector->lightlevel;
+ for (i = 0; i < sector->linecount; i++)
+ {
+ templine = sector->lines[i];
+ tsec = getNextSector(templine, sector);
+ if (!tsec)
+ continue;
+ if (tsec->lightlevel < min)
+ min = tsec->lightlevel;
+ }
+ sector->lightlevel = min;
+ }
+}
+
+//==================================================================
+//
+// TURN LINE'S TAG LIGHTS ON
+//
+//==================================================================
+void EV_LightTurnOn(line_t * line, int bright)
+{
+ int i;
+ int j;
+ sector_t *sector;
+ sector_t *temp;
+ line_t *templine;
+
+ sector = sectors;
+
+ for (i = 0; i < numsectors; i++, sector++)
+ if (sector->tag == line->tag)
+ {
+ //
+ // bright = 0 means to search for highest
+ // light level surrounding sector
+ //
+ if (!bright)
+ {
+ for (j = 0; j < sector->linecount; j++)
+ {
+ templine = sector->lines[j];
+ temp = getNextSector(templine, sector);
+ if (!temp)
+ continue;
+ if (temp->lightlevel > bright)
+ bright = temp->lightlevel;
+ }
+ }
+ sector->lightlevel = bright;
+ }
+}
+
+//==================================================================
+//
+// Spawn glowing light
+//
+//==================================================================
+void T_Glow(glow_t * g)
+{
+ switch (g->direction)
+ {
+ case -1: // DOWN
+ g->sector->lightlevel -= GLOWSPEED;
+ if (g->sector->lightlevel <= g->minlight)
+ {
+ g->sector->lightlevel += GLOWSPEED;
+ g->direction = 1;
+ }
+ break;
+ case 1: // UP
+ g->sector->lightlevel += GLOWSPEED;
+ if (g->sector->lightlevel >= g->maxlight)
+ {
+ g->sector->lightlevel -= GLOWSPEED;
+ g->direction = -1;
+ }
+ break;
+ }
+}
+
+void P_SpawnGlowingLight(sector_t * sector)
+{
+ glow_t *g;
+
+ g = Z_Malloc(sizeof(*g), PU_LEVSPEC, 0);
+ 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 = 0;
+}
diff --git a/src/heretic/p_local.h b/src/heretic/p_local.h
new file mode 100644
index 00000000..4fb58236
--- /dev/null
+++ b/src/heretic/p_local.h
@@ -0,0 +1,287 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_local.h
+
+#ifndef __P_LOCAL__
+#define __P_LOCAL__
+
+#ifndef __R_LOCAL__
+#include "r_local.h"
+#endif
+
+#define STARTREDPALS 1
+#define STARTBONUSPALS 9
+#define NUMREDPALS 8
+#define NUMBONUSPALS 4
+
+#define FOOTCLIPSIZE 10*FRACUNIT
+
+#define TOCENTER -8
+#define FLOATSPEED (FRACUNIT*4)
+
+#define MAXHEALTH 100
+#define MAXCHICKENHEALTH 30
+#define VIEWHEIGHT (41*FRACUNIT)
+
+// 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)
+
+// player radius for movement checking
+#define PLAYERRADIUS 16*FRACUNIT
+
+// MAXRADIUS is for precalculated sector block boxes
+// the spider demon is larger, but we don't have any moving sectors
+// nearby
+#define MAXRADIUS 32*FRACUNIT
+
+#define GRAVITY FRACUNIT
+#define MAXMOVE (30*FRACUNIT)
+
+#define USERANGE (64*FRACUNIT)
+#define MELEERANGE (64*FRACUNIT)
+#define MISSILERANGE (32*64*FRACUNIT)
+
+typedef enum
+{
+ DI_EAST,
+ DI_NORTHEAST,
+ DI_NORTH,
+ DI_NORTHWEST,
+ DI_WEST,
+ DI_SOUTHWEST,
+ DI_SOUTH,
+ DI_SOUTHEAST,
+ DI_NODIR,
+ NUMDIRS
+} dirtype_t;
+
+#define BASETHRESHOLD 100 // follow a player exlusively for 3 seconds
+
+// ***** P_TICK *****
+
+extern thinker_t thinkercap; // both the head and tail of the thinker list
+extern int TimerGame; // tic countdown for deathmatch
+
+void P_InitThinkers(void);
+void P_AddThinker(thinker_t * thinker);
+void P_RemoveThinker(thinker_t * thinker);
+
+// ***** P_PSPR *****
+
+#define USE_GWND_AMMO_1 1
+#define USE_GWND_AMMO_2 1
+#define USE_CBOW_AMMO_1 1
+#define USE_CBOW_AMMO_2 1
+#define USE_BLSR_AMMO_1 1
+#define USE_BLSR_AMMO_2 5
+#define USE_SKRD_AMMO_1 1
+#define USE_SKRD_AMMO_2 5
+#define USE_PHRD_AMMO_1 1
+#define USE_PHRD_AMMO_2 1
+#define USE_MACE_AMMO_1 1
+#define USE_MACE_AMMO_2 5
+
+void P_OpenWeapons(void);
+void P_CloseWeapons(void);
+void P_AddMaceSpot(mapthing_t * mthing);
+void P_RepositionMace(mobj_t * mo);
+void P_SetPsprite(player_t * player, int position, statenum_t stnum);
+void P_SetupPsprites(player_t * curplayer);
+void P_MovePsprites(player_t * curplayer);
+void P_DropWeapon(player_t * player);
+void P_ActivateBeak(player_t * player);
+void P_PostChickenWeapon(player_t * player, weapontype_t weapon);
+void P_UpdateBeak(player_t * player, pspdef_t * psp);
+
+// ***** P_USER *****
+
+void P_PlayerThink(player_t * player);
+void P_Thrust(player_t * player, angle_t angle, fixed_t move);
+void P_PlayerRemoveArtifact(player_t * player, int slot);
+void P_PlayerUseArtifact(player_t * player, artitype_t arti);
+boolean P_UseArtifact(player_t * player, artitype_t arti);
+int P_GetPlayerNum(player_t * player);
+
+// ***** P_MOBJ *****
+
+#define FLOOR_SOLID 0
+#define FLOOR_WATER 1
+#define FLOOR_LAVA 2
+#define FLOOR_SLUDGE 3
+
+#define ONFLOORZ INT_MIN
+#define ONCEILINGZ INT_MAX
+#define FLOATRANDZ (INT_MAX-1)
+
+extern mobjtype_t PuffType;
+extern mobj_t *MissileMobj;
+
+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);
+boolean P_SetMobjStateNF(mobj_t * mobj, statenum_t state);
+void P_ThrustMobj(mobj_t * mo, angle_t angle, fixed_t move);
+int P_FaceMobj(mobj_t * source, mobj_t * target, angle_t * delta);
+boolean P_SeekerMissile(mobj_t * actor, angle_t thresh, angle_t turnMax);
+void P_MobjThinker(mobj_t * mobj);
+void P_BlasterMobjThinker(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);
+void P_BloodSplatter(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator);
+void P_RipperBlood(mobj_t * mo);
+int P_GetThingFloorType(mobj_t * thing);
+int P_HitFloor(mobj_t * thing);
+boolean P_CheckMissileSpawn(mobj_t * missile);
+mobj_t *P_SpawnMissile(mobj_t * source, mobj_t * dest, mobjtype_t type);
+mobj_t *P_SpawnMissileAngle(mobj_t * source, mobjtype_t type,
+ angle_t angle, fixed_t momz);
+mobj_t *P_SpawnPlayerMissile(mobj_t * source, mobjtype_t type);
+mobj_t *P_SPMAngle(mobj_t * source, mobjtype_t type, angle_t angle);
+
+// ***** P_ENEMY *****
+
+void P_NoiseAlert(mobj_t * target, mobj_t * emmiter);
+void P_InitMonsters(void);
+void P_AddBossSpot(fixed_t x, fixed_t y, angle_t angle);
+void P_Massacre(void);
+void P_DSparilTeleport(mobj_t * actor);
+
+// ***** P_MAPUTL *****
+
+typedef struct
+{
+ fixed_t x, y, dx, dy;
+} divline_t;
+
+typedef struct
+{
+ fixed_t frac; // along trace line
+ boolean isaline;
+ union
+ {
+ mobj_t *thing;
+ line_t *line;
+ } d;
+} intercept_t;
+
+#define MAXINTERCEPTS 128
+extern intercept_t intercepts[MAXINTERCEPTS], *intercept_p;
+typedef boolean(*traverser_t) (intercept_t * in);
+
+
+fixed_t P_AproxDistance(fixed_t dx, fixed_t dy);
+int P_PointOnLineSide(fixed_t x, fixed_t y, line_t * line);
+int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t * line);
+void P_MakeDivline(line_t * li, divline_t * dl);
+fixed_t P_InterceptVector(divline_t * v2, divline_t * v1);
+int P_BoxOnLineSide(fixed_t * tmbox, line_t * ld);
+
+extern fixed_t opentop, openbottom, openrange;
+extern fixed_t lowfloor;
+void P_LineOpening(line_t * linedef);
+
+boolean P_BlockLinesIterator(int x, int y, boolean(*func) (line_t *));
+boolean P_BlockThingsIterator(int x, int y, boolean(*func) (mobj_t *));
+
+#define PT_ADDLINES 1
+#define PT_ADDTHINGS 2
+#define PT_EARLYOUT 4
+
+extern divline_t trace;
+boolean P_PathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2,
+ int flags, boolean(*trav) (intercept_t *));
+
+void P_UnsetThingPosition(mobj_t * thing);
+void P_SetThingPosition(mobj_t * thing);
+
+// ***** P_MAP *****
+
+extern boolean floatok; // if true, move would be ok if
+extern fixed_t tmfloorz, tmceilingz; // within tmfloorz - tmceilingz
+
+extern line_t *ceilingline;
+boolean P_TestMobjLocation(mobj_t * mobj);
+boolean P_CheckPosition(mobj_t * thing, fixed_t x, fixed_t y);
+mobj_t *P_CheckOnmobj(mobj_t * thing);
+void P_FakeZMovement(mobj_t * mo);
+boolean P_TryMove(mobj_t * thing, fixed_t x, fixed_t y);
+boolean P_TeleportMove(mobj_t * thing, fixed_t x, fixed_t y);
+void P_SlideMove(mobj_t * mo);
+boolean P_CheckSight(mobj_t * t1, mobj_t * t2);
+void P_UseLines(player_t * player);
+
+boolean P_ChangeSector(sector_t * sector, boolean crunch);
+
+extern mobj_t *linetarget; // who got hit (or NULL)
+fixed_t P_AimLineAttack(mobj_t * t1, angle_t angle, fixed_t distance);
+
+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);
+
+// ***** P_SETUP *****
+
+extern byte *rejectmatrix; // for fast sight rejection
+extern short *blockmaplump; // offsets in blockmap are from here
+extern short *blockmap;
+extern int bmapwidth, bmapheight; // in mapblocks
+extern fixed_t bmaporgx, bmaporgy; // origin of block map
+extern mobj_t **blocklinks; // for thing chains
+
+// ***** P_INTER *****
+
+extern int maxammo[NUMAMMO];
+extern int clipammo[NUMAMMO];
+
+void P_SetMessage(player_t * player, char *message, boolean ultmsg);
+void P_TouchSpecialThing(mobj_t * special, mobj_t * toucher);
+void P_DamageMobj(mobj_t * target, mobj_t * inflictor, mobj_t * source,
+ int damage);
+boolean P_GiveAmmo(player_t * player, ammotype_t ammo, int count);
+boolean P_GiveArtifact(player_t * player, artitype_t arti, mobj_t * mo);
+boolean P_GiveBody(player_t * player, int num);
+boolean P_GivePower(player_t * player, powertype_t power);
+boolean P_ChickenMorphPlayer(player_t * player);
+
+// ***** AM_MAP *****
+
+boolean AM_Responder(event_t * ev);
+void AM_Ticker(void);
+void AM_Drawer(void);
+
+// ***** SB_BAR *****
+
+extern int SB_state;
+extern int ArtifactFlash;
+void SB_PaletteFlash(void);
+
+#include "p_spec.h"
+
+#endif // __P_LOCAL__
diff --git a/src/heretic/p_map.c b/src/heretic/p_map.c
new file mode 100644
index 00000000..0b1c6df1
--- /dev/null
+++ b/src/heretic/p_map.c
@@ -0,0 +1,1698 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+// P_map.c
+
+#include <stdlib.h>
+
+#include "doomdef.h"
+#include "i_system.h"
+#include "m_bbox.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "s_sound.h"
+
+/*
+===============================================================================
+
+NOTES:
+
+
+===============================================================================
+*/
+
+/*
+===============================================================================
+
+mobj_t NOTES
+
+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 it's 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 be 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 it's origin contained.
+
+A valid mobj_t is a mobj_t that has the proper subsector_t filled in for it's
+xy coordinates and is linked into the subsector's sector 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.
+
+
+===============================================================================
+*/
+
+fixed_t tmbbox[4];
+mobj_t *tmthing;
+int tmflags;
+fixed_t tmx, tmy;
+
+boolean floatok; // if true, move would be ok if
+ // within tmfloorz - tmceilingz
+
+fixed_t tmfloorz, tmceilingz, tmdropoffz;
+
+// keep track of the line that lowers the ceiling, so missiles don't explode
+// against sky hack walls
+line_t *ceilingline;
+
+// keep track of special lines as they are hit, but don't process them
+// until the move is proven valid
+#define MAXSPECIALCROSS 8
+line_t *spechit[MAXSPECIALCROSS];
+int numspechit;
+
+mobj_t *onmobj; //generic global onmobj...used for landing on pods/players
+
+/*
+===============================================================================
+
+ TELEPORT MOVE
+
+===============================================================================
+*/
+
+/*
+==================
+=
+= PIT_StompThing
+=
+==================
+*/
+
+boolean PIT_StompThing(mobj_t * thing)
+{
+ fixed_t blockdist;
+
+ if (!(thing->flags & MF_SHOOTABLE))
+ return true;
+
+ blockdist = thing->radius + tmthing->radius;
+ if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
+ return true; // didn't hit it
+
+ if (thing == tmthing)
+ return true; // don't clip against self
+
+ if (!(tmthing->flags2 & MF2_TELESTOMP))
+ { // Not allowed to stomp things
+ return (false);
+ }
+
+ P_DamageMobj(thing, tmthing, tmthing, 10000);
+
+ return true;
+}
+
+
+/*
+===================
+=
+= P_TeleportMove
+=
+===================
+*/
+
+boolean P_TeleportMove(mobj_t * thing, fixed_t x, fixed_t y)
+{
+ int xl, xh, yl, yh, bx, by;
+ subsector_t *newsubsec;
+
+//
+// kill anything occupying the position
+//
+
+ tmthing = thing;
+ tmflags = thing->flags;
+
+ 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 link the thing into its new position
+//
+ P_UnsetThingPosition(thing);
+
+ thing->floorz = tmfloorz;
+ thing->ceilingz = tmceilingz;
+ thing->x = x;
+ thing->y = y;
+
+ P_SetThingPosition(thing);
+
+ return true;
+}
+
+/*
+===============================================================================
+
+ MOVEMENT ITERATOR FUNCTIONS
+
+===============================================================================
+*/
+
+/*
+==================
+=
+= PIT_CheckLine
+=
+= Adjusts tmfloorz and tmceilingz as lines are contacted
+==================
+*/
+
+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);
+ }
+ if (P_BoxOnLineSide(tmbbox, ld) != -1)
+ {
+ return (true);
+ }
+
+// 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.
+*/
+
+ if (!ld->backsector)
+ { // One sided line
+ if (tmthing->flags & MF_MISSILE)
+ { // Missiles can trigger impact specials
+ if (ld->special)
+ {
+ spechit[numspechit] = ld;
+ numspechit++;
+ }
+ }
+ return false;
+ }
+ if (!(tmthing->flags & MF_MISSILE))
+ {
+ if (ld->flags & ML_BLOCKING)
+ { // Explicitly blocking everything
+ return (false);
+ }
+ if (!tmthing->player && ld->flags & ML_BLOCKMONSTERS
+ && tmthing->type != MT_POD)
+ { // Block monsters only
+ return (false);
+ }
+ }
+ P_LineOpening(ld); // set openrange, opentop, openbottom
+ // adjust floor / ceiling heights
+ if (opentop < tmceilingz)
+ {
+ tmceilingz = opentop;
+ ceilingline = ld;
+ }
+ if (openbottom > tmfloorz)
+ {
+ tmfloorz = openbottom;
+ }
+ if (lowfloor < tmdropoffz)
+ {
+ tmdropoffz = lowfloor;
+ }
+ if (ld->special)
+ { // Contacted a special line, add it to the list
+ spechit[numspechit] = ld;
+ numspechit++;
+ }
+ return (true);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC PIT_CheckThing
+//
+//---------------------------------------------------------------------------
+
+boolean PIT_CheckThing(mobj_t * thing)
+{
+ fixed_t blockdist;
+ boolean solid;
+ int damage;
+
+ if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE)))
+ { // Can't hit thing
+ return (true);
+ }
+ blockdist = thing->radius + tmthing->radius;
+ if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
+ { // Didn't hit thing
+ return (true);
+ }
+ if (thing == tmthing)
+ { // Don't clip against self
+ return (true);
+ }
+ if (tmthing->flags2 & MF2_PASSMOBJ)
+ { // check if a mobj passed over/under another object
+ if ((tmthing->type == MT_IMP || tmthing->type == MT_WIZARD)
+ && (thing->type == MT_IMP || thing->type == MT_WIZARD))
+ { // don't let imps/wizards fly over other imps/wizards
+ return false;
+ }
+ if (tmthing->z > thing->z + thing->height
+ && !(thing->flags & MF_SPECIAL))
+ {
+ return (true);
+ }
+ else if (tmthing->z + tmthing->height < thing->z
+ && !(thing->flags & MF_SPECIAL))
+ { // under thing
+ return (true);
+ }
+ }
+ // Check for skulls slamming into things
+ if (tmthing->flags & MF_SKULLFLY)
+ {
+ damage = ((P_Random() % 8) + 1) * tmthing->damage;
+ P_DamageMobj(thing, tmthing, tmthing, damage);
+ tmthing->flags &= ~MF_SKULLFLY;
+ tmthing->momx = tmthing->momy = tmthing->momz = 0;
+ P_SetMobjState(tmthing, tmthing->info->seestate);
+ return (false);
+ }
+ // Check for missile
+ if (tmthing->flags & MF_MISSILE)
+ {
+ // Check for passing through a ghost
+ if ((thing->flags & MF_SHADOW) && (tmthing->flags2 & MF2_THRUGHOST))
+ {
+ return (true);
+ }
+ // Check if it went over / under
+ if (tmthing->z > thing->z + thing->height)
+ { // Over thing
+ return (true);
+ }
+ if (tmthing->z + tmthing->height < thing->z)
+ { // Under thing
+ return (true);
+ }
+ if (tmthing->target && tmthing->target->type == thing->type)
+ { // Don't hit same species as originator
+ if (thing == tmthing->target)
+ { // Don't missile self
+ return (true);
+ }
+ if (thing->type != MT_PLAYER)
+ { // Hit same species as originator, explode, no damage
+ return (false);
+ }
+ }
+ if (!(thing->flags & MF_SHOOTABLE))
+ { // Didn't do any damage
+ return !(thing->flags & MF_SOLID);
+ }
+ if (tmthing->flags2 & MF2_RIP)
+ {
+ if (!(thing->flags & MF_NOBLOOD))
+ { // Ok to spawn some blood
+ P_RipperBlood(tmthing);
+ }
+ S_StartSound(tmthing, sfx_ripslop);
+ damage = ((P_Random() & 3) + 2) * tmthing->damage;
+ P_DamageMobj(thing, tmthing, tmthing->target, damage);
+ if (thing->flags2 & MF2_PUSHABLE
+ && !(tmthing->flags2 & MF2_CANNOTPUSH))
+ { // Push thing
+ thing->momx += tmthing->momx >> 2;
+ thing->momy += tmthing->momy >> 2;
+ }
+ numspechit = 0;
+ return (true);
+ }
+ // Do damage
+ damage = ((P_Random() % 8) + 1) * tmthing->damage;
+ if (damage)
+ {
+ if (!(thing->flags & MF_NOBLOOD) && P_Random() < 192)
+ {
+ P_BloodSplatter(tmthing->x, tmthing->y, tmthing->z, thing);
+ }
+ P_DamageMobj(thing, tmthing, tmthing->target, damage);
+ }
+ return (false);
+ }
+ if (thing->flags2 & MF2_PUSHABLE && !(tmthing->flags2 & MF2_CANNOTPUSH))
+ { // Push thing
+ thing->momx += tmthing->momx >> 2;
+ thing->momy += tmthing->momy >> 2;
+ }
+ // Check for special thing
+ if (thing->flags & MF_SPECIAL)
+ {
+ solid = thing->flags & MF_SOLID;
+ if (tmflags & MF_PICKUP)
+ { // Can be picked up by tmthing
+ P_TouchSpecialThing(thing, tmthing); // Can remove thing
+ }
+ return (!solid);
+ }
+ return (!(thing->flags & MF_SOLID));
+}
+
+//---------------------------------------------------------------------------
+//
+// PIT_CheckOnmobjZ
+//
+//---------------------------------------------------------------------------
+
+boolean PIT_CheckOnmobjZ(mobj_t * thing)
+{
+ fixed_t blockdist;
+
+ if (!(thing->flags & (MF_SOLID | MF_SPECIAL | MF_SHOOTABLE)))
+ { // Can't hit thing
+ return (true);
+ }
+ blockdist = thing->radius + tmthing->radius;
+ if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
+ { // Didn't hit thing
+ return (true);
+ }
+ if (thing == tmthing)
+ { // Don't clip against self
+ return (true);
+ }
+ if (tmthing->z > thing->z + thing->height)
+ {
+ return (true);
+ }
+ else if (tmthing->z + tmthing->height < thing->z)
+ { // under thing
+ return (true);
+ }
+ if (thing->flags & MF_SOLID)
+ {
+ onmobj = thing;
+ }
+ return (!(thing->flags & MF_SOLID));
+}
+
+/*
+===============================================================================
+
+ MOVEMENT CLIPPING
+
+===============================================================================
+*/
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_TestMobjLocation
+//
+// Returns true if the mobj is not blocked by anything at its current
+// location, otherwise returns false.
+//
+//----------------------------------------------------------------------------
+
+boolean P_TestMobjLocation(mobj_t * mobj)
+{
+ int flags;
+
+ flags = mobj->flags;
+ mobj->flags &= ~MF_PICKUP;
+ if (P_CheckPosition(mobj, mobj->x, mobj->y))
+ { // XY is ok, now check Z
+ mobj->flags = flags;
+ if ((mobj->z < mobj->floorz)
+ || (mobj->z + mobj->height > mobj->ceilingz))
+ { // Bad Z
+ return (false);
+ }
+ return (true);
+ }
+ mobj->flags = flags;
+ return (false);
+}
+
+/*
+==================
+=
+= 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, xh, yl, yh, bx, by;
+ subsector_t *newsubsec;
+
+ tmthing = thing;
+ tmflags = thing->flags;
+
+ 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;
+
+ if (tmflags & 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;
+
+ return true;
+}
+
+//=============================================================================
+//
+// P_CheckOnmobj(mobj_t *thing)
+//
+// Checks if the new Z position is legal
+//=============================================================================
+
+mobj_t *P_CheckOnmobj(mobj_t * thing)
+{
+ int xl, xh, yl, yh, bx, by;
+ subsector_t *newsubsec;
+ fixed_t x;
+ fixed_t y;
+ mobj_t oldmo;
+
+ x = thing->x;
+ y = thing->y;
+ tmthing = thing;
+ tmflags = thing->flags;
+ oldmo = *thing; // save the old mobj before the fake zmovement
+ P_FakeZMovement(tmthing);
+
+ 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;
+
+ if (tmflags & MF_NOCLIP)
+ return NULL;
+
+//
+// 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_CheckOnmobjZ))
+ {
+ *tmthing = oldmo;
+ return onmobj;
+ }
+ *tmthing = oldmo;
+ return NULL;
+}
+
+//=============================================================================
+//
+// P_FakeZMovement
+//
+// Fake the zmovement so that we can check if a move is legal
+//=============================================================================
+
+void P_FakeZMovement(mobj_t * mo)
+{
+ int dist;
+ int delta;
+//
+// adjust height
+//
+ mo->z += mo->momz;
+ if (mo->flags & MF_FLOAT && mo->target)
+ { // float down towards target if too close
+ if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT))
+ {
+ dist =
+ P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y);
+ delta = (mo->target->z + (mo->height >> 1)) - mo->z;
+ if (delta < 0 && dist < -(delta * 3))
+ mo->z -= FLOATSPEED;
+ else if (delta > 0 && dist < (delta * 3))
+ mo->z += FLOATSPEED;
+ }
+ }
+ if (mo->player && mo->flags2 & MF2_FLY && !(mo->z <= mo->floorz)
+ && leveltime & 2)
+ {
+ mo->z += finesine[(FINEANGLES / 20 * leveltime >> 2) & FINEMASK];
+ }
+
+//
+// clip movement
+//
+ if (mo->z <= mo->floorz)
+ { // Hit the floor
+ mo->z = mo->floorz;
+ if (mo->momz < 0)
+ {
+ mo->momz = 0;
+ }
+ if (mo->flags & MF_SKULLFLY)
+ { // The skull slammed into something
+ mo->momz = -mo->momz;
+ }
+ if (mo->info->crashstate && (mo->flags & MF_CORPSE))
+ {
+ return;
+ }
+ }
+ else if (mo->flags2 & MF2_LOGRAV)
+ {
+ if (mo->momz == 0)
+ mo->momz = -(GRAVITY >> 3) * 2;
+ else
+ mo->momz -= GRAVITY >> 3;
+ }
+ else if (!(mo->flags & MF_NOGRAVITY))
+ {
+ if (mo->momz == 0)
+ mo->momz = -GRAVITY * 2;
+ else
+ mo->momz -= GRAVITY;
+ }
+
+ if (mo->z + mo->height > mo->ceilingz)
+ { // hit the ceiling
+ if (mo->momz > 0)
+ mo->momz = 0;
+ mo->z = mo->ceilingz - mo->height;
+ if (mo->flags & MF_SKULLFLY)
+ { // the skull slammed into something
+ mo->momz = -mo->momz;
+ }
+ }
+}
+
+//==========================================================================
+//
+// CheckMissileImpact
+//
+//==========================================================================
+
+void CheckMissileImpact(mobj_t * mobj)
+{
+ int i;
+
+ if (!numspechit || !(mobj->flags & MF_MISSILE) || !mobj->target)
+ {
+ return;
+ }
+ if (!mobj->target->player)
+ {
+ return;
+ }
+ for (i = numspechit - 1; i >= 0; i--)
+ {
+ P_ShootSpecialLine(mobj->target, spechit[i]);
+ }
+}
+
+/*
+===================
+=
+= 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)
+{
+ fixed_t oldx, oldy;
+ int side, oldside;
+ line_t *ld;
+
+ floatok = false;
+ if (!P_CheckPosition(thing, x, y))
+ { // Solid wall or thing
+ CheckMissileImpact(thing);
+ return false;
+ }
+ if (!(thing->flags & MF_NOCLIP))
+ {
+ if (tmceilingz - tmfloorz < thing->height)
+ { // Doesn't fit
+ CheckMissileImpact(thing);
+ return false;
+ }
+ floatok = true;
+ if (!(thing->flags & MF_TELEPORT)
+ && tmceilingz - thing->z < thing->height
+ && !(thing->flags2 & MF2_FLY))
+ { // mobj must lower itself to fit
+ CheckMissileImpact(thing);
+ return false;
+ }
+ if (thing->flags2 & MF2_FLY)
+ {
+ if (thing->z + thing->height > tmceilingz)
+ {
+ thing->momz = -8 * FRACUNIT;
+ return false;
+ }
+ else if (thing->z < tmfloorz
+ && tmfloorz - tmdropoffz > 24 * FRACUNIT)
+ {
+ thing->momz = 8 * FRACUNIT;
+ return false;
+ }
+ }
+ if (!(thing->flags & MF_TELEPORT)
+ // The Minotaur floor fire (MT_MNTRFX2) can step up any amount
+ && thing->type != MT_MNTRFX2
+ && tmfloorz - thing->z > 24 * FRACUNIT)
+ { // Too big a step up
+ CheckMissileImpact(thing);
+ return false;
+ }
+ if ((thing->flags & MF_MISSILE) && tmfloorz > thing->z)
+ {
+ CheckMissileImpact(thing);
+ }
+ if (!(thing->flags & (MF_DROPOFF | MF_FLOAT))
+ && tmfloorz - tmdropoffz > 24 * FRACUNIT)
+ { // Can't move over a dropoff
+ return false;
+ }
+ }
+
+//
+// the move is ok, so link the thing into its new position
+//
+ P_UnsetThingPosition(thing);
+
+ oldx = thing->x;
+ oldy = thing->y;
+ thing->floorz = tmfloorz;
+ thing->ceilingz = tmceilingz;
+ thing->x = x;
+ thing->y = y;
+
+ P_SetThingPosition(thing);
+
+ if (thing->flags2 & MF2_FOOTCLIP
+ && P_GetThingFloorType(thing) != FLOOR_SOLID)
+ {
+ thing->flags2 |= MF2_FEETARECLIPPED;
+ }
+ else if (thing->flags2 & MF2_FEETARECLIPPED)
+ {
+ thing->flags2 &= ~MF2_FEETARECLIPPED;
+ }
+
+//
+// if any special lines were hit, do the effect
+//
+ if (!(thing->flags & (MF_TELEPORT | MF_NOCLIP)))
+ while (numspechit--)
+ {
+ // see if the line was crossed
+ ld = spechit[numspechit];
+ side = P_PointOnLineSide(thing->x, thing->y, ld);
+ oldside = P_PointOnLineSide(oldx, oldy, ld);
+ if (side != oldside)
+ {
+ if (ld->special)
+ P_CrossSpecialLine(ld - lines, oldside, thing);
+ }
+ }
+
+ return true;
+}
+
+/*
+==================
+=
+= P_ThingHeightClip
+=
+= Takes a valid thing and adjusts the thing->floorz, thing->ceilingz,
+= anf 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?
+
+ thing->floorz = tmfloorz;
+ thing->ceilingz = tmceilingz;
+
+ if (onfloor)
+ // walking monsters rise and fall with the floor
+ thing->z = thing->floorz;
+ else
+ { // don't adjust a floating monster unless forced to
+ if (thing->z + thing->height > thing->ceilingz)
+ thing->z = thing->ceilingz - thing->height;
+ }
+
+ if (thing->ceilingz - thing->floorz < thing->height)
+ return false;
+
+ return true;
+}
+
+
+/*
+==============================================================================
+
+ SLIDE MOVE
+
+Allows the player to slide along any angled walls
+
+==============================================================================
+*/
+
+fixed_t bestslidefrac, secondslidefrac;
+line_t *bestslideline, *secondslideline;
+mobj_t *slidemo;
+
+fixed_t tmxmove, tmymove;
+
+/*
+==================
+=
+= P_HitSlideLine
+=
+= Adjusts the xmove / ymove so that the next move will slide along the wall
+==================
+*/
+
+void P_HitSlideLine(line_t * ld)
+{
+ int side;
+ angle_t lineangle, moveangle, deltaangle;
+ fixed_t movelen, newlen;
+
+
+ if (ld->slopetype == ST_HORIZONTAL)
+ {
+ tmymove = 0;
+ return;
+ }
+ if (ld->slopetype == ST_VERTICAL)
+ {
+ tmxmove = 0;
+ return;
+ }
+
+ 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);
+ deltaangle = moveangle - lineangle;
+ if (deltaangle > ANG180)
+ deltaangle += ANG180;
+// I_Error ("SlideLine: ang>ANG180");
+
+ lineangle >>= ANGLETOFINESHIFT;
+ deltaangle >>= ANGLETOFINESHIFT;
+
+ movelen = P_AproxDistance(tmxmove, tmymove);
+ newlen = FixedMul(movelen, finecosine[deltaangle]);
+ tmxmove = FixedMul(newlen, finecosine[lineangle]);
+ tmymove = FixedMul(newlen, finesine[lineangle]);
+}
+
+/*
+==============
+=
+= 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;
+ }
+
+ P_LineOpening(li); // set openrange, opentop, openbottom
+ 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
+
+ return true; // this line doesn't block movement
+
+// the line does block movement, see if it is closer than best so far
+ isblocking:
+ if (in->frac < bestslidefrac)
+ {
+ secondslidefrac = bestslidefrac;
+ secondslideline = bestslideline;
+ 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.
+==================
+*/
+
+void P_SlideMove(mobj_t * mo)
+{
+ fixed_t leadx, leady;
+ fixed_t trailx, traily;
+ fixed_t newx, newy;
+ int hitcount;
+
+ slidemo = mo;
+ hitcount = 0;
+ retry:
+ if (++hitcount == 3)
+ 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 most have hit the middle, so stairstep
+ stairstep:
+ if (!P_TryMove(mo, mo->x, mo->y + mo->momy))
+ P_TryMove(mo, mo->x + mo->momx, mo->y);
+ return;
+ }
+
+ bestslidefrac -= 0x800; // fudge a bit to make sure it doesn't hit
+ if (bestslidefrac > 0)
+ {
+ newx = FixedMul(mo->momx, bestslidefrac);
+ newy = FixedMul(mo->momy, bestslidefrac);
+ if (!P_TryMove(mo, mo->x + newx, mo->y + newy))
+ goto stairstep;
+ }
+
+//
+// now continue along the wall
+//
+ bestslidefrac = FRACUNIT - (bestslidefrac + 0x800); // remainder
+ if (bestslidefrac > FRACUNIT)
+ bestslidefrac = FRACUNIT;
+ if (bestslidefrac <= 0)
+ return;
+
+ tmxmove = FixedMul(mo->momx, bestslidefrac);
+ tmymove = FixedMul(mo->momy, bestslidefrac);
+
+ P_HitSlideLine(bestslideline); // clip the moves
+
+ mo->momx = tmxmove;
+ mo->momy = tmymove;
+
+ if (!P_TryMove(mo, mo->x + tmxmove, mo->y + tmymove))
+ {
+ goto retry;
+ }
+}
+
+
+
+/*
+==============================================================================
+
+ P_LineAttack
+
+==============================================================================
+*/
+
+
+mobj_t *linetarget; // who got hit (or NULL)
+mobj_t *shootthing;
+fixed_t shootz; // height if not aiming up or down
+ // ???: use slope for monsters?
+int la_damage;
+fixed_t attackrange;
+
+fixed_t aimslope;
+
+extern fixed_t topslope, bottomslope; // slopes to top and bottom of target
+
+/*
+===============================================================================
+=
+= 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, thingtopslope, 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
+ if (th->type == MT_POD)
+ { // Can't auto-aim at pods
+ 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, y, z;
+ fixed_t frac;
+ line_t *li;
+ mobj_t *th;
+ fixed_t slope;
+ fixed_t dist;
+ fixed_t thingtopslope, thingbottomslope;
+ mobj_t *mo;
+
+ if (in->isaline)
+ {
+ li = in->d.line;
+ if (li->special)
+ P_ShootSpecialLine(shootthing, li);
+ if (!(li->flags & ML_TWOSIDED))
+ goto hitline;
+
+//
+// crosses a two sided line
+//
+ P_LineOpening(li);
+
+ dist = FixedMul(attackrange, in->frac);
+
+ if (li->frontsector->floorheight != li->backsector->floorheight)
+ {
+ slope = FixedDiv(openbottom - shootz, dist);
+ if (slope > aimslope)
+ goto hitline;
+ }
+
+ if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
+ {
+ slope = FixedDiv(opentop - shootz, dist);
+ if (slope < aimslope)
+ goto hitline;
+ }
+
+ return true; // shot continues
+//
+// hit line
+//
+ hitline:
+ // 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)
+ {
+ if (z > li->frontsector->ceilingheight)
+ return false; // don't shoot the sky!
+ if (li->backsector && li->backsector->ceilingpic == skyflatnum)
+ return false; // it's a sky hack wall
+ }
+
+ P_SpawnPuff(x, y, z);
+ return false; // don't go any farther
+ }
+
+//
+// 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 for physical attacks on a ghost
+//
+ if (th->flags & MF_SHADOW && shootthing->player->readyweapon == wp_staff)
+ {
+ 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 < 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));
+ if (PuffType == MT_BLASTERPUFF1)
+ { // Make blaster big puff
+ mo = P_SpawnMobj(x, y, z, MT_BLASTERPUFF2);
+ S_StartSound(mo, sfx_blshit);
+ }
+ else
+ {
+ P_SpawnPuff(x, y, z);
+ }
+ if (la_damage)
+ {
+ if (!(in->d.thing->flags & MF_NOBLOOD) && P_Random() < 192)
+ {
+ P_BloodSplatter(x, y, z, in->d.thing);
+ }
+ P_DamageMobj(th, shootthing, shootthing, la_damage);
+ }
+ return (false); // don't go any farther
+}
+
+/*
+=================
+=
+= P_AimLineAttack
+=
+=================
+*/
+
+fixed_t P_AimLineAttack(mobj_t * t1, angle_t angle, fixed_t distance)
+{
+ fixed_t x2, 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;
+ topslope = 100 * FRACUNIT / 160; // can't shoot outside view angles
+ bottomslope = -100 * FRACUNIT / 160;
+ attackrange = distance;
+ linetarget = NULL;
+
+ 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, 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;
+ if (t1->flags2 & MF2_FEETARECLIPPED)
+ {
+ shootz -= FOOTCLIPSIZE;
+ }
+ 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)
+{
+ if (!in->d.line->special)
+ {
+ P_LineOpening(in->d.line);
+ if (openrange <= 0)
+ {
+ //S_StartSound (usething, sfx_noway);
+ return false; // can't use through a wall
+ }
+ return true; // not a special line, but keep checking
+ }
+
+ if (P_PointOnLineSide(usething->x, usething->y, in->d.line) == 1)
+ return false; // don't use back sides
+
+ P_UseSpecialLine(usething, in->d.line);
+
+ return false; // can't use for than one special line in a row
+}
+
+
+/*
+================
+=
+= P_UseLines
+=
+= Looks for special lines in front of the player to activate
+================
+*/
+
+void P_UseLines(player_t * player)
+{
+ int angle;
+ fixed_t x1, y1, x2, 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];
+
+ P_PathTraverse(x1, y1, x2, y2, PT_ADDLINES, PTR_UseTraverse);
+}
+
+
+
+/*
+==============================================================================
+
+ RADIUS ATTACK
+
+==============================================================================
+*/
+
+mobj_t *bombsource;
+mobj_t *bombspot;
+int bombdamage;
+
+/*
+=================
+=
+= PIT_RadiusAttack
+=
+= Source is the creature that casued the explosion at spot
+=================
+*/
+
+boolean PIT_RadiusAttack(mobj_t * thing)
+{
+ fixed_t dx, dy, dist;
+
+ if (!(thing->flags & MF_SHOOTABLE))
+ {
+ return true;
+ }
+ if (thing->type == MT_MINOTAUR || thing->type == MT_SORCERER1
+ || thing->type == MT_SORCERER2)
+ { // Episode 2 and 3 bosses take no damage from PIT_RadiusAttack
+ return (true);
+ }
+ dx = abs(thing->x - bombspot->x);
+ dy = abs(thing->y - bombspot->y);
+ dist = dx > dy ? dx : dy;
+ dist = (dist - thing->radius) >> FRACBITS;
+ if (dist < 0)
+ {
+ dist = 0;
+ }
+ if (dist >= bombdamage)
+ { // Out of range
+ return true;
+ }
+ if (P_CheckSight(thing, bombspot))
+ { // OK to damage, target is in direct path
+ P_DamageMobj(thing, bombspot, bombsource, bombdamage - dist);
+ }
+ return (true);
+}
+
+/*
+=================
+=
+= P_RadiusAttack
+=
+= Source is the creature that casued the explosion at spot
+=================
+*/
+
+void P_RadiusAttack(mobj_t * spot, mobj_t * source, int damage)
+{
+ int x, y, xl, xh, yl, yh;
+ fixed_t dist;
+
+ dist = (damage + MAXRADIUS) << FRACBITS;
+ yh = (spot->y + dist - bmaporgy) >> MAPBLOCKSHIFT;
+ yl = (spot->y - dist - bmaporgy) >> MAPBLOCKSHIFT;
+ xh = (spot->x + dist - bmaporgx) >> MAPBLOCKSHIFT;
+ xl = (spot->x - dist - bmaporgx) >> MAPBLOCKSHIFT;
+ bombspot = spot;
+ if (spot->type == MT_POD && spot->target)
+ {
+ bombsource = spot->target;
+ }
+ else
+ {
+ 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
+==============================================================================
+*/
+
+boolean crushchange;
+boolean 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->height = 0;
+ thing->radius = 0;
+ return true; // keep checking
+ }
+
+ // crunch dropped items
+ if (thing->flags & MF_DROPPED)
+ {
+ P_RemoveMobj(thing);
+ return true; // keep checking
+ }
+
+ if (!(thing->flags & MF_SHOOTABLE))
+ return true; // assume it is bloody gibs or something
+
+ nofit = true;
+ if (crushchange && !(leveltime & 3))
+ {
+ 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);
+ mo->momx = (P_Random() - P_Random()) << 12;
+ mo->momy = (P_Random() - P_Random()) << 12;
+ }
+
+ return true; // keep checking (crush other things)
+}
+
+/*
+===============
+=
+= P_ChangeSector
+=
+===============
+*/
+
+boolean P_ChangeSector(sector_t * sector, boolean crunch)
+{
+ int x, y;
+
+ nofit = false;
+ crushchange = crunch;
+
+// recheck 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;
+}
diff --git a/src/heretic/p_maputl.c b/src/heretic/p_maputl.c
new file mode 100644
index 00000000..3d848af2
--- /dev/null
+++ b/src/heretic/p_maputl.c
@@ -0,0 +1,784 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_maputl.c
+
+#include <stdlib.h>
+
+#include "doomdef.h"
+#include "m_bbox.h"
+#include "p_local.h"
+
+
+/*
+===================
+=
+= P_AproxDistance
+=
+= Gives an estimation of distance (not exact)
+=
+===================
+*/
+
+fixed_t P_AproxDistance(fixed_t dx, fixed_t dy)
+{
+ dx = abs(dx);
+ dy = abs(dy);
+ if (dx < dy)
+ return dx + dy - (dx >> 1);
+ return dx + dy - (dy >> 1);
+}
+
+
+/*
+==================
+=
+= P_PointOnLineSide
+=
+= Returns 0 or 1
+==================
+*/
+
+int P_PointOnLineSide(fixed_t x, fixed_t y, line_t * line)
+{
+ fixed_t dx, dy;
+ fixed_t left, right;
+
+ if (!line->dx)
+ {
+ if (x <= line->v1->x)
+ return line->dy > 0;
+ return line->dy < 0;
+ }
+ if (!line->dy)
+ {
+ if (y <= line->v1->y)
+ return line->dx < 0;
+ return line->dx > 0;
+ }
+
+ dx = (x - line->v1->x);
+ dy = (y - line->v1->y);
+
+ left = FixedMul(line->dy >> FRACBITS, dx);
+ right = FixedMul(dy, line->dx >> FRACBITS);
+
+ if (right < left)
+ return 0; // front side
+ return 1; // back side
+}
+
+
+/*
+=================
+=
+= P_BoxOnLineSide
+=
+= Considers the line to be infinite
+= Returns side 0 or 1, -1 if box crosses the line
+=================
+*/
+
+int P_BoxOnLineSide(fixed_t * tmbox, line_t * ld)
+{
+ int p1 = 0, p2 = 0;
+
+ switch (ld->slopetype)
+ {
+ case ST_HORIZONTAL:
+ p1 = tmbox[BOXTOP] > ld->v1->y;
+ p2 = tmbox[BOXBOTTOM] > ld->v1->y;
+ if (ld->dx < 0)
+ {
+ p1 ^= 1;
+ p2 ^= 1;
+ }
+ break;
+ case ST_VERTICAL:
+ p1 = tmbox[BOXRIGHT] < ld->v1->x;
+ p2 = tmbox[BOXLEFT] < ld->v1->x;
+ if (ld->dy < 0)
+ {
+ p1 ^= 1;
+ p2 ^= 1;
+ }
+ break;
+ case ST_POSITIVE:
+ p1 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXTOP], ld);
+ p2 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld);
+ break;
+ case ST_NEGATIVE:
+ p1 = P_PointOnLineSide(tmbox[BOXRIGHT], tmbox[BOXTOP], ld);
+ p2 = P_PointOnLineSide(tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld);
+ break;
+ }
+
+ if (p1 == p2)
+ return p1;
+ return -1;
+}
+
+/*
+==================
+=
+= P_PointOnDivlineSide
+=
+= Returns 0 or 1
+==================
+*/
+
+int P_PointOnDivlineSide(fixed_t x, fixed_t y, divline_t * line)
+{
+ fixed_t dx, dy;
+ fixed_t left, right;
+
+ if (!line->dx)
+ {
+ if (x <= line->x)
+ return line->dy > 0;
+ return line->dy < 0;
+ }
+ if (!line->dy)
+ {
+ if (y <= line->y)
+ return line->dx < 0;
+ return line->dx > 0;
+ }
+
+ dx = (x - line->x);
+ dy = (y - line->y);
+
+// try to quickly decide by looking at sign bits
+ if ((line->dy ^ line->dx ^ dx ^ dy) & 0x80000000)
+ {
+ if ((line->dy ^ dx) & 0x80000000)
+ return 1; // (left is negative)
+ return 0;
+ }
+
+ left = FixedMul(line->dy >> 8, dx >> 8);
+ right = FixedMul(dy >> 8, line->dx >> 8);
+
+ if (right < left)
+ return 0; // front side
+ return 1; // back side
+}
+
+
+
+/*
+==============
+=
+= P_MakeDivline
+=
+==============
+*/
+
+void P_MakeDivline(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
+===============
+*/
+
+fixed_t P_InterceptVector(divline_t * v2, divline_t * v1)
+{
+#if 1
+ fixed_t frac, num, den;
+
+ den = FixedMul(v1->dy >> 8, v2->dx) - FixedMul(v1->dx >> 8, v2->dy);
+ if (den == 0)
+ return 0;
+// I_Error ("P_InterceptVector: parallel");
+ num = FixedMul((v1->x - v2->x) >> 8, v1->dy) +
+ FixedMul((v2->y - v1->y) >> 8, v1->dx);
+ frac = FixedDiv(num, den);
+
+ return frac;
+#else
+ float frac, num, den, v1x, v1y, v1dx, v1dy, v2x, v2y, v2dx, v2dy;
+
+ v1x = (float) v1->x / FRACUNIT;
+ v1y = (float) v1->y / FRACUNIT;
+ v1dx = (float) v1->dx / FRACUNIT;
+ v1dy = (float) v1->dy / FRACUNIT;
+ v2x = (float) v2->x / FRACUNIT;
+ v2y = (float) v2->y / FRACUNIT;
+ v2dx = (float) v2->dx / FRACUNIT;
+ v2dy = (float) v2->dy / FRACUNIT;
+
+ den = v1dy * v2dx - v1dx * v2dy;
+ if (den == 0)
+ return 0; // parallel
+ num = (v1x - v2x) * v1dy + (v2y - v1y) * v1dx;
+ frac = num / den;
+
+ return frac * FRACUNIT;
+#endif
+}
+
+/*
+==================
+=
+= P_LineOpening
+=
+= Sets opentop and openbottom to the window through a two sided line
+= OPTIMIZE: keep this precalculated
+==================
+*/
+
+fixed_t opentop, openbottom, openrange;
+fixed_t lowfloor;
+
+void P_LineOpening(line_t * linedef)
+{
+ sector_t *front, *back;
+
+ if (linedef->sidenum[1] == -1)
+ { // single sided line
+ openrange = 0;
+ return;
+ }
+
+ front = linedef->frontsector;
+ back = linedef->backsector;
+
+ if (front->ceilingheight < back->ceilingheight)
+ opentop = front->ceilingheight;
+ else
+ opentop = back->ceilingheight;
+ if (front->floorheight > back->floorheight)
+ {
+ openbottom = front->floorheight;
+ lowfloor = back->floorheight;
+ }
+ else
+ {
+ openbottom = back->floorheight;
+ lowfloor = front->floorheight;
+ }
+
+ openrange = opentop - openbottom;
+}
+
+/*
+===============================================================================
+
+ THING POSITION SETTING
+
+===============================================================================
+*/
+
+/*
+===================
+=
+= P_UnsetThingPosition
+=
+= Unlinks a thing from block map and sectors
+=
+===================
+*/
+
+void P_UnsetThingPosition(mobj_t * thing)
+{
+ int blockx, blocky;
+
+ if (!(thing->flags & MF_NOSECTOR))
+ { // inert things don't need to be in blockmap
+// unlink from subsector
+ if (thing->snext)
+ thing->snext->sprev = thing->sprev;
+ if (thing->sprev)
+ thing->sprev->snext = thing->snext;
+ else
+ thing->subsector->sector->thinglist = thing->snext;
+ }
+
+ if (!(thing->flags & MF_NOBLOCKMAP))
+ { // inert things don't need to be in blockmap
+// unlink from block map
+ if (thing->bnext)
+ thing->bnext->bprev = thing->bprev;
+ if (thing->bprev)
+ thing->bprev->bnext = thing->bnext;
+ else
+ {
+ blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT;
+ blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT;
+ if (blockx >= 0 && blockx < bmapwidth
+ && blocky >= 0 && blocky < bmapheight)
+ blocklinks[blocky * bmapwidth + blockx] = thing->bnext;
+ }
+ }
+}
+
+
+/*
+===================
+=
+= P_SetThingPosition
+=
+= Links a thing into both a block and a subsector based on it's x y
+= Sets thing->subsector properly
+=
+===================
+*/
+
+void P_SetThingPosition(mobj_t * thing)
+{
+ subsector_t *ss;
+ sector_t *sec;
+ int blockx, blocky;
+ mobj_t **link;
+
+//
+// link into subsector
+//
+ ss = R_PointInSubsector(thing->x, thing->y);
+ thing->subsector = ss;
+ if (!(thing->flags & MF_NOSECTOR))
+ { // invisible things don't go into the sector links
+ sec = ss->sector;
+
+ thing->sprev = NULL;
+ thing->snext = sec->thinglist;
+ if (sec->thinglist)
+ sec->thinglist->sprev = thing;
+ sec->thinglist = thing;
+ }
+
+//
+// link into blockmap
+//
+ if (!(thing->flags & MF_NOBLOCKMAP))
+ { // inert things don't need to be in blockmap
+ blockx = (thing->x - bmaporgx) >> MAPBLOCKSHIFT;
+ blocky = (thing->y - bmaporgy) >> MAPBLOCKSHIFT;
+ if (blockx >= 0 && blockx < bmapwidth && blocky >= 0
+ && blocky < bmapheight)
+ {
+ link = &blocklinks[blocky * bmapwidth + blockx];
+ thing->bprev = NULL;
+ thing->bnext = *link;
+ if (*link)
+ (*link)->bprev = thing;
+ *link = thing;
+ }
+ else
+ { // thing is off the map
+ thing->bnext = thing->bprev = NULL;
+ }
+ }
+}
+
+
+
+/*
+===============================================================================
+
+ BLOCK MAP ITERATORS
+
+For each line/thing in the given mapblock, call the passed 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
+===================
+*/
+
+boolean P_BlockLinesIterator(int x, int y, boolean(*func) (line_t *))
+{
+ int offset;
+ short *list;
+ line_t *ld;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return true;
+ offset = y * bmapwidth + x;
+
+ offset = *(blockmap + offset);
+
+ for (list = blockmaplump + offset; *list != -1; list++)
+ {
+ 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
+=
+==================
+*/
+
+boolean P_BlockThingsIterator(int x, int y, boolean(*func) (mobj_t *))
+{
+ mobj_t *mobj;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return true;
+
+ for (mobj = blocklinks[y * bmapwidth + x]; mobj; mobj = mobj->bnext)
+ if (!func(mobj))
+ return false;
+
+ return true;
+}
+
+/*
+===============================================================================
+
+ INTERCEPT ROUTINES
+
+===============================================================================
+*/
+
+intercept_t intercepts[MAXINTERCEPTS], *intercept_p;
+
+divline_t trace;
+boolean earlyout;
+int ptflags;
+
+/*
+==================
+=
+= 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
+= Returns true if earlyout and a solid line hit
+==================
+*/
+
+boolean PIT_AddLineIntercepts(line_t * ld)
+{
+ int s1, 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
+
+// try to early out the check
+ if (earlyout && frac < FRACUNIT && !ld->backsector)
+ return false; // stop checking
+
+ intercept_p->frac = frac;
+ intercept_p->isaline = true;
+ intercept_p->d.line = ld;
+ intercept_p++;
+
+ return true; // continue
+}
+
+
+
+/*
+==================
+=
+= PIT_AddThingIntercepts
+=
+==================
+*/
+
+boolean PIT_AddThingIntercepts(mobj_t * thing)
+{
+ fixed_t x1, y1, x2, y2;
+ int s1, s2;
+ boolean tracepositive;
+ divline_t dl;
+ fixed_t frac;
+
+ tracepositive = (trace.dx ^ trace.dy) > 0;
+
+ // check a corner to corner crossection for hit
+
+ if (tracepositive)
+ {
+ 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
+ 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
+====================
+*/
+
+boolean P_TraverseIntercepts(traverser_t func, fixed_t maxfrac)
+{
+ int count;
+ fixed_t dist;
+ intercept_t *scan, *in;
+
+ count = intercept_p - intercepts;
+ in = 0; // shut up compiler warning
+
+ while (count--)
+ {
+ dist = INT_MAX;
+ for (scan = intercepts; scan < intercept_p; scan++)
+ if (scan->frac < dist)
+ {
+ dist = scan->frac;
+ in = scan;
+ }
+
+ if (dist > maxfrac)
+ return true; // checked everything in range
+#if 0
+ { // don't check these yet, ther may be others inserted
+ in = scan = intercepts;
+ for (scan = intercepts; scan < intercept_p; scan++)
+ if (scan->frac > maxfrac)
+ *in++ = *scan;
+ intercept_p = in;
+ return false;
+ }
+#endif
+
+ 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
+==================
+*/
+
+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, xt2, yt2;
+ fixed_t xstep, ystep;
+ fixed_t partial;
+ fixed_t xintercept, yintercept;
+ int mapx, mapy, mapxstep, mapystep;
+ int count;
+
+ earlyout = flags & PT_EARLYOUT;
+
+ validcount++;
+ intercept_p = intercepts;
+
+ if (((x1 - bmaporgx) & (MAPBLOCKSIZE - 1)) == 0)
+ x1 += FRACUNIT; // don't side exactly on a line
+ if (((y1 - bmaporgy) & (MAPBLOCKSIZE - 1)) == 0)
+ 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, abs(x2 - x1));
+ }
+ else if (xt2 < xt1)
+ {
+ mapxstep = -1;
+ partial = (x1 >> MAPBTOFRAC) & (FRACUNIT - 1);
+ ystep = FixedDiv(y2 - y1, 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, abs(y2 - y1));
+ }
+ else if (yt2 < yt1)
+ {
+ mapystep = -1;
+ partial = (y1 >> MAPBTOFRAC) & (FRACUNIT - 1);
+ xstep = FixedDiv(x2 - x1, 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/heretic/p_mobj.c b/src/heretic/p_mobj.c
new file mode 100644
index 00000000..e07ecf45
--- /dev/null
+++ b/src/heretic/p_mobj.c
@@ -0,0 +1,1633 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_mobj.c
+
+#include "doomdef.h"
+#include "i_system.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "sounds.h"
+#include "s_sound.h"
+
+void G_PlayerReborn(int player);
+void P_SpawnMapThing(mapthing_t * mthing);
+
+mobjtype_t PuffType;
+mobj_t *MissileMobj;
+
+static fixed_t FloatBobOffsets[64] = {
+ 0, 51389, 102283, 152192,
+ 200636, 247147, 291278, 332604,
+ 370727, 405280, 435929, 462380,
+ 484378, 501712, 514213, 521763,
+ 524287, 521763, 514213, 501712,
+ 484378, 462380, 435929, 405280,
+ 370727, 332604, 291278, 247147,
+ 200636, 152192, 102283, 51389,
+ -1, -51390, -102284, -152193,
+ -200637, -247148, -291279, -332605,
+ -370728, -405281, -435930, -462381,
+ -484380, -501713, -514215, -521764,
+ -524288, -521764, -514214, -501713,
+ -484379, -462381, -435930, -405280,
+ -370728, -332605, -291279, -247148,
+ -200637, -152193, -102284, -51389
+};
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_SetMobjState
+//
+// Returns true if the mobj is still present.
+//
+//----------------------------------------------------------------------------
+
+boolean P_SetMobjState(mobj_t * mobj, statenum_t state)
+{
+ state_t *st;
+
+ if (state == S_NULL)
+ { // Remove mobj
+ mobj->state = (state_t *) S_NULL;
+ P_RemoveMobj(mobj);
+ return (false);
+ }
+ st = &states[state];
+ mobj->state = st;
+ mobj->tics = st->tics;
+ mobj->sprite = st->sprite;
+ mobj->frame = st->frame;
+ if (st->action)
+ { // Call action function
+ st->action(mobj);
+ }
+ return (true);
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_SetMobjStateNF
+//
+// Same as P_SetMobjState, but does not call the state function.
+//
+//----------------------------------------------------------------------------
+
+boolean P_SetMobjStateNF(mobj_t * mobj, statenum_t state)
+{
+ state_t *st;
+
+ if (state == S_NULL)
+ { // Remove mobj
+ mobj->state = (state_t *) S_NULL;
+ P_RemoveMobj(mobj);
+ return (false);
+ }
+ st = &states[state];
+ mobj->state = st;
+ mobj->tics = st->tics;
+ mobj->sprite = st->sprite;
+ mobj->frame = st->frame;
+ return (true);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_ExplodeMissile
+//
+//----------------------------------------------------------------------------
+
+void P_ExplodeMissile(mobj_t * mo)
+{
+ if (mo->type == MT_WHIRLWIND)
+ {
+ if (++mo->special2 < 60)
+ {
+ return;
+ }
+ }
+ mo->momx = mo->momy = mo->momz = 0;
+ P_SetMobjState(mo, mobjinfo[mo->type].deathstate);
+ //mo->tics -= P_Random()&3;
+ mo->flags &= ~MF_MISSILE;
+ if (mo->info->deathsound)
+ {
+ S_StartSound(mo, mo->info->deathsound);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_FloorBounceMissile
+//
+//----------------------------------------------------------------------------
+
+void P_FloorBounceMissile(mobj_t * mo)
+{
+ mo->momz = -mo->momz;
+ P_SetMobjState(mo, mobjinfo[mo->type].deathstate);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_ThrustMobj
+//
+//----------------------------------------------------------------------------
+
+void P_ThrustMobj(mobj_t * mo, angle_t angle, fixed_t move)
+{
+ angle >>= ANGLETOFINESHIFT;
+ mo->momx += FixedMul(move, finecosine[angle]);
+ mo->momy += FixedMul(move, finesine[angle]);
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_FaceMobj
+//
+// Returns 1 if 'source' needs to turn clockwise, or 0 if 'source' needs
+// to turn counter clockwise. 'delta' is set to the amount 'source'
+// needs to turn.
+//
+//----------------------------------------------------------------------------
+
+int P_FaceMobj(mobj_t * source, mobj_t * target, angle_t * delta)
+{
+ angle_t diff;
+ angle_t angle1;
+ angle_t angle2;
+
+ angle1 = source->angle;
+ angle2 = R_PointToAngle2(source->x, source->y, target->x, target->y);
+ if (angle2 > angle1)
+ {
+ diff = angle2 - angle1;
+ if (diff > ANG180)
+ {
+ *delta = ANG_MAX - diff;
+ return (0);
+ }
+ else
+ {
+ *delta = diff;
+ return (1);
+ }
+ }
+ else
+ {
+ diff = angle1 - angle2;
+ if (diff > ANG180)
+ {
+ *delta = ANG_MAX - diff;
+ return (1);
+ }
+ else
+ {
+ *delta = diff;
+ return (0);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_SeekerMissile
+//
+// The missile special1 field must be mobj_t *target. Returns true if
+// target was tracked, false if not.
+//
+//----------------------------------------------------------------------------
+
+boolean P_SeekerMissile(mobj_t * actor, angle_t thresh, angle_t turnMax)
+{
+ int dir;
+ int dist;
+ angle_t delta;
+ angle_t angle;
+ mobj_t *target;
+
+ target = (mobj_t *) actor->special1;
+ if (target == NULL)
+ {
+ return (false);
+ }
+ if (!(target->flags & MF_SHOOTABLE))
+ { // Target died
+ actor->special1 = 0;
+ return (false);
+ }
+ dir = P_FaceMobj(actor, target, &delta);
+ if (delta > thresh)
+ {
+ delta >>= 1;
+ if (delta > turnMax)
+ {
+ delta = turnMax;
+ }
+ }
+ if (dir)
+ { // Turn clockwise
+ actor->angle += delta;
+ }
+ else
+ { // Turn counter clockwise
+ actor->angle -= delta;
+ }
+ angle = actor->angle >> ANGLETOFINESHIFT;
+ actor->momx = FixedMul(actor->info->speed, finecosine[angle]);
+ actor->momy = FixedMul(actor->info->speed, finesine[angle]);
+ if (actor->z + actor->height < target->z ||
+ target->z + target->height < actor->z)
+ { // Need to seek vertically
+ dist = P_AproxDistance(target->x - actor->x, target->y - actor->y);
+ dist = dist / actor->info->speed;
+ if (dist < 1)
+ {
+ dist = 1;
+ }
+ actor->momz = (target->z - actor->z) / dist;
+ }
+ return (true);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_XYMovement
+//
+//----------------------------------------------------------------------------
+
+#define STOPSPEED 0x1000
+#define FRICTION_NORMAL 0xe800
+#define FRICTION_LOW 0xf900
+#define FRICTION_FLY 0xeb00
+
+void P_XYMovement(mobj_t * mo)
+{
+ fixed_t ptryx, ptryy;
+ player_t *player;
+ fixed_t xmove, ymove;
+ int special;
+ static int windTab[3] = { 2048 * 5, 2048 * 10, 2048 * 25 };
+
+ if (!mo->momx && !mo->momy)
+ {
+ if (mo->flags & MF_SKULLFLY)
+ { // A flying mobj slammed into something
+ mo->flags &= ~MF_SKULLFLY;
+ mo->momx = mo->momy = mo->momz = 0;
+ P_SetMobjState(mo, mo->info->seestate);
+ }
+ return;
+ }
+ special = mo->subsector->sector->special;
+ if (mo->flags2 & MF2_WINDTHRUST)
+ {
+ switch (special)
+ {
+ case 40:
+ case 41:
+ case 42: // Wind_East
+ P_ThrustMobj(mo, 0, windTab[special - 40]);
+ break;
+ case 43:
+ case 44:
+ case 45: // Wind_North
+ P_ThrustMobj(mo, ANG90, windTab[special - 43]);
+ break;
+ case 46:
+ case 47:
+ case 48: // Wind_South
+ P_ThrustMobj(mo, ANG270, windTab[special - 46]);
+ break;
+ case 49:
+ case 50:
+ case 51: // Wind_West
+ P_ThrustMobj(mo, ANG180, windTab[special - 49]);
+ break;
+ }
+ }
+ 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;
+ do
+ {
+ if (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;
+ }
+ if (!P_TryMove(mo, ptryx, ptryy))
+ { // Blocked move
+ if (mo->flags2 & MF2_SLIDE)
+ { // 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)
+ { // Hack to prevent missiles exploding against the sky
+ if (mo->type == MT_BLOODYSKULL)
+ {
+ mo->momx = mo->momy = 0;
+ mo->momz = -FRACUNIT;
+ }
+ else
+ {
+ P_RemoveMobj(mo);
+ }
+ return;
+ }
+ P_ExplodeMissile(mo);
+ }
+ //else if(mo->info->crashstate)
+ //{
+ // mo->momx = mo->momy = 0;
+ // P_SetMobjState(mo, mo->info->crashstate);
+ // return;
+ //}
+ else
+ {
+ mo->momx = mo->momy = 0;
+ }
+ }
+ }
+ while (xmove || ymove);
+
+ // Friction
+
+ if (player && player->cheats & CF_NOMOMENTUM)
+ { // Debug option for no sliding at all
+ mo->momx = mo->momy = 0;
+ return;
+ }
+ if (mo->flags & (MF_MISSILE | MF_SKULLFLY))
+ { // No friction for missiles
+ return;
+ }
+ if (mo->z > mo->floorz && !(mo->flags2 & MF2_FLY)
+ && !(mo->flags2 & MF2_ONMOBJ))
+ { // No friction when falling
+ return;
+ }
+ if (mo->flags & MF_CORPSE)
+ { // Don't stop sliding if halfway off a step with some momentum
+ if (mo->momx > FRACUNIT / 4 || mo->momx < -FRACUNIT / 4
+ || mo->momy > FRACUNIT / 4 || mo->momy < -FRACUNIT / 4)
+ {
+ if (mo->floorz != mo->subsector->sector->floorheight)
+ {
+ return;
+ }
+ }
+ }
+ if (mo->momx > -STOPSPEED && mo->momx < STOPSPEED
+ && mo->momy > -STOPSPEED && mo->momy < STOPSPEED
+ && (!player || (player->cmd.forwardmove == 0
+ && player->cmd.sidemove == 0)))
+ { // If in a walking frame, stop moving
+ if (player)
+ {
+ if (player->chickenTics)
+ {
+ if ((unsigned) ((player->mo->state - states)
+ - S_CHICPLAY_RUN1) < 4)
+ {
+ P_SetMobjState(player->mo, S_CHICPLAY);
+ }
+ }
+ else
+ {
+ if ((unsigned) ((player->mo->state - states)
+ - S_PLAY_RUN1) < 4)
+ {
+ P_SetMobjState(player->mo, S_PLAY);
+ }
+ }
+ }
+ mo->momx = 0;
+ mo->momy = 0;
+ }
+ else
+ {
+ if (mo->flags2 & MF2_FLY && !(mo->z <= mo->floorz)
+ && !(mo->flags2 & MF2_ONMOBJ))
+ {
+ mo->momx = FixedMul(mo->momx, FRICTION_FLY);
+ mo->momy = FixedMul(mo->momy, FRICTION_FLY);
+ }
+ else if (special == 15) // Friction_Low
+ {
+ mo->momx = FixedMul(mo->momx, FRICTION_LOW);
+ mo->momy = FixedMul(mo->momy, FRICTION_LOW);
+ }
+ else
+ {
+ mo->momx = FixedMul(mo->momx, FRICTION_NORMAL);
+ mo->momy = FixedMul(mo->momy, FRICTION_NORMAL);
+ }
+ }
+}
+
+
+/*
+===============
+=
+= P_ZMovement
+=
+===============
+*/
+
+void P_ZMovement(mobj_t * mo)
+{
+ int dist;
+ int delta;
+//
+// check for smooth step up
+//
+ if (mo->player && mo->z < mo->floorz)
+ {
+ mo->player->viewheight -= mo->floorz - mo->z;
+ mo->player->deltaviewheight =
+ (VIEWHEIGHT - mo->player->viewheight) >> 3;
+ }
+//
+// adjust height
+//
+ mo->z += mo->momz;
+ if (mo->flags & MF_FLOAT && mo->target)
+ { // float down towards target if too close
+ if (!(mo->flags & MF_SKULLFLY) && !(mo->flags & MF_INFLOAT))
+ {
+ dist =
+ P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y);
+ delta = (mo->target->z + (mo->height >> 1)) - mo->z;
+ if (delta < 0 && dist < -(delta * 3))
+ mo->z -= FLOATSPEED;
+ else if (delta > 0 && dist < (delta * 3))
+ mo->z += FLOATSPEED;
+ }
+ }
+ if (mo->player && mo->flags2 & MF2_FLY && !(mo->z <= mo->floorz)
+ && leveltime & 2)
+ {
+ mo->z += finesine[(FINEANGLES / 20 * leveltime >> 2) & FINEMASK];
+ }
+
+//
+// clip movement
+//
+ if (mo->z <= mo->floorz)
+ { // Hit the floor
+ if (mo->flags & MF_MISSILE)
+ {
+ mo->z = mo->floorz;
+ if (mo->flags2 & MF2_FLOORBOUNCE)
+ {
+ P_FloorBounceMissile(mo);
+ return;
+ }
+ else if (mo->type == MT_MNTRFX2)
+ { // Minotaur floor fire can go up steps
+ return;
+ }
+ else
+ {
+ P_ExplodeMissile(mo);
+ return;
+ }
+ }
+ if (mo->z - mo->momz > mo->floorz)
+ { // Spawn splashes, etc.
+ P_HitFloor(mo);
+ }
+ mo->z = mo->floorz;
+ if (mo->momz < 0)
+ {
+ if (mo->player && mo->momz < -GRAVITY * 8 && !(mo->flags2 & MF2_FLY)) // squat down
+ {
+ mo->player->deltaviewheight = mo->momz >> 3;
+ S_StartSound(mo, sfx_plroof);
+ // haleyjd: removed externdriver crap
+ mo->player->centering = true;
+ }
+ mo->momz = 0;
+ }
+ if (mo->flags & MF_SKULLFLY)
+ { // The skull slammed into something
+ mo->momz = -mo->momz;
+ }
+ if (mo->info->crashstate && (mo->flags & MF_CORPSE))
+ {
+ P_SetMobjState(mo, mo->info->crashstate);
+ return;
+ }
+ }
+ else if (mo->flags2 & MF2_LOGRAV)
+ {
+ if (mo->momz == 0)
+ mo->momz = -(GRAVITY >> 3) * 2;
+ else
+ mo->momz -= GRAVITY >> 3;
+ }
+ else if (!(mo->flags & MF_NOGRAVITY))
+ {
+ if (mo->momz == 0)
+ mo->momz = -GRAVITY * 2;
+ else
+ mo->momz -= GRAVITY;
+ }
+
+ if (mo->z + mo->height > mo->ceilingz)
+ { // hit the ceiling
+ if (mo->momz > 0)
+ mo->momz = 0;
+ mo->z = mo->ceilingz - mo->height;
+ if (mo->flags & MF_SKULLFLY)
+ { // the skull slammed into something
+ mo->momz = -mo->momz;
+ }
+ if (mo->flags & MF_MISSILE)
+ {
+ if (mo->subsector->sector->ceilingpic == skyflatnum)
+ {
+ if (mo->type == MT_BLOODYSKULL)
+ {
+ mo->momx = mo->momy = 0;
+ mo->momz = -FRACUNIT;
+ }
+ else
+ {
+ P_RemoveMobj(mo);
+ }
+ return;
+ }
+ P_ExplodeMissile(mo);
+ return;
+ }
+ }
+}
+
+
+/*
+================
+=
+= P_NightmareRespawn
+=
+================
+*/
+
+void P_NightmareRespawn(mobj_t * mobj)
+{
+ fixed_t x, y, z;
+ subsector_t *ss;
+ mobj_t *mo;
+ mapthing_t *mthing;
+
+ x = mobj->spawnpoint.x << FRACBITS;
+ y = mobj->spawnpoint.y << FRACBITS;
+
+ if (!P_CheckPosition(mobj, x, y))
+ return; // somthing is occupying it's position
+
+
+// spawn a teleport fog at old spot
+
+ mo = P_SpawnMobj(mobj->x, mobj->y,
+ mobj->subsector->sector->floorheight + TELEFOGHEIGHT,
+ MT_TFOG);
+ 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 + TELEFOGHEIGHT, MT_TFOG);
+ S_StartSound(mo, sfx_telept);
+
+// spawn the new monster
+ mthing = &mobj->spawnpoint;
+
+// spawn it
+ if (mobj->info->flags & MF_SPAWNCEILING)
+ z = ONCEILINGZ;
+ else
+ z = ONFLOORZ;
+ 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;
+
+ mo->reactiontime = 18;
+
+// remove the old monster
+ P_RemoveMobj(mobj);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_BlasterMobjThinker
+//
+// Thinker for the ultra-fast blaster PL2 ripper-spawning missile.
+//
+//----------------------------------------------------------------------------
+
+void P_BlasterMobjThinker(mobj_t * mobj)
+{
+ int i;
+ fixed_t xfrac;
+ fixed_t yfrac;
+ fixed_t zfrac;
+ fixed_t z;
+ boolean changexy;
+
+ // Handle movement
+ if (mobj->momx || mobj->momy || (mobj->z != mobj->floorz) || mobj->momz)
+ {
+ xfrac = mobj->momx >> 3;
+ yfrac = mobj->momy >> 3;
+ zfrac = mobj->momz >> 3;
+ changexy = xfrac || yfrac;
+ for (i = 0; i < 8; i++)
+ {
+ if (changexy)
+ {
+ if (!P_TryMove(mobj, mobj->x + xfrac, mobj->y + yfrac))
+ { // Blocked move
+ P_ExplodeMissile(mobj);
+ return;
+ }
+ }
+ mobj->z += zfrac;
+ if (mobj->z <= mobj->floorz)
+ { // Hit the floor
+ mobj->z = mobj->floorz;
+ P_HitFloor(mobj);
+ P_ExplodeMissile(mobj);
+ return;
+ }
+ if (mobj->z + mobj->height > mobj->ceilingz)
+ { // Hit the ceiling
+ mobj->z = mobj->ceilingz - mobj->height;
+ P_ExplodeMissile(mobj);
+ return;
+ }
+ if (changexy && (P_Random() < 64))
+ {
+ z = mobj->z - 8 * FRACUNIT;
+ if (z < mobj->floorz)
+ {
+ z = mobj->floorz;
+ }
+ P_SpawnMobj(mobj->x, mobj->y, z, MT_BLASTERSMOKE);
+ }
+ }
+ }
+ // Advance the state
+ if (mobj->tics != -1)
+ {
+ mobj->tics--;
+ while (!mobj->tics)
+ {
+ if (!P_SetMobjState(mobj, mobj->state->nextstate))
+ { // mobj was removed
+ return;
+ }
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_MobjThinker
+//
+//----------------------------------------------------------------------------
+
+void P_MobjThinker(mobj_t * mobj)
+{
+ mobj_t *onmo;
+
+ // Handle X and Y momentums
+ if (mobj->momx || mobj->momy || (mobj->flags & MF_SKULLFLY))
+ {
+ P_XYMovement(mobj);
+ if (mobj->thinker.function == (think_t) - 1)
+ { // mobj was removed
+ return;
+ }
+ }
+ if (mobj->flags2 & MF2_FLOATBOB)
+ { // Floating item bobbing motion
+ mobj->z = mobj->floorz + FloatBobOffsets[(mobj->health++) & 63];
+ }
+ else if ((mobj->z != mobj->floorz) || mobj->momz)
+ { // Handle Z momentum and gravity
+ if (mobj->flags2 & MF2_PASSMOBJ)
+ {
+ if (!(onmo = P_CheckOnmobj(mobj)))
+ {
+ P_ZMovement(mobj);
+ }
+ else
+ {
+ if (mobj->player && mobj->momz < 0)
+ {
+ mobj->flags2 |= MF2_ONMOBJ;
+ mobj->momz = 0;
+ }
+ if (mobj->player && (onmo->player || onmo->type == MT_POD))
+ {
+ mobj->momx = onmo->momx;
+ mobj->momy = onmo->momy;
+ if (onmo->z < onmo->floorz)
+ {
+ mobj->z += onmo->floorz - onmo->z;
+ if (onmo->player)
+ {
+ onmo->player->viewheight -=
+ onmo->floorz - onmo->z;
+ onmo->player->deltaviewheight =
+ (VIEWHEIGHT - onmo->player->viewheight) >> 3;
+ }
+ onmo->z = onmo->floorz;
+ }
+ }
+ }
+ }
+ else
+ {
+ P_ZMovement(mobj);
+ }
+ if (mobj->thinker.function == (think_t) - 1)
+ { // mobj was removed
+ return;
+ }
+ }
+
+//
+// cycle through states, calling action functions at transitions
+//
+ if (mobj->tics != -1)
+ {
+ mobj->tics--;
+ // you can cycle through multiple states in a tic
+ while (!mobj->tics)
+ {
+ if (!P_SetMobjState(mobj, mobj->state->nextstate))
+ { // mobj was removed
+ return;
+ }
+ }
+ }
+ else
+ { // Check for monster 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() > 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;
+ fixed_t space;
+
+ 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;
+ mobj->flags = info->flags;
+ mobj->flags2 = info->flags2;
+ mobj->damage = info->damage;
+ mobj->health = info->spawnhealth;
+ if (gameskill != sk_nightmare)
+ {
+ mobj->reactiontime = info->reactiontime;
+ }
+ mobj->lastlook = P_Random() % MAXPLAYERS;
+
+ // Set the state, but do not use P_SetMobjState, because action
+ // routines can't be called yet. If the spawnstate has an action
+ // routine, it will not be called.
+ st = &states[info->spawnstate];
+ mobj->state = st;
+ mobj->tics = st->tics;
+ mobj->sprite = st->sprite;
+ mobj->frame = st->frame;
+
+ // Set subsector and/or block links.
+ P_SetThingPosition(mobj);
+ mobj->floorz = mobj->subsector->sector->floorheight;
+ mobj->ceilingz = mobj->subsector->sector->ceilingheight;
+ if (z == ONFLOORZ)
+ {
+ mobj->z = mobj->floorz;
+ }
+ else if (z == ONCEILINGZ)
+ {
+ mobj->z = mobj->ceilingz - mobj->info->height;
+ }
+ else if (z == FLOATRANDZ)
+ {
+ space = ((mobj->ceilingz) - (mobj->info->height)) - mobj->floorz;
+ if (space > 48 * FRACUNIT)
+ {
+ space -= 40 * FRACUNIT;
+ mobj->z =
+ ((space * P_Random()) >> 8) + mobj->floorz + 40 * FRACUNIT;
+ }
+ else
+ {
+ mobj->z = mobj->floorz;
+ }
+ }
+ else
+ {
+ mobj->z = z;
+ }
+ if (mobj->flags2 & MF2_FOOTCLIP
+ && P_GetThingFloorType(mobj) != FLOOR_SOLID
+ && mobj->floorz == mobj->subsector->sector->floorheight)
+ {
+ mobj->flags2 |= MF2_FEETARECLIPPED;
+ }
+ else
+ {
+ mobj->flags2 &= ~MF2_FEETARECLIPPED;
+ }
+
+ mobj->thinker.function = P_MobjThinker;
+ P_AddThinker(&mobj->thinker);
+ return (mobj);
+}
+
+/*
+===============
+=
+= P_RemoveMobj
+=
+===============
+*/
+
+void P_RemoveMobj(mobj_t * mobj)
+{
+// unlink from sector and block lists
+ P_UnsetThingPosition(mobj);
+// stop any playing sound
+ S_StopSound(mobj);
+// free block
+ P_RemoveThinker((thinker_t *) mobj);
+}
+
+//=============================================================================
+
+
+/*
+============
+=
+= P_SpawnPlayer
+=
+= Called when a player is spawned on the level
+= Most of the player structure stays unchanged between levels
+============
+*/
+
+void P_SpawnPlayer(mapthing_t * mthing)
+{
+ player_t *p;
+ fixed_t x, y, z;
+ mobj_t *mobj;
+ int i;
+ extern int playerkeys;
+
+ if (!playeringame[mthing->type - 1])
+ return; // not playing
+
+ p = &players[mthing->type - 1];
+
+ if (p->playerstate == PST_REBORN)
+ G_PlayerReborn(mthing->type - 1);
+
+ x = mthing->x << FRACBITS;
+ y = mthing->y << FRACBITS;
+
+ z = ONFLOORZ;
+ mobj = P_SpawnMobj(x, y, z, MT_PLAYER);
+ if (mthing->type > 1) // set color translations for player sprites
+ mobj->flags |= (mthing->type - 1) << MF_TRANSSHIFT;
+
+ mobj->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->chickenTics = 0;
+ p->rain1 = NULL;
+ p->rain2 = NULL;
+ p->extralight = 0;
+ p->fixedcolormap = 0;
+ p->viewheight = VIEWHEIGHT;
+ P_SetupPsprites(p); // setup gun psprite
+ if (deathmatch)
+ { // Give all keys in death match mode
+ for (i = 0; i < NUMKEYS; i++)
+ {
+ p->keys[i] = true;
+ if (p == &players[consoleplayer])
+ {
+ playerkeys = 7;
+ UpdateState |= I_STATBAR;
+ }
+ }
+ }
+ else if (p == &players[consoleplayer])
+ {
+ playerkeys = 0;
+ UpdateState |= I_STATBAR;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_SpawnMapThing
+//
+// The fields of the mapthing should already be in host byte order.
+//
+//----------------------------------------------------------------------------
+
+void P_SpawnMapThing(mapthing_t * mthing)
+{
+ int i;
+ int bit;
+ mobj_t *mobj;
+ fixed_t x, y, z;
+
+// count deathmatch start positions
+ if (mthing->type == 11)
+ {
+ if (deathmatch_p < &deathmatchstarts[10])
+ {
+ memcpy(deathmatch_p, mthing, sizeof(*mthing));
+ deathmatch_p++;
+ }
+ return;
+ }
+
+// check for players specially
+ if (mthing->type <= 4)
+ {
+ // save spots for respawning in network games
+ playerstarts[mthing->type - 1] = *mthing;
+ if (!deathmatch)
+ {
+ P_SpawnPlayer(mthing);
+ }
+ return;
+ }
+
+ // Ambient sound sequences
+ if (mthing->type >= 1200 && mthing->type < 1300)
+ {
+ P_AddAmbientSfx(mthing->type - 1200);
+ return;
+ }
+
+ // Check for boss spots
+ if (mthing->type == 56) // Monster_BossSpot
+ {
+ P_AddBossSpot(mthing->x << FRACBITS, mthing->y << FRACBITS,
+ ANG45 * (mthing->angle / 45));
+ return;
+ }
+
+// check for apropriate skill level
+ if (!netgame && (mthing->options & 16))
+ return;
+
+ if (gameskill == sk_baby)
+ bit = 1;
+ else if (gameskill == sk_nightmare)
+ bit = 4;
+ else
+ bit = 1 << (gameskill - 1);
+ if (!(mthing->options & bit))
+ return;
+
+// find which type to spawn
+ for (i = 0; i < NUMMOBJTYPES; i++)
+ if (mthing->type == mobjinfo[i].doomednum)
+ break;
+
+ if (i == NUMMOBJTYPES)
+ I_Error("P_SpawnMapThing: Unknown type %i at (%i, %i)", mthing->type,
+ mthing->x, mthing->y);
+
+// don't spawn keys and players in deathmatch
+ if (deathmatch && mobjinfo[i].flags & MF_NOTDMATCH)
+ return;
+
+// don't spawn any monsters if -nomonsters
+ if (nomonsters && (mobjinfo[i].flags & MF_COUNTKILL))
+ return;
+
+// spawn it
+ switch (i)
+ { // Special stuff
+ case MT_WSKULLROD:
+ case MT_WPHOENIXROD:
+ case MT_AMSKRDWIMPY:
+ case MT_AMSKRDHEFTY:
+ case MT_AMPHRDWIMPY:
+ case MT_AMPHRDHEFTY:
+ case MT_AMMACEWIMPY:
+ case MT_AMMACEHEFTY:
+ case MT_ARTISUPERHEAL:
+ case MT_ARTITELEPORT:
+ case MT_ITEMSHIELD2:
+ if (gamemode == shareware)
+ { // Don't place on map in shareware version
+ return;
+ }
+ break;
+ case MT_WMACE:
+ if (gamemode != shareware)
+ { // Put in the mace spot list
+ P_AddMaceSpot(mthing);
+ return;
+ }
+ return;
+ default:
+ break;
+ }
+ x = mthing->x << FRACBITS;
+ y = mthing->y << FRACBITS;
+ if (mobjinfo[i].flags & MF_SPAWNCEILING)
+ {
+ z = ONCEILINGZ;
+ }
+ else if (mobjinfo[i].flags2 & MF2_SPAWNFLOAT)
+ {
+ z = FLOATRANDZ;
+ }
+ else
+ {
+ z = ONFLOORZ;
+ }
+ mobj = P_SpawnMobj(x, y, z, i);
+ if (mobj->flags2 & MF2_FLOATBOB)
+ { // Seed random starting index for bobbing motion
+ mobj->health = P_Random();
+ }
+ if (mobj->tics > 0)
+ {
+ mobj->tics = 1 + (P_Random() % mobj->tics);
+ }
+ if (mobj->flags & MF_COUNTKILL)
+ {
+ totalkills++;
+ mobj->spawnpoint = *mthing;
+ }
+ if (mobj->flags & MF_COUNTITEM)
+ {
+ totalitems++;
+ }
+ mobj->angle = ANG45 * (mthing->angle / 45);
+ if (mthing->options & MTF_AMBUSH)
+ {
+ mobj->flags |= MF_AMBUSH;
+ }
+}
+
+/*
+===============================================================================
+
+ GAME SPAWN FUNCTIONS
+
+===============================================================================
+*/
+
+//---------------------------------------------------------------------------
+//
+// PROC P_SpawnPuff
+//
+//---------------------------------------------------------------------------
+
+extern fixed_t attackrange;
+
+void P_SpawnPuff(fixed_t x, fixed_t y, fixed_t z)
+{
+ mobj_t *puff;
+
+ z += ((P_Random() - P_Random()) << 10);
+ puff = P_SpawnMobj(x, y, z, PuffType);
+ if (puff->info->attacksound)
+ {
+ S_StartSound(puff, puff->info->attacksound);
+ }
+ switch (PuffType)
+ {
+ case MT_BEAKPUFF:
+ case MT_STAFFPUFF:
+ puff->momz = FRACUNIT;
+ break;
+ case MT_GAUNTLETPUFF1:
+ case MT_GAUNTLETPUFF2:
+ puff->momz = (fixed_t)(.8 * FRACUNIT);
+ default:
+ break;
+ }
+}
+
+/*
+================
+=
+= P_SpawnBlood
+=
+================
+*/
+
+/*
+void P_SpawnBlood (fixed_t x, fixed_t y, fixed_t z, int damage)
+{
+ mobj_t *th;
+
+ z += ((P_Random()-P_Random())<<10);
+ th = P_SpawnMobj (x,y,z, MT_BLOOD);
+ th->momz = FRACUNIT*2;
+ th->tics -= P_Random()&3;
+
+ if (damage <= 12 && damage >= 9)
+ P_SetMobjState (th,S_BLOOD2);
+ else if (damage < 9)
+ P_SetMobjState (th,S_BLOOD3);
+}
+*/
+
+//---------------------------------------------------------------------------
+//
+// PROC P_BloodSplatter
+//
+//---------------------------------------------------------------------------
+
+void P_BloodSplatter(fixed_t x, fixed_t y, fixed_t z, mobj_t * originator)
+{
+ mobj_t *mo;
+
+ mo = P_SpawnMobj(x, y, z, MT_BLOODSPLATTER);
+ mo->target = originator;
+ mo->momx = (P_Random() - P_Random()) << 9;
+ mo->momy = (P_Random() - P_Random()) << 9;
+ mo->momz = FRACUNIT * 2;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_RipperBlood
+//
+//---------------------------------------------------------------------------
+
+void P_RipperBlood(mobj_t * mo)
+{
+ mobj_t *th;
+ fixed_t x, y, z;
+
+ x = mo->x + ((P_Random() - P_Random()) << 12);
+ y = mo->y + ((P_Random() - P_Random()) << 12);
+ z = mo->z + ((P_Random() - P_Random()) << 12);
+ th = P_SpawnMobj(x, y, z, MT_BLOOD);
+ th->flags |= MF_NOGRAVITY;
+ th->momx = mo->momx >> 1;
+ th->momy = mo->momy >> 1;
+ th->tics += P_Random() & 3;
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_GetThingFloorType
+//
+//---------------------------------------------------------------------------
+
+int P_GetThingFloorType(mobj_t * thing)
+{
+ return (TerrainTypes[thing->subsector->sector->floorpic]);
+/*
+ if(thing->subsector->sector->floorpic
+ == W_GetNumForName("FLTWAWA1")-firstflat)
+ {
+ return(FLOOR_WATER);
+ }
+ else
+ {
+ return(FLOOR_SOLID);
+ }
+*/
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_HitFloor
+//
+//---------------------------------------------------------------------------
+
+int P_HitFloor(mobj_t * thing)
+{
+ mobj_t *mo;
+
+ if (thing->floorz != thing->subsector->sector->floorheight)
+ { // don't splash if landing on the edge above water/lava/etc....
+ return (FLOOR_SOLID);
+ }
+ switch (P_GetThingFloorType(thing))
+ {
+ case FLOOR_WATER:
+ P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASHBASE);
+ mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SPLASH);
+ mo->target = thing;
+ mo->momx = (P_Random() - P_Random()) << 8;
+ mo->momy = (P_Random() - P_Random()) << 8;
+ mo->momz = 2 * FRACUNIT + (P_Random() << 8);
+ S_StartSound(mo, sfx_gloop);
+ return (FLOOR_WATER);
+ case FLOOR_LAVA:
+ P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASPLASH);
+ mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_LAVASMOKE);
+ mo->momz = FRACUNIT + (P_Random() << 7);
+ S_StartSound(mo, sfx_burn);
+ return (FLOOR_LAVA);
+ case FLOOR_SLUDGE:
+ P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGESPLASH);
+ mo = P_SpawnMobj(thing->x, thing->y, ONFLOORZ, MT_SLUDGECHUNK);
+ mo->target = thing;
+ mo->momx = (P_Random() - P_Random()) << 8;
+ mo->momy = (P_Random() - P_Random()) << 8;
+ mo->momz = FRACUNIT + (P_Random() << 8);
+ return (FLOOR_SLUDGE);
+ }
+ return (FLOOR_SOLID);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_CheckMissileSpawn
+//
+// Returns true if the missile is at a valid spawn point, otherwise
+// explodes it and returns false.
+//
+//---------------------------------------------------------------------------
+
+boolean P_CheckMissileSpawn(mobj_t * missile)
+{
+ //missile->tics -= P_Random()&3;
+
+ // move a little forward so an angle can be computed if it
+ // immediately explodes
+ missile->x += (missile->momx >> 1);
+ missile->y += (missile->momy >> 1);
+ missile->z += (missile->momz >> 1);
+ if (!P_TryMove(missile, missile->x, missile->y))
+ {
+ P_ExplodeMissile(missile);
+ return (false);
+ }
+ return (true);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_SpawnMissile
+//
+// Returns NULL if the missile exploded immediately, otherwise returns
+// a mobj_t pointer to the missile.
+//
+//---------------------------------------------------------------------------
+
+mobj_t *P_SpawnMissile(mobj_t * source, mobj_t * dest, mobjtype_t type)
+{
+ fixed_t z;
+ mobj_t *th;
+ angle_t an;
+ int dist;
+
+ switch (type)
+ {
+ case MT_MNTRFX1: // Minotaur swing attack missile
+ z = source->z + 40 * FRACUNIT;
+ break;
+ case MT_MNTRFX2: // Minotaur floor fire missile
+ z = ONFLOORZ;
+ break;
+ case MT_SRCRFX1: // Sorcerer Demon fireball
+ z = source->z + 48 * FRACUNIT;
+ break;
+ case MT_KNIGHTAXE: // Knight normal axe
+ case MT_REDAXE: // Knight red power axe
+ z = source->z + 36 * FRACUNIT;
+ break;
+ default:
+ z = source->z + 32 * FRACUNIT;
+ break;
+ }
+ if (source->flags2 & MF2_FEETARECLIPPED)
+ {
+ z -= FOOTCLIPSIZE;
+ }
+ th = P_SpawnMobj(source->x, source->y, z, type);
+ if (th->info->seesound)
+ {
+ S_StartSound(th, th->info->seesound);
+ }
+ th->target = source; // Originator
+ an = R_PointToAngle2(source->x, source->y, dest->x, dest->y);
+ if (dest->flags & MF_SHADOW)
+ { // Invisible target
+ an += (P_Random() - P_Random()) << 21;
+ }
+ 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;
+ return (P_CheckMissileSpawn(th) ? th : NULL);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_SpawnMissileAngle
+//
+// Returns NULL if the missile exploded immediately, otherwise returns
+// a mobj_t pointer to the missile.
+//
+//---------------------------------------------------------------------------
+
+mobj_t *P_SpawnMissileAngle(mobj_t * source, mobjtype_t type,
+ angle_t angle, fixed_t momz)
+{
+ fixed_t z;
+ mobj_t *mo;
+
+ switch (type)
+ {
+ case MT_MNTRFX1: // Minotaur swing attack missile
+ z = source->z + 40 * FRACUNIT;
+ break;
+ case MT_MNTRFX2: // Minotaur floor fire missile
+ z = ONFLOORZ;
+ break;
+ case MT_SRCRFX1: // Sorcerer Demon fireball
+ z = source->z + 48 * FRACUNIT;
+ break;
+ default:
+ z = source->z + 32 * FRACUNIT;
+ break;
+ }
+ if (source->flags2 & MF2_FEETARECLIPPED)
+ {
+ z -= FOOTCLIPSIZE;
+ }
+ mo = P_SpawnMobj(source->x, source->y, z, type);
+ if (mo->info->seesound)
+ {
+ S_StartSound(mo, mo->info->seesound);
+ }
+ mo->target = source; // Originator
+ mo->angle = angle;
+ angle >>= ANGLETOFINESHIFT;
+ mo->momx = FixedMul(mo->info->speed, finecosine[angle]);
+ mo->momy = FixedMul(mo->info->speed, finesine[angle]);
+ mo->momz = momz;
+ return (P_CheckMissileSpawn(mo) ? mo : NULL);
+}
+
+/*
+================
+=
+= P_SpawnPlayerMissile
+=
+= Tries to aim at a nearby monster
+================
+*/
+
+mobj_t *P_SpawnPlayerMissile(mobj_t * source, mobjtype_t type)
+{
+ angle_t an;
+ fixed_t x, y, z, slope;
+
+ // Try to find a target
+ an = source->angle;
+ slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT);
+ if (!linetarget)
+ {
+ an += 1 << 26;
+ slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT);
+ if (!linetarget)
+ {
+ an -= 2 << 26;
+ slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT);
+ }
+ if (!linetarget)
+ {
+ an = source->angle;
+ slope = ((source->player->lookdir) << FRACBITS) / 173;
+ }
+ }
+ x = source->x;
+ y = source->y;
+ z = source->z + 4 * 8 * FRACUNIT +
+ ((source->player->lookdir) << FRACBITS) / 173;
+ if (source->flags2 & MF2_FEETARECLIPPED)
+ {
+ z -= FOOTCLIPSIZE;
+ }
+ MissileMobj = P_SpawnMobj(x, y, z, type);
+ if (MissileMobj->info->seesound)
+ {
+ S_StartSound(MissileMobj, MissileMobj->info->seesound);
+ }
+ MissileMobj->target = source;
+ MissileMobj->angle = an;
+ MissileMobj->momx = FixedMul(MissileMobj->info->speed,
+ finecosine[an >> ANGLETOFINESHIFT]);
+ MissileMobj->momy = FixedMul(MissileMobj->info->speed,
+ finesine[an >> ANGLETOFINESHIFT]);
+ MissileMobj->momz = FixedMul(MissileMobj->info->speed, slope);
+ if (MissileMobj->type == MT_BLASTERFX1)
+ { // Ultra-fast ripper spawning missile
+ MissileMobj->x += (MissileMobj->momx >> 3);
+ MissileMobj->y += (MissileMobj->momy >> 3);
+ MissileMobj->z += (MissileMobj->momz >> 3);
+ }
+ else
+ { // Normal missile
+ MissileMobj->x += (MissileMobj->momx >> 1);
+ MissileMobj->y += (MissileMobj->momy >> 1);
+ MissileMobj->z += (MissileMobj->momz >> 1);
+ }
+ if (!P_TryMove(MissileMobj, MissileMobj->x, MissileMobj->y))
+ { // Exploded immediately
+ P_ExplodeMissile(MissileMobj);
+ return (NULL);
+ }
+ return (MissileMobj);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_SPMAngle
+//
+//---------------------------------------------------------------------------
+
+mobj_t *P_SPMAngle(mobj_t * source, mobjtype_t type, angle_t angle)
+{
+ mobj_t *th;
+ angle_t an;
+ fixed_t x, y, z, slope;
+
+//
+// see which target is to be aimed at
+//
+ an = angle;
+ slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT);
+ if (!linetarget)
+ {
+ an += 1 << 26;
+ slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT);
+ if (!linetarget)
+ {
+ an -= 2 << 26;
+ slope = P_AimLineAttack(source, an, 16 * 64 * FRACUNIT);
+ }
+ if (!linetarget)
+ {
+ an = angle;
+ slope = ((source->player->lookdir) << FRACBITS) / 173;
+ }
+ }
+ x = source->x;
+ y = source->y;
+ z = source->z + 4 * 8 * FRACUNIT +
+ ((source->player->lookdir) << FRACBITS) / 173;
+ if (source->flags2 & MF2_FEETARECLIPPED)
+ {
+ z -= FOOTCLIPSIZE;
+ }
+ th = P_SpawnMobj(x, y, z, type);
+ if (th->info->seesound)
+ {
+ S_StartSound(th, th->info->seesound);
+ }
+ 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);
+ return (P_CheckMissileSpawn(th) ? th : NULL);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_ContMobjSound
+//
+//---------------------------------------------------------------------------
+
+void A_ContMobjSound(mobj_t * actor)
+{
+ switch (actor->type)
+ {
+ case MT_KNIGHTAXE:
+ S_StartSound(actor, sfx_kgtatk);
+ break;
+ case MT_MUMMYFX1:
+ S_StartSound(actor, sfx_mumhed);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/heretic/p_plats.c b/src/heretic/p_plats.c
new file mode 100644
index 00000000..23aa99d9
--- /dev/null
+++ b/src/heretic/p_plats.c
@@ -0,0 +1,266 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_plats.c
+
+#include "doomdef.h"
+#include "i_system.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+plat_t *activeplats[MAXPLATS];
+
+//==================================================================
+//
+// Move a plat up and down
+//
+//==================================================================
+void T_PlatRaise(plat_t * plat)
+{
+ result_e res;
+
+ switch (plat->status)
+ {
+ case up:
+ res = T_MovePlane(plat->sector, plat->speed,
+ plat->high, plat->crush, 0, 1);
+ if (!(leveltime & 31))
+ {
+ S_StartSound(&plat->sector->soundorg, sfx_stnmov);
+ }
+ if (plat->type == raiseAndChange
+ || plat->type == raiseToNearestAndChange)
+ {
+ if (!(leveltime & 7))
+ {
+ S_StartSound(&plat->sector->soundorg,
+ sfx_stnmov);
+ }
+ }
+ if (res == crushed && (!plat->crush))
+ {
+ plat->count = plat->wait;
+ plat->status = down;
+ S_StartSound(&plat->sector->soundorg, sfx_pstart);
+ }
+ else if (res == pastdest)
+ {
+ plat->count = plat->wait;
+ plat->status = waiting;
+ S_StartSound(&plat->sector->soundorg, sfx_pstop);
+ switch (plat->type)
+ {
+ case downWaitUpStay:
+ P_RemoveActivePlat(plat);
+ break;
+ case raiseAndChange:
+ P_RemoveActivePlat(plat);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case down:
+ res =
+ T_MovePlane(plat->sector, plat->speed, plat->low, false, 0,
+ -1);
+ if (res == pastdest)
+ {
+ plat->count = plat->wait;
+ plat->status = waiting;
+ S_StartSound(&plat->sector->soundorg, sfx_pstop);
+ }
+ else
+ {
+ if (!(leveltime & 31))
+ {
+ S_StartSound(&plat->sector->soundorg,
+ sfx_stnmov);
+ }
+ }
+ break;
+ case waiting:
+ if (!--plat->count)
+ {
+ if (plat->sector->floorheight == plat->low)
+ plat->status = up;
+ else
+ plat->status = down;
+ S_StartSound(&plat->sector->soundorg, sfx_pstart);
+ }
+ case in_stasis:
+ break;
+ }
+}
+
+//==================================================================
+//
+// Do Platforms
+// "amount" is only used for SOME platforms.
+//
+//==================================================================
+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 <type> plats that are in_stasis
+ //
+ switch (type)
+ {
+ case perpetualRaise:
+ P_ActivateInStasis(line->tag);
+ break;
+ default:
+ break;
+ }
+
+ while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
+ {
+ sec = &sectors[secnum];
+ if (sec->specialdata)
+ continue;
+
+ //
+ // Find lowest & highest floors around sector
+ //
+ rtn = 1;
+ plat = Z_Malloc(sizeof(*plat), PU_LEVSPEC, 0);
+ P_AddThinker(&plat->thinker);
+
+ plat->type = type;
+ plat->sector = sec;
+ plat->sector->specialdata = plat;
+ plat->thinker.function = T_PlatRaise;
+ plat->crush = false;
+ plat->tag = line->tag;
+ 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; // NO MORE DAMAGE, IF APPLICABLE
+ S_StartSound(&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(&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(&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() & 1;
+ S_StartSound(&sec->soundorg, sfx_pstart);
+ break;
+ }
+ P_AddActivePlat(plat);
+ }
+ return rtn;
+}
+
+void P_ActivateInStasis(int tag)
+{
+ int i;
+
+ for (i = 0; i < MAXPLATS; i++)
+ if (activeplats[i] &&
+ (activeplats[i])->tag == tag &&
+ (activeplats[i])->status == in_stasis)
+ {
+ (activeplats[i])->status = (activeplats[i])->oldstatus;
+ (activeplats[i])->thinker.function = T_PlatRaise;
+ }
+}
+
+void EV_StopPlat(line_t * line)
+{
+ int j;
+
+ for (j = 0; j < MAXPLATS; j++)
+ if (activeplats[j] && ((activeplats[j])->status != in_stasis) &&
+ ((activeplats[j])->tag == line->tag))
+ {
+ (activeplats[j])->oldstatus = (activeplats[j])->status;
+ (activeplats[j])->status = in_stasis;
+ (activeplats[j])->thinker.function = NULL;
+ }
+}
+
+void P_AddActivePlat(plat_t * plat)
+{
+ int i;
+ for (i = 0; i < MAXPLATS; i++)
+ if (activeplats[i] == NULL)
+ {
+ activeplats[i] = plat;
+ return;
+ }
+ I_Error("P_AddActivePlat: no more plats!");
+}
+
+void P_RemoveActivePlat(plat_t * plat)
+{
+ int i;
+ for (i = 0; i < MAXPLATS; i++)
+ if (plat == activeplats[i])
+ {
+ (activeplats[i])->sector->specialdata = NULL;
+ P_RemoveThinker(&(activeplats[i])->thinker);
+ activeplats[i] = NULL;
+ return;
+ }
+ I_Error("P_RemoveActivePlat: can't find plat!");
+}
diff --git a/src/heretic/p_pspr.c b/src/heretic/p_pspr.c
new file mode 100644
index 00000000..bd47c9a1
--- /dev/null
+++ b/src/heretic/p_pspr.c
@@ -0,0 +1,1908 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_pspr.c
+
+#include "doomdef.h"
+#include "i_system.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "s_sound.h"
+
+// Macros
+
+#define LOWERSPEED FRACUNIT*6
+#define RAISESPEED FRACUNIT*6
+#define WEAPONBOTTOM 128*FRACUNIT
+#define WEAPONTOP 32*FRACUNIT
+#define FLAME_THROWER_TICS 10*35
+#define MAGIC_JUNK 1234
+#define MAX_MACE_SPOTS 8
+
+static int MaceSpotCount;
+static struct
+{
+ fixed_t x;
+ fixed_t y;
+} MaceSpots[MAX_MACE_SPOTS];
+
+fixed_t bulletslope;
+
+static int WeaponAmmoUsePL1[NUMWEAPONS] = {
+ 0, // staff
+ USE_GWND_AMMO_1, // gold wand
+ USE_CBOW_AMMO_1, // crossbow
+ USE_BLSR_AMMO_1, // blaster
+ USE_SKRD_AMMO_1, // skull rod
+ USE_PHRD_AMMO_1, // phoenix rod
+ USE_MACE_AMMO_1, // mace
+ 0, // gauntlets
+ 0 // beak
+};
+
+static int WeaponAmmoUsePL2[NUMWEAPONS] = {
+ 0, // staff
+ USE_GWND_AMMO_2, // gold wand
+ USE_CBOW_AMMO_2, // crossbow
+ USE_BLSR_AMMO_2, // blaster
+ USE_SKRD_AMMO_2, // skull rod
+ USE_PHRD_AMMO_2, // phoenix rod
+ USE_MACE_AMMO_2, // mace
+ 0, // gauntlets
+ 0 // beak
+};
+
+weaponinfo_t wpnlev1info[NUMWEAPONS] = {
+ { // Staff
+ am_noammo, // ammo
+ S_STAFFUP, // upstate
+ S_STAFFDOWN, // downstate
+ S_STAFFREADY, // readystate
+ S_STAFFATK1_1, // atkstate
+ S_STAFFATK1_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Gold wand
+ am_goldwand, // ammo
+ S_GOLDWANDUP, // upstate
+ S_GOLDWANDDOWN, // downstate
+ S_GOLDWANDREADY, // readystate
+ S_GOLDWANDATK1_1, // atkstate
+ S_GOLDWANDATK1_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Crossbow
+ am_crossbow, // ammo
+ S_CRBOWUP, // upstate
+ S_CRBOWDOWN, // downstate
+ S_CRBOW1, // readystate
+ S_CRBOWATK1_1, // atkstate
+ S_CRBOWATK1_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Blaster
+ am_blaster, // ammo
+ S_BLASTERUP, // upstate
+ S_BLASTERDOWN, // downstate
+ S_BLASTERREADY, // readystate
+ S_BLASTERATK1_1, // atkstate
+ S_BLASTERATK1_3, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Skull rod
+ am_skullrod, // ammo
+ S_HORNRODUP, // upstate
+ S_HORNRODDOWN, // downstate
+ S_HORNRODREADY, // readystae
+ S_HORNRODATK1_1, // atkstate
+ S_HORNRODATK1_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Phoenix rod
+ am_phoenixrod, // ammo
+ S_PHOENIXUP, // upstate
+ S_PHOENIXDOWN, // downstate
+ S_PHOENIXREADY, // readystate
+ S_PHOENIXATK1_1, // atkstate
+ S_PHOENIXATK1_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Mace
+ am_mace, // ammo
+ S_MACEUP, // upstate
+ S_MACEDOWN, // downstate
+ S_MACEREADY, // readystate
+ S_MACEATK1_1, // atkstate
+ S_MACEATK1_2, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Gauntlets
+ am_noammo, // ammo
+ S_GAUNTLETUP, // upstate
+ S_GAUNTLETDOWN, // downstate
+ S_GAUNTLETREADY, // readystate
+ S_GAUNTLETATK1_1, // atkstate
+ S_GAUNTLETATK1_3, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Beak
+ am_noammo, // ammo
+ S_BEAKUP, // upstate
+ S_BEAKDOWN, // downstate
+ S_BEAKREADY, // readystate
+ S_BEAKATK1_1, // atkstate
+ S_BEAKATK1_1, // holdatkstate
+ S_NULL // flashstate
+ }
+};
+
+weaponinfo_t wpnlev2info[NUMWEAPONS] = {
+ { // Staff
+ am_noammo, // ammo
+ S_STAFFUP2, // upstate
+ S_STAFFDOWN2, // downstate
+ S_STAFFREADY2_1, // readystate
+ S_STAFFATK2_1, // atkstate
+ S_STAFFATK2_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Gold wand
+ am_goldwand, // ammo
+ S_GOLDWANDUP, // upstate
+ S_GOLDWANDDOWN, // downstate
+ S_GOLDWANDREADY, // readystate
+ S_GOLDWANDATK2_1, // atkstate
+ S_GOLDWANDATK2_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Crossbow
+ am_crossbow, // ammo
+ S_CRBOWUP, // upstate
+ S_CRBOWDOWN, // downstate
+ S_CRBOW1, // readystate
+ S_CRBOWATK2_1, // atkstate
+ S_CRBOWATK2_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Blaster
+ am_blaster, // ammo
+ S_BLASTERUP, // upstate
+ S_BLASTERDOWN, // downstate
+ S_BLASTERREADY, // readystate
+ S_BLASTERATK2_1, // atkstate
+ S_BLASTERATK2_3, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Skull rod
+ am_skullrod, // ammo
+ S_HORNRODUP, // upstate
+ S_HORNRODDOWN, // downstate
+ S_HORNRODREADY, // readystae
+ S_HORNRODATK2_1, // atkstate
+ S_HORNRODATK2_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Phoenix rod
+ am_phoenixrod, // ammo
+ S_PHOENIXUP, // upstate
+ S_PHOENIXDOWN, // downstate
+ S_PHOENIXREADY, // readystate
+ S_PHOENIXATK2_1, // atkstate
+ S_PHOENIXATK2_2, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Mace
+ am_mace, // ammo
+ S_MACEUP, // upstate
+ S_MACEDOWN, // downstate
+ S_MACEREADY, // readystate
+ S_MACEATK2_1, // atkstate
+ S_MACEATK2_1, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Gauntlets
+ am_noammo, // ammo
+ S_GAUNTLETUP2, // upstate
+ S_GAUNTLETDOWN2, // downstate
+ S_GAUNTLETREADY2_1, // readystate
+ S_GAUNTLETATK2_1, // atkstate
+ S_GAUNTLETATK2_3, // holdatkstate
+ S_NULL // flashstate
+ },
+ { // Beak
+ am_noammo, // ammo
+ S_BEAKUP, // upstate
+ S_BEAKDOWN, // downstate
+ S_BEAKREADY, // readystate
+ S_BEAKATK2_1, // atkstate
+ S_BEAKATK2_1, // holdatkstate
+ S_NULL // flashstate
+ }
+};
+
+//---------------------------------------------------------------------------
+//
+// PROC P_OpenWeapons
+//
+// Called at level load before things are loaded.
+//
+//---------------------------------------------------------------------------
+
+void P_OpenWeapons(void)
+{
+ MaceSpotCount = 0;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_AddMaceSpot
+//
+//---------------------------------------------------------------------------
+
+void P_AddMaceSpot(mapthing_t * mthing)
+{
+ if (MaceSpotCount == MAX_MACE_SPOTS)
+ {
+ I_Error("Too many mace spots.");
+ }
+ MaceSpots[MaceSpotCount].x = mthing->x << FRACBITS;
+ MaceSpots[MaceSpotCount].y = mthing->y << FRACBITS;
+ MaceSpotCount++;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_RepositionMace
+//
+// Chooses the next spot to place the mace.
+//
+//---------------------------------------------------------------------------
+
+void P_RepositionMace(mobj_t * mo)
+{
+ int spot;
+ subsector_t *ss;
+
+ P_UnsetThingPosition(mo);
+ spot = P_Random() % MaceSpotCount;
+ mo->x = MaceSpots[spot].x;
+ mo->y = MaceSpots[spot].y;
+ ss = R_PointInSubsector(mo->x, mo->y);
+ mo->z = mo->floorz = ss->sector->floorheight;
+ mo->ceilingz = ss->sector->ceilingheight;
+ P_SetThingPosition(mo);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_CloseWeapons
+//
+// Called at level load after things are loaded.
+//
+//---------------------------------------------------------------------------
+
+void P_CloseWeapons(void)
+{
+ int spot;
+
+ if (!MaceSpotCount)
+ { // No maces placed
+ return;
+ }
+ if (!deathmatch && P_Random() < 64)
+ { // Sometimes doesn't show up if not in deathmatch
+ return;
+ }
+ spot = P_Random() % MaceSpotCount;
+ P_SpawnMobj(MaceSpots[spot].x, MaceSpots[spot].y, ONFLOORZ, MT_WMACE);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_SetPsprite
+//
+//---------------------------------------------------------------------------
+
+void P_SetPsprite(player_t * player, int position, statenum_t stnum)
+{
+ pspdef_t *psp;
+ state_t *state;
+
+ psp = &player->psprites[position];
+ do
+ {
+ 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)
+ { // Set coordinates.
+ psp->sx = state->misc1 << FRACBITS;
+ psp->sy = state->misc2 << FRACBITS;
+ }
+ if (state->action)
+ { // Call action routine.
+ state->action(player, psp);
+ if (!psp->state)
+ {
+ break;
+ }
+ }
+ stnum = psp->state->nextstate;
+ }
+ while (!psp->tics); // An initial state of 0 could cycle through.
+}
+
+/*
+=================
+=
+= P_CalcSwing
+=
+=================
+*/
+
+/*
+fixed_t swingx, swingy;
+void P_CalcSwing (player_t *player)
+{
+ fixed_t swing;
+ int angle;
+
+// OPTIMIZE: tablify this
+
+ swing = player->bob;
+
+ angle = (FINEANGLES/70*leveltime)&FINEMASK;
+ swingx = FixedMul ( swing, finesine[angle]);
+
+ angle = (FINEANGLES/70*leveltime+FINEANGLES/2)&FINEMASK;
+ swingy = -FixedMul ( swingx, finesine[angle]);
+}
+*/
+
+//---------------------------------------------------------------------------
+//
+// PROC P_ActivateBeak
+//
+//---------------------------------------------------------------------------
+
+void P_ActivateBeak(player_t * player)
+{
+ player->pendingweapon = wp_nochange;
+ player->readyweapon = wp_beak;
+ player->psprites[ps_weapon].sy = WEAPONTOP;
+ P_SetPsprite(player, ps_weapon, S_BEAKREADY);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_PostChickenWeapon
+//
+//---------------------------------------------------------------------------
+
+void P_PostChickenWeapon(player_t * player, weapontype_t weapon)
+{
+ if (weapon == wp_beak)
+ { // Should never happen
+ weapon = wp_staff;
+ }
+ player->pendingweapon = wp_nochange;
+ player->readyweapon = weapon;
+ player->psprites[ps_weapon].sy = WEAPONBOTTOM;
+ P_SetPsprite(player, ps_weapon, wpnlev1info[weapon].upstate);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_BringUpWeapon
+//
+// Starts bringing the pending weapon up from the bottom of the screen.
+//
+//---------------------------------------------------------------------------
+
+void P_BringUpWeapon(player_t * player)
+{
+ statenum_t new;
+
+ if (player->pendingweapon == wp_nochange)
+ {
+ player->pendingweapon = player->readyweapon;
+ }
+ if (player->pendingweapon == wp_gauntlets)
+ {
+ S_StartSound(player->mo, sfx_gntact);
+ }
+ if (player->powers[pw_weaponlevel2])
+ {
+ new = wpnlev2info[player->pendingweapon].upstate;
+ }
+ else
+ {
+ new = wpnlev1info[player->pendingweapon].upstate;
+ }
+ player->pendingweapon = wp_nochange;
+ player->psprites[ps_weapon].sy = WEAPONBOTTOM;
+ P_SetPsprite(player, ps_weapon, new);
+}
+
+//---------------------------------------------------------------------------
+//
+// FUNC P_CheckAmmo
+//
+// Returns true if there is enough ammo to shoot. If not, selects the
+// next weapon to use.
+//
+//---------------------------------------------------------------------------
+
+boolean P_CheckAmmo(player_t * player)
+{
+ ammotype_t ammo;
+ int *ammoUse;
+ int count;
+
+ ammo = wpnlev1info[player->readyweapon].ammo;
+ if (player->powers[pw_weaponlevel2] && !deathmatch)
+ {
+ ammoUse = WeaponAmmoUsePL2;
+ }
+ else
+ {
+ ammoUse = WeaponAmmoUsePL1;
+ }
+ count = ammoUse[player->readyweapon];
+ if (ammo == am_noammo || player->ammo[ammo] >= count)
+ {
+ return (true);
+ }
+ // out of ammo, pick a weapon to change to
+ do
+ {
+ if (player->weaponowned[wp_skullrod]
+ && player->ammo[am_skullrod] > ammoUse[wp_skullrod])
+ {
+ player->pendingweapon = wp_skullrod;
+ }
+ else if (player->weaponowned[wp_blaster]
+ && player->ammo[am_blaster] > ammoUse[wp_blaster])
+ {
+ player->pendingweapon = wp_blaster;
+ }
+ else if (player->weaponowned[wp_crossbow]
+ && player->ammo[am_crossbow] > ammoUse[wp_crossbow])
+ {
+ player->pendingweapon = wp_crossbow;
+ }
+ else if (player->weaponowned[wp_mace]
+ && player->ammo[am_mace] > ammoUse[wp_mace])
+ {
+ player->pendingweapon = wp_mace;
+ }
+ else if (player->ammo[am_goldwand] > ammoUse[wp_goldwand])
+ {
+ player->pendingweapon = wp_goldwand;
+ }
+ else if (player->weaponowned[wp_gauntlets])
+ {
+ player->pendingweapon = wp_gauntlets;
+ }
+ else if (player->weaponowned[wp_phoenixrod]
+ && player->ammo[am_phoenixrod] > ammoUse[wp_phoenixrod])
+ {
+ player->pendingweapon = wp_phoenixrod;
+ }
+ else
+ {
+ player->pendingweapon = wp_staff;
+ }
+ }
+ while (player->pendingweapon == wp_nochange);
+ if (player->powers[pw_weaponlevel2])
+ {
+ P_SetPsprite(player, ps_weapon,
+ wpnlev2info[player->readyweapon].downstate);
+ }
+ else
+ {
+ P_SetPsprite(player, ps_weapon,
+ wpnlev1info[player->readyweapon].downstate);
+ }
+ return (false);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_FireWeapon
+//
+//---------------------------------------------------------------------------
+
+void P_FireWeapon(player_t * player)
+{
+ weaponinfo_t *wpinfo;
+ statenum_t attackState;
+
+ if (!P_CheckAmmo(player))
+ {
+ return;
+ }
+ P_SetMobjState(player->mo, S_PLAY_ATK2);
+ wpinfo = player->powers[pw_weaponlevel2] ? &wpnlev2info[0]
+ : &wpnlev1info[0];
+ attackState = player->refire ? wpinfo[player->readyweapon].holdatkstate
+ : wpinfo[player->readyweapon].atkstate;
+ P_SetPsprite(player, ps_weapon, attackState);
+ P_NoiseAlert(player->mo, player->mo);
+ if (player->readyweapon == wp_gauntlets && !player->refire)
+ { // Play the sound for the initial gauntlet attack
+ S_StartSound(player->mo, sfx_gntuse);
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_DropWeapon
+//
+// The player died, so put the weapon away.
+//
+//---------------------------------------------------------------------------
+
+void P_DropWeapon(player_t * player)
+{
+ if (player->powers[pw_weaponlevel2])
+ {
+ P_SetPsprite(player, ps_weapon,
+ wpnlev2info[player->readyweapon].downstate);
+ }
+ else
+ {
+ P_SetPsprite(player, ps_weapon,
+ wpnlev1info[player->readyweapon].downstate);
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_WeaponReady
+//
+// The player can fire the weapon or change to another weapon at this time.
+//
+//---------------------------------------------------------------------------
+
+void A_WeaponReady(player_t * player, pspdef_t * psp)
+{
+ int angle;
+
+ if (player->chickenTics)
+ { // Change to the chicken beak
+ P_ActivateBeak(player);
+ return;
+ }
+ // Change player from attack state
+ if (player->mo->state == &states[S_PLAY_ATK1]
+ || player->mo->state == &states[S_PLAY_ATK2])
+ {
+ P_SetMobjState(player->mo, S_PLAY);
+ }
+ // Check for staff PL2 active sound
+ if ((player->readyweapon == wp_staff)
+ && (psp->state == &states[S_STAFFREADY2_1]) && P_Random() < 128)
+ {
+ S_StartSound(player->mo, sfx_stfcrk);
+ }
+ // Put the weapon away if the player has a pending weapon or has
+ // died.
+ if (player->pendingweapon != wp_nochange || !player->health)
+ {
+ if (player->powers[pw_weaponlevel2])
+ {
+ P_SetPsprite(player, ps_weapon,
+ wpnlev2info[player->readyweapon].downstate);
+ }
+ else
+ {
+ P_SetPsprite(player, ps_weapon,
+ wpnlev1info[player->readyweapon].downstate);
+ }
+ return;
+ }
+
+ // Check for fire. The phoenix rod does not auto fire.
+ if (player->cmd.buttons & BT_ATTACK)
+ {
+ if (!player->attackdown || (player->readyweapon != wp_phoenixrod))
+ {
+ player->attackdown = true;
+ P_FireWeapon(player);
+ return;
+ }
+ }
+ else
+ {
+ player->attackdown = false;
+ }
+
+ // Bob the weapon based on movement speed.
+ angle = (128 * leveltime) & FINEMASK;
+ psp->sx = FRACUNIT + FixedMul(player->bob, finecosine[angle]);
+ angle &= FINEANGLES / 2 - 1;
+ psp->sy = WEAPONTOP + FixedMul(player->bob, finesine[angle]);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC P_UpdateBeak
+//
+//---------------------------------------------------------------------------
+
+void P_UpdateBeak(player_t * player, pspdef_t * psp)
+{
+ psp->sy = WEAPONTOP + (player->chickenPeck << (FRACBITS - 1));
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_BeakReady
+//
+//---------------------------------------------------------------------------
+
+void A_BeakReady(player_t * player, pspdef_t * psp)
+{
+ if (player->cmd.buttons & BT_ATTACK)
+ { // Chicken beak attack
+ player->attackdown = true;
+ P_SetMobjState(player->mo, S_CHICPLAY_ATK1);
+ if (player->powers[pw_weaponlevel2])
+ {
+ P_SetPsprite(player, ps_weapon, S_BEAKATK2_1);
+ }
+ else
+ {
+ P_SetPsprite(player, ps_weapon, S_BEAKATK1_1);
+ }
+ P_NoiseAlert(player->mo, player->mo);
+ }
+ else
+ {
+ if (player->mo->state == &states[S_CHICPLAY_ATK1])
+ { // Take out of attack state
+ P_SetMobjState(player->mo, S_CHICPLAY);
+ }
+ player->attackdown = false;
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_ReFire
+//
+// The player can re fire the weapon without lowering it entirely.
+//
+//---------------------------------------------------------------------------
+
+void A_ReFire(player_t * player, pspdef_t * psp)
+{
+ if ((player->cmd.buttons & BT_ATTACK)
+ && player->pendingweapon == wp_nochange && player->health)
+ {
+ player->refire++;
+ P_FireWeapon(player);
+ }
+ else
+ {
+ player->refire = 0;
+ P_CheckAmmo(player);
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_Lower
+//
+//---------------------------------------------------------------------------
+
+void A_Lower(player_t * player, pspdef_t * psp)
+{
+ if (player->chickenTics)
+ {
+ psp->sy = WEAPONBOTTOM;
+ }
+ else
+ {
+ psp->sy += LOWERSPEED;
+ }
+ if (psp->sy < WEAPONBOTTOM)
+ { // Not lowered all the way yet
+ return;
+ }
+ if (player->playerstate == PST_DEAD)
+ { // Player is dead, so don't bring up a pending weapon
+ psp->sy = WEAPONBOTTOM;
+ return;
+ }
+ 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);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_BeakRaise
+//
+//---------------------------------------------------------------------------
+
+void A_BeakRaise(player_t * player, pspdef_t * psp)
+{
+ psp->sy = WEAPONTOP;
+ P_SetPsprite(player, ps_weapon,
+ wpnlev1info[player->readyweapon].readystate);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_Raise
+//
+//---------------------------------------------------------------------------
+
+void A_Raise(player_t * player, pspdef_t * psp)
+{
+ psp->sy -= RAISESPEED;
+ if (psp->sy > WEAPONTOP)
+ { // Not raised all the way yet
+ return;
+ }
+ psp->sy = WEAPONTOP;
+ if (player->powers[pw_weaponlevel2])
+ {
+ P_SetPsprite(player, ps_weapon,
+ wpnlev2info[player->readyweapon].readystate);
+ }
+ else
+ {
+ P_SetPsprite(player, ps_weapon,
+ wpnlev1info[player->readyweapon].readystate);
+ }
+}
+
+/*
+===============
+=
+= P_BulletSlope
+=
+= Sets a slope so a near miss is at aproximately the height of the
+= intended target
+=
+===============
+*/
+
+void P_BulletSlope(mobj_t * mo)
+{
+ angle_t an;
+
+//
+// see which target is to be aimed at
+//
+ an = mo->angle;
+ bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT);
+ if (!linetarget)
+ {
+ an += 1 << 26;
+ bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT);
+ if (!linetarget)
+ {
+ an -= 2 << 26;
+ bulletslope = P_AimLineAttack(mo, an, 16 * 64 * FRACUNIT);
+ }
+ if (!linetarget)
+ {
+ an += 2 << 26;
+ bulletslope = (mo->player->lookdir << FRACBITS) / 173;
+ }
+ }
+}
+
+//****************************************************************************
+//
+// WEAPON ATTACKS
+//
+//****************************************************************************
+
+//----------------------------------------------------------------------------
+//
+// PROC A_BeakAttackPL1
+//
+//----------------------------------------------------------------------------
+
+void A_BeakAttackPL1(player_t * player, pspdef_t * psp)
+{
+ angle_t angle;
+ int damage;
+ int slope;
+
+ damage = 1 + (P_Random() & 3);
+ angle = player->mo->angle;
+ slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
+ PuffType = MT_BEAKPUFF;
+ P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
+ if (linetarget)
+ {
+ player->mo->angle = R_PointToAngle2(player->mo->x,
+ player->mo->y, linetarget->x,
+ linetarget->y);
+ }
+ S_StartSound(player->mo, sfx_chicpk1 + (P_Random() % 3));
+ player->chickenPeck = 12;
+ psp->tics -= P_Random() & 7;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_BeakAttackPL2
+//
+//----------------------------------------------------------------------------
+
+void A_BeakAttackPL2(player_t * player, pspdef_t * psp)
+{
+ angle_t angle;
+ int damage;
+ int slope;
+
+ damage = HITDICE(4);
+ angle = player->mo->angle;
+ slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
+ PuffType = MT_BEAKPUFF;
+ P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
+ if (linetarget)
+ {
+ player->mo->angle = R_PointToAngle2(player->mo->x,
+ player->mo->y, linetarget->x,
+ linetarget->y);
+ }
+ S_StartSound(player->mo, sfx_chicpk1 + (P_Random() % 3));
+ player->chickenPeck = 12;
+ psp->tics -= P_Random() & 3;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_StaffAttackPL1
+//
+//----------------------------------------------------------------------------
+
+void A_StaffAttackPL1(player_t * player, pspdef_t * psp)
+{
+ angle_t angle;
+ int damage;
+ int slope;
+
+ damage = 5 + (P_Random() & 15);
+ angle = player->mo->angle;
+ angle += (P_Random() - P_Random()) << 18;
+ slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
+ PuffType = MT_STAFFPUFF;
+ P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
+ if (linetarget)
+ {
+ //S_StartSound(player->mo, sfx_stfhit);
+ // turn to face target
+ player->mo->angle = R_PointToAngle2(player->mo->x,
+ player->mo->y, linetarget->x,
+ linetarget->y);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_StaffAttackPL2
+//
+//----------------------------------------------------------------------------
+
+void A_StaffAttackPL2(player_t * player, pspdef_t * psp)
+{
+ angle_t angle;
+ int damage;
+ int slope;
+
+ // P_inter.c:P_DamageMobj() handles target momentums
+ damage = 18 + (P_Random() & 63);
+ angle = player->mo->angle;
+ angle += (P_Random() - P_Random()) << 18;
+ slope = P_AimLineAttack(player->mo, angle, MELEERANGE);
+ PuffType = MT_STAFFPUFF2;
+ P_LineAttack(player->mo, angle, MELEERANGE, slope, damage);
+ if (linetarget)
+ {
+ //S_StartSound(player->mo, sfx_stfpow);
+ // turn to face target
+ player->mo->angle = R_PointToAngle2(player->mo->x,
+ player->mo->y, linetarget->x,
+ linetarget->y);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireBlasterPL1
+//
+//----------------------------------------------------------------------------
+
+void A_FireBlasterPL1(player_t * player, pspdef_t * psp)
+{
+ mobj_t *mo;
+ angle_t angle;
+ int damage;
+
+ mo = player->mo;
+ S_StartSound(mo, sfx_gldhit);
+ player->ammo[am_blaster] -= USE_BLSR_AMMO_1;
+ P_BulletSlope(mo);
+ damage = HITDICE(4);
+ angle = mo->angle;
+ if (player->refire)
+ {
+ angle += (P_Random() - P_Random()) << 18;
+ }
+ PuffType = MT_BLASTERPUFF1;
+ P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage);
+ S_StartSound(player->mo, sfx_blssht);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireBlasterPL2
+//
+//----------------------------------------------------------------------------
+
+void A_FireBlasterPL2(player_t * player, pspdef_t * psp)
+{
+ mobj_t *mo;
+
+ player->ammo[am_blaster] -=
+ deathmatch ? USE_BLSR_AMMO_1 : USE_BLSR_AMMO_2;
+ mo = P_SpawnPlayerMissile(player->mo, MT_BLASTERFX1);
+ if (mo)
+ {
+ mo->thinker.function = P_BlasterMobjThinker;
+ }
+ S_StartSound(player->mo, sfx_blssht);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireGoldWandPL1
+//
+//----------------------------------------------------------------------------
+
+void A_FireGoldWandPL1(player_t * player, pspdef_t * psp)
+{
+ mobj_t *mo;
+ angle_t angle;
+ int damage;
+
+ mo = player->mo;
+ player->ammo[am_goldwand] -= USE_GWND_AMMO_1;
+ P_BulletSlope(mo);
+ damage = 7 + (P_Random() & 7);
+ angle = mo->angle;
+ if (player->refire)
+ {
+ angle += (P_Random() - P_Random()) << 18;
+ }
+ PuffType = MT_GOLDWANDPUFF1;
+ P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage);
+ S_StartSound(player->mo, sfx_gldhit);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireGoldWandPL2
+//
+//----------------------------------------------------------------------------
+
+void A_FireGoldWandPL2(player_t * player, pspdef_t * psp)
+{
+ int i;
+ mobj_t *mo;
+ angle_t angle;
+ int damage;
+ fixed_t momz;
+
+ mo = player->mo;
+ player->ammo[am_goldwand] -=
+ deathmatch ? USE_GWND_AMMO_1 : USE_GWND_AMMO_2;
+ PuffType = MT_GOLDWANDPUFF2;
+ P_BulletSlope(mo);
+ momz = FixedMul(mobjinfo[MT_GOLDWANDFX2].speed, bulletslope);
+ P_SpawnMissileAngle(mo, MT_GOLDWANDFX2, mo->angle - (ANG45 / 8), momz);
+ P_SpawnMissileAngle(mo, MT_GOLDWANDFX2, mo->angle + (ANG45 / 8), momz);
+ angle = mo->angle - (ANG45 / 8);
+ for (i = 0; i < 5; i++)
+ {
+ damage = 1 + (P_Random() & 7);
+ P_LineAttack(mo, angle, MISSILERANGE, bulletslope, damage);
+ angle += ((ANG45 / 8) * 2) / 4;
+ }
+ S_StartSound(player->mo, sfx_gldhit);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireMacePL1B
+//
+//----------------------------------------------------------------------------
+
+void A_FireMacePL1B(player_t * player, pspdef_t * psp)
+{
+ mobj_t *pmo;
+ mobj_t *ball;
+ angle_t angle;
+
+ if (player->ammo[am_mace] < USE_MACE_AMMO_1)
+ {
+ return;
+ }
+ player->ammo[am_mace] -= USE_MACE_AMMO_1;
+ pmo = player->mo;
+ ball = P_SpawnMobj(pmo->x, pmo->y, pmo->z + 28 * FRACUNIT
+ - FOOTCLIPSIZE * ((pmo->flags2 & MF2_FEETARECLIPPED) !=
+ 0), MT_MACEFX2);
+ ball->momz = 2 * FRACUNIT + ((player->lookdir) << (FRACBITS - 5));
+ angle = pmo->angle;
+ ball->target = pmo;
+ ball->angle = angle;
+ ball->z += (player->lookdir) << (FRACBITS - 4);
+ angle >>= ANGLETOFINESHIFT;
+ ball->momx = (pmo->momx >> 1)
+ + FixedMul(ball->info->speed, finecosine[angle]);
+ ball->momy = (pmo->momy >> 1)
+ + FixedMul(ball->info->speed, finesine[angle]);
+ S_StartSound(ball, sfx_lobsht);
+ P_CheckMissileSpawn(ball);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireMacePL1
+//
+//----------------------------------------------------------------------------
+
+void A_FireMacePL1(player_t * player, pspdef_t * psp)
+{
+ mobj_t *ball;
+
+ if (P_Random() < 28)
+ {
+ A_FireMacePL1B(player, psp);
+ return;
+ }
+ if (player->ammo[am_mace] < USE_MACE_AMMO_1)
+ {
+ return;
+ }
+ player->ammo[am_mace] -= USE_MACE_AMMO_1;
+ psp->sx = ((P_Random() & 3) - 2) * FRACUNIT;
+ psp->sy = WEAPONTOP + (P_Random() & 3) * FRACUNIT;
+ ball = P_SPMAngle(player->mo, MT_MACEFX1, player->mo->angle
+ + (((P_Random() & 7) - 4) << 24));
+ if (ball)
+ {
+ ball->special1 = 16; // tics till dropoff
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MacePL1Check
+//
+//----------------------------------------------------------------------------
+
+void A_MacePL1Check(mobj_t * ball)
+{
+ angle_t angle;
+
+ if (ball->special1 == 0)
+ {
+ return;
+ }
+ ball->special1 -= 4;
+ if (ball->special1 > 0)
+ {
+ return;
+ }
+ ball->special1 = 0;
+ ball->flags2 |= MF2_LOGRAV;
+ angle = ball->angle >> ANGLETOFINESHIFT;
+ ball->momx = FixedMul(7 * FRACUNIT, finecosine[angle]);
+ ball->momy = FixedMul(7 * FRACUNIT, finesine[angle]);
+ ball->momz -= ball->momz >> 1;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MaceBallImpact
+//
+//----------------------------------------------------------------------------
+
+void A_MaceBallImpact(mobj_t * ball)
+{
+ if ((ball->z <= ball->floorz) && (P_HitFloor(ball) != FLOOR_SOLID))
+ { // Landed in some sort of liquid
+ P_RemoveMobj(ball);
+ return;
+ }
+ if ((ball->health != MAGIC_JUNK) && (ball->z <= ball->floorz)
+ && ball->momz)
+ { // Bounce
+ ball->health = MAGIC_JUNK;
+ ball->momz = (ball->momz * 192) >> 8;
+ ball->flags2 &= ~MF2_FLOORBOUNCE;
+ P_SetMobjState(ball, ball->info->spawnstate);
+ S_StartSound(ball, sfx_bounce);
+ }
+ else
+ { // Explode
+ ball->flags |= MF_NOGRAVITY;
+ ball->flags2 &= ~MF2_LOGRAV;
+ S_StartSound(ball, sfx_lobhit);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_MaceBallImpact2
+//
+//----------------------------------------------------------------------------
+
+void A_MaceBallImpact2(mobj_t * ball)
+{
+ mobj_t *tiny;
+ angle_t angle;
+
+ if ((ball->z <= ball->floorz) && (P_HitFloor(ball) != FLOOR_SOLID))
+ { // Landed in some sort of liquid
+ P_RemoveMobj(ball);
+ return;
+ }
+ if ((ball->z != ball->floorz) || (ball->momz < 2 * FRACUNIT))
+ { // Explode
+ ball->momx = ball->momy = ball->momz = 0;
+ ball->flags |= MF_NOGRAVITY;
+ ball->flags2 &= ~(MF2_LOGRAV | MF2_FLOORBOUNCE);
+ }
+ else
+ { // Bounce
+ ball->momz = (ball->momz * 192) >> 8;
+ P_SetMobjState(ball, ball->info->spawnstate);
+
+ tiny = P_SpawnMobj(ball->x, ball->y, ball->z, MT_MACEFX3);
+ angle = ball->angle + ANG90;
+ tiny->target = ball->target;
+ tiny->angle = angle;
+ angle >>= ANGLETOFINESHIFT;
+ tiny->momx = (ball->momx >> 1) + FixedMul(ball->momz - FRACUNIT,
+ finecosine[angle]);
+ tiny->momy = (ball->momy >> 1) + FixedMul(ball->momz - FRACUNIT,
+ finesine[angle]);
+ tiny->momz = ball->momz;
+ P_CheckMissileSpawn(tiny);
+
+ tiny = P_SpawnMobj(ball->x, ball->y, ball->z, MT_MACEFX3);
+ angle = ball->angle - ANG90;
+ tiny->target = ball->target;
+ tiny->angle = angle;
+ angle >>= ANGLETOFINESHIFT;
+ tiny->momx = (ball->momx >> 1) + FixedMul(ball->momz - FRACUNIT,
+ finecosine[angle]);
+ tiny->momy = (ball->momy >> 1) + FixedMul(ball->momz - FRACUNIT,
+ finesine[angle]);
+ tiny->momz = ball->momz;
+ P_CheckMissileSpawn(tiny);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireMacePL2
+//
+//----------------------------------------------------------------------------
+
+void A_FireMacePL2(player_t * player, pspdef_t * psp)
+{
+ mobj_t *mo;
+
+ player->ammo[am_mace] -= deathmatch ? USE_MACE_AMMO_1 : USE_MACE_AMMO_2;
+ mo = P_SpawnPlayerMissile(player->mo, MT_MACEFX4);
+ if (mo)
+ {
+ mo->momx += player->mo->momx;
+ mo->momy += player->mo->momy;
+ mo->momz = 2 * FRACUNIT + ((player->lookdir) << (FRACBITS - 5));
+ if (linetarget)
+ {
+ mo->special1 = (int) linetarget;
+ }
+ }
+ S_StartSound(player->mo, sfx_lobsht);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_DeathBallImpact
+//
+//----------------------------------------------------------------------------
+
+void A_DeathBallImpact(mobj_t * ball)
+{
+ int i;
+ mobj_t *target;
+ angle_t angle;
+ boolean newAngle;
+
+ if ((ball->z <= ball->floorz) && (P_HitFloor(ball) != FLOOR_SOLID))
+ { // Landed in some sort of liquid
+ P_RemoveMobj(ball);
+ return;
+ }
+ if ((ball->z <= ball->floorz) && ball->momz)
+ { // Bounce
+ newAngle = false;
+ target = (mobj_t *) ball->special1;
+ if (target)
+ {
+ if (!(target->flags & MF_SHOOTABLE))
+ { // Target died
+ ball->special1 = 0;
+ }
+ else
+ { // Seek
+ angle = R_PointToAngle2(ball->x, ball->y,
+ target->x, target->y);
+ newAngle = true;
+ }
+ }
+ else
+ { // Find new target
+ angle = 0;
+ for (i = 0; i < 16; i++)
+ {
+ P_AimLineAttack(ball, angle, 10 * 64 * FRACUNIT);
+ if (linetarget && ball->target != linetarget)
+ {
+ ball->special1 = (int) linetarget;
+ angle = R_PointToAngle2(ball->x, ball->y,
+ linetarget->x, linetarget->y);
+ newAngle = true;
+ break;
+ }
+ angle += ANG45 / 2;
+ }
+ }
+ if (newAngle)
+ {
+ ball->angle = angle;
+ angle >>= ANGLETOFINESHIFT;
+ ball->momx = FixedMul(ball->info->speed, finecosine[angle]);
+ ball->momy = FixedMul(ball->info->speed, finesine[angle]);
+ }
+ P_SetMobjState(ball, ball->info->spawnstate);
+ S_StartSound(ball, sfx_pstop);
+ }
+ else
+ { // Explode
+ ball->flags |= MF_NOGRAVITY;
+ ball->flags2 &= ~MF2_LOGRAV;
+ S_StartSound(ball, sfx_phohit);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_SpawnRippers
+//
+//----------------------------------------------------------------------------
+
+void A_SpawnRippers(mobj_t * actor)
+{
+ int i;
+ angle_t angle;
+ mobj_t *ripper;
+
+ for (i = 0; i < 8; i++)
+ {
+ ripper = P_SpawnMobj(actor->x, actor->y, actor->z, MT_RIPPER);
+ angle = i * ANG45;
+ ripper->target = actor->target;
+ ripper->angle = angle;
+ angle >>= ANGLETOFINESHIFT;
+ ripper->momx = FixedMul(ripper->info->speed, finecosine[angle]);
+ ripper->momy = FixedMul(ripper->info->speed, finesine[angle]);
+ P_CheckMissileSpawn(ripper);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireCrossbowPL1
+//
+//----------------------------------------------------------------------------
+
+void A_FireCrossbowPL1(player_t * player, pspdef_t * psp)
+{
+ mobj_t *pmo;
+
+ pmo = player->mo;
+ player->ammo[am_crossbow] -= USE_CBOW_AMMO_1;
+ P_SpawnPlayerMissile(pmo, MT_CRBOWFX1);
+ P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle - (ANG45 / 10));
+ P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle + (ANG45 / 10));
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireCrossbowPL2
+//
+//----------------------------------------------------------------------------
+
+void A_FireCrossbowPL2(player_t * player, pspdef_t * psp)
+{
+ mobj_t *pmo;
+
+ pmo = player->mo;
+ player->ammo[am_crossbow] -=
+ deathmatch ? USE_CBOW_AMMO_1 : USE_CBOW_AMMO_2;
+ P_SpawnPlayerMissile(pmo, MT_CRBOWFX2);
+ P_SPMAngle(pmo, MT_CRBOWFX2, pmo->angle - (ANG45 / 10));
+ P_SPMAngle(pmo, MT_CRBOWFX2, pmo->angle + (ANG45 / 10));
+ P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle - (ANG45 / 5));
+ P_SPMAngle(pmo, MT_CRBOWFX3, pmo->angle + (ANG45 / 5));
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_BoltSpark
+//
+//----------------------------------------------------------------------------
+
+void A_BoltSpark(mobj_t * bolt)
+{
+ mobj_t *spark;
+
+ if (P_Random() > 50)
+ {
+ spark = P_SpawnMobj(bolt->x, bolt->y, bolt->z, MT_CRBOWFX4);
+ spark->x += (P_Random() - P_Random()) << 10;
+ spark->y += (P_Random() - P_Random()) << 10;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireSkullRodPL1
+//
+//----------------------------------------------------------------------------
+
+void A_FireSkullRodPL1(player_t * player, pspdef_t * psp)
+{
+ mobj_t *mo;
+
+ if (player->ammo[am_skullrod] < USE_SKRD_AMMO_1)
+ {
+ return;
+ }
+ player->ammo[am_skullrod] -= USE_SKRD_AMMO_1;
+ mo = P_SpawnPlayerMissile(player->mo, MT_HORNRODFX1);
+ // Randomize the first frame
+ if (mo && P_Random() > 128)
+ {
+ P_SetMobjState(mo, S_HRODFX1_2);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FireSkullRodPL2
+//
+// The special2 field holds the player number that shot the rain missile.
+// The special1 field is used for the seeking routines, then as a counter
+// for the sound looping.
+//
+//----------------------------------------------------------------------------
+
+void A_FireSkullRodPL2(player_t * player, pspdef_t * psp)
+{
+ player->ammo[am_skullrod] -=
+ deathmatch ? USE_SKRD_AMMO_1 : USE_SKRD_AMMO_2;
+ P_SpawnPlayerMissile(player->mo, MT_HORNRODFX2);
+ // Use MissileMobj instead of the return value from
+ // P_SpawnPlayerMissile because we need to give info to the mobj
+ // even if it exploded immediately.
+ if (netgame)
+ { // Multi-player game
+ MissileMobj->special2 = P_GetPlayerNum(player);
+ }
+ else
+ { // Always use red missiles in single player games
+ MissileMobj->special2 = 2;
+ }
+ if (linetarget)
+ {
+ MissileMobj->special1 = (int) linetarget;
+ }
+ S_StartSound(MissileMobj, sfx_hrnpow);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_SkullRodPL2Seek
+//
+//----------------------------------------------------------------------------
+
+void A_SkullRodPL2Seek(mobj_t * actor)
+{
+ P_SeekerMissile(actor, ANG1_X * 10, ANG1_X * 30);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_AddPlayerRain
+//
+//----------------------------------------------------------------------------
+
+void A_AddPlayerRain(mobj_t * actor)
+{
+ int playerNum;
+ player_t *player;
+
+ playerNum = netgame ? actor->special2 : 0;
+ if (!playeringame[playerNum])
+ { // Player left the game
+ return;
+ }
+ player = &players[playerNum];
+ if (player->health <= 0)
+ { // Player is dead
+ return;
+ }
+ if (player->rain1 && player->rain2)
+ { // Terminate an active rain
+ if (player->rain1->health < player->rain2->health)
+ {
+ if (player->rain1->health > 16)
+ {
+ player->rain1->health = 16;
+ }
+ player->rain1 = NULL;
+ }
+ else
+ {
+ if (player->rain2->health > 16)
+ {
+ player->rain2->health = 16;
+ }
+ player->rain2 = NULL;
+ }
+ }
+ // Add rain mobj to list
+ if (player->rain1)
+ {
+ player->rain2 = actor;
+ }
+ else
+ {
+ player->rain1 = actor;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_SkullRodStorm
+//
+//----------------------------------------------------------------------------
+
+void A_SkullRodStorm(mobj_t * actor)
+{
+ fixed_t x;
+ fixed_t y;
+ mobj_t *mo;
+ int playerNum;
+ player_t *player;
+
+ if (actor->health-- == 0)
+ {
+ P_SetMobjState(actor, S_NULL);
+ playerNum = netgame ? actor->special2 : 0;
+ if (!playeringame[playerNum])
+ { // Player left the game
+ return;
+ }
+ player = &players[playerNum];
+ if (player->health <= 0)
+ { // Player is dead
+ return;
+ }
+ if (player->rain1 == actor)
+ {
+ player->rain1 = NULL;
+ }
+ else if (player->rain2 == actor)
+ {
+ player->rain2 = NULL;
+ }
+ return;
+ }
+ if (P_Random() < 25)
+ { // Fudge rain frequency
+ return;
+ }
+ x = actor->x + ((P_Random() & 127) - 64) * FRACUNIT;
+ y = actor->y + ((P_Random() & 127) - 64) * FRACUNIT;
+ mo = P_SpawnMobj(x, y, ONCEILINGZ, MT_RAINPLR1 + actor->special2);
+ mo->target = actor->target;
+ mo->momx = 1; // Force collision detection
+ mo->momz = -mo->info->speed;
+ mo->special2 = actor->special2; // Transfer player number
+ P_CheckMissileSpawn(mo);
+ if (!(actor->special1 & 31))
+ {
+ S_StartSound(actor, sfx_ramrain);
+ }
+ actor->special1++;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_RainImpact
+//
+//----------------------------------------------------------------------------
+
+void A_RainImpact(mobj_t * actor)
+{
+ if (actor->z > actor->floorz)
+ {
+ P_SetMobjState(actor, S_RAINAIRXPLR1_1 + actor->special2);
+ }
+ else if (P_Random() < 40)
+ {
+ P_HitFloor(actor);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_HideInCeiling
+//
+//----------------------------------------------------------------------------
+
+void A_HideInCeiling(mobj_t * actor)
+{
+ actor->z = actor->ceilingz + 4 * FRACUNIT;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FirePhoenixPL1
+//
+//----------------------------------------------------------------------------
+
+void A_FirePhoenixPL1(player_t * player, pspdef_t * psp)
+{
+ angle_t angle;
+
+ player->ammo[am_phoenixrod] -= USE_PHRD_AMMO_1;
+ P_SpawnPlayerMissile(player->mo, MT_PHOENIXFX1);
+ //P_SpawnPlayerMissile(player->mo, MT_MNTRFX2);
+ angle = player->mo->angle + ANG180;
+ angle >>= ANGLETOFINESHIFT;
+ player->mo->momx += FixedMul(4 * FRACUNIT, finecosine[angle]);
+ player->mo->momy += FixedMul(4 * FRACUNIT, finesine[angle]);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_PhoenixPuff
+//
+//----------------------------------------------------------------------------
+
+void A_PhoenixPuff(mobj_t * actor)
+{
+ mobj_t *puff;
+ angle_t angle;
+
+ P_SeekerMissile(actor, ANG1_X * 5, ANG1_X * 10);
+ puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF);
+ angle = actor->angle + ANG90;
+ angle >>= ANGLETOFINESHIFT;
+ puff->momx = FixedMul((fixed_t)(FRACUNIT * 1.3), finecosine[angle]);
+ puff->momy = FixedMul((fixed_t)(FRACUNIT * 1.3), finesine[angle]);
+ puff->momz = 0;
+ puff = P_SpawnMobj(actor->x, actor->y, actor->z, MT_PHOENIXPUFF);
+ angle = actor->angle - ANG90;
+ angle >>= ANGLETOFINESHIFT;
+ puff->momx = FixedMul((fixed_t)(FRACUNIT * 1.3), finecosine[angle]);
+ puff->momy = FixedMul((fixed_t)(FRACUNIT * 1.3), finesine[angle]);
+ puff->momz = 0;
+}
+
+//
+// This function was present in the Heretic 1.0 executable for the
+// removed "secondary phoenix flash" object (MT_PHOENIXFX_REMOVED).
+// The purpose of this object is unknown, as is this function.
+//
+
+void A_RemovedPhoenixFunc(mobj_t *actor)
+{
+ I_Error("Action function invoked for removed Phoenix action!");
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_InitPhoenixPL2
+//
+//----------------------------------------------------------------------------
+
+void A_InitPhoenixPL2(player_t * player, pspdef_t * psp)
+{
+ player->flamecount = FLAME_THROWER_TICS;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FirePhoenixPL2
+//
+// Flame thrower effect.
+//
+//----------------------------------------------------------------------------
+
+void A_FirePhoenixPL2(player_t * player, pspdef_t * psp)
+{
+ mobj_t *mo;
+ mobj_t *pmo;
+ angle_t angle;
+ fixed_t x, y, z;
+ fixed_t slope;
+
+ if (--player->flamecount == 0)
+ { // Out of flame
+ P_SetPsprite(player, ps_weapon, S_PHOENIXATK2_4);
+ player->refire = 0;
+ return;
+ }
+ pmo = player->mo;
+ angle = pmo->angle;
+ x = pmo->x + ((P_Random() - P_Random()) << 9);
+ y = pmo->y + ((P_Random() - P_Random()) << 9);
+ z = pmo->z + 26 * FRACUNIT + ((player->lookdir) << FRACBITS) / 173;
+ if (pmo->flags2 & MF2_FEETARECLIPPED)
+ {
+ z -= FOOTCLIPSIZE;
+ }
+ slope = ((player->lookdir) << FRACBITS) / 173 + (FRACUNIT / 10);
+ mo = P_SpawnMobj(x, y, z, MT_PHOENIXFX2);
+ mo->target = pmo;
+ mo->angle = angle;
+ mo->momx = pmo->momx + FixedMul(mo->info->speed,
+ finecosine[angle >> ANGLETOFINESHIFT]);
+ mo->momy = pmo->momy + FixedMul(mo->info->speed,
+ finesine[angle >> ANGLETOFINESHIFT]);
+ mo->momz = FixedMul(mo->info->speed, slope);
+ if (!player->refire || !(leveltime % 38))
+ {
+ S_StartSound(player->mo, sfx_phopow);
+ }
+ P_CheckMissileSpawn(mo);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_ShutdownPhoenixPL2
+//
+//----------------------------------------------------------------------------
+
+void A_ShutdownPhoenixPL2(player_t * player, pspdef_t * psp)
+{
+ player->ammo[am_phoenixrod] -= USE_PHRD_AMMO_2;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FlameEnd
+//
+//----------------------------------------------------------------------------
+
+void A_FlameEnd(mobj_t * actor)
+{
+ actor->momz += (fixed_t)(1.5 * FRACUNIT);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC A_FloatPuff
+//
+//----------------------------------------------------------------------------
+
+void A_FloatPuff(mobj_t * puff)
+{
+ puff->momz += (fixed_t)(1.8 * FRACUNIT);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC A_GauntletAttack
+//
+//---------------------------------------------------------------------------
+
+void A_GauntletAttack(player_t * player, pspdef_t * psp)
+{
+ angle_t angle;
+ int damage;
+ int slope;
+ int randVal;
+ fixed_t dist;
+
+ psp->sx = ((P_Random() & 3) - 2) * FRACUNIT;
+ psp->sy = WEAPONTOP + (P_Random() & 3) * FRACUNIT;
+ angle = player->mo->angle;
+ if (player->powers[pw_weaponlevel2])
+ {
+ damage = HITDICE(2);
+ dist = 4 * MELEERANGE;
+ angle += (P_Random() - P_Random()) << 17;
+ PuffType = MT_GAUNTLETPUFF2;
+ }
+ else
+ {
+ damage = HITDICE(2);
+ dist = MELEERANGE + 1;
+ angle += (P_Random() - P_Random()) << 18;
+ PuffType = MT_GAUNTLETPUFF1;
+ }
+ slope = P_AimLineAttack(player->mo, angle, dist);
+ P_LineAttack(player->mo, angle, dist, slope, damage);
+ if (!linetarget)
+ {
+ if (P_Random() > 64)
+ {
+ player->extralight = !player->extralight;
+ }
+ S_StartSound(player->mo, sfx_gntful);
+ return;
+ }
+ randVal = P_Random();
+ if (randVal < 64)
+ {
+ player->extralight = 0;
+ }
+ else if (randVal < 160)
+ {
+ player->extralight = 1;
+ }
+ else
+ {
+ player->extralight = 2;
+ }
+ if (player->powers[pw_weaponlevel2])
+ {
+ P_GiveBody(player, damage >> 1);
+ S_StartSound(player->mo, sfx_gntpow);
+ }
+ else
+ {
+ S_StartSound(player->mo, sfx_gnthit);
+ }
+ // 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;
+}
+
+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;
+}
+
+//------------------------------------------------------------------------
+//
+// PROC P_SetupPsprites
+//
+// Called at start of level for each player
+//
+//------------------------------------------------------------------------
+
+void P_SetupPsprites(player_t * player)
+{
+ int i;
+
+ // Remove all psprites
+ for (i = 0; i < NUMPSPRITES; i++)
+ {
+ player->psprites[i].state = NULL;
+ }
+ // Spawn the ready weapon
+ player->pendingweapon = player->readyweapon;
+ P_BringUpWeapon(player);
+}
+
+//------------------------------------------------------------------------
+//
+// PROC P_MovePsprites
+//
+// Called every tic by player thinking routine
+//
+//------------------------------------------------------------------------
+
+void P_MovePsprites(player_t * player)
+{
+ int i;
+ pspdef_t *psp;
+ state_t *state;
+
+ psp = &player->psprites[0];
+ for (i = 0; i < NUMPSPRITES; i++, psp++)
+ {
+ if ((state = psp->state) != 0) // a null state means not active
+ {
+ // drop tic count and possibly change state
+ if (psp->tics != -1) // a -1 tic count never changes
+ {
+ psp->tics--;
+ if (!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/heretic/p_setup.c b/src/heretic/p_setup.c
new file mode 100644
index 00000000..a3bd2912
--- /dev/null
+++ b/src/heretic/p_setup.c
@@ -0,0 +1,660 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_main.c
+
+#include <math.h>
+#include <stdlib.h>
+
+#include "doomdef.h"
+#include "i_swap.h"
+#include "i_system.h"
+#include "m_argv.h"
+#include "m_bbox.h"
+#include "p_local.h"
+#include "s_sound.h"
+
+void P_SpawnMapThing(mapthing_t * mthing);
+
+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;
+
+short *blockmaplump; // offsets in blockmap are from here
+short *blockmap;
+int bmapwidth, bmapheight; // in mapblocks
+fixed_t bmaporgx, bmaporgy; // origin of block map
+mobj_t **blocklinks; // for thing chains
+
+byte *rejectmatrix; // for fast sight rejection
+
+mapthing_t deathmatchstarts[10], *deathmatch_p;
+mapthing_t playerstarts[MAXPLAYERS];
+
+/*
+=================
+=
+= P_LoadVertexes
+=
+=================
+*/
+
+void P_LoadVertexes(int lump)
+{
+ byte *data;
+ int i;
+ mapvertex_t *ml;
+ vertex_t *li;
+
+ numvertexes = W_LumpLength(lump) / sizeof(mapvertex_t);
+ vertexes = Z_Malloc(numvertexes * sizeof(vertex_t), PU_LEVEL, 0);
+ data = W_CacheLumpNum(lump, PU_STATIC);
+
+ ml = (mapvertex_t *) data;
+ li = vertexes;
+ for (i = 0; i < numvertexes; i++, li++, ml++)
+ {
+ li->x = SHORT(ml->x) << FRACBITS;
+ li->y = SHORT(ml->y) << FRACBITS;
+ }
+
+ W_ReleaseLumpNum(lump);
+}
+
+
+/*
+=================
+=
+= P_LoadSegs
+=
+=================
+*/
+
+void P_LoadSegs(int lump)
+{
+ byte *data;
+ int i;
+ mapseg_t *ml;
+ seg_t *li;
+ line_t *ldef;
+ int linedef, side;
+
+ numsegs = W_LumpLength(lump) / sizeof(mapseg_t);
+ segs = Z_Malloc(numsegs * sizeof(seg_t), PU_LEVEL, 0);
+ memset(segs, 0, numsegs * sizeof(seg_t));
+ data = W_CacheLumpNum(lump, PU_STATIC);
+
+ ml = (mapseg_t *) data;
+ li = segs;
+ for (i = 0; i < numsegs; i++, li++, ml++)
+ {
+ li->v1 = &vertexes[SHORT(ml->v1)];
+ li->v2 = &vertexes[SHORT(ml->v2)];
+
+ li->angle = (SHORT(ml->angle)) << 16;
+ li->offset = (SHORT(ml->offset)) << 16;
+ linedef = SHORT(ml->linedef);
+ ldef = &lines[linedef];
+ li->linedef = ldef;
+ side = SHORT(ml->side);
+ li->sidedef = &sides[ldef->sidenum[side]];
+ li->frontsector = sides[ldef->sidenum[side]].sector;
+ if (ldef->flags & ML_TWOSIDED)
+ li->backsector = sides[ldef->sidenum[side ^ 1]].sector;
+ else
+ li->backsector = 0;
+ }
+
+ W_ReleaseLumpNum(lump);
+}
+
+
+/*
+=================
+=
+= P_LoadSubsectors
+=
+=================
+*/
+
+void P_LoadSubsectors(int lump)
+{
+ byte *data;
+ int i;
+ mapsubsector_t *ms;
+ subsector_t *ss;
+
+ numsubsectors = W_LumpLength(lump) / sizeof(mapsubsector_t);
+ subsectors = Z_Malloc(numsubsectors * sizeof(subsector_t), PU_LEVEL, 0);
+ data = W_CacheLumpNum(lump, PU_STATIC);
+
+ ms = (mapsubsector_t *) data;
+ memset(subsectors, 0, numsubsectors * sizeof(subsector_t));
+ ss = subsectors;
+ for (i = 0; i < numsubsectors; i++, ss++, ms++)
+ {
+ ss->numlines = SHORT(ms->numsegs);
+ ss->firstline = SHORT(ms->firstseg);
+ }
+
+ W_ReleaseLumpNum(lump);
+}
+
+
+/*
+=================
+=
+= P_LoadSectors
+=
+=================
+*/
+
+void P_LoadSectors(int lump)
+{
+ byte *data;
+ int i;
+ mapsector_t *ms;
+ sector_t *ss;
+
+ numsectors = W_LumpLength(lump) / sizeof(mapsector_t);
+ sectors = Z_Malloc(numsectors * sizeof(sector_t), PU_LEVEL, 0);
+ memset(sectors, 0, numsectors * sizeof(sector_t));
+ data = W_CacheLumpNum(lump, PU_STATIC);
+
+ ms = (mapsector_t *) data;
+ ss = sectors;
+ for (i = 0; i < numsectors; i++, ss++, ms++)
+ {
+ ss->floorheight = SHORT(ms->floorheight) << FRACBITS;
+ ss->ceilingheight = SHORT(ms->ceilingheight) << FRACBITS;
+ ss->floorpic = R_FlatNumForName(ms->floorpic);
+ ss->ceilingpic = R_FlatNumForName(ms->ceilingpic);
+ ss->lightlevel = SHORT(ms->lightlevel);
+ ss->special = SHORT(ms->special);
+ ss->tag = SHORT(ms->tag);
+ ss->thinglist = NULL;
+ }
+
+ W_ReleaseLumpNum(lump);
+}
+
+
+/*
+=================
+=
+= P_LoadNodes
+=
+=================
+*/
+
+void P_LoadNodes(int lump)
+{
+ byte *data;
+ int i, j, k;
+ mapnode_t *mn;
+ node_t *no;
+
+ numnodes = W_LumpLength(lump) / sizeof(mapnode_t);
+ nodes = Z_Malloc(numnodes * sizeof(node_t), PU_LEVEL, 0);
+ data = W_CacheLumpNum(lump, PU_STATIC);
+
+ mn = (mapnode_t *) data;
+ no = nodes;
+ for (i = 0; i < numnodes; i++, no++, mn++)
+ {
+ no->x = SHORT(mn->x) << FRACBITS;
+ no->y = SHORT(mn->y) << FRACBITS;
+ no->dx = SHORT(mn->dx) << FRACBITS;
+ no->dy = SHORT(mn->dy) << FRACBITS;
+ for (j = 0; j < 2; j++)
+ {
+ no->children[j] = SHORT(mn->children[j]);
+ for (k = 0; k < 4; k++)
+ no->bbox[j][k] = SHORT(mn->bbox[j][k]) << FRACBITS;
+ }
+ }
+
+ W_ReleaseLumpNum(lump);
+}
+
+
+
+/*
+=================
+=
+= P_LoadThings
+=
+=================
+*/
+
+void P_LoadThings(int lump)
+{
+ byte *data;
+ int i;
+ mapthing_t spawnthing;
+ mapthing_t *mt;
+ int numthings;
+
+ data = W_CacheLumpNum(lump, PU_STATIC);
+ numthings = W_LumpLength(lump) / sizeof(mapthing_t);
+
+ mt = (mapthing_t *) data;
+ for (i = 0; i < numthings; i++, mt++)
+ {
+ spawnthing.x = SHORT(mt->x);
+ spawnthing.y = SHORT(mt->y);
+ spawnthing.angle = SHORT(mt->angle);
+ spawnthing.type = SHORT(mt->type);
+ spawnthing.options = SHORT(mt->options);
+ P_SpawnMapThing(&spawnthing);
+ }
+
+ W_ReleaseLumpNum(lump);
+}
+
+
+
+/*
+=================
+=
+= P_LoadLineDefs
+=
+= Also counts secret lines for intermissions
+=================
+*/
+
+void P_LoadLineDefs(int lump)
+{
+ byte *data;
+ int i;
+ maplinedef_t *mld;
+ line_t *ld;
+ vertex_t *v1, *v2;
+
+ numlines = W_LumpLength(lump) / sizeof(maplinedef_t);
+ lines = Z_Malloc(numlines * sizeof(line_t), PU_LEVEL, 0);
+ memset(lines, 0, numlines * sizeof(line_t));
+ data = W_CacheLumpNum(lump, PU_STATIC);
+
+ mld = (maplinedef_t *) data;
+ ld = lines;
+ for (i = 0; i < numlines; i++, mld++, ld++)
+ {
+ ld->flags = SHORT(mld->flags);
+ ld->special = SHORT(mld->special);
+ ld->tag = SHORT(mld->tag);
+ v1 = ld->v1 = &vertexes[SHORT(mld->v1)];
+ v2 = ld->v2 = &vertexes[SHORT(mld->v2)];
+ ld->dx = v2->x - v1->x;
+ ld->dy = v2->y - v1->y;
+ if (!ld->dx)
+ ld->slopetype = ST_VERTICAL;
+ else if (!ld->dy)
+ ld->slopetype = ST_HORIZONTAL;
+ else
+ {
+ if (FixedDiv(ld->dy, ld->dx) > 0)
+ ld->slopetype = ST_POSITIVE;
+ else
+ ld->slopetype = 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;
+ }
+ ld->sidenum[0] = SHORT(mld->sidenum[0]);
+ ld->sidenum[1] = SHORT(mld->sidenum[1]);
+ if (ld->sidenum[0] != -1)
+ ld->frontsector = sides[ld->sidenum[0]].sector;
+ else
+ ld->frontsector = 0;
+ if (ld->sidenum[1] != -1)
+ ld->backsector = sides[ld->sidenum[1]].sector;
+ else
+ ld->backsector = 0;
+ }
+
+ W_ReleaseLumpNum(lump);
+}
+
+
+/*
+=================
+=
+= P_LoadSideDefs
+=
+=================
+*/
+
+void P_LoadSideDefs(int lump)
+{
+ byte *data;
+ int i;
+ mapsidedef_t *msd;
+ side_t *sd;
+
+ numsides = W_LumpLength(lump) / sizeof(mapsidedef_t);
+ sides = Z_Malloc(numsides * sizeof(side_t), PU_LEVEL, 0);
+ memset(sides, 0, numsides * sizeof(side_t));
+ data = W_CacheLumpNum(lump, PU_STATIC);
+
+ msd = (mapsidedef_t *) data;
+ sd = sides;
+ for (i = 0; i < numsides; i++, msd++, sd++)
+ {
+ sd->textureoffset = SHORT(msd->textureoffset) << FRACBITS;
+ sd->rowoffset = SHORT(msd->rowoffset) << FRACBITS;
+ sd->toptexture = R_TextureNumForName(msd->toptexture);
+ sd->bottomtexture = R_TextureNumForName(msd->bottomtexture);
+ sd->midtexture = R_TextureNumForName(msd->midtexture);
+ sd->sector = &sectors[SHORT(msd->sector)];
+ }
+
+ W_ReleaseLumpNum(lump);
+}
+
+
+
+/*
+=================
+=
+= P_LoadBlockMap
+=
+=================
+*/
+
+void P_LoadBlockMap(int lump)
+{
+ int i, count;
+ int lumplen;
+
+ lumplen = W_LumpLength(lump);
+
+ blockmaplump = Z_Malloc(lumplen, PU_LEVEL, NULL);
+ W_ReadLump(lump, blockmaplump);
+ blockmap = blockmaplump + 4;
+
+ // Swap all short integers to native byte ordering:
+
+ count = lumplen / 2;
+ for (i = 0; i < count; i++)
+ blockmaplump[i] = SHORT(blockmaplump[i]);
+
+ bmaporgx = blockmaplump[0] << FRACBITS;
+ bmaporgy = blockmaplump[1] << FRACBITS;
+ bmapwidth = blockmaplump[2];
+ bmapheight = blockmaplump[3];
+
+// clear out mobj chains
+ count = sizeof(*blocklinks) * bmapwidth * bmapheight;
+ blocklinks = Z_Malloc(count, PU_LEVEL, 0);
+ memset(blocklinks, 0, count);
+}
+
+
+
+
+/*
+=================
+=
+= P_GroupLines
+=
+= Builds sector line lists and subsector sector numbers
+= Finds block bounding boxes for sectors
+=================
+*/
+
+void P_GroupLines(void)
+{
+ line_t **linebuffer;
+ int i, j, total;
+ line_t *li;
+ sector_t *sector;
+ subsector_t *ss;
+ seg_t *seg;
+ fixed_t bbox[4];
+ int block;
+
+// look up sector number for each subsector
+ ss = subsectors;
+ for (i = 0; i < numsubsectors; i++, ss++)
+ {
+ seg = &segs[ss->firstline];
+ ss->sector = seg->sidedef->sector;
+ }
+
+// count number of lines in each sector
+ li = lines;
+ total = 0;
+ for (i = 0; i < numlines; i++, li++)
+ {
+ total++;
+ li->frontsector->linecount++;
+ if (li->backsector && li->backsector != li->frontsector)
+ {
+ li->backsector->linecount++;
+ total++;
+ }
+ }
+
+// build line tables for each sector
+ linebuffer = Z_Malloc(total * sizeof(line_t *), PU_LEVEL, 0);
+ sector = sectors;
+ for (i = 0; i < numsectors; i++, sector++)
+ {
+ M_ClearBox(bbox);
+ sector->lines = linebuffer;
+ li = lines;
+ for (j = 0; j < numlines; j++, li++)
+ {
+ if (li->frontsector == sector || li->backsector == sector)
+ {
+ *linebuffer++ = li;
+ M_AddToBox(bbox, li->v1->x, li->v1->y);
+ M_AddToBox(bbox, li->v2->x, li->v2->y);
+ }
+ }
+ if (linebuffer - sector->lines != sector->linecount)
+ I_Error("P_GroupLines: miscounted");
+
+ // set the degenmobj_t to the middle of the bounding box
+ sector->soundorg.x = (bbox[BOXRIGHT] + bbox[BOXLEFT]) / 2;
+ sector->soundorg.y = (bbox[BOXTOP] + 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;
+ }
+
+}
+
+//=============================================================================
+
+
+/*
+=================
+=
+= P_SetupLevel
+=
+=================
+*/
+
+void P_SetupLevel(int episode, int map, int playermask, skill_t skill)
+{
+ int i;
+ int parm;
+ char lumpname[9];
+ int lumpnum;
+ mobj_t *mobj;
+
+ totalkills = totalitems = totalsecret = 0;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ players[i].killcount = players[i].secretcount
+ = players[i].itemcount = 0;
+ }
+ players[consoleplayer].viewz = 1; // will be set by player think
+
+ S_Start(); // make sure all sounds are stopped before Z_FreeTags
+
+ Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1);
+
+ P_InitThinkers();
+
+//
+// look for a regular (development) map first
+//
+ lumpname[0] = 'E';
+ lumpname[1] = '0' + episode;
+ lumpname[2] = 'M';
+ lumpname[3] = '0' + map;
+ lumpname[4] = 0;
+ leveltime = 0;
+
+ lumpnum = W_GetNumForName(lumpname);
+
+// note: most of this ordering is important
+ P_LoadBlockMap(lumpnum + ML_BLOCKMAP);
+ P_LoadVertexes(lumpnum + ML_VERTEXES);
+ P_LoadSectors(lumpnum + ML_SECTORS);
+ P_LoadSideDefs(lumpnum + ML_SIDEDEFS);
+
+ P_LoadLineDefs(lumpnum + ML_LINEDEFS);
+ P_LoadSubsectors(lumpnum + ML_SSECTORS);
+ P_LoadNodes(lumpnum + ML_NODES);
+ P_LoadSegs(lumpnum + ML_SEGS);
+
+ rejectmatrix = W_CacheLumpNum(lumpnum + ML_REJECT, PU_LEVEL);
+ P_GroupLines();
+
+ bodyqueslot = 0;
+ deathmatch_p = deathmatchstarts;
+ P_InitAmbientSound();
+ P_InitMonsters();
+ P_OpenWeapons();
+ P_LoadThings(lumpnum + ML_THINGS);
+ P_CloseWeapons();
+
+//
+// if deathmatch, randomly spawn the active players
+//
+ TimerGame = 0;
+ if (deathmatch)
+ {
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i])
+ { // must give a player spot before deathmatchspawn
+ mobj = P_SpawnMobj(playerstarts[i].x << 16,
+ playerstarts[i].y << 16, 0, MT_PLAYER);
+ players[i].mo = mobj;
+ G_DeathMatchSpawnPlayer(i);
+ P_RemoveMobj(mobj);
+ }
+ }
+ parm = M_CheckParm("-timer");
+ if (parm && parm < myargc - 1)
+ {
+ TimerGame = atoi(myargv[parm + 1]) * 35 * 60;
+ }
+ }
+
+// set up world state
+ P_SpawnSpecials();
+
+// build subsector connect matrix
+// P_ConnectSubsectors ();
+
+// preload graphics
+ if (precache)
+ R_PrecacheLevel();
+
+//printf ("free memory: 0x%x\n", Z_FreeMemory());
+
+}
+
+
+/*
+=================
+=
+= P_Init
+=
+=================
+*/
+
+void P_Init(void)
+{
+ P_InitSwitchList();
+ P_InitPicAnims();
+ P_InitTerrainTypes();
+ P_InitLava();
+ R_InitSprites(sprnames);
+}
diff --git a/src/heretic/p_sight.c b/src/heretic/p_sight.c
new file mode 100644
index 00000000..002b32a9
--- /dev/null
+++ b/src/heretic/p_sight.c
@@ -0,0 +1,363 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+// P_sight.c
+
+#include <stdlib.h>
+
+#include "doomdef.h"
+#include "p_local.h"
+
+/*
+==============================================================================
+
+ P_CheckSight
+
+This uses specialized forms of the maputils routines for optimized performance
+
+==============================================================================
+*/
+
+fixed_t sightzstart; // eye z of looker
+fixed_t topslope, bottomslope; // slopes to top and bottom of target
+
+int sightcounts[3];
+
+/*
+==============
+=
+= PTR_SightTraverse
+=
+==============
+*/
+
+boolean PTR_SightTraverse(intercept_t * in)
+{
+ line_t *li;
+ fixed_t slope;
+
+ li = in->d.line;
+
+//
+// crosses a two sided line
+//
+ P_LineOpening(li);
+
+ if (openbottom >= opentop) // quick test for totally closed doors
+ return false; // stop
+
+ if (li->frontsector->floorheight != li->backsector->floorheight)
+ {
+ slope = FixedDiv(openbottom - sightzstart, in->frac);
+ if (slope > bottomslope)
+ bottomslope = slope;
+ }
+
+ if (li->frontsector->ceilingheight != li->backsector->ceilingheight)
+ {
+ slope = FixedDiv(opentop - sightzstart, in->frac);
+ if (slope < topslope)
+ topslope = slope;
+ }
+
+ if (topslope <= bottomslope)
+ return false; // stop
+
+ return true; // keep going
+}
+
+
+
+/*
+==================
+=
+= P_SightBlockLinesIterator
+=
+===================
+*/
+
+boolean P_SightBlockLinesIterator(int x, int y)
+{
+ int offset;
+ short *list;
+ line_t *ld;
+ int s1, s2;
+ divline_t dl;
+
+ offset = y * bmapwidth + x;
+
+ offset = *(blockmap + offset);
+
+ for (list = blockmaplump + offset; *list != -1; list++)
+ {
+ ld = &lines[*list];
+ if (ld->validcount == validcount)
+ continue; // line has already been checked
+ ld->validcount = validcount;
+
+ s1 = P_PointOnDivlineSide(ld->v1->x, ld->v1->y, &trace);
+ s2 = P_PointOnDivlineSide(ld->v2->x, ld->v2->y, &trace);
+ if (s1 == s2)
+ continue; // line isn't crossed
+ P_MakeDivline(ld, &dl);
+ s1 = P_PointOnDivlineSide(trace.x, trace.y, &dl);
+ s2 = P_PointOnDivlineSide(trace.x + trace.dx, trace.y + trace.dy,
+ &dl);
+ if (s1 == s2)
+ continue; // line isn't crossed
+
+ // try to early out the check
+ if (!ld->backsector)
+ return false; // stop checking
+
+ // store the line for later intersection testing
+ intercept_p->d.line = ld;
+ intercept_p++;
+
+ }
+
+ return true; // everything was checked
+}
+
+/*
+====================
+=
+= P_SightTraverseIntercepts
+=
+= Returns true if the traverser function returns true for all lines
+====================
+*/
+
+boolean P_SightTraverseIntercepts(void)
+{
+ int count;
+ fixed_t dist;
+ intercept_t *scan, *in;
+ divline_t dl;
+
+ count = intercept_p - intercepts;
+//
+// calculate intercept distance
+//
+ for (scan = intercepts; scan < intercept_p; scan++)
+ {
+ P_MakeDivline(scan->d.line, &dl);
+ scan->frac = P_InterceptVector(&trace, &dl);
+ }
+
+//
+// go through in order
+//
+ in = 0; // shut up compiler warning
+
+ while (count--)
+ {
+ dist = INT_MAX;
+ for (scan = intercepts; scan < intercept_p; scan++)
+ if (scan->frac < dist)
+ {
+ dist = scan->frac;
+ in = scan;
+ }
+
+ if (!PTR_SightTraverse(in))
+ return false; // don't bother going farther
+ in->frac = INT_MAX;
+ }
+
+ return true; // everything was traversed
+}
+
+
+
+/*
+==================
+=
+= P_SightPathTraverse
+=
+= 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
+==================
+*/
+
+boolean P_SightPathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2)
+{
+ fixed_t xt1, yt1, xt2, yt2;
+ fixed_t xstep, ystep;
+ fixed_t partial;
+ fixed_t xintercept, yintercept;
+ int mapx, mapy, mapxstep, mapystep;
+ int count;
+
+ validcount++;
+ intercept_p = intercepts;
+
+ if (((x1 - bmaporgx) & (MAPBLOCKSIZE - 1)) == 0)
+ x1 += FRACUNIT; // don't side exactly on a line
+ if (((y1 - bmaporgy) & (MAPBLOCKSIZE - 1)) == 0)
+ 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;
+
+// points should never be out of bounds, but check once instead of
+// each block
+ if (xt1 < 0 || yt1 < 0 || xt1 >= bmapwidth || yt1 >= bmapheight
+ || xt2 < 0 || yt2 < 0 || xt2 >= bmapwidth || yt2 >= bmapheight)
+ return false;
+
+ if (xt2 > xt1)
+ {
+ mapxstep = 1;
+ partial = FRACUNIT - ((x1 >> MAPBTOFRAC) & (FRACUNIT - 1));
+ ystep = FixedDiv(y2 - y1, abs(x2 - x1));
+ }
+ else if (xt2 < xt1)
+ {
+ mapxstep = -1;
+ partial = (x1 >> MAPBTOFRAC) & (FRACUNIT - 1);
+ ystep = FixedDiv(y2 - y1, 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, abs(y2 - y1));
+ }
+ else if (yt2 < yt1)
+ {
+ mapystep = -1;
+ partial = (y1 >> MAPBTOFRAC) & (FRACUNIT - 1);
+ xstep = FixedDiv(x2 - x1, 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 (!P_SightBlockLinesIterator(mapx, mapy))
+ {
+ sightcounts[1]++;
+ 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;
+ }
+
+ }
+
+
+//
+// couldn't early out, so go through the sorted list
+//
+ sightcounts[2]++;
+
+ return P_SightTraverseIntercepts();
+}
+
+
+
+/*
+=====================
+=
+= P_CheckSight
+=
+= Returns true if a straight line between t1 and t2 is unobstructed
+= look from eyes of t1 to any part of t2
+=
+=====================
+*/
+
+boolean P_CheckSight(mobj_t * t1, mobj_t * t2)
+{
+ int s1, s2;
+ int pnum, bytenum, bitnum;
+
+//
+// check for trivial rejection
+//
+ s1 = (t1->subsector->sector - sectors);
+ s2 = (t2->subsector->sector - sectors);
+ pnum = s1 * numsectors + s2;
+ bytenum = pnum >> 3;
+ bitnum = 1 << (pnum & 7);
+
+ if (rejectmatrix[bytenum] & bitnum)
+ {
+ sightcounts[0]++;
+ return false; // can't possibly be connected
+ }
+
+//
+// check precisely
+//
+ sightzstart = t1->z + t1->height - (t1->height >> 2);
+ topslope = (t2->z + t2->height) - sightzstart;
+ bottomslope = (t2->z) - sightzstart;
+
+ return P_SightPathTraverse(t1->x, t1->y, t2->x, t2->y);
+}
diff --git a/src/heretic/p_spec.c b/src/heretic/p_spec.c
new file mode 100644
index 00000000..49c067fa
--- /dev/null
+++ b/src/heretic/p_spec.c
@@ -0,0 +1,1308 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_Spec.c
+
+#include "doomdef.h"
+#include "deh_str.h"
+#include "i_system.h"
+#include "i_timer.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+// Macros
+
+#define MAX_AMBIENT_SFX 8 // Per level
+
+// Types
+
+typedef enum
+{
+ afxcmd_play, // (sound)
+ afxcmd_playabsvol, // (sound, volume)
+ afxcmd_playrelvol, // (sound, volume)
+ afxcmd_delay, // (ticks)
+ afxcmd_delayrand, // (andbits)
+ afxcmd_end // ()
+} afxcmd_t;
+
+// Data
+
+int *LevelAmbientSfx[MAX_AMBIENT_SFX];
+int *AmbSfxPtr;
+int AmbSfxCount;
+int AmbSfxTics;
+int AmbSfxVolume;
+
+int AmbSndSeqInit[] = { // Startup
+ afxcmd_end
+};
+int AmbSndSeq1[] = { // Scream
+ afxcmd_play, sfx_amb1,
+ afxcmd_end
+};
+int AmbSndSeq2[] = { // Squish
+ afxcmd_play, sfx_amb2,
+ afxcmd_end
+};
+int AmbSndSeq3[] = { // Drops
+ afxcmd_play, sfx_amb3,
+ afxcmd_delay, 16,
+ afxcmd_delayrand, 31,
+ afxcmd_play, sfx_amb7,
+ afxcmd_delay, 16,
+ afxcmd_delayrand, 31,
+ afxcmd_play, sfx_amb3,
+ afxcmd_delay, 16,
+ afxcmd_delayrand, 31,
+ afxcmd_play, sfx_amb7,
+ afxcmd_delay, 16,
+ afxcmd_delayrand, 31,
+ afxcmd_play, sfx_amb3,
+ afxcmd_delay, 16,
+ afxcmd_delayrand, 31,
+ afxcmd_play, sfx_amb7,
+ afxcmd_delay, 16,
+ afxcmd_delayrand, 31,
+ afxcmd_end
+};
+int AmbSndSeq4[] = { // SlowFootSteps
+ afxcmd_play, sfx_amb4,
+ afxcmd_delay, 15,
+ afxcmd_playrelvol, sfx_amb11, -3,
+ afxcmd_delay, 15,
+ afxcmd_playrelvol, sfx_amb4, -3,
+ afxcmd_delay, 15,
+ afxcmd_playrelvol, sfx_amb11, -3,
+ afxcmd_delay, 15,
+ afxcmd_playrelvol, sfx_amb4, -3,
+ afxcmd_delay, 15,
+ afxcmd_playrelvol, sfx_amb11, -3,
+ afxcmd_delay, 15,
+ afxcmd_playrelvol, sfx_amb4, -3,
+ afxcmd_delay, 15,
+ afxcmd_playrelvol, sfx_amb11, -3,
+ afxcmd_end
+};
+int AmbSndSeq5[] = { // Heartbeat
+ afxcmd_play, sfx_amb5,
+ afxcmd_delay, 35,
+ afxcmd_play, sfx_amb5,
+ afxcmd_delay, 35,
+ afxcmd_play, sfx_amb5,
+ afxcmd_delay, 35,
+ afxcmd_play, sfx_amb5,
+ afxcmd_end
+};
+int AmbSndSeq6[] = { // Bells
+ afxcmd_play, sfx_amb6,
+ afxcmd_delay, 17,
+ afxcmd_playrelvol, sfx_amb6, -8,
+ afxcmd_delay, 17,
+ afxcmd_playrelvol, sfx_amb6, -8,
+ afxcmd_delay, 17,
+ afxcmd_playrelvol, sfx_amb6, -8,
+ afxcmd_end
+};
+int AmbSndSeq7[] = { // Growl
+ afxcmd_play, sfx_bstsit,
+ afxcmd_end
+};
+int AmbSndSeq8[] = { // Magic
+ afxcmd_play, sfx_amb8,
+ afxcmd_end
+};
+int AmbSndSeq9[] = { // Laughter
+ afxcmd_play, sfx_amb9,
+ afxcmd_delay, 16,
+ afxcmd_playrelvol, sfx_amb9, -4,
+ afxcmd_delay, 16,
+ afxcmd_playrelvol, sfx_amb9, -4,
+ afxcmd_delay, 16,
+ afxcmd_playrelvol, sfx_amb10, -4,
+ afxcmd_delay, 16,
+ afxcmd_playrelvol, sfx_amb10, -4,
+ afxcmd_delay, 16,
+ afxcmd_playrelvol, sfx_amb10, -4,
+ afxcmd_end
+};
+int AmbSndSeq10[] = { // FastFootsteps
+ afxcmd_play, sfx_amb4,
+ afxcmd_delay, 8,
+ afxcmd_playrelvol, sfx_amb11, -3,
+ afxcmd_delay, 8,
+ afxcmd_playrelvol, sfx_amb4, -3,
+ afxcmd_delay, 8,
+ afxcmd_playrelvol, sfx_amb11, -3,
+ afxcmd_delay, 8,
+ afxcmd_playrelvol, sfx_amb4, -3,
+ afxcmd_delay, 8,
+ afxcmd_playrelvol, sfx_amb11, -3,
+ afxcmd_delay, 8,
+ afxcmd_playrelvol, sfx_amb4, -3,
+ afxcmd_delay, 8,
+ afxcmd_playrelvol, sfx_amb11, -3,
+ afxcmd_end
+};
+
+int *AmbientSfx[] = {
+ AmbSndSeq1, // Scream
+ AmbSndSeq2, // Squish
+ AmbSndSeq3, // Drops
+ AmbSndSeq4, // SlowFootsteps
+ AmbSndSeq5, // Heartbeat
+ AmbSndSeq6, // Bells
+ AmbSndSeq7, // Growl
+ AmbSndSeq8, // Magic
+ AmbSndSeq9, // Laughter
+ AmbSndSeq10 // FastFootsteps
+};
+
+animdef_t animdefs[] = {
+ // false = flat
+ // true = texture
+ {false, "FLTWAWA3", "FLTWAWA1", 8}, // Water
+ {false, "FLTSLUD3", "FLTSLUD1", 8}, // Sludge
+ {false, "FLTTELE4", "FLTTELE1", 6}, // Teleport
+ {false, "FLTFLWW3", "FLTFLWW1", 9}, // River - West
+ {false, "FLTLAVA4", "FLTLAVA1", 8}, // Lava
+ {false, "FLATHUH4", "FLATHUH1", 8}, // Super Lava
+ {true, "LAVAFL3", "LAVAFL1", 6}, // Texture: Lavaflow
+ {true, "WATRWAL3", "WATRWAL1", 4}, // Texture: Waterfall
+ {-1}
+};
+
+anim_t anims[MAXANIMS];
+anim_t *lastanim;
+
+int *TerrainTypes;
+struct
+{
+ char *name;
+ int type;
+} TerrainTypeDefs[] =
+{
+ { "FLTWAWA1", FLOOR_WATER },
+ { "FLTFLWW1", FLOOR_WATER },
+ { "FLTLAVA1", FLOOR_LAVA },
+ { "FLATHUH1", FLOOR_LAVA },
+ { "FLTSLUD1", FLOOR_SLUDGE },
+ { "END", -1 }
+};
+
+mobj_t LavaInflictor;
+
+//----------------------------------------------------------------------------
+//
+// PROC P_InitLava
+//
+//----------------------------------------------------------------------------
+
+void P_InitLava(void)
+{
+ memset(&LavaInflictor, 0, sizeof(mobj_t));
+ LavaInflictor.type = MT_PHOENIXFX2;
+ LavaInflictor.flags2 = MF2_FIREDAMAGE | MF2_NODMGTHRUST;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_InitTerrainTypes
+//
+//----------------------------------------------------------------------------
+
+void P_InitTerrainTypes(void)
+{
+ int i;
+ int lump;
+ int size;
+
+ size = (numflats + 1) * sizeof(int);
+ TerrainTypes = Z_Malloc(size, PU_STATIC, 0);
+ memset(TerrainTypes, 0, size);
+ for (i = 0; TerrainTypeDefs[i].type != -1; i++)
+ {
+ lump = W_CheckNumForName(TerrainTypeDefs[i].name);
+ if (lump != -1)
+ {
+ TerrainTypes[lump - firstflat] = TerrainTypeDefs[i].type;
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_InitPicAnims
+//
+//----------------------------------------------------------------------------
+
+void P_InitPicAnims(void)
+{
+ char *startname;
+ char *endname;
+ int i;
+
+ lastanim = anims;
+ for (i = 0; animdefs[i].istexture != -1; i++)
+ {
+ startname = DEH_String(animdefs[i].startname);
+ endname = DEH_String(animdefs[i].endname);
+
+ if (animdefs[i].istexture)
+ { // Texture animation
+ if (R_CheckTextureNumForName(startname) == -1)
+ { // Texture doesn't exist
+ continue;
+ }
+ lastanim->picnum = R_TextureNumForName(endname);
+ lastanim->basepic = R_TextureNumForName(startname);
+ }
+ else
+ { // Flat animation
+ if (W_CheckNumForName(startname) == -1)
+ { // Flat doesn't exist
+ continue;
+ }
+ lastanim->picnum = R_FlatNumForName(endname);
+ lastanim->basepic = R_FlatNumForName(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",
+ startname, endname);
+ }
+ lastanim->speed = animdefs[i].speed;
+ lastanim++;
+ }
+}
+
+/*
+==============================================================================
+
+ UTILITIES
+
+==============================================================================
+*/
+
+//
+// Will return a side_t* given the number of the current sector,
+// the line number, and the side (0/1) that you want.
+//
+side_t *getSide(int currentSector, int line, int side)
+{
+ return &sides[(sectors[currentSector].lines[line])->sidenum[side]];
+}
+
+//
+// Will return a sector_t* given the number of the current sector,
+// the line number and the side (0/1) that you want.
+//
+sector_t *getSector(int currentSector, int line, int side)
+{
+ return sides[(sectors[currentSector].lines[line])->sidenum[side]].sector;
+}
+
+//
+// Given the sector number and the line number, will tell you whether
+// the line is two-sided or not.
+//
+int twoSided(int sector, int line)
+{
+ return (sectors[sector].lines[line])->flags & ML_TWOSIDED;
+}
+
+//==================================================================
+//
+// Return sector_t * of sector next to current. NULL if not two-sided line
+//
+//==================================================================
+sector_t *getNextSector(line_t * line, sector_t * sec)
+{
+ if (!(line->flags & ML_TWOSIDED))
+ return NULL;
+
+ if (line->frontsector == sec)
+ return line->backsector;
+
+ return line->frontsector;
+}
+
+//==================================================================
+//
+// FIND LOWEST FLOOR HEIGHT IN 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;
+}
+
+//==================================================================
+//
+// FIND HIGHEST FLOOR HEIGHT IN SURROUNDING SECTORS
+//
+//==================================================================
+fixed_t P_FindHighestFloorSurrounding(sector_t * sec)
+{
+ int i;
+ line_t *check;
+ sector_t *other;
+ fixed_t floor = -500 * FRACUNIT;
+
+ 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;
+}
+
+//==================================================================
+//
+// FIND NEXT HIGHEST FLOOR IN SURROUNDING SECTORS
+//
+//==================================================================
+fixed_t P_FindNextHighestFloor(sector_t * sec, int currentheight)
+{
+ int i;
+ int h;
+ fixed_t min;
+ line_t *check;
+ sector_t *other;
+ fixed_t height = currentheight;
+
+ min = INT_MAX;
+
+ for (i = 0, h = 0; i < sec->linecount; i++)
+ {
+ check = sec->lines[i];
+ other = getNextSector(check, sec);
+
+ if (other != NULL && other->floorheight > height)
+ {
+ if (other->floorheight < min)
+ {
+ min = other->floorheight;
+ }
+
+ ++h;
+ }
+ }
+
+ // Compatibility note, in case of demo desyncs.
+
+ if (h > 20)
+ {
+ fprintf(stderr, "P_FindNextHighestFloor: exceeded Vanilla limit\n");
+ }
+
+ return min;
+}
+
+//==================================================================
+//
+// FIND LOWEST CEILING IN THE SURROUNDING SECTORS
+//
+//==================================================================
+fixed_t P_FindLowestCeilingSurrounding(sector_t * sec)
+{
+ int i;
+ line_t *check;
+ sector_t *other;
+ fixed_t height = INT_MAX;
+
+ 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;
+}
+
+//==================================================================
+//
+// FIND HIGHEST CEILING IN THE SURROUNDING SECTORS
+//
+//==================================================================
+fixed_t P_FindHighestCeilingSurrounding(sector_t * sec)
+{
+ int i;
+ line_t *check;
+ sector_t *other;
+ fixed_t height = 0;
+
+ 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;
+}
+
+//==================================================================
+//
+// RETURN NEXT SECTOR # THAT LINE TAG REFERS TO
+//
+//==================================================================
+int P_FindSectorFromLineTag(line_t * line, int start)
+{
+ int i;
+
+ for (i = start + 1; i < numsectors; i++)
+ if (sectors[i].tag == line->tag)
+ return i;
+ return -1;
+}
+
+//==================================================================
+//
+// Find minimum light from an adjacent sector
+//
+//==================================================================
+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;
+}
+
+/*
+==============================================================================
+
+ EVENTS
+
+Events are operations triggered by using, crossing, or shooting special lines, or by timed thinkers
+
+==============================================================================
+*/
+
+
+
+/*
+===============================================================================
+=
+= P_CrossSpecialLine - TRIGGER
+=
+= Called every time a thing origin is about to cross
+= a line with a non 0 special
+=
+===============================================================================
+*/
+
+void P_CrossSpecialLine(int linenum, int side, mobj_t * thing)
+{
+ line_t *line;
+
+ line = &lines[linenum];
+ if (!thing->player)
+ { // Check if trigger allowed by non-player mobj
+ switch (line->special)
+ {
+ case 39: // Trigger_TELEPORT
+ case 97: // Retrigger_TELEPORT
+ case 4: // Trigger_Raise_Door
+ //case 10: // PLAT DOWN-WAIT-UP-STAY TRIGGER
+ //case 88: // PLAT DOWN-WAIT-UP-STAY RETRIGGER
+ break;
+ default:
+ return;
+ break;
+ }
+ }
+ switch (line->special)
+ {
+ //====================================================
+ // TRIGGERS
+ //====================================================
+ case 2: // Open Door
+ EV_DoDoor(line, open, VDOORSPEED);
+ line->special = 0;
+ break;
+ case 3: // Close Door
+ EV_DoDoor(line, close, VDOORSPEED);
+ line->special = 0;
+ break;
+ case 4: // Raise Door
+ EV_DoDoor(line, normal, VDOORSPEED);
+ line->special = 0;
+ break;
+ case 5: // Raise Floor
+ EV_DoFloor(line, raiseFloor);
+ line->special = 0;
+ break;
+ case 6: // Fast Ceiling Crush & Raise
+ EV_DoCeiling(line, fastCrushAndRaise);
+ line->special = 0;
+ break;
+ case 8: // Trigger_Build_Stairs (8 pixel steps)
+ EV_BuildStairs(line, 8 * FRACUNIT);
+ line->special = 0;
+ break;
+ case 106: // Trigger_Build_Stairs_16 (16 pixel steps)
+ EV_BuildStairs(line, 16 * FRACUNIT);
+ line->special = 0;
+ break;
+ case 10: // PlatDownWaitUp
+ EV_DoPlat(line, downWaitUpStay, 0);
+ line->special = 0;
+ break;
+ case 12: // Light Turn On - brightest near
+ EV_LightTurnOn(line, 0);
+ line->special = 0;
+ break;
+ case 13: // Light Turn On 255
+ EV_LightTurnOn(line, 255);
+ line->special = 0;
+ break;
+ case 16: // Close Door 30
+ EV_DoDoor(line, close30ThenOpen, VDOORSPEED);
+ line->special = 0;
+ break;
+ case 17: // Start Light Strobing
+ EV_StartLightStrobing(line);
+ line->special = 0;
+ break;
+ case 19: // Lower Floor
+ EV_DoFloor(line, lowerFloor);
+ line->special = 0;
+ break;
+ case 22: // Raise floor to nearest height and change texture
+ EV_DoPlat(line, raiseToNearestAndChange, 0);
+ line->special = 0;
+ break;
+ case 25: // Ceiling Crush and Raise
+ EV_DoCeiling(line, crushAndRaise);
+ line->special = 0;
+ break;
+ case 30: // Raise floor to shortest texture height
+ // on either side of lines
+ EV_DoFloor(line, raiseToTexture);
+ line->special = 0;
+ break;
+ case 35: // Lights Very Dark
+ EV_LightTurnOn(line, 35);
+ line->special = 0;
+ break;
+ case 36: // Lower Floor (TURBO)
+ EV_DoFloor(line, turboLower);
+ line->special = 0;
+ break;
+ case 37: // LowerAndChange
+ EV_DoFloor(line, lowerAndChange);
+ line->special = 0;
+ break;
+ case 38: // Lower Floor To Lowest
+ EV_DoFloor(line, lowerFloorToLowest);
+ line->special = 0;
+ break;
+ case 39: // TELEPORT!
+ EV_Teleport(line, side, thing);
+ line->special = 0;
+ break;
+ case 40: // RaiseCeilingLowerFloor
+ EV_DoCeiling(line, raiseToHighest);
+ EV_DoFloor(line, lowerFloorToLowest);
+ line->special = 0;
+ break;
+ case 44: // Ceiling Crush
+ EV_DoCeiling(line, lowerAndCrush);
+ line->special = 0;
+ break;
+ case 52: // EXIT!
+ G_ExitLevel();
+ line->special = 0;
+ break;
+ case 53: // Perpetual Platform Raise
+ EV_DoPlat(line, perpetualRaise, 0);
+ line->special = 0;
+ break;
+ case 54: // Platform Stop
+ EV_StopPlat(line);
+ line->special = 0;
+ break;
+ case 56: // Raise Floor Crush
+ EV_DoFloor(line, raiseFloorCrush);
+ line->special = 0;
+ break;
+ case 57: // Ceiling Crush Stop
+ EV_CeilingCrushStop(line);
+ line->special = 0;
+ break;
+ case 58: // Raise Floor 24
+ EV_DoFloor(line, raiseFloor24);
+ line->special = 0;
+ break;
+ case 59: // Raise Floor 24 And Change
+ EV_DoFloor(line, raiseFloor24AndChange);
+ line->special = 0;
+ break;
+ case 104: // Turn lights off in sector(tag)
+ EV_TurnTagLightsOff(line);
+ line->special = 0;
+ break;
+ case 105: // Trigger_SecretExit
+ G_SecretExitLevel();
+ line->special = 0;
+ break;
+
+ //====================================================
+ // RE-DOABLE TRIGGERS
+ //====================================================
+
+ 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, VDOORSPEED);
+ break;
+ case 76: // Close Door 30
+ EV_DoDoor(line, close30ThenOpen, VDOORSPEED);
+ 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, VDOORSPEED);
+ 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, VDOORSPEED);
+ break;
+ case 100: // Retrigger_Raise_Door_Turbo
+ EV_DoDoor(line, normal, VDOORSPEED * 3);
+ 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;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_ShootSpecialLine
+//
+// Called when a thing shoots a special line.
+//
+//----------------------------------------------------------------------------
+
+void P_ShootSpecialLine(mobj_t * thing, line_t * line)
+{
+ if (!thing->player)
+ { // Check if trigger allowed by non-player mobj
+ switch (line->special)
+ {
+ case 46: // Impact_OpenDoor
+ break;
+ default:
+ return;
+ break;
+ }
+ }
+ switch (line->special)
+ {
+ case 24: // Impact_RaiseFloor
+ EV_DoFloor(line, raiseFloor);
+ P_ChangeSwitchTexture(line, 0);
+ break;
+ case 46: // Impact_OpenDoor
+ EV_DoDoor(line, open, VDOORSPEED);
+ P_ChangeSwitchTexture(line, 1);
+ break;
+ case 47: // Impact_RaiseFloorNear&Change
+ EV_DoPlat(line, raiseToNearestAndChange, 0);
+ P_ChangeSwitchTexture(line, 0);
+ break;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_PlayerInSpecialSector
+//
+// Called every tic frame that the player origin is in a special sector.
+//
+//----------------------------------------------------------------------------
+
+void P_PlayerInSpecialSector(player_t * player)
+{
+ sector_t *sector;
+ static int pushTab[5] = {
+ 2048 * 5,
+ 2048 * 10,
+ 2048 * 25,
+ 2048 * 30,
+ 2048 * 35
+ };
+
+ sector = player->mo->subsector->sector;
+ if (player->mo->z != sector->floorheight)
+ { // Player is not touching the floor
+ return;
+ }
+ switch (sector->special)
+ {
+ case 7: // Damage_Sludge
+ if (!(leveltime & 31))
+ {
+ P_DamageMobj(player->mo, NULL, NULL, 4);
+ }
+ break;
+ case 5: // Damage_LavaWimpy
+ if (!(leveltime & 15))
+ {
+ P_DamageMobj(player->mo, &LavaInflictor, NULL, 5);
+ P_HitFloor(player->mo);
+ }
+ break;
+ case 16: // Damage_LavaHefty
+ if (!(leveltime & 15))
+ {
+ P_DamageMobj(player->mo, &LavaInflictor, NULL, 8);
+ P_HitFloor(player->mo);
+ }
+ break;
+ case 4: // Scroll_EastLavaDamage
+ P_Thrust(player, 0, 2048 * 28);
+ if (!(leveltime & 15))
+ {
+ P_DamageMobj(player->mo, &LavaInflictor, NULL, 5);
+ P_HitFloor(player->mo);
+ }
+ break;
+ case 9: // SecretArea
+ player->secretcount++;
+ sector->special = 0;
+ break;
+ case 11: // Exit_SuperDamage (DOOM E1M8 finale)
+ /*
+ player->cheats &= ~CF_GODMODE;
+ if(!(leveltime&0x1f))
+ {
+ P_DamageMobj(player->mo, NULL, NULL, 20);
+ }
+ if(player->health <= 10)
+ {
+ G_ExitLevel();
+ }
+ */
+ break;
+
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29: // Scroll_North
+ P_Thrust(player, ANG90, pushTab[sector->special - 25]);
+ break;
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24: // Scroll_East
+ P_Thrust(player, 0, pushTab[sector->special - 20]);
+ break;
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34: // Scroll_South
+ P_Thrust(player, ANG270, pushTab[sector->special - 30]);
+ break;
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39: // Scroll_West
+ P_Thrust(player, ANG180, pushTab[sector->special - 35]);
+ break;
+
+ case 40:
+ case 41:
+ case 42:
+ case 43:
+ case 44:
+ case 45:
+ case 46:
+ case 47:
+ case 48:
+ case 49:
+ case 50:
+ case 51:
+ // Wind specials are handled in (P_mobj):P_XYMovement
+ break;
+
+ case 15: // Friction_Low
+ // Only used in (P_mobj):P_XYMovement and (P_user):P_Thrust
+ break;
+
+ default:
+ I_Error("P_PlayerInSpecialSector: "
+ "unknown special %i", sector->special);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_UpdateSpecials
+//
+// Animate planes, scroll walls, etc.
+//
+//----------------------------------------------------------------------------
+
+void P_UpdateSpecials(void)
+{
+ int i;
+ int pic;
+ anim_t *anim;
+ line_t *line;
+
+ // Animate flats and textures
+ for (anim = anims; anim < lastanim; anim++)
+ {
+ for (i = anim->basepic; i < anim->basepic + anim->numpics; i++)
+ {
+ pic =
+ anim->basepic +
+ ((leveltime / anim->speed + i) % anim->numpics);
+ if (anim->istexture)
+ {
+ texturetranslation[i] = pic;
+ }
+ else
+ {
+ flattranslation[i] = pic;
+ }
+ }
+ }
+ // Update scrolling texture offsets
+ for (i = 0; i < numlinespecials; i++)
+ {
+ line = linespeciallist[i];
+ switch (line->special)
+ {
+ case 48: // Effect_Scroll_Left
+ sides[line->sidenum[0]].textureoffset += FRACUNIT;
+ break;
+ case 99: // Effect_Scroll_Right
+ sides[line->sidenum[0]].textureoffset -= FRACUNIT;
+ break;
+ }
+ }
+ // Handle buttons
+ 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;
+ }
+ S_StartSound(buttonlist[i].soundorg, sfx_switch);
+ memset(&buttonlist[i], 0, sizeof(button_t));
+ }
+ }
+ }
+}
+
+//============================================================
+//
+// Special Stuff that can't be categorized
+//
+//============================================================
+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;
+ while ((secnum = P_FindSectorFromLineTag(line, secnum)) >= 0)
+ {
+ s1 = &sectors[secnum];
+
+ // ALREADY MOVING? IF SO, KEEP GOING...
+ if (s1->specialdata)
+ continue;
+
+ rtn = 1;
+ s2 = getNextSector(s1->lines[0], s1);
+ for (i = 0; i < s2->linecount; i++)
+ {
+ if ((!s2->lines[i]->flags & ML_TWOSIDED) ||
+ (s2->lines[i]->backsector == s1))
+ continue;
+ s3 = s2->lines[i]->backsector;
+
+ //
+ // Spawn rising slime
+ //
+ floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0);
+ P_AddThinker(&floor->thinker);
+ s2->specialdata = floor;
+ 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
+ //
+ floor = Z_Malloc(sizeof(*floor), PU_LEVSPEC, 0);
+ P_AddThinker(&floor->thinker);
+ s1->specialdata = floor;
+ 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;
+}
+
+/*
+==============================================================================
+
+ SPECIAL SPAWNING
+
+==============================================================================
+*/
+/*
+================================================================================
+= P_SpawnSpecials
+=
+= After the map has been loaded, scan for specials that
+= spawn thinkers
+=
+===============================================================================
+*/
+
+short numlinespecials;
+line_t *linespeciallist[MAXLINEANIMS];
+
+void P_SpawnSpecials(void)
+{
+ sector_t *sector;
+ int i;
+ int episode;
+
+ episode = 1;
+ if (W_CheckNumForName(DEH_String("texture2")) >= 0)
+ episode = 2;
+
+ //
+ // Init special SECTORs
+ //
+ sector = sectors;
+ for (i = 0; i < numsectors; i++, sector++)
+ {
+ if (!sector->special)
+ continue;
+ switch (sector->special)
+ {
+ case 1: // FLICKERING LIGHTS
+ 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 = 4;
+ break;
+ case 8: // GLOWING LIGHT
+ P_SpawnGlowingLight(sector);
+ break;
+ case 9: // SECRET SECTOR
+ totalsecret++;
+ 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;
+ }
+ }
+
+
+ //
+ // Init line EFFECTs
+ //
+ numlinespecials = 0;
+ for (i = 0; i < numlines; i++)
+ switch (lines[i].special)
+ {
+ case 48: // Effect_Scroll_Left
+ case 99: // Effect_Scroll_Right
+ linespeciallist[numlinespecials] = &lines[i];
+ numlinespecials++;
+ break;
+ }
+
+ //
+ // Init other misc stuff
+ //
+ for (i = 0; i < MAXCEILINGS; i++)
+ activeceilings[i] = NULL;
+ for (i = 0; i < MAXPLATS; i++)
+ activeplats[i] = NULL;
+ for (i = 0; i < MAXBUTTONS; i++)
+ memset(&buttonlist[i], 0, sizeof(button_t));
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_InitAmbientSound
+//
+//----------------------------------------------------------------------------
+
+void P_InitAmbientSound(void)
+{
+ AmbSfxCount = 0;
+ AmbSfxVolume = 0;
+ AmbSfxTics = 10 * TICRATE;
+ AmbSfxPtr = AmbSndSeqInit;
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_AddAmbientSfx
+//
+// Called by (P_mobj):P_SpawnMapThing during (P_setup):P_SetupLevel.
+//
+//----------------------------------------------------------------------------
+
+void P_AddAmbientSfx(int sequence)
+{
+ if (AmbSfxCount == MAX_AMBIENT_SFX)
+ {
+ I_Error("Too many ambient sound sequences");
+ }
+ LevelAmbientSfx[AmbSfxCount++] = AmbientSfx[sequence];
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_AmbientSound
+//
+// Called every tic by (P_tick):P_Ticker.
+//
+//----------------------------------------------------------------------------
+
+void P_AmbientSound(void)
+{
+ afxcmd_t cmd;
+ int sound;
+ boolean done;
+
+ if (!AmbSfxCount)
+ { // No ambient sound sequences on current level
+ return;
+ }
+ if (--AmbSfxTics)
+ {
+ return;
+ }
+ done = false;
+ do
+ {
+ cmd = *AmbSfxPtr++;
+ switch (cmd)
+ {
+ case afxcmd_play:
+ AmbSfxVolume = P_Random() >> 2;
+ S_StartSoundAtVolume(NULL, *AmbSfxPtr++, AmbSfxVolume);
+ break;
+ case afxcmd_playabsvol:
+ sound = *AmbSfxPtr++;
+ AmbSfxVolume = *AmbSfxPtr++;
+ S_StartSoundAtVolume(NULL, sound, AmbSfxVolume);
+ break;
+ case afxcmd_playrelvol:
+ sound = *AmbSfxPtr++;
+ AmbSfxVolume += *AmbSfxPtr++;
+ if (AmbSfxVolume < 0)
+ {
+ AmbSfxVolume = 0;
+ }
+ else if (AmbSfxVolume > 127)
+ {
+ AmbSfxVolume = 127;
+ }
+ S_StartSoundAtVolume(NULL, sound, AmbSfxVolume);
+ break;
+ case afxcmd_delay:
+ AmbSfxTics = *AmbSfxPtr++;
+ done = true;
+ break;
+ case afxcmd_delayrand:
+ AmbSfxTics = P_Random() & (*AmbSfxPtr++);
+ done = true;
+ break;
+ case afxcmd_end:
+ AmbSfxTics = 6 * TICRATE + P_Random();
+ AmbSfxPtr = LevelAmbientSfx[P_Random() % AmbSfxCount];
+ done = true;
+ break;
+ default:
+ I_Error("P_AmbientSound: Unknown afxcmd %d", cmd);
+ break;
+ }
+ }
+ while (done == false);
+}
diff --git a/src/heretic/p_spec.h b/src/heretic/p_spec.h
new file mode 100644
index 00000000..7f5e2c43
--- /dev/null
+++ b/src/heretic/p_spec.h
@@ -0,0 +1,398 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_spec.h
+
+/*
+===============================================================================
+
+ P_SPEC
+
+===============================================================================
+*/
+
+//
+// Animating textures and planes
+//
+typedef struct
+{
+ boolean istexture;
+ int picnum;
+ int basepic;
+ int numpics;
+ int speed;
+} anim_t;
+
+//
+// source animation definition
+//
+typedef struct
+{
+ boolean istexture; // if false, it's a flat
+ char endname[9];
+ char startname[9];
+ int speed;
+} animdef_t;
+
+#define MAXANIMS 32
+
+extern anim_t anims[MAXANIMS], *lastanim;
+extern int *TerrainTypes;
+
+//
+// Animating line specials
+//
+#define MAXLINEANIMS 64
+extern short numlinespecials;
+extern line_t *linespeciallist[MAXLINEANIMS];
+
+// Define values for map objects
+#define MO_TELEPORTMAN 14
+
+// at game start
+void P_InitPicAnims(void);
+void P_InitTerrainTypes(void);
+void P_InitLava(void);
+
+// at map load
+void P_SpawnSpecials(void);
+void P_InitAmbientSound(void);
+void P_AddAmbientSfx(int sequence);
+
+// every tic
+void P_UpdateSpecials(void);
+void P_AmbientSound(void);
+
+// when needed
+boolean P_UseSpecialLine(mobj_t * thing, line_t * line);
+void P_ShootSpecialLine(mobj_t * thing, line_t * line);
+void P_CrossSpecialLine(int linenum, int side, mobj_t * thing);
+
+void P_PlayerInSpecialSector(player_t * player);
+
+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_FindLowestCeilingSurrounding(sector_t * sec);
+fixed_t P_FindHighestCeilingSurrounding(sector_t * sec);
+int P_FindSectorFromLineTag(line_t * line, int start);
+int P_FindMinSurroundingLight(sector_t * sector, int max);
+sector_t *getNextSector(line_t * line, sector_t * sec);
+
+//
+// SPECIAL
+//
+int EV_DoDonut(line_t * line);
+
+/*
+===============================================================================
+
+ P_LIGHTS
+
+===============================================================================
+*/
+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;
+
+#define GLOWSPEED 8
+#define STROBEBRIGHT 5
+#define FASTDARK 15
+#define SLOWDARK 35
+
+void T_LightFlash(lightflash_t * flash);
+void P_SpawnLightFlash(sector_t * sector);
+void T_StrobeFlash(strobe_t * flash);
+void P_SpawnStrobeFlash(sector_t * sector, int fastOrSlow, int inSync);
+void EV_StartLightStrobing(line_t * line);
+void EV_TurnTagLightsOff(line_t * line);
+void EV_LightTurnOn(line_t * line, int bright);
+void T_Glow(glow_t * g);
+void P_SpawnGlowingLight(sector_t * sector);
+
+/*
+===============================================================================
+
+ P_SWITCH
+
+===============================================================================
+*/
+typedef struct
+{
+ char name1[9];
+ char name2[9];
+ short episode;
+} switchlist_t;
+
+typedef enum
+{
+ top,
+ middle,
+ bottom
+} bwhere_e;
+
+typedef struct
+{
+ line_t *line;
+ bwhere_e where;
+ int btexture;
+ int btimer;
+ void *soundorg;
+} button_t;
+
+#define MAXSWITCHES 50 // max # of wall switches in a level
+#define MAXBUTTONS 16 // 4 players, 4 buttons each at once, max.
+#define BUTTONTIME 35 // 1 second
+
+extern button_t buttonlist[MAXBUTTONS];
+
+void P_ChangeSwitchTexture(line_t * line, int useAgain);
+void P_InitSwitchList(void);
+
+/*
+===============================================================================
+
+ P_PLATS
+
+===============================================================================
+*/
+typedef enum
+{
+ up,
+ down,
+ waiting,
+ in_stasis
+} plat_e;
+
+typedef enum
+{
+ perpetualRaise,
+ downWaitUpStay,
+ raiseAndChange,
+ raiseToNearestAndChange
+} plattype_e;
+
+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;
+} plat_t;
+
+#define PLATWAIT 3
+#define PLATSPEED FRACUNIT
+#define MAXPLATS 30
+
+extern plat_t *activeplats[MAXPLATS];
+
+void T_PlatRaise(plat_t * plat);
+int EV_DoPlat(line_t * line, plattype_e type, int amount);
+void P_AddActivePlat(plat_t * plat);
+void P_RemoveActivePlat(plat_t * plat);
+void EV_StopPlat(line_t * line);
+void P_ActivateInStasis(int tag);
+
+/*
+===============================================================================
+
+ P_DOORS
+
+===============================================================================
+*/
+typedef enum
+{
+ normal,
+ close30ThenOpen,
+ close,
+ open,
+ raiseIn5Mins
+} vldoor_e;
+
+typedef struct
+{
+ thinker_t thinker;
+ vldoor_e type;
+ sector_t *sector;
+ fixed_t topheight;
+ fixed_t speed;
+ int direction; // 1 = up, 0 = waiting at top, -1 = down
+ int topwait; // tics to wait at the top
+ // (keep in case a door going down is reset)
+ int topcountdown; // when it reaches 0, start going down
+} vldoor_t;
+
+#define VDOORSPEED FRACUNIT*2
+#define VDOORWAIT 150
+
+void EV_VerticalDoor(line_t * line, mobj_t * thing);
+int EV_DoDoor(line_t * line, vldoor_e type, fixed_t speed);
+void T_VerticalDoor(vldoor_t * door);
+void P_SpawnDoorCloseIn30(sector_t * sec);
+void P_SpawnDoorRaiseIn5Mins(sector_t * sec, int secnum);
+
+/*
+===============================================================================
+
+ P_CEILNG
+
+===============================================================================
+*/
+typedef enum
+{
+ lowerToFloor,
+ raiseToHighest,
+ lowerAndCrush,
+ crushAndRaise,
+ fastCrushAndRaise
+} ceiling_e;
+
+typedef struct
+{
+ thinker_t thinker;
+ ceiling_e type;
+ sector_t *sector;
+ fixed_t bottomheight, topheight;
+ fixed_t speed;
+ boolean crush;
+ int direction; // 1 = up, 0 = waiting, -1 = down
+ int tag; // ID
+ int olddirection;
+} ceiling_t;
+
+#define CEILSPEED FRACUNIT
+#define CEILWAIT 150
+#define MAXCEILINGS 30
+
+extern ceiling_t *activeceilings[MAXCEILINGS];
+
+int EV_DoCeiling(line_t * line, ceiling_e type);
+void T_MoveCeiling(ceiling_t * ceiling);
+void P_AddActiveCeiling(ceiling_t * c);
+void P_RemoveActiveCeiling(ceiling_t * c);
+int EV_CeilingCrushStop(line_t * line);
+void P_ActivateInStasisCeiling(line_t * line);
+
+/*
+===============================================================================
+
+ P_FLOOR
+
+===============================================================================
+*/
+typedef enum
+{
+ lowerFloor, // lower floor to highest surrounding floor
+ lowerFloorToLowest, // lower floor to lowest surrounding floor
+ turboLower, // lower floor to highest surrounding floor VERY FAST
+ raiseFloor, // raise floor to lowest surrounding CEILING
+ raiseFloorToNearest, // raise floor to next highest surrounding floor
+ raiseToTexture, // raise floor to shortest height texture around it
+ lowerAndChange, // lower floor to lowest surrounding floor and change
+ // floorpic
+ raiseFloor24,
+ raiseFloor24AndChange,
+ raiseFloorCrush,
+ donutRaise,
+ raiseBuildStep // One step of a staircase
+} floor_e;
+
+typedef struct
+{
+ thinker_t thinker;
+ floor_e type;
+ boolean crush;
+ sector_t *sector;
+ int direction;
+ int newspecial;
+ short texture;
+ fixed_t floordestheight;
+ fixed_t speed;
+} floormove_t;
+
+#define FLOORSPEED FRACUNIT
+
+typedef enum
+{
+ ok,
+ crushed,
+ pastdest
+} result_e;
+
+result_e T_MovePlane(sector_t * sector, fixed_t speed,
+ fixed_t dest, boolean crush, int floorOrCeiling,
+ int direction);
+
+int EV_BuildStairs(line_t * line, fixed_t stepDelta);
+int EV_DoFloor(line_t * line, floor_e floortype);
+void T_MoveFloor(floormove_t * floor);
+
+/*
+===============================================================================
+
+ P_TELEPT
+
+===============================================================================
+*/
+
+boolean P_Teleport(mobj_t * thing, fixed_t x, fixed_t y, angle_t angle);
+boolean EV_Teleport(line_t * line, int side, mobj_t * thing);
diff --git a/src/heretic/p_switch.c b/src/heretic/p_switch.c
new file mode 100644
index 00000000..2ec758b6
--- /dev/null
+++ b/src/heretic/p_switch.c
@@ -0,0 +1,414 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 "doomdef.h"
+#include "deh_str.h"
+#include "i_system.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+//==================================================================
+//
+// CHANGE THE TEXTURE OF A WALL SWITCH TO ITS OPPOSITE
+//
+//==================================================================
+switchlist_t alphSwitchList[] = {
+ {"SW1OFF", "SW1ON", 1},
+ {"SW2OFF", "SW2ON", 1},
+
+/*
+ {"SW1CTY", "SW2CTY", 1},
+ {"SW1ORGRY", "SW2ORGRY", 1},
+ {"SW1GRSTN", "SW2GRSTN", 1},
+ {"SW1SNDP", "SW2SNDP", 1},
+ {"SW1SPINE", "SW2SPINE", 1},
+ {"SW1SQPEB", "SW2SQPEB", 1},
+ {"SW1TRST1", "SW2TRST1", 1},
+ {"SW1CSTL", "SW2CSTL", 1},
+ {"SW1MOSS", "SW2MOSS", 1},
+ {"SW1SNDSQ", "SW2SNDSQ", 1},
+ {"SW1RED", "SW2RED", 1},
+ {"SW1WOOD", "SW2WOOD", 1},
+ {"SW1BROWN", "SW2BROWN", 1},
+
+ {"SW1TRST2", "SW2TRST2", 2},
+ {"SW1MSC", "SW2MSC", 2},
+ {"SW1MSC2", "SW2MSC2", 2},
+ {"SW1GRDMD", "SW2GRDMD", 2},
+*/
+
+#if 0
+ {"SW1BRCOM", "SW2BRCOM", 1},
+ {"SW1BRN1", "SW2BRN1", 1},
+ {"SW1BRN2", "SW2BRN2", 1},
+ {"SW1BRNGN", "SW2BRNGN", 1},
+ {"SW1BROWN", "SW2BROWN", 1},
+ {"SW1COMM", "SW2COMM", 1},
+ {"SW1COMP", "SW2COMP", 1},
+ {"SW1DIRT", "SW2DIRT", 1},
+ {"SW1EXIT", "SW2EXIT", 1},
+ {"SW1GRAY", "SW2GRAY", 1},
+ {"SW1GRAY1", "SW2GRAY1", 1},
+ {"SW1METAL", "SW2METAL", 1},
+ {"SW1PIPE", "SW2PIPE", 1},
+ {"SW1SLAD", "SW2SLAD", 1},
+ {"SW1STARG", "SW2STARG", 1},
+ {"SW1STON1", "SW2STON1", 1},
+ {"SW1STON2", "SW2STON2", 1},
+ {"SW1STONE", "SW2STONE", 1},
+ {"SW1STRTN", "SW2STRTN", 1},
+
+ {"SW1BLUE", "SW2BLUE", 2},
+ {"SW1CMT", "SW2CMT", 2},
+ {"SW1GARG", "SW2GARG", 2},
+ {"SW1GSTON", "SW2GSTON", 2},
+ {"SW1HOT", "SW2HOT", 2},
+ {"SW1LION", "SW2LION", 2},
+ {"SW1SATYR", "SW2SATYR", 2},
+ {"SW1SKIN", "SW2SKIN", 2},
+ {"SW1VINE", "SW2VINE", 2},
+ {"SW1WOOD", "SW2WOOD", 2},
+#endif
+ {"\0", "\0", 0}
+};
+
+int switchlist[MAXSWITCHES * 2];
+int numswitches;
+button_t buttonlist[MAXBUTTONS];
+
+/*
+===============
+=
+= P_InitSwitchList
+=
+= Only called at game initialization
+=
+===============
+*/
+
+void P_InitSwitchList(void)
+{
+ int i;
+ int index;
+ int episode;
+
+ episode = 1;
+ if (gamemode != shareware)
+ episode = 2;
+
+ for (index = 0, i = 0; i < MAXSWITCHES; i++)
+ {
+ if (!alphSwitchList[i].episode)
+ {
+ numswitches = index / 2;
+ switchlist[index] = -1;
+ break;
+ }
+
+ if (alphSwitchList[i].episode <= episode)
+ {
+ switchlist[index++] =
+ R_TextureNumForName(DEH_String(alphSwitchList[i].name1));
+ switchlist[index++] =
+ R_TextureNumForName(DEH_String(alphSwitchList[i].name2));
+ }
+ }
+}
+
+//==================================================================
+//
+// Start a button counting down till it turns off.
+//
+//==================================================================
+void P_StartButton(line_t * line, bwhere_e w, int texture, int time)
+{
+ int i;
+
+ for (i = 0; i < MAXBUTTONS; i++)
+ if (!buttonlist[i].btimer)
+ {
+ buttonlist[i].line = line;
+ buttonlist[i].where = w;
+ buttonlist[i].btexture = texture;
+ buttonlist[i].btimer = time;
+ buttonlist[i].soundorg = &line->frontsector->soundorg;
+ return;
+ }
+
+ I_Error("P_StartButton: no button slots left!");
+}
+
+//==================================================================
+//
+// Function that changes wall texture.
+// Tell it if switch is ok to use again (1=yes, it's a button).
+//
+//==================================================================
+void P_ChangeSwitchTexture(line_t * line, int useAgain)
+{
+ int texTop;
+ int texMid;
+ int texBot;
+ int i;
+ int sound;
+
+ if (!useAgain)
+ line->special = 0;
+
+ texTop = sides[line->sidenum[0]].toptexture;
+ texMid = sides[line->sidenum[0]].midtexture;
+ texBot = sides[line->sidenum[0]].bottomtexture;
+
+ sound = sfx_switch;
+ //if (line->special == 11) // EXIT SWITCH?
+ // sound = sfx_swtchx;
+
+ for (i = 0; i < numswitches * 2; i++)
+ if (switchlist[i] == texTop)
+ {
+ S_StartSound(buttonlist->soundorg, sound);
+ sides[line->sidenum[0]].toptexture = switchlist[i ^ 1];
+ if (useAgain)
+ P_StartButton(line, top, switchlist[i], BUTTONTIME);
+ return;
+ }
+ else if (switchlist[i] == texMid)
+ {
+ S_StartSound(buttonlist->soundorg, sound);
+ sides[line->sidenum[0]].midtexture = switchlist[i ^ 1];
+ if (useAgain)
+ P_StartButton(line, middle, switchlist[i], BUTTONTIME);
+ return;
+ }
+ else if (switchlist[i] == texBot)
+ {
+ S_StartSound(buttonlist->soundorg, sound);
+ sides[line->sidenum[0]].bottomtexture = switchlist[i ^ 1];
+ if (useAgain)
+ P_StartButton(line, bottom, switchlist[i], BUTTONTIME);
+ return;
+ }
+}
+
+/*
+==============================================================================
+=
+= P_UseSpecialLine
+=
+= Called when a thing uses a special line
+= Only the front sides of lines are usable
+===============================================================================
+*/
+
+boolean P_UseSpecialLine(mobj_t * thing, line_t * line)
+{
+ //
+ // Switches that other things can activate
+ //
+ if (!thing->player)
+ {
+ if (line->flags & ML_SECRET)
+ return false; // never open secret doors
+ switch (line->special)
+ {
+ case 1: // MANUAL DOOR RAISE
+ case 32: // MANUAL BLUE
+ case 33: // MANUAL RED
+ case 34: // MANUAL YELLOW
+ break;
+ default:
+ return false;
+ }
+ }
+
+ //
+ // do something
+ //
+ switch (line->special)
+ {
+ //===============================================
+ // MANUALS
+ //===============================================
+ 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
+ EV_VerticalDoor(line, thing);
+ break;
+ //===============================================
+ // SWITCHES
+ //===============================================
+ case 7: // Switch_Build_Stairs (8 pixel steps)
+ if (EV_BuildStairs(line, 8 * FRACUNIT))
+ {
+ P_ChangeSwitchTexture(line, 0);
+ }
+ break;
+ case 107: // Switch_Build_Stairs_16 (16 pixel steps)
+ if (EV_BuildStairs(line, 16 * FRACUNIT))
+ {
+ P_ChangeSwitchTexture(line, 0);
+ }
+ break;
+ case 9: // Change Donut
+ if (EV_DoDonut(line))
+ P_ChangeSwitchTexture(line, 0);
+ break;
+ case 11: // Exit level
+ G_ExitLevel();
+ P_ChangeSwitchTexture(line, 0);
+ 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, VDOORSPEED))
+ 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: // Lower Ceiling And Crush
+ if (EV_DoCeiling(line, lowerAndCrush))
+ P_ChangeSwitchTexture(line, 0);
+ break;
+ case 50: // Close Door
+ if (EV_DoDoor(line, close, VDOORSPEED))
+ P_ChangeSwitchTexture(line, 0);
+ break;
+ case 51: // Secret EXIT
+ G_SecretExitLevel();
+ P_ChangeSwitchTexture(line, 0);
+ 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, VDOORSPEED))
+ P_ChangeSwitchTexture(line, 0);
+ break;
+ //===============================================
+ // BUTTONS
+ //===============================================
+ case 42: // Close Door
+ if (EV_DoDoor(line, close, VDOORSPEED))
+ 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, VDOORSPEED))
+ 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, VDOORSPEED))
+ 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;
+ }
+
+ return true;
+}
diff --git a/src/heretic/p_telept.c b/src/heretic/p_telept.c
new file mode 100644
index 00000000..91d10f9b
--- /dev/null
+++ b/src/heretic/p_telept.c
@@ -0,0 +1,173 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_telept.c
+
+#include "doomdef.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_Teleport
+//
+//----------------------------------------------------------------------------
+
+boolean P_Teleport(mobj_t * thing, fixed_t x, fixed_t y, angle_t angle)
+{
+ fixed_t oldx;
+ fixed_t oldy;
+ fixed_t oldz;
+ fixed_t aboveFloor;
+ fixed_t fogDelta;
+ player_t *player;
+ unsigned an;
+ mobj_t *fog;
+
+ oldx = thing->x;
+ oldy = thing->y;
+ oldz = thing->z;
+ aboveFloor = thing->z - thing->floorz;
+ if (!P_TeleportMove(thing, x, y))
+ {
+ return (false);
+ }
+ if (thing->player)
+ {
+ player = thing->player;
+ if (player->powers[pw_flight] && aboveFloor)
+ {
+ thing->z = thing->floorz + aboveFloor;
+ if (thing->z + thing->height > thing->ceilingz)
+ {
+ thing->z = thing->ceilingz - thing->height;
+ }
+ player->viewz = thing->z + player->viewheight;
+ }
+ else
+ {
+ thing->z = thing->floorz;
+ player->viewz = thing->z + player->viewheight;
+ player->lookdir = 0;
+ }
+ }
+ else if (thing->flags & MF_MISSILE)
+ {
+ thing->z = thing->floorz + aboveFloor;
+ if (thing->z + thing->height > thing->ceilingz)
+ {
+ thing->z = thing->ceilingz - thing->height;
+ }
+ }
+ else
+ {
+ thing->z = thing->floorz;
+ }
+ // Spawn teleport fog at source and destination
+ fogDelta = thing->flags & MF_MISSILE ? 0 : TELEFOGHEIGHT;
+ fog = P_SpawnMobj(oldx, oldy, oldz + fogDelta, MT_TFOG);
+ S_StartSound(fog, sfx_telept);
+ an = angle >> ANGLETOFINESHIFT;
+ fog = P_SpawnMobj(x + 20 * finecosine[an],
+ y + 20 * finesine[an], thing->z + fogDelta, MT_TFOG);
+ S_StartSound(fog, sfx_telept);
+ if (thing->player && !thing->player->powers[pw_weaponlevel2])
+ { // Freeze player for about .5 sec
+ thing->reactiontime = 18;
+ }
+ thing->angle = angle;
+ if (thing->flags2 & MF2_FOOTCLIP
+ && P_GetThingFloorType(thing) != FLOOR_SOLID)
+ {
+ thing->flags2 |= MF2_FEETARECLIPPED;
+ }
+ else if (thing->flags2 & MF2_FEETARECLIPPED)
+ {
+ thing->flags2 &= ~MF2_FEETARECLIPPED;
+ }
+ if (thing->flags & MF_MISSILE)
+ {
+ angle >>= ANGLETOFINESHIFT;
+ thing->momx = FixedMul(thing->info->speed, finecosine[angle]);
+ thing->momy = FixedMul(thing->info->speed, finesine[angle]);
+ }
+ else
+ {
+ thing->momx = thing->momy = thing->momz = 0;
+ }
+ return (true);
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC EV_Teleport
+//
+//----------------------------------------------------------------------------
+
+boolean EV_Teleport(line_t * line, int side, mobj_t * thing)
+{
+ int i;
+ int tag;
+ mobj_t *m;
+ thinker_t *thinker;
+ sector_t *sector;
+
+ if (thing->flags2 & MF2_NOTELEPORT)
+ {
+ return (false);
+ }
+ if (side == 1)
+ { // Don't teleport when crossing back side
+ return (false);
+ }
+ tag = line->tag;
+ for (i = 0; i < numsectors; i++)
+ {
+ if (sectors[i].tag == tag)
+ {
+ thinker = thinkercap.next;
+ for (thinker = thinkercap.next; thinker != &thinkercap;
+ thinker = thinker->next)
+ {
+ if (thinker->function != P_MobjThinker)
+ { // Not a mobj
+ continue;
+ }
+ m = (mobj_t *) thinker;
+ if (m->type != MT_TELEPORTMAN)
+ { // Not a teleportman
+ continue;
+ }
+ sector = m->subsector->sector;
+ if (sector - sectors != i)
+ { // Wrong sector
+ continue;
+ }
+ return (P_Teleport(thing, m->x, m->y, m->angle));
+ }
+ }
+ }
+ return (false);
+}
diff --git a/src/heretic/p_tick.c b/src/heretic/p_tick.c
new file mode 100644
index 00000000..9758be3a
--- /dev/null
+++ b/src/heretic/p_tick.c
@@ -0,0 +1,668 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_tick.c
+
+#include "doomdef.h"
+#include "i_system.h"
+#include "p_local.h"
+#include "v_video.h"
+
+int leveltime;
+int TimerGame;
+
+/*
+====================
+=
+= P_ArchivePlayers
+=
+====================
+*/
+
+void P_ArchivePlayers(void)
+{
+ int i;
+ int j;
+ player_t dest;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i])
+ {
+ continue;
+ }
+ memcpy(&dest, &players[i], sizeof(player_t));
+ for (j = 0; j < NUMPSPRITES; j++)
+ {
+ if (dest.psprites[j].state)
+ {
+ dest.psprites[j].state =
+ (state_t *) (dest.psprites[j].state - states);
+ }
+ }
+ SV_Write(&dest, sizeof(player_t));
+ }
+}
+
+/*
+====================
+=
+= P_UnArchivePlayers
+=
+====================
+*/
+
+void P_UnArchivePlayers(void)
+{
+ int i, j;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i])
+ continue;
+ memcpy(&players[i], save_p, sizeof(player_t));
+ save_p += sizeof(player_t);
+ players[i].mo = NULL; // will be set when unarc thinker
+ players[i].message = NULL;
+ players[i].attacker = NULL;
+ for (j = 0; j < NUMPSPRITES; j++)
+ if (players[i].psprites[j].state)
+ players[i].psprites[j].state
+ = &states[(int) players[i].psprites[j].state];
+ }
+}
+
+//=============================================================================
+
+
+/*
+====================
+=
+= P_ArchiveWorld
+=
+====================
+*/
+
+void P_ArchiveWorld(void)
+{
+ int i, j;
+ sector_t *sec;
+ line_t *li;
+ side_t *si;
+
+ // Sectors
+ for (i = 0, sec = sectors; i < numsectors; i++, sec++)
+ {
+ SV_WriteWord(sec->floorheight >> FRACBITS);
+ SV_WriteWord(sec->ceilingheight >> FRACBITS);
+ SV_WriteWord(sec->floorpic);
+ SV_WriteWord(sec->ceilingpic);
+ SV_WriteWord(sec->lightlevel);
+ SV_WriteWord(sec->special); // needed?
+ SV_WriteWord(sec->tag); // needed?
+ }
+
+ // Lines
+ for (i = 0, li = lines; i < numlines; i++, li++)
+ {
+ SV_WriteWord(li->flags);
+ SV_WriteWord(li->special);
+ SV_WriteWord(li->tag);
+ for (j = 0; j < 2; j++)
+ {
+ if (li->sidenum[j] == -1)
+ {
+ continue;
+ }
+ si = &sides[li->sidenum[j]];
+ SV_WriteWord(si->textureoffset >> FRACBITS);
+ SV_WriteWord(si->rowoffset >> FRACBITS);
+ SV_WriteWord(si->toptexture);
+ SV_WriteWord(si->bottomtexture);
+ SV_WriteWord(si->midtexture);
+ }
+ }
+}
+
+/*
+====================
+=
+= P_UnArchiveWorld
+=
+====================
+*/
+
+void P_UnArchiveWorld(void)
+{
+ int i, j;
+ sector_t *sec;
+ line_t *li;
+ side_t *si;
+ short *get;
+
+ get = (short *) save_p;
+
+//
+// do sectors
+//
+ for (i = 0, sec = sectors; i < numsectors; i++, sec++)
+ {
+ sec->floorheight = *get++ << FRACBITS;
+ sec->ceilingheight = *get++ << FRACBITS;
+ sec->floorpic = *get++;
+ sec->ceilingpic = *get++;
+ sec->lightlevel = *get++;
+ sec->special = *get++; // needed?
+ sec->tag = *get++; // needed?
+ sec->specialdata = 0;
+ sec->soundtarget = 0;
+ }
+
+//
+// do lines
+//
+ for (i = 0, li = lines; i < numlines; i++, li++)
+ {
+ li->flags = *get++;
+ li->special = *get++;
+ li->tag = *get++;
+ for (j = 0; j < 2; j++)
+ {
+ if (li->sidenum[j] == -1)
+ continue;
+ si = &sides[li->sidenum[j]];
+ si->textureoffset = *get++ << FRACBITS;
+ si->rowoffset = *get++ << FRACBITS;
+ si->toptexture = *get++;
+ si->bottomtexture = *get++;
+ si->midtexture = *get++;
+ }
+ }
+
+ save_p = (byte *) get;
+}
+
+//=============================================================================
+
+typedef enum
+{
+ tc_end,
+ tc_mobj
+} thinkerclass_t;
+
+/*
+====================
+=
+= P_ArchiveThinkers
+=
+====================
+*/
+
+void P_ArchiveThinkers(void)
+{
+ thinker_t *th;
+ mobj_t mobj;
+
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function == P_MobjThinker)
+ {
+ SV_WriteByte(tc_mobj);
+ memcpy(&mobj, th, sizeof(mobj_t));
+ mobj.state = (state_t *) (mobj.state - states);
+ if (mobj.player)
+ {
+ mobj.player = (player_t *) ((mobj.player - players) + 1);
+ }
+ SV_Write(&mobj, sizeof(mobj_t));
+ continue;
+ }
+ //I_Error("P_ArchiveThinkers: Unknown thinker function");
+ }
+
+ // Add a terminating marker
+ SV_WriteByte(tc_end);
+}
+
+/*
+====================
+=
+= P_UnArchiveThinkers
+=
+====================
+*/
+
+void P_UnArchiveThinkers(void)
+{
+ byte tclass;
+ thinker_t *currentthinker, *next;
+ mobj_t *mobj;
+
+//
+// remove all the current thinkers
+//
+ currentthinker = thinkercap.next;
+ while (currentthinker != &thinkercap)
+ {
+ next = currentthinker->next;
+ if (currentthinker->function == P_MobjThinker)
+ P_RemoveMobj((mobj_t *) currentthinker);
+ else
+ Z_Free(currentthinker);
+ currentthinker = next;
+ }
+ P_InitThinkers();
+
+// read in saved thinkers
+ while (1)
+ {
+ tclass = *save_p++;
+ switch (tclass)
+ {
+ case tc_end:
+ return; // end of list
+
+ case tc_mobj:
+ mobj = Z_Malloc(sizeof(*mobj), PU_LEVEL, NULL);
+ memcpy(mobj, save_p, sizeof(*mobj));
+ save_p += sizeof(*mobj);
+ mobj->state = &states[(int) mobj->state];
+ mobj->target = NULL;
+ if (mobj->player)
+ {
+ mobj->player = &players[(int) mobj->player - 1];
+ mobj->player->mo = mobj;
+ }
+ P_SetThingPosition(mobj);
+ mobj->info = &mobjinfo[mobj->type];
+ mobj->floorz = mobj->subsector->sector->floorheight;
+ mobj->ceilingz = mobj->subsector->sector->ceilingheight;
+ mobj->thinker.function = P_MobjThinker;
+ P_AddThinker(&mobj->thinker);
+ break;
+
+ default:
+ I_Error("Unknown tclass %i in savegame", tclass);
+ }
+
+ }
+
+}
+
+//=============================================================================
+
+
+/*
+====================
+=
+= P_ArchiveSpecials
+=
+====================
+*/
+enum
+{
+ tc_ceiling,
+ tc_door,
+ tc_floor,
+ tc_plat,
+ tc_flash,
+ tc_strobe,
+ tc_glow,
+ tc_endspecials
+} specials_e;
+
+void P_ArchiveSpecials(void)
+{
+/*
+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
+*/
+
+ thinker_t *th;
+ ceiling_t ceiling;
+ vldoor_t door;
+ floormove_t floor;
+ plat_t plat;
+ lightflash_t flash;
+ strobe_t strobe;
+ glow_t glow;
+
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function == T_MoveCeiling)
+ {
+ SV_WriteByte(tc_ceiling);
+ memcpy(&ceiling, th, sizeof(ceiling_t));
+ ceiling.sector = (sector_t *) (ceiling.sector - sectors);
+ SV_Write(&ceiling, sizeof(ceiling_t));
+ continue;
+ }
+ if (th->function == T_VerticalDoor)
+ {
+ SV_WriteByte(tc_door);
+ memcpy(&door, th, sizeof(vldoor_t));
+ door.sector = (sector_t *) (door.sector - sectors);
+ SV_Write(&door, sizeof(vldoor_t));
+ continue;
+ }
+ if (th->function == T_MoveFloor)
+ {
+ SV_WriteByte(tc_floor);
+ memcpy(&floor, th, sizeof(floormove_t));
+ floor.sector = (sector_t *) (floor.sector - sectors);
+ SV_Write(&floor, sizeof(floormove_t));
+ continue;
+ }
+ if (th->function == T_PlatRaise)
+ {
+ SV_WriteByte(tc_plat);
+ memcpy(&plat, th, sizeof(plat_t));
+ plat.sector = (sector_t *) (plat.sector - sectors);
+ SV_Write(&plat, sizeof(plat_t));
+ continue;
+ }
+ if (th->function == T_LightFlash)
+ {
+ SV_WriteByte(tc_flash);
+ memcpy(&flash, th, sizeof(lightflash_t));
+ flash.sector = (sector_t *) (flash.sector - sectors);
+ SV_Write(&flash, sizeof(lightflash_t));
+ continue;
+ }
+ if (th->function == T_StrobeFlash)
+ {
+ SV_WriteByte(tc_strobe);
+ memcpy(&strobe, th, sizeof(strobe_t));
+ strobe.sector = (sector_t *) (strobe.sector - sectors);
+ SV_Write(&strobe, sizeof(strobe_t));
+ continue;
+ }
+ if (th->function == T_Glow)
+ {
+ SV_WriteByte(tc_glow);
+ memcpy(&glow, th, sizeof(glow_t));
+ glow.sector = (sector_t *) (glow.sector - sectors);
+ SV_Write(&glow, sizeof(glow_t));
+ continue;
+ }
+ }
+ // Add a terminating marker
+ SV_WriteByte(tc_endspecials);
+}
+
+/*
+====================
+=
+= P_UnArchiveSpecials
+=
+====================
+*/
+
+void P_UnArchiveSpecials(void)
+{
+ byte tclass;
+ ceiling_t *ceiling;
+ vldoor_t *door;
+ floormove_t *floor;
+ plat_t *plat;
+ lightflash_t *flash;
+ strobe_t *strobe;
+ glow_t *glow;
+
+
+// read in saved thinkers
+ while (1)
+ {
+ tclass = *save_p++;
+ switch (tclass)
+ {
+ case tc_endspecials:
+ return; // end of list
+
+ case tc_ceiling:
+ ceiling = Z_Malloc(sizeof(*ceiling), PU_LEVEL, NULL);
+ memcpy(ceiling, save_p, sizeof(*ceiling));
+ save_p += sizeof(*ceiling);
+ ceiling->sector = &sectors[(int) ceiling->sector];
+ ceiling->sector->specialdata = T_MoveCeiling;
+ if (ceiling->thinker.function)
+ ceiling->thinker.function = T_MoveCeiling;
+ P_AddThinker(&ceiling->thinker);
+ P_AddActiveCeiling(ceiling);
+ break;
+
+ case tc_door:
+ door = Z_Malloc(sizeof(*door), PU_LEVEL, NULL);
+ memcpy(door, save_p, sizeof(*door));
+ save_p += sizeof(*door);
+ door->sector = &sectors[(int) door->sector];
+ door->sector->specialdata = door;
+ door->thinker.function = T_VerticalDoor;
+ P_AddThinker(&door->thinker);
+ break;
+
+ case tc_floor:
+ floor = Z_Malloc(sizeof(*floor), PU_LEVEL, NULL);
+ memcpy(floor, save_p, sizeof(*floor));
+ save_p += sizeof(*floor);
+ floor->sector = &sectors[(int) floor->sector];
+ floor->sector->specialdata = T_MoveFloor;
+ floor->thinker.function = T_MoveFloor;
+ P_AddThinker(&floor->thinker);
+ break;
+
+ case tc_plat:
+ plat = Z_Malloc(sizeof(*plat), PU_LEVEL, NULL);
+ memcpy(plat, save_p, sizeof(*plat));
+ save_p += sizeof(*plat);
+ plat->sector = &sectors[(int) plat->sector];
+ plat->sector->specialdata = T_PlatRaise;
+ if (plat->thinker.function)
+ plat->thinker.function = T_PlatRaise;
+ P_AddThinker(&plat->thinker);
+ P_AddActivePlat(plat);
+ break;
+
+ case tc_flash:
+ flash = Z_Malloc(sizeof(*flash), PU_LEVEL, NULL);
+ memcpy(flash, save_p, sizeof(*flash));
+ save_p += sizeof(*flash);
+ flash->sector = &sectors[(int) flash->sector];
+ flash->thinker.function = T_LightFlash;
+ P_AddThinker(&flash->thinker);
+ break;
+
+ case tc_strobe:
+ strobe = Z_Malloc(sizeof(*strobe), PU_LEVEL, NULL);
+ memcpy(strobe, save_p, sizeof(*strobe));
+ save_p += sizeof(*strobe);
+ strobe->sector = &sectors[(int) strobe->sector];
+ strobe->thinker.function = T_StrobeFlash;
+ P_AddThinker(&strobe->thinker);
+ break;
+
+ case tc_glow:
+ glow = Z_Malloc(sizeof(*glow), PU_LEVEL, NULL);
+ memcpy(glow, save_p, sizeof(*glow));
+ save_p += sizeof(*glow);
+ glow->sector = &sectors[(int) glow->sector];
+ glow->thinker.function = T_Glow;
+ P_AddThinker(&glow->thinker);
+ break;
+
+ default:
+ I_Error("P_UnarchiveSpecials:Unknown tclass %i "
+ "in savegame", tclass);
+ }
+
+ }
+
+}
+
+
+
+/*
+===============================================================================
+
+ 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.
+
+===============================================================================
+*/
+
+thinker_t thinkercap; // both the head and tail of the thinker list
+
+/*
+===============
+=
+= P_InitThinkers
+=
+===============
+*/
+
+void P_InitThinkers(void)
+{
+ thinkercap.prev = thinkercap.next = &thinkercap;
+}
+
+
+/*
+===============
+=
+= 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;
+}
+
+/*
+===============
+=
+= P_RemoveThinker
+=
+= Deallocation is lazy -- it will not actually be freed until its
+= thinking turn comes up
+=
+===============
+*/
+
+void P_RemoveThinker(thinker_t * thinker)
+{
+ thinker->function = (think_t) - 1;
+}
+
+/*
+===============
+=
+= P_AllocateThinker
+=
+= Allocates memory and adds a new thinker at the end of the list
+=
+===============
+*/
+
+void P_AllocateThinker(thinker_t * thinker)
+{
+}
+
+
+/*
+===============
+=
+= P_RunThinkers
+=
+===============
+*/
+
+void P_RunThinkers(void)
+{
+ thinker_t *currentthinker;
+
+ currentthinker = thinkercap.next;
+ while (currentthinker != &thinkercap)
+ {
+ if (currentthinker->function == (think_t) - 1)
+ { // time to remove it
+ currentthinker->next->prev = currentthinker->prev;
+ currentthinker->prev->next = currentthinker->next;
+ Z_Free(currentthinker);
+ }
+ else
+ {
+ if (currentthinker->function)
+ currentthinker->function(currentthinker);
+ }
+ currentthinker = currentthinker->next;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_Ticker
+//
+//----------------------------------------------------------------------------
+
+void P_Ticker(void)
+{
+ int i;
+
+ if (paused)
+ {
+ return;
+ }
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i])
+ {
+ P_PlayerThink(&players[i]);
+ }
+ }
+ if (TimerGame)
+ {
+ if (!--TimerGame)
+ {
+ G_ExitLevel();
+ }
+ }
+ P_RunThinkers();
+ P_UpdateSpecials();
+ P_AmbientSound();
+ leveltime++;
+}
diff --git a/src/heretic/p_user.c b/src/heretic/p_user.c
new file mode 100644
index 00000000..16dbed49
--- /dev/null
+++ b/src/heretic/p_user.c
@@ -0,0 +1,1028 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// P_user.c
+
+#include <stdlib.h>
+
+#include "doomdef.h"
+#include "deh_str.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "s_sound.h"
+
+void P_PlayerNextArtifact(player_t * player);
+
+// Macros
+
+#define MAXBOB 0x100000 // 16 pixels of bob
+
+// Data
+
+boolean onground;
+int newtorch; // used in the torch flicker effect.
+int newtorchdelta;
+
+boolean WeaponInShareware[] = {
+ true, // Staff
+ true, // Gold wand
+ true, // Crossbow
+ true, // Blaster
+ false, // Skull rod
+ false, // Phoenix rod
+ false, // Mace
+ true, // Gauntlets
+ true // Beak
+};
+
+/*
+==================
+=
+= P_Thrust
+=
+= moves the given origin along a given angle
+=
+==================
+*/
+
+void P_Thrust(player_t * player, angle_t angle, fixed_t move)
+{
+ angle >>= ANGLETOFINESHIFT;
+ if (player->powers[pw_flight] && !(player->mo->z <= player->mo->floorz))
+ {
+ player->mo->momx += FixedMul(move, finecosine[angle]);
+ player->mo->momy += FixedMul(move, finesine[angle]);
+ }
+ else if (player->mo->subsector->sector->special == 15) // Friction_Low
+ {
+ player->mo->momx += FixedMul(move >> 2, finecosine[angle]);
+ player->mo->momy += FixedMul(move >> 2, finesine[angle]);
+ }
+ else
+ {
+ player->mo->momx += FixedMul(move, finecosine[angle]);
+ player->mo->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
+
+ player->bob = FixedMul(player->mo->momx, player->mo->momx) +
+ FixedMul(player->mo->momy, player->mo->momy);
+ player->bob >>= 2;
+ if (player->bob > MAXBOB)
+ player->bob = MAXBOB;
+ if (player->mo->flags2 & MF2_FLY && !onground)
+ {
+ player->bob = FRACUNIT / 2;
+ }
+
+ if ((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;
+ 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;
+ }
+ }
+
+ if (player->chickenTics)
+ {
+ player->viewz = player->mo->z + player->viewheight - (20 * FRACUNIT);
+ }
+ else
+ {
+ player->viewz = player->mo->z + player->viewheight + bob;
+ }
+ if (player->mo->flags2 & MF2_FEETARECLIPPED
+ && player->playerstate != PST_DEAD
+ && player->mo->z <= player->mo->floorz)
+ {
+ player->viewz -= FOOTCLIPSIZE;
+ }
+ if (player->viewz > player->mo->ceilingz - 4 * FRACUNIT)
+ {
+ player->viewz = player->mo->ceilingz - 4 * FRACUNIT;
+ }
+ if (player->viewz < player->mo->floorz + 4 * FRACUNIT)
+ {
+ player->viewz = player->mo->floorz + 4 * FRACUNIT;
+ }
+}
+
+/*
+=================
+=
+= P_MovePlayer
+=
+=================
+*/
+
+void P_MovePlayer(player_t * player)
+{
+ int look;
+ int fly;
+ ticcmd_t *cmd;
+
+ cmd = &player->cmd;
+ player->mo->angle += (cmd->angleturn << 16);
+
+ onground = (player->mo->z <= player->mo->floorz
+ || (player->mo->flags2 & MF2_ONMOBJ));
+
+ if (player->chickenTics)
+ { // Chicken speed
+ if (cmd->forwardmove && (onground || player->mo->flags2 & MF2_FLY))
+ P_Thrust(player, player->mo->angle, cmd->forwardmove * 2500);
+ if (cmd->sidemove && (onground || player->mo->flags2 & MF2_FLY))
+ P_Thrust(player, player->mo->angle - ANG90, cmd->sidemove * 2500);
+ }
+ else
+ { // Normal speed
+ if (cmd->forwardmove && (onground || player->mo->flags2 & MF2_FLY))
+ P_Thrust(player, player->mo->angle, cmd->forwardmove * 2048);
+ if (cmd->sidemove && (onground || player->mo->flags2 & MF2_FLY))
+ P_Thrust(player, player->mo->angle - ANG90, cmd->sidemove * 2048);
+ }
+
+ if (cmd->forwardmove || cmd->sidemove)
+ {
+ if (player->chickenTics)
+ {
+ if (player->mo->state == &states[S_CHICPLAY])
+ {
+ P_SetMobjState(player->mo, S_CHICPLAY_RUN1);
+ }
+ }
+ else
+ {
+ if (player->mo->state == &states[S_PLAY])
+ {
+ P_SetMobjState(player->mo, S_PLAY_RUN1);
+ }
+ }
+ }
+
+ look = cmd->lookfly & 15;
+ if (look > 7)
+ {
+ look -= 16;
+ }
+ if (look)
+ {
+ if (look == TOCENTER)
+ {
+ player->centering = true;
+ }
+ else
+ {
+ player->lookdir += 5 * look;
+ if (player->lookdir > 90 || player->lookdir < -110)
+ {
+ player->lookdir -= 5 * look;
+ }
+ }
+ }
+ if (player->centering)
+ {
+ if (player->lookdir > 0)
+ {
+ player->lookdir -= 8;
+ }
+ else if (player->lookdir < 0)
+ {
+ player->lookdir += 8;
+ }
+ if (abs(player->lookdir) < 8)
+ {
+ player->lookdir = 0;
+ player->centering = false;
+ }
+ }
+ fly = cmd->lookfly >> 4;
+ if (fly > 7)
+ {
+ fly -= 16;
+ }
+ if (fly && player->powers[pw_flight])
+ {
+ if (fly != TOCENTER)
+ {
+ player->flyheight = fly * 2;
+ if (!(player->mo->flags2 & MF2_FLY))
+ {
+ player->mo->flags2 |= MF2_FLY;
+ player->mo->flags |= MF_NOGRAVITY;
+ }
+ }
+ else
+ {
+ player->mo->flags2 &= ~MF2_FLY;
+ player->mo->flags &= ~MF_NOGRAVITY;
+ }
+ }
+ else if (fly > 0)
+ {
+ P_PlayerUseArtifact(player, arti_fly);
+ }
+ if (player->mo->flags2 & MF2_FLY)
+ {
+ player->mo->momz = player->flyheight * FRACUNIT;
+ if (player->flyheight)
+ {
+ player->flyheight /= 2;
+ }
+ }
+}
+
+/*
+=================
+=
+= P_DeathThink
+=
+=================
+*/
+
+#define ANG5 (ANG90/18)
+
+void P_DeathThink(player_t * player)
+{
+ angle_t angle, delta;
+ extern int inv_ptr;
+ extern int curpos;
+ int lookDelta;
+
+ P_MovePsprites(player);
+
+ onground = (player->mo->z <= player->mo->floorz);
+ if (player->mo->type == MT_BLOODYSKULL)
+ { // Flying bloody skull
+ player->viewheight = 6 * FRACUNIT;
+ player->deltaviewheight = 0;
+ //player->damagecount = 20;
+ if (onground)
+ {
+ if (player->lookdir < 60)
+ {
+ lookDelta = (60 - player->lookdir) / 8;
+ if (lookDelta < 1 && (leveltime & 1))
+ {
+ lookDelta = 1;
+ }
+ else if (lookDelta > 6)
+ {
+ lookDelta = 6;
+ }
+ player->lookdir += lookDelta;
+ }
+ }
+ }
+ else
+ { // Fall to ground
+ player->deltaviewheight = 0;
+ if (player->viewheight > 6 * FRACUNIT)
+ player->viewheight -= FRACUNIT;
+ if (player->viewheight < 6 * FRACUNIT)
+ player->viewheight = 6 * FRACUNIT;
+ if (player->lookdir > 0)
+ {
+ player->lookdir -= 6;
+ }
+ else if (player->lookdir < 0)
+ {
+ player->lookdir += 6;
+ }
+ if (abs(player->lookdir) < 6)
+ {
+ player->lookdir = 0;
+ }
+ }
+ 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)
+ {
+ if (player == &players[consoleplayer])
+ {
+ I_SetPalette(W_CacheLumpName(DEH_String("PLAYPAL"), PU_CACHE));
+ inv_ptr = 0;
+ curpos = 0;
+ newtorch = 0;
+ newtorchdelta = 0;
+ }
+ player->playerstate = PST_REBORN;
+ // Let the mobj know the player has entered the reborn state. Some
+ // mobjs need to know when it's ok to remove themselves.
+ player->mo->special2 = 666;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_ChickenPlayerThink
+//
+//----------------------------------------------------------------------------
+
+void P_ChickenPlayerThink(player_t * player)
+{
+ mobj_t *pmo;
+
+ if (player->health > 0)
+ { // Handle beak movement
+ P_UpdateBeak(player, &player->psprites[ps_weapon]);
+ }
+ if (player->chickenTics & 15)
+ {
+ return;
+ }
+ pmo = player->mo;
+ if (!(pmo->momx + pmo->momy) && P_Random() < 160)
+ { // Twitch view angle
+ pmo->angle += (P_Random() - P_Random()) << 19;
+ }
+ if ((pmo->z <= pmo->floorz) && (P_Random() < 32))
+ { // Jump and noise
+ pmo->momz += FRACUNIT;
+ P_SetMobjState(pmo, S_CHICPLAY_PAIN);
+ return;
+ }
+ if (P_Random() < 48)
+ { // Just noise
+ S_StartSound(pmo, sfx_chicact);
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_GetPlayerNum
+//
+//----------------------------------------------------------------------------
+
+int P_GetPlayerNum(player_t * player)
+{
+ int i;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (player == &players[i])
+ {
+ return (i);
+ }
+ }
+ return (0);
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_UndoPlayerChicken
+//
+//----------------------------------------------------------------------------
+
+boolean P_UndoPlayerChicken(player_t * player)
+{
+ mobj_t *fog;
+ mobj_t *mo;
+ mobj_t *pmo;
+ fixed_t x;
+ fixed_t y;
+ fixed_t z;
+ angle_t angle;
+ int playerNum;
+ weapontype_t weapon;
+ int oldFlags;
+ int oldFlags2;
+
+ pmo = player->mo;
+ x = pmo->x;
+ y = pmo->y;
+ z = pmo->z;
+ angle = pmo->angle;
+ weapon = pmo->special1;
+ oldFlags = pmo->flags;
+ oldFlags2 = pmo->flags2;
+ P_SetMobjState(pmo, S_FREETARGMOBJ);
+ mo = P_SpawnMobj(x, y, z, MT_PLAYER);
+ if (P_TestMobjLocation(mo) == false)
+ { // Didn't fit
+ P_RemoveMobj(mo);
+ mo = P_SpawnMobj(x, y, z, MT_CHICPLAYER);
+ mo->angle = angle;
+ mo->health = player->health;
+ mo->special1 = weapon;
+ mo->player = player;
+ mo->flags = oldFlags;
+ mo->flags2 = oldFlags2;
+ player->mo = mo;
+ player->chickenTics = 2 * 35;
+ return (false);
+ }
+ playerNum = P_GetPlayerNum(player);
+ if (playerNum != 0)
+ { // Set color translation
+ mo->flags |= playerNum << MF_TRANSSHIFT;
+ }
+ mo->angle = angle;
+ mo->player = player;
+ mo->reactiontime = 18;
+ if (oldFlags2 & MF2_FLY)
+ {
+ mo->flags2 |= MF2_FLY;
+ mo->flags |= MF_NOGRAVITY;
+ }
+ player->chickenTics = 0;
+ player->powers[pw_weaponlevel2] = 0;
+ player->health = mo->health = MAXHEALTH;
+ player->mo = mo;
+ angle >>= ANGLETOFINESHIFT;
+ fog = P_SpawnMobj(x + 20 * finecosine[angle],
+ y + 20 * finesine[angle], z + TELEFOGHEIGHT, MT_TFOG);
+ S_StartSound(fog, sfx_telept);
+ P_PostChickenWeapon(player, weapon);
+ return (true);
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_PlayerThink
+//
+//----------------------------------------------------------------------------
+
+void P_PlayerThink(player_t * player)
+{
+ ticcmd_t *cmd;
+ weapontype_t newweapon;
+
+ extern boolean ultimatemsg;
+
+ // No-clip cheat
+ if (player->cheats & CF_NOCLIP)
+ {
+ player->mo->flags |= MF_NOCLIP;
+ }
+ else
+ {
+ player->mo->flags &= ~MF_NOCLIP;
+ }
+ cmd = &player->cmd;
+ if (player->mo->flags & MF_JUSTATTACKED)
+ { // Gauntlets attack auto forward motion
+ cmd->angleturn = 0;
+ cmd->forwardmove = 0xc800 / 512;
+ cmd->sidemove = 0;
+ player->mo->flags &= ~MF_JUSTATTACKED;
+ }
+// messageTics is above the rest of the counters so that messages will
+// go away, even in death.
+ player->messageTics--; // Can go negative
+ if (!player->messageTics)
+ { // Refresh the screen when a message goes away
+ ultimatemsg = false; // clear out any chat messages.
+ BorderTopRefresh = true;
+ }
+ if (player->playerstate == PST_DEAD)
+ {
+ P_DeathThink(player);
+ return;
+ }
+ if (player->chickenTics)
+ {
+ P_ChickenPlayerThink(player);
+ }
+ // Handle movement
+ if (player->mo->reactiontime)
+ { // Player is frozen
+ player->mo->reactiontime--;
+ }
+ else
+ {
+ P_MovePlayer(player);
+ }
+ P_CalcHeight(player);
+ if (player->mo->subsector->sector->special)
+ {
+ P_PlayerInSpecialSector(player);
+ }
+ if (cmd->arti)
+ { // Use an artifact
+ if (cmd->arti == 0xff)
+ {
+ P_PlayerNextArtifact(player);
+ }
+ else
+ {
+ P_PlayerUseArtifact(player, cmd->arti);
+ }
+ }
+ // Check for weapon change
+ if (cmd->buttons & BT_SPECIAL)
+ { // A special event has no other buttons
+ cmd->buttons = 0;
+ }
+ if (cmd->buttons & BT_CHANGE)
+ {
+ // The actual changing of the weapon is done when the weapon
+ // psprite can do it (A_WeaponReady), so it doesn't happen in
+ // the middle of an attack.
+ newweapon = (cmd->buttons & BT_WEAPONMASK) >> BT_WEAPONSHIFT;
+ if (newweapon == wp_staff && player->weaponowned[wp_gauntlets]
+ && !(player->readyweapon == wp_gauntlets))
+ {
+ newweapon = wp_gauntlets;
+ }
+ if (player->weaponowned[newweapon]
+ && newweapon != player->readyweapon)
+ {
+ if (WeaponInShareware[newweapon] || 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;
+ }
+ // Chicken counter
+ if (player->chickenTics)
+ {
+ if (player->chickenPeck)
+ { // Chicken attack counter
+ player->chickenPeck -= 3;
+ }
+ if (!--player->chickenTics)
+ { // Attempt to undo the chicken
+ P_UndoPlayerChicken(player);
+ }
+ }
+ // Cycle psprites
+ P_MovePsprites(player);
+ // Other Counters
+ if (player->powers[pw_invulnerability])
+ {
+ player->powers[pw_invulnerability]--;
+ }
+ if (player->powers[pw_invisibility])
+ {
+ if (!--player->powers[pw_invisibility])
+ {
+ player->mo->flags &= ~MF_SHADOW;
+ }
+ }
+ if (player->powers[pw_infrared])
+ {
+ player->powers[pw_infrared]--;
+ }
+ if (player->powers[pw_flight])
+ {
+ if (!--player->powers[pw_flight])
+ {
+ // haleyjd: removed externdriver crap
+ if (player->mo->z != player->mo->floorz)
+ {
+ player->centering = true;
+ }
+
+ player->mo->flags2 &= ~MF2_FLY;
+ player->mo->flags &= ~MF_NOGRAVITY;
+ BorderTopRefresh = true; //make sure the sprite's cleared out
+ }
+ }
+ if (player->powers[pw_weaponlevel2])
+ {
+ if (!--player->powers[pw_weaponlevel2])
+ {
+ if ((player->readyweapon == wp_phoenixrod)
+ && (player->psprites[ps_weapon].state
+ != &states[S_PHOENIXREADY])
+ && (player->psprites[ps_weapon].state
+ != &states[S_PHOENIXUP]))
+ {
+ P_SetPsprite(player, ps_weapon, S_PHOENIXREADY);
+ player->ammo[am_phoenixrod] -= USE_PHRD_AMMO_2;
+ player->refire = 0;
+ }
+ else if ((player->readyweapon == wp_gauntlets)
+ || (player->readyweapon == wp_staff))
+ {
+ player->pendingweapon = player->readyweapon;
+ }
+ BorderTopRefresh = true;
+ }
+ }
+ if (player->damagecount)
+ {
+ player->damagecount--;
+ }
+ if (player->bonuscount)
+ {
+ player->bonuscount--;
+ }
+ // Colormaps
+ if (player->powers[pw_invulnerability])
+ {
+ if (player->powers[pw_invulnerability] > BLINKTHRESHOLD
+ || (player->powers[pw_invulnerability] & 8))
+ {
+ player->fixedcolormap = INVERSECOLORMAP;
+ }
+ else
+ {
+ player->fixedcolormap = 0;
+ }
+ }
+ else if (player->powers[pw_infrared])
+ {
+ if (player->powers[pw_infrared] <= BLINKTHRESHOLD)
+ {
+ if (player->powers[pw_infrared] & 8)
+ {
+ player->fixedcolormap = 0;
+ }
+ else
+ {
+ player->fixedcolormap = 1;
+ }
+ }
+ else if (!(leveltime & 16) && player == &players[consoleplayer])
+ {
+ if (newtorch)
+ {
+ if (player->fixedcolormap + newtorchdelta > 7
+ || player->fixedcolormap + newtorchdelta < 1
+ || newtorch == player->fixedcolormap)
+ {
+ newtorch = 0;
+ }
+ else
+ {
+ player->fixedcolormap += newtorchdelta;
+ }
+ }
+ else
+ {
+ newtorch = (M_Random() & 7) + 1;
+ newtorchdelta = (newtorch == player->fixedcolormap) ?
+ 0 : ((newtorch > player->fixedcolormap) ? 1 : -1);
+ }
+ }
+ }
+ else
+ {
+ player->fixedcolormap = 0;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_ArtiTele
+//
+//----------------------------------------------------------------------------
+
+void P_ArtiTele(player_t * player)
+{
+ int i;
+ int selections;
+ fixed_t destX;
+ fixed_t destY;
+ angle_t destAngle;
+
+ if (deathmatch)
+ {
+ selections = deathmatch_p - deathmatchstarts;
+ i = P_Random() % selections;
+ destX = deathmatchstarts[i].x << FRACBITS;
+ destY = deathmatchstarts[i].y << FRACBITS;
+ destAngle = ANG45 * (deathmatchstarts[i].angle / 45);
+ }
+ else
+ {
+ destX = playerstarts[0].x << FRACBITS;
+ destY = playerstarts[0].y << FRACBITS;
+ destAngle = ANG45 * (playerstarts[0].angle / 45);
+ }
+ P_Teleport(player->mo, destX, destY, destAngle);
+ S_StartSound(NULL, sfx_wpnup); // Full volume laugh
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_PlayerNextArtifact
+//
+//----------------------------------------------------------------------------
+
+void P_PlayerNextArtifact(player_t * player)
+{
+ extern int inv_ptr;
+ extern int curpos;
+
+ if (player == &players[consoleplayer])
+ {
+ inv_ptr--;
+ if (inv_ptr < 6)
+ {
+ curpos--;
+ if (curpos < 0)
+ {
+ curpos = 0;
+ }
+ }
+ if (inv_ptr < 0)
+ {
+ inv_ptr = player->inventorySlotNum - 1;
+ if (inv_ptr < 6)
+ {
+ curpos = inv_ptr;
+ }
+ else
+ {
+ curpos = 6;
+ }
+ }
+ player->readyArtifact = player->inventory[inv_ptr].type;
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_PlayerRemoveArtifact
+//
+//----------------------------------------------------------------------------
+
+void P_PlayerRemoveArtifact(player_t * player, int slot)
+{
+ int i;
+ extern int inv_ptr;
+ extern int curpos;
+
+ player->artifactCount--;
+ if (!(--player->inventory[slot].count))
+ { // Used last of a type - compact the artifact list
+ player->readyArtifact = arti_none;
+ player->inventory[slot].type = arti_none;
+ for (i = slot + 1; i < player->inventorySlotNum; i++)
+ {
+ player->inventory[i - 1] = player->inventory[i];
+ }
+ player->inventorySlotNum--;
+ if (player == &players[consoleplayer])
+ { // Set position markers and get next readyArtifact
+ inv_ptr--;
+ if (inv_ptr < 6)
+ {
+ curpos--;
+ if (curpos < 0)
+ {
+ curpos = 0;
+ }
+ }
+ if (inv_ptr >= player->inventorySlotNum)
+ {
+ inv_ptr = player->inventorySlotNum - 1;
+ }
+ if (inv_ptr < 0)
+ {
+ inv_ptr = 0;
+ }
+ player->readyArtifact = player->inventory[inv_ptr].type;
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC P_PlayerUseArtifact
+//
+//----------------------------------------------------------------------------
+
+void P_PlayerUseArtifact(player_t * player, artitype_t arti)
+{
+ int i;
+
+ for (i = 0; i < player->inventorySlotNum; i++)
+ {
+ if (player->inventory[i].type == arti)
+ { // Found match - try to use
+ if (P_UseArtifact(player, arti))
+ { // Artifact was used - remove it from inventory
+ P_PlayerRemoveArtifact(player, i);
+ if (player == &players[consoleplayer])
+ {
+ S_StartSound(NULL, sfx_artiuse);
+ ArtifactFlash = 4;
+ }
+ }
+ else
+ { // Unable to use artifact, advance pointer
+ P_PlayerNextArtifact(player);
+ }
+ break;
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+//
+// FUNC P_UseArtifact
+//
+// Returns true if artifact was used.
+//
+//----------------------------------------------------------------------------
+
+boolean P_UseArtifact(player_t * player, artitype_t arti)
+{
+ mobj_t *mo;
+ angle_t angle;
+
+ switch (arti)
+ {
+ case arti_invulnerability:
+ if (!P_GivePower(player, pw_invulnerability))
+ {
+ return (false);
+ }
+ break;
+ case arti_invisibility:
+ if (!P_GivePower(player, pw_invisibility))
+ {
+ return (false);
+ }
+ break;
+ case arti_health:
+ if (!P_GiveBody(player, 25))
+ {
+ return (false);
+ }
+ break;
+ case arti_superhealth:
+ if (!P_GiveBody(player, 100))
+ {
+ return (false);
+ }
+ break;
+ case arti_tomeofpower:
+ if (player->chickenTics)
+ { // Attempt to undo chicken
+ if (P_UndoPlayerChicken(player) == false)
+ { // Failed
+ P_DamageMobj(player->mo, NULL, NULL, 10000);
+ }
+ else
+ { // Succeeded
+ player->chickenTics = 0;
+ S_StartSound(player->mo, sfx_wpnup);
+ }
+ }
+ else
+ {
+ if (!P_GivePower(player, pw_weaponlevel2))
+ {
+ return (false);
+ }
+ if (player->readyweapon == wp_staff)
+ {
+ P_SetPsprite(player, ps_weapon, S_STAFFREADY2_1);
+ }
+ else if (player->readyweapon == wp_gauntlets)
+ {
+ P_SetPsprite(player, ps_weapon, S_GAUNTLETREADY2_1);
+ }
+ }
+ break;
+ case arti_torch:
+ if (!P_GivePower(player, pw_infrared))
+ {
+ return (false);
+ }
+ break;
+ case arti_firebomb:
+ angle = player->mo->angle >> ANGLETOFINESHIFT;
+ mo = P_SpawnMobj(player->mo->x + 24 * finecosine[angle],
+ player->mo->y + 24 * finesine[angle],
+ player->mo->z -
+ 15 * FRACUNIT *
+ (player->mo->flags2 & MF2_FEETARECLIPPED) != 0,
+ MT_FIREBOMB);
+ mo->target = player->mo;
+ break;
+ case arti_egg:
+ mo = player->mo;
+ P_SpawnPlayerMissile(mo, MT_EGGFX);
+ P_SPMAngle(mo, MT_EGGFX, mo->angle - (ANG45 / 6));
+ P_SPMAngle(mo, MT_EGGFX, mo->angle + (ANG45 / 6));
+ P_SPMAngle(mo, MT_EGGFX, mo->angle - (ANG45 / 3));
+ P_SPMAngle(mo, MT_EGGFX, mo->angle + (ANG45 / 3));
+ break;
+ case arti_fly:
+ if (!P_GivePower(player, pw_flight))
+ {
+ return (false);
+ }
+ break;
+ case arti_teleport:
+ P_ArtiTele(player);
+ break;
+ default:
+ return (false);
+ }
+ return (true);
+}
diff --git a/src/heretic/r_bsp.c b/src/heretic/r_bsp.c
new file mode 100644
index 00000000..16ef8ae1
--- /dev/null
+++ b/src/heretic/r_bsp.c
@@ -0,0 +1,484 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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_bsp.c
+
+#include "doomdef.h"
+#include "m_bbox.h"
+#include "r_local.h"
+
+seg_t *curline;
+side_t *sidedef;
+line_t *linedef;
+sector_t *frontsector, *backsector;
+
+drawseg_t drawsegs[MAXDRAWSEGS], *ds_p;
+
+void R_StoreWallRange(int start, int stop);
+
+/*
+====================
+=
+= R_ClearDrawSegs
+=
+====================
+*/
+
+void R_ClearDrawSegs(void)
+{
+ ds_p = drawsegs;
+}
+
+//=============================================================================
+
+
+/*
+===============================================================================
+=
+= ClipWallSegment
+=
+= Clips the given range of columns and includes it in the new clip list
+===============================================================================
+*/
+
+typedef struct
+{
+ int first, last;
+} cliprange_t;
+
+#define MAXSEGS 32
+
+cliprange_t solidsegs[MAXSEGS], *newend; // newend is one past the last valid seg
+
+
+void R_ClipSolidWallSegment(int first, int last)
+{
+ cliprange_t *next, *start;
+
+// find the first range that touches the range (adjacent pixels are touching)
+ start = solidsegs;
+ while (start->last < first - 1)
+ start++;
+
+ if (first < start->first)
+ {
+ if (last < start->first - 1)
+ { // post is entirely visible (above start), so insert a new clippost
+ R_StoreWallRange(first, last);
+ next = newend;
+ newend++;
+ while (next != start)
+ {
+ *next = *(next - 1);
+ next--;
+ }
+ next->first = first;
+ next->last = last;
+ return;
+ }
+
+ // there is a fragment above *start
+ R_StoreWallRange(first, start->first - 1);
+ start->first = first; // adjust the clip size
+ }
+
+ if (last <= start->last)
+ return; // bottom contained in start
+
+ next = start;
+ while (last >= (next + 1)->first - 1)
+ {
+ // there is a fragment between two posts
+ R_StoreWallRange(next->last + 1, (next + 1)->first - 1);
+ next++;
+ if (last <= next->last)
+ { // bottom is contained in next
+ start->last = next->last; // adjust the clip size
+ goto crunch;
+ }
+ }
+
+ // there is a fragment after *next
+ R_StoreWallRange(next->last + 1, last);
+ start->last = last; // adjust the clip size
+
+
+// remove start+1 to next from the clip list,
+// because start now covers their area
+ crunch:
+ if (next == start)
+ return; // post just extended past the bottom of one post
+
+ while (next++ != newend) // remove a post
+ *++start = *next;
+ newend = start + 1;
+}
+
+/*
+===============================================================================
+=
+= R_ClipPassWallSegment
+=
+= Clips the given range of columns, but does not includes it in the clip list
+===============================================================================
+*/
+
+void R_ClipPassWallSegment(int first, int last)
+{
+ cliprange_t *start;
+
+// find the first range that touches the range (adjacent pixels are touching)
+ start = solidsegs;
+ while (start->last < first - 1)
+ start++;
+
+ if (first < start->first)
+ {
+ if (last < start->first - 1)
+ { // post is entirely visible (above start)
+ R_StoreWallRange(first, last);
+ return;
+ }
+
+ // there is a fragment above *start
+ R_StoreWallRange(first, start->first - 1);
+ }
+
+ if (last <= start->last)
+ return; // bottom contained in start
+
+ while (last >= (start + 1)->first - 1)
+ {
+ // there is a fragment between two posts
+ R_StoreWallRange(start->last + 1, (start + 1)->first - 1);
+ start++;
+ if (last <= start->last)
+ return;
+ }
+
+ // there is a fragment after *next
+ R_StoreWallRange(start->last + 1, last);
+}
+
+
+
+/*
+====================
+=
+= R_ClearClipSegs
+=
+====================
+*/
+
+void R_ClearClipSegs(void)
+{
+ solidsegs[0].first = -0x7fffffff;
+ solidsegs[0].last = -1;
+ solidsegs[1].first = viewwidth;
+ solidsegs[1].last = 0x7fffffff;
+ newend = solidsegs + 2;
+}
+
+
+//=============================================================================
+
+/*
+======================
+=
+= R_AddLine
+=
+= Clips the given segment and adds any visible pieces to the line list
+=
+======================
+*/
+
+void R_AddLine(seg_t * line)
+{
+ int x1, x2;
+ angle_t angle1, angle2, span, tspan;
+
+ curline = line;
+
+// OPTIMIZE: quickly reject orthogonal back sides
+
+ angle1 = R_PointToAngle(line->v1->x, line->v1->y);
+ angle2 = R_PointToAngle(line->v2->x, line->v2->y);
+
+//
+// clip to view edges
+// OPTIMIZE: make constant out of 2*clipangle (FIELDOFVIEW)
+ span = angle1 - angle2;
+ if (span >= ANG180)
+ return; // back side
+
+ rw_angle1 = angle1; // global angle needed by segcalc
+ angle1 -= viewangle;
+ angle2 -= viewangle;
+
+ tspan = angle1 + clipangle;
+ if (tspan > 2 * clipangle)
+ {
+ tspan -= 2 * clipangle;
+ if (tspan >= span)
+ return; // totally off the left edge
+ angle1 = clipangle;
+ }
+ tspan = clipangle - angle2;
+ if (tspan > 2 * clipangle)
+ {
+ tspan -= 2 * clipangle;
+ if (tspan >= span)
+ return; // totally off the left edge
+ angle2 = -clipangle;
+ }
+
+//
+// the seg is in the view range, but not necessarily visible
+//
+ angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT;
+ angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT;
+ x1 = viewangletox[angle1];
+ x2 = viewangletox[angle2];
+ if (x1 == x2)
+ return; // does not cross a pixel
+
+ backsector = line->backsector;
+
+ if (!backsector)
+ goto clipsolid; // single sided line
+
+ if (backsector->ceilingheight <= frontsector->floorheight
+ || backsector->floorheight >= frontsector->ceilingheight)
+ goto clipsolid; // closed door
+
+ if (backsector->ceilingheight != frontsector->ceilingheight
+ || backsector->floorheight != frontsector->floorheight)
+ goto clippass; // window
+
+// reject empty lines used for triggers and special events
+ if (backsector->ceilingpic == frontsector->ceilingpic
+ && backsector->floorpic == frontsector->floorpic
+ && backsector->lightlevel == frontsector->lightlevel
+ && curline->sidedef->midtexture == 0)
+ return;
+
+ clippass:
+ R_ClipPassWallSegment(x1, x2 - 1);
+ return;
+
+ clipsolid:
+ R_ClipSolidWallSegment(x1, x2 - 1);
+}
+
+//============================================================================
+
+
+/*
+===============================================================================
+=
+= R_CheckBBox
+=
+= Returns true if some part of the bbox might be visible
+=
+===============================================================================
+*/
+
+int checkcoord[12][4] = {
+ {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}
+};
+
+
+boolean R_CheckBBox(fixed_t * bspcoord)
+{
+ int boxx, boxy, boxpos;
+ fixed_t x1, y1, x2, y2;
+ angle_t angle1, angle2, span, tspan;
+ cliprange_t *start;
+ int sx1, sx2;
+
+// find the corners of the box that define the edges from current viewpoint
+ if (viewx <= bspcoord[BOXLEFT])
+ boxx = 0;
+ else if (viewx < bspcoord[BOXRIGHT])
+ boxx = 1;
+ else
+ boxx = 2;
+
+ if (viewy >= bspcoord[BOXTOP])
+ boxy = 0;
+ else if (viewy > bspcoord[BOXBOTTOM])
+ boxy = 1;
+ else
+ boxy = 2;
+
+ boxpos = (boxy << 2) + boxx;
+ if (boxpos == 5)
+ return true;
+
+ x1 = bspcoord[checkcoord[boxpos][0]];
+ y1 = bspcoord[checkcoord[boxpos][1]];
+ x2 = bspcoord[checkcoord[boxpos][2]];
+ y2 = bspcoord[checkcoord[boxpos][3]];
+
+
+//
+// check clip list for an open space
+//
+ angle1 = R_PointToAngle(x1, y1) - viewangle;
+ angle2 = R_PointToAngle(x2, y2) - viewangle;
+
+ span = angle1 - angle2;
+ if (span >= ANG180)
+ return true; // sitting on a line
+ tspan = angle1 + clipangle;
+ if (tspan > 2 * clipangle)
+ {
+ tspan -= 2 * clipangle;
+ if (tspan >= span)
+ return false; // totally off the left edge
+ angle1 = clipangle;
+ }
+ tspan = clipangle - angle2;
+ if (tspan > 2 * clipangle)
+ {
+ tspan -= 2 * clipangle;
+ if (tspan >= span)
+ return false; // totally off the left edge
+ angle2 = -clipangle;
+ }
+
+
+// find the first clippost that touches the source post (adjacent pixels are touching)
+ angle1 = (angle1 + ANG90) >> ANGLETOFINESHIFT;
+ angle2 = (angle2 + ANG90) >> ANGLETOFINESHIFT;
+ sx1 = viewangletox[angle1];
+ sx2 = viewangletox[angle2];
+ if (sx1 == sx2)
+ return false; // does not cross a pixel
+ sx2--;
+
+ start = solidsegs;
+ while (start->last < sx2)
+ start++;
+ if (sx1 >= start->first && sx2 <= start->last)
+ return false; // the clippost contains the new span
+
+ return true;
+}
+
+
+/*
+================
+=
+= R_Subsector
+=
+= Draw one or more segments
+================
+*/
+
+void R_Subsector(int num)
+{
+ int count;
+ seg_t *line;
+ subsector_t *sub;
+
+#ifdef RANGECHECK
+ if (num >= numsubsectors)
+ I_Error("R_Subsector: ss %i with numss = %i", num, numsubsectors);
+#endif
+
+ sscount++;
+ sub = &subsectors[num];
+ frontsector = sub->sector;
+ count = sub->numlines;
+ line = &segs[sub->firstline];
+
+ if (frontsector->floorheight < viewz)
+ floorplane = R_FindPlane(frontsector->floorheight,
+ frontsector->floorpic,
+ frontsector->lightlevel,
+ frontsector->special);
+ else
+ floorplane = NULL;
+ if (frontsector->ceilingheight > viewz
+ || frontsector->ceilingpic == skyflatnum)
+ ceilingplane = R_FindPlane(frontsector->ceilingheight,
+ frontsector->ceilingpic,
+ frontsector->lightlevel, 0);
+ else
+ ceilingplane = NULL;
+
+ R_AddSprites(frontsector);
+
+ while (count--)
+ {
+ R_AddLine(line);
+ line++;
+ }
+}
+
+
+/*
+===============================================================================
+=
+= RenderBSPNode
+=
+===============================================================================
+*/
+
+void R_RenderBSPNode(int bspnum)
+{
+ node_t *bsp;
+ int side;
+
+ if (bspnum & NF_SUBSECTOR)
+ {
+ if (bspnum == -1)
+ R_Subsector(0);
+ else
+ R_Subsector(bspnum & (~NF_SUBSECTOR));
+ return;
+ }
+
+ bsp = &nodes[bspnum];
+
+//
+// decide which side the view point is on
+//
+ side = R_PointOnSide(viewx, viewy, bsp);
+
+ R_RenderBSPNode(bsp->children[side]); // recursively divide front space
+
+ if (R_CheckBBox(bsp->bbox[side ^ 1])) // possibly divide back space
+ R_RenderBSPNode(bsp->children[side ^ 1]);
+}
diff --git a/src/heretic/r_data.c b/src/heretic/r_data.c
new file mode 100644
index 00000000..5bb79aa4
--- /dev/null
+++ b/src/heretic/r_data.c
@@ -0,0 +1,751 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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_data.c
+
+#include "doomdef.h"
+#include "deh_str.h"
+
+#include "i_swap.h"
+#include "i_system.h"
+#include "r_local.h"
+#include "p_local.h"
+
+extern void CheckAbortStartup(void);
+
+typedef struct
+{
+ int originx; // block origin (allways UL), which has allready
+ int originy; // accounted for the patch's internal origin
+ int patch;
+} texpatch_t;
+
+// a maptexturedef_t describes a rectangular texture, which is composed of one
+// or more mappatch_t structures that arrange graphic patches
+typedef struct
+{
+ char name[8]; // for switch changing, etc
+ short width;
+ short height;
+ short patchcount;
+ texpatch_t patches[1]; // [patchcount] drawn back to front
+ // into the cached texture
+} texture_t;
+
+
+
+int firstflat, lastflat, numflats;
+int firstpatch, lastpatch, numpatches;
+int firstspritelump, lastspritelump, numspritelumps;
+
+int numtextures;
+texture_t **textures;
+int *texturewidthmask;
+fixed_t *textureheight; // needed for texture pegging
+int *texturecompositesize;
+short **texturecolumnlump;
+unsigned short **texturecolumnofs;
+byte **texturecomposite;
+
+int *flattranslation; // for global animation
+int *texturetranslation; // for global animation
+
+fixed_t *spritewidth; // needed for pre rendering
+fixed_t *spriteoffset;
+fixed_t *spritetopoffset;
+
+lighttable_t *colormaps;
+
+
+/*
+==============================================================================
+
+ MAPTEXTURE_T CACHING
+
+when a texture is first needed, it counts the number of composite columns
+required in the texture and allocates space for a column directory and any
+new columns. The directory will simply point inside other patches if there
+is only one patch in a given column, but any columns with multiple patches
+will have new column_ts generated.
+
+==============================================================================
+*/
+
+/*
+===================
+=
+= R_DrawColumnInCache
+=
+= Clip and draw a column from a patch into a cached post
+=
+===================
+*/
+
+void R_DrawColumnInCache(column_t * patch, byte * cache, int originy,
+ int cacheheight)
+{
+ int count, position;
+ byte *source, *dest;
+
+ dest = (byte *) cache + 3;
+
+ while (patch->topdelta != 0xff)
+ {
+ source = (byte *) patch + 3;
+ count = patch->length;
+ position = originy + patch->topdelta;
+ if (position < 0)
+ {
+ count += position;
+ position = 0;
+ }
+ if (position + count > cacheheight)
+ count = cacheheight - position;
+ if (count > 0)
+ memcpy(cache + position, source, count);
+
+ patch = (column_t *) ((byte *) patch + patch->length + 4);
+ }
+}
+
+
+/*
+===================
+=
+= R_GenerateComposite
+=
+===================
+*/
+
+void R_GenerateComposite(int texnum)
+{
+ byte *block;
+ texture_t *texture;
+ texpatch_t *patch;
+ patch_t *realpatch;
+ int x, x1, x2;
+ int i;
+ column_t *patchcol;
+ short *collump;
+ unsigned short *colofs;
+
+ texture = textures[texnum];
+ block = Z_Malloc(texturecompositesize[texnum], PU_STATIC,
+ &texturecomposite[texnum]);
+ collump = texturecolumnlump[texnum];
+ colofs = texturecolumnofs[texnum];
+
+//
+// composite the columns together
+//
+ patch = texture->patches;
+
+ for (i = 0, patch = texture->patches; i < texture->patchcount;
+ i++, patch++)
+ {
+ realpatch = W_CacheLumpNum(patch->patch, PU_CACHE);
+ x1 = patch->originx;
+ x2 = x1 + SHORT(realpatch->width);
+
+ if (x1 < 0)
+ x = 0;
+ else
+ x = x1;
+ if (x2 > texture->width)
+ x2 = texture->width;
+
+ for (; x < x2; x++)
+ {
+ if (collump[x] >= 0)
+ continue; // column does not have multiple patches
+ patchcol = (column_t *) ((byte *) realpatch +
+ LONG(realpatch->columnofs[x - x1]));
+ R_DrawColumnInCache(patchcol, block + colofs[x], patch->originy,
+ texture->height);
+ }
+
+ }
+
+// now that the texture has been built, it is purgable
+ Z_ChangeTag(block, PU_CACHE);
+}
+
+
+/*
+===================
+=
+= R_GenerateLookup
+=
+===================
+*/
+
+void R_GenerateLookup(int texnum)
+{
+ texture_t *texture;
+ byte *patchcount; // [texture->width]
+ texpatch_t *patch;
+ patch_t *realpatch;
+ int x, x1, x2;
+ int i;
+ short *collump;
+ unsigned short *colofs;
+
+ texture = textures[texnum];
+
+ texturecomposite[texnum] = 0; // composited not created yet
+ texturecompositesize[texnum] = 0;
+ collump = texturecolumnlump[texnum];
+ colofs = texturecolumnofs[texnum];
+
+//
+// count the number of columns that are covered by more than one patch
+// fill in the lump / offset, so columns with only a single patch are
+// all done
+//
+ patchcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &patchcount);
+ memset(patchcount, 0, texture->width);
+ patch = texture->patches;
+
+ for (i = 0, patch = texture->patches; i < texture->patchcount;
+ i++, patch++)
+ {
+ realpatch = W_CacheLumpNum(patch->patch, PU_CACHE);
+ x1 = patch->originx;
+ x2 = x1 + SHORT(realpatch->width);
+ if (x1 < 0)
+ x = 0;
+ else
+ x = x1;
+ if (x2 > texture->width)
+ x2 = texture->width;
+ for (; x < x2; x++)
+ {
+ patchcount[x]++;
+ collump[x] = patch->patch;
+ colofs[x] = LONG(realpatch->columnofs[x - x1]) + 3;
+ }
+ }
+
+ for (x = 0; x < texture->width; x++)
+ {
+ if (!patchcount[x])
+ {
+ printf("R_GenerateLookup: column without a patch (%s)\n",
+ texture->name);
+ return;
+ }
+// I_Error ("R_GenerateLookup: column without a patch");
+ if (patchcount[x] > 1)
+ {
+ collump[x] = -1; // use the cached block
+ colofs[x] = texturecompositesize[texnum];
+ if (texturecompositesize[texnum] > 0x10000 - texture->height)
+ I_Error("R_GenerateLookup: texture %i is >64k", texnum);
+ texturecompositesize[texnum] += texture->height;
+ }
+ }
+
+ Z_Free(patchcount);
+}
+
+
+/*
+================
+=
+= R_GetColumn
+=
+================
+*/
+
+byte *R_GetColumn(int tex, int col)
+{
+ int lump, ofs;
+
+ col &= texturewidthmask[tex];
+ lump = texturecolumnlump[tex][col];
+ ofs = texturecolumnofs[tex][col];
+ if (lump > 0)
+ return (byte *) W_CacheLumpNum(lump, PU_CACHE) + ofs;
+ if (!texturecomposite[tex])
+ R_GenerateComposite(tex);
+ return texturecomposite[tex] + ofs;
+}
+
+
+/*
+==================
+=
+= R_InitTextures
+=
+= Initializes the texture list with the textures from the world map
+=
+==================
+*/
+
+void R_InitTextures(void)
+{
+ maptexture_t *mtexture;
+ texture_t *texture;
+ mappatch_t *mpatch;
+ texpatch_t *patch;
+ int i, j;
+ int *maptex, *maptex2, *maptex1;
+ char name[9], *names, *name_p;
+ int *patchlookup;
+ int totalwidth;
+ int nummappatches;
+ int offset, maxoff, maxoff2;
+ int numtextures1, numtextures2;
+ int *directory;
+ char *texture1, *texture2, *pnames;
+
+ texture1 = DEH_String("TEXTURE1");
+ texture2 = DEH_String("TEXTURE2");
+ pnames = DEH_String("PNAMES");
+
+//
+// load the patch names from pnames.lmp
+//
+ name[8] = 0;
+ names = W_CacheLumpName(pnames, PU_STATIC);
+ nummappatches = LONG(*((int *) names));
+ name_p = names + 4;
+ patchlookup = Z_Malloc(nummappatches * sizeof(*patchlookup), PU_STATIC, NULL);
+ for (i = 0; i < nummappatches; i++)
+ {
+ strncpy(name, name_p + i * 8, 8);
+ patchlookup[i] = W_CheckNumForName(name);
+ }
+ W_ReleaseLumpName(pnames);
+
+//
+// load the map texture definitions from textures.lmp
+//
+ maptex = maptex1 = W_CacheLumpName(texture1, PU_STATIC);
+ numtextures1 = LONG(*maptex);
+ maxoff = W_LumpLength(W_GetNumForName(texture1));
+ directory = maptex + 1;
+
+ if (W_CheckNumForName(texture2) != -1)
+ {
+ maptex2 = W_CacheLumpName(texture2, PU_STATIC);
+ numtextures2 = LONG(*maptex2);
+ maxoff2 = W_LumpLength(W_GetNumForName(texture2));
+ }
+ else
+ {
+ maptex2 = NULL;
+ numtextures2 = 0;
+ maxoff2 = 0;
+ }
+ numtextures = numtextures1 + numtextures2;
+
+ //
+ // Init the startup thermometer at this point...
+ //
+ {
+ int start, end;
+ int spramount;
+ start = W_GetNumForName(DEH_String("S_START"));
+ end = W_GetNumForName(DEH_String("S_END"));
+ spramount = end - start + 1;
+ InitThermo(spramount + numtextures + 6);
+ }
+
+ textures = Z_Malloc(numtextures * sizeof(texture_t *), PU_STATIC, 0);
+ texturecolumnlump = Z_Malloc(numtextures * sizeof(short *), PU_STATIC, 0);
+ texturecolumnofs = Z_Malloc(numtextures * sizeof(unsigned short *), PU_STATIC, 0);
+ texturecomposite = Z_Malloc(numtextures * sizeof(byte *), PU_STATIC, 0);
+ texturecompositesize = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0);
+ texturewidthmask = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0);
+ textureheight = Z_Malloc(numtextures * sizeof(fixed_t), PU_STATIC, 0);
+
+ totalwidth = 0;
+
+ for (i = 0; i < numtextures; i++, directory++)
+ {
+#ifdef __NEXT__
+ if (!(i & 63))
+ printf(".");
+#else
+ IncThermo();
+#endif
+ if (i == numtextures1)
+ { // start looking in second texture file
+ maptex = maptex2;
+ maxoff = maxoff2;
+ directory = maptex + 1;
+ }
+
+ offset = LONG(*directory);
+ if (offset > maxoff)
+ I_Error("R_InitTextures: bad texture directory");
+ mtexture = (maptexture_t *) ((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);
+ memcpy(texture->name, mtexture->name, sizeof(texture->name));
+ mpatch = &mtexture->patches[0];
+ patch = &texture->patches[0];
+ for (j = 0; j < texture->patchcount; j++, mpatch++, patch++)
+ {
+ patch->originx = SHORT(mpatch->originx);
+ patch->originy = SHORT(mpatch->originy);
+ patch->patch = patchlookup[SHORT(mpatch->patch)];
+ if (patch->patch == -1)
+ I_Error("R_InitTextures: Missing patch in texture %s",
+ texture->name);
+ }
+ texturecolumnlump[i] = Z_Malloc(texture->width * sizeof(short),
+ PU_STATIC, 0);
+ texturecolumnofs[i] = Z_Malloc(texture->width * sizeof(short),
+ PU_STATIC, 0);
+ j = 1;
+ while (j * 2 <= texture->width)
+ j <<= 1;
+ texturewidthmask[i] = j - 1;
+ textureheight[i] = texture->height << FRACBITS;
+
+ totalwidth += texture->width;
+ }
+
+ Z_Free(patchlookup);
+
+ W_ReleaseLumpName(texture1);
+ if (maptex2)
+ {
+ W_ReleaseLumpName(texture2);
+ }
+
+//
+// precalculate whatever possible
+//
+ for (i = 0; i < numtextures; i++)
+ {
+ R_GenerateLookup(i);
+ CheckAbortStartup();
+ }
+
+//
+// translation table for global animation
+//
+ texturetranslation = Z_Malloc((numtextures + 1) * sizeof(int), PU_STATIC, 0);
+ for (i = 0; i < numtextures; i++)
+ texturetranslation[i] = i;
+}
+
+
+/*
+================
+=
+= R_InitFlats
+=
+=================
+*/
+
+void R_InitFlats(void)
+{
+ int i;
+
+ firstflat = W_GetNumForName(DEH_String("F_START")) + 1;
+ lastflat = W_GetNumForName(DEH_String("F_END")) - 1;
+ numflats = lastflat - firstflat + 1;
+
+// translation table for global animation
+ flattranslation = Z_Malloc((numflats + 1) * sizeof(int), PU_STATIC, 0);
+ for (i = 0; i < numflats; i++)
+ flattranslation[i] = i;
+}
+
+
+/*
+================
+=
+= R_InitSpriteLumps
+=
+= Finds the width and hoffset of all sprites in the wad, so the sprite doesn't
+= need to be cached just for the header during rendering
+=================
+*/
+
+void R_InitSpriteLumps(void)
+{
+ int i;
+ patch_t *patch;
+
+ firstspritelump = W_GetNumForName(DEH_String("S_START")) + 1;
+ lastspritelump = W_GetNumForName(DEH_String("S_END")) - 1;
+ numspritelumps = lastspritelump - firstspritelump + 1;
+ spritewidth = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0);
+ spriteoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0);
+ spritetopoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0);
+
+ for (i = 0; i < numspritelumps; i++)
+ {
+#ifdef __NEXT__
+ if (!(i & 63))
+ printf(".");
+#else
+ IncThermo();
+#endif
+ patch = W_CacheLumpNum(firstspritelump + i, PU_CACHE);
+ spritewidth[i] = SHORT(patch->width) << FRACBITS;
+ spriteoffset[i] = SHORT(patch->leftoffset) << FRACBITS;
+ spritetopoffset[i] = SHORT(patch->topoffset) << FRACBITS;
+ }
+}
+
+
+/*
+================
+=
+= R_InitColormaps
+=
+=================
+*/
+
+void R_InitColormaps(void)
+{
+ int lump, length;
+//
+// load in the light tables
+// 256 byte align tables
+//
+ lump = W_GetNumForName(DEH_String("COLORMAP"));
+ length = W_LumpLength(lump);
+ colormaps = Z_Malloc(length, PU_STATIC, 0);
+ W_ReadLump(lump, colormaps);
+}
+
+
+/*
+================
+=
+= R_InitData
+=
+= Locates all the lumps that will be used by all views
+= Must be called after W_Init
+=================
+*/
+
+void R_InitData(void)
+{
+ //tprintf("\nR_InitTextures ", 0);
+ R_InitTextures();
+ printf (".");
+ //tprintf("R_InitFlats\n", 0);
+ R_InitFlats();
+ IncThermo();
+ printf (".");
+ //tprintf("R_InitSpriteLumps ", 0);
+ R_InitSpriteLumps();
+ IncThermo();
+ printf (".");
+ R_InitColormaps();
+}
+
+
+//=============================================================================
+
+/*
+================
+=
+= R_FlatNumForName
+=
+================
+*/
+
+int R_FlatNumForName(char *name)
+{
+ int i;
+ char namet[9];
+
+ i = W_CheckNumForName(name);
+ if (i == -1)
+ {
+ namet[8] = 0;
+ memcpy(namet, name, 8);
+ I_Error("R_FlatNumForName: %s not found", namet);
+ }
+ return i - firstflat;
+}
+
+
+/*
+================
+=
+= R_CheckTextureNumForName
+=
+================
+*/
+
+int R_CheckTextureNumForName(char *name)
+{
+ int i;
+
+ if (name[0] == '-') // no texture marker
+ return 0;
+
+ for (i = 0; i < numtextures; i++)
+ if (!strncasecmp(textures[i]->name, name, 8))
+ return i;
+
+ return -1;
+}
+
+
+/*
+================
+=
+= R_TextureNumForName
+=
+================
+*/
+
+int R_TextureNumForName(char *name)
+{
+ int i;
+ //char namet[9];
+
+ i = R_CheckTextureNumForName(name);
+ if (i == -1)
+ I_Error("R_TextureNumForName: %s not found", name);
+
+ return i;
+}
+
+
+/*
+=================
+=
+= R_PrecacheLevel
+=
+= Preloads all relevent graphics for the level
+=================
+*/
+
+int flatmemory, texturememory, spritememory;
+
+void R_PrecacheLevel(void)
+{
+ char *flatpresent;
+ char *texturepresent;
+ char *spritepresent;
+ int i, j, k, lump;
+ texture_t *texture;
+ thinker_t *th;
+ spriteframe_t *sf;
+
+ if (demoplayback)
+ return;
+
+//
+// precache flats
+//
+ flatpresent = Z_Malloc(numflats, PU_STATIC, NULL);
+ memset(flatpresent, 0, numflats);
+ for (i = 0; i < numsectors; i++)
+ {
+ flatpresent[sectors[i].floorpic] = 1;
+ flatpresent[sectors[i].ceilingpic] = 1;
+ }
+
+ flatmemory = 0;
+ for (i = 0; i < numflats; i++)
+ if (flatpresent[i])
+ {
+ lump = firstflat + i;
+ flatmemory += lumpinfo[lump].size;
+ W_CacheLumpNum(lump, PU_CACHE);
+ }
+
+ Z_Free(flatpresent);
+
+//
+// precache textures
+//
+ texturepresent = Z_Malloc(numtextures, PU_STATIC, NULL);
+ memset(texturepresent, 0, numtextures);
+
+ for (i = 0; i < numsides; i++)
+ {
+ texturepresent[sides[i].toptexture] = 1;
+ texturepresent[sides[i].midtexture] = 1;
+ texturepresent[sides[i].bottomtexture] = 1;
+ }
+
+ texturepresent[skytexture] = 1;
+
+ texturememory = 0;
+ for (i = 0; i < numtextures; i++)
+ {
+ if (!texturepresent[i])
+ continue;
+ texture = textures[i];
+ for (j = 0; j < texture->patchcount; j++)
+ {
+ lump = texture->patches[j].patch;
+ texturememory += lumpinfo[lump].size;
+ W_CacheLumpNum(lump, PU_CACHE);
+ }
+ }
+
+ Z_Free(texturepresent);
+
+//
+// precache sprites
+//
+ spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL);
+ memset(spritepresent, 0, numsprites);
+
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function == P_MobjThinker)
+ spritepresent[((mobj_t *) th)->sprite] = 1;
+ }
+
+ spritememory = 0;
+ for (i = 0; i < numsprites; i++)
+ {
+ if (!spritepresent[i])
+ continue;
+ for (j = 0; j < sprites[i].numframes; j++)
+ {
+ sf = &sprites[i].spriteframes[j];
+ for (k = 0; k < 8; k++)
+ {
+ lump = firstspritelump + sf->lump[k];
+ spritememory += lumpinfo[lump].size;
+ W_CacheLumpNum(lump, PU_CACHE);
+ }
+ }
+ }
+
+ Z_Free(spritepresent);
+}
diff --git a/src/heretic/r_draw.c b/src/heretic/r_draw.c
new file mode 100644
index 00000000..88653df1
--- /dev/null
+++ b/src/heretic/r_draw.c
@@ -0,0 +1,498 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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_draw.c
+
+#include "doomdef.h"
+#include "deh_str.h"
+#include "r_local.h"
+#include "i_video.h"
+#include "v_video.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.
+
+*/
+
+byte *viewimage;
+int viewwidth, scaledviewwidth, viewheight, viewwindowx, viewwindowy;
+byte *ylookup[MAXHEIGHT];
+int columnofs[MAXWIDTH];
+byte translations[3][256]; // color tables for different players
+
+/*
+==================
+=
+= R_DrawColumn
+=
+= Source is the top of the column to scale
+=
+==================
+*/
+
+lighttable_t *dc_colormap;
+int dc_x;
+int dc_yl;
+int dc_yh;
+fixed_t dc_iscale;
+fixed_t dc_texturemid;
+byte *dc_source; // first pixel in a column (possibly virtual)
+
+int dccount; // just for profiling
+
+void R_DrawColumn(void)
+{
+ int count;
+ byte *dest;
+ fixed_t frac, fracstep;
+
+ count = dc_yh - dc_yl;
+ if (count < 0)
+ return;
+
+#ifdef RANGECHECK
+ if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
+ I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x);
+#endif
+
+ dest = ylookup[dc_yl] + columnofs[dc_x];
+
+ fracstep = dc_iscale;
+ frac = dc_texturemid + (dc_yl - centery) * fracstep;
+
+ do
+ {
+ *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]];
+ dest += SCREENWIDTH;
+ frac += fracstep;
+ }
+ while (count--);
+}
+
+void R_DrawColumnLow(void)
+{
+ int count;
+ byte *dest;
+ fixed_t frac, fracstep;
+
+ count = dc_yh - dc_yl;
+ if (count < 0)
+ return;
+
+#ifdef RANGECHECK
+ if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
+ I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x);
+// dccount++;
+#endif
+
+ dest = ylookup[dc_yl] + columnofs[dc_x];
+
+ fracstep = dc_iscale;
+ frac = dc_texturemid + (dc_yl - centery) * fracstep;
+
+ do
+ {
+ *dest = dc_colormap[dc_source[(frac >> FRACBITS) & 127]];
+ dest += SCREENWIDTH;
+ frac += fracstep;
+ }
+ while (count--);
+}
+
+// Translucent column draw - blended with background using tinttable.
+
+void R_DrawTLColumn(void)
+{
+ int count;
+ byte *dest;
+ fixed_t frac, fracstep;
+
+ if (!dc_yl)
+ dc_yl = 1;
+ if (dc_yh == viewheight - 1)
+ dc_yh = viewheight - 2;
+
+ count = dc_yh - dc_yl;
+ if (count < 0)
+ return;
+
+#ifdef RANGECHECK
+ if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
+ I_Error("R_DrawTLColumn: %i to %i at %i", dc_yl, dc_yh, dc_x);
+#endif
+
+ dest = ylookup[dc_yl] + columnofs[dc_x];
+
+ fracstep = dc_iscale;
+ frac = dc_texturemid + (dc_yl - centery) * fracstep;
+
+ do
+ {
+ *dest =
+ tinttable[((*dest) << 8) +
+ dc_colormap[dc_source[(frac >> FRACBITS) & 127]]];
+
+ dest += SCREENWIDTH;
+ frac += fracstep;
+ }
+ while (count--);
+}
+
+/*
+========================
+=
+= R_DrawTranslatedColumn
+=
+========================
+*/
+
+byte *dc_translation;
+byte *translationtables;
+
+void R_DrawTranslatedColumn(void)
+{
+ int count;
+ byte *dest;
+ fixed_t frac, fracstep;
+
+ count = dc_yh - dc_yl;
+ if (count < 0)
+ return;
+
+#ifdef RANGECHECK
+ if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
+ I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x);
+#endif
+
+ dest = ylookup[dc_yl] + columnofs[dc_x];
+
+ fracstep = dc_iscale;
+ frac = dc_texturemid + (dc_yl - centery) * fracstep;
+
+ do
+ {
+ *dest = dc_colormap[dc_translation[dc_source[frac >> FRACBITS]]];
+ dest += SCREENWIDTH;
+ frac += fracstep;
+ }
+ while (count--);
+}
+
+void R_DrawTranslatedTLColumn(void)
+{
+ int count;
+ byte *dest;
+ fixed_t frac, fracstep;
+
+ count = dc_yh - dc_yl;
+ if (count < 0)
+ return;
+
+#ifdef RANGECHECK
+ if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0 || dc_yh >= SCREENHEIGHT)
+ I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh, dc_x);
+#endif
+
+ dest = ylookup[dc_yl] + columnofs[dc_x];
+
+ fracstep = dc_iscale;
+ frac = dc_texturemid + (dc_yl - centery) * fracstep;
+
+ do
+ {
+ *dest = tinttable[((*dest) << 8)
+ +
+ dc_colormap[dc_translation
+ [dc_source[frac >> FRACBITS]]]];
+ dest += SCREENWIDTH;
+ frac += fracstep;
+ }
+ while (count--);
+}
+
+//--------------------------------------------------------------------------
+//
+// PROC R_InitTranslationTables
+//
+//--------------------------------------------------------------------------
+
+void R_InitTranslationTables(void)
+{
+ int i;
+
+ V_LoadTintTable();
+
+ // Allocate translation tables
+ translationtables = Z_Malloc(256 * 3, PU_STATIC, 0);
+
+ // Fill out the translation tables
+ for (i = 0; i < 256; i++)
+ {
+ if (i >= 225 && i <= 240)
+ {
+ translationtables[i] = 114 + (i - 225); // yellow
+ translationtables[i + 256] = 145 + (i - 225); // red
+ translationtables[i + 512] = 190 + (i - 225); // blue
+ }
+ else
+ {
+ translationtables[i] = translationtables[i + 256]
+ = translationtables[i + 512] = i;
+ }
+ }
+}
+
+/*
+================
+=
+= R_DrawSpan
+=
+================
+*/
+
+int ds_y;
+int ds_x1;
+int ds_x2;
+lighttable_t *ds_colormap;
+fixed_t ds_xfrac;
+fixed_t ds_yfrac;
+fixed_t ds_xstep;
+fixed_t ds_ystep;
+byte *ds_source; // start of a 64*64 tile image
+
+int dscount; // just for profiling
+
+void R_DrawSpan(void)
+{
+ fixed_t xfrac, yfrac;
+ byte *dest;
+ int count, spot;
+
+#ifdef RANGECHECK
+ if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH
+ || (unsigned) ds_y > SCREENHEIGHT)
+ I_Error("R_DrawSpan: %i to %i at %i", ds_x1, ds_x2, ds_y);
+// dscount++;
+#endif
+
+ xfrac = ds_xfrac;
+ yfrac = ds_yfrac;
+
+ dest = ylookup[ds_y] + columnofs[ds_x1];
+ count = ds_x2 - ds_x1;
+ do
+ {
+ spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63);
+ *dest++ = ds_colormap[ds_source[spot]];
+ xfrac += ds_xstep;
+ yfrac += ds_ystep;
+ }
+ while (count--);
+}
+
+void R_DrawSpanLow(void)
+{
+ fixed_t xfrac, yfrac;
+ byte *dest;
+ int count, spot;
+
+#ifdef RANGECHECK
+ if (ds_x2 < ds_x1 || ds_x1 < 0 || ds_x2 >= SCREENWIDTH
+ || (unsigned) ds_y > SCREENHEIGHT)
+ I_Error("R_DrawSpan: %i to %i at %i", ds_x1, ds_x2, ds_y);
+// dscount++;
+#endif
+
+ xfrac = ds_xfrac;
+ yfrac = ds_yfrac;
+
+ dest = ylookup[ds_y] + columnofs[ds_x1];
+ count = ds_x2 - ds_x1;
+ do
+ {
+ spot = ((yfrac >> (16 - 6)) & (63 * 64)) + ((xfrac >> 16) & 63);
+ *dest++ = ds_colormap[ds_source[spot]];
+ xfrac += ds_xstep;
+ yfrac += ds_ystep;
+ }
+ while (count--);
+}
+
+
+
+/*
+================
+=
+= R_InitBuffer
+=
+=================
+*/
+
+void R_InitBuffer(int width, int height)
+{
+ int i;
+
+ viewwindowx = (SCREENWIDTH - width) >> 1;
+ for (i = 0; i < width; i++)
+ columnofs[i] = viewwindowx + i;
+ if (width == SCREENWIDTH)
+ viewwindowy = 0;
+ else
+ viewwindowy = (SCREENHEIGHT - SBARHEIGHT - height) >> 1;
+ for (i = 0; i < height; i++)
+ ylookup[i] = I_VideoBuffer + (i + viewwindowy) * SCREENWIDTH;
+}
+
+
+/*
+==================
+=
+= R_DrawViewBorder
+=
+= Draws the border around the view for different size windows
+==================
+*/
+
+boolean BorderNeedRefresh;
+
+void R_DrawViewBorder(void)
+{
+ byte *src, *dest;
+ int x, y;
+
+ if (scaledviewwidth == SCREENWIDTH)
+ return;
+
+ if (gamemode == shareware)
+ {
+ src = W_CacheLumpName(DEH_String("FLOOR04"), PU_CACHE);
+ }
+ else
+ {
+ src = W_CacheLumpName(DEH_String("FLAT513"), PU_CACHE);
+ }
+ dest = I_VideoBuffer;
+
+ for (y = 0; y < SCREENHEIGHT - SBARHEIGHT; y++)
+ {
+ for (x = 0; x < SCREENWIDTH / 64; x++)
+ {
+ memcpy(dest, src + ((y & 63) << 6), 64);
+ dest += 64;
+ }
+ if (SCREENWIDTH & 63)
+ {
+ memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63);
+ dest += (SCREENWIDTH & 63);
+ }
+ }
+ for (x = viewwindowx; x < viewwindowx + viewwidth; x += 16)
+ {
+ V_DrawPatch(x, viewwindowy - 4,
+ W_CacheLumpName(DEH_String("bordt"), PU_CACHE));
+ V_DrawPatch(x, viewwindowy + viewheight,
+ W_CacheLumpName(DEH_String("bordb"), PU_CACHE));
+ }
+ for (y = viewwindowy; y < viewwindowy + viewheight; y += 16)
+ {
+ V_DrawPatch(viewwindowx - 4, y,
+ W_CacheLumpName(DEH_String("bordl"), PU_CACHE));
+ V_DrawPatch(viewwindowx + viewwidth, y,
+ W_CacheLumpName(DEH_String("bordr"), PU_CACHE));
+ }
+ V_DrawPatch(viewwindowx - 4, viewwindowy - 4,
+ W_CacheLumpName(DEH_String("bordtl"), PU_CACHE));
+ V_DrawPatch(viewwindowx + viewwidth, viewwindowy - 4,
+ W_CacheLumpName(DEH_String("bordtr"), PU_CACHE));
+ V_DrawPatch(viewwindowx + viewwidth, viewwindowy + viewheight,
+ W_CacheLumpName(DEH_String("bordbr"), PU_CACHE));
+ V_DrawPatch(viewwindowx - 4, viewwindowy + viewheight,
+ W_CacheLumpName(DEH_String("bordbl"), PU_CACHE));
+}
+
+/*
+==================
+=
+= R_DrawTopBorder
+=
+= Draws the top border around the view for different size windows
+==================
+*/
+
+boolean BorderTopRefresh;
+
+void R_DrawTopBorder(void)
+{
+ byte *src, *dest;
+ int x, y;
+
+ if (scaledviewwidth == SCREENWIDTH)
+ return;
+
+ if (gamemode == shareware)
+ {
+ src = W_CacheLumpName(DEH_String("FLOOR04"), PU_CACHE);
+ }
+ else
+ {
+ src = W_CacheLumpName(DEH_String("FLAT513"), PU_CACHE);
+ }
+ dest = I_VideoBuffer;
+
+ for (y = 0; y < 30; y++)
+ {
+ for (x = 0; x < SCREENWIDTH / 64; x++)
+ {
+ memcpy(dest, src + ((y & 63) << 6), 64);
+ dest += 64;
+ }
+ if (SCREENWIDTH & 63)
+ {
+ memcpy(dest, src + ((y & 63) << 6), SCREENWIDTH & 63);
+ dest += (SCREENWIDTH & 63);
+ }
+ }
+ if (viewwindowy < 25)
+ {
+ for (x = viewwindowx; x < viewwindowx + viewwidth; x += 16)
+ {
+ V_DrawPatch(x, viewwindowy - 4,
+ W_CacheLumpName(DEH_String("bordt"), PU_CACHE));
+ }
+ V_DrawPatch(viewwindowx - 4, viewwindowy,
+ W_CacheLumpName(DEH_String("bordl"), PU_CACHE));
+ V_DrawPatch(viewwindowx + viewwidth, viewwindowy,
+ W_CacheLumpName(DEH_String("bordr"), PU_CACHE));
+ V_DrawPatch(viewwindowx - 4, viewwindowy + 16,
+ W_CacheLumpName(DEH_String("bordl"), PU_CACHE));
+ V_DrawPatch(viewwindowx + viewwidth, viewwindowy + 16,
+ W_CacheLumpName(DEH_String("bordr"), PU_CACHE));
+
+ V_DrawPatch(viewwindowx - 4, viewwindowy - 4,
+ W_CacheLumpName(DEH_String("bordtl"), PU_CACHE));
+ V_DrawPatch(viewwindowx + viewwidth, viewwindowy - 4,
+ W_CacheLumpName(DEH_String("bordtr"), PU_CACHE));
+ }
+}
diff --git a/src/heretic/r_local.h b/src/heretic/r_local.h
new file mode 100644
index 00000000..184b0288
--- /dev/null
+++ b/src/heretic/r_local.h
@@ -0,0 +1,484 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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_local.h
+
+#ifndef __R_LOCAL__
+#define __R_LOCAL__
+
+#include "i_video.h"
+#include "v_patch.h"
+
+#define ANGLETOSKYSHIFT 22 // sky map is 256*128*4 maps
+
+#define BASEYCENTER 100
+
+#define MAXWIDTH 1120
+#define MAXHEIGHT 832
+
+#define PI 3.141592657
+
+#define CENTERY (SCREENHEIGHT/2)
+
+#define MINZ (FRACUNIT*4)
+
+#define FIELDOFVIEW 2048 // fineangles in the SCREENWIDTH wide window
+
+//
+// lighting constants
+//
+#define LIGHTLEVELS 16
+#define LIGHTSEGSHIFT 4
+#define MAXLIGHTSCALE 48
+#define LIGHTSCALESHIFT 12
+#define MAXLIGHTZ 128
+#define LIGHTZSHIFT 20
+#define NUMCOLORMAPS 32 // number of diminishing
+#define INVERSECOLORMAP 32
+
+/*
+==============================================================================
+
+ INTERNAL MAP TYPES
+
+==============================================================================
+*/
+
+//================ used by play and refresh
+
+typedef struct
+{
+ fixed_t x, y;
+} vertex_t;
+
+struct line_s;
+
+typedef struct
+{
+ fixed_t floorheight, ceilingheight;
+ short floorpic, ceilingpic;
+ short lightlevel;
+ short special, tag;
+
+ 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; // for any sounds played by the sector
+
+ int validcount; // if == validcount, already checked
+ mobj_t *thinglist; // list of mobjs in sector
+ void *specialdata; // thinker_t for reversable actions
+ int linecount;
+ struct line_s **lines; // [linecount] size
+} sector_t;
+
+typedef struct
+{
+ fixed_t textureoffset; // add this to the calculated texture col
+ fixed_t rowoffset; // add this to the calculated texture top
+ short toptexture, bottomtexture, midtexture;
+ sector_t *sector;
+} side_t;
+
+typedef enum
+{ ST_HORIZONTAL, ST_VERTICAL, ST_POSITIVE, ST_NEGATIVE } slopetype_t;
+
+typedef struct line_s
+{
+ vertex_t *v1, *v2;
+ fixed_t dx, dy; // v2 - v1 for side checking
+ short flags;
+ short special, tag;
+ short sidenum[2]; // sidenum[1] will be -1 if one sided
+ fixed_t bbox[4];
+ slopetype_t slopetype; // to aid move clipping
+ sector_t *frontsector, *backsector;
+ int validcount; // if == validcount, already checked
+ void *specialdata; // thinker_t for reversable actions
+} line_t;
+
+
+typedef struct subsector_s
+{
+ sector_t *sector;
+ short numlines;
+ short firstline;
+} subsector_t;
+
+typedef struct
+{
+ vertex_t *v1, *v2;
+ fixed_t offset;
+ angle_t angle;
+ side_t *sidedef;
+ line_t *linedef;
+ sector_t *frontsector;
+ sector_t *backsector; // NULL for one sided lines
+} seg_t;
+
+typedef struct
+{
+ fixed_t x, y, dx, dy; // partition line
+ fixed_t bbox[2][4]; // bounding box for each child
+ unsigned short children[2]; // if NF_SUBSECTOR its a subsector
+} node_t;
+
+
+/*
+==============================================================================
+
+ OTHER TYPES
+
+==============================================================================
+*/
+
+typedef byte lighttable_t; // this could be wider for >8 bit display
+
+#define MAXVISPLANES 128
+#define MAXOPENINGS SCREENWIDTH*64
+
+typedef struct
+{
+ fixed_t height;
+ int picnum;
+ int lightlevel;
+ int special;
+ int minx, maxx;
+ byte pad1; // leave pads for [minx-1]/[maxx+1]
+ byte top[SCREENWIDTH];
+ byte pad2;
+ byte pad3;
+ byte bottom[SCREENWIDTH];
+ byte pad4;
+} visplane_t;
+
+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; // don't clip sprites above this
+ fixed_t tsilheight; // don't clip sprites below this
+// pointers to lists for sprite clipping
+ short *sprtopclip; // adjusted so [x1] is first value
+ short *sprbottomclip; // adjusted so [x1] is first value
+ short *maskedtexturecol; // adjusted so [x1] is first value
+} drawseg_t;
+
+#define SIL_NONE 0
+#define SIL_BOTTOM 1
+#define SIL_TOP 2
+#define SIL_BOTH 3
+
+#define MAXDRAWSEGS 256
+
+// A vissprite_t is a thing that will be drawn during a refresh
+typedef struct vissprite_s
+{
+ struct vissprite_s *prev, *next;
+ 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;
+ lighttable_t *colormap;
+ int mobjflags; // for color translation and shadow draw
+ boolean psprite; // true if psprite
+ fixed_t footclip; // foot clipping
+} vissprite_t;
+
+
+extern visplane_t *floorplane, *ceilingplane;
+
+// Sprites are patches with a special naming convention so they can be
+// recognized by R_InitSprites. 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. Some sprites will only have one picture used
+// for all views.
+
+typedef struct
+{
+ boolean rotate; // if false use 0 for any position
+ short lump[8]; // lump to use for view angles 0-7
+ byte flip[8]; // flip (1 = flip) to use for view angles 0-7
+} spriteframe_t;
+
+typedef struct
+{
+ int numframes;
+ spriteframe_t *spriteframes;
+} spritedef_t;
+
+extern spritedef_t *sprites;
+extern int numsprites;
+
+//=============================================================================
+
+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;
+
+
+
+extern fixed_t viewx, viewy, viewz;
+extern angle_t viewangle;
+extern player_t *viewplayer;
+
+
+extern angle_t clipangle;
+
+extern int viewangletox[FINEANGLES / 2];
+extern angle_t xtoviewangle[SCREENWIDTH + 1];
+
+extern fixed_t rw_distance;
+extern angle_t rw_normalangle;
+
+//
+// R_main.c
+//
+extern int viewwidth, viewheight, viewwindowx, viewwindowy;
+extern int centerx, centery;
+extern int flyheight;
+extern fixed_t centerxfrac;
+extern fixed_t centeryfrac;
+extern fixed_t projection;
+
+extern int validcount;
+
+extern int sscount, linecount, loopcount;
+extern lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE];
+extern lighttable_t *scalelightfixed[MAXLIGHTSCALE];
+extern lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ];
+
+extern int extralight;
+extern lighttable_t *fixedcolormap;
+
+extern fixed_t viewcos, viewsin;
+
+extern int detailshift; // 0 = high, 1 = low
+
+extern void (*colfunc) (void);
+extern void (*basecolfunc) (void);
+extern void (*tlcolfunc) (void);
+extern void (*spanfunc) (void);
+
+int R_PointOnSide(fixed_t x, fixed_t y, node_t * node);
+int R_PointOnSegSide(fixed_t x, fixed_t y, 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);
+fixed_t R_PointToDist(fixed_t x, fixed_t y);
+fixed_t R_ScaleFromGlobalAngle(angle_t visangle);
+subsector_t *R_PointInSubsector(fixed_t x, fixed_t y);
+void R_AddPointToBox(int x, int y, fixed_t * box);
+
+
+//
+// R_bsp.c
+//
+extern seg_t *curline;
+extern side_t *sidedef;
+extern line_t *linedef;
+extern sector_t *frontsector, *backsector;
+
+extern int rw_x;
+extern int rw_stopx;
+
+extern boolean segtextured;
+extern boolean markfloor; // false if the back side is the same plane
+extern boolean markceiling;
+extern boolean skymap;
+
+extern drawseg_t drawsegs[MAXDRAWSEGS], *ds_p;
+
+extern lighttable_t **hscalelight, **vscalelight, **dscalelight;
+
+typedef void (*drawfunc_t) (int start, int stop);
+void R_ClearClipSegs(void);
+
+void R_ClearDrawSegs(void);
+void R_InitSkyMap(void);
+void R_RenderBSPNode(int bspnum);
+
+//
+// R_segs.c
+//
+extern int rw_angle1; // angle to line origin
+
+void R_RenderMaskedSegRange(drawseg_t * ds, int x1, int x2);
+
+
+//
+// R_plane.c
+//
+typedef void (*planefunction_t) (int top, int bottom);
+extern planefunction_t floorfunc, ceilingfunc;
+
+extern int skyflatnum;
+
+extern short openings[MAXOPENINGS], *lastopening;
+
+extern short floorclip[SCREENWIDTH];
+extern short ceilingclip[SCREENWIDTH];
+
+extern fixed_t yslope[SCREENHEIGHT];
+extern fixed_t distscale[SCREENWIDTH];
+
+void R_InitPlanes(void);
+void R_ClearPlanes(void);
+void R_MapPlane(int y, int x1, int x2);
+void R_MakeSpans(int x, int t1, int b1, int t2, int b2);
+void R_DrawPlanes(void);
+
+visplane_t *R_FindPlane(fixed_t height, int picnum, int lightlevel,
+ int special);
+visplane_t *R_CheckPlane(visplane_t * pl, int start, int stop);
+
+
+//
+// R_debug.m
+//
+extern int drawbsp;
+
+//
+// R_data.c
+//
+extern fixed_t *textureheight; // needed for texture pegging
+extern fixed_t *spritewidth; // needed for pre rendering (fracs)
+extern fixed_t *spriteoffset;
+extern fixed_t *spritetopoffset;
+extern lighttable_t *colormaps;
+extern int viewwidth, scaledviewwidth, viewheight;
+extern int firstflat;
+extern int numflats;
+
+extern int *flattranslation; // for global animation
+extern int *texturetranslation; // for global animation
+
+extern int firstspritelump, lastspritelump, numspritelumps;
+
+byte *R_GetColumn(int tex, int col);
+void R_InitData(void);
+void R_PrecacheLevel(void);
+
+
+//
+// R_things.c
+//
+#define MAXVISSPRITES 128
+
+extern vissprite_t vissprites[MAXVISSPRITES], *vissprite_p;
+extern vissprite_t vsprsortedhead;
+
+// constant arrays used for psprite clipping and initializing clipping
+extern short negonearray[SCREENWIDTH];
+extern short screenheightarray[SCREENWIDTH];
+
+// vars for R_DrawMaskedColumn
+extern short *mfloorclip;
+extern short *mceilingclip;
+extern fixed_t spryscale;
+extern fixed_t sprtopscreen;
+extern fixed_t sprbotscreen;
+
+extern fixed_t pspritescale, pspriteiscale;
+
+
+void R_DrawMaskedColumn(column_t * column, signed int baseclip);
+
+
+void R_SortVisSprites(void);
+
+void R_AddSprites(sector_t * sec);
+void R_AddPSprites(void);
+void R_DrawSprites(void);
+void R_InitSprites(char **namelist);
+void R_ClearSprites(void);
+void R_DrawMasked(void);
+void R_ClipVisSprite(vissprite_t * vis, int xl, int xh);
+
+//=============================================================================
+//
+// R_draw.c
+//
+//=============================================================================
+
+extern lighttable_t *dc_colormap;
+extern int dc_x;
+extern int dc_yl;
+extern int dc_yh;
+extern fixed_t dc_iscale;
+extern fixed_t dc_texturemid;
+extern byte *dc_source; // first pixel in a column
+
+void R_DrawColumn(void);
+void R_DrawColumnLow(void);
+void R_DrawTLColumn(void);
+void R_DrawTLColumnLow(void);
+void R_DrawTranslatedColumn(void);
+void R_DrawTranslatedTLColumn(void);
+void R_DrawTranslatedColumnLow(void);
+
+extern int ds_y;
+extern int ds_x1;
+extern int ds_x2;
+extern lighttable_t *ds_colormap;
+extern fixed_t ds_xfrac;
+extern fixed_t ds_yfrac;
+extern fixed_t ds_xstep;
+extern fixed_t ds_ystep;
+extern byte *ds_source; // start of a 64*64 tile image
+
+extern byte *translationtables;
+extern byte *dc_translation;
+
+void R_DrawSpan(void);
+void R_DrawSpanLow(void);
+
+void R_InitBuffer(int width, int height);
+void R_InitTranslationTables(void);
+
+#endif // __R_LOCAL__
diff --git a/src/heretic/r_main.c b/src/heretic/r_main.c
new file mode 100644
index 00000000..1101c234
--- /dev/null
+++ b/src/heretic/r_main.c
@@ -0,0 +1,827 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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_main.c
+
+#include <stdlib.h>
+#include <math.h>
+#include "doomdef.h"
+#include "m_bbox.h"
+#include "r_local.h"
+#include "tables.h"
+
+int viewangleoffset;
+
+// haleyjd: removed WATCOMC
+
+int validcount = 1; // increment every time a check is made
+
+lighttable_t *fixedcolormap;
+extern lighttable_t **walllights;
+
+int centerx, centery;
+fixed_t centerxfrac, centeryfrac;
+fixed_t projection;
+
+int framecount; // just for profiling purposes
+
+int sscount, linecount, loopcount;
+
+fixed_t viewx, viewy, viewz;
+angle_t viewangle;
+fixed_t viewcos, viewsin;
+player_t *viewplayer;
+
+int detailshift; // 0 = high, 1 = low
+
+//
+// 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[SCREENWIDTH + 1];
+
+lighttable_t *scalelight[LIGHTLEVELS][MAXLIGHTSCALE];
+lighttable_t *scalelightfixed[MAXLIGHTSCALE];
+lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ];
+
+int extralight; // bumped light from gun blasts
+
+void (*colfunc) (void);
+void (*basecolfunc) (void);
+void (*tlcolfunc) (void);
+void (*transcolfunc) (void);
+void (*spanfunc) (void);
+
+/*
+===================
+=
+= R_AddPointToBox
+=
+===================
+*/
+
+void R_AddPointToBox(int x, int y, fixed_t * box)
+{
+ if (x < box[BOXLEFT])
+ box[BOXLEFT] = x;
+ if (x > box[BOXRIGHT])
+ box[BOXRIGHT] = x;
+ if (y < box[BOXBOTTOM])
+ box[BOXBOTTOM] = y;
+ if (y > box[BOXTOP])
+ box[BOXTOP] = y;
+}
+
+
+
+/*
+===============================================================================
+=
+= R_PointOnSide
+=
+= Returns side 0 (front) or 1 (back)
+===============================================================================
+*/
+
+int R_PointOnSide(fixed_t x, fixed_t y, node_t * node)
+{
+ fixed_t dx, dy;
+ fixed_t left, right;
+
+ if (!node->dx)
+ {
+ if (x <= node->x)
+ return node->dy > 0;
+ return node->dy < 0;
+ }
+ if (!node->dy)
+ {
+ if (y <= node->y)
+ return node->dx < 0;
+ return node->dx > 0;
+ }
+
+ dx = (x - node->x);
+ dy = (y - node->y);
+
+// try to quickly decide by looking at sign bits
+ if ((node->dy ^ node->dx ^ dx ^ dy) & 0x80000000)
+ {
+ if ((node->dy ^ dx) & 0x80000000)
+ return 1; // (left is negative)
+ return 0;
+ }
+
+ left = FixedMul(node->dy >> FRACBITS, dx);
+ right = FixedMul(dy, node->dx >> FRACBITS);
+
+ if (right < left)
+ return 0; // front side
+ return 1; // back side
+}
+
+
+int R_PointOnSegSide(fixed_t x, fixed_t y, seg_t * line)
+{
+ fixed_t lx, ly;
+ fixed_t ldx, ldy;
+ fixed_t dx, dy;
+ fixed_t left, right;
+
+ lx = line->v1->x;
+ ly = line->v1->y;
+
+ ldx = line->v2->x - lx;
+ ldy = line->v2->y - ly;
+
+ if (!ldx)
+ {
+ if (x <= lx)
+ return ldy > 0;
+ return ldy < 0;
+ }
+ if (!ldy)
+ {
+ if (y <= ly)
+ return ldx < 0;
+ return ldx > 0;
+ }
+
+ dx = (x - lx);
+ dy = (y - ly);
+
+// try to quickly decide by looking at sign bits
+ if ((ldy ^ ldx ^ dx ^ dy) & 0x80000000)
+ {
+ if ((ldy ^ dx) & 0x80000000)
+ return 1; // (left is negative)
+ return 0;
+ }
+
+ left = FixedMul(ldy >> FRACBITS, dx);
+ right = FixedMul(dy, ldx >> FRACBITS);
+
+ if (right < left)
+ return 0; // front side
+ return 1; // back side
+}
+
+
+/*
+===============================================================================
+=
+= R_PointToAngle
+=
+===============================================================================
+*/
+
+angle_t R_PointToAngle(fixed_t x, fixed_t y)
+{
+ x -= viewx;
+ y -= viewy;
+ if ((!x) && (!y))
+ return 0;
+ if (x >= 0)
+ { // x >=0
+ if (y >= 0)
+ { // y>= 0
+ if (x > y)
+ return tantoangle[SlopeDiv(y, x)]; // octant 0
+ else
+ return ANG90 - 1 - tantoangle[SlopeDiv(x, y)]; // octant 1
+ }
+ else
+ { // y<0
+ y = -y;
+ if (x > y)
+ return -tantoangle[SlopeDiv(y, x)]; // octant 8
+ else
+ return ANG270 + tantoangle[SlopeDiv(x, y)]; // octant 7
+ }
+ }
+ else
+ { // x<0
+ x = -x;
+ if (y >= 0)
+ { // y>= 0
+ if (x > y)
+ return ANG180 - 1 - tantoangle[SlopeDiv(y, x)]; // octant 3
+ else
+ return ANG90 + tantoangle[SlopeDiv(x, y)]; // octant 2
+ }
+ else
+ { // y<0
+ y = -y;
+ if (x > y)
+ return ANG180 + tantoangle[SlopeDiv(y, x)]; // octant 4
+ else
+ return ANG270 - 1 - tantoangle[SlopeDiv(x, y)]; // octant 5
+ }
+ }
+
+ return 0;
+}
+
+
+angle_t R_PointToAngle2(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2)
+{
+ viewx = x1;
+ viewy = y1;
+ return R_PointToAngle(x2, y2);
+}
+
+
+fixed_t R_PointToDist(fixed_t x, fixed_t y)
+{
+ int angle;
+ fixed_t dx, dy, temp;
+ fixed_t dist;
+
+ dx = abs(x - viewx);
+ dy = abs(y - viewy);
+
+ if (dy > dx)
+ {
+ temp = dx;
+ dx = dy;
+ dy = temp;
+ }
+
+ angle =
+ (tantoangle[FixedDiv(dy, dx) >> DBITS] + ANG90) >> ANGLETOFINESHIFT;
+
+ dist = FixedDiv(dx, finesine[angle]); // use as cosine
+
+ return dist;
+}
+
+
+
+/*
+=================
+=
+= R_InitPointToAngle
+=
+=================
+*/
+
+void R_InitPointToAngle(void)
+{
+// now getting from tables.c
+#if 0
+ int i;
+ long t;
+ float f;
+//
+// slope (tangent) to angle lookup
+//
+ for (i = 0; i <= SLOPERANGE; i++)
+ {
+ f = atan((float) i / SLOPERANGE) / (3.141592657 * 2);
+ t = 0xffffffff * f;
+ tantoangle[i] = t;
+ }
+#endif
+}
+
+//=============================================================================
+
+/*
+================
+=
+= R_ScaleFromGlobalAngle
+=
+= Returns the texture mapping scale for the current line at the given angle
+= rw_distance must be calculated first
+================
+*/
+
+fixed_t R_ScaleFromGlobalAngle(angle_t visangle)
+{
+ fixed_t scale;
+ int anglea, angleb;
+ int sinea, sineb;
+ fixed_t num, den;
+
+#if 0
+ {
+ fixed_t dist, z;
+ fixed_t sinv, cosv;
+
+ sinv = finesine[(visangle - rw_normalangle) >> ANGLETOFINESHIFT];
+ dist = FixedDiv(rw_distance, sinv);
+ cosv = finecosine[(viewangle - visangle) >> ANGLETOFINESHIFT];
+ z = abs(FixedMul(dist, cosv));
+ scale = FixedDiv(projection, z);
+ return scale;
+ }
+#endif
+
+ anglea = ANG90 + (visangle - viewangle);
+ angleb = ANG90 + (visangle - rw_normalangle);
+// bothe sines are allways positive
+ sinea = finesine[anglea >> ANGLETOFINESHIFT];
+ sineb = finesine[angleb >> ANGLETOFINESHIFT];
+ num = FixedMul(projection, sineb) << detailshift;
+ den = FixedMul(rw_distance, sinea);
+ if (den > num >> 16)
+ {
+ scale = FixedDiv(num, den);
+ if (scale > 64 * FRACUNIT)
+ scale = 64 * FRACUNIT;
+ else if (scale < 256)
+ scale = 256;
+ }
+ else
+ scale = 64 * FRACUNIT;
+
+ return scale;
+}
+
+
+
+/*
+=================
+=
+= R_InitTables
+=
+=================
+*/
+
+void R_InitTables(void)
+{
+// now getting from tables.c
+#if 0
+ int i;
+ float a, fv;
+ int t;
+
+//
+// viewangle tangent table
+//
+ for (i = 0; i < FINEANGLES / 2; i++)
+ {
+ a = (i - FINEANGLES / 4 + 0.5) * PI * 2 / FINEANGLES;
+ fv = FRACUNIT * tan(a);
+ t = fv;
+ finetangent[i] = t;
+ }
+
+//
+// finesine table
+//
+ for (i = 0; i < 5 * FINEANGLES / 4; i++)
+ {
+// OPTIMIZE: mirror...
+ a = (i + 0.5) * PI * 2 / FINEANGLES;
+ t = FRACUNIT * sin(a);
+ finesine[i] = t;
+ }
+#endif
+
+}
+
+
+/*
+=================
+=
+= R_InitTextureMapping
+=
+=================
+*/
+
+void R_InitTextureMapping(void)
+{
+ int i;
+ int x;
+ int t;
+ 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 < FINEANGLES / 2; i++)
+ {
+ if (finetangent[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 xtoviewangleangle[]
+//
+// xtoviewangle will give the smallest view angle that maps to x
+ for (x = 0; x <= viewwidth; x++)
+ {
+ i = 0;
+ while (viewangletox[i] > x)
+ i++;
+ xtoviewangle[x] = (i << ANGLETOFINESHIFT) - ANG90;
+ }
+
+//
+// take out the fencepost cases from viewangletox
+//
+ for (i = 0; i < FINEANGLES / 2; i++)
+ {
+ t = FixedMul(finetangent[i], focallength);
+ t = centerx - t;
+ if (viewangletox[i] == -1)
+ viewangletox[i] = 0;
+ else if (viewangletox[i] == viewwidth + 1)
+ viewangletox[i] = viewwidth;
+ }
+
+ clipangle = xtoviewangle[0];
+}
+
+//=============================================================================
+
+/*
+====================
+=
+= R_InitLightTables
+=
+= Only inits the zlight table, because the scalelight table changes
+= with view size
+=
+====================
+*/
+
+#define DISTMAP 2
+
+void R_InitLightTables(void)
+{
+ int i, j, level, startmap;
+ int scale;
+
+//
+// Calculate the light levels to use for each level / distance combination
+//
+ for (i = 0; i < LIGHTLEVELS; i++)
+ {
+ startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS;
+ for (j = 0; j < MAXLIGHTZ; j++)
+ {
+ scale =
+ FixedDiv((SCREENWIDTH / 2 * FRACUNIT),
+ (j + 1) << LIGHTZSHIFT);
+ scale >>= LIGHTSCALESHIFT;
+ level = startmap - scale / DISTMAP;
+ if (level < 0)
+ level = 0;
+ if (level >= NUMCOLORMAPS)
+ level = NUMCOLORMAPS - 1;
+ zlight[i][j] = colormaps + level * 256;
+ }
+ }
+}
+
+
+/*
+==============
+=
+= R_SetViewSize
+=
+= Don't really change anything here, because i might be in the middle of
+= a refresh. The change will take effect next refresh.
+=
+==============
+*/
+
+boolean setsizeneeded;
+int setblocks, setdetail;
+
+void R_SetViewSize(int blocks, int detail)
+{
+ setsizeneeded = true;
+ setblocks = blocks;
+ setdetail = detail;
+}
+
+/*
+==============
+=
+= R_ExecuteSetViewSize
+=
+==============
+*/
+
+void R_ExecuteSetViewSize(void)
+{
+ fixed_t cosadj, dy;
+ int i, j, level, startmap;
+
+ setsizeneeded = false;
+
+ if (setblocks == 11)
+ {
+ scaledviewwidth = SCREENWIDTH;
+ viewheight = SCREENHEIGHT;
+ }
+ else
+ {
+ scaledviewwidth = setblocks * 32;
+ viewheight = (setblocks * 158 / 10);
+ }
+
+ detailshift = setdetail;
+ viewwidth = scaledviewwidth >> detailshift;
+
+ centery = viewheight / 2;
+ centerx = viewwidth / 2;
+ centerxfrac = centerx << FRACBITS;
+ centeryfrac = centery << FRACBITS;
+ projection = centerxfrac;
+
+ if (!detailshift)
+ {
+ colfunc = basecolfunc = R_DrawColumn;
+ tlcolfunc = R_DrawTLColumn;
+ transcolfunc = R_DrawTranslatedColumn;
+ spanfunc = R_DrawSpan;
+ }
+ else
+ {
+ colfunc = basecolfunc = R_DrawColumnLow;
+ tlcolfunc = R_DrawTLColumn;
+ transcolfunc = R_DrawTranslatedColumn;
+ spanfunc = R_DrawSpanLow;
+ }
+
+ R_InitBuffer(scaledviewwidth, viewheight);
+
+ R_InitTextureMapping();
+
+//
+// psprite scales
+//
+ pspritescale = FRACUNIT * viewwidth / SCREENWIDTH;
+ pspriteiscale = FRACUNIT * SCREENWIDTH / viewwidth;
+
+//
+// thing clipping
+//
+ for (i = 0; i < viewwidth; i++)
+ screenheightarray[i] = viewheight;
+
+//
+// planes
+//
+ for (i = 0; i < viewheight; i++)
+ {
+ dy = ((i - viewheight / 2) << FRACBITS) + FRACUNIT / 2;
+ dy = abs(dy);
+ yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT, dy);
+ }
+
+ for (i = 0; i < viewwidth; i++)
+ {
+ cosadj = abs(finecosine[xtoviewangle[i] >> ANGLETOFINESHIFT]);
+ distscale[i] = FixedDiv(FRACUNIT, cosadj);
+ }
+
+//
+// Calculate the light levels to use for each level / scale combination
+//
+ for (i = 0; i < LIGHTLEVELS; i++)
+ {
+ startmap = ((LIGHTLEVELS - 1 - i) * 2) * NUMCOLORMAPS / LIGHTLEVELS;
+ for (j = 0; j < MAXLIGHTSCALE; j++)
+ {
+ level =
+ startmap -
+ j * SCREENWIDTH / (viewwidth << detailshift) / DISTMAP;
+ if (level < 0)
+ level = 0;
+ if (level >= NUMCOLORMAPS)
+ level = NUMCOLORMAPS - 1;
+ scalelight[i][j] = colormaps + level * 256;
+ }
+ }
+
+//
+// draw the border
+//
+ R_DrawViewBorder(); // erase old menu stuff
+}
+
+
+/*
+==============
+=
+= R_Init
+=
+==============
+*/
+
+int detailLevel;
+int screenblocks = 10;
+
+void R_Init(void)
+{
+ //tprintf("R_InitData ", 1);
+ R_InitData();
+ printf (".");
+ //tprintf("R_InitPointToAngle\n", 0);
+ R_InitPointToAngle();
+ printf (".");
+ //tprintf("R_InitTables ", 0);
+ R_InitTables();
+ // viewwidth / viewheight / detailLevel are set by the defaults
+ printf (".");
+ R_SetViewSize(screenblocks, detailLevel);
+ //tprintf("R_InitPlanes\n", 0);
+ R_InitPlanes();
+ printf (".");
+ //tprintf("R_InitLightTables ", 0);
+ R_InitLightTables();
+ printf (".");
+ //tprintf("R_InitSkyMap\n", 0);
+ R_InitSkyMap();
+ printf (".");
+ R_InitTranslationTables();
+ framecount = 0;
+}
+
+
+/*
+==============
+=
+= R_PointInSubsector
+=
+==============
+*/
+
+subsector_t *R_PointInSubsector(fixed_t x, fixed_t y)
+{
+ node_t *node;
+ int side, nodenum;
+
+ if (!numnodes) // single subsector is a special case
+ return subsectors;
+
+ nodenum = numnodes - 1;
+
+ while (!(nodenum & NF_SUBSECTOR))
+ {
+ node = &nodes[nodenum];
+ side = R_PointOnSide(x, y, node);
+ nodenum = node->children[side];
+ }
+
+ return &subsectors[nodenum & ~NF_SUBSECTOR];
+
+}
+
+//----------------------------------------------------------------------------
+//
+// PROC R_SetupFrame
+//
+//----------------------------------------------------------------------------
+
+void R_SetupFrame(player_t * player)
+{
+ int i;
+ int tableAngle;
+ int tempCentery;
+
+ //drawbsp = 1;
+ viewplayer = player;
+ // haleyjd: removed WATCOMC
+ // haleyjd FIXME: viewangleoffset handling?
+ viewangle = player->mo->angle + viewangleoffset;
+ tableAngle = viewangle >> ANGLETOFINESHIFT;
+ if (player->chickenTics && player->chickenPeck)
+ { // Set chicken attack view position
+ viewx = player->mo->x + player->chickenPeck * finecosine[tableAngle];
+ viewy = player->mo->y + player->chickenPeck * finesine[tableAngle];
+ }
+ else
+ { // Normal view position
+ viewx = player->mo->x;
+ viewy = player->mo->y;
+ }
+ extralight = player->extralight;
+ viewz = player->viewz;
+
+ tempCentery = viewheight / 2 + (player->lookdir) * screenblocks / 10;
+ if (centery != tempCentery)
+ {
+ centery = tempCentery;
+ centeryfrac = centery << FRACBITS;
+ for (i = 0; i < viewheight; i++)
+ {
+ yslope[i] = FixedDiv((viewwidth << detailshift) / 2 * FRACUNIT,
+ abs(((i - centery) << FRACBITS) +
+ FRACUNIT / 2));
+ }
+ }
+ viewsin = finesine[tableAngle];
+ viewcos = finecosine[tableAngle];
+ sscount = 0;
+ if (player->fixedcolormap)
+ {
+ fixedcolormap = colormaps + player->fixedcolormap
+ * 256 * sizeof(lighttable_t);
+ walllights = scalelightfixed;
+ for (i = 0; i < MAXLIGHTSCALE; i++)
+ {
+ scalelightfixed[i] = fixedcolormap;
+ }
+ }
+ else
+ {
+ fixedcolormap = 0;
+ }
+ framecount++;
+ validcount++;
+ if (BorderNeedRefresh)
+ {
+ if (setblocks < 10)
+ {
+ R_DrawViewBorder();
+ }
+ BorderNeedRefresh = false;
+ BorderTopRefresh = false;
+ UpdateState |= I_FULLSCRN;
+ }
+ if (BorderTopRefresh)
+ {
+ if (setblocks < 10)
+ {
+ R_DrawTopBorder();
+ }
+ BorderTopRefresh = false;
+ UpdateState |= I_MESSAGES;
+ }
+}
+
+/*
+==============
+=
+= R_RenderView
+=
+==============
+*/
+
+void R_RenderPlayerView(player_t * player)
+{
+ R_SetupFrame(player);
+ R_ClearClipSegs();
+ R_ClearDrawSegs();
+ R_ClearPlanes();
+ R_ClearSprites();
+ NetUpdate(); // check for new console commands
+ R_RenderBSPNode(numnodes - 1); // the head node is the last node output
+ NetUpdate(); // check for new console commands
+ R_DrawPlanes();
+ NetUpdate(); // check for new console commands
+ R_DrawMasked();
+ NetUpdate(); // check for new console commands
+}
diff --git a/src/heretic/r_plane.c b/src/heretic/r_plane.c
new file mode 100644
index 00000000..cc86b118
--- /dev/null
+++ b/src/heretic/r_plane.c
@@ -0,0 +1,521 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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_planes.c
+
+#include <stdlib.h>
+#include "doomdef.h"
+#include "deh_str.h"
+#include "i_system.h"
+#include "r_local.h"
+
+planefunction_t floorfunc, ceilingfunc;
+
+//
+// sky mapping
+//
+int skyflatnum;
+int skytexture;
+int skytexturemid;
+fixed_t skyiscale;
+
+//
+// opening
+//
+
+visplane_t visplanes[MAXVISPLANES], *lastvisplane;
+visplane_t *floorplane, *ceilingplane;
+
+short openings[MAXOPENINGS], *lastopening;
+
+//
+// clip values are the solid pixel bounding the range
+// floorclip starts out SCREENHEIGHT
+// ceilingclip starts out -1
+//
+short floorclip[SCREENWIDTH];
+short ceilingclip[SCREENWIDTH];
+
+//
+// spanstart holds the start of a plane span
+// initialized to 0 at start
+//
+int spanstart[SCREENHEIGHT];
+int spanstop[SCREENHEIGHT];
+
+//
+// texture mapping
+//
+lighttable_t **planezlight;
+fixed_t planeheight;
+
+fixed_t yslope[SCREENHEIGHT];
+fixed_t distscale[SCREENWIDTH];
+fixed_t basexscale, baseyscale;
+
+fixed_t cachedheight[SCREENHEIGHT];
+fixed_t cacheddistance[SCREENHEIGHT];
+fixed_t cachedxstep[SCREENHEIGHT];
+fixed_t cachedystep[SCREENHEIGHT];
+
+
+/*
+================
+=
+= R_InitSkyMap
+=
+= Called whenever the view size changes
+=
+================
+*/
+
+void R_InitSkyMap(void)
+{
+ skyflatnum = R_FlatNumForName(DEH_String("F_SKY1"));
+ skytexturemid = 200 * FRACUNIT;
+ skyiscale = FRACUNIT;
+}
+
+
+/*
+====================
+=
+= R_InitPlanes
+=
+= Only at game startup
+====================
+*/
+
+void R_InitPlanes(void)
+{
+}
+
+
+/*
+================
+=
+= R_MapPlane
+=
+global vars:
+
+planeheight
+ds_source
+basexscale
+baseyscale
+viewx
+viewy
+
+BASIC PRIMITIVE
+================
+*/
+
+void R_MapPlane(int y, int x1, int x2)
+{
+ angle_t angle;
+ fixed_t distance, length;
+ unsigned index;
+
+#ifdef RANGECHECK
+ if (x2 < x1 || x1 < 0 || x2 >= viewwidth || (unsigned) y > viewheight)
+ I_Error("R_MapPlane: %i, %i at %i", x1, x2, y);
+#endif
+
+ if (planeheight != cachedheight[y])
+ {
+ cachedheight[y] = planeheight;
+ distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]);
+
+ ds_xstep = cachedxstep[y] = FixedMul(distance, basexscale);
+ ds_ystep = cachedystep[y] = FixedMul(distance, baseyscale);
+ }
+ else
+ {
+ distance = cacheddistance[y];
+ ds_xstep = cachedxstep[y];
+ ds_ystep = cachedystep[y];
+ }
+
+ length = FixedMul(distance, distscale[x1]);
+ angle = (viewangle + xtoviewangle[x1]) >> ANGLETOFINESHIFT;
+ ds_xfrac = viewx + FixedMul(finecosine[angle], length);
+ ds_yfrac = -viewy - FixedMul(finesine[angle], length);
+
+ if (fixedcolormap)
+ ds_colormap = fixedcolormap;
+ else
+ {
+ index = distance >> LIGHTZSHIFT;
+ if (index >= MAXLIGHTZ)
+ index = MAXLIGHTZ - 1;
+ ds_colormap = planezlight[index];
+ }
+
+ ds_y = y;
+ ds_x1 = x1;
+ ds_x2 = x2;
+
+ spanfunc(); // high or low detail
+}
+
+//=============================================================================
+
+/*
+====================
+=
+= R_ClearPlanes
+=
+= At begining of frame
+====================
+*/
+
+void R_ClearPlanes(void)
+{
+ int i;
+ angle_t angle;
+
+//
+// opening / clipping determination
+//
+ for (i = 0; i < viewwidth; i++)
+ {
+ floorclip[i] = viewheight;
+ ceilingclip[i] = -1;
+ }
+
+ lastvisplane = visplanes;
+ lastopening = openings;
+
+//
+// texture calculation
+//
+ memset(cachedheight, 0, sizeof(cachedheight));
+ angle = (viewangle - ANG90) >> ANGLETOFINESHIFT; // left to right mapping
+
+ // scale will be unit scale at SCREENWIDTH/2 distance
+ basexscale = FixedDiv(finecosine[angle], centerxfrac);
+ baseyscale = -FixedDiv(finesine[angle], centerxfrac);
+}
+
+
+
+/*
+===============
+=
+= R_FindPlane
+=
+===============
+*/
+
+visplane_t *R_FindPlane(fixed_t height, int picnum,
+ int lightlevel, int special)
+{
+ visplane_t *check;
+
+ if (picnum == skyflatnum)
+ {
+ // all skies map together
+ height = 0;
+ lightlevel = 0;
+ }
+
+ for (check = visplanes; check < lastvisplane; check++)
+ {
+ if (height == check->height
+ && picnum == check->picnum
+ && lightlevel == check->lightlevel && special == check->special)
+ break;
+ }
+
+ if (check < lastvisplane)
+ {
+ return (check);
+ }
+
+ if (lastvisplane - visplanes == MAXVISPLANES)
+ {
+ I_Error("R_FindPlane: no more visplanes");
+ }
+
+ lastvisplane++;
+ check->height = height;
+ check->picnum = picnum;
+ check->lightlevel = lightlevel;
+ check->special = special;
+ check->minx = SCREENWIDTH;
+ check->maxx = -1;
+ 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;
+ int unionl, unionh;
+ int 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; x++)
+ if (pl->top[x] != 0xff)
+ break;
+
+ if (x > intrh)
+ {
+ pl->minx = unionl;
+ pl->maxx = unionh;
+ return pl; // use the same one
+ }
+
+// make a new visplane
+
+ lastvisplane->height = pl->height;
+ lastvisplane->picnum = pl->picnum;
+ lastvisplane->lightlevel = pl->lightlevel;
+ lastvisplane->special = pl->special;
+ pl = lastvisplane++;
+ pl->minx = start;
+ pl->maxx = stop;
+ memset(pl->top, 0xff, sizeof(pl->top));
+
+ return pl;
+}
+
+
+
+//=============================================================================
+
+/*
+================
+=
+= R_MakeSpans
+=
+================
+*/
+
+void R_MakeSpans(int x, int t1, int b1, int t2, int b2)
+{
+ while (t1 < t2 && t1 <= b1)
+ {
+ R_MapPlane(t1, spanstart[t1], x - 1);
+ t1++;
+ }
+ while (b1 > b2 && b1 >= t1)
+ {
+ R_MapPlane(b1, spanstart[b1], x - 1);
+ b1--;
+ }
+
+ while (t2 < t1 && t2 <= b2)
+ {
+ spanstart[t2] = x;
+ t2++;
+ }
+ while (b2 > b1 && b2 >= t2)
+ {
+ spanstart[b2] = x;
+ b2--;
+ }
+}
+
+
+
+/*
+================
+=
+= R_DrawPlanes
+=
+= At the end of each frame
+================
+*/
+
+void R_DrawPlanes(void)
+{
+ visplane_t *pl;
+ int light;
+ int x, stop;
+ int lumpnum;
+ int angle;
+ byte *tempSource;
+
+ byte *dest;
+ int count;
+ fixed_t frac, fracstep;
+
+ extern byte *ylookup[MAXHEIGHT];
+ extern int columnofs[MAXWIDTH];
+
+#ifdef RANGECHECK
+ if (ds_p - drawsegs > MAXDRAWSEGS)
+ I_Error("R_DrawPlanes: drawsegs overflow (%i)", ds_p - drawsegs);
+ if (lastvisplane - visplanes > MAXVISPLANES)
+ I_Error("R_DrawPlanes: visplane overflow (%i)",
+ lastvisplane - visplanes);
+ if (lastopening - openings > MAXOPENINGS)
+ I_Error("R_DrawPlanes: opening overflow (%i)",
+ lastopening - openings);
+#endif
+
+ for (pl = visplanes; pl < lastvisplane; pl++)
+ {
+ if (pl->minx > pl->maxx)
+ continue;
+ //
+ // sky flat
+ //
+ if (pl->picnum == skyflatnum)
+ {
+ dc_iscale = skyiscale;
+ dc_colormap = colormaps; // sky is allways drawn full bright
+ dc_texturemid = skytexturemid;
+ for (x = pl->minx; x <= pl->maxx; x++)
+ {
+ dc_yl = pl->top[x];
+ dc_yh = pl->bottom[x];
+ if (dc_yl <= dc_yh)
+ {
+ angle = (viewangle + xtoviewangle[x]) >> ANGLETOSKYSHIFT;
+ dc_x = x;
+ dc_source = R_GetColumn(skytexture, angle);
+
+ count = dc_yh - dc_yl;
+ if (count < 0)
+ return;
+
+#ifdef RANGECHECK
+ if ((unsigned) dc_x >= SCREENWIDTH || dc_yl < 0
+ || dc_yh >= SCREENHEIGHT)
+ I_Error("R_DrawColumn: %i to %i at %i", dc_yl, dc_yh,
+ dc_x);
+#endif
+
+ dest = ylookup[dc_yl] + columnofs[dc_x];
+
+ fracstep = 1;
+ frac = (dc_texturemid >> FRACBITS) + (dc_yl - centery);
+ do
+ {
+ *dest = dc_source[frac];
+ dest += SCREENWIDTH;
+ frac += fracstep;
+ }
+ while (count--);
+
+// colfunc ();
+ }
+ }
+ continue;
+ }
+
+ //
+ // regular flat
+ //
+ lumpnum = firstflat + flattranslation[pl->picnum];
+
+ tempSource = W_CacheLumpNum(lumpnum, PU_STATIC);
+
+ switch (pl->special)
+ {
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29: // Scroll_North
+ ds_source = tempSource;
+ break;
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ case 24: // Scroll_East
+ ds_source = tempSource + ((63 - ((leveltime >> 1) & 63)) <<
+ (pl->special - 20) & 63);
+ //ds_source = tempSource+((leveltime>>1)&63);
+ break;
+ case 30:
+ case 31:
+ case 32:
+ case 33:
+ case 34: // Scroll_South
+ ds_source = tempSource;
+ break;
+ case 35:
+ case 36:
+ case 37:
+ case 38:
+ case 39: // Scroll_West
+ ds_source = tempSource;
+ break;
+ case 4: // Scroll_EastLavaDamage
+ ds_source =
+ tempSource + (((63 - ((leveltime >> 1) & 63)) << 3) & 63);
+ break;
+ default:
+ ds_source = tempSource;
+ }
+ planeheight = abs(pl->height - viewz);
+ light = (pl->lightlevel >> LIGHTSEGSHIFT) + extralight;
+ if (light >= LIGHTLEVELS)
+ light = LIGHTLEVELS - 1;
+ if (light < 0)
+ light = 0;
+ planezlight = zlight[light];
+
+ pl->top[pl->maxx + 1] = 0xff;
+ pl->top[pl->minx - 1] = 0xff;
+
+ stop = pl->maxx + 1;
+ for (x = pl->minx; x <= stop; x++)
+ R_MakeSpans(x, pl->top[x - 1], pl->bottom[x - 1], pl->top[x],
+ pl->bottom[x]);
+
+ W_ReleaseLumpNum(lumpnum);
+ }
+}
diff --git a/src/heretic/r_segs.c b/src/heretic/r_segs.c
new file mode 100644
index 00000000..4e95a2ae
--- /dev/null
+++ b/src/heretic/r_segs.c
@@ -0,0 +1,669 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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_SEGS.C
+//**
+//** This version has the tall-sector-crossing-precision-bug fixed.
+//**
+//**************************************************************************
+
+#include <stdlib.h>
+
+#include "doomdef.h"
+#include "r_local.h"
+
+// OPTIMIZE: closed two sided lines as single sided
+
+boolean segtextured; // true if any of the segs textures might be vis
+boolean markfloor; // false if the back side is the same plane
+boolean markceiling;
+boolean maskedtexture;
+int toptexture, bottomtexture, midtexture;
+
+
+angle_t rw_normalangle;
+int rw_angle1; // angle to line origin
+
+//
+// wall
+//
+int rw_x;
+int rw_stopx;
+angle_t rw_centerangle;
+fixed_t rw_offset;
+fixed_t rw_distance;
+fixed_t rw_scale;
+fixed_t rw_scalestep;
+fixed_t rw_midtexturemid;
+fixed_t rw_toptexturemid;
+fixed_t rw_bottomtexturemid;
+
+int worldtop, worldbottom, worldhigh, worldlow;
+
+fixed_t pixhigh, pixlow;
+fixed_t pixhighstep, pixlowstep;
+fixed_t topfrac, topstep;
+fixed_t bottomfrac, bottomstep;
+
+
+lighttable_t **walllights;
+
+short *maskedtexturecol;
+
+/*
+================
+=
+= R_RenderMaskedSegRange
+=
+================
+*/
+
+void R_RenderMaskedSegRange(drawseg_t * ds, int x1, int x2)
+{
+ unsigned index;
+ column_t *col;
+ int lightnum;
+ int texnum;
+
+//
+// calculate light table
+// use different light tables for horizontal / vertical / diagonal
+// OPTIMIZE: get rid of LIGHTSEGSHIFT globally
+ curline = ds->curline;
+ frontsector = curline->frontsector;
+ backsector = curline->backsector;
+ texnum = texturetranslation[curline->sidedef->midtexture];
+
+ lightnum = (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight;
+ if (curline->v1->y == curline->v2->y)
+ lightnum--;
+ else if (curline->v1->x == curline->v2->x)
+ lightnum++;
+ if (lightnum < 0)
+ walllights = scalelight[0];
+ else if (lightnum >= LIGHTLEVELS)
+ walllights = scalelight[LIGHTLEVELS - 1];
+ else
+ walllights = scalelight[lightnum];
+
+ 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)
+ {
+ dc_texturemid = frontsector->floorheight > backsector->floorheight
+ ? frontsector->floorheight : backsector->floorheight;
+ dc_texturemid = dc_texturemid + textureheight[texnum] - viewz;
+ }
+ else
+ {
+ dc_texturemid = frontsector->ceilingheight < backsector->ceilingheight
+ ? frontsector->ceilingheight : backsector->ceilingheight;
+ dc_texturemid = dc_texturemid - viewz;
+ }
+ dc_texturemid += curline->sidedef->rowoffset;
+
+ if (fixedcolormap)
+ dc_colormap = fixedcolormap;
+//
+// draw the columns
+//
+ for (dc_x = x1; dc_x <= x2; dc_x++)
+ {
+ // calculate lighting
+ if (maskedtexturecol[dc_x] != SHRT_MAX)
+ {
+ if (!fixedcolormap)
+ {
+ index = spryscale >> LIGHTSCALESHIFT;
+ if (index >= MAXLIGHTSCALE)
+ index = MAXLIGHTSCALE - 1;
+ dc_colormap = walllights[index];
+ }
+
+ sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
+ dc_iscale = 0xffffffffu / (unsigned) spryscale;
+
+ //
+ // draw the texture
+ //
+ col = (column_t *) ((byte *)
+ R_GetColumn(texnum,
+ maskedtexturecol[dc_x]) - 3);
+
+ R_DrawMaskedColumn(col, -1);
+ maskedtexturecol[dc_x] = SHRT_MAX;
+ }
+ spryscale += rw_scalestep;
+ }
+
+}
+
+/*
+================
+=
+= 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)
+
+void R_RenderSegLoop(void)
+{
+ angle_t angle;
+ unsigned index;
+ int yl, yh, mid;
+ fixed_t texturecolumn;
+ int top, bottom;
+
+ texturecolumn = 0; // shut up compiler warning
+
+ for (; rw_x < rw_stopx; rw_x++)
+ {
+//
+// mark floor / ceiling areas
+//
+ yl = (topfrac + HEIGHTUNIT - 1) >> HEIGHTBITS;
+ if (yl < ceilingclip[rw_x] + 1)
+ yl = ceilingclip[rw_x] + 1; // no space above wall
+ if (markceiling)
+ {
+ top = ceilingclip[rw_x] + 1;
+ 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;
+ }
+ }
+
+ yh = bottomfrac >> HEIGHTBITS;
+ if (yh >= floorclip[rw_x])
+ yh = floorclip[rw_x] - 1;
+ if (markfloor)
+ {
+ top = yh + 1;
+ bottom = floorclip[rw_x] - 1;
+ if (top <= ceilingclip[rw_x])
+ top = ceilingclip[rw_x] + 1;
+ if (top <= bottom)
+ {
+ floorplane->top[rw_x] = top;
+ floorplane->bottom[rw_x] = bottom;
+ }
+ }
+
+//
+// texturecolumn and lighting are independent of wall tiers
+//
+ if (segtextured)
+ {
+ // calculate texture offset
+ angle = (rw_centerangle + xtoviewangle[rw_x]) >> ANGLETOFINESHIFT;
+ texturecolumn =
+ rw_offset - FixedMul(finetangent[angle], rw_distance);
+ texturecolumn >>= FRACBITS;
+ // calculate lighting
+ index = rw_scale >> LIGHTSCALESHIFT;
+ if (index >= MAXLIGHTSCALE)
+ index = MAXLIGHTSCALE - 1;
+ dc_colormap = walllights[index];
+ dc_x = rw_x;
+ dc_iscale = 0xffffffffu / (unsigned) rw_scale;
+ }
+
+//
+// draw the wall tiers
+//
+ if (midtexture)
+ { // single sided line
+ dc_yl = yl;
+ dc_yh = yh;
+ dc_texturemid = rw_midtexturemid;
+ dc_source = R_GetColumn(midtexture, texturecolumn);
+ colfunc();
+ ceilingclip[rw_x] = viewheight;
+ floorclip[rw_x] = -1;
+ }
+ else
+ { // two sided line
+ if (toptexture)
+ { // top wall
+ mid = pixhigh >> HEIGHTBITS;
+ pixhigh += pixhighstep;
+ if (mid >= floorclip[rw_x])
+ mid = floorclip[rw_x] - 1;
+ if (mid >= yl)
+ {
+ dc_yl = yl;
+ dc_yh = mid;
+ dc_texturemid = rw_toptexturemid;
+ dc_source = R_GetColumn(toptexture, texturecolumn);
+ colfunc();
+ 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
+ mid = (pixlow + HEIGHTUNIT - 1) >> HEIGHTBITS;
+ pixlow += pixlowstep;
+ if (mid <= ceilingclip[rw_x])
+ mid = ceilingclip[rw_x] + 1; // no space above wall
+ if (mid <= yh)
+ {
+ dc_yl = mid;
+ dc_yh = yh;
+ dc_texturemid = rw_bottomtexturemid;
+ dc_source = R_GetColumn(bottomtexture, texturecolumn);
+ colfunc();
+ floorclip[rw_x] = mid;
+ }
+ else
+ floorclip[rw_x] = yh + 1;
+ }
+ else
+ { // no bottom wall
+ if (markfloor)
+ floorclip[rw_x] = yh + 1;
+ }
+
+ if (maskedtexture)
+ { // save texturecol for backdrawing of masked mid texture
+ maskedtexturecol[rw_x] = texturecolumn;
+ }
+ }
+
+ rw_scale += rw_scalestep;
+ topfrac += topstep;
+ bottomfrac += bottomstep;
+ }
+
+}
+
+
+
+/*
+=====================
+=
+= R_StoreWallRange
+=
+= A wall segment will be drawn between start and stop pixels (inclusive)
+=
+======================
+*/
+
+void R_StoreWallRange(int start, int stop)
+{
+ fixed_t hyp;
+ fixed_t sineval;
+ angle_t distangle, offsetangle;
+ fixed_t vtop;
+ int lightnum;
+
+ if (ds_p == &drawsegs[MAXDRAWSEGS])
+ return; // don't overflow and crash
+
+#ifdef RANGECHECK
+ if (start >= viewwidth || start > stop)
+ I_Error("Bad R_RenderWallRange: %i to %i", start, stop);
+#endif
+
+ 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 = abs(rw_normalangle - rw_angle1);
+ if (offsetangle > ANG90)
+ offsetangle = ANG90;
+ distangle = ANG90 - offsetangle;
+ hyp = R_PointToDist(curline->v1->x, curline->v1->y);
+ sineval = finesine[distangle >> ANGLETOFINESHIFT];
+ rw_distance = FixedMul(hyp, sineval);
+
+
+ ds_p->x1 = rw_x = start;
+ ds_p->x2 = stop;
+ ds_p->curline = curline;
+ rw_stopx = stop + 1;
+
+//
+// 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
+ {
+ //
+ // try to fix the stretched line bug
+ //
+#if 0
+ if (rw_distance < FRACUNIT / 2)
+ {
+ fixed_t trx, try;
+ fixed_t gxt, gyt;
+
+ trx = curline->v1->x - viewx;
+ try = curline->v1->y - viewy;
+
+ gxt = FixedMul(trx, viewcos);
+ gyt = -FixedMul(try, viewsin);
+ ds_p->scale1 = FixedDiv(projection, gxt - gyt);
+ }
+#endif
+ 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];
+ // a single sided line is terminal, so it must mark ends
+ markfloor = markceiling = true;
+ if (linedef->flags & ML_DONTPEGBOTTOM)
+ {
+ vtop = frontsector->floorheight +
+ textureheight[sidedef->midtexture];
+ rw_midtexturemid = vtop - viewz; // bottom of texture at bottom
+ }
+ else
+ rw_midtexturemid = worldtop; // top of texture at top
+ rw_midtexturemid += sidedef->rowoffset;
+ 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 (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;
+// ds_p->sprbottomclip = negonearray;
+ }
+ 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;
+// ds_p->sprtopclip = screenheightarray;
+ }
+
+ if (backsector->ceilingheight <= frontsector->floorheight)
+ {
+ ds_p->sprbottomclip = negonearray;
+ ds_p->bsilheight = INT_MAX;
+ ds_p->silhouette |= SIL_BOTTOM;
+ }
+ if (backsector->floorheight >= frontsector->ceilingheight)
+ {
+ ds_p->sprtopclip = screenheightarray;
+ ds_p->tsilheight = INT_MIN;
+ ds_p->silhouette |= SIL_TOP;
+ }
+ 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;
+
+ if (worldlow != worldbottom
+ || backsector->floorpic != frontsector->floorpic
+ || backsector->lightlevel != frontsector->lightlevel)
+ markfloor = true;
+ else
+ markfloor = false; // same plane on both sides
+
+ if (worldhigh != worldtop
+ || backsector->ceilingpic != frontsector->ceilingpic
+ || backsector->lightlevel != frontsector->lightlevel)
+ markceiling = true;
+ else
+ markceiling = false; // same plane on both sides
+
+ if (backsector->ceilingheight <= frontsector->floorheight
+ || backsector->floorheight >= frontsector->ceilingheight)
+ markceiling = markfloor = true; // closed door
+
+ if (worldhigh < worldtop)
+ { // top texture
+ toptexture = texturetranslation[sidedef->toptexture];
+ if (linedef->flags & ML_DONTPEGTOP)
+ rw_toptexturemid = worldtop; // top of texture at top
+ else
+ {
+ vtop = backsector->ceilingheight +
+ textureheight[sidedef->toptexture];
+ rw_toptexturemid = vtop - viewz; // bottom of texture
+ }
+ }
+ if (worldlow > worldbottom)
+ { // bottom texture
+ bottomtexture = texturetranslation[sidedef->bottomtexture];
+ if (linedef->flags & ML_DONTPEGBOTTOM)
+ { // bottom of texture at bottom
+ rw_bottomtexturemid = worldtop; // top of texture at top
+ }
+ else // top of texture at top
+ rw_bottomtexturemid = worldlow;
+ }
+ rw_toptexturemid += sidedef->rowoffset;
+ rw_bottomtexturemid += sidedef->rowoffset;
+
+ //
+ // 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)
+ {
+ offsetangle = rw_normalangle - rw_angle1;
+ if (offsetangle > ANG180)
+ offsetangle = -offsetangle;
+ if (offsetangle > ANG90)
+ offsetangle = ANG90;
+ sineval = finesine[offsetangle >> ANGLETOFINESHIFT];
+ rw_offset = FixedMul(hyp, sineval);
+ if (rw_normalangle - rw_angle1 < ANG180)
+ rw_offset = -rw_offset;
+ rw_offset += sidedef->textureoffset + curline->offset;
+ rw_centerangle = ANG90 + viewangle - rw_normalangle;
+
+ //
+ // calculate light table
+ // use different light tables for horizontal / vertical / diagonal
+ // OPTIMIZE: get rid of LIGHTSEGSHIFT globally
+ if (!fixedcolormap)
+ {
+ lightnum =
+ (frontsector->lightlevel >> LIGHTSEGSHIFT) + extralight;
+ if (curline->v1->y == curline->v2->y)
+ lightnum--;
+ else if (curline->v1->x == curline->v2->x)
+ lightnum++;
+ if (lightnum < 0)
+ walllights = scalelight[0];
+ else if (lightnum >= LIGHTLEVELS)
+ walllights = scalelight[LIGHTLEVELS - 1];
+ else
+ walllights = scalelight[lightnum];
+ }
+ }
+
+
+//
+// if a floor / ceiling plane is on the wrong side of the view plane
+// it is definately invisible and doesn't need to be marked
+//
+ if (frontsector->floorheight >= viewz)
+ markfloor = false; // above view plane
+ if (frontsector->ceilingheight <= viewz
+ && frontsector->ceilingpic != skyflatnum)
+ markceiling = false; // below view plane
+
+//
+// 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)
+ ceilingplane = R_CheckPlane(ceilingplane, rw_x, rw_stopx - 1);
+ if (markfloor)
+ floorplane = R_CheckPlane(floorplane, rw_x, rw_stopx - 1);
+
+ R_RenderSegLoop();
+
+//
+// save sprite clipping info
+//
+ if (((ds_p->silhouette & SIL_TOP) || maskedtexture) && !ds_p->sprtopclip)
+ {
+ memcpy(lastopening, ceilingclip + start, 2 * (rw_stopx - start));
+ ds_p->sprtopclip = lastopening - start;
+ lastopening += rw_stopx - start;
+ }
+ if (((ds_p->silhouette & SIL_BOTTOM) || maskedtexture)
+ && !ds_p->sprbottomclip)
+ {
+ memcpy(lastopening, floorclip + start, 2 * (rw_stopx - start));
+ 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/heretic/r_things.c b/src/heretic/r_things.c
new file mode 100644
index 00000000..e5ff4b4b
--- /dev/null
+++ b/src/heretic/r_things.c
@@ -0,0 +1,1027 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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_things.c
+#include <stdio.h>
+#include <stdlib.h>
+#include "doomdef.h"
+#include "deh_str.h"
+#include "i_swap.h"
+#include "i_system.h"
+#include "r_local.h"
+
+void R_DrawColumn(void);
+void R_DrawTLColumn(void);
+
+typedef struct
+{
+ int x1, 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, pspriteiscale;
+
+lighttable_t **spritelights;
+
+// constant arrays used for psprite clipping and initializing clipping
+short negonearray[SCREENWIDTH];
+short screenheightarray[SCREENWIDTH];
+
+/*
+===============================================================================
+
+ INITIALIZATION FUNCTIONS
+
+===============================================================================
+*/
+
+// variables used to look up and range check thing_t sprites patches
+spritedef_t *sprites;
+int numsprites;
+
+spriteframe_t sprtemp[26];
+int maxframe;
+char *spritename;
+
+
+
+/*
+=================
+=
+= R_InstallSpriteLump
+=
+= Local function for R_InitSprites
+=================
+*/
+
+void R_InstallSpriteLump(int lump, unsigned frame, unsigned rotation,
+ boolean flipped)
+{
+ int r;
+
+ if (frame >= 26 || 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
+ if (sprtemp[frame].rotate == false)
+ I_Error("R_InitSprites: Sprite %s frame %c has multip rot=0 lump",
+ spritename, 'A' + frame);
+ if (sprtemp[frame].rotate == true)
+ I_Error
+ ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump",
+ spritename, 'A' + frame);
+
+ sprtemp[frame].rotate = false;
+ for (r = 0; r < 8; r++)
+ {
+ sprtemp[frame].lump[r] = lump - firstspritelump;
+ sprtemp[frame].flip[r] = (byte) flipped;
+ }
+ return;
+ }
+
+// the lump is only used for one rotation
+ if (sprtemp[frame].rotate == false)
+ I_Error
+ ("R_InitSprites: Sprite %s frame %c has rotations and a rot=0 lump",
+ spritename, 'A' + frame);
+
+ sprtemp[frame].rotate = true;
+
+ rotation--; // make 0 based
+ if (sprtemp[frame].lump[rotation] != -1)
+ I_Error
+ ("R_InitSprites: Sprite %s : %c : %c has two lumps mapped to it",
+ spritename, 'A' + frame, '1' + rotation);
+
+ sprtemp[frame].lump[rotation] = lump - firstspritelump;
+ sprtemp[frame].flip[rotation] = (byte) flipped;
+}
+
+/*
+=================
+=
+= 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 inconsistant
+=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
+=================
+*/
+
+void R_InitSpriteDefs(char **namelist)
+{
+ char **check;
+ int i, l, frame, rotation;
+ int start, end;
+
+// count the number of sprite names
+ check = namelist;
+ while (*check != NULL)
+ check++;
+ numsprites = check - namelist;
+
+ if (!numsprites)
+ return;
+
+ sprites = Z_Malloc(numsprites * sizeof(*sprites), PU_STATIC, NULL);
+
+ start = firstspritelump - 1;
+ end = lastspritelump + 1;
+
+// scan all the lump names for each of the names, noting the highest
+// frame letter
+// Just compare 4 characters as ints
+ for (i = 0; i < numsprites; i++)
+ {
+ spritename = DEH_String(namelist[i]);
+ memset(sprtemp, -1, sizeof(sprtemp));
+
+ maxframe = -1;
+
+ //
+ // scan the lumps, filling in the frames for whatever is found
+ //
+ for (l = start + 1; l < end; l++)
+ if (!strncasecmp(lumpinfo[l].name, spritename, 4))
+ {
+ frame = lumpinfo[l].name[4] - 'A';
+ rotation = lumpinfo[l].name[5] - '0';
+ R_InstallSpriteLump(l, frame, rotation, false);
+ if (lumpinfo[l].name[6])
+ {
+ frame = lumpinfo[l].name[6] - 'A';
+ rotation = lumpinfo[l].name[7] - '0';
+ R_InstallSpriteLump(l, frame, rotation, true);
+ }
+ }
+
+ //
+ // check the frames that were found for completeness
+ //
+ if (maxframe == -1)
+ {
+ //continue;
+ sprites[i].numframes = 0;
+ if (gamemode == shareware)
+ continue;
+ I_Error("R_InitSprites: No lumps found for sprite %s",
+ spritename);
+ }
+
+ maxframe++;
+ 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 %s frame %c",
+ spritename, frame + 'A');
+ case 0: // only the first rotation is needed
+ break;
+
+ case 1: // must have all 8 frames
+ for (rotation = 0; rotation < 8; rotation++)
+ if (sprtemp[frame].lump[rotation] == -1)
+ I_Error
+ ("R_InitSprites: Sprite %s frame %c is missing rotations",
+ spritename, frame + 'A');
+ }
+ }
+
+ //
+ // allocate space for the frames present and copy sprtemp to it
+ //
+ sprites[i].numframes = maxframe;
+ sprites[i].spriteframes =
+ Z_Malloc(maxframe * sizeof(spriteframe_t), PU_STATIC, NULL);
+ memcpy(sprites[i].spriteframes, sprtemp,
+ maxframe * sizeof(spriteframe_t));
+ }
+
+}
+
+
+/*
+===============================================================================
+
+ GAME FUNCTIONS
+
+===============================================================================
+*/
+
+vissprite_t vissprites[MAXVISSPRITES], *vissprite_p;
+int newvissprite;
+
+
+/*
+===================
+=
+= R_InitSprites
+=
+= Called at program start
+===================
+*/
+
+void R_InitSprites(char **namelist)
+{
+ int i;
+
+ for (i = 0; i < SCREENWIDTH; i++)
+ {
+ negonearray[i] = -1;
+ }
+
+ R_InitSpriteDefs(namelist);
+}
+
+
+/*
+===================
+=
+= R_ClearSprites
+=
+= Called at frame start
+===================
+*/
+
+void R_ClearSprites(void)
+{
+ vissprite_p = vissprites;
+}
+
+
+/*
+===================
+=
+= R_NewVisSprite
+=
+===================
+*/
+
+vissprite_t overflowsprite;
+
+vissprite_t *R_NewVisSprite(void)
+{
+ if (vissprite_p == &vissprites[MAXVISSPRITES])
+ return &overflowsprite;
+ vissprite_p++;
+ return vissprite_p - 1;
+}
+
+
+/*
+================
+=
+= R_DrawMaskedColumn
+=
+= Used for sprites and masked mid textures
+================
+*/
+
+short *mfloorclip;
+short *mceilingclip;
+fixed_t spryscale;
+fixed_t sprtopscreen;
+fixed_t sprbotscreen;
+
+void R_DrawMaskedColumn(column_t * column, signed int baseclip)
+{
+ int topscreen, bottomscreen;
+ fixed_t basetexturemid;
+
+ basetexturemid = dc_texturemid;
+
+ for (; column->topdelta != 0xff;)
+ {
+// calculate unclipped screen coordinates for post
+ topscreen = sprtopscreen + spryscale * column->topdelta;
+ bottomscreen = topscreen + spryscale * column->length;
+ dc_yl = (topscreen + FRACUNIT - 1) >> FRACBITS;
+ dc_yh = (bottomscreen - 1) >> FRACBITS;
+
+ if (dc_yh >= mfloorclip[dc_x])
+ dc_yh = mfloorclip[dc_x] - 1;
+ if (dc_yl <= mceilingclip[dc_x])
+ dc_yl = mceilingclip[dc_x] + 1;
+
+ if (dc_yh >= baseclip && baseclip != -1)
+ dc_yh = baseclip;
+
+ if (dc_yl <= dc_yh)
+ {
+ dc_source = (byte *) column + 3;
+ dc_texturemid = basetexturemid - (column->topdelta << FRACBITS);
+// dc_source = (byte *)column + 3 - column->topdelta;
+ colfunc(); // either R_DrawColumn or R_DrawTLColumn
+ }
+ column = (column_t *) ((byte *) column + column->length + 4);
+ }
+
+ dc_texturemid = basetexturemid;
+}
+
+
+/*
+================
+=
+= R_DrawVisSprite
+=
+= mfloorclip and mceilingclip should also be set
+================
+*/
+
+void R_DrawVisSprite(vissprite_t * vis, int x1, int x2)
+{
+ column_t *column;
+ int texturecolumn;
+ fixed_t frac;
+ patch_t *patch;
+ fixed_t baseclip;
+
+
+ patch = W_CacheLumpNum(vis->patch + firstspritelump, PU_CACHE);
+
+ dc_colormap = vis->colormap;
+
+// if(!dc_colormap)
+// colfunc = tlcolfunc; // NULL colormap = shadow draw
+
+ if (vis->mobjflags & MF_SHADOW)
+ {
+ if (vis->mobjflags & MF_TRANSLATION)
+ {
+ colfunc = R_DrawTranslatedTLColumn;
+ dc_translation = translationtables - 256 +
+ ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8));
+ }
+ else
+ { // Draw using shadow column function
+ colfunc = tlcolfunc;
+ }
+ }
+ else if (vis->mobjflags & MF_TRANSLATION)
+ {
+ // Draw using translated column function
+ colfunc = R_DrawTranslatedColumn;
+ dc_translation = translationtables - 256 +
+ ((vis->mobjflags & MF_TRANSLATION) >> (MF_TRANSSHIFT - 8));
+ }
+
+ dc_iscale = abs(vis->xiscale) >> detailshift;
+ dc_texturemid = vis->texturemid;
+ frac = vis->startfrac;
+ spryscale = vis->scale;
+
+ sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
+
+// check to see if weapon is a vissprite
+ if (vis->psprite)
+ {
+ dc_texturemid += FixedMul(((centery - viewheight / 2) << FRACBITS),
+ vis->xiscale);
+ sprtopscreen += (viewheight / 2 - centery) << FRACBITS;
+ }
+
+ if (vis->footclip && !vis->psprite)
+ {
+ sprbotscreen = sprtopscreen + FixedMul(patch->height << FRACBITS,
+ spryscale);
+ baseclip = (sprbotscreen - FixedMul(vis->footclip << FRACBITS,
+ spryscale)) >> FRACBITS;
+ }
+ else
+ {
+ baseclip = -1;
+ }
+
+ for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale)
+ {
+ texturecolumn = frac >> FRACBITS;
+#ifdef RANGECHECK
+ if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width))
+ I_Error("R_DrawSpriteRange: bad texturecolumn");
+#endif
+ column = (column_t *) ((byte *) patch +
+ LONG(patch->columnofs[texturecolumn]));
+ R_DrawMaskedColumn(column, baseclip);
+ }
+
+ colfunc = basecolfunc;
+}
+
+
+
+/*
+===================
+=
+= R_ProjectSprite
+=
+= Generates a vissprite for a thing if it might be visible
+=
+===================
+*/
+
+void R_ProjectSprite(mobj_t * thing)
+{
+ fixed_t trx, try;
+ fixed_t gxt, gyt;
+ fixed_t tx, tz;
+ fixed_t xscale;
+ int x1, x2;
+ spritedef_t *sprdef;
+ spriteframe_t *sprframe;
+ int lump;
+ unsigned rot;
+ boolean flip;
+ int index;
+ vissprite_t *vis;
+ angle_t ang;
+ fixed_t iscale;
+
+ if (thing->flags2 & MF2_DONTDRAW)
+ { // Never make a vissprite when MF2_DONTDRAW is flagged.
+ return;
+ }
+
+//
+// transform the origin point
+//
+ trx = thing->x - viewx;
+ try = thing->y - viewy;
+
+ gxt = FixedMul(trx, viewcos);
+ gyt = -FixedMul(try, viewsin);
+ tz = gxt - gyt;
+
+ if (tz < MINZ)
+ return; // thing is behind view plane
+ xscale = FixedDiv(projection, tz);
+
+ gxt = -FixedMul(trx, viewsin);
+ gyt = FixedMul(try, viewcos);
+ tx = -(gyt + gxt);
+
+ if (abs(tx) > (tz << 2))
+ return; // too far off the side
+
+//
+// decide which patch to use for sprite reletive to player
+//
+#ifdef RANGECHECK
+ if ((unsigned) thing->sprite >= numsprites)
+ I_Error("R_ProjectSprite: invalid sprite number %i ", thing->sprite);
+#endif
+ sprdef = &sprites[thing->sprite];
+#ifdef RANGECHECK
+ if ((thing->frame & FF_FRAMEMASK) >= sprdef->numframes)
+ I_Error("R_ProjectSprite: invalid sprite frame %i : %i ",
+ thing->sprite, thing->frame);
+#endif
+ sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK];
+
+ if (sprframe->rotate)
+ { // choose a different rotation based on player view
+ ang = R_PointToAngle(thing->x, thing->y);
+ 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];
+ }
+
+//
+// calculate edges of the shape
+//
+ tx -= spriteoffset[lump];
+ x1 = (centerxfrac + FixedMul(tx, xscale)) >> FRACBITS;
+ if (x1 > viewwidth)
+ return; // off the right side
+ tx += spritewidth[lump];
+ x2 = ((centerxfrac + FixedMul(tx, xscale)) >> FRACBITS) - 1;
+ if (x2 < 0)
+ return; // off the left side
+
+
+//
+// store information in a vissprite
+//
+ vis = R_NewVisSprite();
+ vis->mobjflags = thing->flags;
+ vis->psprite = false;
+ vis->scale = xscale << detailshift;
+ vis->gx = thing->x;
+ vis->gy = thing->y;
+ vis->gz = thing->z;
+ vis->gzt = thing->z + spritetopoffset[lump];
+
+ // foot clipping
+ if (thing->flags2 & MF2_FEETARECLIPPED
+ && thing->z <= thing->subsector->sector->floorheight)
+ {
+ vis->footclip = 10;
+ }
+ else
+ vis->footclip = 0;
+ vis->texturemid = vis->gzt - viewz - (vis->footclip << FRACBITS);
+
+ vis->x1 = x1 < 0 ? 0 : x1;
+ vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2;
+ iscale = FixedDiv(FRACUNIT, xscale);
+ if (flip)
+ {
+ vis->startfrac = spritewidth[lump] - 1;
+ vis->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 = colormaps; // full bright
+ else
+ { // diminished light
+ index = xscale >> (LIGHTSCALESHIFT - detailshift);
+ if (index >= MAXLIGHTSCALE)
+ index = MAXLIGHTSCALE - 1;
+ vis->colormap = spritelights[index];
+ }
+}
+
+
+
+
+/*
+========================
+=
+= R_AddSprites
+=
+========================
+*/
+
+void R_AddSprites(sector_t * sec)
+{
+ mobj_t *thing;
+ int lightnum;
+
+ if (sec->validcount == validcount)
+ return; // already added
+
+ sec->validcount = validcount;
+
+ lightnum = (sec->lightlevel >> LIGHTSEGSHIFT) + extralight;
+ if (lightnum < 0)
+ spritelights = scalelight[0];
+ else if (lightnum >= LIGHTLEVELS)
+ spritelights = scalelight[LIGHTLEVELS - 1];
+ else
+ spritelights = scalelight[lightnum];
+
+
+ for (thing = sec->thinglist; thing; thing = thing->snext)
+ R_ProjectSprite(thing);
+}
+
+
+/*
+========================
+=
+= R_DrawPSprite
+=
+========================
+*/
+
+int PSpriteSY[NUMWEAPONS] = {
+ 0, // staff
+ 5 * FRACUNIT, // goldwand
+ 15 * FRACUNIT, // crossbow
+ 15 * FRACUNIT, // blaster
+ 15 * FRACUNIT, // skullrod
+ 15 * FRACUNIT, // phoenix rod
+ 15 * FRACUNIT, // mace
+ 15 * FRACUNIT, // gauntlets
+ 15 * FRACUNIT // beak
+};
+
+void R_DrawPSprite(pspdef_t * psp)
+{
+ fixed_t tx;
+ int x1, x2;
+ spritedef_t *sprdef;
+ spriteframe_t *sprframe;
+ int lump;
+ boolean flip;
+ vissprite_t *vis, avis;
+
+ int tempangle;
+
+//
+// decide which patch to use
+//
+#ifdef RANGECHECK
+ if ((unsigned) psp->state->sprite >= numsprites)
+ I_Error("R_ProjectSprite: invalid sprite number %i ",
+ psp->state->sprite);
+#endif
+ sprdef = &sprites[psp->state->sprite];
+#ifdef RANGECHECK
+ if ((psp->state->frame & FF_FRAMEMASK) >= sprdef->numframes)
+ I_Error("R_ProjectSprite: invalid sprite frame %i : %i ",
+ psp->state->sprite, psp->state->frame);
+#endif
+ sprframe = &sprdef->spriteframes[psp->state->frame & FF_FRAMEMASK];
+
+ lump = sprframe->lump[0];
+ flip = (boolean) sprframe->flip[0];
+
+//
+// calculate edges of the shape
+//
+ tx = psp->sx - 160 * FRACUNIT;
+
+ tx -= spriteoffset[lump];
+ if (viewangleoffset)
+ {
+ tempangle =
+ ((centerxfrac / 1024) * (viewangleoffset >> ANGLETOFINESHIFT));
+ }
+ else
+ {
+ tempangle = 0;
+ }
+ x1 = (centerxfrac + FixedMul(tx, pspritescale) + tempangle) >> FRACBITS;
+ if (x1 > viewwidth)
+ return; // off the right side
+ tx += spritewidth[lump];
+ x2 = ((centerxfrac + FixedMul(tx, pspritescale) +
+ tempangle) >> FRACBITS) - 1;
+ if (x2 < 0)
+ return; // off the left side
+
+//
+// store information in a vissprite
+//
+ vis = &avis;
+ vis->mobjflags = 0;
+ vis->psprite = true;
+ vis->texturemid =
+ (BASEYCENTER << FRACBITS) + FRACUNIT / 2 - (psp->sy -
+ spritetopoffset[lump]);
+ if (viewheight == SCREENHEIGHT)
+ {
+ vis->texturemid -= PSpriteSY[players[consoleplayer].readyweapon];
+ }
+ vis->x1 = x1 < 0 ? 0 : x1;
+ vis->x2 = x2 >= viewwidth ? viewwidth - 1 : x2;
+ vis->scale = pspritescale << detailshift;
+ if (flip)
+ {
+ vis->xiscale = -pspriteiscale;
+ vis->startfrac = spritewidth[lump] - 1;
+ }
+ else
+ {
+ vis->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)
+ {
+ // Invisibility
+ vis->colormap = spritelights[MAXLIGHTSCALE - 1];
+ vis->mobjflags |= MF_SHADOW;
+ }
+ else if (fixedcolormap)
+ {
+ // Fixed color
+ vis->colormap = fixedcolormap;
+ }
+ else if (psp->state->frame & FF_FULLBRIGHT)
+ {
+ // Full bright
+ vis->colormap = colormaps;
+ }
+ else
+ {
+ // local light
+ vis->colormap = spritelights[MAXLIGHTSCALE - 1];
+ }
+ R_DrawVisSprite(vis, vis->x1, vis->x2);
+}
+
+/*
+========================
+=
+= R_DrawPlayerSprites
+=
+========================
+*/
+
+void R_DrawPlayerSprites(void)
+{
+ int i, lightnum;
+ pspdef_t *psp;
+
+//
+// get light level
+//
+ lightnum =
+ (viewplayer->mo->subsector->sector->lightlevel >> LIGHTSEGSHIFT) +
+ extralight;
+ if (lightnum < 0)
+ spritelights = scalelight[0];
+ else if (lightnum >= LIGHTLEVELS)
+ spritelights = scalelight[LIGHTLEVELS - 1];
+ else
+ spritelights = scalelight[lightnum];
+//
+// clip to screen bounds
+//
+ mfloorclip = screenheightarray;
+ mceilingclip = negonearray;
+
+//
+// add all active psprites
+//
+ for (i = 0, psp = viewplayer->psprites; i < NUMPSPRITES; i++, psp++)
+ if (psp->state)
+ R_DrawPSprite(psp);
+
+}
+
+
+/*
+========================
+=
+= R_SortVisSprites
+=
+========================
+*/
+
+vissprite_t vsprsortedhead;
+
+void R_SortVisSprites(void)
+{
+ int i, count;
+ vissprite_t *ds, *best;
+ vissprite_t unsorted;
+ fixed_t bestscale;
+
+ count = vissprite_p - vissprites;
+
+ unsorted.next = unsorted.prev = &unsorted;
+ if (!count)
+ return;
+
+ for (ds = vissprites; ds < vissprite_p; ds++)
+ {
+ ds->next = ds + 1;
+ ds->prev = ds - 1;
+ }
+ vissprites[0].prev = &unsorted;
+ unsorted.next = &vissprites[0];
+ (vissprite_p - 1)->next = &unsorted;
+ unsorted.prev = vissprite_p - 1;
+
+//
+// pull the vissprites out by scale
+//
+ best = 0; // shut up the compiler warning
+ vsprsortedhead.next = vsprsortedhead.prev = &vsprsortedhead;
+ for (i = 0; i < count; i++)
+ {
+ bestscale = INT_MAX;
+ for (ds = unsorted.next; ds != &unsorted; ds = ds->next)
+ {
+ if (ds->scale < bestscale)
+ {
+ bestscale = ds->scale;
+ best = ds;
+ }
+ }
+ best->next->prev = best->prev;
+ best->prev->next = best->next;
+ best->next = &vsprsortedhead;
+ best->prev = vsprsortedhead.prev;
+ vsprsortedhead.prev->next = best;
+ vsprsortedhead.prev = best;
+ }
+}
+
+
+
+/*
+========================
+=
+= R_DrawSprite
+=
+========================
+*/
+
+void R_DrawSprite(vissprite_t * spr)
+{
+ drawseg_t *ds;
+ short clipbot[SCREENWIDTH], cliptop[SCREENWIDTH];
+ int x, r1, r2;
+ fixed_t scale, lowscale;
+ int silhouette;
+
+ 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
+//
+ for (ds = ds_p - 1; ds >= drawsegs; ds--)
+ {
+ //
+ // determine if the drawseg obscures the sprite
+ //
+ if (ds->x1 > spr->x2 || ds->x2 < spr->x1 ||
+ (!ds->silhouette && !ds->maskedtexturecol))
+ continue; // doesn't 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
+//
+ silhouette = ds->silhouette;
+ if (spr->gz >= ds->bsilheight)
+ silhouette &= ~SIL_BOTTOM;
+ if (spr->gzt <= ds->tsilheight)
+ silhouette &= ~SIL_TOP;
+
+ if (silhouette == 1)
+ { // bottom sil
+ for (x = r1; x <= r2; x++)
+ if (clipbot[x] == -2)
+ clipbot[x] = ds->sprbottomclip[x];
+ }
+ else if (silhouette == 2)
+ { // top sil
+ for (x = r1; x <= r2; x++)
+ if (cliptop[x] == -2)
+ cliptop[x] = ds->sprtopclip[x];
+ }
+ else if (silhouette == 3)
+ { // both
+ for (x = r1; x <= r2; x++)
+ {
+ if (clipbot[x] == -2)
+ clipbot[x] = ds->sprbottomclip[x];
+ if (cliptop[x] == -2)
+ cliptop[x] = ds->sprtopclip[x];
+ }
+ }
+
+ }
+
+//
+// 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)
+{
+ vissprite_t *spr;
+ drawseg_t *ds;
+
+ R_SortVisSprites();
+
+ if (vissprite_p > vissprites)
+ {
+ // draw all vissprites back to front
+
+ for (spr = vsprsortedhead.next; spr != &vsprsortedhead;
+ spr = spr->next)
+ R_DrawSprite(spr);
+ }
+
+//
+// render any remaining masked mid textures
+//
+ for (ds = ds_p - 1; ds >= drawsegs; ds--)
+ if (ds->maskedtexturecol)
+ R_RenderMaskedSegRange(ds, ds->x1, ds->x2);
+
+//
+// draw the psprites on top of everything
+//
+// Added for the sideviewing with an external device
+ if (viewangleoffset <= 1024 << ANGLETOFINESHIFT || viewangleoffset >=
+ -1024 << ANGLETOFINESHIFT)
+ { // don't draw on side views
+ R_DrawPlayerSprites();
+ }
+
+// if (!viewangleoffset) // don't draw on side views
+// R_DrawPlayerSprites ();
+}
diff --git a/src/heretic/s_sound.c b/src/heretic/s_sound.c
new file mode 100644
index 00000000..73942201
--- /dev/null
+++ b/src/heretic/s_sound.c
@@ -0,0 +1,608 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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 <stdlib.h>
+
+#include "doomdef.h"
+#include "i_system.h"
+#include "m_random.h"
+#include "sounds.h"
+#include "s_sound.h"
+#include "i_sound.h"
+#include "r_local.h"
+#include "p_local.h"
+
+#include "sounds.h"
+
+#include "w_wad.h"
+#include "z_zone.h"
+
+/*
+===============================================================================
+
+ MUSIC & SFX API
+
+===============================================================================
+*/
+
+void S_ShutDown(void);
+boolean S_StopSoundID(int sound_id, int priority);
+
+static channel_t channel[MAX_CHANNELS];
+
+static void *rs; // Handle for the registered song
+int mus_song = -1;
+int mus_lumpnum;
+void *mus_sndptr;
+byte *soundCurve;
+
+int snd_MaxVolume = 10;
+int snd_MusicVolume = 10;
+int snd_Channels = 16;
+
+int AmbChan;
+
+void S_Start(void)
+{
+ int i;
+
+ S_StartSong((gameepisode - 1) * 9 + gamemap - 1, true);
+
+ //stop all sounds
+ for (i = 0; i < snd_Channels; i++)
+ {
+ if (channel[i].handle)
+ {
+ S_StopSound(channel[i].mo);
+ }
+ }
+ memset(channel, 0, 8 * sizeof(channel_t));
+}
+
+void S_StartSong(int song, boolean loop)
+{
+ int mus_len;
+
+ if (song == mus_song)
+ { // don't replay an old song
+ return;
+ }
+
+ if (rs != NULL)
+ {
+ I_StopSong();
+ I_UnRegisterSong(rs);
+ }
+
+ if (song < mus_e1m1 || song > NUMMUSIC)
+ {
+ return;
+ }
+ mus_lumpnum = W_GetNumForName(S_music[song].name);
+ mus_sndptr = W_CacheLumpNum(mus_lumpnum, PU_MUSIC);
+ mus_len = W_LumpLength(mus_lumpnum);
+ rs = I_RegisterSong(mus_sndptr, mus_len);
+ I_PlaySong(rs, loop); //'true' denotes endless looping.
+ mus_song = song;
+}
+
+static mobj_t *GetSoundListener(void)
+{
+ static degenmobj_t dummy_listener;
+
+ // If we are at the title screen, the console player doesn't have an
+ // object yet, so return a pointer to a static dummy listener instead.
+
+ if (players[consoleplayer].mo != NULL)
+ {
+ return players[consoleplayer].mo;
+ }
+ else
+ {
+ dummy_listener.x = 0;
+ dummy_listener.y = 0;
+ dummy_listener.z = 0;
+
+ return (mobj_t *) &dummy_listener;
+ }
+}
+
+void S_StartSound(void *_origin, int sound_id)
+{
+ mobj_t *origin = _origin;
+ mobj_t *listener;
+ int dist, vol;
+ int i;
+ int priority;
+ int sep;
+ int angle;
+ int absx;
+ int absy;
+
+ static int sndcount = 0;
+ int chan;
+
+ listener = GetSoundListener();
+
+ if (sound_id == 0 || snd_MaxVolume == 0)
+ return;
+ if (origin == NULL)
+ {
+ origin = listener;
+ }
+
+// calculate the distance before other stuff so that we can throw out
+// sounds that are beyond the hearing range.
+ absx = abs(origin->x - listener->x);
+ absy = abs(origin->y - listener->y);
+ dist = absx + absy - (absx > absy ? absy >> 1 : absx >> 1);
+ dist >>= FRACBITS;
+// dist = P_AproxDistance(origin->x-viewx, origin->y-viewy)>>FRACBITS;
+
+ if (dist >= MAX_SND_DIST)
+ {
+// dist = MAX_SND_DIST - 1;
+ return; //sound is beyond the hearing range...
+ }
+ if (dist < 0)
+ {
+ dist = 0;
+ }
+ priority = S_sfx[sound_id].priority;
+ priority *= (10 - (dist / 160));
+ if (!S_StopSoundID(sound_id, priority))
+ {
+ return; // other sounds have greater priority
+ }
+ for (i = 0; i < snd_Channels; i++)
+ {
+ if (origin->player)
+ {
+ i = snd_Channels;
+ break; // let the player have more than one sound.
+ }
+ if (origin == channel[i].mo)
+ { // only allow other mobjs one sound
+ S_StopSound(channel[i].mo);
+ break;
+ }
+ }
+ if (i >= snd_Channels)
+ {
+ if (sound_id >= sfx_wind)
+ {
+ if (AmbChan != -1 && S_sfx[sound_id].priority <=
+ S_sfx[channel[AmbChan].sound_id].priority)
+ {
+ return; //ambient channel already in use
+ }
+ else
+ {
+ AmbChan = -1;
+ }
+ }
+ for (i = 0; i < snd_Channels; i++)
+ {
+ if (channel[i].mo == NULL)
+ {
+ break;
+ }
+ }
+ if (i >= snd_Channels)
+ {
+ //look for a lower priority sound to replace.
+ sndcount++;
+ if (sndcount >= snd_Channels)
+ {
+ sndcount = 0;
+ }
+ for (chan = 0; chan < snd_Channels; chan++)
+ {
+ i = (sndcount + chan) % snd_Channels;
+ if (priority >= channel[i].priority)
+ {
+ chan = -1; //denote that sound should be replaced.
+ break;
+ }
+ }
+ if (chan != -1)
+ {
+ return; //no free channels.
+ }
+ else //replace the lower priority sound.
+ {
+ if (channel[i].handle)
+ {
+ if (I_SoundIsPlaying(channel[i].handle))
+ {
+ I_StopSound(channel[i].handle);
+ }
+ if (S_sfx[channel[i].sound_id].usefulness > 0)
+ {
+ S_sfx[channel[i].sound_id].usefulness--;
+ }
+
+ if (AmbChan == i)
+ {
+ AmbChan = -1;
+ }
+ }
+ }
+ }
+ }
+ if (S_sfx[sound_id].lumpnum == 0)
+ {
+ S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]);
+ }
+
+ // calculate the volume based upon the distance from the sound origin.
+// vol = (snd_MaxVolume*16 + dist*(-snd_MaxVolume*16)/MAX_SND_DIST)>>9;
+ vol = soundCurve[dist];
+
+ if (origin == listener)
+ {
+ sep = 128;
+ }
+ else
+ {
+ angle = R_PointToAngle2(listener->x, listener->y,
+ origin->x, origin->y);
+ angle = (angle - viewangle) >> 24;
+ sep = angle * 2 - 128;
+ if (sep < 64)
+ sep = -sep;
+ if (sep > 192)
+ sep = 512 - sep;
+ }
+
+ // TODO: Play pitch-shifted sounds as in Vanilla Heretic
+
+ channel[i].pitch = (byte) (127 + (M_Random() & 7) - (M_Random() & 7));
+ channel[i].handle = I_StartSound(&S_sfx[sound_id], i, vol, sep);
+ channel[i].mo = origin;
+ channel[i].sound_id = sound_id;
+ channel[i].priority = priority;
+ if (sound_id >= sfx_wind)
+ {
+ AmbChan = i;
+ }
+ if (S_sfx[sound_id].usefulness == -1)
+ {
+ S_sfx[sound_id].usefulness = 1;
+ }
+ else
+ {
+ S_sfx[sound_id].usefulness++;
+ }
+}
+
+void S_StartSoundAtVolume(void *_origin, int sound_id, int volume)
+{
+ mobj_t *origin = _origin;
+ mobj_t *listener;
+ int i;
+
+ listener = GetSoundListener();
+
+ if (sound_id == 0 || snd_MaxVolume == 0)
+ return;
+ if (origin == NULL)
+ {
+ origin = listener;
+ }
+
+ if (volume == 0)
+ {
+ return;
+ }
+ volume = (volume * (snd_MaxVolume + 1) * 8) >> 7;
+
+// no priority checking, as ambient sounds would be the LOWEST.
+ for (i = 0; i < snd_Channels; i++)
+ {
+ if (channel[i].mo == NULL)
+ {
+ break;
+ }
+ }
+ if (i >= snd_Channels)
+ {
+ return;
+ }
+ if (S_sfx[sound_id].lumpnum == 0)
+ {
+ S_sfx[sound_id].lumpnum = I_GetSfxLumpNum(&S_sfx[sound_id]);
+ }
+
+ // TODO: Pitch shifting.
+ channel[i].pitch = (byte) (127 - (M_Random() & 3) + (M_Random() & 3));
+ channel[i].handle = I_StartSound(&S_sfx[sound_id], i, volume, 128);
+ channel[i].mo = origin;
+ channel[i].sound_id = sound_id;
+ channel[i].priority = 1; //super low priority.
+ if (S_sfx[sound_id].usefulness == -1)
+ {
+ S_sfx[sound_id].usefulness = 1;
+ }
+ else
+ {
+ S_sfx[sound_id].usefulness++;
+ }
+}
+
+boolean S_StopSoundID(int sound_id, int priority)
+{
+ int i;
+ int lp; //least priority
+ int found;
+
+ if (S_sfx[sound_id].numchannels == -1)
+ {
+ return (true);
+ }
+ lp = -1; //denote the argument sound_id
+ found = 0;
+ for (i = 0; i < snd_Channels; i++)
+ {
+ if (channel[i].sound_id == sound_id && channel[i].mo)
+ {
+ found++; //found one. Now, should we replace it??
+ if (priority >= channel[i].priority)
+ { // if we're gonna kill one, then this'll be it
+ lp = i;
+ priority = channel[i].priority;
+ }
+ }
+ }
+ if (found < S_sfx[sound_id].numchannels)
+ {
+ return (true);
+ }
+ else if (lp == -1)
+ {
+ return (false); // don't replace any sounds
+ }
+ if (channel[lp].handle)
+ {
+ if (I_SoundIsPlaying(channel[lp].handle))
+ {
+ I_StopSound(channel[lp].handle);
+ }
+ if (S_sfx[channel[i].sound_id].usefulness > 0)
+ {
+ S_sfx[channel[i].sound_id].usefulness--;
+ }
+ channel[lp].mo = NULL;
+ }
+ return (true);
+}
+
+void S_StopSound(void *_origin)
+{
+ mobj_t *origin = _origin;
+ int i;
+
+ for (i = 0; i < snd_Channels; i++)
+ {
+ if (channel[i].mo == origin)
+ {
+ I_StopSound(channel[i].handle);
+ if (S_sfx[channel[i].sound_id].usefulness > 0)
+ {
+ S_sfx[channel[i].sound_id].usefulness--;
+ }
+ channel[i].handle = 0;
+ channel[i].mo = NULL;
+ if (AmbChan == i)
+ {
+ AmbChan = -1;
+ }
+ }
+ }
+}
+
+void S_SoundLink(mobj_t * oldactor, mobj_t * newactor)
+{
+ int i;
+
+ for (i = 0; i < snd_Channels; i++)
+ {
+ if (channel[i].mo == oldactor)
+ channel[i].mo = newactor;
+ }
+}
+
+void S_PauseSound(void)
+{
+ I_PauseSong();
+}
+
+void S_ResumeSound(void)
+{
+ I_ResumeSong();
+}
+
+void S_UpdateSounds(mobj_t * listener)
+{
+ int i, dist, vol;
+ int angle;
+ int sep;
+ int priority;
+ int absx;
+ int absy;
+
+ listener = GetSoundListener();
+ if (snd_MaxVolume == 0)
+ {
+ return;
+ }
+
+ for (i = 0; i < snd_Channels; i++)
+ {
+ if (!channel[i].handle || S_sfx[channel[i].sound_id].usefulness == -1)
+ {
+ continue;
+ }
+ if (!I_SoundIsPlaying(channel[i].handle))
+ {
+ if (S_sfx[channel[i].sound_id].usefulness > 0)
+ {
+ S_sfx[channel[i].sound_id].usefulness--;
+ }
+ channel[i].handle = 0;
+ channel[i].mo = NULL;
+ channel[i].sound_id = 0;
+ if (AmbChan == i)
+ {
+ AmbChan = -1;
+ }
+ }
+ if (channel[i].mo == NULL || channel[i].sound_id == 0
+ || channel[i].mo == listener)
+ {
+ continue;
+ }
+ else
+ {
+ absx = abs(channel[i].mo->x - listener->x);
+ absy = abs(channel[i].mo->y - listener->y);
+ dist = absx + absy - (absx > absy ? absy >> 1 : absx >> 1);
+ dist >>= FRACBITS;
+// dist = P_AproxDistance(channel[i].mo->x-listener->x, channel[i].mo->y-listener->y)>>FRACBITS;
+
+ if (dist >= MAX_SND_DIST)
+ {
+ S_StopSound(channel[i].mo);
+ continue;
+ }
+ if (dist < 0)
+ dist = 0;
+
+// calculate the volume based upon the distance from the sound origin.
+// vol = (*((byte *)W_CacheLumpName("SNDCURVE", PU_CACHE)+dist)*(snd_MaxVolume*8))>>7;
+ vol = soundCurve[dist];
+
+ angle = R_PointToAngle2(listener->x, listener->y,
+ channel[i].mo->x, channel[i].mo->y);
+ angle = (angle - viewangle) >> 24;
+ sep = angle * 2 - 128;
+ if (sep < 64)
+ sep = -sep;
+ if (sep > 192)
+ sep = 512 - sep;
+ // TODO: Pitch shifting.
+ I_UpdateSoundParams(channel[i].handle, vol, sep);
+ priority = S_sfx[channel[i].sound_id].priority;
+ priority *= (10 - (dist >> 8));
+ channel[i].priority = priority;
+ }
+ }
+}
+
+void S_Init(void)
+{
+ soundCurve = Z_Malloc(MAX_SND_DIST, PU_STATIC, NULL);
+ I_InitSound(false);
+ if (snd_Channels > 8)
+ {
+ snd_Channels = 8;
+ }
+ I_SetMusicVolume(snd_MusicVolume * 8);
+ S_SetMaxVolume(true);
+
+ I_AtExit(S_ShutDown, true);
+}
+
+void S_GetChannelInfo(SoundInfo_t * s)
+{
+ int i;
+ ChanInfo_t *c;
+
+ s->channelCount = snd_Channels;
+ s->musicVolume = snd_MusicVolume;
+ s->soundVolume = snd_MaxVolume;
+ for (i = 0; i < snd_Channels; i++)
+ {
+ c = &s->chan[i];
+ c->id = channel[i].sound_id;
+ c->priority = channel[i].priority;
+ c->name = S_sfx[c->id].name;
+ c->mo = channel[i].mo;
+
+ if (c->mo != NULL)
+ {
+ c->distance = P_AproxDistance(c->mo->x - viewx, c->mo->y - viewy)
+ >> FRACBITS;
+ }
+ else
+ {
+ c->distance = 0;
+ }
+ }
+}
+
+void S_SetMaxVolume(boolean fullprocess)
+{
+ int i;
+
+ if (!fullprocess)
+ {
+ soundCurve[0] =
+ (*((byte *) W_CacheLumpName("SNDCURVE", PU_CACHE)) *
+ (snd_MaxVolume * 8)) >> 7;
+ }
+ else
+ {
+ for (i = 0; i < MAX_SND_DIST; i++)
+ {
+ soundCurve[i] =
+ (*((byte *) W_CacheLumpName("SNDCURVE", PU_CACHE) + i) *
+ (snd_MaxVolume * 8)) >> 7;
+ }
+ }
+}
+
+static boolean musicPaused;
+void S_SetMusicVolume(void)
+{
+ I_SetMusicVolume(snd_MusicVolume * 8);
+ if (snd_MusicVolume == 0)
+ {
+ I_PauseSong();
+ musicPaused = true;
+ }
+ else if (musicPaused)
+ {
+ musicPaused = false;
+ I_ResumeSong();
+ }
+}
+
+void S_ShutDown(void)
+{
+ I_StopSong();
+ I_UnRegisterSong(rs);
+ I_ShutdownSound();
+}
+
diff --git a/src/heretic/s_sound.h b/src/heretic/s_sound.h
new file mode 100644
index 00000000..e678fc99
--- /dev/null
+++ b/src/heretic/s_sound.h
@@ -0,0 +1,46 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// soundst.h
+
+#ifndef __SOUNDSTH__
+#define __SOUNDSTH__
+
+extern int snd_MaxVolume;
+extern int snd_MusicVolume;
+
+void S_Start(void);
+void S_StartSound(void *origin, int sound_id);
+void S_StartSoundAtVolume(void *origin, int sound_id, int volume);
+void S_StopSound(void *origin);
+void S_PauseSound(void);
+void S_ResumeSound(void);
+void S_UpdateSounds(mobj_t * listener);
+void S_StartSong(int song, boolean loop);
+void S_Init(void);
+void S_GetChannelInfo(SoundInfo_t * s);
+void S_SetMaxVolume(boolean fullprocess);
+void S_SetMusicVolume(void);
+
+#endif
diff --git a/src/heretic/sb_bar.c b/src/heretic/sb_bar.c
new file mode 100644
index 00000000..51bd3b9c
--- /dev/null
+++ b/src/heretic/sb_bar.c
@@ -0,0 +1,1295 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// SB_bar.c
+
+#include "doomdef.h"
+#include "deh_str.h"
+#include "i_video.h"
+#include "m_cheat.h"
+#include "m_misc.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "s_sound.h"
+#include "v_video.h"
+
+// Types
+
+typedef struct Cheat_s
+{
+ void (*func) (player_t * player, struct Cheat_s * cheat);
+ cheatseq_t *seq;
+} Cheat_t;
+
+// Private Functions
+
+static void DrawSoundInfo(void);
+static void ShadeLine(int x, int y, int height, int shade);
+static void ShadeChain(void);
+static void DrINumber(signed int val, int x, int y);
+static void DrBNumber(signed int val, int x, int y);
+static void DrawCommonBar(void);
+static void DrawMainBar(void);
+static void DrawInventoryBar(void);
+static void DrawFullScreenStuff(void);
+static boolean HandleCheats(byte key);
+static void CheatGodFunc(player_t * player, Cheat_t * cheat);
+static void CheatNoClipFunc(player_t * player, Cheat_t * cheat);
+static void CheatWeaponsFunc(player_t * player, Cheat_t * cheat);
+static void CheatPowerFunc(player_t * player, Cheat_t * cheat);
+static void CheatHealthFunc(player_t * player, Cheat_t * cheat);
+static void CheatKeysFunc(player_t * player, Cheat_t * cheat);
+static void CheatSoundFunc(player_t * player, Cheat_t * cheat);
+static void CheatTickerFunc(player_t * player, Cheat_t * cheat);
+static void CheatArtifact1Func(player_t * player, Cheat_t * cheat);
+static void CheatArtifact2Func(player_t * player, Cheat_t * cheat);
+static void CheatArtifact3Func(player_t * player, Cheat_t * cheat);
+static void CheatWarpFunc(player_t * player, Cheat_t * cheat);
+static void CheatChickenFunc(player_t * player, Cheat_t * cheat);
+static void CheatMassacreFunc(player_t * player, Cheat_t * cheat);
+static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat);
+static void CheatIDDQDFunc(player_t * player, Cheat_t * cheat);
+
+// Public Data
+
+boolean DebugSound; // debug flag for displaying sound info
+
+boolean inventory;
+int curpos;
+int inv_ptr;
+int ArtifactFlash;
+
+static int DisplayTicker = 0;
+
+// Private Data
+
+static int HealthMarker;
+static int ChainWiggle;
+static player_t *CPlayer;
+int playpalette;
+
+patch_t *PatchLTFACE;
+patch_t *PatchRTFACE;
+patch_t *PatchBARBACK;
+patch_t *PatchCHAIN;
+patch_t *PatchSTATBAR;
+patch_t *PatchLIFEGEM;
+//patch_t *PatchEMPWEAP;
+//patch_t *PatchLIL4BOX;
+patch_t *PatchLTFCTOP;
+patch_t *PatchRTFCTOP;
+//patch_t *PatchARMORBOX;
+//patch_t *PatchARTIBOX;
+patch_t *PatchSELECTBOX;
+//patch_t *PatchKILLSPIC;
+//patch_t *PatchMANAPIC;
+//patch_t *PatchPOWERICN;
+patch_t *PatchINVLFGEM1;
+patch_t *PatchINVLFGEM2;
+patch_t *PatchINVRTGEM1;
+patch_t *PatchINVRTGEM2;
+patch_t *PatchINumbers[10];
+patch_t *PatchNEGATIVE;
+patch_t *PatchSmNumbers[10];
+patch_t *PatchBLACKSQ;
+patch_t *PatchINVBAR;
+patch_t *PatchARMCLEAR;
+patch_t *PatchCHAINBACK;
+//byte *ShadeTables;
+int FontBNumBase;
+int spinbooklump;
+int spinflylump;
+
+// Toggle god mode
+cheatseq_t CheatGodSeq = CHEAT("quicken", 0);
+
+// Toggle no clipping mode
+cheatseq_t CheatNoClipSeq = CHEAT("kitty", 0);
+
+// Get all weapons and ammo
+cheatseq_t CheatWeaponsSeq = CHEAT("rambo", 0);
+
+// Toggle tome of power
+cheatseq_t CheatPowerSeq = CHEAT("shazam", 0);
+
+// Get full health
+cheatseq_t CheatHealthSeq = CHEAT("ponce", 0);
+
+// Get all keys
+cheatseq_t CheatKeysSeq = CHEAT("skel", 0);
+
+// Toggle sound debug info
+cheatseq_t CheatSoundSeq = CHEAT("noise", 0);
+
+// Toggle ticker
+cheatseq_t CheatTickerSeq = CHEAT("ticker", 0);
+
+// Get an artifact 1st stage (ask for type)
+cheatseq_t CheatArtifact1Seq = CHEAT("gimme", 0);
+
+// Get an artifact 2nd stage (ask for count)
+cheatseq_t CheatArtifact2Seq = CHEAT("gimme", 1);
+
+// Get an artifact final stage
+cheatseq_t CheatArtifact3Seq = CHEAT("gimme", 2);
+
+// Warp to new level
+cheatseq_t CheatWarpSeq = CHEAT("engage", 2);
+
+// Save a screenshot
+cheatseq_t CheatChickenSeq = CHEAT("cockadoodledoo", 0);
+
+// Kill all monsters
+cheatseq_t CheatMassacreSeq = CHEAT("massacre", 0);
+
+cheatseq_t CheatIDKFASeq = CHEAT("idkfa", 0);
+cheatseq_t CheatIDDQDSeq = CHEAT("iddqd", 0);
+
+static Cheat_t Cheats[] = {
+ {CheatGodFunc, &CheatGodSeq},
+ {CheatNoClipFunc, &CheatNoClipSeq},
+ {CheatWeaponsFunc, &CheatWeaponsSeq},
+ {CheatPowerFunc, &CheatPowerSeq},
+ {CheatHealthFunc, &CheatHealthSeq},
+ {CheatKeysFunc, &CheatKeysSeq},
+ {CheatSoundFunc, &CheatSoundSeq},
+ {CheatTickerFunc, &CheatTickerSeq},
+ {CheatArtifact1Func, &CheatArtifact1Seq},
+ {CheatArtifact2Func, &CheatArtifact2Seq},
+ {CheatArtifact3Func, &CheatArtifact3Seq},
+ {CheatWarpFunc, &CheatWarpSeq},
+ {CheatChickenFunc, &CheatChickenSeq},
+ {CheatMassacreFunc, &CheatMassacreSeq},
+ {CheatIDKFAFunc, &CheatIDKFASeq},
+ {CheatIDDQDFunc, &CheatIDDQDSeq},
+ {NULL, NULL}
+};
+
+//---------------------------------------------------------------------------
+//
+// PROC SB_Init
+//
+//---------------------------------------------------------------------------
+
+void SB_Init(void)
+{
+ int i;
+ int startLump;
+
+ PatchLTFACE = W_CacheLumpName(DEH_String("LTFACE"), PU_STATIC);
+ PatchRTFACE = W_CacheLumpName(DEH_String("RTFACE"), PU_STATIC);
+ PatchBARBACK = W_CacheLumpName(DEH_String("BARBACK"), PU_STATIC);
+ PatchINVBAR = W_CacheLumpName(DEH_String("INVBAR"), PU_STATIC);
+ PatchCHAIN = W_CacheLumpName(DEH_String("CHAIN"), PU_STATIC);
+ if (deathmatch)
+ {
+ PatchSTATBAR = W_CacheLumpName(DEH_String("STATBAR"), PU_STATIC);
+ }
+ else
+ {
+ PatchSTATBAR = W_CacheLumpName(DEH_String("LIFEBAR"), PU_STATIC);
+ }
+ if (!netgame)
+ { // single player game uses red life gem
+ PatchLIFEGEM = W_CacheLumpName(DEH_String("LIFEGEM2"), PU_STATIC);
+ }
+ else
+ {
+ PatchLIFEGEM = W_CacheLumpNum(W_GetNumForName(DEH_String("LIFEGEM0"))
+ + consoleplayer, PU_STATIC);
+ }
+ PatchLTFCTOP = W_CacheLumpName(DEH_String("LTFCTOP"), PU_STATIC);
+ PatchRTFCTOP = W_CacheLumpName(DEH_String("RTFCTOP"), PU_STATIC);
+ PatchSELECTBOX = W_CacheLumpName(DEH_String("SELECTBOX"), PU_STATIC);
+ PatchINVLFGEM1 = W_CacheLumpName(DEH_String("INVGEML1"), PU_STATIC);
+ PatchINVLFGEM2 = W_CacheLumpName(DEH_String("INVGEML2"), PU_STATIC);
+ PatchINVRTGEM1 = W_CacheLumpName(DEH_String("INVGEMR1"), PU_STATIC);
+ PatchINVRTGEM2 = W_CacheLumpName(DEH_String("INVGEMR2"), PU_STATIC);
+ PatchBLACKSQ = W_CacheLumpName(DEH_String("BLACKSQ"), PU_STATIC);
+ PatchARMCLEAR = W_CacheLumpName(DEH_String("ARMCLEAR"), PU_STATIC);
+ PatchCHAINBACK = W_CacheLumpName(DEH_String("CHAINBACK"), PU_STATIC);
+ startLump = W_GetNumForName(DEH_String("IN0"));
+ for (i = 0; i < 10; i++)
+ {
+ PatchINumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC);
+ }
+ PatchNEGATIVE = W_CacheLumpName(DEH_String("NEGNUM"), PU_STATIC);
+ FontBNumBase = W_GetNumForName(DEH_String("FONTB16"));
+ startLump = W_GetNumForName(DEH_String("SMALLIN0"));
+ for (i = 0; i < 10; i++)
+ {
+ PatchSmNumbers[i] = W_CacheLumpNum(startLump + i, PU_STATIC);
+ }
+ playpalette = W_GetNumForName(DEH_String("PLAYPAL"));
+ spinbooklump = W_GetNumForName(DEH_String("SPINBK0"));
+ spinflylump = W_GetNumForName(DEH_String("SPFLY0"));
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SB_Ticker
+//
+//---------------------------------------------------------------------------
+
+void SB_Ticker(void)
+{
+ int delta;
+ int curHealth;
+
+ if (leveltime & 1)
+ {
+ ChainWiggle = P_Random() & 1;
+ }
+ curHealth = players[consoleplayer].mo->health;
+ if (curHealth < 0)
+ {
+ curHealth = 0;
+ }
+ if (curHealth < HealthMarker)
+ {
+ delta = (HealthMarker - curHealth) >> 2;
+ if (delta < 1)
+ {
+ delta = 1;
+ }
+ else if (delta > 8)
+ {
+ delta = 8;
+ }
+ HealthMarker -= delta;
+ }
+ else if (curHealth > HealthMarker)
+ {
+ delta = (curHealth - HealthMarker) >> 2;
+ if (delta < 1)
+ {
+ delta = 1;
+ }
+ else if (delta > 8)
+ {
+ delta = 8;
+ }
+ HealthMarker += delta;
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrINumber
+//
+// Draws a three digit number.
+//
+//---------------------------------------------------------------------------
+
+static void DrINumber(signed int val, int x, int y)
+{
+ patch_t *patch;
+ int oldval;
+
+ oldval = val;
+ if (val < 0)
+ {
+ if (val < -9)
+ {
+ V_DrawPatch(x + 1, y + 1, W_CacheLumpName(DEH_String("LAME"), PU_CACHE));
+ }
+ else
+ {
+ val = -val;
+ V_DrawPatch(x + 18, y, PatchINumbers[val]);
+ V_DrawPatch(x + 9, y, PatchNEGATIVE);
+ }
+ return;
+ }
+ if (val > 99)
+ {
+ patch = PatchINumbers[val / 100];
+ V_DrawPatch(x, y, patch);
+ }
+ val = val % 100;
+ if (val > 9 || oldval > 99)
+ {
+ patch = PatchINumbers[val / 10];
+ V_DrawPatch(x + 9, y, patch);
+ }
+ val = val % 10;
+ patch = PatchINumbers[val];
+ V_DrawPatch(x + 18, y, patch);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrBNumber
+//
+// Draws a three digit number using FontB
+//
+//---------------------------------------------------------------------------
+
+static void DrBNumber(signed int val, int x, int y)
+{
+ patch_t *patch;
+ int xpos;
+ int oldval;
+
+ oldval = val;
+ xpos = x;
+ if (val < 0)
+ {
+ val = 0;
+ }
+ if (val > 99)
+ {
+ patch = W_CacheLumpNum(FontBNumBase + val / 100, PU_CACHE);
+ V_DrawShadowedPatch(xpos + 6 - patch->width / 2, y, patch);
+ }
+ val = val % 100;
+ xpos += 12;
+ if (val > 9 || oldval > 99)
+ {
+ patch = W_CacheLumpNum(FontBNumBase + val / 10, PU_CACHE);
+ V_DrawShadowedPatch(xpos + 6 - patch->width / 2, y, patch);
+ }
+ val = val % 10;
+ xpos += 12;
+ patch = W_CacheLumpNum(FontBNumBase + val, PU_CACHE);
+ V_DrawShadowedPatch(xpos + 6 - patch->width / 2, y, patch);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrSmallNumber
+//
+// Draws a small two digit number.
+//
+//---------------------------------------------------------------------------
+
+static void DrSmallNumber(int val, int x, int y)
+{
+ patch_t *patch;
+
+ if (val == 1)
+ {
+ return;
+ }
+ if (val > 9)
+ {
+ patch = PatchSmNumbers[val / 10];
+ V_DrawPatch(x, y, patch);
+ }
+ val = val % 10;
+ patch = PatchSmNumbers[val];
+ V_DrawPatch(x + 4, y, patch);
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC ShadeLine
+//
+//---------------------------------------------------------------------------
+
+static void ShadeLine(int x, int y, int height, int shade)
+{
+ byte *dest;
+ byte *shades;
+
+ shades = colormaps + 9 * 256 + shade * 2 * 256;
+ dest = I_VideoBuffer + y * SCREENWIDTH + x;
+ while (height--)
+ {
+ *(dest) = *(shades + *dest);
+ dest += SCREENWIDTH;
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC ShadeChain
+//
+//---------------------------------------------------------------------------
+
+static void ShadeChain(void)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ ShadeLine(277 + i, 190, 10, i / 2);
+ ShadeLine(19 + i, 190, 10, 7 - (i / 2));
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawSoundInfo
+//
+// Displays sound debugging information.
+//
+//---------------------------------------------------------------------------
+
+static void DrawSoundInfo(void)
+{
+ int i;
+ SoundInfo_t s;
+ ChanInfo_t *c;
+ char text[32];
+ int x;
+ int y;
+ int xPos[7] = { 1, 75, 112, 156, 200, 230, 260 };
+
+ if (leveltime & 16)
+ {
+ MN_DrTextA(DEH_String("*** SOUND DEBUG INFO ***"), xPos[0], 20);
+ }
+ S_GetChannelInfo(&s);
+ if (s.channelCount == 0)
+ {
+ return;
+ }
+ x = 0;
+ MN_DrTextA(DEH_String("NAME"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("MO.T"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("MO.X"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("MO.Y"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("ID"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("PRI"), xPos[x++], 30);
+ MN_DrTextA(DEH_String("DIST"), xPos[x++], 30);
+ for (i = 0; i < s.channelCount; i++)
+ {
+ c = &s.chan[i];
+ x = 0;
+ y = 40 + i * 10;
+ if (c->mo == NULL)
+ { // Channel is unused
+ MN_DrTextA(DEH_String("------"), xPos[0], y);
+ continue;
+ }
+ sprintf(text, "%s", c->name);
+ M_ForceUppercase(text);
+ MN_DrTextA(text, xPos[x++], y);
+ sprintf(text, "%d", c->mo->type);
+ MN_DrTextA(text, xPos[x++], y);
+ sprintf(text, "%d", c->mo->x >> FRACBITS);
+ MN_DrTextA(text, xPos[x++], y);
+ sprintf(text, "%d", c->mo->y >> FRACBITS);
+ MN_DrTextA(text, xPos[x++], y);
+ sprintf(text, "%d", c->id);
+ MN_DrTextA(text, xPos[x++], y);
+ sprintf(text, "%d", c->priority);
+ MN_DrTextA(text, xPos[x++], y);
+ sprintf(text, "%d", c->distance);
+ MN_DrTextA(text, xPos[x++], y);
+ }
+ UpdateState |= I_FULLSCRN;
+ BorderNeedRefresh = true;
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC SB_Drawer
+//
+//---------------------------------------------------------------------------
+
+char patcharti[][10] = {
+ {"ARTIBOX"}, // none
+ {"ARTIINVU"}, // invulnerability
+ {"ARTIINVS"}, // invisibility
+ {"ARTIPTN2"}, // health
+ {"ARTISPHL"}, // superhealth
+ {"ARTIPWBK"}, // tomeofpower
+ {"ARTITRCH"}, // torch
+ {"ARTIFBMB"}, // firebomb
+ {"ARTIEGGC"}, // egg
+ {"ARTISOAR"}, // fly
+ {"ARTIATLP"} // teleport
+};
+
+char ammopic[][10] = {
+ {"INAMGLD"},
+ {"INAMBOW"},
+ {"INAMBST"},
+ {"INAMRAM"},
+ {"INAMPNX"},
+ {"INAMLOB"}
+};
+
+int SB_state = -1;
+static int oldarti = 0;
+static int oldartiCount = 0;
+static int oldfrags = -9999;
+static int oldammo = -1;
+static int oldarmor = -1;
+static int oldweapon = -1;
+static int oldhealth = -1;
+static int oldlife = -1;
+static int oldkeys = -1;
+
+int playerkeys = 0;
+
+extern boolean automapactive;
+
+void SB_Drawer(void)
+{
+ int frame;
+ static boolean hitCenterFrame;
+
+ // Sound info debug stuff
+ if (DebugSound == true)
+ {
+ DrawSoundInfo();
+ }
+ CPlayer = &players[consoleplayer];
+ if (viewheight == SCREENHEIGHT && !automapactive)
+ {
+ DrawFullScreenStuff();
+ SB_state = -1;
+ }
+ else
+ {
+ if (SB_state == -1)
+ {
+ V_DrawPatch(0, 158, PatchBARBACK);
+ if (players[consoleplayer].cheats & CF_GODMODE)
+ {
+ V_DrawPatch(16, 167,
+ W_CacheLumpName(DEH_String("GOD1"), PU_CACHE));
+ V_DrawPatch(287, 167,
+ W_CacheLumpName(DEH_String("GOD2"), PU_CACHE));
+ }
+ oldhealth = -1;
+ }
+ DrawCommonBar();
+ if (!inventory)
+ {
+ if (SB_state != 0)
+ {
+ // Main interface
+ V_DrawPatch(34, 160, PatchSTATBAR);
+ oldarti = 0;
+ oldammo = -1;
+ oldarmor = -1;
+ oldweapon = -1;
+ oldfrags = -9999; //can't use -1, 'cuz of negative frags
+ oldlife = -1;
+ oldkeys = -1;
+ }
+ DrawMainBar();
+ SB_state = 0;
+ }
+ else
+ {
+ if (SB_state != 1)
+ {
+ V_DrawPatch(34, 160, PatchINVBAR);
+ }
+ DrawInventoryBar();
+ SB_state = 1;
+ }
+ }
+ SB_PaletteFlash();
+
+ // Flight icons
+ if (CPlayer->powers[pw_flight])
+ {
+ if (CPlayer->powers[pw_flight] > BLINKTHRESHOLD
+ || !(CPlayer->powers[pw_flight] & 16))
+ {
+ frame = (leveltime / 3) & 15;
+ if (CPlayer->mo->flags2 & MF2_FLY)
+ {
+ if (hitCenterFrame && (frame != 15 && frame != 0))
+ {
+ V_DrawPatch(20, 17, W_CacheLumpNum(spinflylump + 15,
+ PU_CACHE));
+ }
+ else
+ {
+ V_DrawPatch(20, 17, W_CacheLumpNum(spinflylump + frame,
+ PU_CACHE));
+ hitCenterFrame = false;
+ }
+ }
+ else
+ {
+ if (!hitCenterFrame && (frame != 15 && frame != 0))
+ {
+ V_DrawPatch(20, 17, W_CacheLumpNum(spinflylump + frame,
+ PU_CACHE));
+ hitCenterFrame = false;
+ }
+ else
+ {
+ V_DrawPatch(20, 17, W_CacheLumpNum(spinflylump + 15,
+ PU_CACHE));
+ hitCenterFrame = true;
+ }
+ }
+ BorderTopRefresh = true;
+ UpdateState |= I_MESSAGES;
+ }
+ else
+ {
+ BorderTopRefresh = true;
+ UpdateState |= I_MESSAGES;
+ }
+ }
+
+ if (CPlayer->powers[pw_weaponlevel2] && !CPlayer->chickenTics)
+ {
+ if (CPlayer->powers[pw_weaponlevel2] > BLINKTHRESHOLD
+ || !(CPlayer->powers[pw_weaponlevel2] & 16))
+ {
+ frame = (leveltime / 3) & 15;
+ V_DrawPatch(300, 17,
+ W_CacheLumpNum(spinbooklump + frame, PU_CACHE));
+ BorderTopRefresh = true;
+ UpdateState |= I_MESSAGES;
+ }
+ else
+ {
+ BorderTopRefresh = true;
+ UpdateState |= I_MESSAGES;
+ }
+ }
+/*
+ if(CPlayer->powers[pw_weaponlevel2] > BLINKTHRESHOLD
+ || (CPlayer->powers[pw_weaponlevel2]&8))
+ {
+ V_DrawPatch(291, 0, W_CacheLumpName("ARTIPWBK", PU_CACHE));
+ }
+ else
+ {
+ BorderTopRefresh = true;
+ }
+ }
+*/
+}
+
+// sets the new palette based upon current values of player->damagecount
+// and player->bonuscount
+void SB_PaletteFlash(void)
+{
+ static int sb_palette = 0;
+ int palette;
+ byte *pal;
+
+ CPlayer = &players[consoleplayer];
+
+ if (CPlayer->damagecount)
+ {
+ palette = (CPlayer->damagecount + 7) >> 3;
+ if (palette >= NUMREDPALS)
+ {
+ palette = NUMREDPALS - 1;
+ }
+ palette += STARTREDPALS;
+ }
+ else if (CPlayer->bonuscount)
+ {
+ palette = (CPlayer->bonuscount + 7) >> 3;
+ if (palette >= NUMBONUSPALS)
+ {
+ palette = NUMBONUSPALS - 1;
+ }
+ palette += STARTBONUSPALS;
+ }
+ else
+ {
+ palette = 0;
+ }
+ if (palette != sb_palette)
+ {
+ sb_palette = palette;
+ pal = (byte *) W_CacheLumpNum(playpalette, PU_CACHE) + palette * 768;
+ I_SetPalette(pal);
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawCommonBar
+//
+//---------------------------------------------------------------------------
+
+void DrawCommonBar(void)
+{
+ int chainY;
+ int healthPos;
+
+ V_DrawPatch(0, 148, PatchLTFCTOP);
+ V_DrawPatch(290, 148, PatchRTFCTOP);
+
+ if (oldhealth != HealthMarker)
+ {
+ oldhealth = HealthMarker;
+ healthPos = HealthMarker;
+ if (healthPos < 0)
+ {
+ healthPos = 0;
+ }
+ if (healthPos > 100)
+ {
+ healthPos = 100;
+ }
+ healthPos = (healthPos * 256) / 100;
+ chainY =
+ (HealthMarker == CPlayer->mo->health) ? 191 : 191 + ChainWiggle;
+ V_DrawPatch(0, 190, PatchCHAINBACK);
+ V_DrawPatch(2 + (healthPos % 17), chainY, PatchCHAIN);
+ V_DrawPatch(17 + healthPos, chainY, PatchLIFEGEM);
+ V_DrawPatch(0, 190, PatchLTFACE);
+ V_DrawPatch(276, 190, PatchRTFACE);
+ ShadeChain();
+ UpdateState |= I_STATBAR;
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawMainBar
+//
+//---------------------------------------------------------------------------
+
+void DrawMainBar(void)
+{
+ int i;
+ int temp;
+
+ // Ready artifact
+ if (ArtifactFlash)
+ {
+ V_DrawPatch(180, 161, PatchBLACKSQ);
+
+ temp = W_GetNumForName(DEH_String("useartia")) + ArtifactFlash - 1;
+
+ V_DrawPatch(182, 161, W_CacheLumpNum(temp, PU_CACHE));
+ ArtifactFlash--;
+ oldarti = -1; // so that the correct artifact fills in after the flash
+ UpdateState |= I_STATBAR;
+ }
+ else if (oldarti != CPlayer->readyArtifact
+ || oldartiCount != CPlayer->inventory[inv_ptr].count)
+ {
+ V_DrawPatch(180, 161, PatchBLACKSQ);
+ if (CPlayer->readyArtifact > 0)
+ {
+ V_DrawPatch(179, 160,
+ W_CacheLumpName(DEH_String(patcharti[CPlayer->readyArtifact]),
+ PU_CACHE));
+ DrSmallNumber(CPlayer->inventory[inv_ptr].count, 201, 182);
+ }
+ oldarti = CPlayer->readyArtifact;
+ oldartiCount = CPlayer->inventory[inv_ptr].count;
+ UpdateState |= I_STATBAR;
+ }
+
+ // Frags
+ if (deathmatch)
+ {
+ temp = 0;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ temp += CPlayer->frags[i];
+ }
+ if (temp != oldfrags)
+ {
+ V_DrawPatch(57, 171, PatchARMCLEAR);
+ DrINumber(temp, 61, 170);
+ oldfrags = temp;
+ UpdateState |= I_STATBAR;
+ }
+ }
+ else
+ {
+ temp = HealthMarker;
+ if (temp < 0)
+ {
+ temp = 0;
+ }
+ else if (temp > 100)
+ {
+ temp = 100;
+ }
+ if (oldlife != temp)
+ {
+ oldlife = temp;
+ V_DrawPatch(57, 171, PatchARMCLEAR);
+ DrINumber(temp, 61, 170);
+ UpdateState |= I_STATBAR;
+ }
+ }
+
+ // Keys
+ if (oldkeys != playerkeys)
+ {
+ if (CPlayer->keys[key_yellow])
+ {
+ V_DrawPatch(153, 164, W_CacheLumpName(DEH_String("ykeyicon"), PU_CACHE));
+ }
+ if (CPlayer->keys[key_green])
+ {
+ V_DrawPatch(153, 172, W_CacheLumpName(DEH_String("gkeyicon"), PU_CACHE));
+ }
+ if (CPlayer->keys[key_blue])
+ {
+ V_DrawPatch(153, 180, W_CacheLumpName(DEH_String("bkeyicon"), PU_CACHE));
+ }
+ oldkeys = playerkeys;
+ UpdateState |= I_STATBAR;
+ }
+ // Ammo
+ temp = CPlayer->ammo[wpnlev1info[CPlayer->readyweapon].ammo];
+ if (oldammo != temp || oldweapon != CPlayer->readyweapon)
+ {
+ V_DrawPatch(108, 161, PatchBLACKSQ);
+ if (temp && CPlayer->readyweapon > 0 && CPlayer->readyweapon < 7)
+ {
+ DrINumber(temp, 109, 162);
+ V_DrawPatch(111, 172,
+ W_CacheLumpName(DEH_String(ammopic[CPlayer->readyweapon - 1]),
+ PU_CACHE));
+ }
+ oldammo = temp;
+ oldweapon = CPlayer->readyweapon;
+ UpdateState |= I_STATBAR;
+ }
+
+ // Armor
+ if (oldarmor != CPlayer->armorpoints)
+ {
+ V_DrawPatch(224, 171, PatchARMCLEAR);
+ DrINumber(CPlayer->armorpoints, 228, 170);
+ oldarmor = CPlayer->armorpoints;
+ UpdateState |= I_STATBAR;
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+// PROC DrawInventoryBar
+//
+//---------------------------------------------------------------------------
+
+void DrawInventoryBar(void)
+{
+ char *patch;
+ int i;
+ int x;
+
+ x = inv_ptr - curpos;
+ UpdateState |= I_STATBAR;
+ V_DrawPatch(34, 160, PatchINVBAR);
+ for (i = 0; i < 7; i++)
+ {
+ //V_DrawPatch(50+i*31, 160, W_CacheLumpName("ARTIBOX", PU_CACHE));
+ if (CPlayer->inventorySlotNum > x + i
+ && CPlayer->inventory[x + i].type != arti_none)
+ {
+ patch = DEH_String(patcharti[CPlayer->inventory[x + i].type]);
+
+ V_DrawPatch(50 + i * 31, 160, W_CacheLumpName(patch, PU_CACHE));
+ DrSmallNumber(CPlayer->inventory[x + i].count, 69 + i * 31, 182);
+ }
+ }
+ V_DrawPatch(50 + curpos * 31, 189, PatchSELECTBOX);
+ if (x != 0)
+ {
+ V_DrawPatch(38, 159, !(leveltime & 4) ? PatchINVLFGEM1 :
+ PatchINVLFGEM2);
+ }
+ if (CPlayer->inventorySlotNum - x > 7)
+ {
+ V_DrawPatch(269, 159, !(leveltime & 4) ?
+ PatchINVRTGEM1 : PatchINVRTGEM2);
+ }
+}
+
+void DrawFullScreenStuff(void)
+{
+ char *patch;
+ int i;
+ int x;
+ int temp;
+
+ UpdateState |= I_FULLSCRN;
+ if (CPlayer->mo->health > 0)
+ {
+ DrBNumber(CPlayer->mo->health, 5, 180);
+ }
+ else
+ {
+ DrBNumber(0, 5, 180);
+ }
+ if (deathmatch)
+ {
+ temp = 0;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i])
+ {
+ temp += CPlayer->frags[i];
+ }
+ }
+ DrINumber(temp, 45, 185);
+ }
+ if (!inventory)
+ {
+ if (CPlayer->readyArtifact > 0)
+ {
+ patch = DEH_String(patcharti[CPlayer->readyArtifact]);
+ V_DrawTLPatch(286, 170, W_CacheLumpName(DEH_String("ARTIBOX"), PU_CACHE));
+ V_DrawPatch(286, 170, W_CacheLumpName(patch, PU_CACHE));
+ DrSmallNumber(CPlayer->inventory[inv_ptr].count, 307, 192);
+ }
+ }
+ else
+ {
+ x = inv_ptr - curpos;
+ for (i = 0; i < 7; i++)
+ {
+ V_DrawTLPatch(50 + i * 31, 168,
+ W_CacheLumpName(DEH_String("ARTIBOX"), PU_CACHE));
+ if (CPlayer->inventorySlotNum > x + i
+ && CPlayer->inventory[x + i].type != arti_none)
+ {
+ patch = DEH_String(patcharti[CPlayer->inventory[x + i].type]);
+ V_DrawPatch(50 + i * 31, 168,
+ W_CacheLumpName(patch, PU_CACHE));
+ DrSmallNumber(CPlayer->inventory[x + i].count, 69 + i * 31,
+ 190);
+ }
+ }
+ V_DrawPatch(50 + curpos * 31, 197, PatchSELECTBOX);
+ if (x != 0)
+ {
+ V_DrawPatch(38, 167, !(leveltime & 4) ? PatchINVLFGEM1 :
+ PatchINVLFGEM2);
+ }
+ if (CPlayer->inventorySlotNum - x > 7)
+ {
+ V_DrawPatch(269, 167, !(leveltime & 4) ?
+ PatchINVRTGEM1 : PatchINVRTGEM2);
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+//
+// FUNC SB_Responder
+//
+//--------------------------------------------------------------------------
+
+boolean SB_Responder(event_t * event)
+{
+ if (event->type == ev_keydown)
+ {
+ if (HandleCheats(event->data1))
+ { // Need to eat the key
+ return (true);
+ }
+ }
+ return (false);
+}
+
+//--------------------------------------------------------------------------
+//
+// FUNC HandleCheats
+//
+// Returns true if the caller should eat the key.
+//
+//--------------------------------------------------------------------------
+
+static boolean HandleCheats(byte key)
+{
+ int i;
+ boolean eat;
+
+ if (netgame || gameskill == sk_nightmare)
+ { // Can't cheat in a net-game, or in nightmare mode
+ return (false);
+ }
+ if (players[consoleplayer].health <= 0)
+ { // Dead players can't cheat
+ return (false);
+ }
+ eat = false;
+ for (i = 0; Cheats[i].func != NULL; i++)
+ {
+ if (cht_CheckCheat(Cheats[i].seq, key))
+ {
+ Cheats[i].func(&players[consoleplayer], &Cheats[i]);
+ S_StartSound(NULL, sfx_dorcls);
+ }
+ }
+ return (eat);
+}
+
+//--------------------------------------------------------------------------
+//
+// CHEAT FUNCTIONS
+//
+//--------------------------------------------------------------------------
+
+static void CheatGodFunc(player_t * player, Cheat_t * cheat)
+{
+ player->cheats ^= CF_GODMODE;
+ if (player->cheats & CF_GODMODE)
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATGODON), false);
+ }
+ else
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATGODOFF), false);
+ }
+ SB_state = -1;
+}
+
+static void CheatNoClipFunc(player_t * player, Cheat_t * cheat)
+{
+ player->cheats ^= CF_NOCLIP;
+ if (player->cheats & CF_NOCLIP)
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATNOCLIPON), false);
+ }
+ else
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATNOCLIPOFF), false);
+ }
+}
+
+static void CheatWeaponsFunc(player_t * player, Cheat_t * cheat)
+{
+ int i;
+ //extern boolean *WeaponInShareware;
+
+ player->armorpoints = 200;
+ player->armortype = 2;
+ if (!player->backpack)
+ {
+ for (i = 0; i < NUMAMMO; i++)
+ {
+ player->maxammo[i] *= 2;
+ }
+ player->backpack = true;
+ }
+ for (i = 0; i < NUMWEAPONS - 1; i++)
+ {
+ player->weaponowned[i] = true;
+ }
+ if (gamemode == shareware)
+ {
+ player->weaponowned[wp_skullrod] = false;
+ player->weaponowned[wp_phoenixrod] = false;
+ player->weaponowned[wp_mace] = false;
+ }
+ for (i = 0; i < NUMAMMO; i++)
+ {
+ player->ammo[i] = player->maxammo[i];
+ }
+ P_SetMessage(player, DEH_String(TXT_CHEATWEAPONS), false);
+}
+
+static void CheatPowerFunc(player_t * player, Cheat_t * cheat)
+{
+ if (player->powers[pw_weaponlevel2])
+ {
+ player->powers[pw_weaponlevel2] = 0;
+ P_SetMessage(player, DEH_String(TXT_CHEATPOWEROFF), false);
+ }
+ else
+ {
+ P_UseArtifact(player, arti_tomeofpower);
+ P_SetMessage(player, DEH_String(TXT_CHEATPOWERON), false);
+ }
+}
+
+static void CheatHealthFunc(player_t * player, Cheat_t * cheat)
+{
+ if (player->chickenTics)
+ {
+ player->health = player->mo->health = MAXCHICKENHEALTH;
+ }
+ else
+ {
+ player->health = player->mo->health = MAXHEALTH;
+ }
+ P_SetMessage(player, DEH_String(TXT_CHEATHEALTH), false);
+}
+
+static void CheatKeysFunc(player_t * player, Cheat_t * cheat)
+{
+ extern int playerkeys;
+
+ player->keys[key_yellow] = true;
+ player->keys[key_green] = true;
+ player->keys[key_blue] = true;
+ playerkeys = 7; // Key refresh flags
+ P_SetMessage(player, DEH_String(TXT_CHEATKEYS), false);
+}
+
+static void CheatSoundFunc(player_t * player, Cheat_t * cheat)
+{
+ DebugSound = !DebugSound;
+ if (DebugSound)
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATSOUNDON), false);
+ }
+ else
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATSOUNDOFF), false);
+ }
+}
+
+static void CheatTickerFunc(player_t * player, Cheat_t * cheat)
+{
+ DisplayTicker = !DisplayTicker;
+ if (DisplayTicker)
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATTICKERON), false);
+ }
+ else
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATTICKEROFF), false);
+ }
+
+ I_DisplayFPSDots(DisplayTicker);
+}
+
+static void CheatArtifact1Func(player_t * player, Cheat_t * cheat)
+{
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS1), false);
+}
+
+static void CheatArtifact2Func(player_t * player, Cheat_t * cheat)
+{
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS2), false);
+}
+
+static void CheatArtifact3Func(player_t * player, Cheat_t * cheat)
+{
+ char args[2];
+ int i;
+ int j;
+ artitype_t type;
+ int count;
+
+ cht_GetParam(cheat->seq, args);
+ type = args[0] - 'a' + 1;
+ count = args[1] - '0';
+ if (type == 26 && count == 0)
+ { // All artifacts
+ for (i = arti_none + 1; i < NUMARTIFACTS; i++)
+ {
+ if (gamemode == shareware
+ && (i == arti_superhealth || i == arti_teleport))
+ {
+ continue;
+ }
+ for (j = 0; j < 16; j++)
+ {
+ P_GiveArtifact(player, i, NULL);
+ }
+ }
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS3), false);
+ }
+ else if (type > arti_none && type < NUMARTIFACTS
+ && count > 0 && count < 10)
+ {
+ if (gamemode == shareware
+ && (type == arti_superhealth || type == arti_teleport))
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTSFAIL), false);
+ return;
+ }
+ for (i = 0; i < count; i++)
+ {
+ P_GiveArtifact(player, type, NULL);
+ }
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTS3), false);
+ }
+ else
+ { // Bad input
+ P_SetMessage(player, DEH_String(TXT_CHEATARTIFACTSFAIL), false);
+ }
+}
+
+static void CheatWarpFunc(player_t * player, Cheat_t * cheat)
+{
+ char args[2];
+ int episode;
+ int map;
+
+ cht_GetParam(cheat->seq, args);
+
+ episode = args[0] - '0';
+ map = args[1] - '0';
+ if (D_ValidEpisodeMap(gamemission, gamemode, episode, map))
+ {
+ G_DeferedInitNew(gameskill, episode, map);
+ P_SetMessage(player, DEH_String(TXT_CHEATWARP), false);
+ }
+}
+
+static void CheatChickenFunc(player_t * player, Cheat_t * cheat)
+{
+ extern boolean P_UndoPlayerChicken(player_t * player);
+
+ if (player->chickenTics)
+ {
+ if (P_UndoPlayerChicken(player))
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATCHICKENOFF), false);
+ }
+ }
+ else if (P_ChickenMorphPlayer(player))
+ {
+ P_SetMessage(player, DEH_String(TXT_CHEATCHICKENON), false);
+ }
+}
+
+static void CheatMassacreFunc(player_t * player, Cheat_t * cheat)
+{
+ P_Massacre();
+ P_SetMessage(player, DEH_String(TXT_CHEATMASSACRE), false);
+}
+
+static void CheatIDKFAFunc(player_t * player, Cheat_t * cheat)
+{
+ int i;
+ if (player->chickenTics)
+ {
+ return;
+ }
+ for (i = 1; i < 8; i++)
+ {
+ player->weaponowned[i] = false;
+ }
+ player->pendingweapon = wp_staff;
+ P_SetMessage(player, DEH_String(TXT_CHEATIDKFA), true);
+}
+
+static void CheatIDDQDFunc(player_t * player, Cheat_t * cheat)
+{
+ P_DamageMobj(player->mo, NULL, player->mo, 10000);
+ P_SetMessage(player, DEH_String(TXT_CHEATIDDQD), true);
+}
diff --git a/src/heretic/sounds.c b/src/heretic/sounds.c
new file mode 100644
index 00000000..f62110c6
--- /dev/null
+++ b/src/heretic/sounds.c
@@ -0,0 +1,257 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+
+// sounds.c
+
+#include "doomdef.h"
+#include "i_sound.h"
+#include "sounds.h"
+
+// Music info
+
+#define MUSIC(name) \
+ { name, 0, NULL, NULL }
+
+musicinfo_t S_music[] = {
+ MUSIC("MUS_E1M1"), // 1-1
+ MUSIC("MUS_E1M2"),
+ MUSIC("MUS_E1M3"),
+ MUSIC("MUS_E1M4"),
+ MUSIC("MUS_E1M5"),
+ MUSIC("MUS_E1M6"),
+ MUSIC("MUS_E1M7"),
+ MUSIC("MUS_E1M8"),
+ MUSIC("MUS_E1M9"),
+
+ MUSIC("MUS_E2M1"), // 2-1
+ MUSIC("MUS_E2M2"),
+ MUSIC("MUS_E2M3"),
+ MUSIC("MUS_E2M4"),
+ MUSIC("MUS_E1M4"),
+ MUSIC("MUS_E2M6"),
+ MUSIC("MUS_E2M7"),
+ MUSIC("MUS_E2M8"),
+ MUSIC("MUS_E2M9"),
+
+ MUSIC("MUS_E1M1"), // 3-1
+ MUSIC("MUS_E3M2"),
+ MUSIC("MUS_E3M3"),
+ MUSIC("MUS_E1M6"),
+ MUSIC("MUS_E1M3"),
+ MUSIC("MUS_E1M2"),
+ MUSIC("MUS_E1M5"),
+ MUSIC("MUS_E1M9"),
+ MUSIC("MUS_E2M6"),
+
+ MUSIC("MUS_E1M6"), // 4-1
+ MUSIC("MUS_E1M2"),
+ MUSIC("MUS_E1M3"),
+ MUSIC("MUS_E1M4"),
+ MUSIC("MUS_E1M5"),
+ MUSIC("MUS_E1M1"),
+ MUSIC("MUS_E1M7"),
+ MUSIC("MUS_E1M8"),
+ MUSIC("MUS_E1M9"),
+
+ MUSIC("MUS_E2M1"), // 5-1
+ MUSIC("MUS_E2M2"),
+ MUSIC("MUS_E2M3"),
+ MUSIC("MUS_E2M4"),
+ MUSIC("MUS_E1M4"),
+ MUSIC("MUS_E2M6"),
+ MUSIC("MUS_E2M7"),
+ MUSIC("MUS_E2M8"),
+ MUSIC("MUS_E2M9"),
+
+ MUSIC("MUS_E3M2"), // 6-1
+ MUSIC("MUS_E3M3"), // 6-2
+ MUSIC("MUS_E1M6"), // 6-3
+
+ MUSIC("MUS_TITL"),
+ MUSIC("MUS_INTR"),
+ MUSIC("MUS_CPTD")
+};
+
+// Sound info
+
+ /* Macro for original heretic sfxinfo_t
+#define SOUND(name, priority, numchannels) \
+ { name, NULL, priority, -1, NULL, 0, numchannels }
+#define SOUND_LINK(name, link_id, priority, numchannels) \
+ { name, &S_sfx[link_id], priority, -1, NULL, 0, numchannels }
+ */
+
+#define SOUND(name, priority, numchannels) \
+ { NULL, name, priority, NULL, -1, -1, -1, 0, numchannels, NULL }
+#define SOUND_LINK(name, link_id, priority, numchannels) \
+ { NULL, name, priority, &S_sfx[link_id], 0, 0, -1, 0, numchannels, NULL }
+
+sfxinfo_t S_sfx[] = {
+ SOUND("", 0, 0),
+ SOUND("gldhit", 32, 2),
+ SOUND("gntful", 32, -1),
+ SOUND("gnthit", 32, -1),
+ SOUND("gntpow", 32, -1),
+ SOUND("gntact", 32, -1),
+ SOUND("gntuse", 32, -1),
+ SOUND("phosht", 32, 2),
+ SOUND("phohit", 32, -1),
+ SOUND_LINK("-phopow", sfx_hedat1, 32, 1),
+ SOUND("lobsht", 20, 2),
+ SOUND("lobhit", 20, 2),
+ SOUND("lobpow", 20, 2),
+ SOUND("hrnsht", 32, 2),
+ SOUND("hrnhit", 32, 2),
+ SOUND("hrnpow", 32, 2),
+ SOUND("ramphit", 32, 2),
+ SOUND("ramrain", 10, 2),
+ SOUND("bowsht", 32, 2),
+ SOUND("stfhit", 32, 2),
+ SOUND("stfpow", 32, 2),
+ SOUND("stfcrk", 32, 2),
+ SOUND("impsit", 32, 2),
+ SOUND("impat1", 32, 2),
+ SOUND("impat2", 32, 2),
+ SOUND("impdth", 80, 2),
+ SOUND_LINK("-impact", sfx_impsit, 20, 2),
+ SOUND("imppai", 32, 2),
+ SOUND("mumsit", 32, 2),
+ SOUND("mumat1", 32, 2),
+ SOUND("mumat2", 32, 2),
+ SOUND("mumdth", 80, 2),
+ SOUND_LINK("-mumact", sfx_mumsit, 20, 2),
+ SOUND("mumpai", 32, 2),
+ SOUND("mumhed", 32, 2),
+ SOUND("bstsit", 32, 2),
+ SOUND("bstatk", 32, 2),
+ SOUND("bstdth", 80, 2),
+ SOUND("bstact", 20, 2),
+ SOUND("bstpai", 32, 2),
+ SOUND("clksit", 32, 2),
+ SOUND("clkatk", 32, 2),
+ SOUND("clkdth", 80, 2),
+ SOUND("clkact", 20, 2),
+ SOUND("clkpai", 32, 2),
+ SOUND("snksit", 32, 2),
+ SOUND("snkatk", 32, 2),
+ SOUND("snkdth", 80, 2),
+ SOUND("snkact", 20, 2),
+ SOUND("snkpai", 32, 2),
+ SOUND("kgtsit", 32, 2),
+ SOUND("kgtatk", 32, 2),
+ SOUND("kgtat2", 32, 2),
+ SOUND("kgtdth", 80, 2),
+ SOUND_LINK("-kgtact", sfx_kgtsit, 20, 2),
+ SOUND("kgtpai", 32, 2),
+ SOUND("wizsit", 32, 2),
+ SOUND("wizatk", 32, 2),
+ SOUND("wizdth", 80, 2),
+ SOUND("wizact", 20, 2),
+ SOUND("wizpai", 32, 2),
+ SOUND("minsit", 32, 2),
+ SOUND("minat1", 32, 2),
+ SOUND("minat2", 32, 2),
+ SOUND("minat3", 32, 2),
+ SOUND("mindth", 80, 2),
+ SOUND("minact", 20, 2),
+ SOUND("minpai", 32, 2),
+ SOUND("hedsit", 32, 2),
+ SOUND("hedat1", 32, 2),
+ SOUND("hedat2", 32, 2),
+ SOUND("hedat3", 32, 2),
+ SOUND("heddth", 80, 2),
+ SOUND("hedact", 20, 2),
+ SOUND("hedpai", 32, 2),
+ SOUND("sorzap", 32, 2),
+ SOUND("sorrise", 32, 2),
+ SOUND("sorsit", 200, 2),
+ SOUND("soratk", 32, 2),
+ SOUND("soract", 200, 2),
+ SOUND("sorpai", 200, 2),
+ SOUND("sordsph", 200, 2),
+ SOUND("sordexp", 200, 2),
+ SOUND("sordbon", 200, 2),
+ SOUND_LINK("-sbtsit", sfx_bstsit, 32, 2),
+ SOUND_LINK("-sbtatk", sfx_bstatk, 32, 2),
+ SOUND("sbtdth", 80, 2),
+ SOUND("sbtact", 20, 2),
+ SOUND("sbtpai", 32, 2),
+ SOUND("plroof", 32, 2),
+ SOUND("plrpai", 32, 2),
+ SOUND("plrdth", 80, 2),
+ SOUND("gibdth", 100, 2),
+ SOUND("plrwdth", 80, 2),
+ SOUND("plrcdth", 100, 2),
+ SOUND("itemup", 32, 2),
+ SOUND("wpnup", 32, 2),
+ SOUND("telept", 50, 2),
+ SOUND("doropn", 40, 2),
+ SOUND("dorcls", 40, 2),
+ SOUND("dormov", 40, 2),
+ SOUND("artiup", 32, 2),
+ SOUND("switch", 40, 2),
+ SOUND("pstart", 40, 2),
+ SOUND("pstop", 40, 2),
+ SOUND("stnmov", 40, 2),
+ SOUND("chicpai", 32, 2),
+ SOUND("chicatk", 32, 2),
+ SOUND("chicdth", 40, 2),
+ SOUND("chicact", 32, 2),
+ SOUND("chicpk1", 32, 2),
+ SOUND("chicpk2", 32, 2),
+ SOUND("chicpk3", 32, 2),
+ SOUND("keyup", 50, 2),
+ SOUND("ripslop", 16, 2),
+ SOUND("newpod", 16, -1),
+ SOUND("podexp", 40, -1),
+ SOUND("bounce", 16, 2),
+ SOUND_LINK("-volsht", sfx_bstatk, 16, 2),
+ SOUND_LINK("-volhit", sfx_lobhit, 16, 2),
+ SOUND("burn", 10, 2),
+ SOUND("splash", 10, 1),
+ SOUND("gloop", 10, 2),
+ SOUND("respawn", 10, 1),
+ SOUND("blssht", 32, 2),
+ SOUND("blshit", 32, 2),
+ SOUND("chat", 100, 1),
+ SOUND("artiuse", 32, 1),
+ SOUND("gfrag", 100, 1),
+ SOUND("waterfl", 16, 2),
+
+ // Monophonic sounds
+
+ SOUND("wind", 16, 1),
+ SOUND("amb1", 1, 1),
+ SOUND("amb2", 1, 1),
+ SOUND("amb3", 1, 1),
+ SOUND("amb4", 1, 1),
+ SOUND("amb5", 1, 1),
+ SOUND("amb6", 1, 1),
+ SOUND("amb7", 1, 1),
+ SOUND("amb8", 1, 1),
+ SOUND("amb9", 1, 1),
+ SOUND("amb10", 1, 1),
+ SOUND("amb11", 1, 0)
+};
diff --git a/src/heretic/sounds.h b/src/heretic/sounds.h
new file mode 100644
index 00000000..e50a6ad6
--- /dev/null
+++ b/src/heretic/sounds.h
@@ -0,0 +1,299 @@
+// Emacs style mode select -*- C++ -*-
+//-----------------------------------------------------------------------------
+//
+// Copyright(C) 1993-1996 Id Software, Inc.
+// Copyright(C) 1993-2008 Raven Software
+// Copyright(C) 2008 Simon Howard
+//
+// 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.
+//
+//-----------------------------------------------------------------------------
+
+// sounds.h
+
+#ifndef __SOUNDSH__
+#define __SOUNDSH__
+
+#include "i_sound.h"
+
+#define MAX_SND_DIST 1600
+#define MAX_CHANNELS 16
+
+// Music identifiers
+
+typedef enum
+{
+ 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_e4m1,
+ mus_e4m2,
+ mus_e4m3,
+ mus_e4m4,
+ mus_e4m5,
+ mus_e4m6,
+ mus_e4m7,
+ mus_e4m8,
+ mus_e4m9,
+
+ mus_e5m1,
+ mus_e5m2,
+ mus_e5m3,
+ mus_e5m4,
+ mus_e5m5,
+ mus_e5m6,
+ mus_e5m7,
+ mus_e5m8,
+ mus_e5m9,
+
+ mus_e6m1,
+ mus_e6m2,
+ mus_e6m3,
+
+ mus_titl,
+ mus_intr,
+ mus_cptd,
+ NUMMUSIC
+} musicenum_t;
+
+#if 0
+typedef struct
+{
+ char name[8];
+} musicinfo_t;
+
+typedef struct sfxinfo_s
+{
+ char name[8];
+ struct sfxinfo_s *link; // Make alias for another sound
+ unsigned short priority; // Higher priority takes precendence
+ int usefulness; // Determines when a sound should be cached out
+ void *snd_ptr;
+ int lumpnum;
+ int numchannels; // total number of channels a sound type may occupy
+} sfxinfo_t;
+
+#endif
+
+typedef struct
+{
+ mobj_t *mo;
+ int sound_id;
+ int handle;
+ int pitch;
+ int priority;
+} channel_t;
+
+typedef struct
+{
+ int id;
+ unsigned short priority;
+ char *name;
+ mobj_t *mo;
+ int distance;
+} ChanInfo_t;
+
+typedef struct
+{
+ int channelCount;
+ int musicVolume;
+ int soundVolume;
+ ChanInfo_t chan[8];
+} SoundInfo_t;
+
+// Sound identifiers
+
+typedef enum
+{
+ sfx_None,
+ sfx_gldhit,
+ sfx_gntful,
+ sfx_gnthit,
+ sfx_gntpow,
+ sfx_gntact,
+ sfx_gntuse,
+ sfx_phosht,
+ sfx_phohit,
+ sfx_phopow,
+ sfx_lobsht,
+ sfx_lobhit,
+ sfx_lobpow,
+ sfx_hrnsht,
+ sfx_hrnhit,
+ sfx_hrnpow,
+ sfx_ramphit,
+ sfx_ramrain,
+ sfx_bowsht,
+ sfx_stfhit,
+ sfx_stfpow,
+ sfx_stfcrk,
+ sfx_impsit,
+ sfx_impat1,
+ sfx_impat2,
+ sfx_impdth,
+ sfx_impact,
+ sfx_imppai,
+ sfx_mumsit,
+ sfx_mumat1,
+ sfx_mumat2,
+ sfx_mumdth,
+ sfx_mumact,
+ sfx_mumpai,
+ sfx_mumhed,
+ sfx_bstsit,
+ sfx_bstatk,
+ sfx_bstdth,
+ sfx_bstact,
+ sfx_bstpai,
+ sfx_clksit,
+ sfx_clkatk,
+ sfx_clkdth,
+ sfx_clkact,
+ sfx_clkpai,
+ sfx_snksit,
+ sfx_snkatk,
+ sfx_snkdth,
+ sfx_snkact,
+ sfx_snkpai,
+ sfx_kgtsit,
+ sfx_kgtatk,
+ sfx_kgtat2,
+ sfx_kgtdth,
+ sfx_kgtact,
+ sfx_kgtpai,
+ sfx_wizsit,
+ sfx_wizatk,
+ sfx_wizdth,
+ sfx_wizact,
+ sfx_wizpai,
+ sfx_minsit,
+ sfx_minat1,
+ sfx_minat2,
+ sfx_minat3,
+ sfx_mindth,
+ sfx_minact,
+ sfx_minpai,
+ sfx_hedsit,
+ sfx_hedat1,
+ sfx_hedat2,
+ sfx_hedat3,
+ sfx_heddth,
+ sfx_hedact,
+ sfx_hedpai,
+ sfx_sorzap,
+ sfx_sorrise,
+ sfx_sorsit,
+ sfx_soratk,
+ sfx_soract,
+ sfx_sorpai,
+ sfx_sordsph,
+ sfx_sordexp,
+ sfx_sordbon,
+ sfx_sbtsit,
+ sfx_sbtatk,
+ sfx_sbtdth,
+ sfx_sbtact,
+ sfx_sbtpai,
+ sfx_plroof,
+ sfx_plrpai,
+ sfx_plrdth, // Normal
+ sfx_gibdth, // Extreme
+ sfx_plrwdth, // Wimpy
+ sfx_plrcdth, // Crazy
+ sfx_itemup,
+ sfx_wpnup,
+ sfx_telept,
+ sfx_doropn,
+ sfx_dorcls,
+ sfx_dormov,
+ sfx_artiup,
+ sfx_switch,
+ sfx_pstart,
+ sfx_pstop,
+ sfx_stnmov,
+ sfx_chicpai,
+ sfx_chicatk,
+ sfx_chicdth,
+ sfx_chicact,
+ sfx_chicpk1,
+ sfx_chicpk2,
+ sfx_chicpk3,
+ sfx_keyup,
+ sfx_ripslop,
+ sfx_newpod,
+ sfx_podexp,
+ sfx_bounce,
+ sfx_volsht,
+ sfx_volhit,
+ sfx_burn,
+ sfx_splash,
+ sfx_gloop,
+ sfx_respawn,
+ sfx_blssht,
+ sfx_blshit,
+ sfx_chat,
+ sfx_artiuse,
+ sfx_gfrag,
+ sfx_waterfl,
+
+ // Monophonic sounds
+
+ sfx_wind,
+ sfx_amb1,
+ sfx_amb2,
+ sfx_amb3,
+ sfx_amb4,
+ sfx_amb5,
+ sfx_amb6,
+ sfx_amb7,
+ sfx_amb8,
+ sfx_amb9,
+ sfx_amb10,
+ sfx_amb11,
+ NUMSFX
+} sfxenum_t;
+
+extern sfxinfo_t S_sfx[];
+extern musicinfo_t S_music[];
+
+#endif